Skip to content

Commit 1b3469e

Browse files
committed
cksum: Introduce HashLength newtype to avoid byte/bit misuse
1 parent cb833df commit 1b3469e

5 files changed

Lines changed: 136 additions & 79 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: 101 additions & 47 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::from_bits(Md5::BIT_SIZE)),
210+
Self::Sm3 => Some(HashLength::from_bits(Sm3::BIT_SIZE)),
211+
Self::Sha1 => Some(HashLength::from_bits(Sha1::BIT_SIZE)),
212+
Self::Sha224 => Some(HashLength::from_bits(Sha224::BIT_SIZE)),
213+
Self::Sha256 => Some(HashLength::from_bits(Sha256::BIT_SIZE)),
214+
Self::Sha384 => Some(HashLength::from_bits(Sha384::BIT_SIZE)),
215+
Self::Sha512 => Some(HashLength::from_bits(Sha512::BIT_SIZE)),
216216
_ => None,
217217
}
218218
}
@@ -253,6 +253,46 @@ impl TryFrom<usize> for ShaLength {
253253
}
254254
}
255255

256+
/// Stores a hash length in bits.
257+
#[derive(Debug, Clone, Copy)]
258+
pub struct HashLength {
259+
bit_len: usize,
260+
}
261+
262+
impl HashLength {
263+
#[must_use]
264+
#[inline]
265+
pub(crate) fn from_bytes(n: usize) -> Self {
266+
Self { bit_len: n * 8 }
267+
}
268+
269+
#[must_use]
270+
#[inline]
271+
pub fn from_bits(n: usize) -> Self {
272+
Self { bit_len: n }
273+
}
274+
275+
#[must_use]
276+
#[inline]
277+
pub(crate) fn as_bits(self) -> usize {
278+
self.bit_len
279+
}
280+
281+
#[must_use]
282+
#[inline]
283+
pub(crate) fn as_bytes(self) -> usize {
284+
self.bit_len.div_ceil(8)
285+
}
286+
}
287+
288+
impl From<ShaLength> for HashLength {
289+
fn from(value: ShaLength) -> Self {
290+
Self {
291+
bit_len: value.as_usize(),
292+
}
293+
}
294+
}
295+
256296
/// Represents an actual determined algorithm.
257297
#[derive(Debug, Clone, Copy)]
258298
pub enum SizedAlgoKind {
@@ -265,16 +305,14 @@ pub enum SizedAlgoKind {
265305
Sha1,
266306
Sha2(ShaLength),
267307
Sha3(ShaLength),
268-
// Note: we store Blake*'s length as BYTES.
269-
Blake2b(usize),
270-
Blake3(usize),
271-
// Shake* length are stored in bits.
272-
Shake128(Option<usize>),
273-
Shake256(Option<usize>),
308+
Blake2b(HashLength),
309+
Blake3(HashLength),
310+
Shake128(Option<HashLength>),
311+
Shake256(Option<HashLength>),
274312
}
275313

276314
impl SizedAlgoKind {
277-
pub fn from_unsized(kind: AlgoKind, output_length: Option<usize>) -> UResult<Self> {
315+
pub fn from_unsized(kind: AlgoKind, output_length: Option<HashLength>) -> UResult<Self> {
278316
use AlgoKind as ak;
279317
match (kind, output_length) {
280318
(
@@ -300,12 +338,16 @@ impl SizedAlgoKind {
300338
(ak::Sm3, _) => Ok(Self::Sm3),
301339
(ak::Sha1, _) => Ok(Self::Sha1),
302340

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))),
341+
(ak::Blake2b, l) => Ok(Self::Blake2b(
342+
l.unwrap_or(HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE)),
343+
)),
344+
(ak::Blake3, l) => Ok(Self::Blake3(
345+
l.unwrap_or(HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE)),
346+
)),
305347
(ak::Shake128, l) => Ok(Self::Shake128(l)),
306348
(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)?)),
349+
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l.as_bits())?)),
350+
(ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l.as_bits())?)),
309351
(algo @ (ak::Sha2 | ak::Sha3), None) => {
310352
Err(ChecksumError::LengthRequiredForSha(algo.to_lowercase().into()).into())
311353
}
@@ -323,16 +365,16 @@ impl SizedAlgoKind {
323365
Self::Sha1 => "SHA1".into(),
324366
Self::Sha2(len) => format!("SHA{}", len.as_usize()),
325367
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!(
368+
Self::Blake2b(len) if len.as_bits() == Blake2b::DEFAULT_BIT_SIZE => "BLAKE2b".into(),
369+
Self::Blake2b(len) => format!("BLAKE2b-{}", len.as_bits()),
370+
Self::Blake3(len) => format!("BLAKE3-{}", len.as_bits()),
371+
Self::Shake128(opt_len) => format!(
330372
"SHAKE128-{}",
331-
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
373+
opt_len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits)
332374
),
333-
Self::Shake256(opt_bit_len) => format!(
375+
Self::Shake256(opt_len) => format!(
334376
"SHAKE256-{}",
335-
opt_bit_len.unwrap_or(Shake256::DEFAULT_BIT_SIZE)
377+
opt_len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits)
336378
),
337379
Self::Sysv | Self::Bsd | Self::Crc | Self::Crc32b => {
338380
panic!("Should not be used for tagging")
@@ -358,14 +400,20 @@ impl SizedAlgoKind {
358400
Self::Sha3(Len256) => Box::new(Sha3_256::default()),
359401
Self::Sha3(Len384) => Box::new(Sha3_384::default()),
360402
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-
}
403+
Self::Blake2b(len) => Box::new(Blake2b::with_output_bytes(len.as_bytes())),
404+
Self::Blake3(len) => Box::new(Blake3::with_output_bytes(len.as_bytes())),
405+
Self::Shake128(len_opt) => Box::new(
406+
len_opt
407+
.map(HashLength::as_bits)
408+
.map(Shake128::with_output_bits)
409+
.unwrap_or_default(),
410+
),
411+
Self::Shake256(len_opt) => Box::new(
412+
len_opt
413+
.map(HashLength::as_bits)
414+
.map(Shake256::with_output_bits)
415+
.unwrap_or_default(),
416+
),
369417
}
370418
}
371419

