susy_multihash/
lib.rs

1//! # Multihash
2//!
3//! Implementation of [multihash](https://github.com/multiformats/multihash) in Rust.
4//!
5//! A `Multihash` is a structure that contains a hashing algorithm, plus some hashed data.
6//! A `MultihashRef` is the same as a `Multihash`, except that it doesn't own its data.
7
8mod errors;
9mod hashes;
10
11use sha2::Digest;
12use std::{convert::TryFrom, fmt::Write};
13use unsigned_varint::{decode, encode};
14
15pub use self::errors::{DecodeError, DecodeOwnedError, EncodeError};
16pub use self::hashes::Hash;
17
18/// Helper function for encoding input into output using given `Digest`
19fn digest_encode<D: Digest>(input: &[u8], output: &mut [u8]) {
20    output.copy_from_slice(&D::digest(input))
21}
22
23// And another one to keep the matching DRY
24macro_rules! match_encoder {
25    ($hash_id:ident for ($input:expr, $output:expr) {
26        $( $hashtype:ident => $hash_ty:path, )*
27    }) => ({
28        match $hash_id {
29            $(
30                Hash::$hashtype => digest_encode::<$hash_ty>($input, $output),
31            )*
32
33            _ => return Err(EncodeError::UnsupportedType)
34        }
35    })
36}
37
38/// Encodes data into a multihash.
39///
40/// # Errors
41///
42/// Will return an error if the specified hash type is not supported. See the docs for `Hash`
43/// to see what is supported.
44///
45/// # Examples
46///
47/// ```
48/// use susy_multihash::{encode, Hash};
49///
50/// assert_eq!(
51///     encode(Hash::SHA2256, b"hello world").unwrap().into_bytes(),
52///     vec![18, 32, 185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218, 125, 171, 250, 196,
53///     132, 239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233]
54/// );
55/// ```
56///
57pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
58    let mut buf = encode::u16_buffer();
59    let code = encode::u16(hash.code(), &mut buf);
60
61    let header_len = code.len() + 1;
62    let size = hash.size();
63
64    let mut output = Vec::new();
65    output.resize(header_len + size as usize, 0);
66    output[..code.len()].copy_from_slice(code);
67    output[code.len()] = size;
68
69    match_encoder!(hash for (input, &mut output[header_len..]) {
70        SHA1 => sha1::Sha1,
71        SHA2256 => sha2::Sha256,
72        SHA2512 => sha2::Sha512,
73        SHA3224 => sha3::Sha3_224,
74        SHA3256 => sha3::Sha3_256,
75        SHA3384 => sha3::Sha3_384,
76        SHA3512 => sha3::Sha3_512,
77        Keccak224 => sha3::Keccak224,
78        Keccak256 => sha3::Keccak256,
79        Keccak384 => sha3::Keccak384,
80        Keccak512 => sha3::Keccak512,
81        Blake2b512 => blake2::Blake2b,
82        Blake2s256 => blake2::Blake2s,
83    });
84
85    Ok(Multihash { bytes: output })
86}
87
88/// Represents a valid multihash.
89#[derive(Debug, Clone, PartialEq, Eq, Hash)]
90pub struct Multihash {
91    bytes: Vec<u8>,
92}
93
94impl Multihash {
95    /// Verifies whether `bytes` contains a valid multihash, and if so returns a `Multihash`.
96    #[inline]
97    pub fn from_bytes(bytes: Vec<u8>) -> Result<Multihash, DecodeOwnedError> {
98        if let Err(err) = MultihashRef::from_slice(&bytes) {
99            return Err(DecodeOwnedError {
100                error: err,
101                data: bytes,
102            });
103        }
104
105        Ok(Multihash { bytes })
106    }
107
108    /// Generates a random `Multihash` from a cryptographically secure PRNG.
109    pub fn random(hash: Hash) -> Multihash {
110        let mut buf = encode::u16_buffer();
111        let code = encode::u16(hash.code(), &mut buf);
112
113        let header_len = code.len() + 1;
114        let size = hash.size();
115
116        let mut output = Vec::new();
117        output.resize(header_len + size as usize, 0);
118        output[..code.len()].copy_from_slice(code);
119        output[code.len()] = size;
120
121        for b in output[header_len..].iter_mut() {
122            *b = rand::random();
123        }
124
125        Multihash {
126            bytes: output,
127        }
128    }
129
130    /// Returns the bytes representation of the multihash.
131    #[inline]
132    pub fn into_bytes(self) -> Vec<u8> {
133        self.bytes
134    }
135
136    /// Returns the bytes representation of this multihash.
137    #[inline]
138    pub fn as_bytes(&self) -> &[u8] {
139        &self.bytes
140    }
141
142    /// Builds a `MultihashRef` corresponding to this `Multihash`.
143    #[inline]
144    pub fn as_ref(&self) -> MultihashRef<'_> {
145        MultihashRef { bytes: &self.bytes }
146    }
147
148    /// Returns which hashing algorithm is used in this multihash.
149    #[inline]
150    pub fn algorithm(&self) -> Hash {
151        self.as_ref().algorithm()
152    }
153
154    /// Returns the hashed data.
155    #[inline]
156    pub fn digest(&self) -> &[u8] {
157        self.as_ref().digest()
158    }
159}
160
161impl<'a> PartialEq<MultihashRef<'a>> for Multihash {
162    #[inline]
163    fn eq(&self, other: &MultihashRef<'a>) -> bool {
164        &*self.bytes == other.bytes
165    }
166}
167
168impl TryFrom<Vec<u8>> for Multihash {
169    type Error = DecodeOwnedError;
170
171    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
172        Multihash::from_bytes(value)
173    }
174}
175
176/// Represents a valid multihash.
177#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
178pub struct MultihashRef<'a> {
179    bytes: &'a [u8],
180}
181
182impl<'a> MultihashRef<'a> {
183    /// Verifies whether `bytes` contains a valid multihash, and if so returns a `MultihashRef`.
184    pub fn from_slice(input: &'a [u8]) -> Result<MultihashRef<'a>, DecodeError> {
185        if input.is_empty() {
186            return Err(DecodeError::BadInputLength);
187        }
188
189        // NOTE: We choose u16 here because there is no hashing algorithm implemented in this crate
190        // whose length exceeds 2^16 - 1.
191        let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
192
193        let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;
194        let hash_len = alg.size() as usize;
195
196        // Length of input after hash code should be exactly hash_len + 1
197        if bytes.len() != hash_len + 1 {
198            return Err(DecodeError::BadInputLength);
199        }
200
201        if bytes[0] as usize != hash_len {
202            return Err(DecodeError::BadInputLength);
203        }
204
205        Ok(MultihashRef { bytes: input })
206    }
207
208    /// Returns which hashing algorithm is used in this multihash.
209    #[inline]
210    pub fn algorithm(&self) -> Hash {
211        let (code, _) = decode::u16(&self.bytes).expect("multihash is known to be valid algorithm");
212        Hash::from_code(code).expect("multihash is known to be valid")
213    }
214
215    /// Returns the hashed data.
216    #[inline]
217    pub fn digest(&self) -> &'a [u8] {
218        let (_, bytes) = decode::u16(&self.bytes).expect("multihash is known to be valid digest");
219        &bytes[1..]
220    }
221
222    /// Builds a `Multihash` that owns the data.
223    ///
224    /// This operation allocates.
225    #[inline]
226    pub fn into_owned(&self) -> Multihash {
227        Multihash {
228            bytes: self.bytes.to_owned(),
229        }
230    }
231
232    /// Returns the bytes representation of this multihash.
233    #[inline]
234    pub fn as_bytes(&self) -> &'a [u8] {
235        &self.bytes
236    }
237}
238
239impl<'a> PartialEq<Multihash> for MultihashRef<'a> {
240    #[inline]
241    fn eq(&self, other: &Multihash) -> bool {
242        self.bytes == &*other.bytes
243    }
244}
245
246/// Convert bytes to a hex representation
247pub fn to_hex(bytes: &[u8]) -> String {
248    let mut hex = String::with_capacity(bytes.len() * 2);
249
250    for byte in bytes {
251        write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
252    }
253
254    hex
255}
256
257#[cfg(test)]
258mod tests {
259    use crate::{Hash, Multihash};
260
261    #[test]
262    fn rand_generates_valid_multihash() {
263        // Iterate over every possible hash function.
264        for code in 0 .. u16::max_value() {
265            let hash_fn = match Hash::from_code(code) {
266                Some(c) => c,
267                None => continue,
268            };
269
270            for _ in 0 .. 2000 {
271                let hash = Multihash::random(hash_fn);
272                assert_eq!(hash, Multihash::from_bytes(hash.clone().into_bytes()).unwrap());
273            }
274        }
275    }
276}