Create generic `hash` module; Add `sha1sum`, `sha224sum` and `sha256sum`

applets
This commit is contained in:
Nathan Fisher 2023-02-04 00:56:02 -05:00
parent 8df1f99f70
commit 5e0c1141ef
11 changed files with 363 additions and 87 deletions

107
Cargo.lock generated
View File

@ -19,6 +19,15 @@ version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "blake2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
version = "0.10.3"
@ -30,15 +39,21 @@ dependencies = [
[[package]]
name = "cc"
version = "1.0.78"
version = "1.0.79"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a20104e2335ce8a659d6dd92a51a767a0c062599c73b343fd152cb401e828c3d"
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "4.1.1"
version = "4.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec7a4128863c188deefe750ac1d1dfe66c236909f845af04beed823638dc1b2"
checksum = "f13b9c79b5d1dd500d20ef541215a6423c75829ef43117e1b4d17fd8af0b5d76"
dependencies = [
"bitflags",
"clap_lex",
@ -49,18 +64,18 @@ dependencies = [
[[package]]
name = "clap_complete"
version = "4.1.0"
version = "4.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce8955d4e8cd4f28f9a01c93a050194c4d131e73ca02f6636bcddbed867014d7"
checksum = "3d6540eedc41f8a5a76cf3d8d458057dcdf817be4158a55b5f861f7a5483de75"
dependencies = [
"clap",
]
[[package]]
name = "clap_complete_nushell"
version = "0.1.8"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "956ceeff3734be0ba9e15998f1f1a487cd2928b31444209e8fcd92b30efac13a"
checksum = "c7fa41f5e6aa83bd151b70fd0ceaee703d68cd669522795dc812df9edad1252c"
dependencies = [
"clap",
"clap_complete",
@ -85,6 +100,15 @@ dependencies = [
"roff",
]
[[package]]
name = "cpufeatures"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28d997bd5e24a5928dd43e46dc529867e207907fe0b239c3477d924f7f2ca320"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
@ -109,6 +133,7 @@ checksum = "8168378f4e5023e7218c89c891c0fd8ecdb5e5e4f18cb78f38cf245dd021e76f"
dependencies = [
"block-buffer",
"crypto-common",
"subtle",
]
[[package]]
@ -153,18 +178,15 @@ dependencies = [
[[package]]
name = "hermit-abi"
version = "0.2.6"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
dependencies = [
"libc",
]
checksum = "856b5cb0902c2b6d65d5fd97dfa30f9b70c7538e770b98eab5ed52d8db923e01"
[[package]]
name = "io-lifetimes"
version = "1.0.4"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7d6c6f8c91b4b9ed43484ad1a938e393caf35960fce7f82a040497207bd8e9e"
checksum = "1abeb7a0dd0f8181267ff8adc397075586500b81b28a73e8a0208b00fc170fb3"
dependencies = [
"libc",
"windows-sys",
@ -172,11 +194,11 @@ dependencies = [
[[package]]
name = "is-terminal"
version = "0.4.2"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dfb6c8100ccc63462345b67d1bbc3679177c75ee4bf59bf29c8b1d110b8189"
checksum = "22e18b0a45d56fe973d6db23972bf5bc46f988a4a2385deac9cc29572f09daef"
dependencies = [
"hermit-abi 0.2.6",
"hermit-abi 0.3.0",
"io-lifetimes",
"rustix",
"windows-sys",
@ -217,9 +239,9 @@ checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316"
[[package]]
name = "rustix"
version = "0.36.6"
version = "0.36.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4feacf7db682c6c329c4ede12649cd36ecab0f3be5b7d74e6a20304725db4549"
checksum = "f43abb88211988493c1abb44a70efa56ff0ce98f233b7b276146f1f3f7ba9644"
dependencies = [
"bitflags",
"errno",
@ -244,19 +266,45 @@ version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "010e18bd3bfd1d45a7e666b236c78720df0d9a7698ebaa9c1c559961eb60a38b"
[[package]]
name = "sha1"
version = "0.10.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f04293dc80c3993519f2d7f6f511707ee7094fe0c6d3406feb330cdb3540eba3"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "sha2"
version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82e6b795fe2e3b1e845bafcb27aa35405c4d47cdfc92af5fc8d3002f76cebdc0"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "shitbox"
version = "0.1.0"
dependencies = [
"atty",
"blake2",
"clap",
"clap_complete",
"clap_complete_nushell",
"clap_mangen",
"data-encoding",
"digest",
"libc",
"md-5",
"sc",
"sha1",
"sha2",
"termcolor",
"textwrap",
"walkdir",
@ -274,6 +322,12 @@ 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"
@ -348,9 +402,18 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.42.0"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-targets"
version = "0.42.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2522491fbfcd58cc84d47aeb2958948c4b8982e9a2d8a2a35bbaed431390e7"
dependencies = [
"windows_aarch64_gnullvm",
"windows_aarch64_msvc",

View File

@ -7,17 +7,21 @@ edition = "2021"
[dependencies]
atty = "0.2"
blake2 = "0.10"
clap = "4.1"
clap_complete = "4.0"
clap_complete = "4.1"
clap_complete_nushell = "0.1"
clap_mangen = "0.2"
data-encoding = "2.3"
digest = "0.10.6"
libc = "0.2"
md-5 = "0.10.5"
md-5 = "0.10"
sc = "0.2"
sha1 = "0.10"
sha2 = "0.10"
termcolor = "1.1"
textwrap = { version = "0.16", default-features = false, features = ["smawk"] }
walkdir = "2.3.2"
walkdir = "2.3"
[profile.release]
codegen-units = 1

View File

@ -41,6 +41,7 @@ code between applets, making for an overall smaller binary.
- md5sum
- mkfifo
- mknod
- mktemp
- mountpoint
- nologin
- nproc

View File

@ -44,3 +44,18 @@ pub fn color() -> Arg {
.long("color")
.value_parser(["always", "ansi", "auto", "never"])
}
pub fn check() -> Arg {
Arg::new("check")
.help("read checksums from the FILEs and check them")
.short('c')
.long("check")
.action(ArgAction::SetTrue)
}
pub fn file() -> Arg {
Arg::new("file")
.value_name("FILE")
.num_args(1..)
.default_value("-")
}

View File

@ -1,12 +1,11 @@
use super::Cmd;
use clap::{Arg, ArgAction, Command};
use md5::{Digest, Md5};
use std::{
fmt::Write,
fs::File,
io::{self, BufRead, BufReader, Read},
process, error::Error,
use crate::{
args,
hash::{self, HashType},
};
use clap::Command;
use md5::{Digest, Md5};
use std::{io, process};
#[derive(Debug, Default)]
pub struct Md5sum;
@ -17,17 +16,7 @@ impl Cmd for Md5sum {
.about("compute and check MD5 message digest")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([
Arg::new("check")
.help("read checksums from the FILEs and check them")
.short('c')
.long("check")
.action(ArgAction::SetTrue),
Arg::new("file")
.value_name("FILE")
.num_args(1..)
.default_value("-"),
])
.args([args::check(), args::file()])
}
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
@ -43,49 +32,11 @@ impl Cmd for Md5sum {
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::<Box<dyn Error>>(
io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(),
)?;
let file = split.next().ok_or::<Box<dyn Error>>(
io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(),
)?;
let mut hasher = Md5::new();
let mut buf = vec![];
let mut fd = File::open(file)?;
let _s = fd.read_to_end(&mut buf)?;
hasher.update(&buf);
let res = hasher.finalize();
let mut s = String::new();
for c in res {
write!(s, "{c:x}")?;
}
if s.as_str() == sum {
println!("{file}: OK");
} else {
println!("{file}: FAILED");
erred += 1;
}
}
hash::check_sums(f, HashType::Md5, &mut erred)?;
} else {
let mut hasher = Md5::new();
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)?;
}
hasher.update(&buf);
let res = hasher.finalize();
for c in res {
print!("{c:x}");
}
println!(" {f}");
let hasher = Md5::new();
let s = hash::compute_hash(f, hasher)?;
println!("{s} {f}");
}
}
if erred > 0 {

View File

@ -44,6 +44,9 @@ mod realpath;
mod rev;
mod rm;
mod rmdir;
mod sha1sum;
mod sha224sum;
mod sha256sum;
mod shitbox;
mod sleep;
mod sync;
@ -108,6 +111,9 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
"rev" => Some(Box::new(rev::Rev::default())),
"rm" => Some(Box::new(rm::Rm::default())),
"rmdir" => Some(Box::new(rmdir::Rmdir::default())),
"sha1sum" => Some(Box::new(sha1sum::Sha1sum::default())),
"sha224sum" => Some(Box::new(sha224sum::Sha224sum::default())),
"sha256sum" => Some(Box::new(sha256sum::Sha256sum::default())),
"shitbox" => Some(Box::new(shitbox::Shitbox::default())),
"sleep" => Some(Box::new(sleep::Sleep::default())),
"sync" => Some(Box::new(sync::Sync::default())),
@ -121,7 +127,7 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
}
}
pub static COMMANDS: [&str; 44] = [
pub static COMMANDS: [&str; 47] = [
"base32",
"base64",
"basename",
@ -157,8 +163,11 @@ pub static COMMANDS: [&str; 44] = [
"rev",
"rm",
"rmdir",
"sleep",
"sha1sum",
"sha224sum",
"sha256sum",
"shitbox",
"sleep",
"sync",
"true",
"unlink",

53
src/cmd/sha1sum/mod.rs Normal file
View File

@ -0,0 +1,53 @@
use super::Cmd;
use crate::{
args,
hash::{self, HashType},
};
use clap::Command;
use sha1::{Digest, Sha1};
use std::{io, process};
#[derive(Debug, Default)]
pub struct Sha1sum;
impl Cmd for Sha1sum {
fn cli(&self) -> clap::Command {
Command::new("sha1sum")
.about("compute and check SHA1 message digest")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([args::check(), args::file()])
}
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
let Some(matches) = matches else {
return Err(io::Error::new(io::ErrorKind::Other, "no input").into());
};
if let Some(files) = matches.get_many::<String>("file") {
let mut erred = 0;
for f in files {
if matches.get_flag("check") {
if f == "-" {
return Err(
io::Error::new(io::ErrorKind::Other, "no file specified").into()
);
}
hash::check_sums(f, HashType::Sha1, &mut erred)?;
} else {
let hasher = Sha1::new();
let s = hash::compute_hash(f, hasher)?;
println!("{s} {f}");
}
}
if erred > 0 {
println!("sha1sum: WARNING: {erred} computed checksum did NOT match");
process::exit(1);
}
}
Ok(())
}
fn path(&self) -> Option<crate::Path> {
Some(crate::Path::UsrBin)
}
}

53
src/cmd/sha224sum/mod.rs Normal file
View File

@ -0,0 +1,53 @@
use super::Cmd;
use crate::{
args,
hash::{self, HashType},
};
use clap::Command;
use sha2::{Digest, Sha224};
use std::{io, process};
#[derive(Debug, Default)]
pub struct Sha224sum;
impl Cmd for Sha224sum {
fn cli(&self) -> clap::Command {
Command::new("sha224sum")
.about("compute and check SHA1 message digest")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([args::check(), args::file()])
}
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
let Some(matches) = matches else {
return Err(io::Error::new(io::ErrorKind::Other, "no input").into());
};
if let Some(files) = matches.get_many::<String>("file") {
let mut erred = 0;
for f in files {
if matches.get_flag("check") {
if f == "-" {
return Err(
io::Error::new(io::ErrorKind::Other, "no file specified").into()
);
}
hash::check_sums(f, HashType::Sha224, &mut erred)?;
} else {
let hasher = Sha224::new();
let s = hash::compute_hash(f, hasher)?;
println!("{s} {f}");
}
}
if erred > 0 {
println!("sha224sum: WARNING: {erred} computed checksum did NOT match");
process::exit(1);
}
}
Ok(())
}
fn path(&self) -> Option<crate::Path> {
Some(crate::Path::UsrBin)
}
}

53
src/cmd/sha256sum/mod.rs Normal file
View File

@ -0,0 +1,53 @@
use super::Cmd;
use crate::{
args,
hash::{self, HashType},
};
use clap::Command;
use sha2::{Digest, Sha256};
use std::{io, process};
#[derive(Debug, Default)]
pub struct Sha224sum;
impl Cmd for Sha224sum {
fn cli(&self) -> clap::Command {
Command::new("sha256sum")
.about("compute and check SHA1 message digest")
.author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION"))
.args([args::check(), args::file()])
}
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> {
let Some(matches) = matches else {
return Err(io::Error::new(io::ErrorKind::Other, "no input").into());
};
if let Some(files) = matches.get_many::<String>("file") {
let mut erred = 0;
for f in files {
if matches.get_flag("check") {
if f == "-" {
return Err(
io::Error::new(io::ErrorKind::Other, "no file specified").into()
);
}
hash::check_sums(f, HashType::Sha256, &mut erred)?;
} else {
let hasher = Sha256::new();
let s = hash::compute_hash(f, hasher)?;
println!("{s} {f}");
}
}
if erred > 0 {
println!("sha256sum: WARNING: {erred} computed checksum did NOT match");
process::exit(1);
}
}
Ok(())
}
fn path(&self) -> Option<crate::Path> {
Some(crate::Path::UsrBin)
}
}

73
src/hash/mod.rs Normal file
View File

@ -0,0 +1,73 @@
use {
digest::{Digest, FixedOutput, HashMarker},
md5::Md5,
sha1::Sha1,
sha2::{Sha224, Sha256, Sha384, Sha512},
std::{
error::Error,
fmt::Write,
fs::File,
io::{self, BufRead, BufReader, Read},
},
};
pub enum HashType {
Blake2b,
Md5,
Sha1,
Sha224,
Sha256,
Sha384,
Sha512,
}
pub fn compute_hash<T>(file: &str, mut hasher: T) -> Result<String, Box<dyn Error>>
where
T: Default + FixedOutput + HashMarker,
{
let mut buf = vec![];
if file == "-" {
let _s = io::stdin().read_to_end(&mut buf)?;
} else {
let mut fd = File::open(file)?;
let _s = fd.read_to_end(&mut buf)?;
}
let mut s = String::new();
hasher.update(&buf);
let res = hasher.finalize();
for c in res {
write!(s, "{c:02x}")?;
}
Ok(s)
}
pub fn check_sums(file: &str, hashtype: HashType, erred: &mut usize) -> Result<(), Box<dyn Error>> {
let fd = File::open(file)?;
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>(
io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(),
)?;
let file = split.next().ok_or::<io::Error>(
io::Error::new(io::ErrorKind::Other, "invalid checksum file").into(),
)?;
let s = match hashtype {
HashType::Blake2b => unimplemented!(),
HashType::Md5 => compute_hash(file, Md5::new())?,
HashType::Sha1 => compute_hash(file, Sha1::new())?,
HashType::Sha224 => compute_hash(file, Sha224::new())?,
HashType::Sha256 => compute_hash(file, Sha256::new())?,
HashType::Sha384 => compute_hash(file, Sha384::new())?,
HashType::Sha512 => compute_hash(file, Sha512::new())?,
};
if s.as_str() == sum {
println!("{file}: OK");
} else {
println!("{file}: FAILED");
*erred += 1;
}
}
Ok(())
}

View File

@ -5,6 +5,7 @@ pub mod args;
pub mod bitflags;
mod cmd;
pub mod fs;
pub mod hash;
pub mod math;
pub mod mode;
pub mod pw;