@@ -380,10 +428,10 @@ impl SizedAlgoKind {
380428
Self::Sha1 => 160,
381429
Self::Sha2(len) => len.as_usize(),
382430
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),
431+
Self::Blake2b(len) => len.as_bits(),
432+
Self::Blake3(len) => len.as_bits(),
433+
Self::Shake128(len) => len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits),
434+
Self::Shake256(len) => len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits),
387435
}
388436
}
389437
pub fn is_legacy(&self) -> bool {
@@ -506,7 +554,7 @@ pub enum BlakeLength<'s> {
506554
/// Note: when the input is a string, validation may print error messages.
507555
/// Note: when the algo is Blake2b, values that are above 512
508556
/// (Blake2b::DEFAULT_BIT_SIZE) are errors.
509-
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<usize> {
557+
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<HashLength> {
510558
debug_assert!(matches!(algo, AlgoKind::Blake2b | AlgoKind::Blake3));
511559

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

530578
if n == 0 {
531579
return Ok(match algo {
532-
AlgoKind::Blake2b => Blake2b::DEFAULT_BYTE_SIZE,
533-
AlgoKind::Blake3 => Blake3::DEFAULT_BYTE_SIZE,
580+
AlgoKind::Blake2b => HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE),
581+
AlgoKind::Blake3 => HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE),
534582
_ => unreachable!(),
535583
});
536584
}
@@ -545,7 +593,7 @@ pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResul
545593
return Err(ChecksumError::LengthNotMultipleOf8.into());
546594
}
547595

548-
Ok(n / 8)
596+
Ok(HashLength::from_bits(n))
549597
}
550598

551599
pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) -> UResult<ShaLength> {
@@ -562,7 +610,7 @@ pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) ->
562610
}
563611
}
564612

565-
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<usize> {
613+
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<HashLength> {
566614
// There is a difference in the errors sent when the length is not a number
567615
// vs. its an invalid number.
568616
//
@@ -580,7 +628,7 @@ pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResu
580628
};
581629

582630
if [224, 256, 384, 512].contains(&len) {
583-
Ok(len)
631+
Ok(HashLength::from_bits(len))
584632
} else {
585633
show_error!("{}", ChecksumError::InvalidLength(length.into()));
586634
Err(ChecksumError::InvalidLengthForSha(algo_kind.to_uppercase().into()).into())
@@ -664,17 +712,23 @@ mod tests {
664712
#[test]
665713
fn test_calculate_blake2b_length() {
666714
assert_eq!(
667-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0")).unwrap(),
715+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0"))
716+
.unwrap()
717+
.as_bytes(),
668718
Blake2b::DEFAULT_BYTE_SIZE
669719
);
670720
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("10")).is_err());
671721
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("520")).is_err());
672722
assert_eq!(
673-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512")).unwrap(),
723+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512"))
724+
.unwrap()
725+
.as_bytes(),
674726
Blake2b::DEFAULT_BYTE_SIZE
675727
);
676728
assert_eq!(
677-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256")).unwrap(),
729+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256"))
730+
.unwrap()
731+
.as_bytes(),
678732
32
679733
);
680734
}

0 commit comments

Comments
 (0)