From a95633e520b62451f4139df3e906329d8b9b31a1 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Mon, 27 Oct 2025 13:46:38 +0100 Subject: [PATCH 1/4] util(cksum): Fix gnu cksum-c.sh --- src/uucore/src/lib/features/checksum.rs | 36 +++++++++++----- tests/by-util/test_cksum.rs | 55 +++++++++++++++++++++++++ 2 files changed, 81 insertions(+), 10 deletions(-) diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 30eccc254..e59054561 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -837,25 +837,41 @@ fn identify_algo_name_and_length( last_algo: &mut Option, ) -> Result<(String, Option), LineCheckError> { let algo_from_line = line_info.algo_name.clone().unwrap_or_default(); - let algorithm = algo_from_line.to_lowercase(); + let line_algo = algo_from_line.to_lowercase(); *last_algo = Some(algo_from_line); - // check if we are called with XXXsum (example: md5sum) but we detected a different algo parsing the file - // (for example SHA1 (f) = d...) + // check if we are called with XXXsum (example: md5sum) but we detected a + // different algo parsing the file (for example SHA1 (f) = d...) + // // Also handle the case cksum -s sm3 but the file contains other formats - if algo_name_input.is_some() && algo_name_input != Some(&algorithm) { - return Err(LineCheckError::ImproperlyFormatted); + if let Some(algo_name_input) = algo_name_input { + match (algo_name_input, line_algo.as_str()) { + (l, r) if l == r => (), + // Edge case for SHA2, which matches SHA(224|256|384|512) + ( + ALGORITHM_OPTIONS_SHA2, + ALGORITHM_OPTIONS_SHA224 + | ALGORITHM_OPTIONS_SHA256 + | ALGORITHM_OPTIONS_SHA384 + | ALGORITHM_OPTIONS_SHA512, + ) => (), + _ => return Err(LineCheckError::ImproperlyFormatted), + } } - if !SUPPORTED_ALGORITHMS.contains(&algorithm.as_str()) { + if !SUPPORTED_ALGORITHMS.contains(&line_algo.as_str()) { // Not supported algo, leave early return Err(LineCheckError::ImproperlyFormatted); } let bytes = if let Some(bitlen) = line_info.algo_bit_len { - match algorithm.as_str() { + match line_algo.as_str() { ALGORITHM_OPTIONS_BLAKE2B if bitlen % 8 == 0 => Some(bitlen / 8), - ALGORITHM_OPTIONS_SHA3 if [224, 256, 384, 512].contains(&bitlen) => Some(bitlen), + ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3 + if [224, 256, 384, 512].contains(&bitlen) => + { + Some(bitlen) + } // Either // the algo based line is provided with a bit length // with an algorithm that does not support it (only Blake2B does). @@ -866,14 +882,14 @@ fn identify_algo_name_and_length( // the given length is wrong because it's not a multiple of 8. _ => return Err(LineCheckError::ImproperlyFormatted), } - } else if algorithm == ALGORITHM_OPTIONS_BLAKE2B { + } else if line_algo == ALGORITHM_OPTIONS_BLAKE2B { // Default length with BLAKE2b, Some(64) } else { None }; - Ok((algorithm, bytes)) + Ok((line_algo, bytes)) } /// Given a filename and an algorithm, compute the digest and compare it with diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index e94303a75..afed3d0fe 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -7,6 +7,7 @@ use uutests::at_and_ucmd; use uutests::new_ucmd; use uutests::util::TestScenario; +use uutests::util::log_info; use uutests::util_name; const ALGOS: [&str; 11] = [ @@ -433,6 +434,60 @@ fn test_check_untagged_sha2_multiple_files() { } } +#[test] +fn test_check_sha2_tagged_variant() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + at.touch("f"); + + // SHA2-xxx is an alias to SHAxxx we don't output but we still recognize. + let checksum_lines = [ + ( + "SHA224", + "SHA2-224", + "(f) = d14a028c2a3a2bc9476102bb288234c415a2b01f828ea62ac5b3e42f", + ), + ( + "SHA256", + "SHA2-256", + "(f) = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", + ), + ( + "SHA384", + "SHA2-384", + "(f) = 38b060a751ac96384cd9327eb1b1e36a21fdb71114be07434c0cc7bf63f6e1da274edebfe76f65fbd51ad2f14898b95b", + ), + ( + "SHA512", + "SHA2-512", + "(f) = cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e", + ), + ]; + + for (basic, variant, digest) in checksum_lines { + let stdin = format!("{basic} {digest}"); + log_info("stdin is: ", &stdin); + scene + .ucmd() + .arg("--check") + .arg("--algorithm=sha2") + .pipe_in(stdin) + .succeeds() + .stdout_is("f: OK\n"); + + // Check that the variant works the same + let stdin = format!("{variant} {digest}"); + log_info("stdin is: ", &stdin); + scene + .ucmd() + .arg("--check") + .arg("--algorithm=sha2") + .pipe_in(stdin) + .succeeds() + .stdout_is("f: OK\n"); + } +} + #[test] fn test_sha3_wrong_length() { for l in [0, 13, 819_111_123] { From 69ce42c3cb242b66f91e12d30bb235af5bbe1382 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Tue, 28 Oct 2025 00:50:16 +0100 Subject: [PATCH 2/4] test(cksum): add tests for --length, refactor tests --- tests/by-util/test_cksum.rs | 112 ++++++++++++++---- .../cksum/length_larger_than_512.expected | 4 +- 2 files changed, 92 insertions(+), 24 deletions(-) diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index afed3d0fe..34cc09ab0 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -295,15 +295,79 @@ fn test_untagged_algorithm_stdin() { } #[test] -fn test_sha2_wrong_length() { - for l in [0, 13, 819_111_123] { +fn test_sha_length_invalid() { + for algo in ["sha2", "sha3"] { + for l in ["0", "00", "13", "56", "99999999999999999999999999"] { + new_ucmd!() + .arg("--algorithm") + .arg(algo) + .arg("--length") + .arg(l) + .arg("/dev/null") + .fails_with_code(1) + .no_stdout() + .stderr_contains(format!("invalid length: '{l}'")) + .stderr_contains(format!( + "digest length for '{}' must be 224, 256, 384, or 512", + algo.to_ascii_uppercase() + )); + + // Also fails with --check + new_ucmd!() + .arg("--algorithm") + .arg(algo) + .arg("--length") + .arg(l) + .arg("/dev/null") + .arg("--check") + .fails_with_code(1) + .no_stdout() + .stderr_contains(format!("invalid length: '{l}'")) + .stderr_contains(format!( + "digest length for '{}' must be 224, 256, 384, or 512", + algo.to_ascii_uppercase() + )); + } + + // Different error for NaNs + for l in ["512x", "x512", "512x512"] { + new_ucmd!() + .arg("--algorithm") + .arg(algo) + .arg("--length") + .arg(l) + .arg("/dev/null") + .fails_with_code(1) + .no_stdout() + .stderr_contains(format!("invalid length: '{l}'")); + + // Also fails with --check + new_ucmd!() + .arg("--algorithm") + .arg(algo) + .arg("--length") + .arg(l) + .arg("/dev/null") + .arg("--check") + .fails_with_code(1) + .no_stdout() + .stderr_contains(format!("invalid length: '{l}'")); + } + } +} + +#[test] +fn test_sha_missing_length() { + for algo in ["sha2", "sha3"] { new_ucmd!() - .arg("--algorithm=sha2") - .arg(format!("--length={l}")) + .arg("--algorithm") + .arg(algo) .arg("lorem_ipsum.txt") .fails_with_code(1) .no_stdout() - .stderr_contains(format!("invalid length: '{l}'")); + .stderr_contains(format!( + "--algorithm={algo} requires specifying --length 224, 256, 384, or 512" + )); } } @@ -488,19 +552,6 @@ fn test_check_sha2_tagged_variant() { } } -#[test] -fn test_sha3_wrong_length() { - for l in [0, 13, 819_111_123] { - new_ucmd!() - .arg("--algorithm=sha3") - .arg(format!("--length={l}")) - .arg("lorem_ipsum.txt") - .fails_with_code(1) - .no_stdout() - .stderr_contains(format!("invalid length: '{l}'")); - } -} - #[test] fn test_sha3_single_file() { for l in SHA_LENGTHS { @@ -708,7 +759,7 @@ fn test_length_not_supported() { } #[test] -fn test_length() { +fn test_blake2b_length() { new_ucmd!() .arg("--length=16") .arg("--algorithm=blake2b") @@ -721,7 +772,7 @@ fn test_length() { } #[test] -fn test_length_greater_than_512() { +fn test_blake2b_length_greater_than_512() { new_ucmd!() .arg("--length=1024") .arg("--algorithm=blake2b") @@ -733,7 +784,7 @@ fn test_length_greater_than_512() { } #[test] -fn test_length_is_zero() { +fn test_blake2b_length_is_zero() { new_ucmd!() .arg("--length=0") .arg("--algorithm=blake2b") @@ -745,7 +796,7 @@ fn test_length_is_zero() { } #[test] -fn test_length_repeated() { +fn test_blake2b_length_repeated() { new_ucmd!() .arg("--length=10") .arg("--length=123456") @@ -758,6 +809,23 @@ fn test_length_repeated() { .stdout_is_fixture("length_is_zero.expected"); } +#[test] +fn test_blake2b_length_invalid() { + for len in [ + "1", "01", // Odd + "", + ] { + new_ucmd!() + .arg("--length") + .arg(len) + .arg("--algorithm=blake2b") + .arg("lorem_ipsum.txt") + .arg("alice_in_wonderland.txt") + .fails_with_code(1) + .stderr_contains(format!("invalid length: '{len}'")); + } +} + #[test] fn test_raw_single_file() { for algo in ALGOS { diff --git a/tests/fixtures/cksum/length_larger_than_512.expected b/tests/fixtures/cksum/length_larger_than_512.expected index 312b6230e..8b5d3d4c2 100644 --- a/tests/fixtures/cksum/length_larger_than_512.expected +++ b/tests/fixtures/cksum/length_larger_than_512.expected @@ -1,2 +1,2 @@ -cksum: invalid length: ‘1024’ -cksum: maximum digest length for ‘BLAKE2b’ is 512 bits +cksum: invalid length: '1024' +cksum: maximum digest length for 'BLAKE2b' is 512 bits From e41c842c4f3ba18328ce5251399445b75b07bb59 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Tue, 28 Oct 2025 01:01:16 +0100 Subject: [PATCH 3/4] util(cksum): simple refactors --- src/uu/cksum/src/cksum.rs | 54 +++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 31 deletions(-) diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 37b9165fe..553331177 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -186,13 +186,14 @@ fn cksum<'a, I>(mut options: Options, files: I) -> UResult<()> where I: Iterator, { - let files: Vec<_> = files.collect(); + let mut files = files.peekable(); - if options.output_format.is_raw() && files.len() > 1 { - return Err(Box::new(ChecksumError::RawMultipleFiles)); - } + while let Some(filename) = files.next() { + // Check that in raw mode, we are not provided with several files. + if options.output_format.is_raw() && files.peek().is_some() { + return Err(Box::new(ChecksumError::RawMultipleFiles)); + } - for filename in files { let filepath = Path::new(filename); let stdin_buf; let file_buf; @@ -369,37 +370,25 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let check = matches.get_flag(options::CHECK); - let algo_name: &str = match matches.get_one::(options::ALGORITHM) { - Some(v) => v, - None => { - if check { - // if we are doing a --check, we should not default to crc - "" - } else { - ALGORITHM_OPTIONS_CRC - } - } - }; + let algo_cli = matches + .get_one::(options::ALGORITHM) + .map(String::as_str); let input_length = matches.get_one::(options::LENGTH); - let length = match (input_length, algo_name) { + let length = match (input_length, algo_cli) { // Length for sha2 and sha3 should be saved, it will be validated // afterwards if necessary. - (Some(len), ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3) => Some(*len), + (Some(len), Some(ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3)) => Some(*len), (None | Some(0), _) => None, // Length for Blake2b if saved only if it's not zero. - (Some(len), ALGORITHM_OPTIONS_BLAKE2B) => calculate_blake2b_length(*len)?, + (Some(len), Some(ALGORITHM_OPTIONS_BLAKE2B)) => calculate_blake2b_length(*len)?, // a --length flag set with any other algorithm is an error. _ => { return Err(ChecksumError::LengthOnlyForBlake2bSha2Sha3.into()); } }; - if LEGACY_ALGORITHMS.contains(&algo_name) && check { - return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into()); - } - let files = matches.get_many::(options::FILE).map_or_else( // No files given, read from stdin. || Box::new(iter::once(OsStr::new("-"))) as Box>, @@ -408,6 +397,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { ); if check { + // cksum does not support '--check'ing legacy algorithms + if algo_cli.is_some_and(|algo_name| LEGACY_ALGORITHMS.contains(&algo_name)) { + return Err(ChecksumError::AlgorithmNotSupportedWithCheck.into()); + } + let text_flag = matches.get_flag(options::TEXT); let binary_flag = matches.get_flag(options::BINARY); let strict = matches.get_flag(options::STRICT); @@ -421,13 +415,6 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { return Err(ChecksumError::BinaryTextConflict.into()); } - // Determine the appropriate algorithm option to pass - let algo_option = if algo_name.is_empty() { - None - } else { - Some(algo_name) - }; - // Execute the checksum validation based on the presence of files or the use of stdin let verbose = ChecksumVerbose::new(status, quiet, warn); @@ -438,9 +425,14 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { verbose, }; - return perform_checksum_validation(files, algo_option, length, opts); + return perform_checksum_validation(files, algo_cli, length, opts); } + // Not --check + + // Set the default algorithm to CRC when not '--check'ing. + let algo_name = algo_cli.unwrap_or(ALGORITHM_OPTIONS_CRC); + let (tag, binary) = handle_tag_text_binary_flags(std::env::args_os())?; let algo = detect_algo(algo_name, length)?; From e57902f5dfe032c7cba91ea46c0421ee8d995238 Mon Sep 17 00:00:00 2001 From: Dorian Peron Date: Tue, 28 Oct 2025 03:10:56 +0100 Subject: [PATCH 4/4] util(cksum): Fix gnu cksum-sha3.sh --- src/uu/cksum/src/cksum.rs | 48 ++++++++++------ src/uucore/src/lib/features/checksum.rs | 73 +++++++++++++++++++------ 2 files changed, 86 insertions(+), 35 deletions(-) diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 553331177..75fe79131 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -6,7 +6,7 @@ // 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}; @@ -16,8 +16,8 @@ use uucore::checksum::{ ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC, ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SHA2, ALGORITHM_OPTIONS_SHA3, ALGORITHM_OPTIONS_SYSV, ChecksumError, ChecksumOptions, ChecksumVerbose, HashAlgorithm, - LEGACY_ALGORITHMS, SUPPORTED_ALGORITHMS, calculate_blake2b_length, detect_algo, digest_reader, - perform_checksum_validation, + LEGACY_ALGORITHMS, SUPPORTED_ALGORITHMS, calculate_blake2b_length_str, detect_algo, + digest_reader, perform_checksum_validation, sanitize_sha2_sha3_length_str, }; use uucore::translate; @@ -364,6 +364,30 @@ fn figure_out_output_format( } } +/// Sanitize the `--length` argument depending on `--algorithm` and `--length`. +fn maybe_sanitize_length( + algo_cli: Option<&str>, + input_length: Option<&str>, +) -> UResult> { + match (algo_cli, input_length) { + // No provided length is not a problem so far. + (_, None) => Ok(None), + + // For SHA2 and SHA3, if a length is provided, ensure it is correct. + (Some(algo @ (ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3)), Some(s_len)) => { + sanitize_sha2_sha3_length_str(algo, s_len).map(Some) + } + + // For BLAKE2b, if a length is provided, validate it. + (Some(ALGORITHM_OPTIONS_BLAKE2B), Some(len)) => calculate_blake2b_length_str(len), + + // For any other provided algorithm, check if length is 0. + // Otherwise, this is an error. + (_, Some(len)) if len.parse::() == Ok(0_u32) => Ok(None), + (_, Some(_)) => Err(ChecksumError::LengthOnlyForBlake2bSha2Sha3.into()), + } +} + #[uucore::main] pub fn uumain(args: impl uucore::Args) -> UResult<()> { let matches = uucore::clap_localization::handle_clap_result(uu_app(), args)?; @@ -374,20 +398,11 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { .get_one::(options::ALGORITHM) .map(String::as_str); - let input_length = matches.get_one::(options::LENGTH); + let input_length = matches + .get_one::(options::LENGTH) + .map(String::as_str); - let length = match (input_length, algo_cli) { - // Length for sha2 and sha3 should be saved, it will be validated - // afterwards if necessary. - (Some(len), Some(ALGORITHM_OPTIONS_SHA2 | ALGORITHM_OPTIONS_SHA3)) => Some(*len), - (None | Some(0), _) => None, - // Length for Blake2b if saved only if it's not zero. - (Some(len), Some(ALGORITHM_OPTIONS_BLAKE2B)) => calculate_blake2b_length(*len)?, - // a --length flag set with any other algorithm is an error. - _ => { - return Err(ChecksumError::LengthOnlyForBlake2bSha2Sha3.into()); - } - }; + let length = maybe_sanitize_length(algo_cli, input_length)?; let files = matches.get_many::(options::FILE).map_or_else( // No files given, read from stdin. @@ -500,7 +515,6 @@ pub fn uu_app() -> Command { .arg( Arg::new(options::LENGTH) .long(options::LENGTH) - .value_parser(value_parser!(usize)) .short('l') .help(translate!("cksum-help-length")) .action(ArgAction::Set), diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index e59054561..5e1ffe720 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -12,6 +12,7 @@ use std::{ fmt::Display, fs::File, io::{self, BufReader, Read, Write, stdin}, + num::IntErrorKind, path::Path, str, }; @@ -220,12 +221,12 @@ pub enum ChecksumError { QuietNotCheck, #[error("--length required for {}", .0.quote())] LengthRequired(String), - #[error("unknown algorithm: {0}: clap should have prevented this case")] - UnknownAlgorithm(String), - #[error("length is not a multiple of 8")] - InvalidLength, + #[error("invalid length: {}", .0.quote())] + InvalidLength(String), #[error("digest length for {} must be 224, 256, 384, or 512", .0.quote())] - InvalidLengthFor(String), + InvalidLengthForSha(String), + #[error("--algorithm={0} requires specifying --length 224, 256, 384, or 512")] + LengthRequiredForSha(String), #[error("--length is only supported with --algorithm blake2b, sha2, or sha3")] LengthOnlyForBlake2bSha2Sha3, #[error("the --binary and --text options are meaningless when verifying checksums")] @@ -238,6 +239,8 @@ pub enum ChecksumError { CombineMultipleAlgorithms, #[error("Needs an algorithm to hash with.\nUse --help for more information.")] NeedAlgorithmToHash, + #[error("unknown algorithm: {0}: clap should have prevented this case")] + UnknownAlgorithm(String), #[error("")] Io(#[from] io::Error), } @@ -277,7 +280,7 @@ pub fn create_sha3(bits: usize) -> UResult { bits: 512, }), - _ => Err(ChecksumError::InvalidLengthFor("SHA3".into()).into()), + _ => Err(ChecksumError::InvalidLengthForSha("SHA3".into()).into()), } } @@ -304,7 +307,7 @@ pub fn create_sha2(bits: usize) -> UResult { bits: 512, }), - _ => Err(ChecksumError::InvalidLengthFor("SHA2".into()).into()), + _ => Err(ChecksumError::InvalidLengthForSha("SHA2".into()).into()), } } @@ -1235,21 +1238,29 @@ pub fn digest_reader( /// Calculates the length of the digest. pub fn calculate_blake2b_length(length: usize) -> UResult> { - match length { - 0 => Ok(None), - n if n % 8 != 0 => { - show_error!("invalid length: \u{2018}{length}\u{2019}"); + calculate_blake2b_length_str(length.to_string().as_str()) +} + +/// Calculates the length of the digest. +pub fn calculate_blake2b_length_str(length: &str) -> UResult> { + match length.parse() { + Ok(0) => Ok(None), + Ok(n) if n % 8 != 0 => { + show_error!("{}", ChecksumError::InvalidLength(length.into())); 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}"); + Ok(n) if n > 512 => { + show_error!("{}", ChecksumError::InvalidLength(length.into())); Err(io::Error::new( io::ErrorKind::InvalidInput, - "maximum digest length for \u{2018}BLAKE2b\u{2019} is 512 bits", + format!( + "maximum digest length for {} is 512 bits", + "BLAKE2b".quote() + ), ) .into()) } - n => { + Ok(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. @@ -1259,6 +1270,7 @@ pub fn calculate_blake2b_length(length: usize) -> UResult> { Ok(Some(n / 8)) } } + Err(_) => Err(ChecksumError::InvalidLength(length.into()).into()), } } @@ -1266,10 +1278,35 @@ pub fn validate_sha2_sha3_length(algo_name: &str, length: Option) -> URes match length { Some(len @ (224 | 256 | 384 | 512)) => Ok(len), Some(len) => { - show_error!("invalid length: '{len}'"); - Err(ChecksumError::InvalidLengthFor(algo_name.to_ascii_uppercase()).into()) + show_error!("{}", ChecksumError::InvalidLength(len.to_string())); + Err(ChecksumError::InvalidLengthForSha(algo_name.to_ascii_uppercase()).into()) } - None => Err(ChecksumError::LengthRequired(algo_name.to_ascii_uppercase()).into()), + None => Err(ChecksumError::LengthRequiredForSha(algo_name.into()).into()), + } +} + +pub fn sanitize_sha2_sha3_length_str(algo_name: &str, length: &str) -> UResult { + // There is a difference in the errors sent when the length is not a number + // vs. its an invalid number. + // + // When inputting an invalid number, an extra error message it printed to + // remind of the accepted inputs. + let len = match length.parse::() { + Ok(l) => l, + // Note: Positive overflow while parsing counts as an invalid number, + // but a number still. + Err(e) if *e.kind() == IntErrorKind::PosOverflow => { + show_error!("{}", ChecksumError::InvalidLength(length.into())); + return Err(ChecksumError::InvalidLengthForSha(algo_name.to_ascii_uppercase()).into()); + } + Err(_) => return Err(ChecksumError::InvalidLength(length.into()).into()), + }; + + if [224, 256, 384, 512].contains(&len) { + Ok(len) + } else { + show_error!("{}", ChecksumError::InvalidLength(length.into())); + Err(ChecksumError::InvalidLengthForSha(algo_name.to_ascii_uppercase()).into()) } }