Skip to content

Commit bbec8fe

Browse files
authored
Merge pull request #9344 from RenjiSann/cksum-fix-base64-untagged
cksum: Fix GNU test cksum-base64-untagged
2 parents 7c1a25a + b8cef3c commit bbec8fe

File tree

2 files changed

+152
-29
lines changed

2 files changed

+152
-29
lines changed

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

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
// For the full copyright and license information, please view the LICENSE
44
// file that was distributed with this source code.
55

6-
// spell-checker:ignore rsplit hexdigit bitlen bytelen invalidchecksum xffname
6+
// spell-checker:ignore rsplit hexdigit bitlen bytelen invalidchecksum inva idchecksum xffname
77

88
use std::borrow::Cow;
99
use std::ffi::OsStr;
@@ -296,27 +296,7 @@ impl LineFormat {
296296
SubCase::OpenSSL => ByteSliceExt::rsplit_once(after_paren, b")= ")?,
297297
};
298298

299-
fn is_valid_checksum(checksum: &[u8]) -> bool {
300-
if checksum.is_empty() {
301-
return false;
302-
}
303-
304-
let mut parts = checksum.splitn(2, |&b| b == b'=');
305-
let main = parts.next().unwrap(); // Always exists since checksum isn't empty
306-
let padding = parts.next().unwrap_or_default(); // Empty if no '='
307-
308-
main.iter()
309-
.all(|&b| b.is_ascii_alphanumeric() || b == b'+' || b == b'/')
310-
&& !main.is_empty()
311-
&& padding.len() <= 2
312-
&& padding.iter().all(|&b| b == b'=')
313-
}
314-
if !is_valid_checksum(checksum) {
315-
return None;
316-
}
317-
// SAFETY: we just validated the contents of checksum, we can unsafely make a
318-
// String from it
319-
let checksum_utf8 = unsafe { String::from_utf8_unchecked(checksum.to_vec()) };
299+
let checksum_utf8 = Self::validate_checksum_format(checksum)?;
320300

321301
Some(LineInfo {
322302
algo_name: Some(algo_utf8),
@@ -336,12 +316,8 @@ impl LineFormat {
336316
fn parse_untagged(line: &[u8]) -> Option<LineInfo> {
337317
let space_idx = line.iter().position(|&b| b == b' ')?;
338318
let checksum = &line[..space_idx];
339-
if !checksum.iter().all(|&b| b.is_ascii_hexdigit()) || checksum.is_empty() {
340-
return None;
341-
}
342-
// SAFETY: we just validated the contents of checksum, we can unsafely make a
343-
// String from it
344-
let checksum_utf8 = unsafe { String::from_utf8_unchecked(checksum.to_vec()) };
319+
320+
let checksum_utf8 = Self::validate_checksum_format(checksum)?;
345321

346322
let rest = &line[space_idx..];
347323
let filename = rest
@@ -388,6 +364,34 @@ impl LineFormat {
388364
format: Self::SingleSpace,
389365
})
390366
}
367+
368+
/// Ensure that the given checksum is syntactically valid (that it is either
369+
/// hexadecimal or base64 encoded).
370+
fn validate_checksum_format(checksum: &[u8]) -> Option<String> {
371+
if checksum.is_empty() {
372+
return None;
373+
}
374+
375+
let mut parts = checksum.splitn(2, |&b| b == b'=');
376+
let main = parts.next().unwrap(); // Always exists since checksum isn't empty
377+
let padding = parts.next().unwrap_or_default(); // Empty if no '='
378+
379+
if main.is_empty()
380+
|| !main
381+
.iter()
382+
.all(|&b| b.is_ascii_alphanumeric() || b == b'+' || b == b'/')
383+
{
384+
return None;
385+
}
386+
387+
if padding.len() > 2 || padding.iter().any(|&b| b != b'=') {
388+
return None;
389+
}
390+
391+
// SAFETY: we just validated the contents of checksum, we can unsafely make a
392+
// String from it
393+
Some(unsafe { String::from_utf8_unchecked(checksum.to_vec()) })
394+
}
391395
}
392396

393397
// Helper trait for byte slice operations
@@ -1039,7 +1043,13 @@ mod tests {
10391043
b"b064a020db8018f18ff5ae367d01b212 ",
10401044
Some((b"b064a020db8018f18ff5ae367d01b212", b" ")),
10411045
),
1042-
(b"invalidchecksum test", None),
1046+
// base64 checksums are accepted
1047+
(
1048+
b"b21lbGV0dGUgZHUgZnJvbWFnZQ== ",
1049+
Some((b"b21lbGV0dGUgZHUgZnJvbWFnZQ==", b" ")),
1050+
),
1051+
// Invalid checksums fail
1052+
(b"inva|idchecksum test", None),
10431053
];
10441054

