Thanks to visit codestin.com
Credit goes to github.com

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: 2 additions & 0 deletions src/uu/cksum/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ cksum-help-zero = end each output line with NUL, not newline, and disable file n
# Error messages
cksum-error-is-directory = { $file }: Is a directory
cksum-error-failed-to-read-input = failed to read input
cksum-error-invalid-length = invalid length: { $length }
cksum-error-max-digest-length = maximum digest length for '{ $algorithm }' is { $max_bits } bits
2 changes: 2 additions & 0 deletions src/uu/cksum/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,5 @@ cksum-help-zero = terminer chaque ligne de sortie avec NUL, pas un saut de ligne
# Messages d'erreur
cksum-error-is-directory = { $file } : Est un répertoire
cksum-error-failed-to-read-input = échec de la lecture de l'entrée
cksum-error-invalid-length = longueur invalide : { $length }
cksum-error-max-digest-length = longueur maximale de condensé pour '{ $algorithm }' est { $max_bits } bits
27 changes: 19 additions & 8 deletions src/uu/cksum/src/cksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@
// spell-checker:ignore (ToDO) fname, algo

use clap::builder::ValueParser;
use clap::{Arg, ArgAction, Command, value_parser};
use clap::{Arg, ArgAction, Command};
use std::ffi::{OsStr, OsString};
use std::fs::File;
use std::io::{BufReader, Read, Write, stdin, stdout};
use std::iter;
use std::path::Path;
use uucore::checksum::{
ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC,
ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, ChecksumError, ChecksumOptions,
ChecksumVerbose, SUPPORTED_ALGORITHMS, calculate_blake2b_length, detect_algo, digest_reader,
perform_checksum_validation,
ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SHA2, ALGORITHM_OPTIONS_SYSV, ChecksumError,
ChecksumOptions, ChecksumVerbose, SUPPORTED_ALGORITHMS, detect_algo, digest_reader,
perform_checksum_validation, validate_blake2b_length,
};
use uucore::translate;

Expand Down Expand Up @@ -250,12 +250,23 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
}
};

let input_length = matches.get_one::<usize>(options::LENGTH);
let input_length = matches.get_one::<String>(options::LENGTH);

let length = match input_length {
Some(length) => {
Some(length_str) => {
if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
calculate_blake2b_length(*length)?
validate_blake2b_length(length_str, "cksum")?
} else if algo_name == ALGORITHM_OPTIONS_SHA2
|| algo_name.starts_with("sha3")
|| algo_name == "shake128"
|| algo_name == "shake256"
{
// Parse length for sha2, sha3, and shake algorithms
Some(
length_str
.parse::<usize>()
.map_err(|_| ChecksumError::InvalidLength)?,
)
} else {
return Err(ChecksumError::LengthOnlyForBlake2b.into());
}
Expand Down Expand Up @@ -378,7 +389,7 @@ pub fn uu_app() -> Command {
.arg(
Arg::new(options::LENGTH)
.long(options::LENGTH)
.value_parser(value_parser!(usize))
.value_parser(ValueParser::string())
.short('l')
.help(translate!("cksum-help-length"))
.action(ArgAction::Set),
Expand Down
2 changes: 2 additions & 0 deletions src/uu/hashsum/locales/en-US.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,5 @@ hashsum-help-b3sum = work with BLAKE3

# Error messages
hashsum-error-failed-to-read-input = failed to read input
hashsum-error-invalid-length = invalid length: { $length }
hashsum-error-max-digest-length = maximum digest length for '{ $algorithm }' is { $max_bits } bits
2 changes: 2 additions & 0 deletions src/uu/hashsum/locales/fr-FR.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ hashsum-help-b3sum = travailler avec BLAKE3

# Messages d'erreur
hashsum-error-failed-to-read-input = échec de la lecture de l'entrée
hashsum-error-invalid-length = longueur invalide : { $length }
hashsum-error-max-digest-length = longueur maximale de condensé pour '{ $algorithm }' est { $max_bits } bits
11 changes: 5 additions & 6 deletions src/uu/hashsum/src/hashsum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

use clap::ArgAction;
use clap::builder::ValueParser;
use clap::value_parser;
use clap::{Arg, ArgMatches, Command};
use std::ffi::{OsStr, OsString};
use std::fs::File;
Expand All @@ -19,12 +18,12 @@ use uucore::checksum::ChecksumError;
use uucore::checksum::ChecksumOptions;
use uucore::checksum::ChecksumVerbose;
use uucore::checksum::HashAlgorithm;
use uucore::checksum::calculate_blake2b_length;
use uucore::checksum::create_sha3;
use uucore::checksum::detect_algo;
use uucore::checksum::digest_reader;
use uucore::checksum::escape_filename;
use uucore::checksum::perform_checksum_validation;
use uucore::checksum::validate_blake2b_length;
use uucore::error::{FromIo, UResult};
use uucore::format_usage;
use uucore::sum::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256};
Expand Down Expand Up @@ -185,14 +184,14 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// least somewhat better from a user's perspective.
let matches = uucore::clap_localization::handle_clap_result(command, args)?;

let input_length: Option<&usize> = if binary_name == "b2sum" {
matches.get_one::<usize>(options::LENGTH)
let input_length: Option<&String> = if binary_name == "b2sum" {
matches.get_one::<String>(options::LENGTH)
} else {
None
};

let length = match input_length {
Some(length) => calculate_blake2b_length(*length)?,
Some(length_str) => validate_blake2b_length(length_str, "hashsum")?,
None => None,
};

Expand Down Expand Up @@ -427,7 +426,7 @@ fn uu_app_opt_length(command: Command) -> Command {
command.arg(
Arg::new(options::LENGTH)
.long(options::LENGTH)
.value_parser(value_parser!(usize))
.value_parser(ValueParser::string())
.short('l')
.help(translate!("hashsum-help-length"))
.overrides_with(options::LENGTH)
Expand Down
171 changes: 162 additions & 9 deletions src/uucore/src/lib/features/checksum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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;

Expand All @@ -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";
Copy link
Contributor

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.


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,
Expand Down Expand Up @@ -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}")]
Copy link
Contributor Author

Choose a reason for hiding this comment

The 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),
Copy link
Contributor

Choose a reason for hiding this comment

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

For SHA3 we use InvalidOutputSizeForSha3, and so for consistency reasons I would use a similar name:

Suggested change
InvalidSha2Length(usize),
InvalidOutputSizeForSha2(usize),

#[error("unknown algorithm: clap should have prevented this case")]
UnknownAlgorithm,
#[error("length is not a multiple of 8")]
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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()),
}
}
Expand Down Expand Up @@ -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 {
Expand All @@ -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>> {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm struggling with this function as it duplicates the code of validate_blake2b_length_str and calculate_blake2b_length. And thus it makes it easy to introduce sync issues in the future.

// 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
Copy link
Collaborator

Choose a reason for hiding this comment

The 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();
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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]
Expand Down
Loading
Loading