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

View File

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

View File

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

View File

@ -44,3 +44,18 @@ pub fn color() -> Arg {
.long("color") .long("color")
.value_parser(["always", "ansi", "auto", "never"]) .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 super::Cmd;
use clap::{Arg, ArgAction, Command}; use crate::{
use md5::{Digest, Md5}; args,
use std::{ hash::{self, HashType},
fmt::Write,
fs::File,
io::{self, BufRead, BufReader, Read},
process, error::Error,
}; };
use clap::Command;
use md5::{Digest, Md5};
use std::{io, process};
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct Md5sum; pub struct Md5sum;
@ -17,17 +16,7 @@ impl Cmd for Md5sum {
.about("compute and check MD5 message digest") .about("compute and check MD5 message digest")
.author("Nathan Fisher") .author("Nathan Fisher")
.version(env!("CARGO_PKG_VERSION")) .version(env!("CARGO_PKG_VERSION"))
.args([ .args([args::check(), args::file()])
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("-"),
])
} }
fn run(&self, matches: Option<&clap::ArgMatches>) -> Result<(), Box<dyn std::error::Error>> { 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() io::Error::new(io::ErrorKind::Other, "no file specified").into()
); );
} }
let fd = File::open(f)?; hash::check_sums(f, HashType::Md5, &mut erred)?;
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;
}
}
} else { } else {
let mut hasher = Md5::new(); let hasher = Md5::new();
let mut buf = vec![]; let s = hash::compute_hash(f, hasher)?;
if f == "-" { println!("{s} {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}");
} }
} }
if erred > 0 { if erred > 0 {

View File

@ -44,6 +44,9 @@ mod realpath;
mod rev; mod rev;
mod rm; mod rm;
mod rmdir; mod rmdir;
mod sha1sum;
mod sha224sum;
mod sha256sum;
mod shitbox; mod shitbox;
mod sleep; mod sleep;
mod sync; mod sync;
@ -108,6 +111,9 @@ pub fn get(name: &str) -> Option<Box<dyn Cmd>> {
"rev" => Some(Box::new(rev::Rev::default())), "rev" => Some(Box::new(rev::Rev::default())),
"rm" => Some(Box::new(rm::Rm::default())), "rm" => Some(Box::new(rm::Rm::default())),
"rmdir" => Some(Box::new(rmdir::Rmdir::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())), "shitbox" => Some(Box::new(shitbox::Shitbox::default())),
"sleep" => Some(Box::new(sleep::Sleep::default())), "sleep" => Some(Box::new(sleep::Sleep::default())),
"sync" => Some(Box::new(sync::Sync::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", "base32",
"base64", "base64",
"basename", "basename",
@ -157,8 +163,11 @@ pub static COMMANDS: [&str; 44] = [
"rev", "rev",
"rm", "rm",
"rmdir", "rmdir",
"sleep", "sha1sum",
"sha224sum",
"sha256sum",
"shitbox", "shitbox",
"sleep",
"sync", "sync",
"true", "true",
"unlink", "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; pub mod bitflags;
mod cmd; mod cmd;
pub mod fs; pub mod fs;
pub mod hash;
pub mod math; pub mod math;
pub mod mode; pub mod mode;
pub mod pw; pub mod pw;