10451055
for (input, expected) in test_cases {

tests/by-util/test_cksum.rs

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,6 +2319,119 @@ mod gnu_cksum_base64 {
23192319
}
23202320
}
23212321

2322+
/// This module reimplements the cksum-base64-untagged.sh GNU test.
2323+
mod gnu_cksum_base64_untagged {
2324+
use super::*;
2325+
2326+
macro_rules! decl_sha_test {
2327+
($id:ident, $algo:literal, $len:expr) => {
2328+
mod $id {
2329+
use super::*;
2330+
2331+
#[test]
2332+
fn check_length_guess() {
2333+
let ts = TestScenario::new(util_name!());
2334+
let at = &ts.fixtures;
2335+
2336+
at.write("inp", "test input\n");
2337+
2338+
let compute = ts
2339+
.ucmd()
2340+
.arg("-a")
2341+
.arg($algo)
2342+
.arg("-l")
2343+
.arg(stringify!($len))
2344+
.arg("--base64")
2345+
.arg("--untagged")
2346+
.arg("inp")
2347+
.succeeds();
2348+
2349+
at.write_bytes("check", compute.stdout());
2350+
2351+
ts.ucmd()
2352+
.arg("-a")
2353+
.arg($algo)
2354+
.arg("--check")
2355+
.arg("check")
2356+
.succeeds()
2357+
.stdout_only("inp: OK\n");
2358+
2359+
at.write("check", " inp");
2360+
2361+
ts.ucmd()
2362+
.arg("-a")
2363+
.arg($algo)
2364+
.arg("check")
2365+
.fails()
2366+
.stderr_contains(concat!(
2367+
"--algorithm=",
2368+
$algo,
2369+
" requires specifying --length"
2370+
));
2371+
}
2372+
}
2373+
};
2374+
}
2375+
2376+
decl_sha_test!(sha2_224, "sha2", 224);
2377+
decl_sha_test!(sha2_256, "sha2", 256);
2378+
decl_sha_test!(sha2_384, "sha2", 384);
2379+
decl_sha_test!(sha2_512, "sha2", 512);
2380+
decl_sha_test!(sha3_224, "sha3", 224);
2381+
decl_sha_test!(sha3_256, "sha3", 256);
2382+
decl_sha_test!(sha3_384, "sha3", 384);
2383+
decl_sha_test!(sha3_512, "sha3", 512);
2384+
2385+
macro_rules! decl_blake_test {
2386+
($id:ident, $len:expr) => {
2387+
mod $id {
2388+
use super::*;
2389+
2390+
#[test]
2391+
fn check_length_guess() {
2392+
let ts = TestScenario::new(util_name!());
2393+
let at = &ts.fixtures;
2394+
2395+
at.write("inp", "test input\n");
2396+
2397+
let compute = ts
2398+
.ucmd()
2399+
.arg("-a")
2400+
.arg("blake2b")
2401+
.arg("-l")
2402+
.arg(stringify!($len))
2403+
.arg("--base64")
2404+
.arg("--untagged")
2405+
.arg("inp")
2406+
.succeeds();
2407+
2408+
at.write_bytes("check", compute.stdout());
2409+
2410+
ts.ucmd()
2411+
.arg("-a")
2412+
.arg("blake2b")
2413+
.arg("--check")
2414+
.arg("check")
2415+
.succeeds()
2416+
.stdout_only("inp: OK\n");
2417+
}
2418+
}
2419+
};
2420+
}
2421+
2422+
decl_blake_test!(blake2b_8, 8);
2423+
decl_blake_test!(blake2b_216, 216);
2424+
decl_blake_test!(blake2b_224, 224);
2425+
decl_blake_test!(blake2b_232, 232);
2426+
decl_blake_test!(blake2b_248, 248);
2427+
decl_blake_test!(blake2b_256, 256);
2428+
decl_blake_test!(blake2b_264, 264);
2429+
decl_blake_test!(blake2b_376, 376);
2430+
decl_blake_test!(blake2b_384, 384);
2431+
decl_blake_test!(blake2b_392, 392);
2432+
decl_blake_test!(blake2b_504, 504);
2433+
decl_blake_test!(blake2b_512, 512);
2434+
}
23222435
/// This module reimplements the cksum-c.sh GNU test.
23232436
mod gnu_cksum_c {
23242437
use super::*;

0 commit comments

Comments
 (0)