2018-08-09 14:51:09 +02:00
|
|
|
//! # Multihash
|
|
|
|
//!
|
|
|
|
//! Implementation of [multihash](https://github.com/multiformats/multihash) in Rust.
|
|
|
|
//!
|
|
|
|
//! A `Multihash` is a structure that contains a hashing algorithm, plus some hashed data.
|
|
|
|
//! A `MultihashRef` is the same as a `Multihash`, except that it doesn't own its data.
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
mod errors;
|
|
|
|
mod hashes;
|
|
|
|
|
2018-06-01 10:10:00 +02:00
|
|
|
use sha2::Digest;
|
2019-04-17 14:16:50 +02:00
|
|
|
use std::{convert::TryFrom, fmt::Write};
|
2018-10-01 15:58:11 +08:00
|
|
|
use unsigned_varint::{decode, encode};
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2019-01-29 10:02:29 +00:00
|
|
|
pub use self::errors::{DecodeError, DecodeOwnedError, EncodeError};
|
|
|
|
pub use self::hashes::Hash;
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2019-03-20 18:19:50 +00:00
|
|
|
/// Helper function for encoding input into output using given `Digest`
|
|
|
|
fn digest_encode<D: Digest>(input: &[u8], output: &mut [u8]) {
|
|
|
|
output.copy_from_slice(&D::digest(input))
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// And another one to keep the matching DRY
|
|
|
|
macro_rules! match_encoder {
|
2019-03-20 18:19:50 +00:00
|
|
|
($hash_id:ident for ($input:expr, $output:expr) {
|
|
|
|
$( $hashtype:ident => $hash_ty:path, )*
|
2018-06-01 10:10:00 +02:00
|
|
|
}) => ({
|
2019-03-20 18:19:50 +00:00
|
|
|
match $hash_id {
|
2018-06-01 10:10:00 +02:00
|
|
|
$(
|
2019-03-20 18:19:50 +00:00
|
|
|
Hash::$hashtype => digest_encode::<$hash_ty>($input, $output),
|
2018-06-01 10:10:00 +02:00
|
|
|
)*
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
_ => return Err(EncodeError::UnsupportedType)
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Encodes data into a multihash.
|
|
|
|
///
|
|
|
|
/// # Errors
|
|
|
|
///
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Will return an error if the specified hash type is not supported. See the docs for `Hash`
|
2018-06-01 10:10:00 +02:00
|
|
|
/// to see what is supported.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
///
|
|
|
|
/// ```
|
2018-12-07 15:40:02 +01:00
|
|
|
/// use parity_multihash::{encode, Hash};
|
2018-06-01 10:10:00 +02:00
|
|
|
///
|
|
|
|
/// assert_eq!(
|
2018-08-09 14:51:09 +02:00
|
|
|
/// encode(Hash::SHA2256, b"hello world").unwrap().into_bytes(),
|
2018-06-01 10:10:00 +02:00
|
|
|
/// vec![18, 32, 185, 77, 39, 185, 147, 77, 62, 8, 165, 46, 82, 215, 218, 125, 171, 250, 196,
|
|
|
|
/// 132, 239, 227, 122, 83, 128, 238, 144, 136, 247, 172, 226, 239, 205, 233]
|
|
|
|
/// );
|
|
|
|
/// ```
|
|
|
|
///
|
2018-08-09 14:51:09 +02:00
|
|
|
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
2018-10-01 15:58:11 +08:00
|
|
|
let mut buf = encode::u16_buffer();
|
|
|
|
let code = encode::u16(hash.code(), &mut buf);
|
|
|
|
|
|
|
|
let header_len = code.len() + 1;
|
2018-06-01 10:10:00 +02:00
|
|
|
let size = hash.size();
|
2018-10-01 15:58:11 +08:00
|
|
|
|
2018-06-01 10:10:00 +02:00
|
|
|
let mut output = Vec::new();
|
2018-10-01 15:58:11 +08:00
|
|
|
output.resize(header_len + size as usize, 0);
|
|
|
|
output[..code.len()].copy_from_slice(code);
|
|
|
|
output[code.len()] = size;
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2018-10-01 15:58:11 +08:00
|
|
|
match_encoder!(hash for (input, &mut output[header_len..]) {
|
2018-06-01 10:10:00 +02:00
|
|
|
SHA1 => sha1::Sha1,
|
|
|
|
SHA2256 => sha2::Sha256,
|
|
|
|
SHA2512 => sha2::Sha512,
|
2019-03-20 18:19:50 +00:00
|
|
|
SHA3224 => sha3::Sha3_224,
|
|
|
|
SHA3256 => sha3::Sha3_256,
|
|
|
|
SHA3384 => sha3::Sha3_384,
|
|
|
|
SHA3512 => sha3::Sha3_512,
|
|
|
|
Keccak224 => sha3::Keccak224,
|
|
|
|
Keccak256 => sha3::Keccak256,
|
|
|
|
Keccak384 => sha3::Keccak384,
|
|
|
|
Keccak512 => sha3::Keccak512,
|
2018-10-01 15:58:11 +08:00
|
|
|
Blake2b512 => blake2::Blake2b,
|
|
|
|
Blake2s256 => blake2::Blake2s,
|
2018-06-01 10:10:00 +02:00
|
|
|
});
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
Ok(Multihash { bytes: output })
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Represents a valid multihash.
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct Multihash {
|
2018-10-01 15:58:11 +08:00
|
|
|
bytes: Vec<u8>,
|
2018-08-09 14:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Multihash {
|
|
|
|
/// Verifies whether `bytes` contains a valid multihash, and if so returns a `Multihash`.
|
|
|
|
#[inline]
|
|
|
|
pub fn from_bytes(bytes: Vec<u8>) -> Result<Multihash, DecodeOwnedError> {
|
|
|
|
if let Err(err) = MultihashRef::from_slice(&bytes) {
|
|
|
|
return Err(DecodeOwnedError {
|
|
|
|
error: err,
|
|
|
|
data: bytes,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(Multihash { bytes })
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
2018-11-20 13:44:36 +01:00
|
|
|
/// Generates a random `Multihash` from a cryptographically secure PRNG.
|
|
|
|
pub fn random(hash: Hash) -> Multihash {
|
|
|
|
let mut buf = encode::u16_buffer();
|
|
|
|
let code = encode::u16(hash.code(), &mut buf);
|
|
|
|
|
|
|
|
let header_len = code.len() + 1;
|
|
|
|
let size = hash.size();
|
|
|
|
|
|
|
|
let mut output = Vec::new();
|
|
|
|
output.resize(header_len + size as usize, 0);
|
|
|
|
output[..code.len()].copy_from_slice(code);
|
|
|
|
output[code.len()] = size;
|
|
|
|
|
|
|
|
for b in output[header_len..].iter_mut() {
|
|
|
|
*b = rand::random();
|
|
|
|
}
|
|
|
|
|
|
|
|
Multihash {
|
|
|
|
bytes: output,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Returns the bytes representation of the multihash.
|
|
|
|
#[inline]
|
|
|
|
pub fn into_bytes(self) -> Vec<u8> {
|
|
|
|
self.bytes
|
|
|
|
}
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Returns the bytes representation of this multihash.
|
|
|
|
#[inline]
|
|
|
|
pub fn as_bytes(&self) -> &[u8] {
|
|
|
|
&self.bytes
|
|
|
|
}
|
2018-06-01 10:10:00 +02:00
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Builds a `MultihashRef` corresponding to this `Multihash`.
|
|
|
|
#[inline]
|
2019-02-11 14:58:15 +01:00
|
|
|
pub fn as_ref(&self) -> MultihashRef<'_> {
|
2018-08-09 14:51:09 +02:00
|
|
|
MultihashRef { bytes: &self.bytes }
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Returns which hashing algorithm is used in this multihash.
|
|
|
|
#[inline]
|
|
|
|
pub fn algorithm(&self) -> Hash {
|
|
|
|
self.as_ref().algorithm()
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the hashed data.
|
|
|
|
#[inline]
|
|
|
|
pub fn digest(&self) -> &[u8] {
|
|
|
|
self.as_ref().digest()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PartialEq<MultihashRef<'a>> for Multihash {
|
|
|
|
#[inline]
|
|
|
|
fn eq(&self, other: &MultihashRef<'a>) -> bool {
|
|
|
|
&*self.bytes == other.bytes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:16:50 +02:00
|
|
|
impl TryFrom<Vec<u8>> for Multihash {
|
|
|
|
type Error = DecodeOwnedError;
|
|
|
|
|
|
|
|
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
|
|
|
Multihash::from_bytes(value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
/// Represents a valid multihash.
|
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
|
|
|
pub struct MultihashRef<'a> {
|
2018-10-01 15:58:11 +08:00
|
|
|
bytes: &'a [u8],
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
2018-08-09 14:51:09 +02:00
|
|
|
impl<'a> MultihashRef<'a> {
|
|
|
|
/// Verifies whether `bytes` contains a valid multihash, and if so returns a `MultihashRef`.
|
|
|
|
pub fn from_slice(input: &'a [u8]) -> Result<MultihashRef<'a>, DecodeError> {
|
|
|
|
if input.is_empty() {
|
|
|
|
return Err(DecodeError::BadInputLength);
|
|
|
|
}
|
|
|
|
|
2018-10-01 15:58:11 +08:00
|
|
|
// NOTE: We choose u16 here because there is no hashing algorithm implemented in this crate
|
|
|
|
// whose length exceeds 2^16 - 1.
|
|
|
|
let (code, bytes) = decode::u16(&input).map_err(|_| DecodeError::BadInputLength)?;
|
2018-08-09 14:51:09 +02:00
|
|
|
|
|
|
|
let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;
|
|
|
|
let hash_len = alg.size() as usize;
|
|
|
|
|
2018-10-01 15:58:11 +08:00
|
|
|
// Length of input after hash code should be exactly hash_len + 1
|
|
|
|
if bytes.len() != hash_len + 1 {
|
2018-08-09 14:51:09 +02:00
|
|
|
return Err(DecodeError::BadInputLength);
|
|
|
|
}
|
|
|
|
|
2018-10-01 15:58:11 +08:00
|
|
|
if bytes[0] as usize != hash_len {
|
2018-08-09 14:51:09 +02:00
|
|
|
return Err(DecodeError::BadInputLength);
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(MultihashRef { bytes: input })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns which hashing algorithm is used in this multihash.
|
|
|
|
#[inline]
|
|
|
|
pub fn algorithm(&self) -> Hash {
|
2018-10-01 15:58:11 +08:00
|
|
|
let (code, _) = decode::u16(&self.bytes).expect("multihash is known to be valid algorithm");
|
|
|
|
Hash::from_code(code).expect("multihash is known to be valid")
|
2018-08-09 14:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the hashed data.
|
|
|
|
#[inline]
|
|
|
|
pub fn digest(&self) -> &'a [u8] {
|
2018-10-01 15:58:11 +08:00
|
|
|
let (_, bytes) = decode::u16(&self.bytes).expect("multihash is known to be valid digest");
|
|
|
|
&bytes[1..]
|
2018-08-09 14:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Builds a `Multihash` that owns the data.
|
|
|
|
///
|
|
|
|
/// This operation allocates.
|
|
|
|
#[inline]
|
|
|
|
pub fn into_owned(&self) -> Multihash {
|
2018-10-01 15:58:11 +08:00
|
|
|
Multihash {
|
|
|
|
bytes: self.bytes.to_owned(),
|
|
|
|
}
|
2018-08-09 14:51:09 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the bytes representation of this multihash.
|
|
|
|
#[inline]
|
|
|
|
pub fn as_bytes(&self) -> &'a [u8] {
|
|
|
|
&self.bytes
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> PartialEq<Multihash> for MultihashRef<'a> {
|
|
|
|
#[inline]
|
|
|
|
fn eq(&self, other: &Multihash) -> bool {
|
|
|
|
self.bytes == &*other.bytes
|
|
|
|
}
|
2018-06-01 10:10:00 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Convert bytes to a hex representation
|
|
|
|
pub fn to_hex(bytes: &[u8]) -> String {
|
|
|
|
let mut hex = String::with_capacity(bytes.len() * 2);
|
|
|
|
|
|
|
|
for byte in bytes {
|
|
|
|
write!(hex, "{:02x}", byte).expect("Can't fail on writing to string");
|
|
|
|
}
|
|
|
|
|
|
|
|
hex
|
|
|
|
}
|
2018-11-20 13:44:36 +01:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2019-01-29 10:02:29 +00:00
|
|
|
use crate::{Hash, Multihash};
|
2018-11-20 13:44:36 +01:00
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn rand_generates_valid_multihash() {
|
|
|
|
// Iterate over every possible hash function.
|
|
|
|
for code in 0 .. u16::max_value() {
|
|
|
|
let hash_fn = match Hash::from_code(code) {
|
|
|
|
Some(c) => c,
|
|
|
|
None => continue,
|
|
|
|
};
|
|
|
|
|
|
|
|
for _ in 0 .. 2000 {
|
|
|
|
let hash = Multihash::random(hash_fn);
|
|
|
|
assert_eq!(hash, Multihash::from_bytes(hash.clone().into_bytes()).unwrap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|