use alloc::boxed::Box;
use ic_core::traits::Digest;
use rustls::crypto::hash::{self, HashAlgorithm};
pub(crate) static SHA256: Hash<ic_hash::Sha256> = Hash::new(HashAlgorithm::SHA256);
pub(crate) static SHA384: Hash<ic_hash::Sha384> = Hash::new(HashAlgorithm::SHA384);
pub(crate) struct Hash<D: Digest> {
algorithm: HashAlgorithm,
_digest: core::marker::PhantomData<fn() -> D>,
}
impl<D: Digest> Hash<D> {
const fn new(algorithm: HashAlgorithm) -> Self {
Self {
algorithm,
_digest: core::marker::PhantomData,
}
}
}
impl<D: Digest + Send + Sync + 'static> hash::Hash for Hash<D> {
fn start(&self) -> Box<dyn hash::Context> {
Box::new(Context(D::new()))
}
fn hash(&self, data: &[u8]) -> hash::Output {
let mut d = D::new();
d.update(data);
hash::Output::new(d.finalize().as_ref())
}
fn output_len(&self) -> usize {
D::OUTPUT_LEN
}
fn algorithm(&self) -> HashAlgorithm {
self.algorithm
}
fn fips(&self) -> bool {
false
}
}
struct Context<D: Digest>(D);
impl<D: Digest + Send + Sync + 'static> hash::Context for Context<D> {
fn fork_finish(&self) -> hash::Output {
hash::Output::new(self.0.clone().finalize().as_ref())
}
fn fork(&self) -> Box<dyn hash::Context> {
Box::new(Self(self.0.clone()))
}
fn finish(self: Box<Self>) -> hash::Output {
hash::Output::new(self.0.finalize().as_ref())
}
fn update(&mut self, data: &[u8]) {
self.0.update(data);
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustls::crypto::hash::Hash as _;
#[test]
fn the_adapter_hashes_what_the_algorithm_hashes() {
let out = SHA256.hash(b"abc");
assert_eq!(
ic_core::codec::hex(out.as_ref()),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
let out = SHA384.hash(b"abc");
assert_eq!(
ic_core::codec::hex(out.as_ref()),
"cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed\
8086072ba1e7cc2358baeca134c825a7"
);
}
#[test]
fn a_forked_context_continues_the_same_computation() {
let mut ctx = SHA256.start();
ctx.update(b"abc");
let forked = ctx.fork();
assert_eq!(ctx.fork_finish().as_ref(), forked.fork_finish().as_ref());
assert_eq!(
ic_core::codec::hex(ctx.fork_finish().as_ref()),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
let mut a = ctx.fork();
let mut b = ctx.fork();
a.update(b"d");
b.update(b"e");
assert_ne!(a.fork_finish().as_ref(), b.fork_finish().as_ref());
assert_eq!(
ic_core::codec::hex(ctx.fork_finish().as_ref()),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn the_declared_lengths_are_right() {
assert_eq!(SHA256.output_len(), 32);
assert_eq!(SHA384.output_len(), 48);
assert_eq!(SHA256.hash(b"").as_ref().len(), 32);
assert_eq!(SHA384.hash(b"").as_ref().len(), 48);
}
#[test]
fn neither_hash_claims_fips_validation() {
assert!(!SHA256.fips());
assert!(!SHA384.fips());
}
}