Skip to content

Commit a83d8d5

Browse files
committed
cksum: add support for sha2
1 parent c224b0d commit a83d8d5

3 files changed

Lines changed: 258 additions & 7 deletions

File tree

src/uu/cksum/src/cksum.rs

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ use std::iter;
1414
use std::path::Path;
1515
use uucore::checksum::{
1616
ALGORITHM_OPTIONS_BLAKE2B, ALGORITHM_OPTIONS_BSD, ALGORITHM_OPTIONS_CRC,
17-
ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SYSV, ChecksumError, ChecksumOptions,
18-
ChecksumVerbose, SUPPORTED_ALGORITHMS, detect_algo, digest_reader, perform_checksum_validation,
19-
validate_blake2b_length_with_fluent,
17+
ALGORITHM_OPTIONS_CRC32B, ALGORITHM_OPTIONS_SHA2, ALGORITHM_OPTIONS_SYSV, ChecksumError,
18+
ChecksumOptions, ChecksumVerbose, SUPPORTED_ALGORITHMS, detect_algo, digest_reader,
19+
perform_checksum_validation, validate_blake2b_length,
2020
};
2121
use uucore::translate;
2222

@@ -255,7 +255,18 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
255255
let length = match input_length {
256256
Some(length_str) => {
257257
if algo_name == ALGORITHM_OPTIONS_BLAKE2B {
258-
validate_blake2b_length_with_fluent(length_str, "cksum")?
258+
validate_blake2b_length(length_str, "cksum")?
259+
} else if algo_name == ALGORITHM_OPTIONS_SHA2
260+
|| algo_name.starts_with("sha3")
261+
|| algo_name == "shake128"
262+
|| algo_name == "shake256"
263+
{
264+
// Parse length for sha2, sha3, and shake algorithms
265+
Some(
266+
length_str
267+
.parse::<usize>()
268+
.map_err(|_| ChecksumError::InvalidLength)?,
269+
)
259270
} else {
260271
return Err(ChecksumError::LengthOnlyForBlake2b.into());
261272
}

src/uucore/src/lib/features/checksum.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,16 @@ pub const ALGORITHM_OPTIONS_BLAKE3: &str = "blake3";
4747
pub const ALGORITHM_OPTIONS_SM3: &str = "sm3";
4848
pub const ALGORITHM_OPTIONS_SHAKE128: &str = "shake128";
4949
pub const ALGORITHM_OPTIONS_SHAKE256: &str = "shake256";
50+
pub const ALGORITHM_OPTIONS_SHA2: &str = "sha2";
5051

51-
pub const SUPPORTED_ALGORITHMS: [&str; 16] = [
52+
pub const SUPPORTED_ALGORITHMS: [&str; 17] = [
5253
ALGORITHM_OPTIONS_SYSV,
5354
ALGORITHM_OPTIONS_BSD,
5455
ALGORITHM_OPTIONS_CRC,
5556
ALGORITHM_OPTIONS_CRC32B,
5657
ALGORITHM_OPTIONS_MD5,
5758
ALGORITHM_OPTIONS_SHA1,
59+
ALGORITHM_OPTIONS_SHA2,
5860
ALGORITHM_OPTIONS_SHA3,
5961
ALGORITHM_OPTIONS_SHA224,
6062
ALGORITHM_OPTIONS_SHA256,
@@ -222,6 +224,10 @@ pub enum ChecksumError {
222224
BitsRequiredForShake128,
223225
#[error("--bits required for SHAKE256")]
224226
BitsRequiredForShake256,
227+
#[error("--bits required for SHA2")]
228+
BitsRequiredForSha2,
229+
#[error("Invalid output size for SHA2 (expected 224, 256, 384, or 512), got {0}")]
230+
InvalidSha2Length(usize),
225231
#[error("unknown algorithm: clap should have prevented this case")]
226232
UnknownAlgorithm,
227233
#[error("length is not a multiple of 8")]
@@ -462,6 +468,32 @@ pub fn detect_algo(algo: &str, length: Option<usize>) -> UResult<HashAlgorithm>
462468
let bits = length.ok_or(ChecksumError::BitsRequiredForSha3)?;
463469
create_sha3(bits)
464470
}
471+
ALGORITHM_OPTIONS_SHA2 => {
472+
let bits = length.ok_or(ChecksumError::BitsRequiredForSha2)?;
473+
match bits {
474+
224 => Ok(HashAlgorithm {
475+
name: ALGORITHM_OPTIONS_SHA224,
476+
create_fn: Box::new(|| Box::new(Sha224::new())),
477+
bits: 224,
478+
}),
479+
256 => Ok(HashAlgorithm {
480+
name: ALGORITHM_OPTIONS_SHA256,
481+
create_fn: Box::new(|| Box::new(Sha256::new())),
482+
bits: 256,
483+
}),
484+
384 => Ok(HashAlgorithm {
485+
name: ALGORITHM_OPTIONS_SHA384,
486+
create_fn: Box::new(|| Box::new(Sha384::new())),
487+
bits: 384,
488+
}),
489+
512 => Ok(HashAlgorithm {
490+
name: ALGORITHM_OPTIONS_SHA512,
491+
create_fn: Box::new(|| Box::new(Sha512::new())),
492+
bits: 512,
493+
}),
494+
_ => Err(ChecksumError::InvalidSha2Length(bits).into()),
495+
}
496+
}
465497

466498
_ => Err(ChecksumError::UnknownAlgorithm.into()),
467499
}
@@ -1437,6 +1469,18 @@ mod tests {
14371469
detect_algo(ALGORITHM_OPTIONS_SHA512, None).unwrap().name,
14381470
ALGORITHM_OPTIONS_SHA512
14391471
);
1472+
assert_eq!(
1473+
detect_algo(ALGORITHM_OPTIONS_SHA2, Some(256)).unwrap().name,
1474+
ALGORITHM_OPTIONS_SHA256
1475+
);
1476+
assert_eq!(
1477+
detect_algo(ALGORITHM_OPTIONS_SHA2, Some(384)).unwrap().name,
1478+
ALGORITHM_OPTIONS_SHA384
1479+
);
1480+
assert_eq!(
1481+
detect_algo(ALGORITHM_OPTIONS_SHA2, Some(512)).unwrap().name,
1482+
ALGORITHM_OPTIONS_SHA512
1483+
);
14401484
assert_eq!(
14411485
detect_algo(ALGORITHM_OPTIONS_BLAKE2B, None).unwrap().name,
14421486
ALGORITHM_OPTIONS_BLAKE2B
@@ -1467,6 +1511,7 @@ mod tests {
14671511
assert_eq!(detect_algo("sha3_512", Some(512)).unwrap().name, "SHA3_512");
14681512

14691513
assert!(detect_algo("sha3_512", None).is_err());
1514+
assert!(detect_algo(ALGORITHM_OPTIONS_SHA2, None).is_err());
14701515
}
14711516

14721517
#[test]

tests/by-util/test_cksum.rs

Lines changed: 197 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@ use uutests::new_ucmd;
99
use uutests::util::TestScenario;
1010
use uutests::util_name;
1111

12-
const ALGOS: [&str; 11] = [
13-
"sysv", "bsd", "crc", "md5", "sha1", "sha224", "sha256", "sha384", "sha512", "blake2b", "sm3",
12+
const ALGOS: [&str; 12] = [
13+
"sysv", "bsd", "crc", "md5", "sha1", "sha2", "sha224", "sha256", "sha384", "sha512", "blake2b",
14+
"sm3",
1415
];
1516

1617
#[test]
@@ -2316,4 +2317,198 @@ mod format_mix {
23162317
.stdout_contains("bar: OK")
23172318
.stderr_contains("cksum: WARNING: 1 line is improperly formatted");
23182319
}
2320+
2321+
#[test]
2322+
fn test_sha2_requires_length() {
2323+
// Test that sha2 algorithm requires a length parameter
2324+
new_ucmd!()
2325+
.arg("-a")
2326+
.arg("sha2")
2327+
.arg("lorem_ipsum.txt")
2328+
.fails_with_code(1)
2329+
.no_stdout()
2330+
.stderr_contains("--bits required for SHA2");
2331+
}
2332+
2333+
#[test]
2334+
fn test_sha2_with_length_224() {
2335+
// Test sha2 with 224-bit length (equivalent to sha224)
2336+
let result_sha2 = new_ucmd!()
2337+
.arg("-a")
2338+
.arg("sha2")
2339+
.arg("-l")
2340+
.arg("224")
2341+
.pipe_in("test\n")
2342+
.succeeds()
2343+
.stdout_str()
2344+
.to_owned();
2345+
2346+
let result_sha224 = new_ucmd!()
2347+
.arg("-a")
2348+
.arg("sha224")
2349+
.pipe_in("test\n")
2350+
.succeeds()
2351+
.stdout_str()
2352+
.to_owned();
2353+
2354+
// The outputs should be identical (both produce SHA224)
2355+
assert_eq!(result_sha2, result_sha224);
2356+
assert!(result_sha2.contains("SHA224"));
2357+
}
2358+
2359+
#[test]
2360+
fn test_sha2_with_length_256() {
2361+
// Test sha2 with 256-bit length (equivalent to sha256)
2362+
let result_sha2 = new_ucmd!()
2363+
.arg("-a")
2364+
.arg("sha2")
2365+
.arg("-l")
2366+
.arg("256")
2367+
.pipe_in("test\n")
2368+
.succeeds()
2369+
.stdout_str()
2370+
.to_owned();
2371+
2372+
let result_sha256 = new_ucmd!()
2373+
.arg("-a")
2374+
.arg("sha256")
2375+
.pipe_in("test\n")
2376+
.succeeds()
2377+
.stdout_str()
2378+
.to_owned();
2379+
2380+
// The outputs should be identical (both produce SHA256)
2381+
assert_eq!(result_sha2, result_sha256);
2382+
assert!(result_sha2.contains("SHA256"));
2383+
}
2384+
2385+
#[test]
2386+
fn test_sha2_with_length_384() {
2387+
// Test sha2 with 384-bit length (equivalent to sha384)
2388+
let result_sha2 = new_ucmd!()
2389+
.arg("-a")
2390+
.arg("sha2")
2391+
.arg("-l")
2392+
.arg("384")
2393+
.pipe_in("test\n")
2394+
.succeeds()
2395+
.stdout_str()
2396+
.to_owned();
2397+
2398+
let result_sha384 = new_ucmd!()
2399+
.arg("-a")
2400+
.arg("sha384")
2401+
.pipe_in("test\n")
2402+
.succeeds()
2403+
.stdout_str()
2404+
.to_owned();
2405+
2406+
// The outputs should be identical (both produce SHA384)
2407+
assert_eq!(result_sha2, result_sha384);
2408+
assert!(result_sha2.contains("SHA384"));
2409+
}
2410+
2411+
#[test]
2412+
fn test_sha2_with_length_512() {
2413+
// Test sha2 with 512-bit length (equivalent to sha512)
2414+
let result_sha2 = new_ucmd!()
2415+
.arg("-a")
2416+
.arg("sha2")
2417+
.arg("-l")
2418+
.arg("512")
2419+
.pipe_in("test\n")
2420+
.succeeds()
2421+
.stdout_str()
2422+
.to_owned();
2423+
2424+
let result_sha512 = new_ucmd!()
2425+
.arg("-a")
2426+
.arg("sha512")
2427+
.pipe_in("test\n")
2428+
.succeeds()
2429+
.stdout_str()
2430+
.to_owned();
2431+
2432+
// The outputs should be identical (both produce SHA512)
2433+
assert_eq!(result_sha2, result_sha512);
2434+
assert!(result_sha2.contains("SHA512"));
2435+
}
2436+
2437+
#[test]
2438+
fn test_sha2_invalid_length() {
2439+
// Test sha2 with invalid length parameters
2440+
new_ucmd!()
2441+
.arg("-a")
2442+
.arg("sha2")
2443+
.arg("-l")
2444+
.arg("128")
2445+
.pipe_in("test\n")
2446+
.fails_with_code(1)
2447+
.stderr_contains("Invalid output size for SHA2");
2448+
2449+
new_ucmd!()
2450+
.arg("-a")
2451+
.arg("sha2")
2452+
.arg("-l")
2453+
.arg("1024")
2454+
.pipe_in("test\n")
2455+
.fails_with_code(1)
2456+
.stderr_contains("Invalid output size for SHA2");
2457+
}
2458+
2459+
#[test]
2460+
fn test_sha2_with_raw_output() {
2461+
// Test sha2 with raw (binary) output
2462+
let result = new_ucmd!()
2463+
.arg("-a")
2464+
.arg("sha2")
2465+
.arg("-l")
2466+
.arg("256")
2467+
.arg("--raw")
2468+
.pipe_in("test\n")
2469+
.succeeds();
2470+
2471+
// Raw output should be binary, not hex - check raw bytes instead of trying to convert to string
2472+
let stdout_bytes = result.stdout();
2473+
// Raw output should not be empty and should be exactly 32 bytes for SHA256
2474+
assert_eq!(stdout_bytes.len(), 32);
2475+
// Should not contain ASCII text like "SHA256"
2476+
assert!(!stdout_bytes.starts_with(b"SHA256"));
2477+
}
2478+
2479+
#[test]
2480+
fn test_sha2_with_base64_output() {
2481+
// Test sha2 with base64 output
2482+
let result = new_ucmd!()
2483+
.arg("-a")
2484+
.arg("sha2")
2485+
.arg("-l")
2486+
.arg("256")
2487+
.arg("--base64")
2488+
.pipe_in("test\n")
2489+
.succeeds()
2490+
.stdout_str()
2491+
.to_owned();
2492+
2493+
// Should contain base64 characters and SHA256 label
2494+
assert!(result.contains("SHA256"));
2495+
// Base64 should end with = or == or contain +/
2496+
assert!(result.contains('=') || result.contains('+') || result.contains('/'));
2497+
}
2498+
2499+
#[test]
2500+
fn test_sha2_with_file() {
2501+
// Test sha2 with actual file input
2502+
let (at, mut ucmd) = at_and_ucmd!();
2503+
at.write("test_file.txt", "Hello, world!\n");
2504+
2505+
ucmd.arg("-a")
2506+
.arg("sha2")
2507+
.arg("-l")
2508+
.arg("256")
2509+
.arg("test_file.txt")
2510+
.succeeds()
2511+
.stdout_contains("SHA256")
2512+
.stdout_contains("test_file.txt");
2513+
}
23192514
}

0 commit comments

Comments
 (0)