Skip to content

Commit e30f2a8

Browse files
committed
cksum: Introduce HashLength newtype to avoid byte/bit misuse
1 parent 5706b33 commit e30f2a8

5 files changed

Lines changed: 132 additions & 77 deletions

File tree

src/uu/checksum_common/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ use uucore::checksum::compute::{
1616
ChecksumComputeOptions, OutputFormat, perform_checksum_computation,
1717
};
1818
use uucore::checksum::validate::{self, ChecksumValidateOptions, ChecksumVerbose};
19-
use uucore::checksum::{AlgoKind, ChecksumError, SizedAlgoKind};
19+
use uucore::checksum::{AlgoKind, ChecksumError, HashLength, SizedAlgoKind};
2020
use uucore::error::UResult;
2121
use uucore::line_ending::LineEnding;
2222
use uucore::{crate_version, format_usage, localized_help_template, util_name};
@@ -64,7 +64,7 @@ pub fn standalone_with_length_main(
6464
algo: AlgoKind,
6565
cmd: Command,
6666
args: impl uucore::Args,
67-
validate_len: fn(&str) -> UResult<usize>,
67+
validate_len: fn(&str) -> UResult<HashLength>,
6868
) -> UResult<()> {
6969
let matches = uucore::clap_localization::handle_clap_result(cmd, args)?;
7070
let algo = Some(algo);
@@ -142,7 +142,7 @@ pub fn standalone_checksum_app(about: String, usage: String) -> Command {
142142
/// validation on arguments and proceeds in computing or checking mode.
143143
pub fn checksum_main(
144144
algo: Option<AlgoKind>,
145-
length: Option<usize>,
145+
length: Option<HashLength>,
146146
matches: ArgMatches,
147147
output_format: OutputFormat,
148148
) -> UResult<()> {

src/uu/cksum/src/cksum.rs

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

1313
use uucore::checksum::compute::OutputFormat;
1414
use uucore::checksum::{
15-
AlgoKind, BlakeLength, ChecksumError, parse_blake_length, sanitize_sha2_sha3_length_str,
15+
AlgoKind, BlakeLength, ChecksumError, HashLength, parse_blake_length,
16+
sanitize_sha2_sha3_length_str,
1617
};
1718
use uucore::error::UResult;
1819
use uucore::hardware::{HasHardwareFeatures as _, SimdPolicy};
@@ -47,7 +48,7 @@ fn print_cpu_debug_info() {
4748
fn maybe_sanitize_length(
4849
algo_cli: Option<AlgoKind>,
4950
input_length: Option<&str>,
50-
) -> UResult<Option<usize>> {
51+
) -> UResult<Option<HashLength>> {
5152
match (algo_cli, input_length) {
5253
// No provided length is not a problem so far.
5354
(_, None) => Ok(None),
@@ -63,7 +64,7 @@ fn maybe_sanitize_length(
6364
// will have its extra bits set to zero.
6465
(Some(AlgoKind::Shake128 | AlgoKind::Shake256), Some(len)) => match len.parse::<usize>() {
6566
Ok(0) => Ok(None),
66-
Ok(l) => Ok(Some(l)),
67+
Ok(l) => Ok(Some(HashLength::from_bits(l))),
6768
Err(_) => Err(ChecksumError::InvalidLength(len.into()).into()),
6869
},
6970

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

Lines changed: 97 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,15 @@ impl AlgoKind {
204204
/// When checking untagged format lines, non-XOF non-legacy algorithms
205205
/// should report "improperly formatted lines" if the digest length isn't
206206
/// equivalent to this.
207-
pub fn expected_digest_bit_len(self) -> Option<usize> {
207+
pub fn expected_digest_bit_len(self) -> Option<HashLength> {
208208
match self {
209-
Self::Md5 => Some(Md5::BIT_SIZE),
210-
Self::Sm3 => Some(Sm3::BIT_SIZE),
211-
Self::Sha1 => Some(Sha1::BIT_SIZE),
212-
Self::Sha224 => Some(Sha224::BIT_SIZE),
213-
Self::Sha256 => Some(Sha256::BIT_SIZE),
214-
Self::Sha384 => Some(Sha384::BIT_SIZE),
215-
Self::Sha512 => Some(Sha512::BIT_SIZE),
209+
Self::Md5 => Some(HashLength(Md5::BIT_SIZE)),
210+
Self::Sm3 => Some(HashLength(Sm3::BIT_SIZE)),
211+
Self::Sha1 => Some(HashLength(Sha1::BIT_SIZE)),
212+
Self::Sha224 => Some(HashLength(Sha224::BIT_SIZE)),
213+
Self::Sha256 => Some(HashLength(Sha256::BIT_SIZE)),
214+
Self::Sha384 => Some(HashLength(Sha384::BIT_SIZE)),
215+
Self::Sha512 => Some(HashLength(Sha512::BIT_SIZE)),
216216
_ => None,
217217
}
218218
}
@@ -253,6 +253,42 @@ impl TryFrom<usize> for ShaLength {
253253
}
254254
}
255255

256+
/// Stores a hash length in bits.
257+
#[derive(Debug, Clone, Copy)]
258+
pub struct HashLength(usize);
259+
260+
impl HashLength {
261+
#[must_use]
262+
#[inline]
263+
pub(crate) fn from_bytes(n: usize) -> Self {
264+
Self(n * 8)
265+
}
266+
267+
#[must_use]
268+
#[inline]
269+
pub fn from_bits(n: usize) -> Self {
270+
Self(n)
271+
}
272+
273+
#[must_use]
274+
#[inline]
275+
pub(crate) fn as_bits(self) -> usize {
276+
self.0
277+
}
278+
279+
#[must_use]
280+
#[inline]
281+
pub(crate) fn as_bytes(self) -> usize {
282+
self.0.div_ceil(8)
283+
}
284+
}
285+
286+
impl From<ShaLength> for HashLength {
287+
fn from(value: ShaLength) -> Self {
288+
Self(value.as_usize())
289+
}
290+
}
291+
256292
/// Represents an actual determined algorithm.
257293
#[derive(Debug, Clone, Copy)]
258294
pub enum SizedAlgoKind {
@@ -266,15 +302,15 @@ pub enum SizedAlgoKind {
266302
Sha2(ShaLength),
267303
Sha3(ShaLength),
268304
// Note: we store Blake*'s length as BYTES.
269-
Blake2b(usize),
270-
Blake3(usize),
305+
Blake2b(HashLength),
306+
Blake3(HashLength),
271307
// Shake* length are stored in bits.
272-
Shake128(Option<usize>),
273-
Shake256(Option<usize>),
308+
Shake128(Option<HashLength>),
309+
Shake256(Option<HashLength>),
274310
}
275311

276312
impl SizedAlgoKind {
277-
pub fn from_unsized(kind: AlgoKind, output_length: Option<usize>) -> UResult<Self> {
313+
pub fn from_unsized(kind: AlgoKind, output_length: Option<HashLength>) -> UResult<Self> {
278314
use AlgoKind as ak;
279315
match (kind, output_length) {
280316
(
@@ -300,12 +336,16 @@ impl SizedAlgoKind {
300336
(ak::Sm3, _) => Ok(Self::Sm3),
301337
(ak::Sha1, _) => Ok(Self::Sha1),
302338

303-
(ak::Blake2b, l) => Ok(Self::Blake2b(l.unwrap_or(Blake2b::DEFAULT_BYTE_SIZE))),
304-
(ak::Blake3, l) => Ok(Self::Blake3(l.unwrap_or(Blake3::DEFAULT_BYTE_SIZE))),
339+
(ak::Blake2b, l) => Ok(Self::Blake2b(
340+
l.unwrap_or(HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE)),
341+
)),
342+
(ak::Blake3, l) => Ok(Self::Blake3(
343+
l.unwrap_or(HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE)),
344+
)),
305345
(ak::Shake128, l) => Ok(Self::Shake128(l)),
306346
(ak::Shake256, l) => Ok(Self::Shake256(l)),
307-
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l)?)),
308-
(ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l)?)),
347+
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l.as_bits())?)),
348+
(ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l.as_bits())?)),
309349
(algo @ (ak::Sha2 | ak::Sha3), None) => {
310350
Err(ChecksumError::LengthRequiredForSha(algo.to_lowercase().into()).into())
311351
}
@@ -323,16 +363,16 @@ impl SizedAlgoKind {
323363
Self::Sha1 => "SHA1".into(),
324364
Self::Sha2(len) => format!("SHA{}", len.as_usize()),
325365
Self::Sha3(len) => format!("SHA3-{}", len.as_usize()),
326-
Self::Blake2b(Blake2b::DEFAULT_BYTE_SIZE) => "BLAKE2b".into(),
327-
Self::Blake2b(byte_len) => format!("BLAKE2b-{}", byte_len * 8),
328-
Self::Blake3(byte_len) => format!("BLAKE3-{}", byte_len * 8),
329-
Self::Shake128(opt_bit_len) => format!(
366+
Self::Blake2b(len) if len.as_bits() == Blake2b::DEFAULT_BIT_SIZE => "BLAKE2b".into(),
367+
Self::Blake2b(len) => format!("BLAKE2b-{}", len.as_bits()),
368+
Self::Blake3(len) => format!("BLAKE3-{}", len.as_bits()),
369+
Self::Shake128(opt_len) => format!(
330370
"SHAKE128-{}",
331-
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
371+
opt_len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits)
332372
),
333-
Self::Shake256(opt_bit_len) => format!(
373+
Self::Shake256(opt_len) => format!(
334374
"SHAKE256-{}",
335-
opt_bit_len.unwrap_or(Shake256::DEFAULT_BIT_SIZE)
375+
opt_len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits)
336376
),
337377
Self::Sysv | Self::Bsd | Self::Crc | Self::Crc32b => {
338378
panic!("Should not be used for tagging")
@@ -358,14 +398,20 @@ impl SizedAlgoKind {
358398
Self::Sha3(Len256) => Box::new(Sha3_256::default()),
359399
Self::Sha3(Len384) => Box::new(Sha3_384::default()),
360400
Self::Sha3(Len512) => Box::new(Sha3_512::default()),
361-
Self::Blake2b(len) => Box::new(Blake2b::with_output_bytes(*len)),
362-
Self::Blake3(len) => Box::new(Blake3::with_output_bytes(*len)),
363-
Self::Shake128(len_opt) => {
364-
Box::new(len_opt.map(Shake128::with_output_bits).unwrap_or_default())
365-
}
366-
Self::Shake256(len_opt) => {
367-
Box::new(len_opt.map(Shake256::with_output_bits).unwrap_or_default())
368-
}
401+
Self::Blake2b(len) => Box::new(Blake2b::with_output_bytes(len.as_bytes())),
402+
Self::Blake3(len) => Box::new(Blake3::with_output_bytes(len.as_bytes())),
403+
Self::Shake128(len_opt) => Box::new(
404+
len_opt
405+
.map(HashLength::as_bits)
406+
.map(Shake128::with_output_bits)
407+
.unwrap_or_default(),
408+
),
409+
Self::Shake256(len_opt) => Box::new(
410+
len_opt
411+
.map(HashLength::as_bits)
412+
.map(Shake256::with_output_bits)
413+
.unwrap_or_default(),
414+
),
369415
}
370416
}
371417

@@ -380,10 +426,10 @@ impl SizedAlgoKind {
380426
Self::Sha1 => 160,
381427
Self::Sha2(len) => len.as_usize(),
382428
Self::Sha3(len) => len.as_usize(),
383-
Self::Blake2b(len) => len * 8,
384-
Self::Blake3(len) => len * 8,
385-
Self::Shake128(len) => len.unwrap_or(Shake128::DEFAULT_BIT_SIZE),
386-
Self::Shake256(len) => len.unwrap_or(Shake256::DEFAULT_BIT_SIZE),
429+
Self::Blake2b(len) => len.as_bits(),
430+
Self::Blake3(len) => len.as_bits(),
431+
Self::Shake128(len) => len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits),
432+
Self::Shake256(len) => len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits),
387433
}
388434
}
389435
pub fn is_legacy(&self) -> bool {
@@ -506,7 +552,7 @@ pub enum BlakeLength<'s> {
506552
/// Note: when the input is a string, validation may print error messages.
507553
/// Note: when the algo is Blake2b, values that are above 512
508554
/// (Blake2b::DEFAULT_BIT_SIZE) are errors.
509-
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<usize> {
555+
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<HashLength> {
510556
debug_assert!(matches!(algo, AlgoKind::Blake2b | AlgoKind::Blake3));
511557

512558
let print_error = || {
@@ -529,8 +575,8 @@ pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResul
529575

530576
if n == 0 {
531577
return Ok(match algo {
532-
AlgoKind::Blake2b => Blake2b::DEFAULT_BYTE_SIZE,
533-
AlgoKind::Blake3 => Blake3::DEFAULT_BYTE_SIZE,
578+
AlgoKind::Blake2b => HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE),
579+
AlgoKind::Blake3 => HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE),
534580
_ => unreachable!(),
535581
});
536582
}
@@ -545,7 +591,7 @@ pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResul
545591
return Err(ChecksumError::LengthNotMultipleOf8.into());
546592
}
547593

548-
Ok(n / 8)
594+
Ok(HashLength::from_bits(n))
549595
}
550596

551597
pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) -> UResult<ShaLength> {
@@ -562,7 +608,7 @@ pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) ->
562608
}
563609
}
564610

