Skip to content
Merged
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
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,7 @@ rayon = "1.10"
regex = "1.10.4"
rlimit = "0.11.0"
rstest = "0.26.0"
rstest_reuse = "0.7.0"
rustc-hash = "2.1.1"
rust-ini = "0.21.0"
same-file = "1.0.6"
Expand Down Expand Up @@ -640,6 +641,7 @@ uucore = { workspace = true, features = [
walkdir.workspace = true
hex-literal = "1.0.0"
rstest.workspace = true
rstest_reuse.workspace = true

[target.'cfg(unix)'.dev-dependencies]
nix = { workspace = true, features = [
Expand Down
10 changes: 3 additions & 7 deletions src/uu/b2sum/src/b2sum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,14 @@ use clap::Command;

use uu_checksum_common::{standalone_checksum_app_with_length, standalone_with_length_main};

use uucore::checksum::{AlgoKind, calculate_blake2b_length_str};
use uucore::checksum::{AlgoKind, calculate_blake_length_str};
use uucore::error::UResult;
use uucore::translate;

#[uucore::main]
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
standalone_with_length_main(
AlgoKind::Blake2b,
uu_app(),
args,
calculate_blake2b_length_str,
)
let calculate_blake2b_length = |s: &str| calculate_blake_length_str(AlgoKind::Blake2b, s);
standalone_with_length_main(AlgoKind::Blake2b, uu_app(), args, calculate_blake2b_length)
}

#[inline]
Expand Down
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
8 changes: 5 additions & 3 deletions src/uu/cksum/src/cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use uu_checksum_common::{ChecksumCommand, checksum_main, default_checksum_app, o

use uucore::checksum::compute::OutputFormat;
use uucore::checksum::{
AlgoKind, ChecksumError, calculate_blake2b_length_str, sanitize_sha2_sha3_length_str,
AlgoKind, ChecksumError, calculate_blake_length_str, sanitize_sha2_sha3_length_str,
};
use uucore::error::UResult;
use uucore::hardware::{HasHardwareFeatures as _, SimdPolicy};
Expand Down Expand Up @@ -67,8 +67,10 @@ fn maybe_sanitize_length(
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),
// For BLAKE, if a length is provided, validate it.
(Some(algo @ (AlgoKind::Blake2b | AlgoKind::Blake3)), Some(len)) => {
calculate_blake_length_str(algo, len)
}

// For any other provided algorithm, check if length is 0.
// Otherwise, this is an error.
Expand Down
56 changes: 38 additions & 18 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)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the length is not validated here, but it is for the blake2b algo

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've addressed that in #11437

(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 @@ -293,7 +294,8 @@ impl SizedAlgoKind {
}
// [`calculate_blake2b_length`] expects a length in bits but we
// have a length in bytes.
(ak::Blake2b, Some(l)) => Ok(Self::Blake2b(calculate_blake2b_length_str(
(algo @ ak::Blake2b, Some(l)) => Ok(Self::Blake2b(calculate_blake_length_str(
algo,
&(8 * l).to_string(),
)?)),
(ak::Blake2b, None) => Ok(Self::Blake2b(None)),
Expand All @@ -310,11 +312,16 @@ 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) * 8
)
}
Self::Shake128(opt_bit_len) => format!(
"SHAKE128-{}",
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
Expand All @@ -339,7 +346,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 +357,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 +378,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 Expand Up @@ -486,20 +495,22 @@ pub fn digest_reader<T: Read>(
Ok((digest.result(), output_size))
}

/// Calculates the length of the digest.
pub fn calculate_blake2b_length_str(bit_length: &str) -> UResult<Option<usize>> {
/// Calculates the BYTE length of the digest.
pub fn calculate_blake_length_str(algo: AlgoKind, bit_length: &str) -> UResult<Option<usize>> {
debug_assert!(matches!(algo, AlgoKind::Blake2b | AlgoKind::Blake3));

// Blake2b's length is parsed in an u64.
match bit_length.parse::<usize>() {
Ok(0) => Ok(None),

// Error cases
Ok(n) if n > 512 => {
Ok(n) if n > 512 && algo == AlgoKind::Blake2b => {
show_error!("{}", ChecksumError::InvalidLength(bit_length.into()));
Err(ChecksumError::LengthTooBigForBlake("BLAKE2b".into()).into())
Err(ChecksumError::LengthTooBigForBlake(algo.to_uppercase().into()).into())
}
Err(e) if *e.kind() == IntErrorKind::PosOverflow => {
show_error!("{}", ChecksumError::InvalidLength(bit_length.into()));
Err(ChecksumError::LengthTooBigForBlake("BLAKE2b".into()).into())
Err(ChecksumError::LengthTooBigForBlake(algo.to_uppercase().into()).into())
}
Err(_) => Err(ChecksumError::InvalidLength(bit_length.into()).into()),

Expand Down Expand Up @@ -632,10 +643,19 @@ mod tests {

#[test]
fn test_calculate_blake2b_length() {
assert_eq!(calculate_blake2b_length_str("0").unwrap(), None);
assert!(calculate_blake2b_length_str("10").is_err());
assert!(calculate_blake2b_length_str("520").is_err());
assert_eq!(calculate_blake2b_length_str("512").unwrap(), None);
assert_eq!(calculate_blake2b_length_str("256").unwrap(), Some(32));
assert_eq!(
calculate_blake_length_str(AlgoKind::Blake2b, "0").unwrap(),
None
);
assert!(calculate_blake_length_str(AlgoKind::Blake2b, "10").is_err());
assert!(calculate_blake_length_str(AlgoKind::Blake2b, "520").is_err());
assert_eq!(
calculate_blake_length_str(AlgoKind::Blake2b, "512").unwrap(),
None
);
assert_eq!(
calculate_blake_length_str(AlgoKind::Blake2b, "256").unwrap(),
Some(32)
);
}
}
39 changes: 20 additions & 19 deletions src/uucore/src/lib/features/checksum/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,7 @@ fn identify_algo_name_and_length(
algo_name_input: Option<AlgoKind>,
last_algo: &mut Option<String>,
) -> Result<(AlgoKind, Option<usize>), LineCheckError> {
use AlgoKind as ak;
let algo_from_line = line_info.algo_name.clone().unwrap_or_default();
let Ok(line_algo) = AlgoKind::from_cksum(algo_from_line.to_lowercase()) else {
// Unknown algorithm
Expand All @@ -629,34 +630,33 @@ fn identify_algo_name_and_length(
match (algo_name_input, line_algo) {
(l, r) if l == r => (),
// Edge case for SHA2, which matches SHA(224|256|384|512)
(
AlgoKind::Sha2,
AlgoKind::Sha224 | AlgoKind::Sha256 | AlgoKind::Sha384 | AlgoKind::Sha512,
) => (),
(ak::Sha2, ak::Sha224 | ak::Sha256 | ak::Sha384 | ak::Sha512) => (),
_ => return Err(LineCheckError::ImproperlyFormatted),
}
}

let bytes = if let Some(bitlen) = line_info.algo_bit_len {
match line_algo {
AlgoKind::Blake2b if bitlen % 8 == 0 => Some(bitlen / 8),
AlgoKind::Sha2 | AlgoKind::Sha3 if [224, 256, 384, 512].contains(&bitlen) => {
Some(bitlen)
}
AlgoKind::Shake128 | AlgoKind::Shake256 => Some(bitlen),
ak::Blake2b | ak::Blake3 if bitlen % 8 == 0 => Some(bitlen / 8),
ak::Sha2 | ak::Sha3 if [224, 256, 384, 512].contains(&bitlen) => Some(bitlen),
ak::Shake128 | ak::Shake256 => Some(bitlen),
// Either
// the algo based line is provided with a bit length
// with an algorithm that does not support it (only Blake2B does).
// the algo based line is provided with a bit length with an
// algorithm that does not support it (only Blake2b, Blake3, sha2,
// and sha3 do).
//
// eg: MD5-128 (foo.txt) = fffffffff
// ^ This is illegal
// OR
// the given length is wrong because it's not a multiple of 8.
_ => return Err(LineCheckError::ImproperlyFormatted),
}
} else if line_algo == AlgoKind::Blake2b {
} else if line_algo == ak::Blake2b {
// Default length with BLAKE2b,
Some(64)
} else if line_algo == ak::Blake3 {
// Default length with BLAKE3,
Some(32)
} else {
None
};
Expand Down 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 All @@ -764,6 +764,7 @@ fn process_non_algo_based_line(
cli_algo_length: Option<usize>,
opts: ChecksumValidateOptions,
) -> Result<(), LineCheckError> {
use AlgoKind as ak;
let mut filename_to_check = line_info.filename.as_slice();
if filename_to_check.starts_with(b"*")
&& line_number == 0
Expand All @@ -778,16 +779,16 @@ fn process_non_algo_based_line(
// When a specific algorithm name is input, use it and use the provided
// bits except when dealing with blake2b, sha2 and sha3, where we will
// detect the length.
let (algo_kind, algo_byte_len) = match cli_algo_kind {
AlgoKind::Blake2b => (AlgoKind::Blake2b, Some(expected_checksum.len())),
algo @ (AlgoKind::Sha2 | AlgoKind::Sha3) => {
let algo_byte_len = match cli_algo_kind {
ak::Blake2b | ak::Blake3 => Some(expected_checksum.len()),
ak::Sha2 | ak::Sha3 => {
// multiplication by 8 to get the number of bits
(algo, Some(expected_checksum.len() * 8))
Some(expected_checksum.len() * 8)
}
_ => (cli_algo_kind, cli_algo_length),
_ => cli_algo_length,
};

let algo = SizedAlgoKind::from_unsized(algo_kind, algo_byte_len)?;
let algo = SizedAlgoKind::from_unsized(cli_algo_kind, algo_byte_len)?;

compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
}
Expand Down
37 changes: 30 additions & 7 deletions src/uucore/src/lib/features/sum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,25 +122,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
Loading
Loading