aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Schadt <kingdread@gmx.de>2026-04-23 14:11:14 +0200
committerDaniel Schadt <kingdread@gmx.de>2026-04-23 14:11:14 +0200
commit3f31e03159406b238f6ded01903cbdbc1b3b499a (patch)
tree979a93eda22b4e80087921547000b30aa53a6e37
downloadleona-3f31e03159406b238f6ded01903cbdbc1b3b499a.tar.gz
leona-3f31e03159406b238f6ded01903cbdbc1b3b499a.tar.bz2
leona-3f31e03159406b238f6ded01903cbdbc1b3b499a.zip
initial commit
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock801
-rw-r--r--Cargo.toml26
-rw-r--r--benches/leona.rs54
-rw-r--r--src/lib.rs428
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)
+ }
+}