pub mod prelude {
pub use super::MultihashCode;
pub use multihash_codetable::MultihashDigest as _;
}
use multihash_derive::{Hasher, MultihashDigest};
#[derive(Clone, Copy, Debug, Eq, MultihashDigest, PartialEq)]
#[mh(alloc_size = 64)]
pub enum MultihashCode {
#[mh(code = 0x0, hasher = IdentityHasher::<64>)]
Identity,
#[mh(code = 0x12, hasher = multihash_codetable::Sha2_256)]
Sha2_256,
#[mh(code = 0x13, hasher = multihash_codetable::Sha2_512)]
Sha2_512,
#[mh(code = 0x17, hasher = multihash_codetable::Sha3_224)]
Sha3_224,
#[mh(code = 0x16, hasher = multihash_codetable::Sha3_256)]
Sha3_256,
#[mh(code = 0x15, hasher = multihash_codetable::Sha3_384)]
Sha3_384,
#[mh(code = 0x14, hasher = multihash_codetable::Sha3_512)]
Sha3_512,
#[mh(code = 0x1a, hasher = multihash_codetable::Keccak224)]
Keccak224,
#[mh(code = 0x1b, hasher = multihash_codetable::Keccak256)]
Keccak256,
#[mh(code = 0x1c, hasher = multihash_codetable::Keccak384)]
Keccak384,
#[mh(code = 0x1d, hasher = multihash_codetable::Keccak512)]
Keccak512,
#[mh(code = 0xb220, hasher = multihash_codetable::Blake2b256)]
Blake2b256,
#[mh(code = 0xb240, hasher = multihash_codetable::Blake2b512)]
Blake2b512,
#[mh(code = 0xb250, hasher = multihash_codetable::Blake2s128)]
Blake2s128,
#[mh(code = 0xb260, hasher = multihash_codetable::Blake2s256)]
Blake2s256,
#[mh(code = 0x1e, hasher = multihash_codetable::Blake3_256)]
Blake3_256,
#[mh(code = 0x1053, hasher = multihash_codetable::Ripemd160)]
Ripemd160,
#[mh(code = 0x1054, hasher = multihash_codetable::Ripemd256)]
Ripemd256,
#[mh(code = 0x1055, hasher = multihash_codetable::Ripemd320)]
Ripemd320,
}
impl MultihashCode {
pub const IDENTITY_MAX_SIZE: usize = 64;
pub fn checked_digest(&self, data: &[u8]) -> anyhow::Result<Multihash> {
anyhow::ensure!(
*self != Self::Identity || data.len() <= Self::IDENTITY_MAX_SIZE,
"identity multihash input of {} bytes exceeds the {}-byte limit",
data.len(),
Self::IDENTITY_MAX_SIZE
);
Ok(self.digest(data))
}
pub fn digest_byte_stream<R: std::io::Read>(&self, bytes: &mut R) -> anyhow::Result<Multihash> {
fn hash<'a, H: Hasher, R: std::io::Read>(
hasher: &'a mut H,
bytes: &'a mut R,
) -> anyhow::Result<&'a [u8]> {
let mut buf = [0; 1024];
loop {
let n = bytes.read(&mut buf)?;
if n == 0 {
break;
}
if let Some(b) = buf.get(0..n) {
hasher.update(b);
}
}
Ok(hasher.finalize())
}
Ok(match self {
Self::Sha2_256 => {
let mut hasher = multihash_codetable::Sha2_256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Sha2_512 => {
let mut hasher = multihash_codetable::Sha2_512::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Sha3_224 => {
let mut hasher = multihash_codetable::Sha3_224::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Sha3_256 => {
let mut hasher = multihash_codetable::Sha3_256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Sha3_384 => {
let mut hasher = multihash_codetable::Sha3_384::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Sha3_512 => {
let mut hasher = multihash_codetable::Sha3_512::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Keccak224 => {
let mut hasher = multihash_codetable::Keccak224::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Keccak256 => {
let mut hasher = multihash_codetable::Keccak256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Keccak384 => {
let mut hasher = multihash_codetable::Keccak384::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Keccak512 => {
let mut hasher = multihash_codetable::Keccak512::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Blake2b256 => {
let mut hasher = multihash_codetable::Blake2b256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Blake2b512 => {
let mut hasher = multihash_codetable::Blake2b512::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Blake2s128 => {
let mut hasher = multihash_codetable::Blake2s128::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Blake2s256 => {
let mut hasher = multihash_codetable::Blake2s256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Blake3_256 => {
let mut hasher = multihash_codetable::Blake3_256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Ripemd160 => {
let mut hasher = multihash_codetable::Ripemd160::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Ripemd256 => {
let mut hasher = multihash_codetable::Ripemd256::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
Self::Ripemd320 => {
let mut hasher = multihash_codetable::Ripemd320::default();
self.wrap(hash(&mut hasher, bytes)?)?
}
_ => {
anyhow::bail!("`digest_byte_stream` is unimplemented for {self:?}");
}
})
}
}
#[derive(Debug)]
pub struct IdentityHasher<const S: usize> {
i: usize,
bytes: [u8; S],
}
impl<const S: usize> Default for IdentityHasher<S> {
fn default() -> Self {
Self {
i: 0,
bytes: [0u8; S],
}
}
}
impl<const S: usize> multihash_derive::Hasher for IdentityHasher<S> {
fn update(&mut self, input: &[u8]) {
let start = self.i.min(self.bytes.len());
let end = (self.i + input.len()).min(self.bytes.len());
self.bytes[start..end].copy_from_slice(input);
self.i = end;
}
fn finalize(&mut self) -> &[u8] {
&self.bytes[..self.i]
}
fn reset(&mut self) {
self.i = 0
}
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use super::*;
use crate::utils::rand::forest_rng;
use quickcheck_macros::quickcheck;
use rand::RngCore as _;
use rstest::rstest;
#[quickcheck]
fn checked_digest_bounds(data: Vec<u8>) -> bool {
use MultihashCode::*;
let ok_sha = Sha2_256.checked_digest(&data).is_ok();
let ok_identity = Identity.checked_digest(&data).is_ok()
== (data.len() <= MultihashCode::IDENTITY_MAX_SIZE);
ok_sha && ok_identity
}
fn random_bytes(len: usize) -> Vec<u8> {
let mut bytes = vec![0; len];
forest_rng().fill_bytes(&mut bytes);
bytes
}
#[rstest]
fn test_digest_byte_stream(
#[values(0, 1, 100, 1024, 10000)] len: usize,
#[values(
MultihashCode::Sha2_256,
MultihashCode::Sha2_512,
MultihashCode::Sha3_224,
MultihashCode::Sha3_256,
MultihashCode::Sha3_384,
MultihashCode::Sha3_512,
MultihashCode::Keccak224,
MultihashCode::Keccak256,
MultihashCode::Keccak384,
MultihashCode::Keccak512,
MultihashCode::Blake2b256,
MultihashCode::Blake2b512,
MultihashCode::Blake2s128,
MultihashCode::Blake2s256,
MultihashCode::Blake3_256,
MultihashCode::Ripemd160,
MultihashCode::Ripemd256,
MultihashCode::Ripemd320
)]
code: MultihashCode,
) {
let bytes = random_bytes(len);
let mh1 = code.digest(&bytes);
let mh2 = code.digest_byte_stream(&mut Cursor::new(&bytes)).unwrap();
assert_eq!(mh1, mh2);
}
#[test]
fn test_identity_digest_byte_stream_fails() {
MultihashCode::Identity
.digest_byte_stream(&mut Cursor::new(random_bytes(100)))
.unwrap_err();
}
}