From ae1113d7dc849c4cb0d2230f952e330a1e1a5743 Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Mon, 10 Apr 2023 18:31:18 +0200 Subject: [PATCH 1/6] b2sum: add support to -l option --- src/uu/hashsum/src/hashsum.rs | 36 ++++++++++++++++++++++++++++-- src/uucore/src/lib/features/sum.rs | 23 +++++++++++++++---- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 460ab6f19..67431fe9f 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -65,7 +65,23 @@ fn detect_algo( "sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box, 256), "sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box, 384), "sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box, 512), - "b2sum" => ("BLAKE2", Box::new(Blake2b::new()) as Box, 512), + "b2sum" => match matches.get_one::("length") { + Some(length_in_bits) => { + // blake2 output size must be a multiple of 8 bits + if length_in_bits % 8 == 0 { + let length_in_bytes = length_in_bits / 8; + ( + "BLAKE2", + Box::new(Blake2b::with_output_bytes(length_in_bytes)), + *length_in_bits, + ) + } else { + crash!(1, "Invalid length (expected a multiple of 8)") + } + } + // by default, blake2 uses 64bytes (512 bits) + None => ("BLAKE2", Box::new(Blake2b::new()) as Box, 512), + }, "b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box, 256), "sha3sum" => match matches.get_one::("bits") { Some(224) => ( @@ -379,6 +395,22 @@ pub fn uu_app_common() -> Command { ) } +pub fn uu_app_length() -> Command { + uu_app_opt_length(uu_app_common()) +} + +fn uu_app_opt_length(command: Command) -> Command { + // Needed for variable-length output sums (e.g. SHAKE) + command.arg( + Arg::new("length") + .short('l') + .long("length") + .help("digest length in bits; must not exceed the max for the blake2 algorithm (512) and must be a multiple of 8") + .value_name("BITS") + .value_parser(parse_bit_num), + ) +} + pub fn uu_app_b3sum() -> Command { uu_app_b3sum_opts(uu_app_common()) } @@ -454,7 +486,7 @@ fn uu_app(binary_name: &str) -> Command { uu_app_common() } // b2sum supports the md5sum options plus -l/--length. - "b2sum" => uu_app_common(), // TODO: Implement -l/--length + "b2sum" => uu_app_length(), // These have never been part of GNU Coreutils, but can function with the same // options as md5sum. "sha3-224sum" | "sha3-256sum" | "sha3-384sum" | "sha3-512sum" => uu_app_common(), diff --git a/src/uucore/src/lib/features/sum.rs b/src/uucore/src/lib/features/sum.rs index 339414630..c1cfaf5f8 100644 --- a/src/uucore/src/lib/features/sum.rs +++ b/src/uucore/src/lib/features/sum.rs @@ -38,10 +38,25 @@ pub trait Digest { } } -pub struct Blake2b(blake2b_simd::State); +/// first element of the tuple is the blake2b state +/// second is the number of output bits +pub struct Blake2b(blake2b_simd::State, usize); + +impl Blake2b { + /// Return a new Blake2b instance with a custom output bytes length + pub fn with_output_bytes(output_bytes: usize) -> Self { + let mut params = blake2b_simd::Params::new(); + params.hash_length(output_bytes); + + let state = params.to_state(); + Self(state, output_bytes * 8) + } +} + impl Digest for Blake2b { fn new() -> Self { - Self(blake2b_simd::State::new()) + // by default, Blake2b output is 512 bits long (= 64B) + Self::with_output_bytes(64) } fn hash_update(&mut self, input: &[u8]) { @@ -54,11 +69,11 @@ impl Digest for Blake2b { } fn reset(&mut self) { - *self = Self::new(); + *self = Self::with_output_bytes(self.output_bytes()); } fn output_bits(&self) -> usize { - 512 + self.1 } } From 0798cfe155419c827194b0cf940ed0b5f3fa3191 Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Mon, 10 Apr 2023 20:27:49 +0200 Subject: [PATCH 2/6] b2sum: added test for length option --- tests/by-util/test_hashsum.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/by-util/test_hashsum.rs b/tests/by-util/test_hashsum.rs index a227b9fc3..55bef3792 100644 --- a/tests/by-util/test_hashsum.rs +++ b/tests/by-util/test_hashsum.rs @@ -125,6 +125,23 @@ fn test_check_sha1() { .stderr_is(""); } +#[test] +fn test_check_b2sum_length_option() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("testf", "foobar\n"); + at.write("testf.b2sum", "6a testf\n"); + + scene + .ccmd("b2sum") + .arg("--length=8") + .arg("-c") + .arg(at.subdir.join("testf.b2sum")) + .succeeds() + .stdout_only("testf: OK\n"); +} + #[test] fn test_check_file_not_found_warning() { let scene = TestScenario::new(util_name!()); From a9e65e881c3dd6999ffc7dbfaafdf983c7bf63e1 Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Mon, 10 Apr 2023 21:20:54 +0200 Subject: [PATCH 3/6] b2sum: handled 0 length case and greater than 512 case --- src/uu/hashsum/src/hashsum.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 67431fe9f..30c794e07 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -67,6 +67,15 @@ fn detect_algo( "sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box, 512), "b2sum" => match matches.get_one::("length") { Some(length_in_bits) => { + if *length_in_bits > 512 { + crash!(1, "Invalid length (maximum digest length is 512 bits)") + } + + // --length=0 falls back to default behaviour + if *length_in_bits == 0 { + return ("BLAKE2", Box::new(Blake2b::new()) as Box, 512); + } + // blake2 output size must be a multiple of 8 bits if length_in_bits % 8 == 0 { let length_in_bytes = length_in_bits / 8; @@ -400,7 +409,6 @@ pub fn uu_app_length() -> Command { } fn uu_app_opt_length(command: Command) -> Command { - // Needed for variable-length output sums (e.g. SHAKE) command.arg( Arg::new("length") .short('l') From c4d637125c31341501539c3616a3fa2c9699b16e Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Tue, 11 Apr 2023 09:28:41 +0200 Subject: [PATCH 4/6] b2sum: changed british spelling for american spelling --- src/uu/hashsum/src/hashsum.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 30c794e07..3c3fdd2f9 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -71,7 +71,7 @@ fn detect_algo( crash!(1, "Invalid length (maximum digest length is 512 bits)") } - // --length=0 falls back to default behaviour + // --length=0 falls back to default behavior if *length_in_bits == 0 { return ("BLAKE2", Box::new(Blake2b::new()) as Box, 512); } @@ -88,7 +88,7 @@ fn detect_algo( crash!(1, "Invalid length (expected a multiple of 8)") } } - // by default, blake2 uses 64bytes (512 bits) + // by default, blake2 uses 64 bytes (512 bits) None => ("BLAKE2", Box::new(Blake2b::new()) as Box, 512), }, "b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box, 256), From fdbebfad9da500c72b0a9c127f59c95e06b248f6 Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Wed, 12 Apr 2023 09:49:20 +0200 Subject: [PATCH 5/6] b2sum: moved 0 case into None arm --- src/uu/hashsum/src/hashsum.rs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/uu/hashsum/src/hashsum.rs b/src/uu/hashsum/src/hashsum.rs index 3c3fdd2f9..5678a58b3 100644 --- a/src/uu/hashsum/src/hashsum.rs +++ b/src/uu/hashsum/src/hashsum.rs @@ -66,16 +66,14 @@ fn detect_algo( "sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box, 384), "sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box, 512), "b2sum" => match matches.get_one::("length") { + // by default, blake2 uses 64 bytes (512 bits) + // --length=0 falls back to default behavior + Some(0) | None => ("BLAKE2", Box::new(Blake2b::new()) as Box, 512), Some(length_in_bits) => { if *length_in_bits > 512 { crash!(1, "Invalid length (maximum digest length is 512 bits)") } - // --length=0 falls back to default behavior - if *length_in_bits == 0 { - return ("BLAKE2", Box::new(Blake2b::new()) as Box, 512); - } - // blake2 output size must be a multiple of 8 bits if length_in_bits % 8 == 0 { let length_in_bytes = length_in_bits / 8; @@ -88,8 +86,6 @@ fn detect_algo( crash!(1, "Invalid length (expected a multiple of 8)") } } - // by default, blake2 uses 64 bytes (512 bits) - None => ("BLAKE2", Box::new(Blake2b::new()) as Box, 512), }, "b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box, 256), "sha3sum" => match matches.get_one::("bits") { From 756293a9c47ec9fc6eaef7b7c87e5c7e3be41bea Mon Sep 17 00:00:00 2001 From: Marras Antoine Date: Wed, 12 Apr 2023 21:05:11 +0200 Subject: [PATCH 6/6] b2sum: added tests to handle 0, non multiple of 8 and and more than 512 cases --- tests/by-util/test_hashsum.rs | 49 ++++++++++++++++++++++++++++++++++- 1 file changed, 48 insertions(+), 1 deletion(-) diff --git a/tests/by-util/test_hashsum.rs b/tests/by-util/test_hashsum.rs index 55bef3792..645f11ef2 100644 --- a/tests/by-util/test_hashsum.rs +++ b/tests/by-util/test_hashsum.rs @@ -126,7 +126,24 @@ fn test_check_sha1() { } #[test] -fn test_check_b2sum_length_option() { +fn test_check_b2sum_length_option_0() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("testf", "foobar\n"); + at.write("testf.b2sum", "9e2bf63e933e610efee4a8d6cd4a9387e80860edee97e27db3b37a828d226ab1eb92a9cdd8ca9ca67a753edaf8bd89a0558496f67a30af6f766943839acf0110 testf\n"); + + scene + .ccmd("b2sum") + .arg("--length=0") + .arg("-c") + .arg(at.subdir.join("testf.b2sum")) + .succeeds() + .stdout_only("testf: OK\n"); +} + +#[test] +fn test_check_b2sum_length_option_8() { let scene = TestScenario::new(util_name!()); let at = &scene.fixtures; @@ -142,6 +159,36 @@ fn test_check_b2sum_length_option() { .stdout_only("testf: OK\n"); } +#[test] +fn test_invalid_b2sum_length_option_not_multiple_of_8() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("testf", "foobar\n"); + + scene + .ccmd("b2sum") + .arg("--length=9") + .arg(at.subdir.join("testf")) + .fails() + .code_is(1); +} + +#[test] +fn test_invalid_b2sum_length_option_too_large() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + + at.write("testf", "foobar\n"); + + scene + .ccmd("b2sum") + .arg("--length=513") + .arg(at.subdir.join("testf")) + .fails() + .code_is(1); +} + #[test] fn test_check_file_not_found_warning() { let scene = TestScenario::new(util_name!());