Compare commits

...

10 commits

Author SHA1 Message Date
6b02b20dad release 2.1.0 2022-09-20 16:00:53 +02:00
2baa7ab62a updated documentation 2022-09-20 14:19:20 +02:00
f1958765bc fixed botched docs 2022-09-20 14:08:12 +02:00
40e2593a5e added SHA1 2022-09-20 14:07:04 +02:00
e9ab63efe4 sha-1 wip 2022-09-19 13:03:47 +02:00
e8c0d38021 improved overall crate documentation 2022-09-17 15:49:17 +02:00
57ab36f117 bumped version number 2022-09-16 14:12:38 +02:00
9799c875a7 updated README 2022-09-16 14:12:14 +02:00
c5ca4a85df updated README 2022-09-16 14:10:44 +02:00
167a6cb25a added md2 and md4, updated API to v2 2022-09-16 14:07:57 +02:00
13 changed files with 935 additions and 799 deletions

570
Cargo.lock generated
View file

@ -2,598 +2,64 @@
# 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"
version = "2.1.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",
"serde",
]
[[package]]
name = "proc-macro2"
version = "1.0.32"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba508cc11742c0dc5c1659771673afbab7a0efab23aa17e854cbab0837ed0b43"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.10"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38bc8cc6a5f2e3655e0899c1b848643b2562f853f114bfec7be120678e3ace05"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
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"
version = "1.0.144"
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"
checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860"
dependencies = [
"half",
"serde",
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.130"
version = "1.0.144"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b"
checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00"
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"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2afee18b8beb5a596ecb4a2dce128c719b4ba399d34126b9e4396e3f9860966"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-xid",
"unicode-ident",
]
[[package]]
name = "textwrap"
version = "0.11.0"
name = "unicode-ident"
version = "1.0.4"
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"
checksum = "dcc811dc4066ac62f84f11307873c4850cb653bfa9b1719cee2bd2204a4bc5dd"

View file

@ -1,6 +1,6 @@
[package]
name = "lore"
version = "2.0.0"
version = "2.1.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 = []
[dev-dependencies]
criterion = { version = "^0.3", features = ["html_reports"] }
[dependencies]
serde = { version = ">=1.0.0", features = ["derive"], optional = true }
[[bench]]
name = "md5"
harness = false
[features]
default = []
serde = ["dep:serde"]

View file

@ -1,19 +1,32 @@
# Lore
Hashing algorithms. Currently only implements md5.
Nightly-only hashing algorithms with a straight-forward API and no required dependencies.
Doesn't use any dependencies.
This crate currently implements:
Performance/stability not guaranteed.
- MD2, MD4, and MD5
- SHA-1
## Example
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:
```rust
use lore::md5;
fn main() {
let input = "lol xd";
let digest = md5::hash(input);
assert_eq!(digest, "982d7f24f8985a6baa5cf129acc73561");
}
let input = "lol xd";
let digest = lore::md5(input);
assert_eq!(digest.to_string(), "982d7f24f8985a6baa5cf129acc73561");
```

View file

@ -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);

1
rust-toolchain Normal file
View file

@ -0,0 +1 @@
nightly

View file

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

120
src/hash.rs Normal file
View file

@ -0,0 +1,120 @@
#[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])
);
}
}

146
src/hash/md2.rs Normal file
View file

@ -0,0 +1,146 @@
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()
);
}
}

245
src/hash/md4.rs Normal file
View file

@ -0,0 +1,245 @@
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);
}
}
}

205
src/hash/md5.rs Normal file
View file

@ -0,0 +1,205 @@
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);
}
}
}

148
src/hash/sha1.rs Normal file
View file

@ -0,0 +1,148 @@
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 +1,21 @@
pub mod md5;
// 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;

View file

@ -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<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());
}
}