Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/uu/cksum/benches/cksum_bench.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ bench_algorithm!(cksum_sha224, "sha224");
bench_algorithm!(cksum_sha256, "sha256");
bench_algorithm!(cksum_sha384, "sha384");
bench_algorithm!(cksum_sha512, "sha512");
// broken. benchmarking error messages issues/10002 bench_algorithm!(cksum_blake3, "blake3");
bench_algorithm!(cksum_blake3, "blake3");
bench_shake_algorithm!(cksum_shake128, "shake128", Shake128);
bench_shake_algorithm!(cksum_shake256, "shake256", Shake256);

Expand Down
13 changes: 8 additions & 5 deletions src/uu/cksum/src/cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,14 @@ fn maybe_sanitize_length(
// 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 BLAKE3, the length is interpreted as a byte length.
(Some(AlgoKind::Shake128 | AlgoKind::Shake256 | AlgoKind::Blake3), 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),
Expand Down
19 changes: 12 additions & 7 deletions src/uucore/src/lib/features/checksum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,8 @@ impl AlgoKind {
ALGORITHM_OPTIONS_SHA384 => Sha384,
ALGORITHM_OPTIONS_SHA512 => Sha512,

// Extensions not in GNU as of version 9.10
ALGORITHM_OPTIONS_BLAKE3 => Blake3,
ALGORITHM_OPTIONS_SHAKE128 => Shake128,
ALGORITHM_OPTIONS_SHAKE256 => Shake256,
_ => return Err(ChecksumError::UnknownAlgorithm(algo.as_ref().to_string()).into()),
Expand Down Expand Up @@ -245,11 +247,11 @@ pub enum SizedAlgoKind {
Md5,
Sm3,
Sha1,
Blake3,
Sha2(ShaLength),
Sha3(ShaLength),
// Note: we store Blake2b's length as BYTES.
// Note: we store Blake*'s length as BYTES.
Blake2b(Option<usize>),
Blake3(Option<usize>),
// Shake* length are stored in bits.
Shake128(Option<usize>),
Shake256(Option<usize>),
Expand All @@ -267,7 +269,6 @@ impl SizedAlgoKind {
| ak::Md5
| ak::Sm3
| ak::Sha1
| ak::Blake3
| ak::Sha224
| ak::Sha256
| ak::Sha384
Expand All @@ -282,8 +283,8 @@ impl SizedAlgoKind {
(ak::Md5, _) => Ok(Self::Md5),
(ak::Sm3, _) => Ok(Self::Sm3),
(ak::Sha1, _) => Ok(Self::Sha1),
(ak::Blake3, _) => Ok(Self::Blake3),

(ak::Blake3, l) => Ok(Self::Blake3(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)?)),
Expand All @@ -310,11 +311,13 @@ impl SizedAlgoKind {
Self::Md5 => "MD5".into(),
Self::Sm3 => "SM3".into(),
Self::Sha1 => "SHA1".into(),
Self::Blake3 => "BLAKE3".into(),
Self::Sha2(len) => format!("SHA{}", len.as_usize()),
Self::Sha3(len) => format!("SHA3-{}", len.as_usize()),
Self::Blake2b(Some(byte_len)) => format!("BLAKE2b-{}", byte_len * 8),
Self::Blake2b(None) => "BLAKE2b".into(),
Self::Blake3(byte_len) => {
format!("BLAKE3-{}", byte_len.unwrap_or(Blake3::DEFAULT_BYTE_SIZE))
}
Self::Shake128(opt_bit_len) => format!(
"SHAKE128-{}",
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
Expand All @@ -339,7 +342,6 @@ impl SizedAlgoKind {
Self::Md5 => Box::new(Md5::default()),
Self::Sm3 => Box::new(Sm3::default()),
Self::Sha1 => Box::new(Sha1::default()),
Self::Blake3 => Box::new(Blake3::default()),
Self::Sha2(Len224) => Box::new(Sha224::default()),
Self::Sha2(Len256) => Box::new(Sha256::default()),
Self::Sha2(Len384) => Box::new(Sha384::default()),
Expand All @@ -351,6 +353,9 @@ impl SizedAlgoKind {
Self::Blake2b(len_opt) => {
Box::new(len_opt.map(Blake2b::with_output_bytes).unwrap_or_default())
}
Self::Blake3(len_opt) => {
Box::new(len_opt.map(Blake3::with_output_bytes).unwrap_or_default())
}
Self::Shake128(len_opt) => {
Box::new(len_opt.map(Shake128::with_output_bits).unwrap_or_default())
}
Expand All @@ -369,7 +374,7 @@ impl SizedAlgoKind {
Self::Md5 => 128,
Self::Sm3 => 512,
Self::Sha1 => 160,
Self::Blake3 => 256,
Self::Blake3(len) => len.unwrap_or(Blake3::DEFAULT_BYTE_SIZE) * 8,
Self::Sha2(len) => len.as_usize(),
Self::Sha3(len) => len.as_usize(),
Self::Blake2b(len) => len.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE * 8),
Expand Down
2 changes: 1 addition & 1 deletion src/uucore/src/lib/features/checksum/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -741,7 +741,7 @@ fn process_algo_based_line(
// If the digest bitlen is known, we can check the format of the expected
// checksum with it.
let digest_char_length_hint = match (algo_kind, algo_byte_len) {
(AlgoKind::Blake2b, Some(byte_len)) => Some(byte_len),
(AlgoKind::Blake2b | AlgoKind::Blake3, Some(byte_len)) => Some(byte_len),
(AlgoKind::Shake128 | AlgoKind::Shake256, Some(bit_len)) => Some(bit_len.div_ceil(8)),
(AlgoKind::Shake128, None) => Some(sum::Shake128::DEFAULT_BIT_SIZE.div_ceil(8)),
(AlgoKind::Shake256, None) => Some(sum::Shake256::DEFAULT_BIT_SIZE.div_ceil(8)),
Expand Down
40 changes: 32 additions & 8 deletions src/uucore/src/lib/features/sum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,8 @@ pub trait Digest {
}

fn result(&mut self) -> DigestOutput {
let mut buf: Vec<u8> = vec![0; self.output_bytes()];
let out_bytes: usize = self.output_bytes();
let mut buf: Vec<u8> = vec![0; out_bytes];
self.hash_finalize(&mut buf);
DigestOutput::Vec(buf)
}
Expand Down Expand Up @@ -122,25 +123,48 @@ impl Digest for Blake2b {
}
}

#[derive(Default)]
pub struct Blake3(blake3::Hasher);
pub struct Blake3 {
digest: blake3::Hasher,
byte_size: usize,
}

impl Blake3 {
/// Default length for the BLAKE3 digest in bytes.
pub const DEFAULT_BYTE_SIZE: usize = 32;

pub fn with_output_bytes(output_bytes: usize) -> Self {
Self {
digest: blake3::Hasher::new(),
byte_size: output_bytes,
}
}
}

impl Default for Blake3 {
fn default() -> Self {
Self {
digest: blake3::Hasher::default(),
byte_size: Self::DEFAULT_BYTE_SIZE,
}
}
}

impl Digest for Blake3 {
fn hash_update(&mut self, input: &[u8]) {
self.0.update(input);
self.digest.update(input);
}

fn hash_finalize(&mut self, out: &mut [u8]) {
let hash_result = &self.0.finalize();
out.copy_from_slice(hash_result.as_bytes());
let mut hash_result = self.digest.finalize_xof();
hash_result.fill(out);
}

fn reset(&mut self) {
*self = Self::default();
*self = Self::with_output_bytes(self.output_bytes());
}

fn output_bits(&self) -> usize {
256
self.byte_size * 8
}
}

Expand Down
59 changes: 59 additions & 0 deletions tests/by-util/test_cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3287,3 +3287,62 @@ fn test_check_shake256_no_length() {
.fails()
.stderr_only("cksum: 'standard input': no properly formatted checksum lines found\n");
}

#[rstest]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9"
)]
fn test_blake3b_no_length(#[case] input: &[u8], #[case] expected: &str) {
new_ucmd!()
.arg("-a")
.arg("blake3")
.pipe_in(input)
.succeeds()
.stdout_only(format!("BLAKE3-32 (-) = {expected}\n"));
}

#[rstest]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
0
)]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9",
32
)]
#[case(
b"foo",
"04e0bb39f30b1a3feb89f536c93be15055482df748674b00d26e5a75777702e9791074b7511b59d31c71c62f5a745689fa6c",
50
)]
#[case(b"foo", "04e0bb39f3", 5)]
#[case(b"foo", "04e0", 2)]
#[case(b"foo", "04", 1)]
fn test_blake3b(#[case] input: &[u8], #[case] expected: &str, #[case] length: usize) {
new_ucmd!()
.arg("-a")
.arg("blake3")
.args(&["-l", &length.to_string()])
.pipe_in(input)
.succeeds()
.stdout_only(format!(
"BLAKE3-{} (-) = {expected}\n",
match length {
0 => "32".to_string(),
i => i.to_string(),
}
));

// with --untagged
new_ucmd!()
.arg("-a")
.arg("blake3")
.arg("--untagged")
.args(&["-l", &length.to_string()])
.pipe_in(input)
.succeeds()
.stdout_only(format!("{expected} -\n"));
}
Loading