diff --git a/src/uu/cksum/locales/en-US.ftl b/src/uu/cksum/locales/en-US.ftl index 338ae874ce4..8a0c8f26c08 100644 --- a/src/uu/cksum/locales/en-US.ftl +++ b/src/uu/cksum/locales/en-US.ftl @@ -14,6 +14,9 @@ cksum-after-help = DIGEST determines the digest algorithm and default output for - sha512: (equivalent to sha512sum) - blake2b: (equivalent to b2sum) - sm3: (only available through cksum) + - sha3: (requires --length: 224, 256, 384, or 512) + - shake128: (requires --length) + - shake256: (requires --length) # Help messages cksum-help-algorithm = select the digest type to use. See DIGEST below @@ -29,6 +32,7 @@ cksum-help-status = don't output anything, status code shows success cksum-help-quiet = don't print OK for each successfully verified file cksum-help-ignore-missing = don't fail or report status for missing files cksum-help-zero = end each output line with NUL, not newline, and disable file name escaping +cksum-help-debug = indicate which implementation used # Error messages cksum-error-is-directory = { $file }: Is a directory diff --git a/src/uu/cksum/locales/fr-FR.ftl b/src/uu/cksum/locales/fr-FR.ftl index 52b4b9ed742..14dab2761f1 100644 --- a/src/uu/cksum/locales/fr-FR.ftl +++ b/src/uu/cksum/locales/fr-FR.ftl @@ -12,8 +12,11 @@ cksum-after-help = DIGEST détermine l'algorithme de condensé et le format de s - sha256 : (équivalent à sha256sum) - sha384 : (équivalent à sha384sum) - sha512 : (équivalent à sha512sum) - - blake2b : (équivalent à b2sum) - - sm3 : (disponible uniquement via cksum) + - blake2b: (équivalent à b2sum) + - sm3: (uniquement disponible via cksum) + - sha3: (nécessite --length: 224, 256, 384, ou 512) + - shake128: (nécessite --length) + - shake256: (nécessite --length) # Messages d'aide cksum-help-algorithm = sélectionner le type de condensé à utiliser. Voir DIGEST ci-dessous @@ -28,7 +31,8 @@ cksum-help-warn = avertir des lignes de somme de contrôle mal formatées cksum-help-status = ne rien afficher, le code de statut indique le succès cksum-help-quiet = ne pas afficher OK pour chaque fichier vérifié avec succès cksum-help-ignore-missing = ne pas échouer ou signaler le statut pour les fichiers manquants -cksum-help-zero = terminer chaque ligne de sortie avec NUL, pas un saut de ligne, et désactiver l'échappement des noms de fichiers +cksum-help-zero = terminer chaque ligne de sortie par NUL, et non par newline, et désactiver l'échappement des noms de fichiers +cksum-help-debug = indiquer quelle implémentation est utilisée # Messages d'erreur cksum-error-is-directory = { $file } : Est un répertoire diff --git a/src/uu/cksum/src/cksum.rs b/src/uu/cksum/src/cksum.rs index 7d595e625ac..3cd764af89b 100644 --- a/src/uu/cksum/src/cksum.rs +++ b/src/uu/cksum/src/cksum.rs @@ -15,7 +15,7 @@ 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, + ChecksumVerbose, SUPPORTED_ALGORITHMS, calculate_blake2b_length, digest_reader, perform_checksum_validation, }; use uucore::translate; @@ -201,6 +201,7 @@ mod options { pub const IGNORE_MISSING: &str = "ignore-missing"; pub const QUIET: &str = "quiet"; pub const ZERO: &str = "zero"; + pub const DEBUG: &str = "debug"; } /*** @@ -256,8 +257,17 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { Some(length) => { if algo_name == ALGORITHM_OPTIONS_BLAKE2B { calculate_blake2b_length(*length)? + } else if algo_name.starts_with("sha3") + || algo_name == "shake128" + || algo_name == "shake256" + { + // SHA3 and SHAKE algorithms require --length in bits + Some(*length) } else { - return Err(ChecksumError::LengthOnlyForBlake2b.into()); + return Err(USimpleError::new( + 1, + "--length is only supported with --algorithm=blake2b, sha3, shake128, or shake256", + )); } } None => None, @@ -308,7 +318,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let (tag, asterisk) = handle_tag_text_binary_flags(std::env::args_os())?; - let algo = detect_algo(algo_name, length)?; + let algo = uucore::checksum::detect_algo_with_label(algo_name, length, true)?; let line_ending = LineEnding::from_zero_flag(matches.get_flag(options::ZERO)); let output_format = if matches.get_flag(options::RAW) { @@ -462,5 +472,11 @@ pub fn uu_app() -> Command { .help(translate!("cksum-help-zero")) .action(ArgAction::SetTrue), ) + .arg( + Arg::new(options::DEBUG) + .long(options::DEBUG) + .help(translate!("cksum-help-debug")) + .action(ArgAction::SetTrue), + ) .after_help(translate!("cksum-after-help")) } diff --git a/src/uucore/src/lib/features/checksum.rs b/src/uucore/src/lib/features/checksum.rs index 44293ca0deb..7a6a9dfe499 100644 --- a/src/uucore/src/lib/features/checksum.rs +++ b/src/uucore/src/lib/features/checksum.rs @@ -357,7 +357,11 @@ fn print_file_report( } } -pub fn detect_algo(algo: &str, length: Option) -> UResult { +pub fn detect_algo_with_label( + algo: &str, + length: Option, + is_cksum: bool, +) -> UResult { match algo { ALGORITHM_OPTIONS_SYSV => Ok(HashAlgorithm { name: ALGORITHM_OPTIONS_SYSV, @@ -437,7 +441,12 @@ pub fn detect_algo(algo: &str, length: Option) -> UResult bits: 512, }), ALGORITHM_OPTIONS_SHAKE128 | "shake128sum" => { - let bits = length.ok_or(ChecksumError::BitsRequiredForShake128)?; + let Some(bits) = length else { + if is_cksum { + return Err(USimpleError::new(1, "--length required for SHAKE128")); + } + return Err(ChecksumError::BitsRequiredForShake128.into()); + }; Ok(HashAlgorithm { name: ALGORITHM_OPTIONS_SHAKE128, create_fn: Box::new(|| Box::new(Shake128::new())), @@ -445,7 +454,12 @@ pub fn detect_algo(algo: &str, length: Option) -> UResult }) } ALGORITHM_OPTIONS_SHAKE256 | "shake256sum" => { - let bits = length.ok_or(ChecksumError::BitsRequiredForShake256)?; + let Some(bits) = length else { + if is_cksum { + return Err(USimpleError::new(1, "--length required for SHAKE256")); + } + return Err(ChecksumError::BitsRequiredForShake256.into()); + }; Ok(HashAlgorithm { name: ALGORITHM_OPTIONS_SHAKE256, create_fn: Box::new(|| Box::new(Shake256::new())), @@ -453,7 +467,12 @@ pub fn detect_algo(algo: &str, length: Option) -> UResult }) } _ if algo.starts_with("sha3") => { - let bits = length.ok_or(ChecksumError::BitsRequiredForSha3)?; + let Some(bits) = length else { + if is_cksum { + return Err(USimpleError::new(1, "--length required for SHA3")); + } + return Err(ChecksumError::BitsRequiredForSha3.into()); + }; create_sha3(bits) } @@ -461,6 +480,10 @@ pub fn detect_algo(algo: &str, length: Option) -> UResult } } +pub fn detect_algo(algo: &str, length: Option) -> UResult { + detect_algo_with_label(algo, length, false) +} + #[derive(Debug, PartialEq, Eq, Clone, Copy)] enum LineFormat { AlgoBased, diff --git a/tests/by-util/test_cksum.rs b/tests/by-util/test_cksum.rs index 3e84a2d04e6..f5d7e737a1b 100644 --- a/tests/by-util/test_cksum.rs +++ b/tests/by-util/test_cksum.rs @@ -2215,6 +2215,243 @@ mod gnu_cksum_c { } } +#[test] +fn test_debug_flag() { + // Test that --debug flag is accepted (no-op for now) + new_ucmd!() + .arg("--debug") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_is_fixture("crc_single_file.expected"); + + // Test --debug with algorithm + new_ucmd!() + .arg("--debug") + .arg("-a") + .arg("md5") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_is_fixture("md5_single_file.expected"); + + // Test --debug with multiple files + new_ucmd!() + .arg("--debug") + .arg("lorem_ipsum.txt") + .arg("alice_in_wonderland.txt") + .succeeds() + .stdout_is_fixture("crc_multiple_files.expected"); +} + +#[test] +fn test_sha3_with_length() { + // Test SHA3-224 + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("224") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHA3_224 (lorem_ipsum.txt) = "); + + // Test SHA3-256 + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("256") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHA3_256 (lorem_ipsum.txt) = "); + + // Test SHA3-384 + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("384") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHA3_384 (lorem_ipsum.txt) = "); + + // Test SHA3-512 + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("512") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHA3_512 (lorem_ipsum.txt) = "); +} + +#[test] +fn test_sha3_invalid_length() { + // Test SHA3 with invalid length (not 224, 256, 384, or 512) + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("128") + .arg("lorem_ipsum.txt") + .fails() + .stderr_contains("Invalid output size for SHA3"); + + // Test SHA3 without length + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("lorem_ipsum.txt") + .fails() + .stderr_contains("--length required for SHA3"); +} + +#[test] +fn test_shake128_with_length() { + // Test SHAKE128 with various lengths + new_ucmd!() + .arg("-a") + .arg("shake128") + .arg("--length") + .arg("256") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE128 (lorem_ipsum.txt) = "); + + // Test SHAKE128 with short length + new_ucmd!() + .arg("-a") + .arg("shake128") + .arg("--length") + .arg("64") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE128 (lorem_ipsum.txt) = "); + + // Test SHAKE128 with long length + new_ucmd!() + .arg("-a") + .arg("shake128") + .arg("--length") + .arg("512") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE128 (lorem_ipsum.txt) = "); +} + +#[test] +fn test_shake128_without_length() { + // Test SHAKE128 without --length (should fail) + new_ucmd!() + .arg("-a") + .arg("shake128") + .arg("lorem_ipsum.txt") + .fails() + .stderr_contains("--length required for SHAKE128"); +} + +#[test] +fn test_shake256_with_length() { + // Test SHAKE256 with various lengths + new_ucmd!() + .arg("-a") + .arg("shake256") + .arg("--length") + .arg("256") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE256 (lorem_ipsum.txt) = "); + + // Test SHAKE256 with short length + new_ucmd!() + .arg("-a") + .arg("shake256") + .arg("--length") + .arg("128") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE256 (lorem_ipsum.txt) = "); + + // Test SHAKE256 with long length + new_ucmd!() + .arg("-a") + .arg("shake256") + .arg("--length") + .arg("1024") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHAKE256 (lorem_ipsum.txt) = "); +} + +#[test] +fn test_shake256_without_length() { + // Test SHAKE256 without --length (should fail) + new_ucmd!() + .arg("-a") + .arg("shake256") + .arg("lorem_ipsum.txt") + .fails() + .stderr_contains("--length required for SHAKE256"); +} + +#[test] +fn test_sha3_shake_stdin() { + // Test SHA3 with stdin + new_ucmd!() + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("256") + .pipe_in_fixture("lorem_ipsum.txt") + .succeeds() + .stdout_contains("c9b081db1c20bdb4d8f376f45b3fb412b48c75932451d13dd79bea61e7bddbe4"); + + // Test SHAKE128 with stdin + new_ucmd!() + .arg("-a") + .arg("shake128") + .arg("--length") + .arg("256") + .pipe_in_fixture("lorem_ipsum.txt") + .succeeds(); + + // Test SHAKE256 with stdin + new_ucmd!() + .arg("-a") + .arg("shake256") + .arg("--length") + .arg("256") + .pipe_in_fixture("lorem_ipsum.txt") + .succeeds(); +} + +#[test] +fn test_sha3_base64_output() { + // Test SHA3 with base64 output format + new_ucmd!() + .arg("--base64") + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("256") + .arg("lorem_ipsum.txt") + .succeeds() + .stdout_contains("SHA3_256 (lorem_ipsum.txt) = "); +} + +#[test] +fn test_sha3_untagged() { + // Test SHA3 with untagged output + new_ucmd!() + .arg("--untagged") + .arg("-a") + .arg("sha3") + .arg("--length") + .arg("256") + .arg("lorem_ipsum.txt") + .succeeds(); +} + /// The tests in this module check the behavior of cksum when given different /// checksum formats and algorithms in the same file, while specifying an /// algorithm on CLI or not.