diff --git a/src/uucore/src/lib/features/checksum/mod.rs b/src/uucore/src/lib/features/checksum/mod.rs index e6145d374..d18e2adaa 100644 --- a/src/uucore/src/lib/features/checksum/mod.rs +++ b/src/uucore/src/lib/features/checksum/mod.rs @@ -200,6 +200,22 @@ impl AlgoKind { use AlgoKind::*; matches!(self, Sysv | Bsd | Crc | Crc32b) } + + /// When checking untagged format lines, non-XOF non-legacy algorithms + /// should report "improperly formatted lines" if the digest length isn't + /// equivalent to this. + pub fn expected_digest_bit_len(self) -> Option { + match self { + Self::Md5 => Some(Md5::BIT_SIZE), + Self::Sm3 => Some(Sm3::BIT_SIZE), + Self::Sha1 => Some(Sha1::BIT_SIZE), + Self::Sha224 => Some(Sha224::BIT_SIZE), + Self::Sha256 => Some(Sha256::BIT_SIZE), + Self::Sha384 => Some(Sha384::BIT_SIZE), + Self::Sha512 => Some(Sha512::BIT_SIZE), + _ => None, + } + } } /// Holds a length for a SHA2 of SHA3 algorithm kind. diff --git a/src/uucore/src/lib/features/checksum/validate.rs b/src/uucore/src/lib/features/checksum/validate.rs index fa51539fe..bb47ff6c9 100644 --- a/src/uucore/src/lib/features/checksum/validate.rs +++ b/src/uucore/src/lib/features/checksum/validate.rs @@ -15,7 +15,7 @@ use std::io::{self, BufReader, Read, Write, stderr, stdin}; use os_display::Quotable; use crate::checksum::{ - AlgoKind, BlakeLength, ChecksumError, ReadingMode, SizedAlgoKind, digest_reader, + AlgoKind, BlakeLength, ChecksumError, ReadingMode, ShaLength, SizedAlgoKind, digest_reader, parse_blake_length, unescape_filename, }; use crate::error::{FromIo, UError, UIoError, UResult, USimpleError}; @@ -490,7 +490,7 @@ impl LineInfo { } /// Extract the expected digest from the checksum string and decode it -fn get_raw_expected_digest(checksum: &str, byte_len_hint: Option) -> Option> { +fn get_raw_expected_digest(checksum: &str, bit_len_hint: Option) -> Option> { // If the length of the digest is not a multiple of 2, then it must be // improperly formatted (1 byte is 2 hex digits, and base64 strings should // always be a multiple of 4). @@ -498,6 +498,8 @@ fn get_raw_expected_digest(checksum: &str, byte_len_hint: Option) -> Opti return None; } + let byte_len_hint = bit_len_hint.map(|n| n.div_ceil(8)); + let checks_hint = |len| byte_len_hint.is_none_or(|hint| hint == len); // If the length of the string matches the one to be expected (in case it's @@ -741,23 +743,23 @@ fn process_algo_based_line( ) -> Result<(), LineCheckError> { let filename_to_check = line_info.filename.as_slice(); - let (algo_kind, algo_byte_len) = - identify_algo_name_and_length(line_info, cli_algo_kind, last_algo)?; + let (algo_kind, algo_len) = identify_algo_name_and_length(line_info, cli_algo_kind, last_algo)?; // 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 | 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)), + let digest_bit_length_hint = match (algo_kind, algo_len) { + (AlgoKind::Blake2b | AlgoKind::Blake3, Some(byte_len)) => Some(byte_len * 8), + (AlgoKind::Shake128 | AlgoKind::Shake256, Some(bit_len)) => Some(bit_len), + (AlgoKind::Shake128, None) => Some(sum::Shake128::DEFAULT_BIT_SIZE), + (AlgoKind::Shake256, None) => Some(sum::Shake256::DEFAULT_BIT_SIZE), _ => None, }; - let expected_checksum = get_raw_expected_digest(&line_info.checksum, digest_char_length_hint) + let expected_checksum = get_raw_expected_digest(&line_info.checksum, digest_bit_length_hint) .ok_or(LineCheckError::ImproperlyFormatted)?; - let algo = SizedAlgoKind::from_unsized(algo_kind, algo_byte_len)?; + let algo = SizedAlgoKind::from_unsized(algo_kind, algo_len) + .map_err(|_| LineCheckError::ImproperlyFormatted)?; compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts) } @@ -779,7 +781,9 @@ fn process_non_algo_based_line( // Remove the leading asterisk if present - only for the first line filename_to_check = &filename_to_check[1..]; } - let expected_checksum = get_raw_expected_digest(&line_info.checksum, None) + + let expected_digest_sum = cli_algo_kind.expected_digest_bit_len(); + let expected_checksum = get_raw_expected_digest(&line_info.checksum, expected_digest_sum) .ok_or(LineCheckError::ImproperlyFormatted)?; // When a specific algorithm name is input, use it and use the provided @@ -789,7 +793,11 @@ fn process_non_algo_based_line( ak::Blake2b | ak::Blake3 => Some(expected_checksum.len()), ak::Sha2 | ak::Sha3 => { // multiplication by 8 to get the number of bits - Some(expected_checksum.len() * 8) + Some( + ShaLength::try_from(expected_checksum.len() * 8) + .map_err(|_| LineCheckError::ImproperlyFormatted)? + .as_usize(), + ) } _ => cli_algo_length, }; diff --git a/src/uucore/src/lib/features/sum.rs b/src/uucore/src/lib/features/sum.rs index 5c998cd07..7a9f1a5ce 100644 --- a/src/uucore/src/lib/features/sum.rs +++ b/src/uucore/src/lib/features/sum.rs @@ -176,6 +176,10 @@ impl Digest for Blake3 { #[derive(Default)] pub struct Sm3(sm3::Sm3); +impl Sm3 { + pub const BIT_SIZE: usize = 256; +} + impl Digest for Sm3 { fn hash_update(&mut self, input: &[u8]) { ::update(&mut self.0, input); @@ -190,7 +194,7 @@ impl Digest for Sm3 { } fn output_bits(&self) -> usize { - 256 + Self::BIT_SIZE } } @@ -367,6 +371,9 @@ impl Digest for SysV { // Implements the Digest trait for sha2 / sha3 algorithms with fixed output macro_rules! impl_digest_common { ($algo_type: ty, $size: literal) => { + impl $algo_type { + pub const BIT_SIZE: usize = $size; + } impl Default for $algo_type { fn default() -> Self { Self(Default::default()) @@ -386,7 +393,7 @@ macro_rules! impl_digest_common { } fn output_bits(&self) -> usize { - $size + Self::BIT_SIZE } } };