// Copyright 2018-2021 the Deno authors. All rights reserved. MIT license. use digest::{ core_api::BlockSizeUser, Digest, DynDigest, ExtendableOutput, ExtendableOutputReset, Reset, Update, }; use typenum::{U32, U48}; /// Enum wrapper over all supported digest implementations. #[derive(Clone)] pub enum Context { Blake2b(Box), Blake2b256(Box>), Blake2b384(Box>), Blake2s(Box), Blake3(Box), Keccak224(Box), Keccak256(Box), Keccak384(Box), Keccak512(Box), Md4(Box), Md5(Box), Ripemd160(Box), Sha1(Box), Sha3_224(Box), Sha3_256(Box), Sha3_384(Box), Sha3_512(Box), Sha224(Box), Sha256(Box), Sha384(Box), Sha512(Box), Shake128(Box), Shake256(Box), Tiger(Box), } use Context::*; impl Context { pub fn new(algorithm_name: &str) -> Result { Ok(match algorithm_name { "BLAKE2B" => Blake2b(Default::default()), "BLAKE2B-256" => Blake2b256(Default::default()), "BLAKE2B-384" => Blake2b384(Default::default()), "BLAKE2S" => Blake2s(Default::default()), "BLAKE3" => Blake3(Default::default()), "KECCAK-224" => Keccak224(Default::default()), "KECCAK-256" => Keccak256(Default::default()), "KECCAK-384" => Keccak384(Default::default()), "KECCAK-512" => Keccak512(Default::default()), "MD4" => Md4(Default::default()), "MD5" => Md5(Default::default()), "RIPEMD-160" => Ripemd160(Default::default()), "SHA-1" => Sha1(Default::default()), "SHA3-224" => Sha3_224(Default::default()), "SHA3-256" => Sha3_256(Default::default()), "SHA3-384" => Sha3_384(Default::default()), "SHA3-512" => Sha3_512(Default::default()), "SHA-224" => Sha224(Default::default()), "SHA-256" => Sha256(Default::default()), "SHA-384" => Sha384(Default::default()), "SHA-512" => Sha512(Default::default()), "SHAKE128" => Shake128(Default::default()), "SHAKE256" => Shake256(Default::default()), "TIGER" => Tiger(Default::default()), _ => return Err("unsupported algorithm"), }) } /// The input block length for the algorithm, in bytes. pub fn input_block_length(&self) -> usize { // For algorithm types that implement BlockInput and have a statically // available BlockSize as part of their type definition, we use that value. fn static_block_length(_: &T) -> usize { ::to_usize() } match self { Blake2b(context) => static_block_length(&**context), Blake2b256(context) => static_block_length(&**context), Blake2b384(context) => static_block_length(&**context), Blake2s(context) => static_block_length(&**context), Blake3(_) => 64, Keccak224(context) => static_block_length(&**context), Keccak256(context) => static_block_length(&**context), Keccak384(context) => static_block_length(&**context), Keccak512(context) => static_block_length(&**context), Md4(context) => static_block_length(&**context), Md5(context) => static_block_length(&**context), Ripemd160(context) => static_block_length(&**context), Sha1(context) => static_block_length(&**context), Sha3_224(context) => static_block_length(&**context), Sha3_256(context) => static_block_length(&**context), Sha3_384(context) => static_block_length(&**context), Sha3_512(context) => static_block_length(&**context), Sha224(context) => static_block_length(&**context), Sha256(context) => static_block_length(&**context), Sha384(context) => static_block_length(&**context), Sha512(context) => static_block_length(&**context), Tiger(context) => static_block_length(&**context), // https://doi.org/10.6028/NIST.FIPS.202 specifies that: // - In general, the input block size (in bits) of a sponge function is // its rate. // - SPONGE[f, pad, r] = The sponge function in which the underlying // function is f, the padding rule is pad, and the rate is r. // - KECCAK[c] = SPONGE[KECCAK-p[1600, 24], pad10*1, 1600–c] // - SHAKE128(M, d) = KECCAK[256] (M || 1111, d) Shake128(_) => 168, // implying a rate of (1600 - 256) bits = 168 bytes // - SHAKE256(M, d) = KECCAK[512] (M || 1111, d). Shake256(_) => 136, // implying a rate of (1600 - 512) bits = 136 bytes } } /// The output digest length for the algorithm, in bytes. /// /// If the algorithm is variable-length, this returns its default length. pub fn output_length(&self) -> usize { match self { Blake2b(context) => context.output_size(), Blake2b256(context) => context.output_size(), Blake2b384(context) => context.output_size(), Blake2s(context) => context.output_size(), Blake3(context) => context.output_size(), Keccak224(context) => context.output_size(), Keccak256(context) => context.output_size(), Keccak384(context) => context.output_size(), Keccak512(context) => context.output_size(), Md4(context) => context.output_size(), Md5(context) => context.output_size(), Ripemd160(context) => context.output_size(), Sha1(context) => context.output_size(), Sha3_224(context) => context.output_size(), Sha3_256(context) => context.output_size(), Sha3_384(context) => context.output_size(), Sha3_512(context) => context.output_size(), Sha224(context) => context.output_size(), Sha256(context) => context.output_size(), Sha384(context) => context.output_size(), Sha512(context) => context.output_size(), Tiger(context) => context.output_size(), // https://doi.org/10.6028/NIST.FIPS.202's Table 4 indicates that in order // to reach the target security strength for these algorithms, the output // size (in bits) needs to be (at least) two times larger than that // security strength (in bits). Shake128(_) => 32, // implying a length of (2 * 128) bits = 32 bytes Shake256(_) => 64, // implying a length of (2 * 256) bits = 64 bytes } } /// Whether the algorithm has an extendable variable-length digest output /// (whether it is an "XOF"). pub const fn extendable(&self) -> bool { matches!(self, Blake3(_) | Shake128(_) | Shake256(_)) } /// The name of the algorithm used by this context. /// /// Names are all uppercase (for ease of case-insensitive comparisons) and /// will match the name formatting used in WebCrypto if the algorithm is /// supported by WebCrypto, and otherwise match the formatting used in the /// official specification for the algorithm. pub const fn algorithm_name(&self) -> &'static str { match self { Blake2b(_) => "BLAKE2B", Blake2b256(_) => "BLAKE2B-256", Blake2b384(_) => "BLAKE2B-384", Blake2s(_) => "BLAKE2S", Blake3(_) => "BLAKE3", Keccak224(_) => "KECCAK-224", Keccak256(_) => "KECCAK-256", Keccak384(_) => "KECCAK-384", Keccak512(_) => "KECCAK-512", Md4(_) => "MD5", Md5(_) => "MD5", Ripemd160(_) => "RIPEMD-160", Sha1(_) => "SHA-1", Sha3_224(_) => "SHA3-224", Sha3_256(_) => "SHA3-256", Sha3_384(_) => "SHA3-384", Sha3_512(_) => "SHA3-512", Sha224(_) => "SHA-224", Sha256(_) => "SHA-256", Sha384(_) => "SHA-384", Sha512(_) => "SHA-512", Shake128(_) => "SHAKE128", Shake256(_) => "SHAKE256", Tiger(_) => "TIGER", } } pub fn reset(&mut self) { match self { Blake2b(context) => Reset::reset(&mut **context), Blake2b256(context) => Reset::reset(&mut **context), Blake2b384(context) => Reset::reset(&mut **context), Blake2s(context) => Reset::reset(&mut **context), Blake3(context) => Reset::reset(&mut **context), Keccak224(context) => Reset::reset(&mut **context), Keccak256(context) => Reset::reset(&mut **context), Keccak384(context) => Reset::reset(&mut **context), Keccak512(context) => Reset::reset(&mut **context), Md4(context) => Reset::reset(&mut **context), Md5(context) => Reset::reset(&mut **context), Ripemd160(context) => Reset::reset(&mut **context), Sha1(context) => Reset::reset(&mut **context), Sha3_224(context) => Reset::reset(&mut **context), Sha3_256(context) => Reset::reset(&mut **context), Sha3_384(context) => Reset::reset(&mut **context), Sha3_512(context) => Reset::reset(&mut **context), Sha224(context) => Reset::reset(&mut **context), Sha256(context) => Reset::reset(&mut **context), Sha384(context) => Reset::reset(&mut **context), Sha512(context) => Reset::reset(&mut **context), Shake128(context) => Reset::reset(&mut **context), Shake256(context) => Reset::reset(&mut **context), Tiger(context) => Reset::reset(&mut **context), }; } pub fn update(&mut self, data: &[u8]) { match self { Blake2b(context) => Digest::update(&mut **context, data), Blake2b256(context) => Digest::update(&mut **context, data), Blake2b384(context) => Digest::update(&mut **context, data), Blake2s(context) => Digest::update(&mut **context, data), Blake3(context) => Digest::update(&mut **context, data), Keccak224(context) => Digest::update(&mut **context, data), Keccak256(context) => Digest::update(&mut **context, data), Keccak384(context) => Digest::update(&mut **context, data), Keccak512(context) => Digest::update(&mut **context, data), Md4(context) => Digest::update(&mut **context, data), Md5(context) => Digest::update(&mut **context, data), Ripemd160(context) => Digest::update(&mut **context, data), Sha1(context) => Digest::update(&mut **context, data), Sha3_224(context) => Digest::update(&mut **context, data), Sha3_256(context) => Digest::update(&mut **context, data), Sha3_384(context) => Digest::update(&mut **context, data), Sha3_512(context) => Digest::update(&mut **context, data), Sha224(context) => Digest::update(&mut **context, data), Sha256(context) => Digest::update(&mut **context, data), Sha384(context) => Digest::update(&mut **context, data), Sha512(context) => Digest::update(&mut **context, data), Tiger(context) => Digest::update(&mut **context, data), Shake128(context) => (&mut **context).update(data), Shake256(context) => (&mut **context).update(data), }; } pub fn digest_and_drop( self, length: Option, ) -> Result, &'static str> { if let Some(length) = length { if !self.extendable() && length != self.output_length() { return Err( "non-default length specified for non-extendable algorithm", ); } } let length = length.unwrap_or_else(|| self.output_length()); Ok(match self { Blake2b(context) => context.finalize(), Blake2b256(context) => context.finalize(), Blake2b384(context) => context.finalize(), Blake2s(context) => context.finalize(), Blake3(context) => context.finalize_boxed(length), Keccak224(context) => context.finalize(), Keccak256(context) => context.finalize(), Keccak384(context) => context.finalize(), Keccak512(context) => context.finalize(), Md4(context) => context.finalize(), Md5(context) => context.finalize(), Ripemd160(context) => context.finalize(), Sha1(context) => context.finalize(), Sha3_224(context) => context.finalize(), Sha3_256(context) => context.finalize(), Sha3_384(context) => context.finalize(), Sha3_512(context) => context.finalize(), Sha224(context) => context.finalize(), Sha256(context) => context.finalize(), Sha384(context) => context.finalize(), Sha512(context) => context.finalize(), Tiger(context) => context.finalize(), Shake128(context) => context.finalize_boxed(length), Shake256(context) => context.finalize_boxed(length), }) } pub fn digest_and_reset( &mut self, length: Option, ) -> Result, &'static str> { if let Some(length) = length { if !self.extendable() && length != self.output_length() { return Err( "non-default length specified for non-extendable algorithm", ); } } let length = length.unwrap_or_else(|| self.output_length()); Ok(match self { Blake2b(context) => DynDigest::finalize_reset(context.as_mut()), Blake2b256(context) => DynDigest::finalize_reset(context.as_mut()), Blake2b384(context) => DynDigest::finalize_reset(context.as_mut()), Blake2s(context) => DynDigest::finalize_reset(context.as_mut()), Blake3(context) => { ExtendableOutputReset::finalize_boxed_reset(context.as_mut(), length) } Keccak224(context) => DynDigest::finalize_reset(context.as_mut()), Keccak256(context) => DynDigest::finalize_reset(context.as_mut()), Keccak384(context) => DynDigest::finalize_reset(context.as_mut()), Keccak512(context) => DynDigest::finalize_reset(context.as_mut()), Md4(context) => DynDigest::finalize_reset(context.as_mut()), Md5(context) => DynDigest::finalize_reset(context.as_mut()), Ripemd160(context) => DynDigest::finalize_reset(context.as_mut()), Sha1(context) => DynDigest::finalize_reset(context.as_mut()), Sha3_224(context) => DynDigest::finalize_reset(context.as_mut()), Sha3_256(context) => DynDigest::finalize_reset(context.as_mut()), Sha3_384(context) => DynDigest::finalize_reset(context.as_mut()), Sha3_512(context) => DynDigest::finalize_reset(context.as_mut()), Sha224(context) => DynDigest::finalize_reset(context.as_mut()), Sha256(context) => DynDigest::finalize_reset(context.as_mut()), Sha384(context) => DynDigest::finalize_reset(context.as_mut()), Sha512(context) => DynDigest::finalize_reset(context.as_mut()), Tiger(context) => DynDigest::finalize_reset(context.as_mut()), Shake128(context) => { ExtendableOutputReset::finalize_boxed_reset(context.as_mut(), length) } Shake256(context) => { ExtendableOutputReset::finalize_boxed_reset(context.as_mut(), length) } }) } pub fn digest( &self, length: Option, ) -> Result, &'static str> { self.clone().digest_and_drop(length) } }