uucore: Make Shake* algorithms optionally accept a length

This commit is contained in:
Dorian Peron
2026-02-08 00:00:42 +01:00
committed by Dorian Péron
parent 280abcd619
commit 690b149b4b
4 changed files with 64 additions and 32 deletions
+2 -2
View File
@@ -57,7 +57,7 @@ macro_rules! bench_shake_algorithm {
let data = text_data::generate_by_size(100, 80);
bencher.bench(|| {
let mut shake = Shake128::new();
let mut shake = Shake128::with_output_bits(256);
shake.hash_update(&data);
// SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison
@@ -76,7 +76,7 @@ macro_rules! bench_shake_algorithm {
let data = text_data::generate_by_size(100, 80);
bencher.bench(|| {
let mut shake = Shake256::new();
let mut shake = Shake256::with_output_bits(512);
shake.hash_update(&data);
// SHAKE algorithms can output any length, use 256 bits (32 bytes) for meaningful comparison
+10
View File
@@ -100,6 +100,16 @@ fn maybe_sanitize_length(
sanitize_sha2_sha3_length_str(algo, s_len).map(Some)
}
// SHAKE128 and SHAKE256 algorithms optionally take a bit length. No
// validation is performed on this length, any value is valid. If the
// given length is not a multiple of 8, the last byte of the output
// will have its extra bits set to zero.
(Some(AlgoKind::Shake128 | AlgoKind::Shake256), Some(len)) => match len.parse::<usize>() {
Ok(0) => Ok(None),
Ok(l) => Ok(Some(l)),
Err(_) => Err(ChecksumError::InvalidLength(len.into()).into()),
},
// For BLAKE2b, if a length is provided, validate it.
(Some(AlgoKind::Blake2b), Some(len)) => calculate_blake2b_length_str(len),
+26 -21
View File
@@ -114,6 +114,9 @@ impl AlgoKind {
ALGORITHM_OPTIONS_SHA256 => Sha256,
ALGORITHM_OPTIONS_SHA384 => Sha384,
ALGORITHM_OPTIONS_SHA512 => Sha512,
ALGORITHM_OPTIONS_SHAKE128 => Shake128,
ALGORITHM_OPTIONS_SHAKE256 => Shake256,
_ => return Err(ChecksumError::UnknownAlgorithm(algo.as_ref().to_string()).into()),
})
}
@@ -247,8 +250,8 @@ pub enum SizedAlgoKind {
Sha3(ShaLength),
// Note: we store Blake2b's length as BYTES.
Blake2b(Option<usize>),
Shake128(usize),
Shake256(usize),
Shake128(Option<usize>),
Shake256(Option<usize>),
}
impl SizedAlgoKind {
@@ -280,8 +283,8 @@ impl SizedAlgoKind {
(ak::Sha1, _) => Ok(Self::Sha1),
(ak::Blake3, _) => Ok(Self::Blake3),
(ak::Shake128, Some(l)) => Ok(Self::Shake128(l)),
(ak::Shake256, Some(l)) => Ok(Self::Shake256(l)),
(ak::Shake128, l) => Ok(Self::Shake128(l)),
(ak::Shake256, l) => Ok(Self::Shake256(l)),
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l)?)),
(ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l)?)),
(algo @ (ak::Sha2 | ak::Sha3), None) => {
@@ -298,7 +301,6 @@ impl SizedAlgoKind {
(ak::Sha256, None) => Ok(Self::Sha2(ShaLength::Len256)),
(ak::Sha384, None) => Ok(Self::Sha2(ShaLength::Len384)),
(ak::Sha512, None) => Ok(Self::Sha2(ShaLength::Len512)),
(_, None) => Err(ChecksumError::LengthRequired(kind.to_uppercase().into()).into()),
}
}
@@ -341,27 +343,30 @@ impl SizedAlgoKind {
Self::Blake2b(len_opt) => Box::new(Blake2b::with_output_bytes(
len_opt.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE),
)),
Self::Shake128(_) => Box::new(Shake128::new()),
Self::Shake256(_) => Box::new(Shake256::new()),
Self::Shake128(len_opt) => Box::new(Shake128::with_output_bits(
len_opt.unwrap_or(Shake128::DEFAULT_BIT_SIZE),
)),
Self::Shake256(len_opt) => Box::new(Shake256::with_output_bits(
len_opt.unwrap_or(Shake256::DEFAULT_BIT_SIZE),
)),
}
}
pub fn bitlen(&self) -> usize {
use SizedAlgoKind::*;
match self {
Sysv => 512,
Bsd => 1024,
Crc => 256,
Crc32b => 32,
Md5 => 128,
Sm3 => 512,
Sha1 => 160,
Blake3 => 256,
Sha2(len) => len.as_usize(),
Sha3(len) => len.as_usize(),
Blake2b(len) => len.unwrap_or(512),
Shake128(len) => *len,
Shake256(len) => *len,
Self::Sysv => 512,
Self::Bsd => 1024,
Self::Crc => 256,
Self::Crc32b => 32,
Self::Md5 => 128,
Self::Sm3 => 512,
Self::Sha1 => 160,
Self::Blake3 => 256,
Self::Sha2(len) => len.as_usize(),
Self::Sha3(len) => len.as_usize(),
Self::Blake2b(len) => len.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE * 8),
Self::Shake128(len) => len.unwrap_or(Shake128::DEFAULT_BIT_SIZE),
Self::Shake256(len) => len.unwrap_or(Shake256::DEFAULT_BIT_SIZE),
}
}
pub fn is_legacy(&self) -> bool {
+26 -9
View File
@@ -377,27 +377,38 @@ macro_rules! impl_digest_common {
// Implements the Digest trait for sha2 / sha3 algorithms with variable output
macro_rules! impl_digest_shake {
($algo_type: ty, $output_bits: literal) => {
($algo_type: ty, $default_output_bits: literal) => {
impl $algo_type {
pub fn new() -> Self {
Self(Default::default())
pub const DEFAULT_BIT_SIZE: usize = $default_output_bits;
pub fn with_output_bits(bits: usize) -> Self {
Self {
digest: Default::default(),
bit_size: bits,
}
}
}
impl Digest for $algo_type {
fn hash_update(&mut self, input: &[u8]) {
digest::Update::update(&mut self.0, input);
digest::Update::update(&mut self.digest, input);
}
fn hash_finalize(&mut self, out: &mut [u8]) {
digest::ExtendableOutputReset::finalize_xof_reset_into(&mut self.0, out);
digest::ExtendableOutputReset::finalize_xof_reset_into(&mut self.digest, out);
// Remove the last bits if the requested length is not a multiple of 8.
let extra = self.output_bits() % 8;
if extra != 0 {
out[out.len() - 1] &= (1 << extra) - 1;
}
}
fn reset(&mut self) {
*self = Self::new();
*self = Self::with_output_bits(self.bit_size);
}
fn output_bits(&self) -> usize {
$output_bits
self.bit_size
}
fn result(&mut self) -> DigestOutput {
@@ -431,8 +442,14 @@ impl_digest_common!(Sha3_256, 256);
impl_digest_common!(Sha3_384, 384);
impl_digest_common!(Sha3_512, 512);
pub struct Shake128(sha3::Shake128);
pub struct Shake256(sha3::Shake256);
pub struct Shake128 {
digest: sha3::Shake128,
bit_size: usize,
}
pub struct Shake256 {
digest: sha3::Shake256,
bit_size: usize,
}
impl_digest_shake!(Shake128, 256);
impl_digest_shake!(Shake256, 512);