mirror of
https://github.com/denoland/deno.git
synced 2024-11-23 15:16:54 -05:00
08e5606c34
Previously we had many different code paths all handling digests in different places, all with wildly different digest support. This commit rewrites this to use a single digest handling mechanism for all digest operations. It adds various aliases for digest algorithms, like node does. For example `sha1WithRSAEncryption` is an alias for `sha1`. It also adds support for `md5-sha1` digests in various places.
304 lines
7.8 KiB
Rust
304 lines
7.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::GarbageCollected;
|
|
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 GarbageCollected 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))),
|
|
}))
|
|
}
|
|
}
|
|
|
|
macro_rules! match_fixed_digest {
|
|
($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => {
|
|
match $algorithm_name {
|
|
"blake2b512" => {
|
|
type $type = ::blake2::Blake2b512;
|
|
$body
|
|
}
|
|
"blake2s256" => {
|
|
type $type = ::blake2::Blake2s256;
|
|
$body
|
|
}
|
|
_ => match_fixed_digest_with_eager_block_buffer!($algorithm_name, fn <$type>() $body, _ => $other)
|
|
}
|
|
};
|
|
}
|
|
pub(crate) use match_fixed_digest;
|
|
|
|
macro_rules! match_fixed_digest_with_eager_block_buffer {
|
|
($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => {
|
|
match $algorithm_name {
|
|
"rsa-sm3" | "sm3" | "sm3withrsaencryption" => {
|
|
type $type = ::sm3::Sm3;
|
|
$body
|
|
}
|
|
"md5-sha1" => {
|
|
type $type = crate::ops::crypto::md5_sha1::Md5Sha1;
|
|
$body
|
|
}
|
|
_ => match_fixed_digest_with_oid!($algorithm_name, fn <$type>() $body, _ => $other)
|
|
}
|
|
};
|
|
}
|
|
pub(crate) use match_fixed_digest_with_eager_block_buffer;
|
|
|
|
macro_rules! match_fixed_digest_with_oid {
|
|
($algorithm_name:expr, fn <$type:ident>() $body:block, _ => $other:block) => {
|
|
match $algorithm_name {
|
|
"rsa-md5" | "md5" | "md5withrsaencryption" | "ssl3-md5" => {
|
|
type $type = ::md5::Md5;
|
|
$body
|
|
}
|
|
"rsa-ripemd160" | "ripemd" | "ripemd160" | "ripemd160withrsa"
|
|
| "rmd160" => {
|
|
type $type = ::ripemd::Ripemd160;
|
|
$body
|
|
}
|
|
"rsa-sha1"
|
|
| "rsa-sha1-2"
|
|
| "sha1"
|
|
| "sha1-2"
|
|
| "sha1withrsaencryption"
|
|
| "ssl3-sha1" => {
|
|
type $type = ::sha1::Sha1;
|
|
$body
|
|
}
|
|
"rsa-sha224" | "sha224" | "sha224withrsaencryption" => {
|
|
type $type = ::sha2::Sha224;
|
|
$body
|
|
}
|
|
"rsa-sha256" | "sha256" | "sha256withrsaencryption" => {
|
|
type $type = ::sha2::Sha256;
|
|
$body
|
|
}
|
|
"rsa-sha384" | "sha384" | "sha384withrsaencryption" => {
|
|
type $type = ::sha2::Sha384;
|
|
$body
|
|
}
|
|
"rsa-sha512" | "sha512" | "sha512withrsaencryption" => {
|
|
type $type = ::sha2::Sha512;
|
|
$body
|
|
}
|
|
"rsa-sha512/224" | "sha512-224" | "sha512-224withrsaencryption" => {
|
|
type $type = ::sha2::Sha512_224;
|
|
$body
|
|
}
|
|
"rsa-sha512/256" | "sha512-256" | "sha512-256withrsaencryption" => {
|
|
type $type = ::sha2::Sha512_256;
|
|
$body
|
|
}
|
|
"rsa-sha3-224" | "id-rsassa-pkcs1-v1_5-with-sha3-224" | "sha3-224" => {
|
|
type $type = ::sha3::Sha3_224;
|
|
$body
|
|
}
|
|
"rsa-sha3-256" | "id-rsassa-pkcs1-v1_5-with-sha3-256" | "sha3-256" => {
|
|
type $type = ::sha3::Sha3_256;
|
|
$body
|
|
}
|
|
"rsa-sha3-384" | "id-rsassa-pkcs1-v1_5-with-sha3-384" | "sha3-384" => {
|
|
type $type = ::sha3::Sha3_384;
|
|
$body
|
|
}
|
|
"rsa-sha3-512" | "id-rsassa-pkcs1-v1_5-with-sha3-512" | "sha3-512" => {
|
|
type $type = ::sha3::Sha3_512;
|
|
$body
|
|
}
|
|
_ => $other,
|
|
}
|
|
};
|
|
}
|
|
|
|
pub(crate) use match_fixed_digest_with_oid;
|
|
|
|
pub enum Hash {
|
|
FixedSize(Box<dyn DynDigest>),
|
|
|
|
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_fixed_digest!(
|
|
algorithm_name,
|
|
fn <D>() {
|
|
let digest: D = Digest::new();
|
|
if let Some(length) = output_length {
|
|
if length != digest.output_size() {
|
|
return Err(generic_error(
|
|
"Output length mismatch for non-extendable algorithm",
|
|
));
|
|
}
|
|
}
|
|
FixedSize(Box::new(digest))
|
|
},
|
|
_ => {
|
|
return Err(generic_error(format!(
|
|
"Digest method not supported: {algorithm_name}"
|
|
)))
|
|
}
|
|
);
|
|
|
|
Ok(algorithm)
|
|
}
|
|
|
|
pub fn update(&mut self, data: &[u8]) {
|
|
match self {
|
|
FixedSize(context) => DynDigest::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 {
|
|
FixedSize(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 {
|
|
FixedSize(context) => {
|
|
if let Some(length) = output_length {
|
|
if length != context.output_size() {
|
|
return Err(generic_error(
|
|
"Output length mismatch for non-extendable algorithm",
|
|
));
|
|
}
|
|
}
|
|
FixedSize(context.box_clone())
|
|
}
|
|
|
|
Shake128(context, _) => Shake128(context.clone(), output_length),
|
|
Shake256(context, _) => Shake256(context.clone(), output_length),
|
|
};
|
|
Ok(hash)
|
|
}
|
|
|
|
pub fn get_hashes() -> Vec<&'static str> {
|
|
vec![
|
|
"RSA-MD5",
|
|
"RSA-RIPEMD160",
|
|
"RSA-SHA1",
|
|
"RSA-SHA1-2",
|
|
"RSA-SHA224",
|
|
"RSA-SHA256",
|
|
"RSA-SHA3-224",
|
|
"RSA-SHA3-256",
|
|
"RSA-SHA3-384",
|
|
"RSA-SHA3-512",
|
|
"RSA-SHA384",
|
|
"RSA-SHA512",
|
|
"RSA-SHA512/224",
|
|
"RSA-SHA512/256",
|
|
"RSA-SM3",
|
|
"blake2b512",
|
|
"blake2s256",
|
|
"id-rsassa-pkcs1-v1_5-with-sha3-224",
|
|
"id-rsassa-pkcs1-v1_5-with-sha3-256",
|
|
"id-rsassa-pkcs1-v1_5-with-sha3-384",
|
|
"id-rsassa-pkcs1-v1_5-with-sha3-512",
|
|
"md5",
|
|
"md5-sha1",
|
|
"md5WithRSAEncryption",
|
|
"ripemd",
|
|
"ripemd160",
|
|
"ripemd160WithRSA",
|
|
"rmd160",
|
|
"sha1",
|
|
"sha1WithRSAEncryption",
|
|
"sha224",
|
|
"sha224WithRSAEncryption",
|
|
"sha256",
|
|
"sha256WithRSAEncryption",
|
|
"sha3-224",
|
|
"sha3-256",
|
|
"sha3-384",
|
|
"sha3-512",
|
|
"sha384",
|
|
"sha384WithRSAEncryption",
|
|
"sha512",
|
|
"sha512-224",
|
|
"sha512-224WithRSAEncryption",
|
|
"sha512-256",
|
|
"sha512-256WithRSAEncryption",
|
|
"sha512WithRSAEncryption",
|
|
"shake128",
|
|
"shake256",
|
|
"sm3",
|
|
"sm3WithRSAEncryption",
|
|
"ssl3-md5",
|
|
"ssl3-sha1",
|
|
]
|
|
}
|
|
}
|