mirror of
https://github.com/denoland/deno.git
synced 2024-12-01 16:51:13 -05:00
1e8a6b94b1
Changes in this PR: - Added new fixed size hash algorithms (blake2b512, blake2s256, sha512-224, sha512-256, sha3-224, sha3-256, sha3-384, sha3-512, sm3) - Added variable size hash algorithms (the concept), with the algorithms shake128 and shake256 - Use cppgc instead of resources for the hasher - Enable Node's crypto.Hash tests and fix found bugs
310 lines
8.8 KiB
Rust
310 lines
8.8 KiB
Rust
// 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<RefCell<Option<Hash>>>,
|
|
}
|
|
|
|
impl GcResource for Hasher {}
|
|
|
|
impl Hasher {
|
|
pub fn new(
|
|
algorithm: &str,
|
|
output_length: Option<usize>,
|
|
) -> Result<Self, AnyError> {
|
|
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<Box<[u8]>> {
|
|
let hash = self.hash.borrow_mut().take()?;
|
|
Some(hash.digest_and_drop())
|
|
}
|
|
|
|
pub fn clone_inner(
|
|
&self,
|
|
output_length: Option<usize>,
|
|
) -> Result<Option<Self>, 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<blake2::Blake2b512>),
|
|
Blake2s256(Box<blake2::Blake2s256>),
|
|
|
|
Md4(Box<md4::Md4>),
|
|
Md5(Box<md5::Md5>),
|
|
|
|
Ripemd160(Box<ripemd::Ripemd160>),
|
|
|
|
Sha1(Box<sha1::Sha1>),
|
|
|
|
Sha224(Box<sha2::Sha224>),
|
|
Sha256(Box<sha2::Sha256>),
|
|
Sha384(Box<sha2::Sha384>),
|
|
Sha512(Box<sha2::Sha512>),
|
|
Sha512_224(Box<sha2::Sha512_224>),
|
|
Sha512_256(Box<sha2::Sha512_256>),
|
|
|
|
Sha3_224(Box<sha3::Sha3_224>),
|
|
Sha3_256(Box<sha3::Sha3_256>),
|
|
Sha3_384(Box<sha3::Sha3_384>),
|
|
Sha3_512(Box<sha3::Sha3_512>),
|
|
|
|
Sm3(Box<sm3::Sm3>),
|
|
|
|
Shake128(Box<sha3::Shake128>, /* output_length: */ Option<usize>),
|
|
Shake256(Box<sha3::Shake256>, /* output_length: */ Option<usize>),
|
|
}
|
|
|
|
use Hash::*;
|
|
|
|
impl Hash {
|
|
pub fn new(
|
|
algorithm_name: &str,
|
|
output_length: Option<usize>,
|
|
) -> Result<Self, AnyError> {
|
|
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<usize>,
|
|
) -> Result<Self, AnyError> {
|
|
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",
|
|
]
|
|
}
|
|
}
|