Skip to main content

forest/utils/
multihash.rs

1// Copyright 2019-2026 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3
4//!
5//! This module back-fills the Identify hasher and code that was removed in `multihash` crate.
6//! See <https://github.com/multiformats/rust-multihash/blob/master/CHANGELOG.md#-breaking-changes>
7//! and <https://github.com/multiformats/rust-multihash/pull/289>
8//!
9
10pub mod prelude {
11    pub use super::MultihashCode;
12    pub use multihash_codetable::MultihashDigest as _;
13}
14
15use multihash_derive::{Hasher, MultihashDigest};
16
17/// Extends [`multihash_codetable::Code`] with `Identity`
18// `alloc_size` bounds every digest here; it also sizes the identity buffer
19// (`IdentityHasher::<64>` below). Keep it in sync with `IDENTITY_MAX_SIZE`.
20#[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)]
21#[mh(alloc_size = 64)]
22pub enum MultihashCode {
23    #[mh(code = 0x0, hasher = IdentityHasher::<64>)]
24    Identity,
25    /// SHA-256 (32-byte hash size)
26    #[mh(code = 0x12, hasher = multihash_codetable::Sha2_256)]
27    Sha2_256,
28    /// SHA-512 (64-byte hash size)
29    #[mh(code = 0x13, hasher = multihash_codetable::Sha2_512)]
30    Sha2_512,
31    /// SHA3-224 (28-byte hash size)
32    #[mh(code = 0x17, hasher = multihash_codetable::Sha3_224)]
33    Sha3_224,
34    /// SHA3-256 (32-byte hash size)
35    #[mh(code = 0x16, hasher = multihash_codetable::Sha3_256)]
36    Sha3_256,
37    /// SHA3-384 (48-byte hash size)
38    #[mh(code = 0x15, hasher = multihash_codetable::Sha3_384)]
39    Sha3_384,
40    /// SHA3-512 (64-byte hash size)
41    #[mh(code = 0x14, hasher = multihash_codetable::Sha3_512)]
42    Sha3_512,
43    /// Keccak-224 (28-byte hash size)
44    #[mh(code = 0x1a, hasher = multihash_codetable::Keccak224)]
45    Keccak224,
46    /// Keccak-256 (32-byte hash size)
47    #[mh(code = 0x1b, hasher = multihash_codetable::Keccak256)]
48    Keccak256,
49    /// Keccak-384 (48-byte hash size)
50    #[mh(code = 0x1c, hasher = multihash_codetable::Keccak384)]
51    Keccak384,
52    /// Keccak-512 (64-byte hash size)
53    #[mh(code = 0x1d, hasher = multihash_codetable::Keccak512)]
54    Keccak512,
55    /// BLAKE2b-256 (32-byte hash size)
56    #[mh(code = 0xb220, hasher = multihash_codetable::Blake2b256)]
57    Blake2b256,
58    /// BLAKE2b-512 (64-byte hash size)
59    #[mh(code = 0xb240, hasher = multihash_codetable::Blake2b512)]
60    Blake2b512,
61    /// BLAKE2s-128 (16-byte hash size)
62    #[mh(code = 0xb250, hasher = multihash_codetable::Blake2s128)]
63    Blake2s128,
64    /// BLAKE2s-256 (32-byte hash size)
65    #[mh(code = 0xb260, hasher = multihash_codetable::Blake2s256)]
66    Blake2s256,
67    /// BLAKE3-256 (32-byte hash size)
68    #[mh(code = 0x1e, hasher = multihash_codetable::Blake3_256)]
69    Blake3_256,
70    /// RIPEMD-160 (20-byte hash size)
71    #[mh(code = 0x1053, hasher = multihash_codetable::Ripemd160)]
72    Ripemd160,
73    /// RIPEMD-256 (32-byte hash size)
74    #[mh(code = 0x1054, hasher = multihash_codetable::Ripemd256)]
75    Ripemd256,
76    /// RIPEMD-320 (40-byte hash size)
77    #[mh(code = 0x1055, hasher = multihash_codetable::Ripemd320)]
78    Ripemd320,
79}
80
81impl MultihashCode {
82    /// Max input the [`MultihashCode::Identity`] hasher can hold: the
83    /// `IdentityHasher::<64>` buffer / `#[mh(alloc_size = 64)]` declared above.
84    pub const IDENTITY_MAX_SIZE: usize = 64;
85
86    /// [`MultihashDigest::digest`], failing instead of hashing input the hasher
87    /// cannot hold. Use this when the code and data come from decoded input.
88    pub fn checked_digest(&self, data: &[u8]) -> anyhow::Result<Multihash> {
89        anyhow::ensure!(
90            *self != Self::Identity || data.len() <= Self::IDENTITY_MAX_SIZE,
91            "identity multihash input of {} bytes exceeds the {}-byte limit",
92            data.len(),
93            Self::IDENTITY_MAX_SIZE
94        );
95        Ok(self.digest(data))
96    }
97
98    /// Calculate the [`Multihash`] of the input byte stream.
99    pub fn digest_byte_stream<R: std::io::Read>(&self, bytes: &mut R) -> anyhow::Result<Multihash> {
100        fn hash<'a, H: Hasher, R: std::io::Read>(
101            hasher: &'a mut H,
102            bytes: &'a mut R,
103        ) -> anyhow::Result<&'a [u8]> {
104            let mut buf = [0; 1024];
105            loop {
106                let n = bytes.read(&mut buf)?;
107                if n == 0 {
108                    break;
109                }
110                if let Some(b) = buf.get(0..n) {
111                    hasher.update(b);
112                }
113            }
114            Ok(hasher.finalize())
115        }
116
117        Ok(match self {
118            Self::Sha2_256 => {
119                let mut hasher = multihash_codetable::Sha2_256::default();
120                self.wrap(hash(&mut hasher, bytes)?)?
121            }
122            Self::Sha2_512 => {
123                let mut hasher = multihash_codetable::Sha2_512::default();
124                self.wrap(hash(&mut hasher, bytes)?)?
125            }
126            Self::Sha3_224 => {
127                let mut hasher = multihash_codetable::Sha3_224::default();
128                self.wrap(hash(&mut hasher, bytes)?)?
129            }
130            Self::Sha3_256 => {
131                let mut hasher = multihash_codetable::Sha3_256::default();
132                self.wrap(hash(&mut hasher, bytes)?)?
133            }
134            Self::Sha3_384 => {
135                let mut hasher = multihash_codetable::Sha3_384::default();
136                self.wrap(hash(&mut hasher, bytes)?)?
137            }
138            Self::Sha3_512 => {
139                let mut hasher = multihash_codetable::Sha3_512::default();
140                self.wrap(hash(&mut hasher, bytes)?)?
141            }
142            Self::Keccak224 => {
143                let mut hasher = multihash_codetable::Keccak224::default();
144                self.wrap(hash(&mut hasher, bytes)?)?
145            }
146            Self::Keccak256 => {
147                let mut hasher = multihash_codetable::Keccak256::default();
148                self.wrap(hash(&mut hasher, bytes)?)?
149            }
150            Self::Keccak384 => {
151                let mut hasher = multihash_codetable::Keccak384::default();
152                self.wrap(hash(&mut hasher, bytes)?)?
153            }
154            Self::Keccak512 => {
155                let mut hasher = multihash_codetable::Keccak512::default();
156                self.wrap(hash(&mut hasher, bytes)?)?
157            }
158            Self::Blake2b256 => {
159                let mut hasher = multihash_codetable::Blake2b256::default();
160                self.wrap(hash(&mut hasher, bytes)?)?
161            }
162            Self::Blake2b512 => {
163                let mut hasher = multihash_codetable::Blake2b512::default();
164                self.wrap(hash(&mut hasher, bytes)?)?
165            }
166            Self::Blake2s128 => {
167                let mut hasher = multihash_codetable::Blake2s128::default();
168                self.wrap(hash(&mut hasher, bytes)?)?
169            }
170            Self::Blake2s256 => {
171                let mut hasher = multihash_codetable::Blake2s256::default();
172                self.wrap(hash(&mut hasher, bytes)?)?
173            }
174            Self::Blake3_256 => {
175                let mut hasher = multihash_codetable::Blake3_256::default();
176                self.wrap(hash(&mut hasher, bytes)?)?
177            }
178            Self::Ripemd160 => {
179                let mut hasher = multihash_codetable::Ripemd160::default();
180                self.wrap(hash(&mut hasher, bytes)?)?
181            }
182            Self::Ripemd256 => {
183                let mut hasher = multihash_codetable::Ripemd256::default();
184                self.wrap(hash(&mut hasher, bytes)?)?
185            }
186            Self::Ripemd320 => {
187                let mut hasher = multihash_codetable::Ripemd320::default();
188                self.wrap(hash(&mut hasher, bytes)?)?
189            }
190            _ => {
191                anyhow::bail!("`digest_byte_stream` is unimplemented for {self:?}");
192            }
193        })
194    }
195}
196
197/// Identity hasher with a maximum size.
198///
199/// # Panics
200///
201/// Panics if the input is bigger than the maximum size.
202/// Ported from <https://github.com/multiformats/rust-multihash/pull/289>
203#[derive(Debug)]
204pub struct IdentityHasher<const S: usize> {
205    i: usize,
206    bytes: [u8; S],
207}
208
209impl<const S: usize> Default for IdentityHasher<S> {
210    fn default() -> Self {
211        Self {
212            i: 0,
213            bytes: [0u8; S],
214        }
215    }
216}
217
218impl<const S: usize> multihash_derive::Hasher for IdentityHasher<S> {
219    fn update(&mut self, input: &[u8]) {
220        let start = self.i.min(self.bytes.len());
221        let end = (self.i + input.len()).min(self.bytes.len());
222        self.bytes[start..end].copy_from_slice(input);
223        self.i = end;
224    }
225
226    fn finalize(&mut self) -> &[u8] {
227        &self.bytes[..self.i]
228    }
229
230    fn reset(&mut self) {
231        self.i = 0
232    }
233}
234
235#[cfg(test)]
236mod tests {
237    use std::io::Cursor;
238
239    use super::*;
240    use crate::utils::rand::forest_rng;
241    use quickcheck_macros::quickcheck;
242    use rand::RngCore as _;
243
244    #[quickcheck]
245    fn checked_digest_bounds(data: Vec<u8>) -> bool {
246        use MultihashCode::*;
247        let ok_sha = Sha2_256.checked_digest(&data).is_ok();
248        let ok_identity = Identity.checked_digest(&data).is_ok()
249            == (data.len() <= MultihashCode::IDENTITY_MAX_SIZE);
250        ok_sha && ok_identity
251    }
252
253    #[test]
254    fn test_digest_byte_stream() {
255        use MultihashCode::*;
256
257        for len in [0, 1, 100, 1024, 10000] {
258            let mut bytes = vec![0; len];
259            forest_rng().fill_bytes(&mut bytes);
260            let mut cursor = Cursor::new(bytes.clone());
261            for code in [
262                Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Keccak224, Keccak256,
263                Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3_256,
264                Ripemd160, Ripemd256, Ripemd320,
265            ] {
266                cursor.set_position(0);
267                let mh1 = code.digest(&bytes);
268                let mh2 = code.digest_byte_stream(&mut cursor).unwrap();
269                assert_eq!(mh1, mh2);
270            }
271
272            cursor.set_position(0);
273            Identity.digest_byte_stream(&mut cursor).unwrap_err();
274        }
275    }
276}