Skip to content

Commit

Permalink
feat: add Blake3 support (#74)
Browse files Browse the repository at this point in the history
Implements trivial support for blake3 using the default 32 byte digest.
  • Loading branch information
mriise authored Jul 24, 2020
1 parent e916ea0 commit dc0e0f9
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 21 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ edition = "2018"
[dependencies]
blake2b_simd = { version = "0.5.9", default-features = false }
blake2s_simd = { version = "0.5.9", default-features = false }
blake3 = "0.3.5"
digest = { version = "0.8", default-features = false }
sha-1 = { version = "0.8", default-features = false }
sha2 = { version = "0.8", default-features = false }
Expand All @@ -23,6 +24,7 @@ rand = { version = "0.7.3", optional = true }

[dev-dependencies]
criterion = "0.3"
hex = "0.4.2"
quickcheck = "0.9.2"
rand = "0.7.3"

Expand Down
7 changes: 5 additions & 2 deletions benches/multihash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@ use criterion::{black_box, criterion_group, criterion_main, Criterion};
use rand::Rng;

use multihash::{
Blake2b256, Blake2b512, Blake2s128, Blake2s256, Identity, Keccak224, Keccak256, Keccak384,
Keccak512, MultihashDigest, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384, Sha3_512,
Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3, Identity, Keccak224, Keccak256,
Keccak384, Keccak512, MultihashDigest, Sha1, Sha2_256, Sha2_512, Sha3_224, Sha3_256, Sha3_384,
Sha3_512,
};

macro_rules! group_digest {
Expand Down Expand Up @@ -61,6 +62,7 @@ fn bench_digest(c: &mut Criterion) {
"blake2b_512" => Blake2b512, &data
"blake2s_128" => Blake2s128, &data
"blake2s_256" => Blake2s256, &data
"blake3" => Blake3, &data
);
}

Expand All @@ -85,6 +87,7 @@ fn bench_stream(c: &mut Criterion) {
"blake2b_512" => Blake2b512, &data
"blake2s_128" => Blake2s128, &data
"blake2s_256" => Blake2s256, &data
"blake3" => Blake3, &data
);
}

Expand Down
4 changes: 2 additions & 2 deletions src/arb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ use rand::seq::SliceRandom;

use crate::{Code, Code::*, Multihash, MultihashDigest};

const HASHES: [Code; 16] = [
const HASHES: [Code; 17] = [
Identity, Sha1, Sha2_256, Sha2_512, Sha3_512, Sha3_384, Sha3_256, Sha3_224, Keccak224,
Keccak256, Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256,
Keccak256, Keccak384, Keccak512, Blake2b256, Blake2b512, Blake2s128, Blake2s256, Blake3,
];

/// Generates a random hash algorithm.
Expand Down
81 changes: 81 additions & 0 deletions src/hashes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,80 @@ macro_rules! derive_digest {
}
)*
};
($(
#[$doc:meta]
@blake3 $type:ty as $name:ident;
@code_doc $code_doc:literal,
)*) => {
$(
#[$doc]
#[derive(Clone, Debug)]
pub struct $name($type);
impl $name {
#[doc = $code_doc]
pub const CODE: Code = Code::$name;
/// Hash some input and return the Multihash digest.
pub fn digest(data: &[u8]) -> Multihash {
let digest = blake3::hash(data);
wrap(Self::CODE, digest.as_bytes())
}
}
impl Multihasher<Code> for $name {
const CODE: Code = Code::$name;
#[inline]
fn digest(data: &[u8]) -> Multihash {
Self::digest(data)
}
}
impl Default for $name {
fn default() -> Self {
$name(blake3::Hasher::new())
}
}
impl MultihashDigest<Code> for $name {
#[inline]
fn code(&self) -> Code {
Self::CODE
}
#[inline]
fn digest(&self, data: &[u8]) -> Multihash {
Self::digest(data)
}
#[inline]
fn input(&mut self, data: &[u8]) {
self.0.update(data);
}
#[inline]
fn result(self) -> Multihash {
let digest = self.0.finalize();
wrap(Self::CODE, digest.as_bytes())
}
#[inline]
fn result_reset(&mut self) -> Multihash {
let digest = self.0.finalize();
let hash = wrap(Self::CODE, digest.as_bytes());
self.0.reset();
hash
}
#[inline]
fn reset(&mut self) {
self.0.reset();
}
}
impl ::std::io::Write for $name {
#[inline]
fn write(&mut self, buf: &[u8]) -> ::std::io::Result<usize> {
<$name as MultihashDigest<Code>>::input(self, buf);
Ok(buf.len())
}
#[inline]
fn flush(&mut self) -> ::std::io::Result<()> {
self.0.finalize();
Ok(())
}
}
)*
}
}

