mirror of
https://github.com/fluencelabs/rust-libp2p
synced 2025-06-24 07:11:38 +00:00
Use unsigned-varints, add BLAKE2 support in multihash (#525)
* Add support for unsigned varints * Depend on unsigned-varint 0.2 without default features * Change hash code type from u8 to u64 * Fix hash codes and enum variants for BLAKE2 to fit the standard (see #524) * Run cargo fmt on crate * Expand hash_types test to include all variants * Add support for BLAKE2b-512 and BLAKE2s-256 * Depend on blake2 crate 0.7 with no default features * Update encode! macro for support for blake2 crate * Update all tests to include BLAKE2b-512 and BLAKE2s-256 * Reduce hash code size from u64 to u16 * Fix typo in doc comment * Bump tiny-keccak to version 1.4 * Remove unnecessary default-features = false in Cargo.toml
This commit is contained in:
committed by
Toralf Wittner
parent
4fa680e282
commit
ad0807b3f3
@ -16,6 +16,8 @@ readme = "README.md"
|
||||
documentation = "https://docs.rs/multihash/"
|
||||
|
||||
[dependencies]
|
||||
blake2 = { version = "0.7", default-features = false }
|
||||
sha1 = "0.5"
|
||||
sha2 = { version = "0.7", default-features = false }
|
||||
tiny-keccak = "1.2"
|
||||
tiny-keccak = "1.4"
|
||||
unsigned-varint = "0.2"
|
||||
|
@ -1,4 +1,4 @@
|
||||
use std::{fmt, error};
|
||||
use std::{error, fmt};
|
||||
|
||||
/// Error that can happen when encoding some bytes into a multihash.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -16,8 +16,7 @@ impl fmt::Display for EncodeError {
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for EncodeError {
|
||||
}
|
||||
impl error::Error for EncodeError {}
|
||||
|
||||
/// Error that can happen when decoding some bytes.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
@ -32,14 +31,13 @@ impl fmt::Display for DecodeError {
|
||||
#[inline]
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match *self {
|
||||
DecodeError::BadInputLength => write!(f, "Not matching input length"),
|
||||
DecodeError::BadInputLength => write!(f, "Not matching input length"),
|
||||
DecodeError::UnknownCode => write!(f, "Found unknown code"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for DecodeError {
|
||||
}
|
||||
impl error::Error for DecodeError {}
|
||||
|
||||
/// Error that can happen when decoding some bytes.
|
||||
///
|
||||
@ -59,5 +57,4 @@ impl fmt::Display for DecodeOwnedError {
|
||||
}
|
||||
}
|
||||
|
||||
impl error::Error for DecodeOwnedError {
|
||||
}
|
||||
impl error::Error for DecodeOwnedError {}
|
||||
|
@ -1,4 +1,3 @@
|
||||
|
||||
/// List of types currently supported in the multihash spec.
|
||||
///
|
||||
/// Not all hash types are supported by this library.
|
||||
@ -26,15 +25,19 @@ pub enum Hash {
|
||||
Keccak384,
|
||||
/// Keccak-512 (64-byte hash size)
|
||||
Keccak512,
|
||||
/// BLAKE2b-512 (64-byte hash size)
|
||||
Blake2b512,
|
||||
/// Encoding unsupported
|
||||
Blake2b,
|
||||
Blake2b256,
|
||||
/// BLAKE2s-256 (32-byte hash size)
|
||||
Blake2s256,
|
||||
/// Encoding unsupported
|
||||
Blake2s,
|
||||
Blake2s128,
|
||||
}
|
||||
|
||||
impl Hash {
|
||||
/// Get the corresponding hash code.
|
||||
pub fn code(&self) -> u8 {
|
||||
pub fn code(&self) -> u16 {
|
||||
match *self {
|
||||
Hash::SHA1 => 0x11,
|
||||
Hash::SHA2256 => 0x12,
|
||||
@ -47,8 +50,10 @@ impl Hash {
|
||||
Hash::Keccak256 => 0x1B,
|
||||
Hash::Keccak384 => 0x1C,
|
||||
Hash::Keccak512 => 0x1D,
|
||||
Hash::Blake2b => 0x40,
|
||||
Hash::Blake2s => 0x41,
|
||||
Hash::Blake2b512 => 0xB240,
|
||||
Hash::Blake2b256 => 0xB220,
|
||||
Hash::Blake2s256 => 0xB260,
|
||||
Hash::Blake2s128 => 0xB250,
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,13 +71,15 @@ impl Hash {
|
||||
Hash::Keccak256 => 32,
|
||||
Hash::Keccak384 => 48,
|
||||
Hash::Keccak512 => 64,
|
||||
Hash::Blake2b => 64,
|
||||
Hash::Blake2s => 32,
|
||||
Hash::Blake2b512 => 64,
|
||||
Hash::Blake2b256 => 32,
|
||||
Hash::Blake2s256 => 32,
|
||||
Hash::Blake2s128 => 16,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the algorithm corresponding to a code, or `None` if no algorith is matching.
|
||||
pub fn from_code(code: u8) -> Option<Hash> {
|
||||
/// Returns the algorithm corresponding to a code, or `None` if no algorithm is matching.
|
||||
pub fn from_code(code: u16) -> Option<Hash> {
|
||||
Some(match code {
|
||||
0x11 => Hash::SHA1,
|
||||
0x12 => Hash::SHA2256,
|
||||
@ -85,8 +92,10 @@ impl Hash {
|
||||
0x1B => Hash::Keccak256,
|
||||
0x1C => Hash::Keccak384,
|
||||
0x1D => Hash::Keccak512,
|
||||
0x40 => Hash::Blake2b,
|
||||
0x41 => Hash::Blake2s,
|
||||
0xB240 => Hash::Blake2b512,
|
||||
0xB220 => Hash::Blake2b256,
|
||||
0xB260 => Hash::Blake2s256,
|
||||
0xB250 => Hash::Blake2s128,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
@ -4,39 +4,47 @@
|
||||
//!
|
||||
//! 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.
|
||||
//!
|
||||
|
||||
extern crate blake2;
|
||||
extern crate sha1;
|
||||
extern crate sha2;
|
||||
extern crate tiny_keccak;
|
||||
extern crate unsigned_varint;
|
||||
|
||||
mod errors;
|
||||
mod hashes;
|
||||
|
||||
use std::fmt::Write;
|
||||
|
||||
use sha2::Digest;
|
||||
use tiny_keccak::Keccak;
|
||||
use unsigned_varint::{decode, encode};
|
||||
|
||||
pub use errors::{DecodeError, DecodeOwnedError, EncodeError};
|
||||
pub use hashes::Hash;
|
||||
pub use errors::{EncodeError, DecodeError, DecodeOwnedError};
|
||||
|
||||
// Helper macro for encoding input into output using sha1, sha2 or tiny_keccak
|
||||
// Helper macro for encoding input into output using sha1, sha2, tiny_keccak, or blake2
|
||||
macro_rules! encode {
|
||||
(sha1, Sha1, $input:expr, $output:expr) => ({
|
||||
(sha1, Sha1, $input:expr, $output:expr) => {{
|
||||
let mut hasher = sha1::Sha1::new();
|
||||
hasher.update($input);
|
||||
$output.copy_from_slice(&hasher.digest().bytes());
|
||||
});
|
||||
(sha2, $algorithm:ident, $input:expr, $output:expr) => ({
|
||||
}};
|
||||
(sha2, $algorithm:ident, $input:expr, $output:expr) => {{
|
||||
let mut hasher = sha2::$algorithm::default();
|
||||
hasher.input($input);
|
||||
$output.copy_from_slice(hasher.result().as_ref());
|
||||
});
|
||||
(tiny, $constructor:ident, $input:expr, $output:expr) => ({
|
||||
}};
|
||||
(tiny, $constructor:ident, $input:expr, $output:expr) => {{
|
||||
let mut kec = Keccak::$constructor();
|
||||
kec.update($input);
|
||||
kec.finalize($output);
|
||||
});
|
||||
}};
|
||||
(blake2, $algorithm:ident, $input:expr, $output:expr) => {{
|
||||
let mut hasher = blake2::$algorithm::default();
|
||||
hasher.input($input);
|
||||
$output.copy_from_slice(hasher.result().as_ref());
|
||||
}};
|
||||
}
|
||||
|
||||
// And another one to keep the matching DRY
|
||||
@ -74,13 +82,18 @@ macro_rules! match_encoder {
|
||||
/// ```
|
||||
///
|
||||
pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
||||
let size = hash.size();
|
||||
let mut output = Vec::new();
|
||||
output.resize(2 + size as usize, 0);
|
||||
output[0] = hash.code();
|
||||
output[1] = size;
|
||||
let mut buf = encode::u16_buffer();
|
||||
let code = encode::u16(hash.code(), &mut buf);
|
||||
|
||||
match_encoder!(hash for (input, &mut output[2..]) {
|
||||
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;
|
||||
|
||||
match_encoder!(hash for (input, &mut output[header_len..]) {
|
||||
SHA1 => sha1::Sha1,
|
||||
SHA2256 => sha2::Sha256,
|
||||
SHA2512 => sha2::Sha512,
|
||||
@ -92,6 +105,8 @@ pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
||||
Keccak256 => tiny::new_keccak256,
|
||||
Keccak384 => tiny::new_keccak384,
|
||||
Keccak512 => tiny::new_keccak512,
|
||||
Blake2b512 => blake2::Blake2b,
|
||||
Blake2s256 => blake2::Blake2s,
|
||||
});
|
||||
|
||||
Ok(Multihash { bytes: output })
|
||||
@ -100,7 +115,7 @@ pub fn encode(hash: Hash, input: &[u8]) -> Result<Multihash, EncodeError> {
|
||||
/// Represents a valid multihash.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct Multihash {
|
||||
bytes: Vec<u8>
|
||||
bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
impl Multihash {
|
||||
@ -158,7 +173,7 @@ impl<'a> PartialEq<MultihashRef<'a>> for Multihash {
|
||||
/// Represents a valid multihash.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct MultihashRef<'a> {
|
||||
bytes: &'a [u8]
|
||||
bytes: &'a [u8],
|
||||
}
|
||||
|
||||
impl<'a> MultihashRef<'a> {
|
||||
@ -168,25 +183,19 @@ impl<'a> MultihashRef<'a> {
|
||||
return Err(DecodeError::BadInputLength);
|
||||
}
|
||||
|
||||
// TODO: note that `input[0]` and `input[1]` and technically variable-length integers,
|
||||
// but there's no hashing algorithm implemented in this crate whose code or digest length
|
||||
// is superior to 128
|
||||
let code = input[0];
|
||||
|
||||
// TODO: see comment just above about varints
|
||||
if input[0] >= 128 || input[1] >= 128 {
|
||||
return Err(DecodeError::BadInputLength);
|
||||
}
|
||||
// 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)?;
|
||||
|
||||
let alg = Hash::from_code(code).ok_or(DecodeError::UnknownCode)?;
|
||||
let hash_len = alg.size() as usize;
|
||||
|
||||
// length of input should be exactly hash_len + 2
|
||||
if input.len() != hash_len + 2 {
|
||||
// Length of input after hash code should be exactly hash_len + 1
|
||||
if bytes.len() != hash_len + 1 {
|
||||
return Err(DecodeError::BadInputLength);
|
||||
}
|
||||
|
||||
if input[1] as usize != hash_len {
|
||||
if bytes[0] as usize != hash_len {
|
||||
return Err(DecodeError::BadInputLength);
|
||||
}
|
||||
|
||||
@ -196,13 +205,15 @@ impl<'a> MultihashRef<'a> {
|
||||
/// Returns which hashing algorithm is used in this multihash.
|
||||
#[inline]
|
||||
pub fn algorithm(&self) -> Hash {
|
||||
Hash::from_code(self.bytes[0]).expect("multihash is known to be valid")
|
||||
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")
|
||||
}
|
||||
|
||||
/// Returns the hashed data.
|
||||
#[inline]
|
||||
pub fn digest(&self) -> &'a [u8] {
|
||||
&self.bytes[2..]
|
||||
let (_, bytes) = decode::u16(&self.bytes).expect("multihash is known to be valid digest");
|
||||
&bytes[1..]
|
||||
}
|
||||
|
||||
/// Builds a `Multihash` that owns the data.
|
||||
@ -210,7 +221,9 @@ impl<'a> MultihashRef<'a> {
|
||||
/// This operation allocates.
|
||||
#[inline]
|
||||
pub fn into_owned(&self) -> Multihash {
|
||||
Multihash { bytes: self.bytes.to_owned() }
|
||||
Multihash {
|
||||
bytes: self.bytes.to_owned(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the bytes representation of this multihash.
|
||||
|
@ -7,11 +7,10 @@ fn hex_to_bytes(s: &str) -> Vec<u8> {
|
||||
let mut c = 0;
|
||||
let mut v = Vec::new();
|
||||
while c < s.len() {
|
||||
v.push(u8::from_str_radix(&s[c..c+2], 16).unwrap());
|
||||
v.push(u8::from_str_radix(&s[c..c + 2], 16).unwrap());
|
||||
c += 2;
|
||||
}
|
||||
v
|
||||
|
||||
}
|
||||
|
||||
macro_rules! assert_encode {
|
||||
@ -41,6 +40,8 @@ fn multihash_encode() {
|
||||
Keccak256, b"hello world", "1B2047173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad";
|
||||
Keccak384, b"hello world", "1C3065fc99339a2a40e99d3c40d695b22f278853ca0f925cde4254bcae5e22ece47e6441f91b6568425adc9d95b0072eb49f";
|
||||
Keccak512, b"hello world", "1D403ee2b40047b8060f68c67242175660f4174d0af5c01d47168ec20ed619b0b7c42181f40aa1046f39e2ef9efc6910782a998e0013d172458957957fac9405b67d";
|
||||
Blake2b512, b"hello world", "c0e40240021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0";
|
||||
Blake2s256, b"hello world", "e0e402209aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b";
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,6 +73,8 @@ fn assert_decode() {
|
||||
Keccak256, "1B2047173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad";
|
||||
Keccak384, "1C3065fc99339a2a40e99d3c40d695b22f278853ca0f925cde4254bcae5e22ece47e6441f91b6568425adc9d95b0072eb49f";
|
||||
Keccak512, "1D403ee2b40047b8060f68c67242175660f4174d0af5c01d47168ec20ed619b0b7c42181f40aa1046f39e2ef9efc6910782a998e0013d172458957957fac9405b67d";
|
||||
Blake2b512, "c0e40240021ced8799296ceca557832ab941a50b4a11f83478cf141f51f933f653ab9fbcc05a037cddbed06e309bf334942c4e58cdf1a46e237911ccd7fcf9787cbc7fd0";
|
||||
Blake2s256, "e0e402209aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b";
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,12 +95,26 @@ macro_rules! assert_roundtrip {
|
||||
#[test]
|
||||
fn assert_roundtrip() {
|
||||
assert_roundtrip!(
|
||||
SHA1, SHA2256, SHA2512, SHA3224, SHA3256, SHA3384, SHA3512,
|
||||
Keccak224, Keccak256, Keccak384, Keccak512
|
||||
SHA1, SHA2256, SHA2512, SHA3224, SHA3256, SHA3384, SHA3512, Keccak224, Keccak256,
|
||||
Keccak384, Keccak512, Blake2b512, Blake2s256
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hash_types() {
|
||||
assert_eq!(Hash::SHA1.size(), 20);
|
||||
assert_eq!(Hash::SHA2256.size(), 32);
|
||||
assert_eq!(Hash::SHA2512.size(), 64);
|
||||
assert_eq!(Hash::SHA3224.size(), 28);
|
||||
assert_eq!(Hash::SHA3256.size(), 32);
|
||||
assert_eq!(Hash::SHA3384.size(), 48);
|
||||
assert_eq!(Hash::SHA3512.size(), 64);
|
||||
assert_eq!(Hash::Keccak224.size(), 28);
|
||||
assert_eq!(Hash::Keccak256.size(), 32);
|
||||
assert_eq!(Hash::Keccak384.size(), 48);
|
||||
assert_eq!(Hash::Keccak512.size(), 64);
|
||||
assert_eq!(Hash::Blake2b512.size(), 64);
|
||||
assert_eq!(Hash::Blake2b256.size(), 32);
|
||||
assert_eq!(Hash::Blake2s256.size(), 32);
|
||||
assert_eq!(Hash::Blake2s128.size(), 16);
|
||||
}
|
||||
|
Reference in New Issue
Block a user