diff options
| author | Daniel Schadt <kingdread@gmx.de> | 2026-04-23 14:11:14 +0200 |
|---|---|---|
| committer | Daniel Schadt <kingdread@gmx.de> | 2026-04-23 14:11:14 +0200 |
| commit | 3f31e03159406b238f6ded01903cbdbc1b3b499a (patch) | |
| tree | 979a93eda22b4e80087921547000b30aa53a6e37 | |
| download | leona-3f31e03159406b238f6ded01903cbdbc1b3b499a.tar.gz leona-3f31e03159406b238f6ded01903cbdbc1b3b499a.tar.bz2 leona-3f31e03159406b238f6ded01903cbdbc1b3b499a.zip | |
initial commit
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | Cargo.lock | 801 | ||||
| -rw-r--r-- | Cargo.toml | 26 | ||||
| -rw-r--r-- | benches/leona.rs | 54 | ||||
| -rw-r--r-- | src/lib.rs | 428 |
5 files changed, 1310 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..b8f1e75 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,801 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aes" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66bd29a732b644c0431c6140f370d097879203d79b80c94a6747ba0872adaef8" +dependencies = [ + "cipher", + "cpubits", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "blake2" +version = "0.11.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690" +dependencies = [ + "digest", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "cipher" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" +dependencies = [ + "block-buffer", + "crypto-common", + "inout", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpubits" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ef0c543070d296ea414df2dd7625d1b24866ce206709d8a4a424f28377f5861" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctr" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17469f8eb9bdbfad10f71f4cfddfd38b01143520c0e717d8796ccb4d44d44e42" +dependencies = [ + "cipher", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "digest" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", + "ctutils", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest", +] + +[[package]] +name = "hybrid-array" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" +dependencies = [ + "typenum", + "zerocopy", +] + +[[package]] +name = "inout" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leona" +version = "0.1.0" +dependencies = [ + "aes", + "blake2", + "chacha20", + "cipher", + "criterion", + "crypto-common", + "ctr", + "digest", + "hmac", + "hybrid-array", + "sha2", + "thiserror", + "typenum", + "zerocopy", +] + +[[package]] +name = "libc" +version = "0.2.185" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[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 = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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 = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d" +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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0c500ff --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "leona" +version = "0.1.0" +edition = "2024" + +[dependencies] +blake2 = "0.11.0-rc.6" +cipher = "0.5.1" +crypto-common = "0.2.1" +digest = { version = "0.11.2", features = ["mac"] } +hybrid-array = { version = "0.4.10", features = ["zerocopy"] } +thiserror = { version = "2.0.18", default-features = false } +typenum = "1.20.0" +zerocopy = "0.8.48" + +[dev-dependencies] +aes = "0.9.0" +chacha20 = { version = "0.10.0", features = ["cipher"] } +criterion = "0.8.2" +ctr = "0.10.0" +hmac = "0.13.0" +sha2 = "0.11.0" + +[[bench]] +name = "leona" +harness = false diff --git a/benches/leona.rs b/benches/leona.rs new file mode 100644 index 0000000..fe6329e --- /dev/null +++ b/benches/leona.rs @@ -0,0 +1,54 @@ +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; + +use leona::{KeyedHash, Lioness, ZeroIv}; + +use aes::Aes256; +use blake2::Blake2sMac256; +use chacha20::ChaCha20; +use ctr::Ctr32BE; +use hmac::Hmac; +use sha2::Sha256; +use typenum::U32; + +use cipher::stream::StreamCipher; +use crypto_common::{KeyInit, KeySizeUser}; +use digest::{FixedOutput, Update}; +use hybrid_array::Array; +use typenum::{IsLessOrEqual, True, U64}; +use zerocopy::FromZeros; + +fn inner<S, H>(c: &mut Criterion, name_cipher: &str, name_hash: &str) +where + S: KeySizeUser<KeySize = H::OutputSize> + KeyInit + StreamCipher, + H: KeySizeUser + KeyInit + FixedOutput + Update, + S::KeySize: IsLessOrEqual<U64, Output = True>, + H::KeySize: IsLessOrEqual<U64, Output = True>, + Array<u8, S::KeySize>: FromZeros, +{ + const KB: usize = 1024; + + let mut group = c.benchmark_group(format!("{name_cipher}/{name_hash}")); + let cipher = Lioness::<S, H>::new_dynamic(b""); + + for size in [KB, 2 * KB, 4 * KB, 8 * KB, 16 * KB] { + let mut buf = vec![0u8; size]; + + group.throughput(Throughput::Bytes(size as u64)); + + group.bench_function(BenchmarkId::new("encrypt", size), |b| { + b.iter(|| cipher.encrypt(&mut buf)); + }); + } +} + +fn bench(c: &mut Criterion) { + inner::<ZeroIv<ChaCha20>, Blake2sMac256>(c, "chacha20", "blake2s256"); + inner::<ZeroIv<ChaCha20>, KeyedHash<U32, Sha256>>(c, "chacha20", "sha256"); + inner::<ZeroIv<ChaCha20>, Hmac<Sha256>>(c, "chacha20", "sha256-hmac"); + inner::<ZeroIv<Ctr32BE<Aes256>>, Blake2sMac256>(c, "aes256", "blake2s256"); + inner::<ZeroIv<Ctr32BE<Aes256>>, KeyedHash<U32, Sha256>>(c, "aes256", "sha256"); + inner::<ZeroIv<Ctr32BE<Aes256>>, Hmac<Sha256>>(c, "aes256", "sha256-hmac"); +} + +criterion_group!(benches, bench); +criterion_main!(benches); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4c5af26 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,428 @@ +#![no_std] +//! LIONESS implementation in Rust. +//! +//! # ☣️ Cryptographic hazmat ☣️ +//! +//! This crate is not battle tested, nor is it audited. Its usage for critical systems is strongly +//! discouraged. It mainly exists as a learning exercise. +//! +//! # LIONESS +//! +//! [LIONESS](https://link.springer.com/content/pdf/10.1007/3-540-60865-6_48.pdf) is a block cipher +//! based on the Luby-Rackoff construction. It combines a hash function and a stream cipher to +//! produce a block permutation that allows for blocks of arbitrary(*) length to be encrypted. +//! +//! Internally, the cipher uses two instantiations of the keyed hash function, and two +//! instantiations of the stream cipher, leading to four separate round keys. The stream cipher and +//! the hash must be compatible: The output size of the hash must be the same as the key size of +//! the cipher. +//! +//! (*): The construction does not specify how short inputs (smaller than the hash output) should +//! be encrypted. As such, [Lioness] will error on such inputs, and you have to pad the input +//! manually. +//! +//! # Implementation +//! +//! The cipher is implemented as the [Lioness] struct, and is kept generic over the stream cipher +//! (using the [`cipher`](https://crates.io/crates/cipher) crate), as well as the hash function +//! (using the [`digest`](https://crates.io/crates/digest) crate). Rust's type system is used to +//! ensure that the cipher is compatible with the hash (the hash's output size must match the +//! cipher's key size). +//! +//! The construction is implemented in-place and works without allocations. +//! +//! This crate implements several convenience methods: +//! +//! To prevent users from having to manually create four round keys, we provide +//! [Lioness::new_dynamic]. This method internally uses `blake2` to create distinct round keys from +//! a given (potentially short) input key. +//! +//! To use ciphers that require initialization vectors (IVs), we provide [ZeroIv]. This struct +//! wraps a cipher that requires an IV, and supplies a zero-IV. +//! +//! To use unkeyed hash functions (like SHA), we provide [KeyedHash]. This struct wraps a hash +//! function and turns it into a keyed hash by prepending the key to the input data. +//! +//! # Examples +//! +//! A simple working combination for [Lioness] is to use +//! [ChaCha20](https://docs.rs/chacha20/latest/chacha20/type.ChaCha20.html) and +//! [Blake2s256](https://docs.rs/blake2/latest/blake2/type.Blake2sMac256.html) (32 byte key/hash +//! output). Note that we use the MAC variant of Blake2 to get a keyed version of the hash +//! directly: +//! +//! ``` +//! use leona::{Lioness, ZeroIv}; +//! use blake2::Blake2sMac256; +//! use chacha20::ChaCha20; +//! +//! type Cipher = Lioness<ZeroIv<ChaCha20>, Blake2sMac256>; +//! +//! let mut data = [0u8; 64]; +//! data[..11].copy_from_slice(b"hello world"); +//! +//! let cipher = Cipher::new_dynamic(b"secret"); +//! cipher.encrypt(&mut data); +//! assert_ne!(&data[..11], b"hello world"); +//! cipher.decrypt(&mut data); +//! assert_eq!(&data[..11], b"hello world"); +//! ``` +//! +//! You can also use AES in counter mode as a stream cipher, and SHA256 as a hash. Note how we have +//! to use the `Ctr32BE` wrapper to turn AES into a stream cipher, and [KeyedHash] to turn SHA into +//! a keyed hash. We choose Aes256, since its key size matches SHA256's output size (32 bytes): +//! +//! ``` +//! use leona::{KeyedHash, Lioness, ZeroIv}; +//! use aes::Aes256; +//! use ctr::Ctr32BE; +//! use sha2::Sha256; +//! use typenum::U32; +//! +//! type Cipher = Lioness<ZeroIv<Ctr32BE<Aes256>>, KeyedHash<U32, Sha256>>; +//! +//! let mut data = [0u8; 64]; +//! data[..11].copy_from_slice(b"hello world"); +//! +//! let cipher = Cipher::new_dynamic(b"secret"); +//! cipher.encrypt(&mut data); +//! assert_ne!(&data[..11], b"hello world"); +//! cipher.decrypt(&mut data); +//! assert_eq!(&data[..11], b"hello world"); +//! ``` +//! +//! Alternatively, you can use [Hmac](https://docs.rs/hmac/latest/hmac/index.html) to turn unkeyed +//! hash functions into keyed hashes: +//! +//! ``` +//! use leona::{KeyedHash, Lioness, ZeroIv}; +//! use aes::Aes256; +//! use ctr::Ctr32BE; +//! use hmac::Hmac; +//! use sha2::Sha256; +//! +//! type Cipher = Lioness<ZeroIv<Ctr32BE<Aes256>>, Hmac<Sha256>>; +//! +//! let mut data = [0u8; 64]; +//! data[..11].copy_from_slice(b"hello world"); +//! +//! let cipher = Cipher::new_dynamic(b"secret"); +//! cipher.encrypt(&mut data); +//! assert_ne!(&data[..11], b"hello world"); +//! cipher.decrypt(&mut data); +//! assert_eq!(&data[..11], b"hello world"); +//! ``` +//! +//! If the input data is shorter than the hash output, encryption will fail: +//! +//! ``` +//! # use leona::{Lioness, ZeroIv}; +//! # use blake2::Blake2sMac256; +//! # use chacha20::ChaCha20; +//! # type Cipher = Lioness<ZeroIv<ChaCha20>, Blake2sMac256>; +//! let mut data = [0u8; 16]; +//! data[..11].copy_from_slice(b"hello world"); +//! +//! let cipher = Cipher::new_dynamic(b"secret"); +//! assert!(cipher.encrypt(&mut data).is_err()); +//! ``` +//! +//! If you need more control over the keys, use the [Lioness::new] constructor. In this case, you +//! can provide all round keys separately. +//! +//! # Alternatives +//! +//! LIONESS is slow, and it cannot encrypt short data. If you need a wide-block cipher, consider a +//! different construction such as `aez` (via [aez](https://crates.io/crates/aez) or +//! [zears](https://crates.io/crates/zears)): +//! +//! | | `aes256` | `chacha20` | +//! |--------------|--------------|--------------| +//! | **`sha256`** | 645.58 MiB/s | 495.01 MiB/s | +//! | **`blake2`** | 285.56 MiB/s | 251.19 MiB/s | +//! +//! For comparison, `zears` achieves 5.8170 GiB/s (+simd, target-cpu=native). +use core::marker::PhantomData; + +use blake2::Blake2b; +use cipher::{ + InOutBuf, + stream::{StreamCipher, StreamCipherError}, +}; +use crypto_common::{KeyInit, KeyIvInit, KeySizeUser, OutputSizeUser}; +use digest::{CustomizedInit, FixedOutput, Update}; +use hybrid_array::{Array, ArraySize}; +use thiserror::Error; +use typenum::{IsLessOrEqual, True, U64, Unsigned}; +use zerocopy::FromZeros; + +const SALT_1: &[u8] = b"k1"; +const SALT_2: &[u8] = b"k2"; +const SALT_3: &[u8] = b"k3"; +const SALT_4: &[u8] = b"k4"; + +/// Errors that can occur during encryption and decryption. +#[derive(Error, Debug, Clone)] +pub enum Error { + /// The given input data is shorter than the hash output. + #[error("given input data is too short")] + InputTooShort, +} + +/// Main struct implementing the LIONESS construction. +/// +/// `S` represents the stream cipher and `H` represents the hash algorithm. +/// +/// See the crate level documentation for more details. +pub struct Lioness<S, H> +where + S: KeySizeUser, + H: KeySizeUser, +{ + k_1: Array<u8, S::KeySize>, + k_2: Array<u8, H::KeySize>, + k_3: Array<u8, S::KeySize>, + k_4: Array<u8, H::KeySize>, +} + +fn hash_key<L>(salt: &[u8], input: &[u8]) -> Array<u8, L> +where + L: ArraySize + IsLessOrEqual<U64, Output = True>, +{ + let mut hasher = Blake2b::new_customized(salt); + hasher.update(input); + hasher.finalize_fixed() +} + +fn xor_assign<L: ArraySize>(a: &mut L::ArrayType<u8>, b: &L::ArrayType<u8>) { + a.as_mut() + .iter_mut() + .zip(b.as_ref().iter()) + .for_each(|(x, y)| *x ^= *y); +} + +type Key<K> = <<K as KeySizeUser>::KeySize as ArraySize>::ArrayType<u8>; + +impl<S, H> Lioness<S, H> +where + S: KeySizeUser<KeySize = H::OutputSize> + KeyInit + StreamCipher, + H: KeySizeUser + KeyInit + FixedOutput + Update, + S::KeySize: IsLessOrEqual<U64, Output = True>, + H::KeySize: IsLessOrEqual<U64, Output = True>, + Array<u8, S::KeySize>: FromZeros, +{ + /// Create a new [Lioness] instance from the given key. + /// + /// Unlike [Lioness::new], you are not required to pass the correct key sizes manually. + /// Instead, `blake2` is used internally to derive keys of the correct length. + /// + /// Note that this only works for keys up to 64 bytes. If you have longer keys, you need to use + /// [Lioness::new]. + pub fn new_dynamic(key: &[u8]) -> Self { + Self::new( + hash_key::<S::KeySize>(SALT_1, key).0, + hash_key::<H::KeySize>(SALT_2, key).0, + hash_key::<S::KeySize>(SALT_3, key).0, + hash_key::<H::KeySize>(SALT_4, key).0, + ) + } +} + +impl<S, H> Lioness<S, H> +where + S: KeySizeUser<KeySize = H::OutputSize> + KeyInit + StreamCipher, + H: KeySizeUser + KeyInit + FixedOutput + Update, + Array<u8, S::KeySize>: FromZeros, +{ + /// Create a new [Lioness] instance from the given round keys. + /// + /// `k_1` and `k_3` are used for the stream cipher and must match its key length, while `k_2` + /// and `k_4` are used for the hash function. + /// + /// Don't be scared of the long type signatures. They are just normal arrays of `u8`: + /// + /// ``` + /// # use leona::{Lioness, ZeroIv}; + /// # use blake2::Blake2sMac256; + /// # use chacha20::ChaCha20; + /// type Cipher = Lioness<ZeroIv<ChaCha20>, Blake2sMac256>; + /// Cipher::new([1; 32], [2; 32], [3; 32], [4; 32]); + /// ``` + pub fn new(k_1: Key<S>, k_2: Key<H>, k_3: Key<S>, k_4: Key<H>) -> Self { + Self { + k_1: k_1.into(), + k_2: k_2.into(), + k_3: k_3.into(), + k_4: k_4.into(), + } + } + + /// Encrypt the given buffer in-place. + /// + /// If the buffer is too short (shorter than the hash output), an error is returned. + pub fn encrypt(&self, data: &mut [u8]) -> Result<(), Error> { + let l_len = S::KeySize::USIZE; + if data.len() < l_len { + return Err(Error::InputTooShort); + } + + assert!(data.len() >= l_len); + + // R = R + S(L + K_1) + let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed(); + key.copy_from_slice(&data[..l_len]); + xor_assign::<S::KeySize>(&mut key.0, &self.k_1.0); + Self::apply_keystream(key.into(), &mut data[l_len..]); + + // L = L + H(K_2, R) + let hash = Self::hash(&self.k_2, &data[l_len..]); + data[..l_len] + .iter_mut() + .zip(hash) + .for_each(|(x, y)| *x ^= y); + + // R = R + S(L + K_3) + let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed(); + key.copy_from_slice(&data[..l_len]); + xor_assign::<S::KeySize>(&mut key.0, &self.k_3.0); + Self::apply_keystream(key.into(), &mut data[l_len..]); + + // L = L + H(K_4, R) + let hash = Self::hash(&self.k_4, &data[l_len..]); + data[..l_len] + .iter_mut() + .zip(hash) + .for_each(|(x, y)| *x ^= y); + + Ok(()) + } + + /// Decrypt the given buffer in-place. + /// + /// If the buffer is too short (shorter than the hash output), an error is returned. + pub fn decrypt(&self, data: &mut [u8]) -> Result<(), Error> { + let l_len = S::KeySize::USIZE; + if data.len() < l_len { + return Err(Error::InputTooShort); + } + + assert!(data.len() >= l_len); + + // L = L + H(K_4, R) + let hash = Self::hash(&self.k_4, &data[l_len..]); + data[..l_len] + .iter_mut() + .zip(hash) + .for_each(|(x, y)| *x ^= y); + + // R = R + S(L + K_3) + let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed(); + key.copy_from_slice(&data[..l_len]); + xor_assign::<S::KeySize>(&mut key.0, &self.k_3.0); + Self::apply_keystream(key.into(), &mut data[l_len..]); + + // L = L + H(K_2, R) + let hash = Self::hash(&self.k_2, &data[l_len..]); + data[..l_len] + .iter_mut() + .zip(hash) + .for_each(|(x, y)| *x ^= y); + + // R = R + S(L + K_1) + let mut key = <Array<u8, S::KeySize> as FromZeros>::new_zeroed(); + key.copy_from_slice(&data[..l_len]); + xor_assign::<S::KeySize>(&mut key.0, &self.k_1.0); + Self::apply_keystream(key.into(), &mut data[l_len..]); + + Ok(()) + } + + fn apply_keystream(key: Key<S>, data: &mut [u8]) { + let mut cipher = S::new(&Array(key)); + cipher.apply_keystream(data); + } + + fn hash(key: &Array<u8, H::KeySize>, data: &[u8]) -> Array<u8, H::OutputSize> { + let mut hasher = H::new(key); + hasher.update(data); + hasher.finalize_fixed() + } +} + +/// A helper struct that turns ciphers with IV into ciphers without IV. +/// +/// This turns ciphers that require an IV (like `ChaCha20`), represented by +/// [KeyIvInit](https://docs.rs/cipher/latest/cipher/trait.KeyIvInit.html), into ciphers that only +/// require a key, represented by +/// [KeyInit](https://docs.rs/cipher/latest/cipher/trait.KeyInit.html). To do so, an all-zero IV is +/// supplied. +#[derive(Debug, Clone)] +pub struct ZeroIv<C>(C); + +impl<C: KeySizeUser> KeySizeUser for ZeroIv<C> { + type KeySize = C::KeySize; +} + +impl<C: KeyIvInit> KeyInit for ZeroIv<C> +where + Array<u8, C::IvSize>: FromZeros, +{ + fn new(key: &Array<u8, Self::KeySize>) -> Self { + let iv = <Array<u8, C::IvSize> as FromZeros>::new_zeroed(); + Self(C::new(key, &iv)) + } +} + +impl<C: StreamCipher> StreamCipher for ZeroIv<C> { + fn check_remaining(&self, data_len: usize) -> Result<(), StreamCipherError> { + self.0.check_remaining(data_len) + } + + fn unchecked_apply_keystream_inout(&mut self, buf: InOutBuf<'_, '_, u8>) { + self.0.unchecked_apply_keystream_inout(buf) + } + + fn unchecked_write_keystream(&mut self, buf: &mut [u8]) { + self.0.unchecked_write_keystream(buf) + } +} + +/// A helper struct that turns unkeyed hashes into keyed hashes. +/// +/// This turns hashes that have no key (like SHA256) into hashes that have a key. Note that the +/// construction is rather simple: the key is prepended to the input data. This method is suggested +/// in the LIONESS paper +/// +/// > The keyed hash function H_K(M): +/// > (a) is based on an unkeyed hash function H'(M), in which we append and/or prepend the key to +/// > the message [...] +#[derive(Debug, Clone)] +pub struct KeyedHash<L, H>(H, PhantomData<L>); + +impl<L: ArraySize, H> KeySizeUser for KeyedHash<L, H> { + type KeySize = L; +} + +impl<L: ArraySize, H: Default + Update> KeyInit for KeyedHash<L, H> { + fn new(key: &Array<u8, Self::KeySize>) -> Self { + let mut hasher = H::default(); + hasher.update(key); + KeyedHash(hasher, PhantomData) + } +} + +impl<L: ArraySize, H: OutputSizeUser> OutputSizeUser for KeyedHash<L, H> { + type OutputSize = H::OutputSize; +} + +impl<L: ArraySize, H: Update> Update for KeyedHash<L, H> { + fn update(&mut self, data: &[u8]) { + self.0.update(data) + } +} + +impl<L: ArraySize, H: FixedOutput> FixedOutput for KeyedHash<L, H> { + fn finalize_into(self, buffer: &mut Array<u8, Self::OutputSize>) { + self.0.finalize_into(buffer) + } +} |
