Skip to content

Commit 7a67af5

Browse files
committed
cksum: Add support for blake3
1 parent 209bb97 commit 7a67af5

5 files changed

Lines changed: 96 additions & 54 deletions

File tree

src/uu/b2sum/src/b2sum.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,14 @@ use clap::Command;
99

1010
use uu_checksum_common::{standalone_checksum_app_with_length, standalone_with_length_main};
1111

12-
use uucore::checksum::{AlgoKind, calculate_blake2b_length_str};
12+
use uucore::checksum::{AlgoKind, calculate_blake_length_str};
1313
use uucore::error::UResult;
1414
use uucore::translate;
1515

1616
#[uucore::main]
1717
pub fn uumain(args: impl uucore::Args) -> UResult<()> {
18-
standalone_with_length_main(
19-
AlgoKind::Blake2b,
20-
uu_app(),
21-
args,
22-
calculate_blake2b_length_str,
23-
)
18+
let calculate_blake2b_length = |s: &str| calculate_blake_length_str(AlgoKind::Blake2b, s);
19+
standalone_with_length_main(AlgoKind::Blake2b, uu_app(), args, calculate_blake2b_length)
2420
}
2521

2622
#[inline]

src/uu/cksum/src/cksum.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ use uu_checksum_common::{ChecksumCommand, checksum_main, default_checksum_app, o
1212

1313
use uucore::checksum::compute::OutputFormat;
1414
use uucore::checksum::{
15-
AlgoKind, ChecksumError, calculate_blake2b_length_str, sanitize_sha2_sha3_length_str,
15+
AlgoKind, ChecksumError, calculate_blake_length_str, sanitize_sha2_sha3_length_str,
1616
};
1717
use uucore::error::UResult;
1818
use uucore::hardware::{HasHardwareFeatures as _, SimdPolicy};
@@ -67,8 +67,10 @@ fn maybe_sanitize_length(
6767
Err(_) => Err(ChecksumError::InvalidLength(len.into()).into()),
6868
},
6969

70-
// For BLAKE2b, if a length is provided, validate it.
71-
(Some(AlgoKind::Blake2b), Some(len)) => calculate_blake2b_length_str(len),
70+
// For BLAKE, if a length is provided, validate it.
71+
(Some(algo @ (AlgoKind::Blake2b | AlgoKind::Blake3)), Some(len)) => {
72+
calculate_blake_length_str(algo, len)
73+
}
7274

7375
// For any other provided algorithm, check if length is 0.
7476
// Otherwise, this is an error.

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

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ impl AlgoKind {
115115
ALGORITHM_OPTIONS_SHA384 => Sha384,
116116
ALGORITHM_OPTIONS_SHA512 => Sha512,
117117

118+
// Extensions not in GNU as of version 9.10
119+
ALGORITHM_OPTIONS_BLAKE3 => Blake3,
118120
ALGORITHM_OPTIONS_SHAKE128 => Shake128,
119121
ALGORITHM_OPTIONS_SHAKE256 => Shake256,
120122
_ => return Err(ChecksumError::UnknownAlgorithm(algo.as_ref().to_string()).into()),
@@ -245,11 +247,11 @@ pub enum SizedAlgoKind {
245247
Md5,
246248
Sm3,
247249
Sha1,
248-
Blake3,
249250
Sha2(ShaLength),
250251
Sha3(ShaLength),
251-
// Note: we store Blake2b's length as BYTES.
252+
// Note: we store Blake*'s length as BYTES.
252253
Blake2b(Option<usize>),
254+
Blake3(Option<usize>),
253255
// Shake* length are stored in bits.
254256
Shake128(Option<usize>),
255257
Shake256(Option<usize>),
@@ -267,7 +269,6 @@ impl SizedAlgoKind {
267269
| ak::Md5
268270
| ak::Sm3
269271
| ak::Sha1
270-
| ak::Blake3
271272
| ak::Sha224
272273
| ak::Sha256
273274
| ak::Sha384
@@ -282,8 +283,8 @@ impl SizedAlgoKind {
282283
(ak::Md5, _) => Ok(Self::Md5),
283284
(ak::Sm3, _) => Ok(Self::Sm3),
284285
(ak::Sha1, _) => Ok(Self::Sha1),
285-
(ak::Blake3, _) => Ok(Self::Blake3),
286286

287+
(ak::Blake3, l) => Ok(Self::Blake3(l)),
287288
(ak::Shake128, l) => Ok(Self::Shake128(l)),
288289
(ak::Shake256, l) => Ok(Self::Shake256(l)),
289290
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l)?)),
@@ -293,7 +294,8 @@ impl SizedAlgoKind {
293294
}
294295
// [`calculate_blake2b_length`] expects a length in bits but we
295296
// have a length in bytes.
296-
(ak::Blake2b, Some(l)) => Ok(Self::Blake2b(calculate_blake2b_length_str(
297+
(algo @ ak::Blake2b, Some(l)) => Ok(Self::Blake2b(calculate_blake_length_str(
298+
algo,
297299
&(8 * l).to_string(),
298300
)?)),
299301
(ak::Blake2b, None) => Ok(Self::Blake2b(None)),
@@ -310,11 +312,16 @@ impl SizedAlgoKind {
310312
Self::Md5 => "MD5".into(),
311313
Self::Sm3 => "SM3".into(),
312314
Self::Sha1 => "SHA1".into(),
313-
Self::Blake3 => "BLAKE3".into(),
314315
Self::Sha2(len) => format!("SHA{}", len.as_usize()),
315316
Self::Sha3(len) => format!("SHA3-{}", len.as_usize()),
316317
Self::Blake2b(Some(byte_len)) => format!("BLAKE2b-{}", byte_len * 8),
317318
Self::Blake2b(None) => "BLAKE2b".into(),
319+
Self::Blake3(byte_len) => {
320+
format!(
321+
"BLAKE3-{}",
322+
byte_len.unwrap_or(Blake3::DEFAULT_BYTE_SIZE) * 8
323+
)
324+
}
318325
Self::Shake128(opt_bit_len) => format!(
319326
"SHAKE128-{}",
320327
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
@@ -339,7 +346,6 @@ impl SizedAlgoKind {
339346
Self::Md5 => Box::new(Md5::default()),
340347
Self::Sm3 => Box::new(Sm3::default()),
341348
Self::Sha1 => Box::new(Sha1::default()),
342-
Self::Blake3 => Box::new(Blake3::default()),
343349
Self::Sha2(Len224) => Box::new(Sha224::default()),
344350
Self::Sha2(Len256) => Box::new(Sha256::default()),
345351
Self::Sha2(Len384) => Box::new(Sha384::default()),
@@ -351,6 +357,9 @@ impl SizedAlgoKind {
351357
Self::Blake2b(len_opt) => {
352358
Box::new(len_opt.map(Blake2b::with_output_bytes).unwrap_or_default())
353359
}
360+
Self::Blake3(len_opt) => {
361+
Box::new(len_opt.map(Blake3::with_output_bytes).unwrap_or_default())
362+
}
354363
Self::Shake128(len_opt) => {
355364
Box::new(len_opt.map(Shake128::with_output_bits).unwrap_or_default())
356365
}
@@ -369,7 +378,7 @@ impl SizedAlgoKind {
369378
Self::Md5 => 128,
370379
Self::Sm3 => 512,
371380
Self::Sha1 => 160,
372-
Self::Blake3 => 256,
381+
Self::Blake3(len) => len.unwrap_or(Blake3::DEFAULT_BYTE_SIZE) * 8,
373382
Self::Sha2(len) => len.as_usize(),
374383
Self::Sha3(len) => len.as_usize(),
375384
Self::Blake2b(len) => len.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE * 8),
@@ -486,20 +495,22 @@ pub fn digest_reader<T: Read>(
486495
Ok((digest.result(), output_size))
487496
}
488497

489-
/// Calculates the length of the digest.
490-
pub fn calculate_blake2b_length_str(bit_length: &str) -> UResult<Option<usize>> {
498+
/// Calculates the BYTE length of the digest.
499+
pub fn calculate_blake_length_str(algo: AlgoKind, bit_length: &str) -> UResult<Option<usize>> {
500+
debug_assert!(matches!(algo, AlgoKind::Blake2b | AlgoKind::Blake3));
501+
491502
// Blake2b's length is parsed in an u64.
492503
match bit_length.parse::<usize>() {
493504
Ok(0) => Ok(None),
494505

495506
// Error cases
496-
Ok(n) if n > 512 => {
507+
Ok(n) if n > 512 && algo == AlgoKind::Blake2b => {
497508
show_error!("{}", ChecksumError::InvalidLength(bit_length.into()));
498-
Err(ChecksumError::LengthTooBigForBlake("BLAKE2b".into()).into())
509+
Err(ChecksumError::LengthTooBigForBlake(algo.to_uppercase().into()).into())
499510
}
500511
Err(e) if *e.kind() == IntErrorKind::PosOverflow => {
501512
show_error!("{}", ChecksumError::InvalidLength(bit_length.into()));
502-
Err(ChecksumError::LengthTooBigForBlake("BLAKE2b".into()).into())
513+
Err(ChecksumError::LengthTooBigForBlake(algo.to_uppercase().into()).into())
503514
}
504515
Err(_) => Err(ChecksumError::InvalidLength(bit_length.into()).into()),
505516

@@ -632,10 +643,19 @@ mod tests {
632643

633644
#[test]
634645
fn test_calculate_blake2b_length() {
635-
assert_eq!(calculate_blake2b_length_str("0").unwrap(), None);
636-
assert!(calculate_blake2b_length_str("10").is_err());
637-
assert!(calculate_blake2b_length_str("520").is_err());
638-
assert_eq!(calculate_blake2b_length_str("512").unwrap(), None);
639-
assert_eq!(calculate_blake2b_length_str("256").unwrap(), Some(32));
646+
assert_eq!(
647+
calculate_blake_length_str(AlgoKind::Blake2b, "0").unwrap(),
648+
None
649+
);
650+
assert!(calculate_blake_length_str(AlgoKind::Blake2b, "10").is_err());
651+
assert!(calculate_blake_length_str(AlgoKind::Blake2b, "520").is_err());
652+
assert_eq!(
653+
calculate_blake_length_str(AlgoKind::Blake2b, "512").unwrap(),
654+
None
655+
);
656+
assert_eq!(
657+
calculate_blake_length_str(AlgoKind::Blake2b, "256").unwrap(),
658+
Some(32)
659+
);
640660
}
641661
}

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

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -614,6 +614,7 @@ fn identify_algo_name_and_length(
614614
algo_name_input: Option<AlgoKind>,
615615
last_algo: &mut Option<String>,
616616
) -> Result<(AlgoKind, Option<usize>), LineCheckError> {
617+
use AlgoKind as ak;
617618
let algo_from_line = line_info.algo_name.clone().unwrap_or_default();
618619
let Ok(line_algo) = AlgoKind::from_cksum(algo_from_line.to_lowercase()) else {
619620
// Unknown algorithm
@@ -629,34 +630,33 @@ fn identify_algo_name_and_length(
629630
match (algo_name_input, line_algo) {
630631
(l, r) if l == r => (),
631632
// Edge case for SHA2, which matches SHA(224|256|384|512)
632-
(
633-
AlgoKind::Sha2,
634-
AlgoKind::Sha224 | AlgoKind::Sha256 | AlgoKind::Sha384 | AlgoKind::Sha512,
635-
) => (),
633+
(ak::Sha2, ak::Sha224 | ak::Sha256 | ak::Sha384 | ak::Sha512) => (),
636634
_ => return Err(LineCheckError::ImproperlyFormatted),
637635
}
638636
}
639637

640638
let bytes = if let Some(bitlen) = line_info.algo_bit_len {
641639
match line_algo {
642-
AlgoKind::Blake2b if bitlen % 8 == 0 => Some(bitlen / 8),
643-
AlgoKind::Sha2 | AlgoKind::Sha3 if [224, 256, 384, 512].contains(&bitlen) => {
644-
Some(bitlen)
645-
}
646-
AlgoKind::Shake128 | AlgoKind::Shake256 => Some(bitlen),
640+
ak::Blake2b | ak::Blake3 if bitlen % 8 == 0 => Some(bitlen / 8),
641+
ak::Sha2 | ak::Sha3 if [224, 256, 384, 512].contains(&bitlen) => Some(bitlen),
642+
ak::Shake128 | ak::Shake256 => Some(bitlen),
647643
// Either
648-
// the algo based line is provided with a bit length
649-
// with an algorithm that does not support it (only Blake2B does).
644+
// the algo based line is provided with a bit length with an
645+
// algorithm that does not support it (only Blake2b, Blake3, sha2,
646+
// and sha3 do).
650647
//
651648
// eg: MD5-128 (foo.txt) = fffffffff
652649
// ^ This is illegal
653650
// OR
654651
// the given length is wrong because it's not a multiple of 8.
655652
_ => return Err(LineCheckError::ImproperlyFormatted),
656653
}
657-
} else if line_algo == AlgoKind::Blake2b {
654+
} else if line_algo == ak::Blake2b {
658655
// Default length with BLAKE2b,
659656
Some(64)
657+
} else if line_algo == ak::Blake3 {
658+
// Default length with BLAKE3,
659+
Some(32)
660660
} else {
661661
None
662662
};
@@ -741,7 +741,7 @@ fn process_algo_based_line(
741741
// If the digest bitlen is known, we can check the format of the expected
742742
// checksum with it.
743743
let digest_char_length_hint = match (algo_kind, algo_byte_len) {
744-
(AlgoKind::Blake2b, Some(byte_len)) => Some(byte_len),
744+
(AlgoKind::Blake2b | AlgoKind::Blake3, Some(byte_len)) => Some(byte_len),
745745
(AlgoKind::Shake128 | AlgoKind::Shake256, Some(bit_len)) => Some(bit_len.div_ceil(8)),
746746
(AlgoKind::Shake128, None) => Some(sum::Shake128::DEFAULT_BIT_SIZE.div_ceil(8)),
747747
(AlgoKind::Shake256, None) => Some(sum::Shake256::DEFAULT_BIT_SIZE.div_ceil(8)),
@@ -764,6 +764,7 @@ fn process_non_algo_based_line(
764764
cli_algo_length: Option<usize>,
765765
opts: ChecksumValidateOptions,
766766
) -> Result<(), LineCheckError> {
767+
use AlgoKind as ak;
767768
let mut filename_to_check = line_info.filename.as_slice();
768769
if filename_to_check.starts_with(b"*")
769770
&& line_number == 0
@@ -778,16 +779,16 @@ fn process_non_algo_based_line(
778779
// When a specific algorithm name is input, use it and use the provided
779780
// bits except when dealing with blake2b, sha2 and sha3, where we will
780781
// detect the length.
781-
let (algo_kind, algo_byte_len) = match cli_algo_kind {
782-
AlgoKind::Blake2b => (AlgoKind::Blake2b, Some(expected_checksum.len())),
783-
algo @ (AlgoKind::Sha2 | AlgoKind::Sha3) => {
782+
let algo_byte_len = match cli_algo_kind {
783+
ak::Blake2b | ak::Blake3 => Some(expected_checksum.len()),
784+
ak::Sha2 | ak::Sha3 => {
784785
// multiplication by 8 to get the number of bits
785-
(algo, Some(expected_checksum.len() * 8))
786+
Some(expected_checksum.len() * 8)
786787
}
787-
_ => (cli_algo_kind, cli_algo_length),
788+
_ => cli_algo_length,
788789
};
789790

790-
let algo = SizedAlgoKind::from_unsized(algo_kind, algo_byte_len)?;
791+
let algo = SizedAlgoKind::from_unsized(cli_algo_kind, algo_byte_len)?;
791792

792793
compute_and_check_digest_from_file(filename_to_check, &expected_checksum, algo, opts)
793794
}

src/uucore/src/lib/features/sum.rs

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -122,25 +122,48 @@ impl Digest for Blake2b {
122122
}
123123
}
124124

125-
#[derive(Default)]
126-
pub struct Blake3(blake3::Hasher);
125+
pub struct Blake3 {
126+
digest: blake3::Hasher,
127+
byte_size: usize,
128+
}
129+
130+
impl Blake3 {
131+
/// Default length for the BLAKE3 digest in bytes.
132+
pub const DEFAULT_BYTE_SIZE: usize = 32;
133+
134+
pub fn with_output_bytes(output_bytes: usize) -> Self {
135+
Self {
136+
digest: blake3::Hasher::new(),
137+
byte_size: output_bytes,
138+
}
139+
}
140+
}
141+
142+
impl Default for Blake3 {
143+
fn default() -> Self {
144+
Self {
145+
digest: blake3::Hasher::default(),
146+
byte_size: Self::DEFAULT_BYTE_SIZE,
147+
}
148+
}
149+
}
127150

128151
impl Digest for Blake3 {
129152
fn hash_update(&mut self, input: &[u8]) {
130-
self.0.update(input);
153+
self.digest.update(input);
131154
}
132155

133156
fn hash_finalize(&mut self, out: &mut [u8]) {
134-
let hash_result = &self.0.finalize();
135-
out.copy_from_slice(hash_result.as_bytes());
157+
let mut hash_result = self.digest.finalize_xof();
158+
hash_result.fill(out);
136159
}
137160

138161
fn reset(&mut self) {
139-
*self = Self::default();
162+
*self = Self::with_output_bytes(self.output_bytes());
140163
}
141164

142165
fn output_bits(&self) -> usize {
143-
256
166+
self.byte_size * 8
144167
}
145168
}
146169

0 commit comments

Comments
 (0)