Compare commits

..

No commits in common. "6b02b20dadb558888c330873f04d014aa6f45bd4" and "1c43531f3fe5d574764046c7e1fc3ecbf5779e20" have entirely different histories.

13 changed files with 804 additions and 940 deletions

580
Cargo.lock generated
View file

@ -3,44 +3,436 @@
version = 3
[[package]]
name = "lore"
version = "2.1.0"
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bstr"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223"
dependencies = [
"lazy_static",
"memchr",
"regex-automata",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
name = "bumpalo"
version = "3.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c"
[[package]]
name = "cast"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a"
dependencies = [
"unicode-ident",
"rustc_version",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"bitflags",
"textwrap",
"unicode-width",
]
[[package]]
name = "criterion"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1604dafd25fba2fe2d5895a9da139f8dc9b319a5fe5354ca137cbbce4e178d10"
dependencies = [
"atty",
"cast",
"clap",
"criterion-plot",
"csv",
"itertools",
"lazy_static",
"num-traits",
"oorandom",
"plotters",
"rayon",
"regex",
"serde",
"serde_cbor",
"serde_derive",
"serde_json",
"tinytemplate",
"walkdir",
]
[[package]]
name = "criterion-plot"
version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d00996de9f2f7559f7f4dc286073197f83e92256a59ed395f9aac01fe717da57"
dependencies = [
"cast",
"itertools",
]
[[package]]
name = "crossbeam-channel"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "06ed27e177f16d65f0f0c22a213e17c696ace5dd64b14258b52f9417ccb52db4"
dependencies = [
"cfg-if",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ec02e091aa634e2c3ada4a392989e7c3116673ef0ac5b72232439094d73b7fd"
dependencies = [
"cfg-if",
"crossbeam-utils",
"lazy_static",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db"
dependencies = [
"cfg-if",
"lazy_static",
]
[[package]]
name = "csv"
version = "1.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22813a6dc45b335f9bade10bf7271dc477e81113e89eb251a0bc2a8a81c536e1"
dependencies = [
"bstr",
"csv-core",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "csv-core"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b2466559f260f48ad25fe6317b3c8dac77b5bdb5763ac7d9d6103530663bc90"
dependencies = [
"memchr",
]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "itertools"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69ddb889f9d0d08a67338271fa9b62996bc788c7796a5c18cf057420aaed5eaf"
dependencies = [
"either",
]
[[package]]
name = "itoa"
version = "0.4.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4"
[[package]]
name = "js-sys"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
dependencies = [
"wasm-bindgen",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "libc"
version = "0.2.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fbe5e23404da5b4f555ef85ebed98fb4083e55a00c317800bc2a50ede9f3d219"
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "lore"
version = "2.0.0"
dependencies = [
"criterion",
]
[[package]]
name = "memchr"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
[[package]]
name = "memoffset"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59accc507f1338036a0477ef61afdae33cde60840f4dfe481319ce3ad116ddf9"
dependencies = [
"autocfg",
]
[[package]]
name = "num-traits"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
dependencies = [
"autocfg",
]
[[package]]
name = "num_cpus"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3"
dependencies = [
"hermit-abi",
"libc",
]
[[package]]
name = "oorandom"
version = "11.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575"
[[package]]
name = "plotters"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a3fd9ec30b9749ce28cd91f255d569591cdf937fe280c312143e3c4bad6f2a"
dependencies = [
"num-traits",
"plotters-backend",
"plotters-svg",
"wasm-bindgen",
"web-sys",
]
[[package]]
name = "plotters-backend"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d88417318da0eaf0fdcdb51a0ee6c3bed624333bff8f946733049380be67ac1c"
[[package]]
name = "plotters-svg"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "521fa9638fa597e1dc53e9412a4f9cefb01187ee1f7413076f9e6749e2885ba9"
dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro2"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.144"
name = "rayon"
version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
dependencies = [
"serde_derive",
"autocfg",
"crossbeam-deque",
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
dependencies = [
"crossbeam-channel",
"crossbeam-deque",
"crossbeam-utils",
"lazy_static",
"num_cpus",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132"
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rustc_version"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366"
dependencies = [
"semver",
]
[[package]]
name = "ryu"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "same-file"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
dependencies = [
"winapi-util",
]
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "semver"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
[[package]]
name = "serde"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913"
[[package]]
name = "serde_cbor"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2bef2ebfde456fb76bbcf9f59315333decc4fda0b2b44b420243c11e0f5ec1f5"
dependencies = [
"half",
"serde",
]
[[package]]
name = "serde_derive"
version = "1.0.144"
version = "1.0.130"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
dependencies = [
"proc-macro2",
"quote",
@ -48,18 +440,160 @@ dependencies = [
]
[[package]]
name = "syn"
version = "1.0.99"
name = "serde_json"
version = "1.0.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
"itoa",
"ryu",
"serde",
]
[[package]]
name = "unicode-ident"
version = "1.0.4"
name = "syn"
version = "1.0.81"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "tinytemplate"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc"
dependencies = [
"serde",
"serde_json",
]
[[package]]
name = "unicode-width"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "walkdir"
version = "2.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "808cf2735cd4b6866113f648b791c6adc5714537bc222d9347bb203386ffda56"
dependencies = [
"same-file",
"winapi",
"winapi-util",
]
[[package]]
name = "wasm-bindgen"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
dependencies = [
"cfg-if",
"wasm-bindgen-macro",
]
[[package]]
name = "wasm-bindgen-backend"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
dependencies = [
"bumpalo",
"lazy_static",
"log",
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-macro"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
]
[[package]]
name = "wasm-bindgen-macro-support"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
dependencies = [
"proc-macro2",
"quote",
"syn",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]
[[package]]
name = "wasm-bindgen-shared"
version = "0.2.78"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
[[package]]
name = "web-sys"
version = "0.3.55"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
dependencies = [
"js-sys",
"wasm-bindgen",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

View file

@ -1,6 +1,6 @@
[package]
name = "lore"
version = "2.1.0"
version = "2.0.0"
edition = "2018"
description = "Zero-dependency hashing algorithms"
license = "BSD-3-Clause"
@ -11,9 +11,9 @@ keywords = ["algorithms", "compression", "cryptography", "md5", "hash"]
categories = ["algorithms", "compression", "cryptography"]
exclude = []
[dependencies]
serde = { version = ">=1.0.0", features = ["derive"], optional = true }
[dev-dependencies]
criterion = { version = "^0.3", features = ["html_reports"] }
[features]
default = []
serde = ["dep:serde"]
[[bench]]
name = "md5"
harness = false

View file

@ -1,32 +1,19 @@
# Lore
Nightly-only hashing algorithms with a straight-forward API and no required dependencies.
Hashing algorithms. Currently only implements md5.
This crate currently implements:
Doesn't use any dependencies.
- MD2, MD4, and MD5
- SHA-1
Performance/stability not guaranteed.
Performance is not a priority of this crate, rather, the primary purpose of this crate is learning, as well as providing tests for the intermediate steps of algorithms.
This includes padding, checksums and round step functions.
The functions of this crate should probably not be used for production purposes.
Once [`slice::array_chunks`] is stabilized, this crate can be made to work on stable Rust.
The crate could be rewritten to use stable already, but this would increase the verbosity of many expressions.
[`slice::array_chunks`]: https://doc.rust-lang.org/std/primitive.slice.html#method.array_chunks
# Features
[Serde](https://crates.io/crates/serde) support is included, and is gated behind the `serde` feature.
# Examples
Basic usage:
## Example
```rust
let input = "lol xd";
let digest = lore::md5(input);
assert_eq!(digest.to_string(), "982d7f24f8985a6baa5cf129acc73561");
use lore::md5;
fn main() {
let input = "lol xd";
let digest = md5::hash(input);
assert_eq!(digest, "982d7f24f8985a6baa5cf129acc73561");
}
```

14
benches/md5.rs Normal file
View file

@ -0,0 +1,14 @@
use criterion::*;
use lore::md5;
fn md5(c: &mut Criterion) {
c.bench_function("md5", |b| {
b.iter(|| {
let hash = md5::hash(black_box("tihi xd"));
hash.to_hex_string();
})
});
}
criterion_group!(benches, md5);
criterion_main!(benches);

View file

@ -1 +0,0 @@
nightly

View file

@ -1 +1,10 @@
fn main() {}
use lore::md5;
fn main() {
let input = "lol xd";
assert_eq!(
md5::hash(input).to_hex_string(),
"982d7f24f8985a6baa5cf129acc73561"
);
}

View file

@ -1,120 +0,0 @@
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use std::{fmt::Display, hash::Hash};
pub mod md2;
pub mod md4;
pub mod md5;
pub mod sha1;
/// A variable-size digest, which can easily be converted into a hexadecimal string for user-facing output.
///
/// This struct is returned by all hashing functions.
///
/// # Examples
///
/// [`Digest`] implements [`Display`], which means it can automatically be converted to a human-readable format by formatting it:
///
/// ```rust
/// let digest = lore::md5("example");
/// println!("Digest: {}", digest); // -> Digest: 1a79a4d60de6718e8e5b326e338ae533
/// ```
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Digest<const S: usize>([u8; S]);
/// Convert the digest into a hexadecimal string representation.
impl<const S: usize> Display for Digest<S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(
&self
.0
.iter()
.map(|u| format!("{:02x}", u))
.collect::<String>(),
)
}
}
impl<const S: usize> From<Digest<S>> for [u8; S] {
fn from(digest: Digest<S>) -> Self {
digest.0
}
}
impl<'a, const S: usize> From<&'a Digest<S>> for &'a [u8] {
fn from(digest: &'a Digest<S>) -> Self {
digest.0.as_slice()
}
}
impl<const S: usize> From<Digest<S>> for Vec<u8> {
fn from(digest: Digest<S>) -> Self {
digest.0.to_vec()
}
}
impl<const S: usize> AsRef<[u8]> for Digest<S> {
fn as_ref(&self) -> &[u8] {
&self.0[..]
}
}
pub fn bytes_to_words_le(bytes: impl AsRef<[u8]>) -> Vec<u32> {
bytes
.as_ref()
.array_chunks::<4>()
.map(|chunk| u32::from_le_bytes(*chunk))
.collect()
}
pub fn words_to_bytes_le(words: impl AsRef<[u32]>) -> Vec<u8> {
words
.as_ref()
.iter()
.flat_map(|w| w.to_le_bytes())
.collect()
}
pub fn bytes_to_words_be(bytes: impl AsRef<[u8]>) -> Vec<u32> {
bytes
.as_ref()
.array_chunks::<4>()
.map(|chunk| u32::from_be_bytes(*chunk))
.collect()
}
pub fn words_to_bytes_be(words: impl AsRef<[u32]>) -> Vec<u8> {
words
.as_ref()
.iter()
.flat_map(|w| w.to_be_bytes())
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bytes_to_words_le_works() {
assert_eq!(
vec![0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476],
bytes_to_words_le([
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10
])
);
}
#[test]
fn words_to_bytes_le_works() {
assert_eq!(
vec![
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54,
0x32, 0x10
],
words_to_bytes_le([0x6745_2301, 0xefcd_ab89, 0x98ba_dcfe, 0x1032_5476])
);
}
}

View file

@ -1,146 +0,0 @@
use crate::hash::Digest;
/// see RFC1319
const S: [u8; 256] = [
0x29, 0x2E, 0x43, 0xC9, 0xA2, 0xD8, 0x7C, 0x01, 0x3D, 0x36, 0x54, 0xA1, 0xEC, 0xF0, 0x06, 0x13,
0x62, 0xA7, 0x05, 0xF3, 0xC0, 0xC7, 0x73, 0x8C, 0x98, 0x93, 0x2B, 0xD9, 0xBC, 0x4C, 0x82, 0xCA,
0x1E, 0x9B, 0x57, 0x3C, 0xFD, 0xD4, 0xE0, 0x16, 0x67, 0x42, 0x6F, 0x18, 0x8A, 0x17, 0xE5, 0x12,
0xBE, 0x4E, 0xC4, 0xD6, 0xDA, 0x9E, 0xDE, 0x49, 0xA0, 0xFB, 0xF5, 0x8E, 0xBB, 0x2F, 0xEE, 0x7A,
0xA9, 0x68, 0x79, 0x91, 0x15, 0xB2, 0x07, 0x3F, 0x94, 0xC2, 0x10, 0x89, 0x0B, 0x22, 0x5F, 0x21,
0x80, 0x7F, 0x5D, 0x9A, 0x5A, 0x90, 0x32, 0x27, 0x35, 0x3E, 0xCC, 0xE7, 0xBF, 0xF7, 0x97, 0x03,
0xFF, 0x19, 0x30, 0xB3, 0x48, 0xA5, 0xB5, 0xD1, 0xD7, 0x5E, 0x92, 0x2A, 0xAC, 0x56, 0xAA, 0xC6,
0x4F, 0xB8, 0x38, 0xD2, 0x96, 0xA4, 0x7D, 0xB6, 0x76, 0xFC, 0x6B, 0xE2, 0x9C, 0x74, 0x04, 0xF1,
0x45, 0x9D, 0x70, 0x59, 0x64, 0x71, 0x87, 0x20, 0x86, 0x5B, 0xCF, 0x65, 0xE6, 0x2D, 0xA8, 0x02,
0x1B, 0x60, 0x25, 0xAD, 0xAE, 0xB0, 0xB9, 0xF6, 0x1C, 0x46, 0x61, 0x69, 0x34, 0x40, 0x7E, 0x0F,
0x55, 0x47, 0xA3, 0x23, 0xDD, 0x51, 0xAF, 0x3A, 0xC3, 0x5C, 0xF9, 0xCE, 0xBA, 0xC5, 0xEA, 0x26,
0x2C, 0x53, 0x0D, 0x6E, 0x85, 0x28, 0x84, 0x09, 0xD3, 0xDF, 0xCD, 0xF4, 0x41, 0x81, 0x4D, 0x52,
0x6A, 0xDC, 0x37, 0xC8, 0x6C, 0xC1, 0xAB, 0xFA, 0x24, 0xE1, 0x7B, 0x08, 0x0C, 0xBD, 0xB1, 0x4A,
0x78, 0x88, 0x95, 0x8B, 0xE3, 0x63, 0xE8, 0x6D, 0xE9, 0xCB, 0xD5, 0xFE, 0x3B, 0x00, 0x1D, 0x39,
0xF2, 0xEF, 0xB7, 0x0E, 0x66, 0x58, 0xD0, 0xE4, 0xA6, 0x77, 0x72, 0xF8, 0xEB, 0x75, 0x4B, 0x0A,
0x31, 0x44, 0x50, 0xB4, 0x8F, 0xED, 0x1F, 0x1A, 0xDB, 0x99, 0x8D, 0x33, 0x9F, 0x11, 0x83, 0x14,
];
fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = message.as_ref().to_vec();
let padding_length = 16 - (message.len() % 16);
message.append(&mut vec![padding_length as u8; padding_length]);
message
}
fn checksum(message: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = message.as_ref().to_vec();
let mut checksum = vec![0u8; 16];
let mut last = 0;
for block in message.array_chunks::<16>() {
for i in 0..16 {
checksum[i] ^= S[(block[i] ^ last) as usize];
last = checksum[i];
}
}
message.append(&mut checksum);
message
}
/// Computes the MD2 hash value (digest) of the input bytes.
///
/// Returns a 16-byte `Digest` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let input = "abc";
/// let digest = lore::md2(input);
///
/// assert_eq!(digest.to_string(), "da853b0d3f88d99b30283a69e6ded6bb");
/// ```
pub fn hash(msg: impl AsRef<[u8]>) -> Digest<16> {
let padded = checksum(pad(msg));
let buffer = padded
.array_chunks::<16>()
.fold([0u8; 48], |mut buffer, chunk| {
// copy chunk into buffer
for i in 0..16 {
buffer[16 + i] = chunk[i];
buffer[32 + i] = buffer[16 + i] ^ buffer[i];
}
// do 18 rounds
let mut t = 0;
for i in 0..18 {
for b in buffer.iter_mut() {
*b ^= S[t as usize];
t = *b;
}
t = t.wrapping_add(i);
}
buffer
});
let digest = *buffer.array_chunks::<16>().next().unwrap();
Digest(digest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn md2_pad() {
// assert that padding length is always correct
assert!(pad([]).len() % 16 == 0);
assert!(pad([0u8]).len() % 16 == 0);
assert!(pad([0u8; 15]).len() % 16 == 0);
assert!(pad([0u8; 16]).len() % 16 == 0);
assert!(pad([0u8; 476]).len() % 16 == 0);
// check some simple cases
assert_eq!(vec![16u8; 16], pad([]));
assert_eq!(
vec![0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15],
pad([0u8])
);
}
#[test]
fn md2_checksum() {
assert_eq!(
vec![
16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 98, 56, 103, 182,
175, 82, 121, 94, 95, 33, 78, 151, 32, 190, 234, 141
],
checksum(vec![16u8; 16])
);
assert_eq!(
vec![
0u8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 41, 224, 10, 216,
109, 48, 122, 164, 206, 220, 139, 100, 42, 212, 254, 68
],
checksum(vec![
0u8, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15
])
);
}
#[test]
fn md2_hash() {
assert_eq!(
"a9046c73e00331af68917d3804f70655",
hash(b"hello").to_string()
);
assert_eq!("8350e5a3e24c153df2275c9f80692773", hash(b"").to_string());
assert_eq!("32ec01ec4a6dac72c0ab96fb34c0b5d1", hash(b"a").to_string());
assert_eq!("da853b0d3f88d99b30283a69e6ded6bb", hash(b"abc").to_string());
assert_eq!(
"03d85a0d629d2c442e987525319fc471",
hash(b"The quick brown fox jumps over the lazy dog").to_string()
);
}
}

View file

@ -1,245 +0,0 @@
use crate::hash::{bytes_to_words_le, words_to_bytes_le, Digest};
// based on RFC1320
const A: u32 = 0x67452301;
const B: u32 = 0xefcdab89;
const C: u32 = 0x98badcfe;
const D: u32 = 0x10325476;
// additional constants for round 1, 2 & 3
const C1: u32 = 0;
const C2: u32 = 0x5a827999;
const C3: u32 = 0x6ed9eba1;
// shifts & indices for each step
const S: [u32; 48] = [
3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 7, 11, 19, 3, 5, 9, 13, 3, 5, 9, 13, 3, 5, 9, 13,
3, 5, 9, 13, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15, 3, 9, 11, 15,
];
const W: [usize; 48] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14,
3, 7, 11, 15, 0, 8, 4, 12, 2, 10, 6, 14, 1, 9, 5, 13, 3, 11, 7, 15,
];
// round functions
const F: fn(u32, u32, u32) -> u32 = |x, y, z| (x & y) | (!x & z);
const G: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & y) | (x & z) | (y & z);
const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z;
// pad the message to next 512-bit interval
pub fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = message.as_ref().to_vec();
let message_length = message.len().wrapping_mul(8) as u64;
// add 1 bit (le)
message.push(0x80);
// add 0 bits until length in bits is congruent to 448 mod 512
while (message.len()) % 64 != 56 {
message.push(0u8);
}
// append message length (64 bits)
message.extend(message_length.to_le_bytes());
message
}
// compute an invidiual step in the md4 algorithm
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], i: usize) -> [u32; 4] {
// choose function and constant based on which round is currently active
let (f, k) = match i {
0..=15 => (F, C1),
16..=31 => (G, C2),
32..=47 => (H, C3),
_ => panic!("This function shouldn't be called using an index outside 0..48"),
};
// main operation
a = f(b, c, d)
.wrapping_add(a)
.wrapping_add(words[W[i]])
.wrapping_add(k)
.rotate_left(S[i]);
[a, b, c, d]
}
/// Computes the MD4 hash value (digest) of the input bytes.
///
/// Returns a 16-byte `Digest` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let input = "abc";
/// let digest = lore::md4(input);
///
/// assert_eq!(digest.to_string(), "a448017aaf21d8525fc10ae87aa6729d");
/// ```
pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
let padded = pad(message);
let buffer = padded.array_chunks::<64>().map(bytes_to_words_le).fold(
[A, B, C, D],
|[a, b, c, d], words| {
// perform rounds on this chunk of data
let mut state = [a, b, c, d];
for i in 0..48 {
state = step(state, &words, i);
state.rotate_right(1);
}
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
]
},
);
let digest = *words_to_bytes_le(buffer)
.array_chunks::<16>()
.next()
.unwrap();
Digest(digest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn md4_pad() {
assert_eq!(pad([1u8]).len() % 64, 0);
assert_eq!(pad([1u8; 63]).len() % 64, 0);
assert_eq!(pad([1u8; 65]).len() % 64, 0);
assert_eq!(pad([1u8; 511]).len() % 64, 0);
assert_eq!(pad([1u8; 512]).len() % 64, 0);
assert_eq!(pad([1u8; 513]).len() % 64, 0);
assert_eq!(pad([1u8; 4472]).len() % 64, 0);
assert_eq!(
vec![
0x01, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
],
pad([1u8])
);
}
#[test]
fn md4_hash() {
assert_eq!(
"1bee69a46ba811185c194762abaeae90",
hash("The quick brown fox jumps over the lazy dog").to_string()
);
assert_eq!(
"b86e130ce7028da59e672d56ad0113df",
hash("The quick brown fox jumps over the lazy cog").to_string()
);
assert_eq!("31d6cfe0d16ae931b73c59d7e0c089c0", hash("").to_string());
// RFC 1320 test suite
assert_eq!(hash("").to_string(), "31d6cfe0d16ae931b73c59d7e0c089c0");
assert_eq!(hash("a").to_string(), "bde52cb31de33e46245e05fbdbd6fb24");
assert_eq!(hash("abc").to_string(), "a448017aaf21d8525fc10ae87aa6729d");
assert_eq!(
hash("message digest").to_string(),
"d9130a8164549fe818874806e1c7014b"
);
assert_eq!(
hash("abcdefghijklmnopqrstuvwxyz").to_string(),
"d79e1c308aa5bbcdeea8ed63df412da9"
);
assert_eq!(
hash("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789").to_string(),
"043f8582f241db351ce627e153e7f0e4"
);
assert_eq!(
hash(
"12345678901234567890123456789012345678901234567890123456789012345678901234567890"
)
.to_string(),
"e33b4ddc9c38f2199c3e7b164fcc0536"
);
assert_eq!(
hash("Rosetta Code").to_string(),
"a52bcfc6a0d0d300cdc5ddbfbefe478b"
);
}
#[test]
fn md4_steps() {
let expected: [[u32; 4]; 48] = [
[0x2b9b7a8b, 0xefcdab89, 0x98badcfe, 0x10325476],
[0x1ebbf3f6, 0x2b9b7a8b, 0xefcdab89, 0x98badcfe],
[0xf636674f, 0x1ebbf3f6, 0x2b9b7a8b, 0xefcdab89],
[0x3e787c49, 0xf636674f, 0x1ebbf3f6, 0x2b9b7a8b],
[0x127b1453, 0x3e787c49, 0xf636674f, 0x1ebbf3f6],
[0x9c35a18a, 0x127b1453, 0x3e787c49, 0xf636674f],
[0x7e1c9145, 0x9c35a18a, 0x127b1453, 0x3e787c49],
[0x0adad780, 0x7e1c9145, 0x9c35a18a, 0x127b1453],
[0x85c62aed, 0x0adad780, 0x7e1c9145, 0x9c35a18a],
[0x881a850b, 0x85c62aed, 0x0adad780, 0x7e1c9145],
[0xf71e7006, 0x881a850b, 0x85c62aed, 0x0adad780],
[0x135c5da7, 0xf71e7006, 0x881a850b, 0x85c62aed],
[0x0727d7d9, 0x135c5da7, 0xf71e7006, 0x881a850b],
[0x9b7d493d, 0x0727d7d9, 0x135c5da7, 0xf71e7006],
[0x1e300fd2, 0x9b7d493d, 0x0727d7d9, 0x135c5da7],
[0xb60174a1, 0x1e300fd2, 0x9b7d493d, 0x0727d7d9],
[0x2a7873ab, 0xb60174a1, 0x1e300fd2, 0x9b7d493d],
[0x86074f26, 0x2a7873ab, 0xb60174a1, 0x1e300fd2],
[0x68021c3d, 0x86074f26, 0x2a7873ab, 0xb60174a1],
[0xc9ad2750, 0x68021c3d, 0x86074f26, 0x2a7873ab],
[0x6b1b8763, 0xc9ad2750, 0x68021c3d, 0x86074f26],
[0x329a0609, 0x6b1b8763, 0xc9ad2750, 0x68021c3d],
[0x3f3a2e5c, 0x329a0609, 0x6b1b8763, 0xc9ad2750],
[0x34e64be9, 0x3f3a2e5c, 0x329a0609, 0x6b1b8763],
[0x0de3f443, 0x34e64be9, 0x3f3a2e5c, 0x329a0609],
[0x5fddbd79, 0x0de3f443, 0x34e64be9, 0x3f3a2e5c],
[0x494abd6f, 0x5fddbd79, 0x0de3f443, 0x34e64be9],
[0x9069bba6, 0x494abd6f, 0x5fddbd79, 0x0de3f443],
[0x0d815e5e, 0x9069bba6, 0x494abd6f, 0x5fddbd79],
[0x753ed018, 0x0d815e5e, 0x9069bba6, 0x494abd6f],
[0xee224d71, 0x753ed018, 0x0d815e5e, 0x9069bba6],
[0xd232eb01, 0xee224d71, 0x753ed018, 0x0d815e5e],
[0x57e97dc9, 0xd232eb01, 0xee224d71, 0x753ed018],
[0x252ee4a0, 0x57e97dc9, 0xd232eb01, 0xee224d71],
[0x8d5bd7ef, 0x252ee4a0, 0x57e97dc9, 0xd232eb01],
[0x92942054, 0x8d5bd7ef, 0x252ee4a0, 0x57e97dc9],
[0x38475e43, 0x92942054, 0x8d5bd7ef, 0x252ee4a0],
[0x22f47377, 0x38475e43, 0x92942054, 0x8d5bd7ef],
[0xe6878422, 0x22f47377, 0x38475e43, 0x92942054],
[0x5ab5fed1, 0xe6878422, 0x22f47377, 0x38475e43],
[0x32463ee3, 0x5ab5fed1, 0xe6878422, 0x22f47377],
[0x85465040, 0x32463ee3, 0x5ab5fed1, 0xe6878422],
[0xb801aa18, 0x85465040, 0x32463ee3, 0x5ab5fed1],
[0xd796ec48, 0xb801aa18, 0x85465040, 0x32463ee3],
[0x5f8a08a4, 0xd796ec48, 0xb801aa18, 0x85465040],
[0x7b15aa48, 0x5f8a08a4, 0xd796ec48, 0xb801aa18],
[0x2722e8cf, 0x7b15aa48, 0x5f8a08a4, 0xd796ec48],
[0x11062517, 0x2722e8cf, 0x7b15aa48, 0x5f8a08a4],
];
// perform first step of md4 round 1
let words: [u32; 16] = [
0x65736f52, 0x20617474, 0x65646f43, 0x00000080, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000060, 0x00000000,
];
let mut state: [u32; 4] = [A, B, C, D];
#[allow(clippy::needless_range_loop)]
for i in 0..48 {
state = step(state, &words, i);
assert_eq!(expected[i], state);
state.rotate_right(1);
}
}
}

