Skip to content

Commit 733b4ec

Browse files
committed
cksum: Introduce HashLength newtype to avoid byte/bit misuse
1 parent 1717352 commit 733b4ec

5 files changed

Lines changed: 136 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: 101 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::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 {
@@ -266,15 +306,15 @@ pub enum SizedAlgoKind {
266306
Sha2(ShaLength),
267307
Sha3(ShaLength),
268308
// Note: we store Blake*'s length as BYTES.
269-
Blake2b(usize),
270-
Blake3(usize),
309+
Blake2b(HashLength),
310+
Blake3(HashLength),
271311
// Shake* length are stored in bits.
272-
Shake128(Option<usize>),
273-
Shake256(Option<usize>),
312+
Shake128(Option<HashLength>),
313+
Shake256(Option<HashLength>),
274314
}
275315

276316
impl SizedAlgoKind {
277-
pub fn from_unsized(kind: AlgoKind, output_length: Option<usize>) -> UResult<Self> {
317+
pub fn from_unsized(kind: AlgoKind, output_length: Option<HashLength>) -> UResult<Self> {
278318
use AlgoKind as ak;
279319
match (kind, output_length) {
280320
(
@@ -300,12 +340,16 @@ impl SizedAlgoKind {
300340
(ak::Sm3, _) => Ok(Self::Sm3),
301341
(ak::Sha1, _) => Ok(Self::Sha1),
302342

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))),
343+
(ak::Blake2b, l) => Ok(Self::Blake2b(
344+
l.unwrap_or(HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE)),
345+
)),
346+
(ak::Blake3, l) => Ok(Self::Blake3(
347+
l.unwrap_or(HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE)),
348+
)),
305349
(ak::Shake128, l) => Ok(Self::Shake128(l)),
306350
(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)?)),
351+
(ak::Sha2, Some(l)) => Ok(Self::Sha2(ShaLength::try_from(l.as_bits())?)),
352+
(ak::Sha3, Some(l)) => Ok(Self::Sha3(ShaLength::try_from(l.as_bits())?)),
309353
(algo @ (ak::Sha2 | ak::Sha3), None) => {
310354
Err(ChecksumError::LengthRequiredForSha(algo.to_lowercase().into()).into())
311355
}
@@ -323,16 +367,16 @@ impl SizedAlgoKind {
323367
Self::Sha1 => "SHA1".into(),
324368
Self::Sha2(len) => format!("SHA{}", len.as_usize()),
325369
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!(
370+
Self::Blake2b(len) if len.as_bits() == Blake2b::DEFAULT_BIT_SIZE => "BLAKE2b".into(),
371+
Self::Blake2b(len) => format!("BLAKE2b-{}", len.as_bits()),
372+
Self::Blake3(len) => format!("BLAKE3-{}", len.as_bits()),
373+
Self::Shake128(opt_len) => format!(
330374
"SHAKE128-{}",
331-
opt_bit_len.unwrap_or(Shake128::DEFAULT_BIT_SIZE)
375+
opt_len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits)
332376
),
333-
Self::Shake256(opt_bit_len) => format!(
377+
Self::Shake256(opt_len) => format!(
334378
"SHAKE256-{}",
335-
opt_bit_len.unwrap_or(Shake256::DEFAULT_BIT_SIZE)
379+
opt_len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits)
336380
),
337381
Self::Sysv | Self::Bsd | Self::Crc | Self::Crc32b => {
338382
panic!("Should not be used for tagging")
@@ -358,14 +402,20 @@ impl SizedAlgoKind {
358402
Self::Sha3(Len256) => Box::new(Sha3_256::default()),
359403
Self::Sha3(Len384) => Box::new(Sha3_384::default()),
360404
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-
}
405+
Self::Blake2b(len) => Box::new(Blake2b::with_output_bytes(len.as_bytes())),
406+
Self::Blake3(len) => Box::new(Blake3::with_output_bytes(len.as_bytes())),
407+
Self::Shake128(len_opt) => Box::new(
408+
len_opt
409+
.map(HashLength::as_bits)
410+
.map(Shake128::with_output_bits)
411+
.unwrap_or_default(),
412+
),
413+
Self::Shake256(len_opt) => Box::new(
414+
len_opt
415+
.map(HashLength::as_bits)
416+
.map(Shake256::with_output_bits)
417+
.unwrap_or_default(),
418+
),
369419
}
370420
}
371421

