#![cfg(feature = "rust-crypto-traits")]
use digest::{
FixedOutput, HashMarker, Output, OutputSizeUser, Reset, Update, XofReader,
typenum::{U20, U32, U48, U64},
};
use crate::Hasher as NativeHasher;
use crate::Xof as NativeXof;
macro_rules! bridge_fixed_hash {
($wrapper:ident, $native:path, $out_size:ty, $doc:literal) => {
#[doc = $doc]
#[derive(Clone)]
pub struct $wrapper($native);
impl Default for $wrapper {
#[inline]
fn default() -> Self {
Self(<$native as NativeHasher>::new())
}
}
impl OutputSizeUser for $wrapper {
type OutputSize = $out_size;
}
impl HashMarker for $wrapper {}
impl Update for $wrapper {
#[inline]
fn update(&mut self, data: &[u8]) {
NativeHasher::update(&mut self.0, data);
}
}
impl FixedOutput for $wrapper {
#[inline]
fn finalize_into(self, out: &mut Output<Self>) {
NativeHasher::finalize_into(self.0, out.as_mut_slice());
}
}
impl Reset for $wrapper {
#[inline]
fn reset(&mut self) {
self.0 = <$native as NativeHasher>::new();
}
}
impl digest::FixedOutputReset for $wrapper {
#[inline]
fn finalize_into_reset(&mut self, out: &mut Output<Self>) {
let taken = core::mem::replace(&mut self.0, <$native as NativeHasher>::new());
NativeHasher::finalize_into(taken, out.as_mut_slice());
}
}
};
}
bridge_fixed_hash!(
Sha1,
crate::hash::sha1::Sha1,
U20,
"`digest::Digest` wrapper for [`crate::hash::sha1::Sha1`] (SHA-1, 160-bit)."
);
bridge_fixed_hash!(
Sha256,
crate::hash::sha256::Sha256,
U32,
"`digest::Digest` wrapper for [`crate::hash::sha256::Sha256`]."
);
bridge_fixed_hash!(
Sha384,
crate::hash::sha384::Sha384,
U48,
"`digest::Digest` wrapper for [`crate::hash::sha384::Sha384`]."
);
bridge_fixed_hash!(
Sha512,
crate::hash::sha512::Sha512,
U64,
"`digest::Digest` wrapper for [`crate::hash::sha512::Sha512`]."
);
bridge_fixed_hash!(
Sha3_256,
crate::hash::sha3::Sha3_256,
U32,
"`digest::Digest` wrapper for [`crate::hash::sha3::Sha3_256`]."
);
bridge_fixed_hash!(
Sha3_384,
crate::hash::sha3::Sha3_384,
U48,
"`digest::Digest` wrapper for [`crate::hash::sha3::Sha3_384`]."
);
bridge_fixed_hash!(
Sha3_512,
crate::hash::sha3::Sha3_512,
U64,
"`digest::Digest` wrapper for [`crate::hash::sha3::Sha3_512`]."
);
bridge_fixed_hash!(
Ripemd160,
crate::hash::ripemd160::Ripemd160,
U20,
"`digest::Digest` wrapper for [`crate::hash::ripemd160::Ripemd160`] (RIPEMD-160, 160-bit)."
);
pub struct XofStream<X: NativeXof>(X);
impl<X: NativeXof> XofReader for XofStream<X> {
#[inline]
fn read(&mut self, buffer: &mut [u8]) {
self.0.squeeze(buffer);
}
}
macro_rules! bridge_xof {
($wrapper:ident, $native:path, $doc:literal) => {
#[doc = $doc]
#[derive(Clone, Default)]
pub struct $wrapper($native);
impl Update for $wrapper {
#[inline]
fn update(&mut self, data: &[u8]) {
NativeXof::update(&mut self.0, data);
}
}
impl Reset for $wrapper {
#[inline]
fn reset(&mut self) {
self.0 = <$native as NativeXof>::new();
}
}
impl digest::ExtendableOutput for $wrapper {
type Reader = XofStream<$native>;
#[inline]
fn finalize_xof(self) -> Self::Reader {
XofStream(self.0)
}
}
};
}
impl Default for crate::hash::sha3::Shake128 {
fn default() -> Self {
<Self as NativeXof>::new()
}
}
impl Default for crate::hash::sha3::Shake256 {
fn default() -> Self {
<Self as NativeXof>::new()
}
}
bridge_xof!(
Shake128,
crate::hash::sha3::Shake128,
"`digest::ExtendableOutput` wrapper for [`crate::hash::sha3::Shake128`]."
);
bridge_xof!(
Shake256,
crate::hash::sha3::Shake256,
"`digest::ExtendableOutput` wrapper for [`crate::hash::sha3::Shake256`]."
);
#[cfg(test)]
mod tests {
use super::*;
use digest::{Digest as RcDigest, ExtendableOutput as RcExtendableOutput};
#[test]
fn bridge_sha256_empty_matches_fips() {
let out = <Sha256 as RcDigest>::digest(b"");
let expected = hex_lit::hex("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
assert_eq!(out.as_slice(), expected.as_slice());
}
#[test]
fn bridge_sha256_abc_matches_fips() {
let out = <Sha256 as RcDigest>::digest(b"abc");
let expected = hex_lit::hex("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
assert_eq!(out.as_slice(), expected.as_slice());
}
#[test]
fn bridge_all_fixed_hashes_match_native() {
let data = b"cross-check";
assert_eq!(
<Sha1 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha1::Sha1 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha256 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha256::Sha256 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha384 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha384::Sha384 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha512 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha512::Sha512 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha3_256 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha3::Sha3_256 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha3_384 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha3::Sha3_384 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Sha3_512 as RcDigest>::digest(data).as_slice(),
<crate::hash::sha3::Sha3_512 as NativeHasher>::hash(data).as_slice(),
);
assert_eq!(
<Ripemd160 as RcDigest>::digest(data).as_slice(),
<crate::hash::ripemd160::Ripemd160 as NativeHasher>::hash(data).as_slice(),
);
}
#[test]
fn bridge_incremental_update_matches_oneshot() {
let mut h = <Sha256 as RcDigest>::new();
RcDigest::update(&mut h, b"abc");
RcDigest::update(&mut h, b"def");
let incremental = h.finalize();
let oneshot = <Sha256 as RcDigest>::digest(b"abcdef");
assert_eq!(incremental, oneshot);
}
#[test]
fn bridge_finalize_reset_leaves_clean_state() {
let mut h = <Sha256 as RcDigest>::new();
RcDigest::update(&mut h, b"first");
let first = h.finalize_reset();
RcDigest::update(&mut h, b"second");
let second = h.finalize();
assert_ne!(first, second);
assert_eq!(first, <Sha256 as RcDigest>::digest(b"first"));
assert_eq!(second, <Sha256 as RcDigest>::digest(b"second"));
}
#[test]
fn bridge_shake128_matches_native() {
let data = b"shake it up";
let mut bridge = Shake128::default();
Update::update(&mut bridge, data);
let mut reader = bridge.finalize_xof();
let mut out_bridge = [0u8; 64];
reader.read(&mut out_bridge);
let mut native = <crate::hash::sha3::Shake128 as NativeXof>::new();
NativeXof::update(&mut native, data);
let mut out_native = [0u8; 64];
NativeXof::squeeze(&mut native, &mut out_native);
assert_eq!(out_bridge, out_native);
}
#[test]
fn bridge_shake128_chunked_read_matches_single() {
let data = b"hello shake";
let mut bridge1 = Shake128::default();
Update::update(&mut bridge1, data);
let mut reader1 = bridge1.finalize_xof();
let mut full = [0u8; 80];
reader1.read(&mut full);
let mut bridge2 = Shake128::default();
Update::update(&mut bridge2, data);
let mut reader2 = bridge2.finalize_xof();
let mut chunked = [0u8; 80];
reader2.read(&mut chunked[..32]);
reader2.read(&mut chunked[32..48]);
reader2.read(&mut chunked[48..]);
assert_eq!(full, chunked);
}
mod hex_lit {
pub fn hex(s: &str) -> Vec<u8> {
assert!(s.len() % 2 == 0);
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
}
}