boring-rustls-provider 5.0.0

A BoringSSL-based rustls crypto provider with optional FIPS and post-quantum support
Documentation
use boring::hash::{Hasher, MessageDigest};
use rustls::crypto::hash;

pub const SHA256: &dyn hash::Hash = &Hash(boring::nid::Nid::SHA256);
pub const SHA384: &dyn hash::Hash = &Hash(boring::nid::Nid::SHA384);

pub struct Hash(pub boring::nid::Nid);

impl hash::Hash for Hash {
    fn start(&self) -> Box<dyn hash::Context> {
        let digest = MessageDigest::from_nid(self.0).expect("failed getting hash digest");
        let hasher = Hasher::new(digest).expect("failed getting hasher");
        Box::new(HasherContext(hasher))
    }

    fn hash(&self, data: &[u8]) -> hash::Output {
        let mut hasher = self.start();
        hasher.update(data);
        hasher.finish()
    }

    fn algorithm(&self) -> hash::HashAlgorithm {
        match self.0 {
            boring::nid::Nid::SHA256 => hash::HashAlgorithm::SHA256,
            boring::nid::Nid::SHA384 => hash::HashAlgorithm::SHA384,
            boring::nid::Nid::SHA512 => hash::HashAlgorithm::SHA512,
            _ => unreachable!("hash::Hash is only instantiated with SHA-2 digests"),
        }
    }

    fn fips(&self) -> bool {
        cfg!(feature = "fips")
    }

    fn output_len(&self) -> usize {
        MessageDigest::from_nid(self.0)
            .expect("failed getting digest")
            .size()
    }
}

struct HasherContext(Hasher);

impl hash::Context for HasherContext {
    fn fork_finish(&self) -> hash::Output {
        let mut cloned = self.0.clone();

        hash::Output::new(&cloned.finish().expect("failed finishing hash")[..])
    }

    fn fork(&self) -> Box<dyn hash::Context> {
        Box::new(HasherContext(self.0.clone()))
    }

    fn finish(mut self: Box<Self>) -> hash::Output {
        hash::Output::new(&self.0.finish().expect("failed finishing hash")[..])
    }

    fn update(&mut self, data: &[u8]) {
        self.0.update(data).expect("failed adding data to hash");
    }
}

#[cfg(test)]
mod tests {
    use super::SHA256;
    use hex_literal::hex;

    #[test]
    fn test_context() {
        let mut hash = SHA256.start();
        hash.update(b"ABCDE");
        let abcde = hash.fork_finish();
        hash.update(b"FGHIJ");
        let abcdefghij = hash.finish();

        assert_eq!(
            abcde.as_ref(),
            hex!("f0393febe8baaa55e32f7be2a7cc180bf34e52137d99e056c817a9c07b8f239a")
        );
        assert_eq!(
            abcdefghij.as_ref(),
            hex!("261305762671a58cae5b74990bcfc236c2336fb04a0fbac626166d9491d2884c")
        );
    }

    #[test]
    fn test_sha256() {
        let hash = SHA256.hash("test".as_bytes());

        assert_eq!(
            hash.as_ref(),
            hex!("9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08")
        );
    }
}