565-
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<usize> {
611+
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<HashLength> {
566612
// There is a difference in the errors sent when the length is not a number
567613
// vs. its an invalid number.
568614
//
@@ -580,7 +626,7 @@ pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResu
580626
};
581627

582628
if [224, 256, 384, 512].contains(&len) {
583-
Ok(len)
629+
Ok(HashLength::from_bits(len))
584630
} else {
585631
show_error!("{}", ChecksumError::InvalidLength(length.into()));
586632
Err(ChecksumError::InvalidLengthForSha(algo_kind.to_uppercase().into()).into())
@@ -664,17 +710,23 @@ mod tests {
664710
#[test]
665711
fn test_calculate_blake2b_length() {
666712
assert_eq!(
667-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0")).unwrap(),
713+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0"))
714+
.unwrap()
715+
.as_bytes(),
668716
Blake2b::DEFAULT_BYTE_SIZE
669717
);
670718
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("10")).is_err());
671719
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("520")).is_err());
672720
assert_eq!(
673-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512")).unwrap(),
721+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512"))
722+
.unwrap()
723+
.as_bytes(),
674724
Blake2b::DEFAULT_BYTE_SIZE
675725
);
676726
assert_eq!(
677-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256")).unwrap(),
727+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256"))
728+
.unwrap()
729+
.as_bytes(),
678730
32
679731
);
680732
}

0 commit comments

Comments
 (0)