View file

@ -1,205 +0,0 @@
use crate::hash::{bytes_to_words_le, md4::pad, words_to_bytes_le, Digest};
// based on RFC1321
const A: u32 = 0x67452301;
const B: u32 = 0xefcdab89;
const C: u32 = 0x98badcfe;
const D: u32 = 0x10325476;
const K: [u32; 64] = [
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
];
// shifts & indices for each step
const S: [u32; 64] = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,
14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15,
21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
];
const W: [usize; 64] = [
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 1, 6, 11, 0, 5, 10, 15, 4, 9, 14, 3, 8,
13, 2, 7, 12, 5, 8, 11, 14, 1, 4, 7, 10, 13, 0, 3, 6, 9, 12, 15, 2, 0, 7, 14, 5, 12, 3, 10, 1,
8, 15, 6, 13, 4, 11, 2, 9,
];
// round functions
const F: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & y) | (!x & z);
const G: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & z) | (y & !z);
const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z;
const I: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| y ^ (x | !z);
fn step([mut a, b, c, d]: [u32; 4], words: &[u32], index: usize) -> [u32; 4] {
let round_function = match index {
0..=15 => F,
16..=31 => G,
32..=47 => H,
48..=63 => I,
_ => panic!("This function shouldn't be called using an index outside 0..48"),
};
a = round_function(b, c, d)
.wrapping_add(a)
.wrapping_add(words[W[index]])
.wrapping_add(K[index])
.rotate_left(S[index])
.wrapping_add(b);
[a, b, c, d]
}
/// Computes the MD5 hash value (digest) of the input bytes.
///
/// Returns a 16-byte `Digest` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// let input = "lol";
/// let digest = lore::md5(input);
///
/// assert_eq!(digest.to_string(), "9cdfb439c7876e703e307864c9167a15");
///
/// ```
pub fn hash(message: impl AsRef<[u8]>) -> Digest<16> {
// the padding function for MD5 is exactly equivalent to the MD4 version, so we reuse it.
let padded = pad(message);
let buffer = padded.array_chunks::<64>().map(bytes_to_words_le).fold(
[A, B, C, D],
|[a, b, c, d], words| {
// initialize state
let mut state = [a, b, c, d];
for i in 0..64 {
state = step(state, &words, i);
println!("{state:08x?}");
state.rotate_right(1);
}
// add the computed state to the buffer
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
]
},
);
let digest = *words_to_bytes_le(buffer)
.array_chunks::<16>()
.next()
.unwrap();
Digest(digest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn md5_hash() {
assert_eq!("d41d8cd98f00b204e9800998ecf8427e", hash("").to_string());
assert_eq!(
"73e51861b65c1e83d6136fb6a002585e",
hash("tihi xd").to_string()
);
assert_eq!("9cdfb439c7876e703e307864c9167a15", hash("lol").to_string());
assert_eq!(
"fd6f92edff2b7db7cd92b494c4926ef0",
hash("Lorem ipsum dolor sit amet. Nam placeat iste aut dolorem necessitatibus sit unde magni. Nam cumque quia nam fugit quibusdam ut incidunt minima nam dignissimos iusto et voluptatum magnam. Et pariatur eius vel excepturi odit libero sit molestiae consequatur vel nostrum ullam. Sit sint beatae eos doloribus sapiente aut aperiam ullam ut laboriosam debitis! Qui reiciendis rerum est omnis galisum aut similique iure est dolores fuga sit rerum dicta. Aut excepturi fuga et enim ipsum a quia ut velit atque id iste nobis. Non molestiae vero et veritatis aliquam sed enim ducimus. At sunt tempora quo quisquam unde aut ipsum nulla et fugit eius in nulla fugit qui magnam excepturi aut rerum officia. Et officia magnam eos reiciendis voluptatum ea voluptas recusandae in similique enim. Ut provident laudantium ut temporibus eius ut laudantium voluptate aut ducimus impedit. Et asperiores aperiam sit deleniti voluptas ex porro omnis ut voluptas assumenda aut necessitatibus aliquid.").to_string()
);
}
#[test]
fn md5_steps() {
let expected: [[u32; 4]; 64] = [
[0x5954a129, 0xefcdab89, 0x98badcfe, 0x10325476],
[0x3171555d, 0x5954a129, 0xefcdab89, 0x98badcfe],
[0x24368ecc, 0x3171555d, 0x5954a129, 0xefcdab89],
[0x1d414db3, 0x24368ecc, 0x3171555d, 0x5954a129],
[0x9da81fec, 0x1d414db3, 0x24368ecc, 0x3171555d],
[0x983a9b4c, 0x9da81fec, 0x1d414db3, 0x24368ecc],
[0x01f76eec, 0x983a9b4c, 0x9da81fec, 0x1d414db3],
[0x82251f6b, 0x01f76eec, 0x983a9b4c, 0x9da81fec],
[0x3648b77a, 0x82251f6b, 0x01f76eec, 0x983a9b4c],
[0xa57749ed, 0x3648b77a, 0x82251f6b, 0x01f76eec],
[0x69859a5a, 0xa57749ed, 0x3648b77a, 0x82251f6b],
[0x8dd64e23, 0x69859a5a, 0xa57749ed, 0x3648b77a],
[0x4cc08388, 0x8dd64e23, 0x69859a5a, 0xa57749ed],
[0x9a1db095, 0x4cc08388, 0x8dd64e23, 0x69859a5a],
[0xf3a1ec18, 0x9a1db095, 0x4cc08388, 0x8dd64e23],
[0x68bf5f16, 0xf3a1ec18, 0x9a1db095, 0x4cc08388],
[0x08cf03db, 0x68bf5f16, 0xf3a1ec18, 0x9a1db095],
[0x03bceaa0, 0x08cf03db, 0x68bf5f16, 0xf3a1ec18],
[0x2809715f, 0x03bceaa0, 0x08cf03db, 0x68bf5f16],
[0xc305e2e6, 0x2809715f, 0x03bceaa0, 0x08cf03db],
[0x0386e9c7, 0xc305e2e6, 0x2809715f, 0x03bceaa0],
[0x0f4c9f59, 0x0386e9c7, 0xc305e2e6, 0x2809715f],
[0x8814e065, 0x0f4c9f59, 0x0386e9c7, 0xc305e2e6],
[0xd8d052d2, 0x8814e065, 0x0f4c9f59, 0x0386e9c7],
[0x8ff59707, 0xd8d052d2, 0x8814e065, 0x0f4c9f59],
[0x4069945d, 0x8ff59707, 0xd8d052d2, 0x8814e065],
[0x213a0570, 0x4069945d, 0x8ff59707, 0xd8d052d2],
[0xf2affb96, 0x213a0570, 0x4069945d, 0x8ff59707],
[0x555223a9, 0xf2affb96, 0x213a0570, 0x4069945d],
[0x37ba19ca, 0x555223a9, 0xf2affb96, 0x213a0570],
[0x003749f2, 0x37ba19ca, 0x555223a9, 0xf2affb96],
[0x20617338, 0x003749f2, 0x37ba19ca, 0x555223a9],
[0xf3e971ee, 0x20617338, 0x003749f2, 0x37ba19ca],
[0x4ec4ee85, 0xf3e971ee, 0x20617338, 0x003749f2],
[0xe62bf9a6, 0x4ec4ee85, 0xf3e971ee, 0x20617338],
[0x0ae8a02f, 0xe62bf9a6, 0x4ec4ee85, 0xf3e971ee],
[0xbc31561a, 0x0ae8a02f, 0xe62bf9a6, 0x4ec4ee85],
[0x6a9f6576, 0xbc31561a, 0x0ae8a02f, 0xe62bf9a6],
[0x42e91ea3, 0x6a9f6576, 0xbc31561a, 0x0ae8a02f],
[0x7a181668, 0x42e91ea3, 0x6a9f6576, 0xbc31561a],
[0xedcc403b, 0x7a181668, 0x42e91ea3, 0x6a9f6576],
[0x1fcae4da, 0xedcc403b, 0x7a181668, 0x42e91ea3],
[0x217c84d1, 0x1fcae4da, 0xedcc403b, 0x7a181668],
[0xf02591fa, 0x217c84d1, 0x1fcae4da, 0xedcc403b],
[0x5375b853, 0xf02591fa, 0x217c84d1, 0x1fcae4da],
[0xecd77499, 0x5375b853, 0xf02591fa, 0x217c84d1],
[0x4bd1053f, 0xecd77499, 0x5375b853, 0xf02591fa],
[0x7625a818, 0x4bd1053f, 0xecd77499, 0x5375b853],
[0xf7223b53, 0x7625a818, 0x4bd1053f, 0xecd77499],
[0x2e422a17, 0xf7223b53, 0x7625a818, 0x4bd1053f],
[0xe5235245, 0x2e422a17, 0xf7223b53, 0x7625a818],
[0x8e8a212d, 0xe5235245, 0x2e422a17, 0xf7223b53],
[0x551950d2, 0x8e8a212d, 0xe5235245, 0x2e422a17],
[0xf067530c, 0x551950d2, 0x8e8a212d, 0xe5235245],
[0xdb4e97cc, 0xf067530c, 0x551950d2, 0x8e8a212d],
[0x5b429768, 0xdb4e97cc, 0xf067530c, 0x551950d2],
[0xb0c06d7a, 0x5b429768, 0xdb4e97cc, 0xf067530c],
[0xd1906cf3, 0xb0c06d7a, 0x5b429768, 0xdb4e97cc],
[0x3fc74ed9, 0xd1906cf3, 0xb0c06d7a, 0x5b429768],
[0xa6b24624, 0x3fc74ed9, 0xd1906cf3, 0xb0c06d7a],
[0xf9d3c272, 0xa6b24624, 0x3fc74ed9, 0xd1906cf3],
[0x4e25ae2a, 0xf9d3c272, 0xa6b24624, 0x3fc74ed9],
[0x1db436d8, 0x4e25ae2a, 0xf9d3c272, 0xa6b24624],
[0x9350b12d, 0x1db436d8, 0x4e25ae2a, 0xf9d3c272],
];
let words: [u32; 16] = [
0x69686974, 0x80647820, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000038, 0x00000000,
];
let mut state = [A, B, C, D];
#[allow(clippy::needless_range_loop)]
for i in 0..64 {
state = step(state, &words, i);
assert_eq!(expected[i], state);
state.rotate_right(1);
}
}
}

