From 167a6cb25acb3daa2f5c8a5e7a825d60818a8326 Mon Sep 17 00:00:00 2001 From: vodofrede Date: Fri, 16 Sep 2022 14:07:57 +0200 Subject: [PATCH] added md2 and md4, updated API to v2 --- Cargo.lock | 592 --------------------------------------------- Cargo.toml | 5 - benches/md5.rs | 14 -- rust-toolchain | 1 + src/bin/sandbox.rs | 9 +- src/hash.rs | 64 +++++ src/hash/md2.rs | 140 +++++++++++ src/hash/md4.rs | 239 ++++++++++++++++++ src/hash/md5.rs | 197 +++++++++++++++ src/hash/sha1.rs | 7 + src/lib.rs | 8 +- src/md5.rs | 205 ---------------- 12 files changed, 656 insertions(+), 825 deletions(-) delete mode 100644 benches/md5.rs create mode 100644 rust-toolchain create mode 100644 src/hash.rs create mode 100644 src/hash/md2.rs create mode 100644 src/hash/md4.rs create mode 100644 src/hash/md5.rs create mode 100644 src/hash/sha1.rs delete mode 100644 src/md5.rs diff --git a/Cargo.lock b/Cargo.lock index 72c458a..a3fff31 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,598 +2,6 @@ # It is not intended for manual editing. version = 3 -[[package]] -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 = "bumpalo" -version = "3.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" - -[[package]] -name = "cast" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c24dab4283a142afa2fdca129b80ad2c6284e073930f964c3a1293c225ee39a" -dependencies = [ - "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.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rayon" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90" -dependencies = [ - "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.130" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e466864e431129c7e0d3476b92f20458e5879919a0596c6472738d9fa2d342f8" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "syn" -version = "1.0.81" -source = "registry+https://github.com/rust-lang/crates.io-index" -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" diff --git a/Cargo.toml b/Cargo.toml index 1fe7d3a..2ac176c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,8 +12,3 @@ categories = ["algorithms", "compression", "cryptography"] exclude = [] [dev-dependencies] -criterion = { version = "^0.3", features = ["html_reports"] } - -[[bench]] -name = "md5" -harness = false \ No newline at end of file diff --git a/benches/md5.rs b/benches/md5.rs deleted file mode 100644 index be8d3b6..0000000 --- a/benches/md5.rs +++ /dev/null @@ -1,14 +0,0 @@ -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); diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 0000000..07ade69 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/src/bin/sandbox.rs b/src/bin/sandbox.rs index c2b4a1d..bd95776 100644 --- a/src/bin/sandbox.rs +++ b/src/bin/sandbox.rs @@ -1,10 +1,3 @@ -use lore::md5; - fn main() { - let input = "lol xd"; - - assert_eq!( - md5::hash(input).to_hex_string(), - "982d7f24f8985a6baa5cf129acc73561" - ); + println!("{}", lore::md2("abc")); } diff --git a/src/hash.rs b/src/hash.rs new file mode 100644 index 0000000..1693236 --- /dev/null +++ b/src/hash.rs @@ -0,0 +1,64 @@ +use std::fmt::Display; + +pub mod md2; +pub mod md4; +pub mod md5; + +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] +pub struct Digest([u8; S]); + +impl Display for Digest { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str( + &self + .0 + .iter() + .map(|u| format!("{:02x}", u)) + .collect::>() + .join(""), + ) + } +} + +pub(crate) fn bytes_to_words_le(bytes: impl AsRef<[u8]>) -> Vec { + bytes + .as_ref() + .array_chunks::<4>() + .map(|chunk| u32::from_le_bytes(*chunk)) + .collect() +} + +pub(crate) fn words_to_bytes_le(words: impl AsRef<[u32]>) -> Vec { + words + .as_ref() + .iter() + .flat_map(|w| w.to_le_bytes()) + .collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bytes_to_words_le_works() { + assert_eq!( + vec![0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476], + 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([0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476]) + ) + } +} diff --git a/src/hash/md2.rs b/src/hash/md2.rs new file mode 100644 index 0000000..3f0b63a --- /dev/null +++ b/src/hash/md2.rs @@ -0,0 +1,140 @@ +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 { + 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 { + 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 +} + +/// Compute the MD2 hash of the input bytes +/// # Examples +/// ``` +/// 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() + ); + } +} diff --git a/src/hash/md4.rs b/src/hash/md4.rs new file mode 100644 index 0000000..8d888eb --- /dev/null +++ b/src/hash/md4.rs @@ -0,0 +1,239 @@ +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(crate) fn pad(message: impl AsRef<[u8]>) -> Vec { + 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], index: usize) -> [u32; 4] { + // choose function and constant based on which round is currently active + let (rc, f) = match index { + 0..=15 => (C1, F), + 16..=31 => (C2, G), + 32..=47 => (C3, H), + _ => 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[index]]) + .wrapping_add(rc) + .rotate_left(S[index]); + + [a, b, c, d] +} + +/// Compute the MD4 hash of the input bytes +/// # Examples +/// ``` +/// 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); + } + } +} diff --git a/src/hash/md5.rs b/src/hash/md5.rs new file mode 100644 index 0000000..32d09d8 --- /dev/null +++ b/src/hash/md5.rs @@ -0,0 +1,197 @@ +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 f = 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 = f(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] +} + +/// Compute the MD5 hash of the input bytes +/// # Examples +/// ``` +/// let input = "lol"; +/// let digest = lore::md5(input); +/// +/// assert_eq!(digest.to_string(), "9cdfb439c7876e703e307864c9167a15"); +/// ``` +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| { + println!("{words:08x?}"); + + let mut state = [a, b, c, d]; + + for i in 0..64 { + state = step(state, &words, i); + println!("{state:08x?}"); + 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 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); + } + } +} diff --git a/src/hash/sha1.rs b/src/hash/sha1.rs new file mode 100644 index 0000000..ff9bed9 --- /dev/null +++ b/src/hash/sha1.rs @@ -0,0 +1,7 @@ +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sha1() {} +} diff --git a/src/lib.rs b/src/lib.rs index a4f2b05..398f617 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,7 @@ -pub mod md5; +#![feature(array_chunks)] + +mod hash; + +pub use hash::md2::hash as md2; +pub use hash::md4::hash as md4; +pub use hash::md5::hash as md5; diff --git a/src/md5.rs b/src/md5.rs deleted file mode 100644 index 39dcc92..0000000 --- a/src/md5.rs +++ /dev/null @@ -1,205 +0,0 @@ -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 { - 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()); - } -}