impl_code! {
Expand Down Expand Up @@ -240,6 +314,8 @@ impl_code! {
Blake2s128 => 0xb250,
/// BLAKE2s-256 (32-byte hash size)
Blake2s256 => 0xb260,
/// BLAKE3 (32-byte hash size)
Blake3 => 0x1e,
}

/// The Identity hasher.
Expand Down Expand Up @@ -344,3 +420,8 @@ derive_digest! {
@blake Blake2s | Blake2sParams as Blake2s256 32;
@code_doc "The code of the Blake2-256 hasher, 0xb260.",
}
derive_digest! {
/// The Blake3 hasher.
@blake3 blake3::Hasher as Blake3;
@code_doc "The code of the Blake3 hasher, 0x1e.",
}
30 changes: 13 additions & 17 deletions tests/lib.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,20 @@
use multihash::*;

/// Helper function to convert a hex-encoded byte array back into a bytearray
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());
c += 2;
}
v
}

macro_rules! assert_encode {
{$( $alg:ty, $data:expr, $expect:expr; )*} => {
$(
let hex = hex_to_bytes($expect);
let bytes = hex::decode($expect).unwrap();
assert_eq!(
<$alg>::digest($data).into_bytes(),
hex,
bytes,
"{:?} encodes correctly", stringify!($alg)
);

let mut hasher = <$alg>::default();
&mut hasher.input($data);
assert_eq!(
hasher.result().into_bytes(),
hex,
bytes,
"{:?} encodes correctly", stringify!($alg)
);
)*
Expand Down Expand Up @@ -55,13 +44,14 @@ fn multihash_encode() {
Blake2s256, b"hello world", "e0e402209aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b";
Blake2b256, b"hello world", "a0e40220256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610";
Blake2s128, b"hello world", "d0e4021037deae0226c30da2ab424a7b8ee14e83";
Blake3, b"hello world", "1e20d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
}
}

macro_rules! assert_decode {
{$( $alg:ty, $hash:expr; )*} => {
$(
let hash = hex_to_bytes($hash);
let hash = hex::decode($hash).unwrap();
assert_eq!(
MultihashRef::from_slice(&hash).unwrap().algorithm(),
<$alg>::CODE,
Expand Down Expand Up @@ -91,6 +81,7 @@ fn assert_decode() {
Blake2s256, "e0e402209aec6806794561107e594b1f6a8a6b0c92a0cba9acf5e5e93cca06f781813b0b";
Blake2b256, "a0e40220256c83b297114d201b30179f3f0ef0cace9783622da5974326b436178aeef610";
Blake2s128, "d0e4021037deae0226c30da2ab424a7b8ee14e83";
Blake3, "1e20d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24";
}
}

Expand Down Expand Up @@ -128,15 +119,15 @@ fn assert_roundtrip() {

/// Testing the public interface of `Multihash` and `MultihashRef`
fn test_methods(hash: impl MultihashDigest<Code>, prefix: &str, digest: &str) {
let expected_bytes = hex_to_bytes(&format!("{}{}", prefix, digest));
let expected_bytes = hex::decode(&format!("{}{}", prefix, digest)).unwrap();
let multihash = hash.digest(b"hello world");
assert_eq!(
Multihash::from_bytes(expected_bytes.clone()).unwrap(),
multihash
);
assert_eq!(multihash.as_bytes(), &expected_bytes[..]);
assert_eq!(multihash.algorithm(), hash.code());
assert_eq!(multihash.digest(), &hex_to_bytes(digest)[..]);
assert_eq!(multihash.digest(), hex::decode(digest).unwrap().as_slice());

let multihash_ref = multihash.as_ref();
assert_eq!(multihash, multihash_ref);
Expand Down Expand Up @@ -226,6 +217,11 @@ fn multihash_methods() {
"d0e40210",
"37deae0226c30da2ab424a7b8ee14e83",
);
test_methods(
Blake3::default(),
"1e20",
"d74981efa70a0c880b8d8c1985d075dbcbf679b99a5f9914e5aaf96b831a9e24",
);
}

#[test]
Expand Down

0 comments on commit dc0e0f9

Please sign in to comment.