Skip to main content

bitcoin_io/
hash.rs

1// SPDX-License-Identifier: CC0-1.0
2
3//! I/O hashing support.
4//!
5//! Support for various hashing related things e.g.
6//!
7//! - Hashing to a writer.
8//! - Implement I/O traits for hash engines.
9
10use hashes::hmac::HmacEngine;
11use hashes::{
12    hash160, ripemd160, sha1, sha256, sha256d, sha256t, sha384, sha512, sha512_256, siphash24,
13    HashEngine as _,
14};
15
16use crate::BufRead;
17
18macro_rules! impl_write {
19    ($ty: ty, $write_fn: expr, $flush_fn: expr $(, $bounded_ty: ident : $bounds: path),*) => {
20        // `std::io::Write` is implemented in `bitcoin_hashes` because of the orphan rule.
21        impl<$($bounded_ty: $bounds),*> crate::Write for $ty {
22            #[inline]
23            fn write(&mut self, buf: &[u8]) -> crate::Result<usize> {
24                $write_fn(self, buf)}
25
26            #[inline]
27            fn flush(&mut self) -> crate::Result<()> {
28                $flush_fn(self)
29            }
30        }
31    }
32}
33
34impl_write!(
35    hash160::HashEngine,
36    |us: &mut hash160::HashEngine, buf| {
37        hashes::HashEngine::input(us, buf);
38        Ok(buf.len())
39    },
40    |_us| { Ok(()) }
41);
42
43impl_write!(
44    ripemd160::HashEngine,
45    |us: &mut ripemd160::HashEngine, buf| {
46        hashes::HashEngine::input(us, buf);
47        Ok(buf.len())
48    },
49    |_us| { Ok(()) }
50);
51
52impl_write!(
53    sha1::HashEngine,
54    |us: &mut sha1::HashEngine, buf| {
55        hashes::HashEngine::input(us, buf);
56        Ok(buf.len())
57    },
58    |_us| { Ok(()) }
59);
60
61impl_write!(
62    sha256::HashEngine,
63    |us: &mut sha256::HashEngine, buf| {
64        hashes::HashEngine::input(us, buf);
65        Ok(buf.len())
66    },
67    |_us| { Ok(()) }
68);
69
70impl_write!(
71    sha256d::HashEngine,
72    |us: &mut sha256d::HashEngine, buf| {
73        hashes::HashEngine::input(us, buf);
74        Ok(buf.len())
75    },
76    |_us| { Ok(()) }
77);
78
79impl_write!(
80    sha256t::HashEngine<T>,
81    |us: &mut sha256t::HashEngine<T>, buf| {
82        hashes::HashEngine::input(us, buf);
83        Ok(buf.len())
84    },
85    |_us| { Ok(()) },
86    T: sha256t::Tag
87);
88
89impl_write!(
90    sha384::HashEngine,
91    |us: &mut sha384::HashEngine, buf| {
92        hashes::HashEngine::input(us, buf);
93        Ok(buf.len())
94    },
95    |_us| { Ok(()) }
96);
97
98impl_write!(
99    sha512::HashEngine,
100    |us: &mut sha512::HashEngine, buf| {
101        hashes::HashEngine::input(us, buf);
102        Ok(buf.len())
103    },
104    |_us| { Ok(()) }
105);
106
107impl_write!(
108    sha512_256::HashEngine,
109    |us: &mut sha512_256::HashEngine, buf| {
110        hashes::HashEngine::input(us, buf);
111        Ok(buf.len())
112    },
113    |_us| { Ok(()) }
114);
115
116impl_write!(
117    siphash24::HashEngine,
118    |us: &mut siphash24::HashEngine, buf| {
119        hashes::HashEngine::input(us, buf);
120        Ok(buf.len())
121    },
122    |_us| { Ok(()) }
123);
124
125impl_write!(
126    HmacEngine<T>,
127    |us: &mut HmacEngine<T>, buf| {
128        us.input(buf);
129        Ok(buf.len())
130    },
131    |_us| { Ok(()) },
132    T: hashes::HashEngine
133);
134
135/// Hashes data from a reader.
136///
137/// # Errors
138///
139/// If an I/O error occurs while reading from the underlying reader.
140pub fn hash_reader<T>(reader: &mut impl BufRead) -> Result<T::Hash, crate::Error>
141where
142    T: hashes::HashEngine + Default,
143{
144    let mut engine = T::default();
145    loop {
146        let bytes = reader.fill_buf()?;
147
148        let read = bytes.len();
149        // Empty slice means EOF.
150        if read == 0 {
151            break;
152        }
153
154        engine.input(bytes);
155        reader.consume(read);
156    }
157    Ok(engine.finalize())
158}
159
160#[cfg(test)]
161#[cfg(feature = "alloc")]
162mod tests {
163    use alloc::format;
164    use alloc::vec;
165
166    use hashes::hmac;
167
168    use super::*;
169    use crate::{Cursor, Write as _};
170
171    macro_rules! write_test {
172        ($mod:ident, $exp_empty:expr, $exp_256:expr, $exp_64k:expr,) => {
173            #[test]
174            fn $mod() {
175                let mut engine = $mod::Hash::engine();
176                engine.write_all(&[]).unwrap();
177                assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_empty);
178
179                let mut engine = $mod::Hash::engine();
180                engine.write_all(&[1; 256]).unwrap();
181                assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_256);
182
183                let mut engine = $mod::Hash::engine();
184                let large_buffer = vec![99u8; 64000];
185                engine.write_all(&large_buffer).unwrap();
186                assert_eq!(format!("{}", $mod::Hash::from_engine(engine)), $exp_64k);
187            }
188        };
189    }
190
191    write_test!(
192        sha1,
193        "da39a3ee5e6b4b0d3255bfef95601890afd80709",
194        "ac458b067c6b021c7e9358229b636e9d1e4cb154",
195        "e4b66838f9f7b6f91e5be32a02ae78094df402e7",
196    );
197
198    write_test!(
199        sha256,
200        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
201        "2661920f2409dd6c8adeb0c44972959f232b6429afa913845d0fd95e7e768234",
202        "5c5e904f5d4fd587c7a906bf846e08a927286f388c54c39213a4884695271bbc",
203    );
204
205    write_test!(
206        sha256d,
207        "56944c5d3f98413ef45cf54545538103cc9f298e0575820ad3591376e2e0f65d",
208        "374000d830c75d10d9417e493a7652920f30efbd300e3fb092f24c28c20baf64",
209        "0050d4148ad7a0437ca0643fad5bf4614cd95d9ba21fde52370b37dcc3f03307",
210    );
211
212    write_test!(
213        sha384,
214        "38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b",
215        "82135637ef6d6dd31a20e2bc9998681a3eecaf8f8c76d45e545214de38439d9a533848ec75f53e4b1a8805709c5124d0",
216        "fb7511d9a98c5686f9c2f55e242397815c9229d8759451e1710b8da6861e08d52f0357176f4b74f8cad9e23ab65411c7",
217    );
218
219    write_test!(
220        sha512,
221        "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce\
222         47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e",
223        "57ecf739d3a7ca647639adae80a05f4f361304bfcbfa1ceba93296b096e74287\
224         45fc10c142cecdd3bb587a3dba598c072f6f78b31cc0a06a3da0105ee51f75d6",
225        "dd28f78c53f3bc9bd0c2dca9642a1ad402a70412f985c1f6e54fadb98ce9c458\
226         4761df8d04ed04bb734ba48dd2106bb9ea54524f1394cdd18e6da3166e71c3ee",
227    );
228
229    write_test!(
230        sha512_256,
231        "c672b8d1ef56ed28ab87c3622c5114069bdd3ad7b8f9737498d0c01ecef0967a",
232        "8d4bb96e7956cf5f08bf5c45f7982630c46b0b022f25cbaf722ae97c06a6e7a2",
233        "3367646f3e264653f7dd664ac2cb6d3b96329e86ffb7a29a1082e2a4ddc9ee7a",
234    );
235
236    write_test!(
237        ripemd160,
238        "9c1185a5c5e9fc54612808977ee8f548b2258d31",
239        "e571a1ca5b780aa52bafdb9ec852544ffca418ba",
240        "ddd2ecce739e823629c7d46ab18918e9c4a51c75",
241    );
242
243    write_test!(
244        hash160,
245        "b472a266d0bd89c13706a4132ccfb16f7c3b9fcb",
246        "671356a1a874695ad3bc20cae440f4360835bd5a",
247        "a9608c952c8dbcc20c53803d2ca5ad31d64d9313",
248    );
249
250    #[test]
251    fn hmac() {
252        let mut engine = hmac::HmacEngine::<sha256::HashEngine>::new(&[0xde, 0xad, 0xbe, 0xef]);
253        engine.write_all(&[]).unwrap();
254        assert_eq!(
255            format!("{}", engine.finalize()),
256            "bf5515149cf797955c4d3194cca42472883281951697c8375d9d9b107f384225"
257        );
258
259        let mut engine = hmac::HmacEngine::<sha256::HashEngine>::new(&[0xde, 0xad, 0xbe, 0xef]);
260        engine.write_all(&[1; 256]).unwrap();
261        assert_eq!(
262            format!("{}", engine.finalize()),
263            "59c9aca10c81c73cb4c196d94db741b6bf2050e0153d5a45f2526bff34675ac5"
264        );
265
266        let mut engine = hmac::HmacEngine::<sha256::HashEngine>::new(&[0xde, 0xad, 0xbe, 0xef]);
267        let large_buffer = vec![99u8; 64000];
268        engine.write_all(&large_buffer).unwrap();
269        assert_eq!(
270            format!("{}", engine.finalize()),
271            "30df499717415a395379a1eaabe50038036e4abb5afc94aa55c952f4aa57be08"
272        );
273    }
274
275    #[test]
276    fn siphash24() {
277        let mut engine = siphash24::HashEngine::with_keys(0, 0);
278        engine.write_all(&[]).unwrap();
279        assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "d70077739d4b921e");
280
281        let mut engine = siphash24::HashEngine::with_keys(0, 0);
282        engine.write_all(&[1; 256]).unwrap();
283        assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "3a3ccefde9b5b1e3");
284
285        let mut engine = siphash24::HashEngine::with_keys(0, 0);
286        let large_buffer = vec![99u8; 64000];
287        engine.write_all(&large_buffer).unwrap();
288        assert_eq!(format!("{}", siphash24::Hash::from_engine(engine)), "ce456e4e4ecbc5bf");
289    }
290
291    // Data and expected hashes taken from `bitcoin_hashes/tests/regression.rs`.
292    const DATA: &str = "arbitrary data to hash as a regression test";
293    const HMAC_KEY: &[u8] = b"some key";
294
295    macro_rules! impl_hash_reader_test {
296        ($($test_name:ident, $module:ident, $want:literal);* $(;)?) => {
297            $(
298                #[test]
299                fn $test_name() {
300                    let hash = $module::Hash::hash(DATA.as_bytes());
301                    let got = format!("{}", hash);
302                    assert_eq!(got, $want);
303
304                    let mut reader = Cursor::new(DATA);
305                    let hash_from_reader = $crate::hash_reader::<$module::HashEngine>(&mut reader).unwrap();
306                    assert_eq!(hash_from_reader, hash)
307                }
308            )*
309        }
310    }
311
312    impl_hash_reader_test! {
313        hash_from_reader_hash160, hash160, "a17909f6d5373b0085c4180ba207126e5040f74d";
314        hash_from_reader_ripemd160, ripemd160, "e6801701c77a1cd85662335258c7869631b4a9a8";
315        hash_from_reader_sha1, sha1, "e1e81eeabadafa3d5d41cc3f405385426b0f47fd";
316        hash_from_reader_sha256, sha256, "d291c6c5a07fa1d9315cdae090ebe14169fbe0a219cd55a48d0d2104eab6ec51";
317        hash_from_reader_sha256d, sha256d, "93a743b022290bde3233a619b21aaebe06c5cf5cc959464c41be35711e37731b";
318        hash_from_reader_sha384, sha384, "f545bd83d297978d47a7f26b858a54188499dfb4d7d570a6a2362c765031d57a29d7e002df5e34d184e70b65a4f47153";
319        hash_from_reader_sha512, sha512, "057d0a37e9e0ac9a93acde0752748da059a27bcf946c7af00692ac1a95db8d21f965f40af22efc4710f100f8d3e43f79f77b1f48e1e400a95b7344b7bc0dfd10";
320        hash_from_reader_sha512_256, sha512_256, "e204244c429b5bca037a2a8a6e7ed8a42b808ceaff182560840bb8c5c8e9a2ec";
321    }
322
323    #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
324    pub struct RegHashTag; // Name comes from regression tests in `bitcoin_hashes`.
325
326    impl sha256t::Tag for RegHashTag {
327        const MIDSTATE: sha256::Midstate = sha256::Midstate::new([0xab; 32], 64);
328    }
329
330    type RegHash = sha256t::Hash<RegHashTag>;
331
332    #[test]
333    fn regression_sha256t() {
334        let hash = RegHash::hash(DATA.as_bytes());
335        let got = format!("{}", hash);
336        let want = "17db326d7c13867376ccca1f8a211377be3cbeaeb372f167822284866ddf14ca";
337        assert_eq!(got, want);
338    }
339
340    #[test]
341    fn regression_hmac_sha256_with_key() {
342        let mut engine = HmacEngine::<sha256::HashEngine>::new(HMAC_KEY);
343        engine.input(DATA.as_bytes());
344        let hash = engine.finalize();
345
346        let got = format!("{}", hash);
347        let want = "d159cecaf4adf90b6a641bab767e4817d3a51c414acea3682686c35ec0b37b52";
348        assert_eq!(got, want);
349    }
350
351    #[test]
352    fn regression_hmac_sha512_with_key() {
353        let mut engine = HmacEngine::<sha512::HashEngine>::new(HMAC_KEY);
354        engine.input(DATA.as_bytes());
355        let hash = engine.finalize();
356
357        let got = format!("{}", hash);
358        let want = "8511773748f89ba22c07fb3a2981a12c1823695119de41f4a62aead6b848bd34939acf16475c35ed7956114fead3e794cc162ecd35e447a4dabc3227d55f757b";
359        assert_eq!(got, want);
360    }
361
362    #[test]
363    fn regression_siphash24_with_key() {
364        let mut engine = siphash24::HashEngine::with_keys(0, 0);
365        engine.input(DATA.as_bytes());
366        let hash = siphash24::Hash::from_engine(engine);
367
368        let got = format!("{}", hash);
369        let want = "e823ed82311d601a";
370        assert_eq!(got, want);
371    }
372}