-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
fix b2sum, md5sum and hashsum regressions #8760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -17,6 +17,7 @@ use std::{ | |||||
| }; | ||||||
|
|
||||||
| use crate::{ | ||||||
| display::Quotable as DisplayQuotable, | ||||||
| error::{FromIo, UError, UResult, USimpleError}, | ||||||
| os_str_as_bytes, os_str_from_bytes, | ||||||
| quoting_style::{QuotingStyle, locale_aware_escape_name}, | ||||||
|
|
@@ -25,7 +26,7 @@ use crate::{ | |||||
| Blake2b, Blake3, Bsd, CRC32B, Crc, Digest, DigestWriter, Md5, Sha1, Sha3_224, Sha3_256, | ||||||
| Sha3_384, Sha3_512, Sha224, Sha256, Sha384, Sha512, Shake128, Shake256, Sm3, SysV, | ||||||
| }, | ||||||
| util_name, | ||||||
| translate, util_name, | ||||||
| }; | ||||||
| use thiserror::Error; | ||||||
|
|
||||||
|
|
@@ -46,14 +47,16 @@ pub const ALGORITHM_OPTIONS_BLAKE3: &str = "blake3"; | |||||
| pub const ALGORITHM_OPTIONS_SM3: &str = "sm3"; | ||||||
| pub const ALGORITHM_OPTIONS_SHAKE128: &str = "shake128"; | ||||||
| pub const ALGORITHM_OPTIONS_SHAKE256: &str = "shake256"; | ||||||
| pub const ALGORITHM_OPTIONS_SHA2: &str = "sha2"; | ||||||
|
|
||||||
| pub const SUPPORTED_ALGORITHMS: [&str; 16] = [ | ||||||
| pub const SUPPORTED_ALGORITHMS: [&str; 17] = [ | ||||||
| ALGORITHM_OPTIONS_SYSV, | ||||||
| ALGORITHM_OPTIONS_BSD, | ||||||
| ALGORITHM_OPTIONS_CRC, | ||||||
| ALGORITHM_OPTIONS_CRC32B, | ||||||
| ALGORITHM_OPTIONS_MD5, | ||||||
| ALGORITHM_OPTIONS_SHA1, | ||||||
| ALGORITHM_OPTIONS_SHA2, | ||||||
| ALGORITHM_OPTIONS_SHA3, | ||||||
| ALGORITHM_OPTIONS_SHA224, | ||||||
| ALGORITHM_OPTIONS_SHA256, | ||||||
|
|
@@ -221,6 +224,10 @@ pub enum ChecksumError { | |||||
| BitsRequiredForShake128, | ||||||
| #[error("--bits required for SHAKE256")] | ||||||
| BitsRequiredForShake256, | ||||||
| #[error("--bits required for SHA2")] | ||||||
| BitsRequiredForSha2, | ||||||
| #[error("Invalid output size for SHA2 (expected 224, 256, 384, or 512), got {0}")] | ||||||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i noticed that we aren't translating this file, i will fix it next |
||||||
| InvalidSha2Length(usize), | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For SHA3 we use
Suggested change
|
||||||
| #[error("unknown algorithm: clap should have prevented this case")] | ||||||
| UnknownAlgorithm, | ||||||
| #[error("length is not a multiple of 8")] | ||||||
|
|
@@ -278,6 +285,42 @@ pub fn create_sha3(bits: usize) -> UResult<HashAlgorithm> { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Create a SHA-2 `HashAlgorithm` based on the specified output size in bits. | ||||||
| /// | ||||||
| /// # Arguments | ||||||
| /// | ||||||
| /// * `bits` - The output size in bits for the SHA-2 algorithm. | ||||||
| /// | ||||||
| /// # Returns | ||||||
| /// | ||||||
| /// Returns a `UResult` with an `HashAlgorithm` or an `Err` if an unsupported | ||||||
| /// output size is provided. | ||||||
| pub fn create_sha2(bits: usize) -> UResult<HashAlgorithm> { | ||||||
| match bits { | ||||||
| 224 => Ok(HashAlgorithm { | ||||||
| name: ALGORITHM_OPTIONS_SHA224, | ||||||
| create_fn: Box::new(|| Box::new(Sha224::new())), | ||||||
| bits: 224, | ||||||
| }), | ||||||
| 256 => Ok(HashAlgorithm { | ||||||
| name: ALGORITHM_OPTIONS_SHA256, | ||||||
| create_fn: Box::new(|| Box::new(Sha256::new())), | ||||||
| bits: 256, | ||||||
| }), | ||||||
| 384 => Ok(HashAlgorithm { | ||||||
| name: ALGORITHM_OPTIONS_SHA384, | ||||||
| create_fn: Box::new(|| Box::new(Sha384::new())), | ||||||
| bits: 384, | ||||||
| }), | ||||||
| 512 => Ok(HashAlgorithm { | ||||||
| name: ALGORITHM_OPTIONS_SHA512, | ||||||
| create_fn: Box::new(|| Box::new(Sha512::new())), | ||||||
| bits: 512, | ||||||
| }), | ||||||
| _ => Err(ChecksumError::InvalidSha2Length(bits).into()), | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| #[allow(clippy::comparison_chain)] | ||||||
| fn print_cksum_report(res: &ChecksumResult) { | ||||||
| if res.bad_format == 1 { | ||||||
|
|
@@ -457,11 +500,14 @@ pub fn detect_algo(algo: &str, length: Option<usize>) -> UResult<HashAlgorithm> | |||||
| bits, | ||||||
| }) | ||||||
| } | ||||||
| ALGORITHM_OPTIONS_SHA2 => { | ||||||
| let bits = length.ok_or(ChecksumError::BitsRequiredForSha2)?; | ||||||
| create_sha2(bits) | ||||||
| } | ||||||
| _ if algo.starts_with("sha3") => { | ||||||
| let bits = length.ok_or(ChecksumError::BitsRequiredForSha3)?; | ||||||
| create_sha3(bits) | ||||||
| } | ||||||
|
|
||||||
| _ => Err(ChecksumError::UnknownAlgorithm.into()), | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -1185,22 +1231,47 @@ pub fn digest_reader<T: Read>( | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Validates and calculates the length of the digest from a string input. | ||||||
| /// This function handles very large numbers that might not fit in usize. | ||||||
| pub fn validate_blake2b_length_str(length_str: &str) -> UResult<Option<usize>> { | ||||||
| // First try to parse as u128 to handle very large numbers | ||||||
| match length_str.parse::<u128>() { | ||||||
| Ok(length_u128) => { | ||||||
| if length_u128 > usize::MAX as u128 { | ||||||
| // For very large numbers, always show the max length error | ||||||
| show_error!("invalid length: '{length_str}'"); | ||||||
| return Err(io::Error::new( | ||||||
| io::ErrorKind::InvalidInput, | ||||||
| "maximum digest length for 'BLAKE2b' is 512 bits", | ||||||
| ) | ||||||
| .into()); | ||||||
| } | ||||||
| let length = length_u128 as usize; | ||||||
| calculate_blake2b_length(length) | ||||||
| } | ||||||
| Err(_) => { | ||||||
| show_error!("invalid length: '{length_str}'"); | ||||||
| Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid length").into()) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Calculates the length of the digest. | ||||||
| pub fn calculate_blake2b_length(length: usize) -> UResult<Option<usize>> { | ||||||
| match length { | ||||||
| 0 => Ok(None), | ||||||
| n if n % 8 != 0 => { | ||||||
| show_error!("invalid length: \u{2018}{length}\u{2019}"); | ||||||
| Err(io::Error::new(io::ErrorKind::InvalidInput, "length is not a multiple of 8").into()) | ||||||
| } | ||||||
| n if n > 512 => { | ||||||
| show_error!("invalid length: \u{2018}{length}\u{2019}"); | ||||||
| show_error!("invalid length: '{length}'"); | ||||||
| Err(io::Error::new( | ||||||
| io::ErrorKind::InvalidInput, | ||||||
| "maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits", | ||||||
| "maximum digest length for 'BLAKE2b' is 512 bits", | ||||||
| ) | ||||||
| .into()) | ||||||
| } | ||||||
| n if n % 8 != 0 => { | ||||||
| show_error!("invalid length: '{length}'"); | ||||||
| Err(io::Error::new(io::ErrorKind::InvalidInput, "length is not a multiple of 8").into()) | ||||||
| } | ||||||
| n => { | ||||||
| // Divide by 8, as our blake2b implementation expects bytes instead of bits. | ||||||
| if n == 512 { | ||||||
|
|
@@ -1214,6 +1285,75 @@ pub fn calculate_blake2b_length(length: usize) -> UResult<Option<usize>> { | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| /// Validate BLAKE2b length with Fluent error messages | ||||||
| /// This function is used by utilities that need localized error messages | ||||||
| pub fn validate_blake2b_length(length_str: &str, utility_name: &str) -> UResult<Option<usize>> { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm struggling with this function as it duplicates the code of |
||||||
| // First try to parse as u128 to handle very large numbers | ||||||
| match length_str.parse::<u128>() { | ||||||
| Ok(length_u128) => { | ||||||
| if length_u128 > usize::MAX as u128 { | ||||||
| // For very large numbers, always show the max length error | ||||||
| // Use the original string to avoid precision issues | ||||||
| let error_key = format!("{}-error-invalid-length", utility_name); | ||||||
| let max_key = format!("{}-error-max-digest-length", utility_name); | ||||||
| show_error!( | ||||||
| "{}", | ||||||
| translate!(&error_key, "length" => DisplayQuotable::quote(length_str)) | ||||||
| ); | ||||||
| return Err(io::Error::new( | ||||||
| io::ErrorKind::InvalidInput, | ||||||
| translate!(&max_key, "algorithm" => "BLAKE2b", "max_bits" => 512), | ||||||
| ) | ||||||
| .into()); | ||||||
| } | ||||||
| let length = length_u128 as usize; | ||||||
| match length { | ||||||
| 0 => Ok(None), | ||||||
| n if n > 512 => { | ||||||
| let error_key = format!("{}-error-invalid-length", utility_name); | ||||||
| let max_key = format!("{}-error-max-digest-length", utility_name); | ||||||
| show_error!( | ||||||
| "{}", | ||||||
| translate!(&error_key, "length" => DisplayQuotable::quote(length_str)) | ||||||
| ); | ||||||
| Err(io::Error::new( | ||||||
| io::ErrorKind::InvalidInput, | ||||||
| translate!(&max_key, "algorithm" => "BLAKE2b", "max_bits" => 512), | ||||||
| ) | ||||||
| .into()) | ||||||
|
Comment on lines
+1313
to
+1323
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is almost a duplicate of the ones above, there might be a way to factorize it with closures ? |
||||||
| } | ||||||
| n if n % 8 != 0 => { | ||||||
| let error_key = format!("{}-error-invalid-length", utility_name); | ||||||
| show_error!( | ||||||
| "{}", | ||||||
| translate!(&error_key, "length" => DisplayQuotable::quote(length_str)) | ||||||
| ); | ||||||
| Err(io::Error::new( | ||||||
| io::ErrorKind::InvalidInput, | ||||||
| "length is not a multiple of 8", | ||||||
| ) | ||||||
| .into()) | ||||||
| } | ||||||
| n => { | ||||||
| // Divide by 8, as our blake2b implementation expects bytes instead of bits. | ||||||
| if n == 512 { | ||||||
| // When length is 512, it is blake2b's default. | ||||||
| // So, don't show it | ||||||
| Ok(None) | ||||||
| } else { | ||||||
| Ok(Some(n / 8)) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
| Err(_) => { | ||||||
| let error_key = format!("{}-error-invalid-length", utility_name); | ||||||
| show_error!("{}", translate!(&error_key, "length" => length_str.quote())); | ||||||
| Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid length").into()) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| pub fn unescape_filename(filename: &[u8]) -> (Vec<u8>, &'static str) { | ||||||
| let mut unescaped = Vec::with_capacity(filename.len()); | ||||||
| let mut byte_iter = filename.iter().peekable(); | ||||||
|
|
@@ -1339,6 +1479,18 @@ mod tests { | |||||
| detect_algo(ALGORITHM_OPTIONS_SHA512, None).unwrap().name, | ||||||
| ALGORITHM_OPTIONS_SHA512 | ||||||
| ); | ||||||
| assert_eq!( | ||||||
| detect_algo(ALGORITHM_OPTIONS_SHA2, Some(256)).unwrap().name, | ||||||
| ALGORITHM_OPTIONS_SHA256 | ||||||
| ); | ||||||
| assert_eq!( | ||||||
| detect_algo(ALGORITHM_OPTIONS_SHA2, Some(384)).unwrap().name, | ||||||
| ALGORITHM_OPTIONS_SHA384 | ||||||
| ); | ||||||
| assert_eq!( | ||||||
| detect_algo(ALGORITHM_OPTIONS_SHA2, Some(512)).unwrap().name, | ||||||
| ALGORITHM_OPTIONS_SHA512 | ||||||
| ); | ||||||
| assert_eq!( | ||||||
| detect_algo(ALGORITHM_OPTIONS_BLAKE2B, None).unwrap().name, | ||||||
| ALGORITHM_OPTIONS_BLAKE2B | ||||||
|
|
@@ -1369,6 +1521,7 @@ mod tests { | |||||
| assert_eq!(detect_algo("sha3_512", Some(512)).unwrap().name, "SHA3_512"); | ||||||
|
|
||||||
| assert!(detect_algo("sha3_512", None).is_err()); | ||||||
| assert!(detect_algo(ALGORITHM_OPTIONS_SHA2, None).is_err()); | ||||||
| } | ||||||
|
|
||||||
| #[test] | ||||||
|
|
||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A detail: I would move this line up to line 39, so that it is between the consts for SHA1 and SHA3.