View file

@ -1,148 +0,0 @@
use crate::hash::{bytes_to_words_be, words_to_bytes_be, Digest};
// based on RFC3174, Secure Hash Algorithm 1
// round functions
const F1: fn(u32, u32, u32) -> u32 = |x, y, z| (x & y) | ((!x) & z);
const F2: fn(u32, u32, u32) -> u32 = |x, y, z| x ^ y ^ z;
const F3: fn(u32, u32, u32) -> u32 = |x, y, z| (x & y) | (x & z) | (y & z);
const F4: fn(u32, u32, u32) -> u32 = |x, y, z| x ^ y ^ z;
// round constants
const K1: u32 = 0x5a827999;
const K2: u32 = 0x6ed9eba1;
const K3: u32 = 0x8f1bbcdc;
const K4: u32 = 0xca62c1d6;
// buffer 2 initial constants
const H0: u32 = 0x67452301;
const H1: u32 = 0xefcdab89;
const H2: u32 = 0x98badcfe;
const H3: u32 = 0x10325476;
const H4: u32 = 0xc3d2e1f0;
fn pad(message: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = message.as_ref().to_vec();
let message_length = message.len().wrapping_mul(8) as u64;
// push 1 bit (little endian)
message.push(0x80);
// pad with 0 bits until length is congruent with 64 mod 56 bytes
while (message.len() % 64) < 56 {
message.push(0);
}
// append the length of the original message (big endian)
message.extend(message_length.to_be_bytes());
message
}
fn step([a, b, c, d, e]: [u32; 5], words: &[u32], i: usize) -> [u32; 5] {
let (k, f) = match i {
0..=19 => (K1, F1),
20..=39 => (K2, F2),
40..=59 => (K3, F3),
60..=79 => (K4, F4),
_ => panic!("step function should not be called with index outside of range 0..80"),
};
[
a.rotate_left(5)
.wrapping_add(f(b, c, d))
.wrapping_add(e)
.wrapping_add(k)
.wrapping_add(words[i]),
a,
b.rotate_left(30),
c,
d,
]
}
/// Computes the SHA1 hash value (digest) of the input bytes.
///
/// Returns a 20-byte `Digest` which implements `Display` in order to get at hexadecimal-string representation.
///
/// # Examples
///
/// Basic usage:
///
/// ```rust
/// let input = "abc";
/// let digest = lore::sha1(input);
///
/// assert_eq!(digest.to_string(), "a9993e364706816aba3e25717850c26c9cd0d89d")
/// ```
pub fn hash(message: impl AsRef<[u8]>) -> Digest<20> {
let padded = pad(message);
let buffer = padded
.array_chunks::<64>()
.map(|chunk| bytes_to_words_be(*chunk))
.fold([H0, H1, H2, H3, H4], |[a, b, c, d, e], mut words| {
// extend 16 words to 80 words
for i in 16..80 {
words.push(
(words[i - 3] ^ words[i - 8] ^ words[i - 14] ^ words[i - 16]).rotate_left(1),
);
}
// initialize state
let mut state = [a, b, c, d, e];
// perform 80 steps
for i in 0..80 {
state = step(state, &words, i);
}
// add computed round state to buffer
[
a.wrapping_add(state[0]),
b.wrapping_add(state[1]),
c.wrapping_add(state[2]),
d.wrapping_add(state[3]),
e.wrapping_add(state[4]),
]
});
let digest = *words_to_bytes_be(buffer)
.array_chunks::<20>()
.next()
.unwrap();
Digest(digest)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn sha1_pad() {
let expected: [u8; 64] = [
0x61, 0x62, 0x63, 0x64, 0x65, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28,
];
assert_eq!(
expected.to_vec(),
pad([0b01100001, 0b01100010, 0b01100011, 0b01100100, 0b01100101])
);
}
#[test]
fn sha1_hash() {
assert_eq!(
hash("").to_string(),
"da39a3ee5e6b4b0d3255bfef95601890afd80709"
);
assert_eq!(
hash("abc").to_string(),
"a9993e364706816aba3e25717850c26c9cd0d89d"
);
}
}

