// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. use deno_core::error::generic_error; use deno_core::error::AnyError; use deno_core::GcResource; use digest::Digest; use digest::DynDigest; use digest::ExtendableOutput; use digest::Update; use std::cell::RefCell; use std::rc::Rc; pub struct Hasher { pub hash: Rc>>, } impl GcResource for Hasher {} impl Hasher { pub fn new( algorithm: &str, output_length: Option, ) -> Result { let hash = Hash::new(algorithm, output_length)?; Ok(Self { hash: Rc::new(RefCell::new(Some(hash))), }) } pub fn update(&self, data: &[u8]) -> bool { if let Some(hash) = self.hash.borrow_mut().as_mut() { hash.update(data); true } else { false } } pub fn digest(&self) -> Option> { let hash = self.hash.borrow_mut().take()?; Some(hash.digest_and_drop()) } pub fn clone_inner( &self, output_length: Option, ) -> Result, AnyError> { let hash = self.hash.borrow(); let Some(hash) = hash.as_ref() else { return Ok(None); }; let hash = hash.clone_hash(output_length)?; Ok(Some(Self { hash: Rc::new(RefCell::new(Some(hash))), })) } } pub enum Hash { Blake2b512(Box), Blake2s256(Box), Md4(Box), Md5(Box), Ripemd160(Box), Sha1(Box), Sha224(Box), Sha256(Box), Sha384(Box), Sha512(Box), Sha512_224(Box), Sha512_256(Box), Sha3_224(Box), Sha3_256(Box), Sha3_384(Box), Sha3_512(Box), Sm3(Box), Shake128(Box, /* output_length: */ Option), Shake256(Box, /* output_length: */ Option), } use Hash::*; impl Hash { pub fn new( algorithm_name: &str, output_length: Option, ) -> Result { match algorithm_name { "shake128" => return Ok(Shake128(Default::default(), output_length)), "shake256" => return Ok(Shake256(Default::default(), output_length)), _ => {} } let algorithm = match algorithm_name { "blake2b512" => Blake2b512(Default::default()), "blake2s256" => Blake2s256(Default::default()), "md4" => Md4(Default::default()), "md5" => Md5(Default::default()), "ripemd160" => Ripemd160(Default::default()), "sha1" => Sha1(Default::default()), "sha224" => Sha224(Default::default()), "sha256" => Sha256(Default::default()), "sha384" => Sha384(Default::default()), "sha512" => Sha512(Default::default()), "sha512-224" => Sha512_224(Default::default()), "sha512-256" => Sha512_256(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()), "sm3" => Sm3(Default::default()), _ => { return Err(generic_error(format!( "Digest method not supported: {algorithm_name}" ))) } }; if let Some(length) = output_length { if length != algorithm.output_length() { return Err(generic_error( "Output length mismatch for non-extendable algorithm", )); } } Ok(algorithm) } pub fn output_length(&self) -> usize { match self { Blake2b512(context) => context.output_size(), Blake2s256(context) => context.output_size(), Md4(context) => context.output_size(), Md5(context) => context.output_size(), Ripemd160(context) => context.output_size(), Sha1(context) => context.output_size(), Sha224(context) => context.output_size(), Sha256(context) => context.output_size(), Sha384(context) => context.output_size(), Sha512(context) => context.output_size(), Sha512_224(context) => context.output_size(), Sha512_256(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(), Sm3(context) => context.output_size(), Shake128(_, _) => unreachable!( "output_length() should not be called on extendable algorithms" ), Shake256(_, _) => unreachable!( "output_length() should not be called on extendable algorithms" ), } } pub fn update(&mut self, data: &[u8]) { match self { Blake2b512(context) => Digest::update(&mut **context, data), Blake2s256(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), 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), Sha512_224(context) => Digest::update(&mut **context, data), Sha512_256(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), Sm3(context) => Digest::update(&mut **context, data), Shake128(context, _) => Update::update(&mut **context, data), Shake256(context, _) => Update::update(&mut **context, data), }; } pub fn digest_and_drop(self) -> Box<[u8]> { match self { Blake2b512(context) => context.finalize(), Blake2s256(context) => context.finalize(), Md4(context) => context.finalize(), Md5(context) => context.finalize(), Ripemd160(context) => context.finalize(), Sha1(context) => context.finalize(), Sha224(context) => context.finalize(), Sha256(context) => context.finalize(), Sha384(context) => context.finalize(), Sha512(context) => context.finalize(), Sha512_224(context) => context.finalize(), Sha512_256(context) => context.finalize(), Sha3_224(context) => context.finalize(), Sha3_256(context) => context.finalize(), Sha3_384(context) => context.finalize(), Sha3_512(context) => context.finalize(), Sm3(context) => context.finalize(), // The default output lengths align with Node.js Shake128(context, output_length) => { context.finalize_boxed(output_length.unwrap_or(16)) } Shake256(context, output_length) => { context.finalize_boxed(output_length.unwrap_or(32)) } } } pub fn clone_hash( &self, output_length: Option, ) -> Result { let hash = match self { Shake128(context, _) => { return Ok(Shake128(context.clone(), output_length)) } Shake256(context, _) => { return Ok(Shake256(context.clone(), output_length)) } Blake2b512(context) => Blake2b512(context.clone()), Blake2s256(context) => Blake2s256(context.clone()), Md4(context) => Md4(context.clone()), Md5(context) => Md5(context.clone()), Ripemd160(context) => Ripemd160(context.clone()), Sha1(context) => Sha1(context.clone()), Sha224(context) => Sha224(context.clone()), Sha256(context) => Sha256(context.clone()), Sha384(context) => Sha384(context.clone()), Sha512(context) => Sha512(context.clone()), Sha512_224(context) => Sha512_224(context.clone()), Sha512_256(context) => Sha512_256(context.clone()), Sha3_224(context) => Sha3_224(context.clone()), Sha3_256(context) => Sha3_256(context.clone()), Sha3_384(context) => Sha3_384(context.clone()), Sha3_512(context) => Sha3_512(context.clone()), Sm3(context) => Sm3(context.clone()), }; if let Some(length) = output_length { if length != hash.output_length() { return Err(generic_error( "Output length mismatch for non-extendable algorithm", )); } } Ok(hash) } pub fn get_hashes() -> Vec<&'static str> { vec![ "blake2s256", "blake2b512", "md4", "md5", "ripemd160", "sha1", "sha224", "sha256", "sha384", "sha512", "sha512-224", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256", "sm3", ] } }