@@ -380,10 +430,10 @@ impl SizedAlgoKind {
380430
Self::Sha1 => 160,
381431
Self::Sha2(len) => len.as_usize(),
382432
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),
433+
Self::Blake2b(len) => len.as_bits(),
434+
Self::Blake3(len) => len.as_bits(),
435+
Self::Shake128(len) => len.map_or(Shake128::DEFAULT_BIT_SIZE, HashLength::as_bits),
436+
Self::Shake256(len) => len.map_or(Shake256::DEFAULT_BIT_SIZE, HashLength::as_bits),
387437
}
388438
}
389439
pub fn is_legacy(&self) -> bool {
@@ -506,7 +556,7 @@ pub enum BlakeLength<'s> {
506556
/// Note: when the input is a string, validation may print error messages.
507557
/// Note: when the algo is Blake2b, values that are above 512
508558
/// (Blake2b::DEFAULT_BIT_SIZE) are errors.
509-
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<usize> {
559+
pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResult<HashLength> {
510560
debug_assert!(matches!(algo, AlgoKind::Blake2b | AlgoKind::Blake3));
511561

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

530580
if n == 0 {
531581
return Ok(match algo {
532-
AlgoKind::Blake2b => Blake2b::DEFAULT_BYTE_SIZE,
533-
AlgoKind::Blake3 => Blake3::DEFAULT_BYTE_SIZE,
582+
AlgoKind::Blake2b => HashLength::from_bits(Blake2b::DEFAULT_BIT_SIZE),
583+
AlgoKind::Blake3 => HashLength::from_bits(Blake3::DEFAULT_BIT_SIZE),
534584
_ => unreachable!(),
535585
});
536586
}
@@ -545,7 +595,7 @@ pub fn parse_blake_length(algo: AlgoKind, bit_length: BlakeLength<'_>) -> UResul
545595
return Err(ChecksumError::LengthNotMultipleOf8.into());
546596
}
547597

548-
Ok(n / 8)
598+
Ok(HashLength::from_bits(n))
549599
}
550600

551601
pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) -> UResult<ShaLength> {
@@ -562,7 +612,7 @@ pub fn validate_sha2_sha3_length(algo_name: AlgoKind, length: Option<usize>) ->
562612
}
563613
}
564614

565-
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<usize> {
615+
pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResult<HashLength> {
566616
// There is a difference in the errors sent when the length is not a number
567617
// vs. its an invalid number.
568618
//
@@ -580,7 +630,7 @@ pub fn sanitize_sha2_sha3_length_str(algo_kind: AlgoKind, length: &str) -> UResu
580630
};
581631

582632
if [224, 256, 384, 512].contains(&len) {
583-
Ok(len)
633+
Ok(HashLength::from_bits(len))
584634
} else {
585635
show_error!("{}", ChecksumError::InvalidLength(length.into()));
586636
Err(ChecksumError::InvalidLengthForSha(algo_kind.to_uppercase().into()).into())
@@ -664,17 +714,23 @@ mod tests {
664714
#[test]
665715
fn test_calculate_blake2b_length() {
666716
assert_eq!(
667-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0")).unwrap(),
717+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("0"))
718+
.unwrap()
719+
.as_bytes(),
668720
Blake2b::DEFAULT_BYTE_SIZE
669721
);
670722
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("10")).is_err());
671723
assert!(parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("520")).is_err());
672724
assert_eq!(
673-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512")).unwrap(),
725+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("512"))
726+
.unwrap()
727+
.as_bytes(),
674728
Blake2b::DEFAULT_BYTE_SIZE
675729
);
676730
assert_eq!(
677-
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256")).unwrap(),
731+
parse_blake_length(AlgoKind::Blake2b, BlakeLength::String("256"))
732+
.unwrap()
733+
.as_bytes(),
678734
32
679735
);
680736
}

0 commit comments

Comments
 (0)