Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 105 additions & 99 deletions src/uu/hashsum/src/hashsum.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,13 @@ use std::io::{self, stdin, BufRead, BufReader, Read};
use std::iter;
use std::num::ParseIntError;
use std::path::Path;
use uucore::error::USimpleError;
use uucore::error::{FromIo, UError, UResult};
use uucore::sum::{
Blake2b, Blake3, Digest, DigestWriter, Md5, Sha1, Sha224, Sha256, Sha384, Sha3_224, Sha3_256,
Sha3_384, Sha3_512, Sha512, Shake128, Shake256,
};
use uucore::{crash, display::Quotable, show_warning};
use uucore::{display::Quotable, show_warning};
use uucore::{format_usage, help_about, help_usage};

const NAME: &str = "hashsum";
Expand All @@ -51,28 +52,32 @@ struct Options {
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
///
/// # Panics
///
/// Panics if the length is not a multiple of 8 or if it is greater than 512.
fn create_blake2b(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits or an Err if the length is not a multiple of 8 or if it is
/// greater than 512.
fn create_blake2b(matches: &ArgMatches) -> UResult<(&'static str, Box<dyn Digest>, usize)> {
match matches.get_one::<usize>("length") {
Some(0) | None => ("BLAKE2", Box::new(Blake2b::new()) as Box<dyn Digest>, 512),
Some(0) | None => Ok(("BLAKE2", Box::new(Blake2b::new()) as Box<dyn Digest>, 512)),
Some(length_in_bits) => {
if *length_in_bits > 512 {
crash!(1, "Invalid length (maximum digest length is 512 bits)")
return Err(USimpleError::new(
1,
"Invalid length (maximum digest length is 512 bits)",
));
}

if length_in_bits % 8 == 0 {
let length_in_bytes = length_in_bits / 8;
(
Ok((
"BLAKE2",
Box::new(Blake2b::with_output_bytes(length_in_bytes)),
*length_in_bits,
)
))
} else {
crash!(1, "Invalid length (expected a multiple of 8)")
Err(USimpleError::new(
1,
"Invalid length (expected a multiple of 8)",
))
}
}
}
Expand All @@ -82,78 +87,70 @@ fn create_blake2b(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
///
/// # Panics
///
/// Panics if an unsupported output size is provided, or if the `--bits` flag is missing.
fn create_sha3(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits or an Err if an unsupported output size is provided, or if
/// the `--bits` flag is missing.
fn create_sha3(matches: &ArgMatches) -> UResult<(&'static str, Box<dyn Digest>, usize)> {
match matches.get_one::<usize>("bits") {
Some(224) => (
Some(224) => Ok((
"SHA3-224",
Box::new(Sha3_224::new()) as Box<dyn Digest>,
224,
),
Some(256) => (
)),
Some(256) => Ok((
"SHA3-256",
Box::new(Sha3_256::new()) as Box<dyn Digest>,
256,
),
Some(384) => (
)),
Some(384) => Ok((
"SHA3-384",
Box::new(Sha3_384::new()) as Box<dyn Digest>,
384,
),
Some(512) => (
)),
Some(512) => Ok((
"SHA3-512",
Box::new(Sha3_512::new()) as Box<dyn Digest>,
512,
),
Some(_) => crash!(
)),
Some(_) => Err(USimpleError::new(
1,
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)"
),
None => crash!(1, "--bits required for SHA3"),
"Invalid output size for SHA3 (expected 224, 256, 384, or 512)",
)),
None => Err(USimpleError::new(1, "--bits required for SHA3")),
}
}

/// Creates a SHAKE-128 hasher instance based on the specified bits argument.
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
///
/// # Panics
///
/// Panics if the `--bits` flag is missing.
fn create_shake128(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits, or an Err if `--bits` flag is missing.
fn create_shake128(matches: &ArgMatches) -> UResult<(&'static str, Box<dyn Digest>, usize)> {
match matches.get_one::<usize>("bits") {
Some(bits) => (
Some(bits) => Ok((
"SHAKE128",
Box::new(Shake128::new()) as Box<dyn Digest>,
*bits,
),
None => crash!(1, "--bits required for SHAKE-128"),
)),
None => Err(USimpleError::new(1, "--bits required for SHAKE-128")),
}
}

/// Creates a SHAKE-256 hasher instance based on the specified bits argument.
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
///
/// # Panics
///
/// Panics if the `--bits` flag is missing.
fn create_shake256(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits, or an Err if the `--bits` flag is missing.
fn create_shake256(matches: &ArgMatches) -> UResult<(&'static str, Box<dyn Digest>, usize)> {
match matches.get_one::<usize>("bits") {
Some(bits) => (
Some(bits) => Ok((
"SHAKE256",
Box::new(Shake256::new()) as Box<dyn Digest>,
*bits,
),
None => crash!(1, "--bits required for SHAKE-256"),
)),
None => Err(USimpleError::new(1, "--bits required for SHAKE-256")),
}
}

Expand All @@ -166,46 +163,46 @@ fn create_shake256(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usiz
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits, or an Err if a matching algorithm is not found.
fn detect_algo(
program: &str,
matches: &ArgMatches,
) -> (&'static str, Box<dyn Digest + 'static>, usize) {
let (name, alg, output_bits) = match program {
"md5sum" => ("MD5", Box::new(Md5::new()) as Box<dyn Digest>, 128),
"sha1sum" => ("SHA1", Box::new(Sha1::new()) as Box<dyn Digest>, 160),
"sha224sum" => ("SHA224", Box::new(Sha224::new()) as Box<dyn Digest>, 224),
"sha256sum" => ("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256),
"sha384sum" => ("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384),
"sha512sum" => ("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512),
) -> UResult<(&'static str, Box<dyn Digest + 'static>, usize)> {
match program {
"md5sum" => Ok(("MD5", Box::new(Md5::new()) as Box<dyn Digest>, 128)),
"sha1sum" => Ok(("SHA1", Box::new(Sha1::new()) as Box<dyn Digest>, 160)),
"sha224sum" => Ok(("SHA224", Box::new(Sha224::new()) as Box<dyn Digest>, 224)),
"sha256sum" => Ok(("SHA256", Box::new(Sha256::new()) as Box<dyn Digest>, 256)),
"sha384sum" => Ok(("SHA384", Box::new(Sha384::new()) as Box<dyn Digest>, 384)),
"sha512sum" => Ok(("SHA512", Box::new(Sha512::new()) as Box<dyn Digest>, 512)),
"b2sum" => create_blake2b(matches),
"b3sum" => ("BLAKE3", Box::new(Blake3::new()) as Box<dyn Digest>, 256),
"b3sum" => Ok(("BLAKE3", Box::new(Blake3::new()) as Box<dyn Digest>, 256)),
"sha3sum" => create_sha3(matches),
"sha3-224sum" => (
"sha3-224sum" => Ok((
"SHA3-224",
Box::new(Sha3_224::new()) as Box<dyn Digest>,
224,
),
"sha3-256sum" => (
)),
"sha3-256sum" => Ok((
"SHA3-256",
Box::new(Sha3_256::new()) as Box<dyn Digest>,
256,
),
"sha3-384sum" => (
)),
"sha3-384sum" => Ok((
"SHA3-384",
Box::new(Sha3_384::new()) as Box<dyn Digest>,
384,
),
"sha3-512sum" => (
)),
"sha3-512sum" => Ok((
"SHA3-512",
Box::new(Sha3_512::new()) as Box<dyn Digest>,
512,
),
)),
"shake128sum" => create_shake128(matches),
"shake256sum" => create_shake256(matches),
_ => create_algorithm_from_flags(matches),
};
(name, alg, output_bits)
}
}

/// Creates a hasher instance based on the command-line flags.
Expand All @@ -216,80 +213,89 @@ fn detect_algo(
///
/// # Returns
///
/// Returns a tuple containing the algorithm name, the hasher instance, and the output length in bits.
///
/// # Panics
///
/// Panics if multiple hash algorithms are specified or if a required flag is missing.
/// Returns a UResult of a tuple containing the algorithm name, the hasher instance, and
/// the output length in bits or an Err if multiple hash algorithms are specified or if a
/// required flag is missing.
#[allow(clippy::cognitive_complexity)]
fn create_algorithm_from_flags(matches: &ArgMatches) -> (&'static str, Box<dyn Digest>, usize) {
fn create_algorithm_from_flags(
matches: &ArgMatches,
) -> UResult<(&'static str, Box<dyn Digest>, usize)> {
let mut alg: Option<Box<dyn Digest>> = None;
let mut name: &'static str = "";
let mut output_bits = 0;
let mut set_or_crash = |n, val, bits| {
let mut set_or_err = |n, val, bits| {
if alg.is_some() {
crash!(1, "You cannot combine multiple hash algorithms!");
return Err(USimpleError::new(
1,
"You cannot combine multiple hash algorithms!",
));
};
name = n;
alg = Some(val);
output_bits = bits;

Ok(())
};

if matches.get_flag("md5") {
set_or_crash("MD5", Box::new(Md5::new()), 128);
set_or_err("MD5", Box::new(Md5::new()), 128)?;
}
if matches.get_flag("sha1") {
set_or_crash("SHA1", Box::new(Sha1::new()), 160);
set_or_err("SHA1", Box::new(Sha1::new()), 160)?;
}
if matches.get_flag("sha224") {
set_or_crash("SHA224", Box::new(Sha224::new()), 224);
set_or_err("SHA224", Box::new(Sha224::new()), 224)?;
}
if matches.get_flag("sha256") {
set_or_crash("SHA256", Box::new(Sha256::new()), 256);
set_or_err("SHA256", Box::new(Sha256::new()), 256)?;
}
if matches.get_flag("sha384") {
set_or_crash("SHA384", Box::new(Sha384::new()), 384);
set_or_err("SHA384", Box::new(Sha384::new()), 384)?;
}
if matches.get_flag("sha512") {
set_or_crash("SHA512", Box::new(Sha512::new()), 512);
set_or_err("SHA512", Box::new(Sha512::new()), 512)?;
}
if matches.get_flag("b2sum") {
set_or_crash("BLAKE2", Box::new(Blake2b::new()), 512);
set_or_err("BLAKE2", Box::new(Blake2b::new()), 512)?;
}
if matches.get_flag("b3sum") {
set_or_crash("BLAKE3", Box::new(Blake3::new()), 256);
set_or_err("BLAKE3", Box::new(Blake3::new()), 256)?;
}
if matches.get_flag("sha3") {
let (n, val, bits) = create_sha3(matches);
set_or_crash(n, val, bits);
let (n, val, bits) = create_sha3(matches)?;
set_or_err(n, val, bits)?;
}
if matches.get_flag("sha3-224") {
set_or_crash("SHA3-224", Box::new(Sha3_224::new()), 224);
set_or_err("SHA3-224", Box::new(Sha3_224::new()), 224)?;
}
if matches.get_flag("sha3-256") {
set_or_crash("SHA3-256", Box::new(Sha3_256::new()), 256);
set_or_err("SHA3-256", Box::new(Sha3_256::new()), 256)?;
}
if matches.get_flag("sha3-384") {
set_or_crash("SHA3-384", Box::new(Sha3_384::new()), 384);
set_or_err("SHA3-384", Box::new(Sha3_384::new()), 384)?;
}
if matches.get_flag("sha3-512") {
set_or_crash("SHA3-512", Box::new(Sha3_512::new()), 512);
set_or_err("SHA3-512", Box::new(Sha3_512::new()), 512)?;
}
if matches.get_flag("shake128") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_crash("SHAKE128", Box::new(Shake128::new()), *bits),
None => crash!(1, "--bits required for SHAKE-128"),
}
Some(bits) => set_or_err("SHAKE128", Box::new(Shake128::new()), *bits)?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE-128")),
};
}
if matches.get_flag("shake256") {
match matches.get_one::<usize>("bits") {
Some(bits) => set_or_crash("SHAKE256", Box::new(Shake256::new()), *bits),
None => crash!(1, "--bits required for SHAKE-256"),
}
Some(bits) => set_or_err("SHAKE256", Box::new(Shake256::new()), *bits)?,
None => return Err(USimpleError::new(1, "--bits required for SHAKE-256")),
};
}

let alg = alg.unwrap_or_else(|| crash!(1, "You must specify hash algorithm!"));
(name, alg, output_bits)
let alg = match alg {
Some(a) => a,
None => return Err(USimpleError::new(1, "You must specify hash algorithm!")),
};

Ok((name, alg, output_bits))
}

// TODO: return custom error type
Expand Down Expand Up @@ -319,7 +325,7 @@ pub fn uumain(mut args: impl uucore::Args) -> UResult<()> {
// least somewhat better from a user's perspective.
let matches = command.try_get_matches_from(args)?;

let (name, algo, bits) = detect_algo(&binary_name, &matches);
let (name, algo, bits) = detect_algo(&binary_name, &matches)?;

let binary = if matches.get_flag("binary") {
true
Expand Down