From f0099a295877b05665c334c6e1ef95a7ec82b1ce Mon Sep 17 00:00:00 2001 From: Nathan Fisher Date: Sat, 4 Feb 2023 11:06:24 -0500 Subject: [PATCH] Add `b2sum` applet - switch to `blake2b_simd` crate to get around limitations of `blake2` crate, which will not accept a non-const numerical specifier for length --- Cargo.lock | 37 ++++++++++----- Cargo.toml | 2 +- src/cmd/b2sum/mod.rs | 104 +++++++++++++++++++++++++++++++++++++++++++ src/cmd/mod.rs | 5 ++- 4 files changed, 134 insertions(+), 14 deletions(-) create mode 100644 src/cmd/b2sum/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8b94988..59070ec 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "atty" version = "0.2.14" @@ -20,12 +32,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "blake2" -version = "0.10.6" +name = "blake2b_simd" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" +checksum = "72936ee4afc7f8f736d1c38383b56480b5497b4617b4a77bdbf1d2ababc76127" dependencies = [ - "digest", + "arrayref", + "arrayvec", + "constant_time_eq", ] [[package]] @@ -100,6 +114,12 @@ dependencies = [ "roff", ] +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "cpufeatures" version = "0.2.5" @@ -133,7 +153,6 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f" dependencies = [ "block-buffer", "crypto-common", - "subtle", ] [[package]] @@ -293,7 +312,7 @@ name = "shitbox" version = "0.1.0" dependencies = [ "atty", - "blake2", + "blake2b_simd", "clap", "clap_complete", "clap_complete_nushell", @@ -322,12 +341,6 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" -[[package]] -name = "subtle" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" - [[package]] name = "termcolor" version = "1.2.0" diff --git a/Cargo.toml b/Cargo.toml index a12454e..800ece7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" [dependencies] atty = "0.2" -blake2 = "0.10" +blake2b_simd = "1.0" clap = "4.1" clap_complete = "4.1" clap_complete_nushell = "0.1" diff --git a/src/cmd/b2sum/mod.rs b/src/cmd/b2sum/mod.rs new file mode 100644 index 0000000..ad76675 --- /dev/null +++ b/src/cmd/b2sum/mod.rs @@ -0,0 +1,104 @@ +use super::Cmd; +use crate::args; +use blake2b_simd::Params; +use clap::{value_parser, Arg, Command}; +use std::{ + fs::File, + io::{self, BufRead, BufReader, Read}, + process, +}; + +#[derive(Debug, Default)] +pub struct B2sum; + +impl Cmd for B2sum { + fn cli(&self) -> clap::Command { + Command::new("b2sum") + .about("compute and check MD5 message digest") + .author("Nathan Fisher") + .version(env!("CARGO_PKG_VERSION")) + .args([ + args::check(), + args::file(), + Arg::new("length") + .help( + "digest length in bits; must not exceed the max for the \ + blake2 algorithm and must be a multiple of 8" + ) + .short('l') + .long("length") + .default_value("512") + .value_parser(value_parser!(usize)), + ]) + } + + fn run(&self, matches: &clap::ArgMatches) -> Result<(), Box> { + if let Some(files) = matches.get_many::("file") { + let mut erred = 0; + let len = matches.get_one("length").unwrap_or(&512); + if *len % 8 != 0 { + let msg = format!("bad hash length: {len}"); + return Err(io::Error::new(io::ErrorKind::Other, msg).into()); + } + let len = len / 8; + for f in files { + if matches.get_flag("check") { + if f == "-" { + return Err( + io::Error::new(io::ErrorKind::Other, "no file specified").into() + ); + } + let fd = File::open(f)?; + let reader = BufReader::new(fd); + for line in reader.lines() { + let line = line?; + let mut split = line.split_whitespace(); + let sum = split.next().ok_or::( + io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), + )?; + let file = split.next().ok_or::( + io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(), + )?; + let mut buf = vec![]; + let mut fd = File::open(file)?; + fd.read_to_end(&mut buf)?; + let hash = Params::new() + .hash_length(len) + .to_state() + .update(&buf) + .finalize(); + if hash.to_hex().as_str() == sum { + println!("{file}: OK"); + } else { + println!("{file}: FAILED"); + erred += 1; + } + } + } else { + let mut buf = vec![]; + if f == "-" { + let _s = io::stdin().read_to_end(&mut buf)?; + } else { + let mut fd = File::open(f)?; + let _s = fd.read_to_end(&mut buf)?; + } + let hash = Params::new() + .hash_length(len) + .to_state() + .update(&buf) + .finalize(); + println!("{} {f}", &hash.to_hex()); + } + } + if erred > 0 { + println!("b2sum: WARNING: {erred} computed checksum did NOT match"); + process::exit(1); + } + } + Ok(()) + } + + fn path(&self) -> Option { + Some(crate::Path::UsrBin) + } +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 680c472..7e8fa94 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -1,6 +1,7 @@ use clap::ArgMatches; use std::{error::Error, fmt}; +mod b2sum; pub mod base32; mod base64; mod basename; @@ -78,6 +79,7 @@ pub trait Cmd: fmt::Debug + Sync { #[allow(clippy::box_default)] pub fn get(name: &str) -> Option> { match name { + "b2sum" => Some(Box::new(b2sum::B2sum::default())), "base64" => Some(Box::new(base64::Base64::default())), "base32" => Some(Box::new(base32::Base32::default())), "basename" => Some(Box::new(basename::Basename::default())), @@ -131,7 +133,8 @@ pub fn get(name: &str) -> Option> { } } -pub static COMMANDS: [&str; 49] = [ +pub static COMMANDS: [&str; 50] = [ + "b2sum", "base32", "base64", "basename",