View file

@ -1,21 +1 @@
// rust nightly features
#![feature(array_chunks)]
// lints
#![deny(missing_docs)]
#![warn(clippy::all, clippy::pedantic, clippy::cargo)]
#![allow(
clippy::unreadable_literal,
clippy::missing_panics_doc,
clippy::cast_possible_truncation,
clippy::many_single_char_names
)]
// docs
#![doc = include_str!("../README.md")]
mod hash;
pub use hash::md2::hash as md2;
pub use hash::md4::hash as md4;
pub use hash::md5::hash as md5;
pub use hash::sha1::hash as sha1;
pub use hash::Digest;
pub mod md5;

205
src/md5.rs Normal file
View file

@ -0,0 +1,205 @@
use std::convert::TryInto;
// round functions as defined in RFC1321
const F: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & y) | (!x & z);
const G: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| (x & z) | (y & !z);
const H: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| x ^ y ^ z;
const I: fn(u32, u32, u32) -> u32 = |x: u32, y: u32, z: u32| y ^ (x | !z);
// constants used in RFC132
const K: [u32; 64] = [
0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,
0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,
0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,
0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,
0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391,
];
// shifts per round
const S: [u32; 64] = [
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9,
14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15,
21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21,
];
pub struct Hash([u32; 4]);
impl Hash {
pub fn to_hex_string(&self) -> String {
format!(
"{:08x}{:08x}{:08x}{:08x}",
self.0[0], self.0[1], self.0[2], self.0[3]
)
}
}
fn preprocess(msg: impl AsRef<[u8]>) -> Vec<u8> {
let mut message = msg.as_ref().to_vec();
let len_in_bits = message.len().wrapping_mul(8) as u64;
// padding
// add one bit first (le), then pad with zeroes
message.push(0b10000000);
while (message.len() * 8) % 512 != 448 {
message.push(0u8);
}
// append length
message.extend(len_in_bits.to_le_bytes());
message
}
#[inline(always)]
fn op(
function: impl Fn(u32, u32, u32) -> u32,
state: (u32, u32, u32, u32),
word: u32,
index: usize,
) -> u32 {
function(state.1, state.2, state.3)
.wrapping_add(state.0)
.wrapping_add(word)
.wrapping_add(K[index])
.rotate_left(S[index])
.wrapping_add(state.1)
}
pub fn hash(input: impl AsRef<[u8]>) -> Hash {
let (a, b, c, d) = preprocess(input).chunks_exact(64).fold(
(0x67452301u32, 0xefcdab89u32, 0x98badcfeu32, 0x10325476u32),
|(mut a0, mut b0, mut c0, mut d0), chunk| {
let mut words = [0u32; 16];
for (word, c) in words.iter_mut().zip(chunk.chunks_exact(4)) {
*word = u32::from_le_bytes(c.try_into().unwrap());
}
let (mut a, mut b, mut c, mut d) = (a0, b0, c0, d0);
// round 1
a = op(F, (a, b, c, d), words[0], 0);
d = op(F, (d, a, b, c), words[1], 1);
c = op(F, (c, d, a, b), words[2], 2);
b = op(F, (b, c, d, a), words[3], 3);
a = op(F, (a, b, c, d), words[4], 4);
d = op(F, (d, a, b, c), words[5], 5);
c = op(F, (c, d, a, b), words[6], 6);
b = op(F, (b, c, d, a), words[7], 7);
a = op(F, (a, b, c, d), words[8], 8);
d = op(F, (d, a, b, c), words[9], 9);
c = op(F, (c, d, a, b), words[10], 10);
b = op(F, (b, c, d, a), words[11], 11);
a = op(F, (a, b, c, d), words[12], 12);
d = op(F, (d, a, b, c), words[13], 13);
c = op(F, (c, d, a, b), words[14], 14);
b = op(F, (b, c, d, a), words[15], 15);
// round 2
a = op(G, (a, b, c, d), words[1], 16);
d = op(G, (d, a, b, c), words[6], 17);
c = op(G, (c, d, a, b), words[11], 18);
b = op(G, (b, c, d, a), words[0], 19);
a = op(G, (a, b, c, d), words[5], 20);
d = op(G, (d, a, b, c), words[10], 21);
c = op(G, (c, d, a, b), words[15], 22);
b = op(G, (b, c, d, a), words[4], 23);
a = op(G, (a, b, c, d), words[9], 24);
d = op(G, (d, a, b, c), words[14], 25);
c = op(G, (c, d, a, b), words[3], 26);
b = op(G, (b, c, d, a), words[8], 27);
a = op(G, (a, b, c, d), words[13], 28);
d = op(G, (d, a, b, c), words[2], 29);
c = op(G, (c, d, a, b), words[7], 30);
b = op(G, (b, c, d, a), words[12], 31);
// round 3
a = op(H, (a, b, c, d), words[5], 32);
d = op(H, (d, a, b, c), words[8], 33);
c = op(H, (c, d, a, b), words[11], 34);
b = op(H, (b, c, d, a), words[14], 35);
a = op(H, (a, b, c, d), words[1], 36);
d = op(H, (d, a, b, c), words[4], 37);
c = op(H, (c, d, a, b), words[7], 38);
b = op(H, (b, c, d, a), words[10], 39);
a = op(H, (a, b, c, d), words[13], 40);
d = op(H, (d, a, b, c), words[0], 41);
c = op(H, (c, d, a, b), words[3], 42);
b = op(H, (b, c, d, a), words[6], 43);
a = op(H, (a, b, c, d), words[9], 44);
d = op(H, (d, a, b, c), words[12], 45);
c = op(H, (c, d, a, b), words[15], 46);
b = op(H, (b, c, d, a), words[2], 47);
// round 4
a = op(I, (a, b, c, d), words[0], 48);
d = op(I, (d, a, b, c), words[7], 49);
c = op(I, (c, d, a, b), words[14], 50);
b = op(I, (b, c, d, a), words[5], 51);
a = op(I, (a, b, c, d), words[12], 52);
d = op(I, (d, a, b, c), words[3], 53);
c = op(I, (c, d, a, b), words[10], 54);
b = op(I, (b, c, d, a), words[1], 55);
a = op(I, (a, b, c, d), words[8], 56);
d = op(I, (d, a, b, c), words[15], 57);
c = op(I, (c, d, a, b), words[6], 58);
b = op(I, (b, c, d, a), words[13], 59);
a = op(I, (a, b, c, d), words[4], 60);
d = op(I, (d, a, b, c), words[11], 61);
c = op(I, (c, d, a, b), words[2], 62);
b = op(I, (b, c, d, a), words[9], 63);
a0 = a0.wrapping_add(a);
b0 = b0.wrapping_add(b);
c0 = c0.wrapping_add(c);
d0 = d0.wrapping_add(d);
(a0, b0, c0, d0)
},
);
Hash([
a.swap_bytes(),
b.swap_bytes(),
c.swap_bytes(),
d.swap_bytes(),
])
}
#[cfg(test)]
mod tests {
use crate::md5;
#[test]
fn md5() {
assert_eq!(
"73e51861b65c1e83d6136fb6a002585e",
md5::hash("tihi xd").to_hex_string()
);
assert_eq!(
"9cdfb439c7876e703e307864c9167a15",
md5::hash("lol").to_hex_string()
);
assert_eq!(
"d41d8cd98f00b204e9800998ecf8427e",
md5::hash("").to_hex_string()
);
assert_eq!("fd6f92edff2b7db7cd92b494c4926ef0", md5::hash("Lorem ipsum dolor sit amet. Nam placeat iste aut dolorem necessitatibus sit unde magni. Nam cumque quia nam fugit quibusdam ut incidunt minima nam dignissimos iusto et voluptatum magnam. Et pariatur eius vel excepturi odit libero sit molestiae consequatur vel nostrum ullam. Sit sint beatae eos doloribus sapiente aut aperiam ullam ut laboriosam debitis! Qui reiciendis rerum est omnis galisum aut similique iure est dolores fuga sit rerum dicta. Aut excepturi fuga et enim ipsum a quia ut velit atque id iste nobis. Non molestiae vero et veritatis aliquam sed enim ducimus. At sunt tempora quo quisquam unde aut ipsum nulla et fugit eius in nulla fugit qui magnam excepturi aut rerum officia. Et officia magnam eos reiciendis voluptatum ea voluptas recusandae in similique enim. Ut provident laudantium ut temporibus eius ut laudantium voluptate aut ducimus impedit. Et asperiores aperiam sit deleniti voluptas ex porro omnis ut voluptas assumenda aut necessitatibus aliquid.").to_hex_string());
}
}