From d646d00be547ec394a2e7af8b1079a9e79d33e68 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 09:12:45 -0600 Subject: [PATCH 1/8] Add hqc-kem Signed-off-by: Mike Lodder --- Cargo.toml | 1 + hqc-kem/Cargo.lock | 1139 +++++++++++++++ hqc-kem/Cargo.toml | 64 + hqc-kem/README.md | 209 +++ hqc-kem/benches/kem.rs | 40 + hqc-kem/hybrid-array-patch/.cargo-ok | 1 + .../hybrid-array-patch/.cargo_vcs_info.json | 6 + .../hybrid-array-patch/.github/dependabot.yml | 12 + .../.github/workflows/hybrid-array.yml | 109 ++ .../.github/workflows/publish.yml | 26 + hqc-kem/hybrid-array-patch/.gitignore | 10 + hqc-kem/hybrid-array-patch/CHANGELOG.md | 194 +++ hqc-kem/hybrid-array-patch/Cargo.lock | 146 ++ hqc-kem/hybrid-array-patch/Cargo.toml | 138 ++ hqc-kem/hybrid-array-patch/Cargo.toml.orig | 77 ++ hqc-kem/hybrid-array-patch/LICENSE-APACHE | 201 +++ hqc-kem/hybrid-array-patch/LICENSE-MIT | 25 + hqc-kem/hybrid-array-patch/README.md | 68 + hqc-kem/hybrid-array-patch/src/flatten.rs | 137 ++ hqc-kem/hybrid-array-patch/src/from_fn.rs | 106 ++ hqc-kem/hybrid-array-patch/src/iter.rs | 107 ++ hqc-kem/hybrid-array-patch/src/lib.rs | 1217 +++++++++++++++++ hqc-kem/hybrid-array-patch/src/serde.rs | 71 + hqc-kem/hybrid-array-patch/src/sizes.rs | 1164 ++++++++++++++++ hqc-kem/hybrid-array-patch/src/traits.rs | 199 +++ hqc-kem/hybrid-array-patch/tests/ctutils.rs | 42 + hqc-kem/hybrid-array-patch/tests/mod.rs | 201 +++ hqc-kem/hybrid-array-patch/tests/subtle.rs | 29 + hqc-kem/kat/hqc-1.rsp | 9 + hqc-kem/kat/hqc-3.rsp | 9 + hqc-kem/kat/hqc-5.rsp | 9 + hqc-kem/src/code.rs | 18 + hqc-kem/src/error.rs | 38 + hqc-kem/src/fft.rs | 255 ++++ hqc-kem/src/gf256.rs | 77 ++ hqc-kem/src/kem.rs | 188 +++ hqc-kem/src/kem_impl.rs | 232 ++++ hqc-kem/src/lib.rs | 176 +++ hqc-kem/src/params.rs | 225 +++ hqc-kem/src/pkcs8_impl.rs | 260 ++++ hqc-kem/src/pke.rs | 154 +++ hqc-kem/src/poly.rs | 504 +++++++ hqc-kem/src/reed_muller.rs | 149 ++ hqc-kem/src/reed_solomon.rs | 233 ++++ hqc-kem/src/sampling.rs | 146 ++ hqc-kem/src/shake.rs | 95 ++ hqc-kem/src/sizes.rs | 13 + hqc-kem/src/types.rs | 433 ++++++ hqc-kem/tests/kat.rs | 174 +++ 49 files changed, 9136 insertions(+) create mode 100644 hqc-kem/Cargo.lock create mode 100644 hqc-kem/Cargo.toml create mode 100644 hqc-kem/README.md create mode 100644 hqc-kem/benches/kem.rs create mode 100644 hqc-kem/hybrid-array-patch/.cargo-ok create mode 100644 hqc-kem/hybrid-array-patch/.cargo_vcs_info.json create mode 100644 hqc-kem/hybrid-array-patch/.github/dependabot.yml create mode 100644 hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml create mode 100644 hqc-kem/hybrid-array-patch/.github/workflows/publish.yml create mode 100644 hqc-kem/hybrid-array-patch/.gitignore create mode 100644 hqc-kem/hybrid-array-patch/CHANGELOG.md create mode 100644 hqc-kem/hybrid-array-patch/Cargo.lock create mode 100644 hqc-kem/hybrid-array-patch/Cargo.toml create mode 100644 hqc-kem/hybrid-array-patch/Cargo.toml.orig create mode 100644 hqc-kem/hybrid-array-patch/LICENSE-APACHE create mode 100644 hqc-kem/hybrid-array-patch/LICENSE-MIT create mode 100644 hqc-kem/hybrid-array-patch/README.md create mode 100644 hqc-kem/hybrid-array-patch/src/flatten.rs create mode 100644 hqc-kem/hybrid-array-patch/src/from_fn.rs create mode 100644 hqc-kem/hybrid-array-patch/src/iter.rs create mode 100644 hqc-kem/hybrid-array-patch/src/lib.rs create mode 100644 hqc-kem/hybrid-array-patch/src/serde.rs create mode 100644 hqc-kem/hybrid-array-patch/src/sizes.rs create mode 100644 hqc-kem/hybrid-array-patch/src/traits.rs create mode 100644 hqc-kem/hybrid-array-patch/tests/ctutils.rs create mode 100644 hqc-kem/hybrid-array-patch/tests/mod.rs create mode 100644 hqc-kem/hybrid-array-patch/tests/subtle.rs create mode 100644 hqc-kem/kat/hqc-1.rsp create mode 100644 hqc-kem/kat/hqc-3.rsp create mode 100644 hqc-kem/kat/hqc-5.rsp create mode 100644 hqc-kem/src/code.rs create mode 100644 hqc-kem/src/error.rs create mode 100644 hqc-kem/src/fft.rs create mode 100644 hqc-kem/src/gf256.rs create mode 100644 hqc-kem/src/kem.rs create mode 100644 hqc-kem/src/kem_impl.rs create mode 100644 hqc-kem/src/lib.rs create mode 100644 hqc-kem/src/params.rs create mode 100644 hqc-kem/src/pkcs8_impl.rs create mode 100644 hqc-kem/src/pke.rs create mode 100644 hqc-kem/src/poly.rs create mode 100644 hqc-kem/src/reed_muller.rs create mode 100644 hqc-kem/src/reed_solomon.rs create mode 100644 hqc-kem/src/sampling.rs create mode 100644 hqc-kem/src/shake.rs create mode 100644 hqc-kem/src/sizes.rs create mode 100644 hqc-kem/src/types.rs create mode 100644 hqc-kem/tests/kat.rs diff --git a/Cargo.toml b/Cargo.toml index a5dedb5..bc13478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,7 @@ members = [ "dhkem", "frodo-kem", "ml-kem", + "hqc-kem", "module-lattice", "x-wing" ] diff --git a/hqc-kem/Cargo.lock b/hqc-kem/Cargo.lock new file mode 100644 index 0000000..28bae06 --- /dev/null +++ b/hqc-kem/Cargo.lock @@ -0,0 +1,1139 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[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 = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-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.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" +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", + "cpufeatures 0.3.0", + "rand_core", +] + +[[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 = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +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 = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[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.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +dependencies = [ + "hybrid-array", + "rand_core", +] + +[[package]] +name = "der" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +dependencies = [ + "const-oid", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common 0.1.7", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "rand_core", + "wasip2", + "wasip3", +] + +[[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 = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hqc-kem" +version = "0.1.0" +dependencies = [ + "const-oid", + "criterion", + "hex", + "hybrid-array", + "kem", + "pkcs8", + "rand", + "serde", + "serdect", + "sha3", + "subtle", + "thiserror", + "typenum", + "zeroize", +] + +[[package]] +name = "hybrid-array" +version = "0.4.9" +dependencies = [ + "typenum", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", + "serde", + "serde_core", +] + +[[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.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + +[[package]] +name = "kem" +version = "0.3.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ae2c3347ff4a7af4f679a9e397c2c7e6034a00b773dd2dd3c001d7f40897c9" +dependencies = [ + "crypto-common 0.2.1", + "rand_core", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[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 = "pem-rfc7468" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9" +dependencies = [ + "base64ct", +] + +[[package]] +name = "pkcs8" +version = "0.11.0-rc.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577" +dependencies = [ + "der", + "spki", +] + +[[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 = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[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 = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" + +[[package]] +name = "rayon" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +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 = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[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 = "serdect" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9af4a3e75ebd5599b30d4de5768e00b5095d518a79fefc3ecbaf77e665d1ec06" +dependencies = [ + "base16ct", + "serde", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[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.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[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 = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.92" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84cde8507f4d7cfcb1185b8cb5890c494ffea65edbe1ba82cfd63661c805ed94" +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 = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[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 = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +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/hqc-kem/Cargo.toml b/hqc-kem/Cargo.toml new file mode 100644 index 0000000..3fa2614 --- /dev/null +++ b/hqc-kem/Cargo.toml @@ -0,0 +1,64 @@ +[package] +name = "hqc-kem" +categories = ["cryptography"] +description = "Pure Rust implementation of HQC-KEM as described in FIPS 207" +edition = "2024" +homepage = "https://github.com/RustCrypto/KEMs/tree/master/hqc-kem" +keywords = ["hqc", "kem", "post-quantum", "cryptography", "codes"] +license = "MIT OR Apache-2.0" +readme = "README.md" +repository = "https://github.com/RustCrypto/KEMs" +version = "0.1.0" + +[features] +default = ["kgen", "ecap", "dcap"] +kgen = [] +ecap = [] +dcap = [] +kem = ["dep:kem_traits", "dep:hybrid-array", "dep:typenum", "kgen", "ecap", "dcap"] +pkcs8 = ["dep:const-oid", "dep:pkcs8"] +pem = ["pkcs8/pem"] +alloc = ["pkcs8?/alloc"] +serde = ["dep:serdect", "dep:serde"] + +[dependencies] +const-oid = { version = "0.10", optional = true } +hex = "0.4" +hybrid-array = { version = "0.4.7", optional = true, features = ["extra-sizes"] } +kem_traits = { package = "kem", version = "0.3.0-rc.6", optional = true } +pkcs8 = { version = "0.11.0-rc.11", optional = true, default-features = false } +rand = "0.10" +sha3 = "0.10" +serdect = { version = "0.4", optional = true } +serde = { version = "1", features = ["derive"], optional = true } +subtle = "2" +thiserror = "2" +typenum = { version = "1", optional = true } +zeroize = { version = "1", features = ["derive"] } + +[dev-dependencies] +criterion = { version = "0.8", features = ["html_reports"] } +hex = "0.4" +rand = { version = "0.10", features = ["thread_rng"] } + +[[bench]] +name = "kem" +harness = false + +[patch.crates-io] +hybrid-array = { path = "hybrid-array-patch" } + +[lints.rust] +missing_docs = "deny" +missing_debug_implementations = "deny" +unused_import_braces = "deny" +unused_lifetimes = "deny" +unused_parens = "deny" +unused_qualifications = "deny" +unused_results = "deny" +unused_extern_crates = "deny" + +[lints.clippy] +unwrap_used = "deny" +panic = "warn" +panic_in_result_fn = "warn" diff --git a/hqc-kem/README.md b/hqc-kem/README.md new file mode 100644 index 0000000..ca258fb --- /dev/null +++ b/hqc-kem/README.md @@ -0,0 +1,209 @@ +# hqc-kem + +Pure Rust implementation of **HQC-KEM** (Hamming Quasi-Cyclic Key Encapsulation Mechanism), a post-quantum KEM based on quasi-cyclic codes over the ring Z_2[X]/(X^n-1). + +HQC uses concatenated Reed-Solomon + Reed-Muller error correction with the Fujisaki-Okamoto transform for IND-CCA2 security. It's currently selected as the backup approved KEM to ML-KEM. + +[![Crates.io](https://img.shields.io/crates/v/hqc-kem.svg)](https://crates.io/crates/hqc-kem) +[![Documentation](https://docs.rs/hqc-kem/badge.svg)](https://docs.rs/hqc-kem) +[![License](https://img.shields.io/crates/l/hqc-kem.svg)](https://github.com/mikelodder7/hqc-kem) + +## References + +- [NIST FIPS 207 (HQC)](https://csrc.nist.gov/pubs/fips/207/ipd) - HQC Initial Public Draft +- [NIST Post-Quantum Cryptography](https://csrc.nist.gov/projects/post-quantum-cryptography) - NIST PQC project page +- [HQC Official Site](https://pqc-hqc.org/) - Reference implementations, specifications, and KAT vectors +- [HQC v5.0.0 Specification](https://pqc-hqc.org/doc/hqc-spec-2025-02-10.pdf) - Full specification document + +## Security Levels + +| Level | Type Alias | NIST Category | Public Key | Secret Key | Ciphertext | Shared Secret | +|-------|-----------|---------------|------------|------------|------------|---------------| +| HQC-128 | `Hqc128` | Level 1 (128-bit) | 2,241 B | 2,321 B | 4,433 B | 32 B | +| HQC-192 | `Hqc192` | Level 3 (192-bit) | 4,514 B | 4,602 B | 8,978 B | 32 B | +| HQC-256 | `Hqc256` | Level 5 (256-bit) | 7,237 B | 7,333 B | 14,421 B | 32 B | + +### Key Generation + +```rust +use hqc_kem::{Hqc256, HqcKem}; + +let mut rng = rand::rng(); +let (ek, dk) = Hqc256::generate_key(&mut rng); + +// Access raw bytes +let pk_bytes: &[u8] = ek.as_ref(); +let sk_bytes: &[u8] = dk.as_ref(); +``` + +### Encapsulation + +```rust +use hqc_kem::{Hqc256, HqcKem}; + +let mut rng = rand::rng(); +let (ek, dk) = Hqc256::generate_key(&mut rng); + +// Sender encapsulates with the public key +let (ct, shared_secret) = ek.encapsulate(&mut rng); + +let ct_bytes: &[u8] = ct.as_ref(); +let ss_bytes: &[u8] = shared_secret.as_ref(); +``` + +### Decapsulation + +```rust +use hqc_kem::{Hqc256, HqcKem}; + +let mut rng = rand::rng(); +let (ek, dk) = Hqc256::generate_key(&mut rng); +let (ct, ss_sender) = ek.encapsulate(&mut rng); + +// Receiver decapsulates with the secret key +let ss_receiver = dk.decapsulate(&ct); + +assert_eq!(ss_sender, ss_receiver); +``` + +### Serialization / Deserialization + +All types implement `AsRef<[u8]>` and `TryFrom<&[u8]>` for raw byte conversion: + +```rust +use hqc_kem::{Hqc128, HqcKem, EncapsulationKey, Hqc128Params}; + +let mut rng = rand::rng(); +let (ek, dk) = Hqc128::generate_key(&mut rng); + +// Serialize to bytes +let pk_bytes: Vec = ek.as_ref().to_vec(); + +// Deserialize from bytes +let ek_restored: EncapsulationKey = pk_bytes.as_slice().try_into() + .expect("invalid public key length"); +``` + +With the `serde` feature enabled, all types implement `Serialize` and `Deserialize`: + +```toml +[dependencies] +hqc-kem = { version = "0.1", features = ["serde"] } +``` + +```rust,ignore +use hqc_kem::{Hqc128, HqcKem}; + +let mut rng = rand::rng(); +let (ek, _dk) = Hqc128::generate_key(&mut rng); + +// Serialize to JSON (hex-encoded) +let json = serde_json::to_string(&ek).unwrap(); + +// Deserialize from JSON +let ek_restored: hqc_kem::EncapsulationKey = + serde_json::from_str(&json).unwrap(); +``` + +### Deterministic Key Generation + +Generate identical key pairs from a 32-byte seed: + +```rust +use hqc_kem::{Hqc128, HqcKem}; + +let seed = [0x42u8; 32]; +let (ek, dk) = Hqc128::generate_key_deterministic(&seed); + +// Same seed always produces the same key pair +let (ek2, dk2) = Hqc128::generate_key_deterministic(&seed); +assert_eq!(ek.as_ref(), ek2.as_ref()); +``` + +### Deterministic Encapsulation + +Produce identical ciphertext and shared secret from a message and salt: + +```rust +use hqc_kem::{Hqc128, HqcKem, hqc128}; + +let mut rng = rand::rng(); +let (ek, dk) = Hqc128::generate_key(&mut rng); + +// Message size depends on security level (16/24/32 bytes) +let m = [0xABu8; hqc128::MESSAGE_SIZE]; +let salt = [0xCDu8; hqc128::SALT_SIZE]; + +let (ct, ss) = ek.encapsulate_deterministic(&m, &salt).unwrap(); + +// Same inputs always produce the same output +let (ct2, ss2) = ek.encapsulate_deterministic(&m, &salt).unwrap(); +assert_eq!(ct.as_ref(), ct2.as_ref()); +assert_eq!(ss, ss2); + +// Decapsulation works as usual +let ss3 = dk.decapsulate(&ct); +assert_eq!(ss, ss3); +``` + +Message sizes per security level: + +| Level | `MESSAGE_SIZE` | `SALT_SIZE` | +|-------|---------------|-------------| +| HQC-128 | 16 bytes | 16 bytes | +| HQC-192 | 24 bytes | 16 bytes | +| HQC-256 | 32 bytes | 16 bytes | + +### Module-Style API + +For a more concise import style, use the security-level modules directly: + +```rust +use hqc_kem::hqc128; + +let mut rng = rand::rng(); +let (ek, dk) = hqc128::generate_key(&mut rng); +let (ct, ss1) = ek.encapsulate(&mut rng); +let ss2 = dk.decapsulate(&ct); +assert_eq!(ss1, ss2); +``` + +### Generic Code + +Write code that works across all security levels: + +```rust,ignore +use hqc_kem::{HqcKem, HqcParams, EncapsulationKey, DecapsulationKey}; + +fn roundtrip(rng: &mut impl rand::CryptoRng) { + let (ek, dk) = HqcKem::

::generate_key(rng); + let (ct, ss1) = ek.encapsulate(rng); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss1, ss2); +} +``` + +## Features + +| Feature | Default | Description | +|---------|---------|-------------| +| `kgen` | Yes | Key generation (`HqcKem::generate_key`) | +| `ecap` | Yes | Encapsulation (`EncapsulationKey::encapsulate`) | +| `dcap` | Yes | Decapsulation (`DecapsulationKey::decapsulate`) | +| `serde` | No | Serde `Serialize`/`Deserialize` for all types | + +## Security + +- Constant-time operations for side-channel resistance (via `subtle` crate) +- Secret key material is zeroized on drop (via `zeroize` crate) +- Shared secrets use constant-time equality comparison +- IND-CCA2 security via Fujisaki-Okamoto transform with implicit rejection + +## License + +Licensed under either of: + +- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +- [MIT License](http://opensource.org/licenses/MIT) + +at your option. diff --git a/hqc-kem/benches/kem.rs b/hqc-kem/benches/kem.rs new file mode 100644 index 0000000..40311e0 --- /dev/null +++ b/hqc-kem/benches/kem.rs @@ -0,0 +1,40 @@ +#![allow(missing_docs, unused_results)] + +use criterion::{Criterion, criterion_group, criterion_main}; +use hqc_kem::{hqc128, hqc192, hqc256}; + +fn bench_keygen(c: &mut Criterion) { + let mut rng = rand::rng(); + + c.bench_function("keygen_128", |b| b.iter(|| hqc128::generate_key(&mut rng))); + c.bench_function("keygen_192", |b| b.iter(|| hqc192::generate_key(&mut rng))); + c.bench_function("keygen_256", |b| b.iter(|| hqc256::generate_key(&mut rng))); +} + +fn bench_encaps(c: &mut Criterion) { + let mut rng = rand::rng(); + let (ek128, _) = hqc128::generate_key(&mut rng); + let (ek192, _) = hqc192::generate_key(&mut rng); + let (ek256, _) = hqc256::generate_key(&mut rng); + + c.bench_function("encaps_128", |b| b.iter(|| ek128.encapsulate(&mut rng))); + c.bench_function("encaps_192", |b| b.iter(|| ek192.encapsulate(&mut rng))); + c.bench_function("encaps_256", |b| b.iter(|| ek256.encapsulate(&mut rng))); +} + +fn bench_decaps(c: &mut Criterion) { + let mut rng = rand::rng(); + let (ek128, dk128) = hqc128::generate_key(&mut rng); + let (ek192, dk192) = hqc192::generate_key(&mut rng); + let (ek256, dk256) = hqc256::generate_key(&mut rng); + let (ct128, _) = ek128.encapsulate(&mut rng); + let (ct192, _) = ek192.encapsulate(&mut rng); + let (ct256, _) = ek256.encapsulate(&mut rng); + + c.bench_function("decaps_128", |b| b.iter(|| dk128.decapsulate(&ct128))); + c.bench_function("decaps_192", |b| b.iter(|| dk192.decapsulate(&ct192))); + c.bench_function("decaps_256", |b| b.iter(|| dk256.decapsulate(&ct256))); +} + +criterion_group!(benches, bench_keygen, bench_encaps, bench_decaps); +criterion_main!(benches); diff --git a/hqc-kem/hybrid-array-patch/.cargo-ok b/hqc-kem/hybrid-array-patch/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json b/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json new file mode 100644 index 0000000..d20bb7c --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "1cccda21548ebb317e6c3a0b47dbd70c746a0f84" + }, + "path_in_vcs": "" +} \ No newline at end of file diff --git a/hqc-kem/hybrid-array-patch/.github/dependabot.yml b/hqc-kem/hybrid-array-patch/.github/dependabot.yml new file mode 100644 index 0000000..397bdaa --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: +- package-ecosystem: cargo + directory: "/" + schedule: + interval: daily + open-pull-requests-limit: 10 +- package-ecosystem: github-actions + directory: "/" + schedule: + interval: weekly + open-pull-requests-limit: 10 diff --git a/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml b/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml new file mode 100644 index 0000000..70df483 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml @@ -0,0 +1,109 @@ +name: hybrid-array + +on: + pull_request: + paths-ignore: + - README.md + push: + branches: master + paths-ignore: + - README.md + +permissions: + contents: read + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-D warnings" + RUSTDOCFLAGS: "-D warnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.85.0 # MSRV + - stable + target: + - armv7a-none-eabi + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v6 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - uses: RustCrypto/actions/cargo-hack-install@master + - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-all-features --optional-deps bytemuck,ctutils,serde,subtle,zeroize + + careful: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo install cargo-careful + - run: cargo careful test --all-features + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: 1.85.0 + components: clippy + - run: cargo clippy --all-targets --all-features -- -D warnings + + doc: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - run: cargo doc --all-features + + miri: + runs-on: ubuntu-latest + env: + MIRIFLAGS: "-Zmiri-symbolic-alignment-check -Zmiri-strict-provenance" + strategy: + matrix: + target: + - x86_64-unknown-linux-gnu + - s390x-unknown-linux-gnu + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@nightly + - run: rustup component add miri && cargo miri setup + - run: cargo miri test --target ${{ matrix.target }} --all-features + + rustfmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + components: rustfmt + - run: cargo fmt --all -- --check + + test: + strategy: + matrix: + toolchain: + - 1.85.0 # MSRV + - stable + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.toolchain }} + - uses: RustCrypto/actions/cargo-hack-install@master + - run: cargo hack test --feature-powerset --optional-deps arbitrary,bytemuck,serde,subtle,zeroize --group-features arbitrary,bytemuck,ctutils,serde,subtle,zeroize + - run: cargo test --all-features --release diff --git a/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml b/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml new file mode 100644 index 0000000..1ac4433 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml @@ -0,0 +1,26 @@ +name: Publish to crates.io +on: + push: + tags: [ 'v*' ] + +jobs: + publish: + runs-on: ubuntu-latest + environment: publish + permissions: + id-token: write + steps: + - uses: actions/checkout@v6 + - uses: rust-lang/crates-io-auth-action@v1 + id: auth + + - name: Check crate version + run: | + CRATE_VERSION=v$(grep -m 1 "^version =" Cargo.toml | cut -d'"' -f2) + echo $CRATE_VERSION ${{ github.ref_name }} + [[ $CRATE_VERSION == ${{ github.ref_name }} ]] + + - name: Publish + env: + CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} + run: cargo publish diff --git a/hqc-kem/hybrid-array-patch/.gitignore b/hqc-kem/hybrid-array-patch/.gitignore new file mode 100644 index 0000000..73fab07 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb diff --git a/hqc-kem/hybrid-array-patch/CHANGELOG.md b/hqc-kem/hybrid-array-patch/CHANGELOG.md new file mode 100644 index 0000000..7a421ae --- /dev/null +++ b/hqc-kem/hybrid-array-patch/CHANGELOG.md @@ -0,0 +1,194 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.4.9 (2026-03-30) +### Added +- Functions for casting from core references ([#181]) + - `Array::cast_from_core`: `const fn` equivalent of `From<[T; N]>` + - `Array::cast_from_core_mut`: `mut` equivalent of the above + - `Array::from_ref`: cast `&T` to `&Array` + - `Array::from_mut`: `mut` equivalent of the above + +[#181]: https://github.com/RustCrypto/hybrid-array/pull/181 + +## 0.4.8 (2026-03-08) +### Added +- `ctutils` support ([#177]) + +[#177]: https://github.com/RustCrypto/hybrid-array/pull/177 + +## 0.4.7 (2026-02-03) +### Added +- `Flatten` and `Unflatten` traits ([#170]) +- `Array::slice_as_(mut_)array` ([#171]) +- `SliceExt` ([#172]) + +[#170]: https://github.com/RustCrypto/hybrid-array/pull/170 +[#171]: https://github.com/RustCrypto/hybrid-array/pull/171 +[#172]: https://github.com/RustCrypto/hybrid-array/pull/172 + +## 0.4.6 (2026-01-22) +### Added +- Optional dependency on `zerocopy` with impls for `Array` ([#162]) +- `Debug` bound to `ArraySize` ([#165]) + +[#162]: https://github.com/RustCrypto/hybrid-array/pull/162 +[#165]: https://github.com/RustCrypto/hybrid-array/pull/165 + +## 0.4.5 (2025-09-29) +### Added +- Impl `arbitrary::Arbitrary` for `Array` ([#153]) + +### Changed +- Switch from `doc_auto_cfg` to `doc_cfg` ([#154]) + +[#153]: https://github.com/RustCrypto/hybrid-array/pull/153 +[#154]: https://github.com/RustCrypto/hybrid-array/pull/154 + +## 0.4.4 (2025-09-24) +### Added +- Enable the `subtle/const-generics` feature ([#149]) + +[#149]: https://github.com/RustCrypto/hybrid-array/pull/149 + +## 0.4.3 (2025-09-23) +### Added +- `Array::as_(mut_)ptr` ([#147]) + +### Changed +- Remove bounds on `Array::slice_as_flattened(_mut)`; make `const fn` ([#144]) +- Make `Array::as_(mut_)slice` a `const fn` ([#147]) +- Make `Array::::as_flattened(_mut)` a `const fn` ([#147]) + +[#144]: https://github.com/RustCrypto/hybrid-array/pull/144 +[#147]: https://github.com/RustCrypto/hybrid-array/pull/147 + +## 0.4.2 (2025-09-21) [YANKED] +### Added +- `Array::slice_as_flattened(_mut)` ([#142]) + +[#142]: https://github.com/RustCrypto/hybrid-array/pull/142 + +## 0.4.1 (2025-09-10) +### Changed +- Make slice conversions `const fn` ([#140]) + +[#140]: https://github.com/RustCrypto/hybrid-array/pull/140 + +## 0.4.0 (2025-09-01) +### Added +- `ArraySize` impls for `U536` and `U568` ([#128]) +- `AsArrayRef`/`AsArrayMut` traits with impls on `[T; N]` and `Array` ([#135]) +- `alloc` feature with `Box`/`Vec` conversions to/from `Array` ([#136], [#138]) + +### Removed +- `AsRef`/`AsMut` impls on `[T; N]` ([#133]) + +[#128]: https://github.com/RustCrypto/hybrid-array/pull/128 +[#133]: https://github.com/RustCrypto/hybrid-array/pull/133 +[#135]: https://github.com/RustCrypto/hybrid-array/pull/135 +[#136]: https://github.com/RustCrypto/hybrid-array/pull/136 +[#138]: https://github.com/RustCrypto/hybrid-array/pull/138 + +## 0.3.1 (2025-03-30) +### Added +- `subtle` feature ([#126]) + +[#126]: https://github.com/RustCrypto/hybrid-array/pull/126 + +## 0.3.0 (2025-02-21) +### Changed +- Bump edition to 2024; MSRV 1.85 ([#116]) + +### Removed +- `U3293` as an unused ML-DSA size ([#117]) + +[#116]: https://github.com/RustCrypto/hybrid-array/pull/116 +[#117]: https://github.com/RustCrypto/hybrid-array/pull/117 + +## 0.2.3 (2024-12-07) +### Added +- Additional ML-DSA sizes ([#108]) + +[#108]: https://github.com/RustCrypto/hybrid-array/pull/108 + +## 0.2.2 (2024-11-11) +### Added +- FrodoKEM sizes ([#104]) + +[#104]: https://github.com/RustCrypto/hybrid-array/pull/104 + +## 0.2.1 (2024-10-20) +### Fixed +- MSRV badge ([9d47c798](https://github.com/RustCrypto/hybrid-array/commit/9d47c79861057b3a04bb19cb2dfaa1f75cbf9ddd)) + +## 0.2.0 (2024-10-19) +### Added +- Reference conversion support from core arrays ([utils#904]) +- Impl `Default` for `Array` ([utils#905]) +- `Deref`/`DerefMut` impls for `Array` ([utils#908], [utils#913]) +- Impl `From>` for `[T; N]` ([utils#945]) +- Impl `IntoIterator` for all `ArraySize`s ([utils#956]) +- Impl `IntoIterator` for references to all `ArraySize`s ([utils#957]) +- Concat and split methods ([utils#958]) +- `slice_as_chunks(_mut)` methods ([utils#974]) +- Impl `Zeroize`/`ZeroizeOnDrop` for `Array` ([utils#984]) +- `AssocArraySize` trait ([utils#1006], [#40]) +- `sizes` submodule ([utils#1014], [#68]) +- `ArrayN` type alias ([utils#1017]) +- Impl `FromIterator` ([utils#1039]) +- `Array::try_from_iter` ([#4]) +- Helper functions for `Array, U>` ([#8]) +- `Send` and `Sync` impls for `Array` ([#15]) +- `Array::map` ([#61]) +- Support all array sizes up to `U512` ([#67]) +- `Array>::as_flattened{_mut}()` ([#86]) +- `serde` support ([#88]) +- `bytemuck` support ([#99]) + +### Changed +- Use GATs for `ArraySize` ([utils#893]) +- Make `ArraySize` an `unsafe trait` ([utils#914]) +- MSRV 1.81 ([#85]) + +### Removed +- `ByteArray` type alias ([utils#995]) +- `ArrayOps` trait ([#30]) +- `std` feature ([#85]) + +[utils#893]: https://github.com/RustCrypto/utils/pull/893 +[utils#904]: https://github.com/RustCrypto/utils/pull/904 +[utils#905]: https://github.com/RustCrypto/utils/pull/905 +[utils#908]: https://github.com/RustCrypto/utils/pull/908 +[utils#913]: https://github.com/RustCrypto/utils/pull/913 +[utils#914]: https://github.com/RustCrypto/utils/pull/914 +[utils#945]: https://github.com/RustCrypto/utils/pull/945 +[utils#956]: https://github.com/RustCrypto/utils/pull/956 +[utils#957]: https://github.com/RustCrypto/utils/pull/957 +[utils#958]: https://github.com/RustCrypto/utils/pull/958 +[utils#974]: https://github.com/RustCrypto/utils/pull/974 +[utils#984]: https://github.com/RustCrypto/utils/pull/984 +[utils#995]: https://github.com/RustCrypto/utils/pull/995 +[utils#1006]: https://github.com/RustCrypto/utils/pull/1006 +[utils#1014]: https://github.com/RustCrypto/utils/pull/1014 +[utils#1017]: https://github.com/RustCrypto/utils/pull/1017 +[utils#1039]: https://github.com/RustCrypto/utils/pull/1039 +[#4]: https://github.com/RustCrypto/hybrid-array/pull/4 +[#8]: https://github.com/RustCrypto/hybrid-array/pull/8 +[#15]: https://github.com/RustCrypto/hybrid-array/pull/15 +[#30]: https://github.com/RustCrypto/hybrid-array/pull/30 +[#40]: https://github.com/RustCrypto/hybrid-array/pull/40 +[#61]: https://github.com/RustCrypto/hybrid-array/pull/61 +[#67]: https://github.com/RustCrypto/hybrid-array/pull/67 +[#68]: https://github.com/RustCrypto/hybrid-array/pull/68 +[#85]: https://github.com/RustCrypto/hybrid-array/pull/85 +[#86]: https://github.com/RustCrypto/hybrid-array/pull/86 +[#88]: https://github.com/RustCrypto/hybrid-array/pull/88 +[#99]: https://github.com/RustCrypto/hybrid-array/pull/99 + +## 0.1.0 (2022-05-07) +- Initial release diff --git a/hqc-kem/hybrid-array-patch/Cargo.lock b/hqc-kem/hybrid-array-patch/Cargo.lock new file mode 100644 index 0000000..ee8d9c4 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/Cargo.lock @@ -0,0 +1,146 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "cmov" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" + +[[package]] +name = "ctutils" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4" +dependencies = [ + "cmov", +] + +[[package]] +name = "hybrid-array" +version = "0.4.9" +dependencies = [ + "arbitrary", + "bytemuck", + "ctutils", + "serde", + "subtle", + "typenum", + "zerocopy", + "zeroize", +] + +[[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.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[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 = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "zerocopy" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/hqc-kem/hybrid-array-patch/Cargo.toml b/hqc-kem/hybrid-array-patch/Cargo.toml new file mode 100644 index 0000000..c04416a --- /dev/null +++ b/hqc-kem/hybrid-array-patch/Cargo.toml @@ -0,0 +1,138 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +rust-version = "1.85" +name = "hybrid-array" +version = "0.4.9" +authors = ["RustCrypto Developers"] +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = """ +Hybrid typenum-based and const generic array types designed to provide the +flexibility of typenum-based expressions while also allowing interoperability +and a transition path to const generics +""" +documentation = "https://docs.rs/hybrid-array" +readme = "README.md" +keywords = ["generic-array"] +categories = [ + "no-std", + "data-structures", +] +license = "MIT OR Apache-2.0" +repository = "https://github.com/RustCrypto/hybrid-array" + +[package.metadata.docs.rs] +all-features = true + +[features] +alloc = [] +extra-sizes = [] + +[lib] +name = "hybrid_array" +path = "src/lib.rs" + +[[test]] +name = "ctutils" +path = "tests/ctutils.rs" + +[[test]] +name = "mod" +path = "tests/mod.rs" + +[[test]] +name = "subtle" +path = "tests/subtle.rs" + +[dependencies.arbitrary] +version = "1" +optional = true + +[dependencies.bytemuck] +version = "1" +optional = true +default-features = false + +[dependencies.ctutils] +version = "0.4" +optional = true + +[dependencies.serde] +version = "1" +optional = true +default-features = false + +[dependencies.subtle] +version = "2" +features = ["const-generics"] +optional = true +default-features = false + +[dependencies.typenum] +version = "1.17" +features = ["const-generics"] + +[dependencies.zerocopy] +version = "0.8" +features = ["derive"] +optional = true + +[dependencies.zeroize] +version = "1.8" +optional = true +default-features = false + +[lints.clippy] +arithmetic_side_effects = "warn" +borrow_as_ptr = "warn" +cast_lossless = "warn" +cast_possible_truncation = "warn" +cast_possible_wrap = "warn" +cast_precision_loss = "warn" +cast_sign_loss = "warn" +checked_conversions = "warn" +doc_markdown = "warn" +from_iter_instead_of_collect = "warn" +implicit_saturating_sub = "warn" +manual_assert = "warn" +map_unwrap_or = "warn" +missing_errors_doc = "warn" +missing_panics_doc = "warn" +mod_module_files = "warn" +must_use_candidate = "warn" +panic = "warn" +panic_in_result_fn = "warn" +ptr_as_ptr = "warn" +redundant_closure_for_method_calls = "warn" +ref_as_ptr = "warn" +return_self_not_must_use = "warn" +semicolon_if_nothing_returned = "warn" +std_instead_of_alloc = "warn" +std_instead_of_core = "warn" +trivially_copy_pass_by_ref = "warn" +unwrap_in_result = "warn" +unwrap_used = "warn" + +[lints.rust] +missing_copy_implementations = "warn" +missing_debug_implementations = "warn" +missing_docs = "warn" +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_lifetimes = "warn" +unused_qualifications = "warn" diff --git a/hqc-kem/hybrid-array-patch/Cargo.toml.orig b/hqc-kem/hybrid-array-patch/Cargo.toml.orig new file mode 100644 index 0000000..186674c --- /dev/null +++ b/hqc-kem/hybrid-array-patch/Cargo.toml.orig @@ -0,0 +1,77 @@ +[package] +name = "hybrid-array" +version = "0.4.9" +description = """ +Hybrid typenum-based and const generic array types designed to provide the +flexibility of typenum-based expressions while also allowing interoperability +and a transition path to const generics +""" +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +documentation = "https://docs.rs/hybrid-array" +repository = "https://github.com/RustCrypto/hybrid-array" +categories = ["no-std", "data-structures"] +keywords = ["generic-array"] +readme = "README.md" +edition = "2024" +rust-version = "1.85" + +[dependencies] +typenum = { version = "1.17", features = ["const-generics"] } + +# optional dependencies +arbitrary = { version = "1", optional = true } +bytemuck = { version = "1", optional = true, default-features = false } +ctutils = { version = "0.4", optional = true } +serde = { version = "1", optional = true, default-features = false } +subtle = { version = "2", optional = true, default-features = false, features = ["const-generics"] } +zeroize = { version = "1.8", optional = true, default-features = false } +zerocopy = { version = "0.8", optional = true, features = ["derive"] } + +[features] +alloc = [] +extra-sizes = [] + +[lints.clippy] +arithmetic_side_effects = "warn" +borrow_as_ptr = "warn" +cast_lossless = "warn" +cast_possible_truncation = "warn" +cast_possible_wrap = "warn" +cast_precision_loss = "warn" +cast_sign_loss = "warn" +checked_conversions = "warn" +doc_markdown = "warn" +from_iter_instead_of_collect = "warn" +manual_assert = "warn" +map_unwrap_or = "warn" +missing_errors_doc = "warn" +missing_panics_doc = "warn" +mod_module_files = "warn" +must_use_candidate = "warn" +implicit_saturating_sub = "warn" +panic = "warn" +panic_in_result_fn = "warn" +ptr_as_ptr = "warn" +redundant_closure_for_method_calls = "warn" +ref_as_ptr = "warn" +return_self_not_must_use = "warn" +semicolon_if_nothing_returned = "warn" +trivially_copy_pass_by_ref = "warn" +std_instead_of_alloc = "warn" +std_instead_of_core = "warn" +# undocumented_unsafe_blocks = "warn" TODO +unwrap_in_result = "warn" +unwrap_used = "warn" + +[lints.rust] +missing_copy_implementations = "warn" +missing_debug_implementations = "warn" +missing_docs = "warn" +trivial_casts = "warn" +trivial_numeric_casts = "warn" +unused_lifetimes = "warn" +unused_qualifications = "warn" + +[package.metadata.docs.rs] +all-features = true diff --git a/hqc-kem/hybrid-array-patch/LICENSE-APACHE b/hqc-kem/hybrid-array-patch/LICENSE-APACHE new file mode 100644 index 0000000..78173fa --- /dev/null +++ b/hqc-kem/hybrid-array-patch/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/hqc-kem/hybrid-array-patch/LICENSE-MIT b/hqc-kem/hybrid-array-patch/LICENSE-MIT new file mode 100644 index 0000000..5b37ba5 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/LICENSE-MIT @@ -0,0 +1,25 @@ +Copyright (c) 2022-2026 The RustCrypto Project Developers + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/hqc-kem/hybrid-array-patch/README.md b/hqc-kem/hybrid-array-patch/README.md new file mode 100644 index 0000000..c65e596 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/README.md @@ -0,0 +1,68 @@ +# [RustCrypto]: Hybrid Const Generic / Typenum Arrays + +[![crate][crate-image]][crate-link] +[![Docs][docs-image]][docs-link] +[![Build Status][build-image]][build-link] +![Apache2/MIT licensed][license-image] +![Rust Version][rustc-image] +[![Project Chat][chat-image]][chat-link] + +Hybrid array type combining const generics with the expressiveness of +[`typenum`]-based constraints, providing an alternative to [`generic-array`] +and a incremental transition path to const generics. + +## About + +This crate uses `typenum` to enable the following features which aren't yet +possible with the stable implementation of const generics: + +- [#60551: Associated constants in traits can not be used in const generics][rust-issue-60551] +- [#76560: Complex generic constants: `feature(generic_const_exprs)`][rust-issue-76560] + +Internally the crate is built on const generics and provides traits which make +it possible to convert between const generic types and `typenum` types. + +## Minimum Supported Rust Version (MSRV) Policy + +MSRV increases are not considered breaking changes and can happen in patch +releases. + +The crate MSRV accounts for all supported targets and crate feature +combinations, excluding explicitly unstable features. + +## License + +Licensed under either of: + +- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) +- [MIT license](http://opensource.org/licenses/MIT) + +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall be +dual licensed as above, without any additional terms or conditions. + +[//]: # (badges) + +[crate-image]: https://img.shields.io/crates/v/hybrid-array?logo=rust +[crate-link]: https://crates.io/crates/hybrid-array +[docs-image]: https://docs.rs/hybrid-array/badge.svg +[docs-link]: https://docs.rs/hybrid-array/ +[build-image]: https://github.com/RustCrypto/hybrid-array/actions/workflows/hybrid-array.yml/badge.svg?branch=master +[build-link]: https://github.com/RustCrypto/hybrid-array/actions/workflows/hybrid-array.yml?query=branch:master +[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg +[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg +[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg +[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils + +[//]: # (links) + +[RustCrypto]: https://github.com/rustcrypto +[RustCrypto/utils#378]: https://github.com/RustCrypto/utils/issues/378 +[`typenum`]: https://github.com/paholg/typenum +[`generic-array`]: https://github.com/fizyk20/generic-array +[rust-issue-60551]: https://github.com/rust-lang/rust/issues/60551 +[rust-issue-76560]: https://github.com/rust-lang/rust/issues/76560 diff --git a/hqc-kem/hybrid-array-patch/src/flatten.rs b/hqc-kem/hybrid-array-patch/src/flatten.rs new file mode 100644 index 0000000..f3159b4 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/flatten.rs @@ -0,0 +1,137 @@ +use crate::{ + Array, ArraySize, + typenum::{Prod, Quot, U0, Unsigned}, +}; +use core::{ + mem::ManuallyDrop, + ops::{Div, Mul, Rem}, + ptr, +}; + +/// Defines a sequence of sequences that can be merged into a bigger overall sequence. +pub trait Flatten { + /// Size of the output array. + type OutputSize: ArraySize; + + /// Flatten array. + fn flatten(self) -> Array; +} + +impl Flatten> for Array, N> +where + N: ArraySize, + M: ArraySize + Mul, + Prod: ArraySize, +{ + type OutputSize = Prod; + + // SAFETY: this is the reverse transmute between [T; K*N] and [[T; K], M], which is guaranteed + // to be safe by the Rust memory layout of these types. + fn flatten(self) -> Array { + let whole = ManuallyDrop::new(self); + unsafe { ptr::read(whole.as_ptr().cast()) } + } +} + +/// Defines a sequence that can be split into a sequence of smaller sequences of uniform size. +pub trait Unflatten +where + M: ArraySize, +{ + /// Part of the array we're decomposing into. + type Part; + + /// Unflatten array into `Self::Part` chunks. + fn unflatten(self) -> Array; +} + +impl Unflatten for Array +where + N: ArraySize + Div + Rem, + M: ArraySize, + Quot: ArraySize, +{ + type Part = Array>; + + // SAFETY: this is doing the same thing as what is done in `Array::split`. + // Basically, this is doing transmute between [T; K*N] and [[T; K], M], which is guaranteed to + // be safe by the Rust memory layout of these types. + fn unflatten(self) -> Array { + let part_size = Quot::::USIZE; + let whole = ManuallyDrop::new(self); + Array::from_fn(|i| unsafe { + let offset = i.checked_mul(part_size).expect("overflow"); + ptr::read(whole.as_ptr().add(offset).cast()) + }) + } +} + +impl<'a, T, N, M> Unflatten for &'a Array +where + N: ArraySize + Div + Rem, + M: ArraySize, + Quot: ArraySize, +{ + type Part = &'a Array>; + + // SAFETY: this is doing the same thing as what is done in `Array::split`. + // Basically, this is doing transmute between [T; K*N] and [[T; K], M], which is guaranteed to + // be safe by the Rust memory layout of these types. + fn unflatten(self) -> Array { + let part_size = Quot::::USIZE; + let mut ptr: *const T = self.as_ptr(); + Array::from_fn(|_i| unsafe { + let part = &*(ptr.cast()); + ptr = ptr.add(part_size); + part + }) + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::{ + Array, + sizes::{U2, U5}, + }; + + #[test] + fn flatten() { + let flat: Array = Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); + let unflat2: Array, _> = Array([ + Array([1, 2]), + Array([3, 4]), + Array([5, 6]), + Array([7, 8]), + Array([9, 10]), + ]); + let unflat5: Array, _> = + Array([Array([1, 2, 3, 4, 5]), Array([6, 7, 8, 9, 10])]); + + // Flatten + let actual = unflat2.flatten(); + assert_eq!(flat, actual); + + let actual = unflat5.flatten(); + assert_eq!(flat, actual); + + // Unflatten + let actual: Array, U5> = flat.unflatten(); + assert_eq!(unflat2, actual); + + let actual: Array, U2> = flat.unflatten(); + assert_eq!(unflat5, actual); + + // Unflatten on references + let actual: Array<&Array, U5> = (&flat).unflatten(); + for (i, part) in actual.iter().enumerate() { + assert_eq!(&unflat2[i], *part); + } + + let actual: Array<&Array, U2> = (&flat).unflatten(); + for (i, part) in actual.iter().enumerate() { + assert_eq!(&unflat5[i], *part); + } + } +} diff --git a/hqc-kem/hybrid-array-patch/src/from_fn.rs b/hqc-kem/hybrid-array-patch/src/from_fn.rs new file mode 100644 index 0000000..8a507c6 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/from_fn.rs @@ -0,0 +1,106 @@ +//! Support for constructing arrays using a provided generator function. + +use crate::{Array, ArraySize}; +use core::{ + convert::Infallible, + mem::{self, MaybeUninit}, + ptr, +}; + +impl Array +where + U: ArraySize, +{ + /// Create array where each array element `T` is returned by the `f` call. + #[inline] + pub fn from_fn(mut f: impl FnMut(usize) -> T) -> Self { + let Ok(ret) = Self::try_from_fn::(|n| Ok(f(n))); + ret + } + + /// Create array fallibly where each array element `T` is returned by the `f` call, or return + /// an error if any are encountered. + /// + /// # Errors + /// + /// Propagates the `E` type returned from the provided `F` in the event of error. + pub fn try_from_fn(f: impl FnMut(usize) -> Result) -> Result { + let mut array = Array::, U>::uninit(); + try_from_fn_erased(array.0.as_mut(), f)?; + + // SAFETY: if we got here, every element of the array was initialized + Ok(unsafe { array.assume_init() }) + } +} + +/// Fills a `MaybeUninit` slice using the given fallible generator function. +/// +/// Using a slice avoids monomorphizing for each array size. +#[inline] +fn try_from_fn_erased(buffer: &mut [MaybeUninit], mut f: F) -> Result<(), E> +where + F: FnMut(usize) -> Result, +{ + let mut guard = Guard { + array_mut: buffer, + initialized: 0, + }; + + while guard.initialized < guard.array_mut.len() { + let item = f(guard.initialized)?; + + // SAFETY: the loop's condition ensures we won't push too many items + unsafe { guard.push_unchecked(item) }; + } + + mem::forget(guard); + Ok(()) +} + +/// Drop guard which tracks the total number of initialized items, and handles dropping them in +/// the event a panic occurs. +/// +/// Use `mem::forget` when the array has been fully constructed. +struct Guard<'a, T> { + /// Array being constructed. + array_mut: &'a mut [MaybeUninit], + + /// Number of items in the array which have been initialized. + initialized: usize, +} + +impl Guard<'_, T> { + /// Push an item onto the guard, writing to its `MaybeUninit` slot and incrementing the + /// counter of the number of initialized items. + /// + /// # Safety + /// + /// This can only be called n-times for as many elements are in the slice. + #[inline] + pub unsafe fn push_unchecked(&mut self, item: T) { + // SAFETY: the `initialized` counter tracks the number of initialized items, so as long as + // this is called the correct number of times for the array size writes will always be + // in-bounds and to an uninitialized slot in the array. + unsafe { + self.array_mut + .get_unchecked_mut(self.initialized) + .write(item); + self.initialized = self.initialized.saturating_add(1); + } + } +} + +impl Drop for Guard<'_, T> { + fn drop(&mut self) { + debug_assert!(self.initialized <= self.array_mut.len()); + + // SAFETY: the loop only iterates over initialized items + unsafe { + let p: *mut T = self.array_mut.as_mut_ptr().cast(); + + for i in 0..self.initialized { + ptr::drop_in_place(p.add(i)); + } + } + } +} diff --git a/hqc-kem/hybrid-array-patch/src/iter.rs b/hqc-kem/hybrid-array-patch/src/iter.rs new file mode 100644 index 0000000..281bab1 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/iter.rs @@ -0,0 +1,107 @@ +//! Support for constructing arrays using a provided iterator function and other iterator-related +//! functionality. + +use crate::{Array, ArraySize}; +use core::{ + fmt, + slice::{Iter, IterMut}, +}; + +/// Couldn't construct an array from an iterator because the number of items in the iterator +/// didn't match the array size. +#[derive(Clone, Copy, Debug)] +pub struct TryFromIteratorError; + +impl fmt::Display for TryFromIteratorError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str("iterator did not contain the correct number of items for the array size") + } +} + +impl core::error::Error for TryFromIteratorError {} + +impl Array +where + U: ArraySize, +{ + /// Construct an array from the given iterator, returning [`TryFromIteratorError`] in the event + /// that the number of items in the iterator does not match the array size. + /// + /// # Errors + /// + /// Returns [`TryFromIteratorError`] in the event the iterator does not return a number of + /// items which is exactly equal to the array size. + pub fn try_from_iter>(iter: I) -> Result { + let mut iter = iter.into_iter(); + let ret = Self::try_from_fn(|_| iter.next().ok_or(TryFromIteratorError))?; + + match iter.next() { + None => Ok(ret), + Some(_) => Err(TryFromIteratorError), + } + } +} + +impl FromIterator for Array +where + U: ArraySize, +{ + fn from_iter>(iter: I) -> Self { + let mut iter = iter.into_iter(); + let ret = Self::from_fn(|_| { + iter.next() + .expect("iterator should have enough items to fill array") + }); + + assert!( + iter.next().is_none(), + "too many items in iterator to fit in array" + ); + + ret + } +} + +impl IntoIterator for Array +where + U: ArraySize, +{ + type Item = T; + type IntoIter = as IntoIterator>::IntoIter; + + /// Creates a consuming iterator, that is, one that moves each value out of the array (from + /// start to end). + /// + /// The array cannot be used after calling this unless `T` implements `Copy`, so the whole + /// array is copied. + #[inline] + fn into_iter(self) -> Self::IntoIter { + self.0.into_iter() + } +} + +impl<'a, T, U> IntoIterator for &'a Array +where + U: ArraySize, +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + #[inline] + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +impl<'a, T, U> IntoIterator for &'a mut Array +where + U: ArraySize, +{ + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + #[inline] + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} diff --git a/hqc-kem/hybrid-array-patch/src/lib.rs b/hqc-kem/hybrid-array-patch/src/lib.rs new file mode 100644 index 0000000..afa3a03 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/lib.rs @@ -0,0 +1,1217 @@ +#![no_std] +#![cfg_attr(docsrs, feature(doc_cfg))] +#![doc = include_str!("../README.md")] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" +)] + +//! ## Features +//! +//! This crate exposes the following feature flags. The default is NO features. +//! +//! - `bytemuck`: impls the `Pod` and `Zeroable` traits +//! - `serde`: impls the `Deserialize` and `Serialize` traits for `Array` +//! - `zeroize`: impls [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) for `Array` +//! +//! ## Usage +//! +//! The two core types in this crate are as follows: +//! +//! - [`Array`]: wrapper for `[T; N]` where `U` is an [`ArraySize`] provided by [`typenum`] +//! whose associated [`ArraySize::ArrayType`] determines the inner array size. +//! - [`ArrayN`]: type alias for [`Array`] which is const generic around `const N: usize`. +//! This provides a linkage between const generics and [`typenum`]. +//! +//! The [`Array`] type has an inner `pub [T; N]` field, which means writing a literal can be +//! expressed as follows: +//! +//! ``` +//! use hybrid_array::{Array, sizes::U4}; +//! +//! let arr: Array = Array([1, 2, 3, 4]); +//! ``` +//! +//! ### About [`typenum`] +//! +//! The [`typenum`] crate provides a type-level implementation of numbers and arithmetic operations. +//! +//! While [`typenum`] can be used to express arbitrary integers using the type system, the +//! `hybrid-array` crate is limited to the array sizes in the [`sizes`] module, which have +//! names like [`U0`][`sizes::U0`], [`U1`][`sizes::U1`], [`U2`][`sizes::U2`], [`U3`][`sizes::U3`], +//! etc. All supported sizes will have an impl of [`ArraySize`], which is the trait providing +//! linkage between [`typenum`]-based types and core arrays / const generics. +//! +//! [`ArraySize`] bounds on the [`typenum::Unsigned`] trait, which can be used to obtain integer +//! sizes of arrays via associated constants. For example, to obtain the size of an `ArraySize` as +//! a `usize`, use the associated [`typenum::Unsigned::USIZE`] constant. +//! +//! ### [`AsArrayRef`] and [`AsArrayMut`] traits +//! +//! These traits simplify obtaining references to [`Array`] and are impl'd for both [`Array`] +//! and `[T; N]`. They're analogous to traits like [`AsRef`] and [`AsMut`]. +//! +//! They make it possible to write code which uses `[T; N]` or `&[T; N]` in the external facing +//! API which can obtain references to `&Array` and call other functions which accept such +//! references, without the caller having to use `Array` in their code and while still supporting +//! generic sizes. +//! +//! For more information and a code example, see [`AsArrayRef`]. +//! +//! ## Relationship with `generic-array` +//! +//! `hybrid-array` is directly inspired by the [`generic-array`] crate. +//! +//! However, where `generic-array` predates const generics and uses a core which is built +//! on `unsafe` code, `hybrid-array`'s core implementation is built on safe code and const +//! generic implementations. This allows the inner `[T; N]` field of an `Array` to be `pub` as +//! noted above, and in general for the implementation to be significantly simpler, easier-to-audit, +//! and with significantly less use of `unsafe`. +//! +//! The only places `hybrid-array` uses unsafe are where it is absolutely necessary, primarily +//! for reference conversions between `Array` and `[T; N]`, and also to provide features +//! which are not yet stable in `core`/`std`, such as [`Array::try_from_fn`]. +//! +//! [`generic-array`]: https://docs.rs/generic-array +//! +//! ## Migrating from `generic-array` +//! +//! *NOTE: this guide assumes a migration from `generic-array` v0.14* +//! +//! `hybrid-array` has been designed to largely be a drop-in replacement for +//! `generic-array`, albeit with a public inner array type and significantly less +//! `unsafe` code. +//! +//! The bulk of the migration work can be accomplished by making the following find/replace-style +//! substitutions in your `.rs` files: +//! +//! - Replace `generic_array` with `hybrid_array` +//! - Replace `GenericArray` with `Array` +//! - Replace `ArrayLength` with `ArraySize` +//! - Replace usages of the `Concat` and `Split` traits with [`Array::concat`] and [`Array::split`] +//! - Replace `>::ArrayType` with `::ArrayType` +//! - Replace usages of the `arr![N; A, B, C]` macro with `Array([A, B, C])` +//! +//! If you have any questions, please +//! [start a discussion](https://github.com/RustCrypto/hybrid-array/discussions). + +#[cfg(feature = "alloc")] +extern crate alloc; + +pub mod sizes; + +mod flatten; +mod from_fn; +mod iter; +mod traits; + +#[cfg(feature = "serde")] +mod serde; + +pub use crate::{ + flatten::{Flatten, Unflatten}, + iter::TryFromIteratorError, + traits::*, +}; +pub use typenum; + +use core::{ + array::TryFromSliceError, + borrow::{Borrow, BorrowMut}, + cmp::Ordering, + fmt::{self, Debug}, + hash::{Hash, Hasher}, + mem::{self, ManuallyDrop, MaybeUninit}, + ops::{Add, Deref, DerefMut, Index, IndexMut, Sub}, + ptr, + slice::{self, Iter, IterMut}, +}; +use typenum::{Diff, Sum, U1}; + +#[cfg(feature = "arbitrary")] +use arbitrary::Arbitrary; + +#[cfg(feature = "bytemuck")] +use bytemuck::{Pod, Zeroable}; + +#[cfg(feature = "zeroize")] +use zeroize::{Zeroize, ZeroizeOnDrop}; + +#[cfg(feature = "zerocopy")] +use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; + +/// Type alias for [`Array`] which is const generic around a size `N`, ala `[T; N]`. +pub type ArrayN = Array::Size>; + +/// [`Array`] is a newtype for an inner `[T; N]` array where `N` is determined by a generic +/// [`ArraySize`] parameter, which is a marker trait for a numeric value determined by ZSTs that +/// impl the [`typenum::Unsigned`] trait. +/// +/// The inner `[T; N]` field is `pub` which means it's possible to write [`Array`] literals like: +/// +/// [`Array`] is defined as `repr(transparent)`, meaning it can be used anywhere an appropriately +/// sized `[T; N]` type is used in unsafe code / FFI. +/// +/// ``` +/// use hybrid_array::{Array, sizes::U3}; +/// +/// let arr: Array = Array([1, 2, 3]); +/// ``` +#[cfg_attr( + feature = "zerocopy", + derive(IntoBytes, FromBytes, Immutable, Unaligned, KnownLayout) +)] +#[repr(transparent)] +pub struct Array(pub U::ArrayType); + +type SplitResult = (Array, Array>); +type SplitRefResult<'a, T, U, N> = (&'a Array, &'a Array>); +type SplitRefMutResult<'a, T, U, N> = (&'a mut Array, &'a mut Array>); + +impl Array +where + U: ArraySize, +{ + /// Returns a slice containing the entire array. Equivalent to `&s[..]`. + #[inline] + pub const fn as_slice(&self) -> &[T] { + // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` + // newtype for `[T; N]`. + unsafe { slice::from_raw_parts(self.as_ptr(), U::USIZE) } + } + + /// Returns a mutable slice containing the entire array. Equivalent to `&mut s[..]`. + #[inline] + pub const fn as_mut_slice(&mut self) -> &mut [T] { + // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` + // newtype for `[T; N]`. + unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), U::USIZE) } + } + + /// Returns a pointer to the start of the array. + pub const fn as_ptr(&self) -> *const T { + ptr::from_ref::(self).cast::() + } + + /// Returns a mutable pointer to the start of the array. + pub const fn as_mut_ptr(&mut self) -> *mut T { + ptr::from_mut::(self).cast::() + } + + /// Returns an iterator over the array. + #[inline] + pub fn iter(&self) -> Iter<'_, T> { + self.as_slice().iter() + } + + /// Returns an iterator that allows modifying each value. + #[inline] + pub fn iter_mut(&mut self) -> IterMut<'_, T> { + self.as_mut().iter_mut() + } + + /// Returns an array of the same size as `self`, with function `f` applied to each element in + /// order. + pub fn map(self, f: F) -> Array + where + F: FnMut(T) -> O, + { + self.into_iter().map(f).collect() + } + + /// Concatenates `self` with `other`. + #[inline] + pub fn concat(self, other: Array) -> Array> + where + N: ArraySize, + U: Add, + Sum: ArraySize, + { + let mut c = Array::uninit(); + let (left, right) = c.split_at_mut(self.len()); + for (val, dst) in self.into_iter().zip(left) { + dst.write(val); + } + for (val, dst) in other.into_iter().zip(right) { + dst.write(val); + } + // SAFETY: We wrote to every element of `c`. + unsafe { c.assume_init() } + } + + /// Splits `self` at index `N` in two arrays. + /// + /// New arrays hold the original memory from `self`. + #[inline] + pub fn split(self) -> SplitResult + where + U: Sub, + N: ArraySize, + Diff: ArraySize, + { + unsafe { + let array = ManuallyDrop::new(self); + let head = ptr::read(array.as_ptr().cast()); + let tail = ptr::read(array.as_ptr().add(N::USIZE).cast()); + (head, tail) + } + } + + /// Splits `&self` at index `N` in two array references. + #[inline] + pub fn split_ref(&self) -> SplitRefResult<'_, T, U, N> + where + U: Sub, + N: ArraySize, + Diff: ArraySize, + { + unsafe { + let array_ptr = self.as_ptr(); + let head = &*array_ptr.cast(); + let tail = &*array_ptr.add(N::USIZE).cast(); + (head, tail) + } + } + + /// Splits `&mut self` at index `N` in two mutable array references. + #[inline] + pub fn split_ref_mut(&mut self) -> SplitRefMutResult<'_, T, U, N> + where + U: Sub, + N: ArraySize, + Diff: ArraySize, + { + unsafe { + let array_ptr = self.as_mut_ptr(); + let head = &mut *array_ptr.cast(); + let tail = &mut *array_ptr.add(N::USIZE).cast(); + (head, tail) + } + } + + /// Get a reference to an array from a slice, if the slice is exactly the size of the array. + /// + /// Returns `None` if the slice's length is not exactly equal to the array size. + #[inline] + #[must_use] + pub const fn slice_as_array(slice: &[T]) -> Option<&Self> { + if slice.len() == U::USIZE { + // SAFETY: `Self` is ensured to be layout-identical to `[T; U::USIZE]`, and immediately + // above we validated that `slice` is also layout-identical to `[T; U::USIZE]`, + // therefore the cast is valid. + unsafe { Some(&*slice.as_ptr().cast()) } + } else { + None + } + } + + /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the + /// array. + /// + /// Returns `None` if the slice's length is not exactly equal to the array size. + #[inline] + #[must_use] + pub const fn slice_as_mut_array(slice: &mut [T]) -> Option<&mut Self> { + if slice.len() == U::USIZE { + // SAFETY: `Self` is ensured to be layout-identical to `[T; U::USIZE]`, and immediately + // above we validated that `slice` is also layout-identical to `[T; U::USIZE]`, + // therefore the cast is valid. + unsafe { Some(&mut *slice.as_mut_ptr().cast()) } + } else { + None + } + } + + /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning + /// of the slice, and a remainder slice with length strictly less than `U`. + /// + /// # Panics + /// Panics if `U` is 0. + #[allow(clippy::arithmetic_side_effects)] + #[inline] + pub const fn slice_as_chunks(buf: &[T]) -> (&[Self], &[T]) { + assert!(U::USIZE != 0, "chunk size must be non-zero"); + // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus + // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, + // thus overflow on multiplication and underflow on substraction are impossible. + let chunks_len = buf.len() / U::USIZE; + let tail_pos = U::USIZE * chunks_len; + let tail_len = buf.len() - tail_pos; + unsafe { + let ptr = buf.as_ptr(); + let chunks = slice::from_raw_parts(ptr.cast(), chunks_len); + let tail = slice::from_raw_parts(ptr.add(tail_pos), tail_len); + (chunks, tail) + } + } + + /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning + /// of the slice, and a remainder slice with length strictly less than `U`. + /// + /// # Panics + /// Panics if `U` is 0. + #[allow(clippy::arithmetic_side_effects)] + #[inline] + pub const fn slice_as_chunks_mut(buf: &mut [T]) -> (&mut [Self], &mut [T]) { + assert!(U::USIZE != 0, "chunk size must be non-zero"); + // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus + // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, + // thus overflow on multiplication and underflow on substraction are impossible. + let chunks_len = buf.len() / U::USIZE; + let tail_pos = U::USIZE * chunks_len; + let tail_len = buf.len() - tail_pos; + unsafe { + let ptr = buf.as_mut_ptr(); + let chunks = slice::from_raw_parts_mut(ptr.cast(), chunks_len); + let tail = slice::from_raw_parts_mut(ptr.add(tail_pos), tail_len); + (chunks, tail) + } + } + + /// Obtain a flattened slice from a slice of array chunks. + /// + /// # Panics + /// - if the length calculation for the flattened slice overflows + #[inline] + pub const fn slice_as_flattened(slice: &[Self]) -> &[T] { + let len = slice + .len() + .checked_mul(U::USIZE) + .expect("slice len overflow"); + + // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` + // newtype for `[T; N]`. + unsafe { slice::from_raw_parts(slice.as_ptr().cast(), len) } + } + + /// Obtain a mutable flattened slice from a mutable slice of array chunks. + /// + /// # Panics + /// - if the length calculation for the flattened slice overflows + #[inline] + pub const fn slice_as_flattened_mut(slice: &mut [Self]) -> &mut [T] { + let len = slice + .len() + .checked_mul(U::USIZE) + .expect("slice len overflow"); + + // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` + // newtype for `[T; N]`. + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), len) } + } +} + +impl Array { + /// Convert a reference to `T` into a reference to an [`Array`] of length [`U1`]. + pub const fn from_ref(r: &T) -> &Self { + Self::cast_from_core(core::array::from_ref(r)) + } + + /// Converts a mutable reference to `T` into a mutable reference to an [`Array`] of + /// length [`U1`]. + pub const fn from_mut(r: &mut T) -> &mut Self { + Self::cast_from_core_mut(core::array::from_mut(r)) + } +} + +impl Array, V> +where + U: ArraySize, + V: ArraySize, +{ + /// Takes a `&Array, >>`, and flattens it to a `&[T]`. + /// + /// # Panics + /// + /// This panics if the length of the resulting slice would overflow a `usize`. + /// + /// This is only possible when flattening a slice of arrays of zero-sized + /// types, and thus tends to be irrelevant in practice. If + /// `size_of::() > 0`, this will never panic. + /// + /// # Examples + /// + /// ``` + /// use hybrid_array::{Array, typenum::{U0, U2, U3, U5, U10}}; + /// + /// let a: Array, U2> = Array([Array([1, 2, 3]), Array([4, 5, 6])]); + /// assert_eq!(a.as_flattened(), &[1, 2, 3, 4, 5, 6]); + /// + /// let b: Array, U3> = Array([Array([1, 2]), Array([3, 4]), Array([5, 6])]); + /// assert_eq!(a.as_flattened(), b.as_flattened()); + /// + /// let c: Array<[usize; 2], U3> = Array([[1, 2], [3, 4], [5, 6]]); + /// assert_eq!(a.as_flattened(), c.as_flattened()); + /// + /// let slice_of_empty_arrays: &Array, U0> = &Array::from_fn(|_| Array([1, 2, 3, 4, 5])); + /// assert!(slice_of_empty_arrays.as_flattened().is_empty()); + /// + /// let empty_slice_of_arrays: &Array, U0> = &Array([]); + /// assert!(empty_slice_of_arrays.as_flattened().is_empty()); + /// ``` + pub const fn as_flattened(&self) -> &[T] { + Array::slice_as_flattened(self.as_slice()) + } + + /// Takes a `&mut Array,M>`, and flattens it to a `&mut [T]`. + /// + /// # Panics + /// + /// This panics if the length of the resulting slice would overflow a `usize`. + /// + /// This is only possible when flattening a slice of arrays of zero-sized + /// types, and thus tends to be irrelevant in practice. If + /// `size_of::() > 0`, this will never panic. + /// + /// # Examples + /// + /// ``` + /// use hybrid_array::{Array, typenum::U3}; + /// + /// fn add_5_to_all(slice: &mut [i32]) { + /// for i in slice { + /// *i += 5; + /// } + /// } + /// + /// let mut array: Array, U3> = Array([Array([1_i32, 2, 3]), Array([4, 5, 6]), Array([7, 8, 9])]); + /// add_5_to_all(array.as_flattened_mut()); + /// assert_eq!(array, Array([Array([6, 7, 8]), Array([9, 10, 11]), Array([12, 13, 14])])); + /// ``` + pub const fn as_flattened_mut(&mut self) -> &mut [T] { + Array::slice_as_flattened_mut(self.as_mut_slice()) + } +} + +// Impls which depend on the inner array type being `[T; N]`. +impl Array +where + U: ArraySize = [T; N]>, +{ + /// Cast a reference to a core array to an [`Array`] reference. + #[inline] + pub const fn cast_from_core(array_ref: &[T; N]) -> &Self { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` + unsafe { &*array_ref.as_ptr().cast() } + } + + /// Cast a mutable reference to a core array to an [`Array`] reference. + #[inline] + pub const fn cast_from_core_mut(array_ref: &mut [T; N]) -> &mut Self { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; 1]` + unsafe { &mut *array_ref.as_mut_ptr().cast() } + } + + /// Transform slice to slice of core array type. + #[inline] + pub const fn cast_slice_from_core(slice: &[[T; N]]) -> &[Self] { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` + unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len()) } + } + + /// Transform mutable slice to mutable slice of core array type. + #[inline] + pub const fn cast_slice_from_core_mut(slice: &mut [[T; N]]) -> &mut [Self] { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) } + } + + /// Transform slice to slice of core array type. + #[inline] + pub const fn cast_slice_to_core(slice: &[Self]) -> &[[T; N]] { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` + unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len()) } + } + + /// Transform mutable slice to mutable slice of core array type. + #[inline] + pub const fn cast_slice_to_core_mut(slice: &mut [Self]) -> &mut [[T; N]] { + // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` + unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) } + } +} + +impl Array, U> +where + U: ArraySize, +{ + /// Create an uninitialized array of [`MaybeUninit`]s for the given type. + #[must_use] + pub const fn uninit() -> Array, U> { + // SAFETY: `Array` is a `repr(transparent)` newtype for `[MaybeUninit, N]`, i.e. an + // array of uninitialized memory mediated via the `MaybeUninit` interface, where the inner + // type is constrained by `ArraySize` impls which can only be added by this crate. + // + // Calling `uninit().assume_init()` triggers the `clippy::uninit_assumed_init` lint, but + // as just mentioned the inner type we're "assuming init" for is `[MaybeUninit, N]`, + // i.e. an array of uninitialized memory, which is always valid because definitionally no + // initialization is required of uninitialized memory. + #[allow(clippy::uninit_assumed_init)] + Self(unsafe { MaybeUninit::uninit().assume_init() }) + } + + /// Extract the values from an array of `MaybeUninit` containers. + /// + /// # Safety + /// + /// It is up to the caller to guarantee that all elements of the array are in an initialized + /// state. + #[inline] + pub unsafe fn assume_init(self) -> Array { + unsafe { + // `Array` is a `repr(transparent)` newtype for a generic inner type which is constrained to + // be `[T; N]` by the `ArraySize` impls in this crate. + // + // Since we're working with a type-erased inner type and ultimately trying to convert + // `[MaybeUninit; N]` to `[T; N]`, we can't use simpler approaches like a pointer cast + // or `transmute`, since the compiler can't prove to itself that the size will be the same. + // + // We've taken unique ownership of `self`, which is a `MaybeUninit` array, and as such we + // don't need to worry about `Drop` impls because `MaybeUninit` does not impl `Drop`. + // Since we have unique ownership of `self`, it's okay to make a copy because we're throwing + // the original away (and this should all get optimized to a noop by the compiler, anyway). + mem::transmute_copy(&self) + } + } +} + +impl AsRef> for Array +where + U: ArraySize, +{ + #[inline] + fn as_ref(&self) -> &Self { + self + } +} + +impl AsRef<[T]> for Array +where + U: ArraySize, +{ + #[inline] + fn as_ref(&self) -> &[T] { + self.0.as_ref() + } +} + +impl AsRef<[T; N]> for Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn as_ref(&self) -> &[T; N] { + &self.0 + } +} + +impl AsMut<[T]> for Array +where + U: ArraySize, +{ + #[inline] + fn as_mut(&mut self) -> &mut [T] { + self.0.as_mut() + } +} + +impl AsMut<[T; N]> for Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn as_mut(&mut self) -> &mut [T; N] { + &mut self.0 + } +} + +impl Borrow<[T]> for Array +where + U: ArraySize, +{ + #[inline] + fn borrow(&self) -> &[T] { + self.0.as_ref() + } +} + +impl Borrow<[T; N]> for Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn borrow(&self) -> &[T; N] { + &self.0 + } +} + +impl BorrowMut<[T]> for Array +where + U: ArraySize, +{ + #[inline] + fn borrow_mut(&mut self) -> &mut [T] { + self.0.as_mut() + } +} + +impl BorrowMut<[T; N]> for Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn borrow_mut(&mut self) -> &mut [T; N] { + &mut self.0 + } +} + +impl Clone for Array +where + T: Clone, + U: ArraySize, +{ + #[inline] + fn clone(&self) -> Self { + Self::from_fn(|n| self.0.as_ref()[n].clone()) + } +} + +impl Copy for Array +where + T: Copy, + U: ArraySize, + U::ArrayType: Copy, +{ +} + +impl Debug for Array +where + T: Debug, + U: ArraySize, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Array").field(&self.0.as_ref()).finish() + } +} + +impl Default for Array +where + T: Default, + U: ArraySize, +{ + #[inline] + fn default() -> Self { + Self::from_fn(|_| Default::default()) + } +} + +impl Deref for Array +where + U: ArraySize, +{ + type Target = [T]; + + #[inline] + fn deref(&self) -> &[T] { + self.0.as_ref() + } +} + +impl DerefMut for Array +where + U: ArraySize, +{ + #[inline] + fn deref_mut(&mut self) -> &mut [T] { + self.0.as_mut() + } +} + +impl Eq for Array +where + T: Eq, + U: ArraySize, +{ +} + +impl From<[T; N]> for Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(arr: [T; N]) -> Array { + Array(arr) + } +} + +impl From> for [T; N] +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(arr: Array) -> [T; N] { + arr.0 + } +} + +impl<'a, T, U, const N: usize> From<&'a [T; N]> for &'a Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(array_ref: &'a [T; N]) -> &'a Array { + Array::cast_from_core(array_ref) + } +} + +impl<'a, T, U, const N: usize> From<&'a Array> for &'a [T; N] +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(array_ref: &'a Array) -> &'a [T; N] { + array_ref.as_ref() + } +} + +impl<'a, T, U, const N: usize> From<&'a mut [T; N]> for &'a mut Array +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(array_ref: &'a mut [T; N]) -> &'a mut Array { + Array::cast_from_core_mut(array_ref) + } +} + +impl<'a, T, U, const N: usize> From<&'a mut Array> for &'a mut [T; N] +where + U: ArraySize = [T; N]>, +{ + #[inline] + fn from(array_ref: &'a mut Array) -> &'a mut [T; N] { + array_ref.as_mut() + } +} + +#[cfg(feature = "alloc")] +impl From> for alloc::boxed::Box<[T]> +where + U: ArraySize, +{ + #[inline] + fn from(array: Array) -> alloc::boxed::Box<[T]> { + array.into_iter().collect() + } +} + +#[cfg(feature = "alloc")] +impl From<&Array> for alloc::boxed::Box<[T]> +where + T: Clone, + U: ArraySize, +{ + #[inline] + fn from(array: &Array) -> alloc::boxed::Box<[T]> { + array.as_slice().into() + } +} + +#[cfg(feature = "alloc")] +impl From> for alloc::vec::Vec +where + U: ArraySize, +{ + #[inline] + fn from(array: Array) -> alloc::vec::Vec { + array.into_iter().collect() + } +} + +#[cfg(feature = "alloc")] +impl From<&Array> for alloc::vec::Vec +where + T: Clone, + U: ArraySize, +{ + #[inline] + fn from(array: &Array) -> alloc::vec::Vec { + array.as_slice().into() + } +} + +impl Hash for Array +where + T: Hash, + U: ArraySize, +{ + #[inline] + fn hash(&self, state: &mut H) { + self.0.as_ref().hash(state); + } +} + +impl Index for Array +where + [T]: Index, + U: ArraySize, +{ + type Output = <[T] as Index>::Output; + + #[inline] + fn index(&self, index: I) -> &Self::Output { + Index::index(self.as_slice(), index) + } +} + +impl IndexMut for Array +where + [T]: IndexMut, + U: ArraySize, +{ + #[inline] + fn index_mut(&mut self, index: I) -> &mut Self::Output { + IndexMut::index_mut(self.as_mut_slice(), index) + } +} + +impl PartialEq for Array +where + T: PartialEq, + U: ArraySize, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq(other.0.as_ref()) + } +} + +impl PartialEq<[T; N]> for Array +where + T: PartialEq, + U: ArraySize = [T; N]>, +{ + #[inline] + fn eq(&self, other: &[T; N]) -> bool { + self.0.eq(other) + } +} + +impl PartialEq> for [T; N] +where + T: PartialEq, + U: ArraySize = [T; N]>, +{ + #[inline] + fn eq(&self, other: &Array) -> bool { + self.eq(&other.0) + } +} + +impl PartialOrd for Array +where + T: PartialOrd, + U: ArraySize, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + self.0.as_ref().partial_cmp(other.0.as_ref()) + } +} + +impl Ord for Array +where + T: Ord, + U: ArraySize, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.0.as_ref().cmp(other.0.as_ref()) + } +} + +/// SAFETY: `Array` is a `repr(transparent)` newtype for `[T; N]`, so as long as `T: Send` it should +/// also be `Send`. +unsafe impl Send for Array where T: Send {} + +/// SAFETY: `Array` is a `repr(transparent)` newtype for `[T; N]`, so as long as `T: Sync` it should +/// also be `Sync`. +unsafe impl Sync for Array where T: Sync {} + +impl<'a, T, U> TryFrom<&'a [T]> for &'a Array +where + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(slice: &'a [T]) -> Result { + check_slice_length::(slice)?; + + // SAFETY: `Array` is a `repr(transparent)` newtype for a core + // array with length checked above. + Ok(unsafe { &*slice.as_ptr().cast() }) + } +} + +impl<'a, T, U> TryFrom<&'a mut [T]> for &'a mut Array +where + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(slice: &'a mut [T]) -> Result { + check_slice_length::(slice)?; + + // SAFETY: `Array` is a `repr(transparent)` newtype for a core + // array with length checked above. + Ok(unsafe { &mut *slice.as_mut_ptr().cast() }) + } +} + +impl<'a, T, U> TryFrom<&'a [T]> for Array +where + Self: Clone, + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(slice: &'a [T]) -> Result, TryFromSliceError> { + <&'a Self>::try_from(slice).cloned() + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for Array +where + Self: Clone, + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(b: alloc::boxed::Box<[T]>) -> Result { + Self::try_from(&*b) + } +} + +#[cfg(feature = "alloc")] +impl<'a, T, U> TryFrom<&'a alloc::boxed::Box<[T]>> for Array +where + Self: Clone, + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(b: &'a alloc::boxed::Box<[T]>) -> Result { + Self::try_from(&**b) + } +} + +#[cfg(feature = "alloc")] +impl TryFrom> for Array +where + Self: Clone, + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(v: alloc::vec::Vec) -> Result { + Self::try_from(v.as_slice()) + } +} + +#[cfg(feature = "alloc")] +impl<'a, T, U> TryFrom<&'a alloc::vec::Vec> for Array +where + Self: Clone, + U: ArraySize, +{ + type Error = TryFromSliceError; + + #[inline] + fn try_from(v: &'a alloc::vec::Vec) -> Result { + Self::try_from(v.as_slice()) + } +} + +// Deprecated legacy methods to ease migrations from `generic-array` +impl Array +where + U: ArraySize, +{ + /// Convert the given slice into a reference to a hybrid array. + /// + /// # Panics + /// + /// Panics if the slice's length doesn't match the array type. + #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] + #[inline] + pub fn from_slice(slice: &[T]) -> &Self { + slice.try_into().expect("slice length mismatch") + } + + /// Convert the given mutable slice to a mutable reference to a hybrid array. + /// + /// # Panics + /// + /// Panics if the slice's length doesn't match the array type. + #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] + #[inline] + pub fn from_mut_slice(slice: &mut [T]) -> &mut Self { + slice.try_into().expect("slice length mismatch") + } + + /// Clone the contents of the slice as a new hybrid array. + /// + /// # Panics + /// + /// Panics if the slice's length doesn't match the array type. + #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] + #[inline] + pub fn clone_from_slice(slice: &[T]) -> Self + where + Self: Clone, + { + slice.try_into().expect("slice length mismatch") + } +} + +#[cfg(feature = "arbitrary")] +impl<'a, T, U> Arbitrary<'a> for Array +where + T: Arbitrary<'a>, + U: ArraySize, +{ + fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { + Self::try_from_fn(|_n| Arbitrary::arbitrary(u)) + } +} + +#[cfg(feature = "bytemuck")] +unsafe impl Pod for Array +where + T: Pod, + U: ArraySize, + U::ArrayType: Copy, +{ +} + +#[cfg(feature = "bytemuck")] +unsafe impl Zeroable for Array +where + T: Zeroable, + U: ArraySize, +{ +} + +#[cfg(feature = "ctutils")] +impl ctutils::CtAssign for Array +where + [T]: ctutils::CtAssign, + U: ArraySize, +{ + #[inline] + fn ct_assign(&mut self, other: &Self, choice: ctutils::Choice) { + self.as_mut_slice().ct_assign(other.as_slice(), choice); + } +} + +#[cfg(feature = "ctutils")] +impl ctutils::CtSelect for Array +where + U: ArraySize, + U::ArrayType: ctutils::CtSelect, +{ + #[inline] + fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self { + Self(self.0.ct_select(&other.0, choice)) + } +} + +#[cfg(feature = "ctutils")] +impl ctutils::CtEq for Array +where + U: ArraySize, + U::ArrayType: ctutils::CtEq, +{ + #[inline] + fn ct_eq(&self, other: &Self) -> ctutils::Choice { + self.0.ct_eq(&other.0) + } +} + +#[cfg(feature = "subtle")] +impl subtle::ConditionallySelectable for Array +where + Self: Copy, + T: subtle::ConditionallySelectable, + U: ArraySize, +{ + #[inline] + fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self { + let mut output = *a; + output.conditional_assign(b, choice); + output + } + + fn conditional_assign(&mut self, other: &Self, choice: subtle::Choice) { + for (a_i, b_i) in self.iter_mut().zip(other) { + a_i.conditional_assign(b_i, choice); + } + } +} + +#[cfg(feature = "subtle")] +impl subtle::ConstantTimeEq for Array +where + T: subtle::ConstantTimeEq, + U: ArraySize, +{ + #[inline] + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.iter() + .zip(other.iter()) + .fold(subtle::Choice::from(1), |acc, (a, b)| acc & a.ct_eq(b)) + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for Array +where + T: Zeroize, + U: ArraySize, +{ + #[inline] + fn zeroize(&mut self) { + self.0.as_mut().iter_mut().zeroize(); + } +} + +#[cfg(feature = "zeroize")] +impl ZeroizeOnDrop for Array +where + T: ZeroizeOnDrop, + U: ArraySize, +{ +} + +/// Generate a [`TryFromSliceError`] if the slice doesn't match the given length. +#[cfg_attr(debug_assertions, allow(clippy::panic_in_result_fn))] +fn check_slice_length(slice: &[T]) -> Result<(), TryFromSliceError> { + debug_assert_eq!(Array::<(), U>::default().len(), U::USIZE); + + if slice.len() != U::USIZE { + // Hack: `TryFromSliceError` lacks a public constructor + <&[T; 1]>::try_from([].as_slice())?; + + #[cfg(debug_assertions)] + unreachable!(); + } + + Ok(()) +} diff --git a/hqc-kem/hybrid-array-patch/src/serde.rs b/hqc-kem/hybrid-array-patch/src/serde.rs new file mode 100644 index 0000000..4647581 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/serde.rs @@ -0,0 +1,71 @@ +//! Support for serializing and deserializing `Array` using `serde`. + +use crate::{Array, ArraySize}; +use core::{fmt, marker::PhantomData}; +use serde::{ + de::{self, Deserialize, Deserializer, SeqAccess, Visitor}, + ser::{Serialize, SerializeTuple, Serializer}, +}; + +impl<'de, T, U> Deserialize<'de> for Array +where + T: Deserialize<'de>, + U: ArraySize, +{ + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + T: Deserialize<'de>, + { + struct ArrayVisitor { + element: PhantomData, + } + + impl<'de, T, U> Visitor<'de> for ArrayVisitor> + where + T: Deserialize<'de>, + U: ArraySize, + { + type Value = Array; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(formatter, "an array of length {}", U::USIZE) + } + + fn visit_seq(self, mut seq: A) -> Result, A::Error> + where + A: SeqAccess<'de>, + { + Array::::try_from_fn(|i| { + seq.next_element()? + .ok_or_else(|| de::Error::invalid_length(i, &self)) + }) + } + } + + let visitor = ArrayVisitor { + element: PhantomData, + }; + + deserializer.deserialize_tuple(U::USIZE, visitor) + } +} + +impl Serialize for Array +where + T: Serialize, + U: ArraySize, +{ + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + let mut seq = serializer.serialize_tuple(U::USIZE)?; + + for elem in self { + seq.serialize_element(elem)?; + } + + seq.end() + } +} diff --git a/hqc-kem/hybrid-array-patch/src/sizes.rs b/hqc-kem/hybrid-array-patch/src/sizes.rs new file mode 100644 index 0000000..d42ba6d --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/sizes.rs @@ -0,0 +1,1164 @@ +//! Supported array sizes: [`typenum::Unsigned`] types with an [`ArraySize`] impl. +//! +//! We support the following array sizes by default: +//! +//! - 0-512 +//! - 528-1024 (multiples of 16) +//! - 2048, 4096, 8192 +//! +//! When the `extra-sizes` feature is enabled: 1040-4064 (multiples of 32) + +use super::{ArraySize, AssocArraySize}; + +#[cfg(feature = "extra-sizes")] +pub use extra_sizes::*; + +/// Implement the `ArraySize` and `AssocArraySize` traits for a given list of `N => UN, ...` +/// mappings. +/// +/// `N` is used over `UN::USIZE` in order to improve compile times (avoids associated constant +/// resolution) +macro_rules! impl_array_sizes { + ($testname:ident, $($len:expr => $ty:ident),+ $(,)?) => { + $( + // SAFETY: we depend on `<$ty as Unsigned>::USIZE == $len` for unsafe pointer casts. + // We ensure this property holds by having the macro write a test for that below. + unsafe impl ArraySize for $ty { + type ArrayType = [T; $len]; + } + + impl AssocArraySize for [T; $len] { + type Size = $ty; + } + )+ + + #[test] + fn $testname() { + use typenum::Unsigned; + $( + assert_eq!($len, $ty::USIZE); + )+ + } + }; +} + +/// Implement array sizes, also importing the relevant constants. +macro_rules! impl_array_sizes_with_import { + ($testname:ident, $($len:expr => $ty:ident),+ $(,)?) => { + $( + pub use typenum::consts::$ty; + )+ + impl_array_sizes!($testname, $($len => $ty),+); + }; +} + +impl_array_sizes_with_import! { + base, + 0 => U0, + 1 => U1, + 2 => U2, + 3 => U3, + 4 => U4, + 5 => U5, + 6 => U6, + 7 => U7, + 8 => U8, + 9 => U9, + 10 => U10, + 11 => U11, + 12 => U12, + 13 => U13, + 14 => U14, + 15 => U15, + 16 => U16, + 17 => U17, + 18 => U18, + 19 => U19, + 20 => U20, + 21 => U21, + 22 => U22, + 23 => U23, + 24 => U24, + 25 => U25, + 26 => U26, + 27 => U27, + 28 => U28, + 29 => U29, + 30 => U30, + 31 => U31, + 32 => U32, + 33 => U33, + 34 => U34, + 35 => U35, + 36 => U36, + 37 => U37, + 38 => U38, + 39 => U39, + 40 => U40, + 41 => U41, + 42 => U42, + 43 => U43, + 44 => U44, + 45 => U45, + 46 => U46, + 47 => U47, + 48 => U48, + 49 => U49, + 50 => U50, + 51 => U51, + 52 => U52, + 53 => U53, + 54 => U54, + 55 => U55, + 56 => U56, + 57 => U57, + 58 => U58, + 59 => U59, + 60 => U60, + 61 => U61, + 62 => U62, + 63 => U63, + 64 => U64, + 65 => U65, + 66 => U66, + 67 => U67, + 68 => U68, + 69 => U69, + 70 => U70, + 71 => U71, + 72 => U72, + 73 => U73, + 74 => U74, + 75 => U75, + 76 => U76, + 77 => U77, + 78 => U78, + 79 => U79, + 80 => U80, + 81 => U81, + 82 => U82, + 83 => U83, + 84 => U84, + 85 => U85, + 86 => U86, + 87 => U87, + 88 => U88, + 89 => U89, + 90 => U90, + 91 => U91, + 92 => U92, + 93 => U93, + 94 => U94, + 95 => U95, + 96 => U96, + 97 => U97, + 98 => U98, + 99 => U99, + 100 => U100, + 101 => U101, + 102 => U102, + 103 => U103, + 104 => U104, + 105 => U105, + 106 => U106, + 107 => U107, + 108 => U108, + 109 => U109, + 110 => U110, + 111 => U111, + 112 => U112, + 113 => U113, + 114 => U114, + 115 => U115, + 116 => U116, + 117 => U117, + 118 => U118, + 119 => U119, + 120 => U120, + 121 => U121, + 122 => U122, + 123 => U123, + 124 => U124, + 125 => U125, + 126 => U126, + 127 => U127, + 128 => U128, + 129 => U129, + 130 => U130, + 131 => U131, + 132 => U132, + 133 => U133, + 134 => U134, + 135 => U135, + 136 => U136, + 137 => U137, + 138 => U138, + 139 => U139, + 140 => U140, + 141 => U141, + 142 => U142, + 143 => U143, + 144 => U144, + 145 => U145, + 146 => U146, + 147 => U147, + 148 => U148, + 149 => U149, + 150 => U150, + 151 => U151, + 152 => U152, + 153 => U153, + 154 => U154, + 155 => U155, + 156 => U156, + 157 => U157, + 158 => U158, + 159 => U159, + 160 => U160, + 161 => U161, + 162 => U162, + 163 => U163, + 164 => U164, + 165 => U165, + 166 => U166, + 167 => U167, + 168 => U168, + 169 => U169, + 170 => U170, + 171 => U171, + 172 => U172, + 173 => U173, + 174 => U174, + 175 => U175, + 176 => U176, + 177 => U177, + 178 => U178, + 179 => U179, + 180 => U180, + 181 => U181, + 182 => U182, + 183 => U183, + 184 => U184, + 185 => U185, + 186 => U186, + 187 => U187, + 188 => U188, + 189 => U189, + 190 => U190, + 191 => U191, + 192 => U192, + 193 => U193, + 194 => U194, + 195 => U195, + 196 => U196, + 197 => U197, + 198 => U198, + 199 => U199, + 200 => U200, + 201 => U201, + 202 => U202, + 203 => U203, + 204 => U204, + 205 => U205, + 206 => U206, + 207 => U207, + 208 => U208, + 209 => U209, + 210 => U210, + 211 => U211, + 212 => U212, + 213 => U213, + 214 => U214, + 215 => U215, + 216 => U216, + 217 => U217, + 218 => U218, + 219 => U219, + 220 => U220, + 221 => U221, + 222 => U222, + 223 => U223, + 224 => U224, + 225 => U225, + 226 => U226, + 227 => U227, + 228 => U228, + 229 => U229, + 230 => U230, + 231 => U231, + 232 => U232, + 233 => U233, + 234 => U234, + 235 => U235, + 236 => U236, + 237 => U237, + 238 => U238, + 239 => U239, + 240 => U240, + 241 => U241, + 242 => U242, + 243 => U243, + 244 => U244, + 245 => U245, + 246 => U246, + 247 => U247, + 248 => U248, + 249 => U249, + 250 => U250, + 251 => U251, + 252 => U252, + 253 => U253, + 254 => U254, + 255 => U255, + 256 => U256, + 257 => U257, + 258 => U258, + 259 => U259, + 260 => U260, + 261 => U261, + 262 => U262, + 263 => U263, + 264 => U264, + 265 => U265, + 266 => U266, + 267 => U267, + 268 => U268, + 269 => U269, + 270 => U270, + 271 => U271, + 272 => U272, + 273 => U273, + 274 => U274, + 275 => U275, + 276 => U276, + 277 => U277, + 278 => U278, + 279 => U279, + 280 => U280, + 281 => U281, + 282 => U282, + 283 => U283, + 284 => U284, + 285 => U285, + 286 => U286, + 287 => U287, + 288 => U288, + 289 => U289, + 290 => U290, + 291 => U291, + 292 => U292, + 293 => U293, + 294 => U294, + 295 => U295, + 296 => U296, + 297 => U297, + 298 => U298, + 299 => U299, + 300 => U300, + 301 => U301, + 302 => U302, + 303 => U303, + 304 => U304, + 305 => U305, + 306 => U306, + 307 => U307, + 308 => U308, + 309 => U309, + 310 => U310, + 311 => U311, + 312 => U312, + 313 => U313, + 314 => U314, + 315 => U315, + 316 => U316, + 317 => U317, + 318 => U318, + 319 => U319, + 320 => U320, + 321 => U321, + 322 => U322, + 323 => U323, + 324 => U324, + 325 => U325, + 326 => U326, + 327 => U327, + 328 => U328, + 329 => U329, + 330 => U330, + 331 => U331, + 332 => U332, + 333 => U333, + 334 => U334, + 335 => U335, + 336 => U336, + 337 => U337, + 338 => U338, + 339 => U339, + 340 => U340, + 341 => U341, + 342 => U342, + 343 => U343, + 344 => U344, + 345 => U345, + 346 => U346, + 347 => U347, + 348 => U348, + 349 => U349, + 350 => U350, + 351 => U351, + 352 => U352, + 353 => U353, + 354 => U354, + 355 => U355, + 356 => U356, + 357 => U357, + 358 => U358, + 359 => U359, + 360 => U360, + 361 => U361, + 362 => U362, + 363 => U363, + 364 => U364, + 365 => U365, + 366 => U366, + 367 => U367, + 368 => U368, + 369 => U369, + 370 => U370, + 371 => U371, + 372 => U372, + 373 => U373, + 374 => U374, + 375 => U375, + 376 => U376, + 377 => U377, + 378 => U378, + 379 => U379, + 380 => U380, + 381 => U381, + 382 => U382, + 383 => U383, + 384 => U384, + 385 => U385, + 386 => U386, + 387 => U387, + 388 => U388, + 389 => U389, + 390 => U390, + 391 => U391, + 392 => U392, + 393 => U393, + 394 => U394, + 395 => U395, + 396 => U396, + 397 => U397, + 398 => U398, + 399 => U399, + 400 => U400, + 401 => U401, + 402 => U402, + 403 => U403, + 404 => U404, + 405 => U405, + 406 => U406, + 407 => U407, + 408 => U408, + 409 => U409, + 410 => U410, + 411 => U411, + 412 => U412, + 413 => U413, + 414 => U414, + 415 => U415, + 416 => U416, + 417 => U417, + 418 => U418, + 419 => U419, + 420 => U420, + 421 => U421, + 422 => U422, + 423 => U423, + 424 => U424, + 425 => U425, + 426 => U426, + 427 => U427, + 428 => U428, + 429 => U429, + 430 => U430, + 431 => U431, + 432 => U432, + 433 => U433, + 434 => U434, + 435 => U435, + 436 => U436, + 437 => U437, + 438 => U438, + 439 => U439, + 440 => U440, + 441 => U441, + 442 => U442, + 443 => U443, + 444 => U444, + 445 => U445, + 446 => U446, + 447 => U447, + 448 => U448, + 449 => U449, + 450 => U450, + 451 => U451, + 452 => U452, + 453 => U453, + 454 => U454, + 455 => U455, + 456 => U456, + 457 => U457, + 458 => U458, + 459 => U459, + 460 => U460, + 461 => U461, + 462 => U462, + 463 => U463, + 464 => U464, + 465 => U465, + 466 => U466, + 467 => U467, + 468 => U468, + 469 => U469, + 470 => U470, + 471 => U471, + 472 => U472, + 473 => U473, + 474 => U474, + 475 => U475, + 476 => U476, + 477 => U477, + 478 => U478, + 479 => U479, + 480 => U480, + 481 => U481, + 482 => U482, + 483 => U483, + 484 => U484, + 485 => U485, + 486 => U486, + 487 => U487, + 488 => U488, + 489 => U489, + 490 => U490, + 491 => U491, + 492 => U492, + 493 => U493, + 494 => U494, + 495 => U495, + 496 => U496, + 497 => U497, + 498 => U498, + 499 => U499, + 500 => U500, + 501 => U501, + 502 => U502, + 503 => U503, + 504 => U504, + 505 => U505, + 506 => U506, + 507 => U507, + 508 => U508, + 509 => U509, + 510 => U510, + 511 => U511, + 512 => U512, + 528 => U528, + 536 => U536, + 544 => U544, + 560 => U560, + 568 => U568, + 576 => U576, + 592 => U592, + 608 => U608, + 624 => U624, + 640 => U640, + 656 => U656, + 672 => U672, + 688 => U688, + 704 => U704, + 720 => U720, + 736 => U736, + 752 => U752, + 768 => U768, + 784 => U784, + 800 => U800, + 816 => U816, + 832 => U832, + 848 => U848, + 864 => U864, + 880 => U880, + 896 => U896, + 912 => U912, + 928 => U928, + 944 => U944, + 960 => U960, + 976 => U976, + 992 => U992, + 1008 => U1008, + 1024 => U1024, + 2048 => U2048, + 4096 => U4096, + 8192 => U8192, +} + +/// Additional typenum size aliases beyond what are normally provided. +/// +/// These are defined using their component bits rather than `Add` to avoid conflicting impls. +#[cfg(feature = "extra-sizes")] +#[allow(missing_docs)] +mod extra_sizes { + use super::{ArraySize, AssocArraySize}; + use typenum::{ + UInt, UTerm, + consts::{B0, B1}, + }; + + // This macro constructs a UInt type from a sequence of bits. The bits are interpreted as the + // little-endian representation of the integer in question. For example, uint!(1 1 0 1 0 0 1) is + // U75 (not U105). + macro_rules! uint { + () => { UTerm }; + (0 $($bs:tt)*) => { UInt< uint!($($bs)*), B0 > }; + (1 $($bs:tt)*) => { UInt< uint!($($bs)*), B1 > }; + } + + pub type U1040 = uint!(0 0 0 0 1 0 0 0 0 0 1); + pub type U1056 = uint!(0 0 0 0 0 1 0 0 0 0 1); + pub type U1072 = uint!(0 0 0 0 1 1 0 0 0 0 1); + pub type U1088 = uint!(0 0 0 0 0 0 1 0 0 0 1); + pub type U1104 = uint!(0 0 0 0 1 0 1 0 0 0 1); + pub type U1120 = uint!(0 0 0 0 0 1 1 0 0 0 1); + pub type U1136 = uint!(0 0 0 0 1 1 1 0 0 0 1); + pub type U1152 = uint!(0 0 0 0 0 0 0 1 0 0 1); + pub type U1168 = uint!(0 0 0 0 1 0 0 1 0 0 1); + pub type U1184 = uint!(0 0 0 0 0 1 0 1 0 0 1); + pub type U1200 = uint!(0 0 0 0 1 1 0 1 0 0 1); + pub type U1216 = uint!(0 0 0 0 0 0 1 1 0 0 1); + pub type U1232 = uint!(0 0 0 0 1 0 1 1 0 0 1); + pub type U1248 = uint!(0 0 0 0 0 1 1 1 0 0 1); + pub type U1264 = uint!(0 0 0 0 1 1 1 1 0 0 1); + pub type U1280 = uint!(0 0 0 0 0 0 0 0 1 0 1); + pub type U1296 = uint!(0 0 0 0 1 0 0 0 1 0 1); + pub type U1312 = uint!(0 0 0 0 0 1 0 0 1 0 1); + pub type U1328 = uint!(0 0 0 0 1 1 0 0 1 0 1); + pub type U1344 = uint!(0 0 0 0 0 0 1 0 1 0 1); + pub type U1360 = uint!(0 0 0 0 1 0 1 0 1 0 1); + pub type U1376 = uint!(0 0 0 0 0 1 1 0 1 0 1); + pub type U1392 = uint!(0 0 0 0 1 1 1 0 1 0 1); + pub type U1408 = uint!(0 0 0 0 0 0 0 1 1 0 1); + pub type U1424 = uint!(0 0 0 0 1 0 0 1 1 0 1); + pub type U1440 = uint!(0 0 0 0 0 1 0 1 1 0 1); + pub type U1456 = uint!(0 0 0 0 1 1 0 1 1 0 1); + pub type U1472 = uint!(0 0 0 0 0 0 1 1 1 0 1); + pub type U1488 = uint!(0 0 0 0 1 0 1 1 1 0 1); + pub type U1504 = uint!(0 0 0 0 0 1 1 1 1 0 1); + pub type U1520 = uint!(0 0 0 0 1 1 1 1 1 0 1); + pub type U1536 = uint!(0 0 0 0 0 0 0 0 0 1 1); + pub type U1552 = uint!(0 0 0 0 1 0 0 0 0 1 1); + pub type U1568 = uint!(0 0 0 0 0 1 0 0 0 1 1); + pub type U1584 = uint!(0 0 0 0 1 1 0 0 0 1 1); + pub type U1600 = uint!(0 0 0 0 0 0 1 0 0 1 1); + pub type U1616 = uint!(0 0 0 0 1 0 1 0 0 1 1); + pub type U1632 = uint!(0 0 0 0 0 1 1 0 0 1 1); + pub type U1648 = uint!(0 0 0 0 1 1 1 0 0 1 1); + pub type U1664 = uint!(0 0 0 0 0 0 0 1 0 1 1); + pub type U1680 = uint!(0 0 0 0 1 0 0 1 0 1 1); + pub type U1696 = uint!(0 0 0 0 0 1 0 1 0 1 1); + pub type U1712 = uint!(0 0 0 0 1 1 0 1 0 1 1); + pub type U1728 = uint!(0 0 0 0 0 0 1 1 0 1 1); + pub type U1744 = uint!(0 0 0 0 1 0 1 1 0 1 1); + pub type U1760 = uint!(0 0 0 0 0 1 1 1 0 1 1); + pub type U1776 = uint!(0 0 0 0 1 1 1 1 0 1 1); + pub type U1792 = uint!(0 0 0 0 0 0 0 0 1 1 1); + pub type U1808 = uint!(0 0 0 0 1 0 0 0 1 1 1); + pub type U1824 = uint!(0 0 0 0 0 1 0 0 1 1 1); + pub type U1840 = uint!(0 0 0 0 1 1 0 0 1 1 1); + pub type U1856 = uint!(0 0 0 0 0 0 1 0 1 1 1); + pub type U1872 = uint!(0 0 0 0 1 0 1 0 1 1 1); + pub type U1888 = uint!(0 0 0 0 0 1 1 0 1 1 1); + pub type U1904 = uint!(0 0 0 0 1 1 1 0 1 1 1); + pub type U1920 = uint!(0 0 0 0 0 0 0 1 1 1 1); + pub type U1936 = uint!(0 0 0 0 1 0 0 1 1 1 1); + pub type U1952 = uint!(0 0 0 0 0 1 0 1 1 1 1); + pub type U1968 = uint!(0 0 0 0 1 1 0 1 1 1 1); + pub type U1984 = uint!(0 0 0 0 0 0 1 1 1 1 1); + pub type U2000 = uint!(0 0 0 0 1 0 1 1 1 1 1); + pub type U2016 = uint!(0 0 0 0 0 1 1 1 1 1 1); + pub type U2032 = uint!(0 0 0 0 1 1 1 1 1 1 1); + pub type U2064 = uint!(0 0 0 0 1 0 0 0 0 0 0 1); + pub type U2080 = uint!(0 0 0 0 0 1 0 0 0 0 0 1); + pub type U2096 = uint!(0 0 0 0 1 1 0 0 0 0 0 1); + pub type U2112 = uint!(0 0 0 0 0 0 1 0 0 0 0 1); + pub type U2128 = uint!(0 0 0 0 1 0 1 0 0 0 0 1); + pub type U2144 = uint!(0 0 0 0 0 1 1 0 0 0 0 1); + pub type U2160 = uint!(0 0 0 0 1 1 1 0 0 0 0 1); + pub type U2176 = uint!(0 0 0 0 0 0 0 1 0 0 0 1); + pub type U2192 = uint!(0 0 0 0 1 0 0 1 0 0 0 1); + pub type U2208 = uint!(0 0 0 0 0 1 0 1 0 0 0 1); + pub type U2224 = uint!(0 0 0 0 1 1 0 1 0 0 0 1); + pub type U2240 = uint!(0 0 0 0 0 0 1 1 0 0 0 1); + pub type U2256 = uint!(0 0 0 0 1 0 1 1 0 0 0 1); + pub type U2272 = uint!(0 0 0 0 0 1 1 1 0 0 0 1); + pub type U2288 = uint!(0 0 0 0 1 1 1 1 0 0 0 1); + pub type U2304 = uint!(0 0 0 0 0 0 0 0 1 0 0 1); + pub type U2320 = uint!(0 0 0 0 1 0 0 0 1 0 0 1); + pub type U2336 = uint!(0 0 0 0 0 1 0 0 1 0 0 1); + pub type U2352 = uint!(0 0 0 0 1 1 0 0 1 0 0 1); + pub type U2368 = uint!(0 0 0 0 0 0 1 0 1 0 0 1); + pub type U2384 = uint!(0 0 0 0 1 0 1 0 1 0 0 1); + pub type U2400 = uint!(0 0 0 0 0 1 1 0 1 0 0 1); + pub type U2416 = uint!(0 0 0 0 1 1 1 0 1 0 0 1); + pub type U2432 = uint!(0 0 0 0 0 0 0 1 1 0 0 1); + pub type U2448 = uint!(0 0 0 0 1 0 0 1 1 0 0 1); + pub type U2464 = uint!(0 0 0 0 0 1 0 1 1 0 0 1); + pub type U2480 = uint!(0 0 0 0 1 1 0 1 1 0 0 1); + pub type U2496 = uint!(0 0 0 0 0 0 1 1 1 0 0 1); + pub type U2512 = uint!(0 0 0 0 1 0 1 1 1 0 0 1); + pub type U2528 = uint!(0 0 0 0 0 1 1 1 1 0 0 1); + pub type U2544 = uint!(0 0 0 0 1 1 1 1 1 0 0 1); + pub type U2560 = uint!(0 0 0 0 0 0 0 0 0 1 0 1); + pub type U2576 = uint!(0 0 0 0 1 0 0 0 0 1 0 1); + pub type U2592 = uint!(0 0 0 0 0 1 0 0 0 1 0 1); + pub type U2608 = uint!(0 0 0 0 1 1 0 0 0 1 0 1); + pub type U2624 = uint!(0 0 0 0 0 0 1 0 0 1 0 1); + pub type U2640 = uint!(0 0 0 0 1 0 1 0 0 1 0 1); + pub type U2656 = uint!(0 0 0 0 0 1 1 0 0 1 0 1); + pub type U2672 = uint!(0 0 0 0 1 1 1 0 0 1 0 1); + pub type U2688 = uint!(0 0 0 0 0 0 0 1 0 1 0 1); + pub type U2704 = uint!(0 0 0 0 1 0 0 1 0 1 0 1); + pub type U2720 = uint!(0 0 0 0 0 1 0 1 0 1 0 1); + pub type U2736 = uint!(0 0 0 0 1 1 0 1 0 1 0 1); + pub type U2752 = uint!(0 0 0 0 0 0 1 1 0 1 0 1); + pub type U2768 = uint!(0 0 0 0 1 0 1 1 0 1 0 1); + pub type U2784 = uint!(0 0 0 0 0 1 1 1 0 1 0 1); + pub type U2800 = uint!(0 0 0 0 1 1 1 1 0 1 0 1); + pub type U2816 = uint!(0 0 0 0 0 0 0 0 1 1 0 1); + pub type U2832 = uint!(0 0 0 0 1 0 0 0 1 1 0 1); + pub type U2848 = uint!(0 0 0 0 0 1 0 0 1 1 0 1); + pub type U2864 = uint!(0 0 0 0 1 1 0 0 1 1 0 1); + pub type U2880 = uint!(0 0 0 0 0 0 1 0 1 1 0 1); + pub type U2896 = uint!(0 0 0 0 1 0 1 0 1 1 0 1); + pub type U2912 = uint!(0 0 0 0 0 1 1 0 1 1 0 1); + pub type U2928 = uint!(0 0 0 0 1 1 1 0 1 1 0 1); + pub type U2944 = uint!(0 0 0 0 0 0 0 1 1 1 0 1); + pub type U2960 = uint!(0 0 0 0 1 0 0 1 1 1 0 1); + pub type U2976 = uint!(0 0 0 0 0 1 0 1 1 1 0 1); + pub type U2992 = uint!(0 0 0 0 1 1 0 1 1 1 0 1); + pub type U3008 = uint!(0 0 0 0 0 0 1 1 1 1 0 1); + pub type U3024 = uint!(0 0 0 0 1 0 1 1 1 1 0 1); + pub type U3040 = uint!(0 0 0 0 0 1 1 1 1 1 0 1); + pub type U3056 = uint!(0 0 0 0 1 1 1 1 1 1 0 1); + pub type U3072 = uint!(0 0 0 0 0 0 0 0 0 0 1 1); + pub type U3088 = uint!(0 0 0 0 1 0 0 0 0 0 1 1); + pub type U3104 = uint!(0 0 0 0 0 1 0 0 0 0 1 1); + pub type U3120 = uint!(0 0 0 0 1 1 0 0 0 0 1 1); + pub type U3136 = uint!(0 0 0 0 0 0 1 0 0 0 1 1); + pub type U3152 = uint!(0 0 0 0 1 0 1 0 0 0 1 1); + pub type U3168 = uint!(0 0 0 0 0 1 1 0 0 0 1 1); + pub type U3184 = uint!(0 0 0 0 1 1 1 0 0 0 1 1); + pub type U3200 = uint!(0 0 0 0 0 0 0 1 0 0 1 1); + pub type U3216 = uint!(0 0 0 0 1 0 0 1 0 0 1 1); + pub type U3232 = uint!(0 0 0 0 0 1 0 1 0 0 1 1); + pub type U3248 = uint!(0 0 0 0 1 1 0 1 0 0 1 1); + pub type U3264 = uint!(0 0 0 0 0 0 1 1 0 0 1 1); + pub type U3280 = uint!(0 0 0 0 1 0 1 1 0 0 1 1); + pub type U3296 = uint!(0 0 0 0 0 1 1 1 0 0 1 1); + pub type U3312 = uint!(0 0 0 0 1 1 1 1 0 0 1 1); + pub type U3328 = uint!(0 0 0 0 0 0 0 0 1 0 1 1); + pub type U3344 = uint!(0 0 0 0 1 0 0 0 1 0 1 1); + pub type U3360 = uint!(0 0 0 0 0 1 0 0 1 0 1 1); + pub type U3376 = uint!(0 0 0 0 1 1 0 0 1 0 1 1); + pub type U3392 = uint!(0 0 0 0 0 0 1 0 1 0 1 1); + pub type U3408 = uint!(0 0 0 0 1 0 1 0 1 0 1 1); + pub type U3424 = uint!(0 0 0 0 0 1 1 0 1 0 1 1); + pub type U3440 = uint!(0 0 0 0 1 1 1 0 1 0 1 1); + pub type U3456 = uint!(0 0 0 0 0 0 0 1 1 0 1 1); + pub type U3472 = uint!(0 0 0 0 1 0 0 1 1 0 1 1); + pub type U3488 = uint!(0 0 0 0 0 1 0 1 1 0 1 1); + pub type U3504 = uint!(0 0 0 0 1 1 0 1 1 0 1 1); + pub type U3520 = uint!(0 0 0 0 0 0 1 1 1 0 1 1); + pub type U3536 = uint!(0 0 0 0 1 0 1 1 1 0 1 1); + pub type U3552 = uint!(0 0 0 0 0 1 1 1 1 0 1 1); + pub type U3568 = uint!(0 0 0 0 1 1 1 1 1 0 1 1); + pub type U3584 = uint!(0 0 0 0 0 0 0 0 0 1 1 1); + pub type U3600 = uint!(0 0 0 0 1 0 0 0 0 1 1 1); + pub type U3616 = uint!(0 0 0 0 0 1 0 0 0 1 1 1); + pub type U3632 = uint!(0 0 0 0 1 1 0 0 0 1 1 1); + pub type U3648 = uint!(0 0 0 0 0 0 1 0 0 1 1 1); + pub type U3664 = uint!(0 0 0 0 1 0 1 0 0 1 1 1); + pub type U3680 = uint!(0 0 0 0 0 1 1 0 0 1 1 1); + pub type U3696 = uint!(0 0 0 0 1 1 1 0 0 1 1 1); + pub type U3712 = uint!(0 0 0 0 0 0 0 1 0 1 1 1); + pub type U3728 = uint!(0 0 0 0 1 0 0 1 0 1 1 1); + pub type U3744 = uint!(0 0 0 0 0 1 0 1 0 1 1 1); + pub type U3760 = uint!(0 0 0 0 1 1 0 1 0 1 1 1); + pub type U3776 = uint!(0 0 0 0 0 0 1 1 0 1 1 1); + pub type U3792 = uint!(0 0 0 0 1 0 1 1 0 1 1 1); + pub type U3808 = uint!(0 0 0 0 0 1 1 1 0 1 1 1); + pub type U3824 = uint!(0 0 0 0 1 1 1 1 0 1 1 1); + pub type U3840 = uint!(0 0 0 0 0 0 0 0 1 1 1 1); + pub type U3856 = uint!(0 0 0 0 1 0 0 0 1 1 1 1); + pub type U3872 = uint!(0 0 0 0 0 1 0 0 1 1 1 1); + pub type U3888 = uint!(0 0 0 0 1 1 0 0 1 1 1 1); + pub type U3904 = uint!(0 0 0 0 0 0 1 0 1 1 1 1); + pub type U3920 = uint!(0 0 0 0 1 0 1 0 1 1 1 1); + pub type U3936 = uint!(0 0 0 0 0 1 1 0 1 1 1 1); + pub type U3952 = uint!(0 0 0 0 1 1 1 0 1 1 1 1); + pub type U3968 = uint!(0 0 0 0 0 0 0 1 1 1 1 1); + pub type U3984 = uint!(0 0 0 0 1 0 0 1 1 1 1 1); + pub type U4000 = uint!(0 0 0 0 0 1 0 1 1 1 1 1); + pub type U4016 = uint!(0 0 0 0 1 1 0 1 1 1 1 1); + pub type U4032 = uint!(0 0 0 0 0 0 1 1 1 1 1 1); + pub type U4048 = uint!(0 0 0 0 1 0 1 1 1 1 1 1); + pub type U4064 = uint!(0 0 0 0 0 1 1 1 1 1 1 1); + pub type U4080 = uint!(0 0 0 0 1 1 1 1 1 1 1 1); + + // ML-DSA sizes + // + // Includes the public key, private key, and signature sizes not covered elsewhere, as well as + // some intermediate value sizes. + pub type U2420 = uint!(0 0 1 0 1 1 1 0 1 0 0 1); + pub type U3309 = uint!(1 0 1 1 0 1 1 1 0 0 1 1); + pub type U4480 = uint!(0 0 0 0 0 0 0 1 1 0 0 0 1); + pub type U4544 = uint!(0 0 0 0 0 0 1 1 1 0 0 0 1); + pub type U4595 = uint!(1 1 0 0 1 1 1 1 1 0 0 0 1); + pub type U4627 = uint!(1 1 0 0 1 0 0 0 0 1 0 0 1); + pub type U4896 = uint!(0 0 0 0 0 1 0 0 1 1 0 0 1); + + // SLH-DSA sizes + pub type U7856 = uint!(0 0 0 0 1 1 0 1 0 1 1 1 1); + pub type U16224 = uint!(0 0 0 0 0 1 1 0 1 1 1 1 1 1); + pub type U17088 = uint!(0 0 0 0 0 0 1 1 0 1 0 0 0 0 1); + pub type U29792 = uint!(0 0 0 0 0 1 1 0 0 0 1 0 1 1 1); + pub type U35664 = uint!(0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 1); + pub type U49856 = uint!(0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1); + + // Kemeleon ML-KEM Encoding sizes + pub type U749 = uint!(1 0 1 1 0 1 1 1 0 1); + pub type U781 = uint!(1 0 1 1 0 0 0 0 1 1); + pub type U877 = uint!(1 0 1 1 0 1 1 0 1 1); + pub type U1124 = uint!(0 0 1 0 0 1 1 0 0 0 1); + pub type U1156 = uint!(0 0 1 0 0 0 0 1 0 0 1); + pub type U1252 = uint!(0 0 1 0 0 1 1 1 0 0 1); + pub type U1498 = uint!(0 1 0 1 1 0 1 1 1 0 1); + pub type U1530 = uint!(0 1 0 1 1 1 1 1 1 0 1); + pub type U1658 = uint!(0 1 0 1 1 1 1 0 0 1 1); + + // LMS sizes + pub type U2047 = uint!(1 1 1 1 1 1 1 1 1 1 1); + pub type U2180 = uint!(0 0 1 0 0 0 0 1 0 0 0 1); + pub type U4292 = uint!(0 0 1 0 0 0 1 1 0 0 0 0 1); + pub type U8516 = uint!(0 0 1 0 0 0 1 0 1 0 0 0 0 1); + + // FrodoKEM640 sizes + + pub type U9616 = uint!(0 0 0 0 1 0 0 1 1 0 1 0 0 1); + pub type U19888 = uint!(0 0 0 0 1 1 0 1 1 0 1 1 0 0 1); + pub type U9720 = uint!(0 0 0 1 1 1 1 1 1 0 1 0 0 1); + pub type U9752 = uint!(0 0 0 1 1 0 0 0 0 1 1 0 0 1); + + // FrodoKEM976 sizes + pub type U15632 = uint!(0 0 0 0 1 0 0 0 1 0 1 1 1 1); + pub type U31296 = uint!(0 0 0 0 0 0 1 0 0 1 0 1 1 1 1); + pub type U15744 = uint!(0 0 0 0 0 0 0 1 1 0 1 1 1 1); + pub type U15792 = uint!(0 0 0 0 1 1 0 1 1 0 1 1 1 1); + + // FrodoKEM1344 sizes + pub type U21520 = uint!(0 0 0 0 1 0 0 0 0 0 1 0 1 0 1); + pub type U43088 = uint!(0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 1); + pub type U21632 = uint!(0 0 0 0 0 0 0 1 0 0 1 0 1 0 1); + pub type U21696 = uint!(0 0 0 0 0 0 1 1 0 0 1 0 1 0 1); + + // HKDF-Expand common max output sizes + pub type U8160 = uint!(0 0 0 0 0 1 1 1 1 1 1 1 1); + pub type U12240 = uint!(0 0 0 0 1 0 1 1 1 1 1 1 0 1); + pub type U16320 = uint!(0 0 0 0 0 0 1 1 1 1 1 1 1 1); + + impl_array_sizes! { + base_extra, + 1040 => U1040, + 1056 => U1056, + 1072 => U1072, + 1088 => U1088, + 1104 => U1104, + 1120 => U1120, + 1136 => U1136, + 1152 => U1152, + 1168 => U1168, + 1184 => U1184, + 1200 => U1200, + 1216 => U1216, + 1232 => U1232, + 1248 => U1248, + 1264 => U1264, + 1280 => U1280, + 1296 => U1296, + 1312 => U1312, + 1328 => U1328, + 1344 => U1344, + 1360 => U1360, + 1376 => U1376, + 1392 => U1392, + 1408 => U1408, + 1424 => U1424, + 1440 => U1440, + 1456 => U1456, + 1472 => U1472, + 1488 => U1488, + 1504 => U1504, + 1520 => U1520, + 1536 => U1536, + 1552 => U1552, + 1568 => U1568, + 1584 => U1584, + 1600 => U1600, + 1616 => U1616, + 1632 => U1632, + 1648 => U1648, + 1664 => U1664, + 1680 => U1680, + 1696 => U1696, + 1712 => U1712, + 1728 => U1728, + 1744 => U1744, + 1760 => U1760, + 1776 => U1776, + 1792 => U1792, + 1808 => U1808, + 1824 => U1824, + 1840 => U1840, + 1856 => U1856, + 1872 => U1872, + 1888 => U1888, + 1904 => U1904, + 1920 => U1920, + 1936 => U1936, + 1952 => U1952, + 1968 => U1968, + 1984 => U1984, + 2000 => U2000, + 2016 => U2016, + 2032 => U2032, + 2064 => U2064, + 2080 => U2080, + 2096 => U2096, + 2112 => U2112, + 2128 => U2128, + 2144 => U2144, + 2160 => U2160, + 2176 => U2176, + 2192 => U2192, + 2208 => U2208, + 2224 => U2224, + 2240 => U2240, + 2256 => U2256, + 2272 => U2272, + 2288 => U2288, + 2304 => U2304, + 2320 => U2320, + 2336 => U2336, + 2352 => U2352, + 2368 => U2368, + 2384 => U2384, + 2400 => U2400, + 2416 => U2416, + 2432 => U2432, + 2448 => U2448, + 2464 => U2464, + 2480 => U2480, + 2496 => U2496, + 2512 => U2512, + 2528 => U2528, + 2544 => U2544, + 2560 => U2560, + 2576 => U2576, + 2592 => U2592, + 2608 => U2608, + 2624 => U2624, + 2640 => U2640, + 2656 => U2656, + 2672 => U2672, + 2688 => U2688, + 2704 => U2704, + 2720 => U2720, + 2736 => U2736, + 2752 => U2752, + 2768 => U2768, + 2784 => U2784, + 2800 => U2800, + 2816 => U2816, + 2832 => U2832, + 2848 => U2848, + 2864 => U2864, + 2880 => U2880, + 2896 => U2896, + 2912 => U2912, + 2928 => U2928, + 2944 => U2944, + 2960 => U2960, + 2976 => U2976, + 2992 => U2992, + 3008 => U3008, + 3024 => U3024, + 3040 => U3040, + 3056 => U3056, + 3072 => U3072, + 3088 => U3088, + 3104 => U3104, + 3120 => U3120, + 3136 => U3136, + 3152 => U3152, + 3168 => U3168, + 3184 => U3184, + 3200 => U3200, + 3216 => U3216, + 3232 => U3232, + 3248 => U3248, + 3264 => U3264, + 3280 => U3280, + 3296 => U3296, + 3312 => U3312, + 3328 => U3328, + 3344 => U3344, + 3360 => U3360, + 3376 => U3376, + 3392 => U3392, + 3408 => U3408, + 3424 => U3424, + 3440 => U3440, + 3456 => U3456, + 3472 => U3472, + 3488 => U3488, + 3504 => U3504, + 3520 => U3520, + 3536 => U3536, + 3552 => U3552, + 3568 => U3568, + 3584 => U3584, + 3600 => U3600, + 3616 => U3616, + 3632 => U3632, + 3648 => U3648, + 3664 => U3664, + 3680 => U3680, + 3696 => U3696, + 3712 => U3712, + 3728 => U3728, + 3744 => U3744, + 3760 => U3760, + 3776 => U3776, + 3792 => U3792, + 3808 => U3808, + 3824 => U3824, + 3840 => U3840, + 3856 => U3856, + 3872 => U3872, + 3888 => U3888, + 3904 => U3904, + 3920 => U3920, + 3936 => U3936, + 3952 => U3952, + 3968 => U3968, + 3984 => U3984, + 4000 => U4000, + 4016 => U4016, + 4032 => U4032, + 4048 => U4048, + 4064 => U4064, + 4080 => U4080, + } + + // ML-DSA sizes + impl_array_sizes! { + ml_dsa, + 2420 => U2420, + 3309 => U3309, + 4480 => U4480, + 4544 => U4544, + 4595 => U4595, + 4627 => U4627, + 4896 => U4896, + } + + // SLH-DSA sizes + impl_array_sizes! { + slh_dsa, + 7856 => U7856, + 16224 => U16224, + 17088 => U17088, + 29792 => U29792, + 35664 => U35664, + 49856 => U49856, + } + + // Kemeleon ML-KEM Encoding sizes + impl_array_sizes! { + kemeleon, + 749 => U749, + 781 => U781, + 877 => U877, + 1124 => U1124, + 1156 => U1156, + 1252 => U1252, + 1498 => U1498, + 1530 => U1530, + 1658 => U1658, + } + + // LMS sizes + impl_array_sizes! { + lms, + 2047 => U2047, + 2180 => U2180, + 4292 => U4292, + 8516 => U8516, + } + + // Frodo sizes + impl_array_sizes! { + frodokem, + 9616 => U9616, + 19888 => U19888, + 9720 => U9720, + 9752 => U9752, + 15632 => U15632, + 31296 => U31296, + 15744 => U15744, + 15792 => U15792, + 21520 => U21520, + 43088 => U43088, + 21632 => U21632, + 21696 => U21696, + } + + // HKDF-Expand common max output sizes + impl_array_sizes! { + hkdf_expand_max_output, + 8160 => U8160, + 12240 => U12240, + 16320 => U16320, + } + + // HQC-KEM sizes (FIPS 207) + pub type U2241 = uint!(1 0 0 0 0 0 1 1 0 0 0 1); + pub type U4433 = uint!(1 0 0 0 1 0 1 0 1 0 0 0 1); + pub type U4514 = uint!(0 1 0 0 0 1 0 1 1 0 0 0 1); + pub type U7237 = uint!(1 0 1 0 0 0 1 0 0 0 1 1 1); + pub type U8978 = uint!(0 1 0 0 1 0 0 0 1 1 0 0 0 1); + pub type U14421 = uint!(1 0 1 0 1 0 1 0 0 0 0 1 1 1); + + impl_array_sizes! { + hqc, + 2241 => U2241, + 4433 => U4433, + 4514 => U4514, + 7237 => U7237, + 8978 => U8978, + 14421 => U14421, + } +} diff --git a/hqc-kem/hybrid-array-patch/src/traits.rs b/hqc-kem/hybrid-array-patch/src/traits.rs new file mode 100644 index 0000000..7c9f183 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/src/traits.rs @@ -0,0 +1,199 @@ +//! Trait definitions. + +use crate::Array; +use core::{ + borrow::{Borrow, BorrowMut}, + fmt::Debug, + ops::{Index, IndexMut, Range}, +}; +use typenum::Unsigned; + +/// Trait which associates a [`usize`] size and `ArrayType` with a +/// `typenum`-provided [`Unsigned`] integer. +/// +/// # Safety +/// +/// `ArrayType` MUST be an array with a number of elements exactly equal to +/// [`Unsigned::USIZE`]. Breaking this requirement will cause undefined behavior. +/// +/// NOTE: This trait is effectively sealed and can not be implemented by third-party crates. +/// It is implemented only for a number of types defined in [`typenum::consts`]. +pub unsafe trait ArraySize: Unsigned + Debug { + /// Array type which corresponds to this size. + /// + /// This is always defined to be `[T; N]` where `N` is the same as + /// [`ArraySize::USIZE`][`typenum::Unsigned::USIZE`]. + type ArrayType: AssocArraySize + + AsRef<[T]> + + AsMut<[T]> + + Borrow<[T]> + + BorrowMut<[T]> + + From> + + Index + + Index> + + IndexMut + + IndexMut> + + Into> + + IntoIterator; +} + +/// Associates an [`ArraySize`] with a given type. Can be used to accept `[T; N]` const generic +/// arguments and convert to [`Array`] internally. +/// +/// This trait is also the magic glue that makes the [`ArrayN`][`crate::ArrayN`] type alias work. +/// +/// # Example +/// +/// ``` +/// use hybrid_array::{ArrayN, AssocArraySize}; +/// +/// pub fn example(bytes: &[u8; N]) +/// where +/// [u8; N]: AssocArraySize + AsRef> +/// { +/// // _arrayn is ArrayN +/// let _arrayn = bytes.as_ref(); +/// } +/// ``` +pub trait AssocArraySize: Sized { + /// Size of an array type, expressed as a [`typenum`]-based [`ArraySize`]. + type Size: ArraySize; +} + +impl AssocArraySize for Array +where + U: ArraySize, +{ + type Size = U; +} + +/// Obtain an `&Array` reference for a given type. +/// +/// This provides functionality equivalent to `AsRef` or `Borrow`, but is deliberately +/// implemented as its own trait both so it can leverage [`AssocArraySize`] to determine the +/// array size, and also to avoid inference problems that occur when third party impls of traits +/// like [`AsRef`] and [`Borrow`] are added to `[T; N]`. +/// +/// # Usage with `[T; N]` +/// +/// ``` +/// use hybrid_array::{Array, ArraySize, AsArrayRef}; +/// +/// pub fn getn_hybrid(arr: &Array, n: usize) -> &T { +/// &arr[2] +/// } +/// +/// pub fn getn_generic(arr: &[T; N], n: usize) -> &T +/// where +/// [T; N]: AsArrayRef +/// { +/// getn_hybrid(arr.as_array_ref(), n) +/// } +/// +/// let array = [0u8, 1, 2, 3]; +/// let x = getn_generic(&array, 2); +/// assert_eq!(x, &2); +/// ``` +pub trait AsArrayRef: AssocArraySize { + /// Converts this type into an immutable [`Array`] reference. + fn as_array_ref(&self) -> &Array; +} + +/// Obtain a `&mut Array` reference for a given type. +/// +/// Companion trait to [`AsArrayRef`] for mutable references, equivalent to [`AsMut`] or +/// [`BorrowMut`]. +pub trait AsArrayMut: AsArrayRef { + /// Converts this type into a mutable [`Array`] reference. + fn as_array_mut(&mut self) -> &mut Array; +} + +impl AsArrayRef for Array +where + U: ArraySize, +{ + fn as_array_ref(&self) -> &Self { + self + } +} + +impl AsArrayMut for Array +where + U: ArraySize, +{ + fn as_array_mut(&mut self) -> &mut Self { + self + } +} + +impl AsArrayRef for [T; N] +where + Self: AssocArraySize, + U: ArraySize = Self>, +{ + fn as_array_ref(&self) -> &Array { + self.into() + } +} + +impl AsArrayMut for [T; N] +where + Self: AssocArraySize, + U: ArraySize = Self>, +{ + fn as_array_mut(&mut self) -> &mut Array { + self.into() + } +} + +/// Extension trait for `[T]` providing methods for working with [`Array`]. +pub trait SliceExt: sealed::Sealed { + /// Get a reference to an array from a slice, if the slice is exactly the size of the array. + /// + /// Returns `None` if the slice's length is not exactly equal to the array size. + fn as_hybrid_array(&self) -> Option<&Array>; + + /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the + /// array. + /// + /// Returns `None` if the slice's length is not exactly equal to the array size. + fn as_mut_hybrid_array(&mut self) -> Option<&mut Array>; + + /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning + /// of the slice, and a remainder slice with length strictly less than `U`. + /// + /// # Panics + /// If `U` is 0. + fn as_hybrid_chunks(&self) -> (&[Array], &[T]); + + /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning + /// of the slice, and a remainder slice with length strictly less than `U`. + /// + /// # Panics + /// If `U` is 0. + fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]); +} + +impl SliceExt for [T] { + fn as_hybrid_array(&self) -> Option<&Array> { + Array::slice_as_array(self) + } + + fn as_mut_hybrid_array(&mut self) -> Option<&mut Array> { + Array::slice_as_mut_array(self) + } + + fn as_hybrid_chunks(&self) -> (&[Array], &[T]) { + Array::slice_as_chunks(self) + } + + fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]) { + Array::slice_as_chunks_mut(self) + } +} + +impl sealed::Sealed for [T] {} + +mod sealed { + pub trait Sealed {} +} diff --git a/hqc-kem/hybrid-array-patch/tests/ctutils.rs b/hqc-kem/hybrid-array-patch/tests/ctutils.rs new file mode 100644 index 0000000..86896b0 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/tests/ctutils.rs @@ -0,0 +1,42 @@ +//! Tests for `ctutils` integration. + +#![cfg(feature = "ctutils")] + +use ctutils::{Choice, CtAssign, CtEq, CtSelect}; +use hybrid_array::{Array, typenum::U3}; + +#[test] +fn ct_assign() { + let a: Array = Array([0, 0, 0]); + let b: Array = Array([1, 2, 3]); + let mut c = a; + + c.ct_assign(&b, Choice::FALSE); + assert_eq!(a, c); + + c.ct_assign(&b, Choice::TRUE); + assert_eq!(b, c); +} + +#[test] +fn ct_eq() { + let a: Array = Array([0, 0, 0]); + let b: Array = Array([1, 2, 3]); + + assert!(a.ct_eq(&a).to_bool()); + assert!(!a.ct_ne(&a).to_bool()); + assert!(!a.ct_eq(&b).to_bool()); + assert!(a.ct_ne(&b).to_bool()); +} + +#[test] +fn ct_select() { + let a: Array = Array([0, 0, 0]); + let b: Array = Array([1, 2, 3]); + + let c = a.ct_select(&b, Choice::FALSE); + assert_eq!(a, c); + + let d = a.ct_select(&b, Choice::TRUE); + assert_eq!(b, d); +} diff --git a/hqc-kem/hybrid-array-patch/tests/mod.rs b/hqc-kem/hybrid-array-patch/tests/mod.rs new file mode 100644 index 0000000..0973d98 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/tests/mod.rs @@ -0,0 +1,201 @@ +#![allow(missing_docs, clippy::cast_possible_truncation, clippy::unwrap_used)] + +use core::mem::MaybeUninit; +use hybrid_array::{Array, ArrayN}; +use typenum::{U0, U2, U3, U4, U5, U6, U7}; + +const EXAMPLE_SLICE: &[u8] = &[1, 2, 3, 4, 5, 6]; + +/// Ensure `ArrayN` works as expected. +const _FOO: ArrayN = Array([1, 2, 3, 4]); + +#[test] +fn tryfrom_slice_for_clonable_array() { + assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); + assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); + + let array_ref = Array::::try_from(EXAMPLE_SLICE).expect("slice contains 6 bytes"); + assert_eq!(&*array_ref, EXAMPLE_SLICE); + + assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); +} + +#[test] +fn tryfrom_slice_for_array_ref() { + assert!(<&Array>::try_from(EXAMPLE_SLICE).is_err()); + assert!(<&Array::>::try_from(EXAMPLE_SLICE).is_err()); + + let array_ref = <&Array>::try_from(EXAMPLE_SLICE).expect("slice contains 6 bytes"); + assert_eq!(array_ref.as_slice(), EXAMPLE_SLICE); + + assert!(<&Array::>::try_from(EXAMPLE_SLICE).is_err()); +} + +#[test] +fn slice_as_array() { + type A = Array; + assert_eq!(A::slice_as_array(&[]), None); + assert_eq!(A::slice_as_array(&[1]), None); + assert_eq!(A::slice_as_array(&[1, 2]), Some(&Array([1, 2]))); + assert_eq!(A::slice_as_array(&[1, 2, 3]), None); +} + +#[test] +fn slice_as_mut_array() { + type A = Array; + assert_eq!(A::slice_as_mut_array(&mut []), None); + assert_eq!(A::slice_as_mut_array(&mut [1]), None); + assert_eq!(A::slice_as_mut_array(&mut [1, 2]), Some(&mut Array([1, 2]))); + assert_eq!(A::slice_as_mut_array(&mut [1, 2, 3]), None); +} + +#[test] +fn concat() { + let prefix = Array::::try_from(&EXAMPLE_SLICE[..2]).unwrap(); + let suffix = Array::::try_from(&EXAMPLE_SLICE[2..]).unwrap(); + + let array = prefix.concat(suffix); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +fn split() { + let array = Array::::try_from(EXAMPLE_SLICE).unwrap(); + let (prefix, suffix) = array.split::(); + + assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..2]); + assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[2..]); +} + +#[test] +fn split_ref() { + let array = Array::::try_from(EXAMPLE_SLICE).unwrap(); + let (prefix, suffix) = array.split_ref::(); + + assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..3]); + assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[3..]); +} + +#[test] +fn split_ref_mut() { + let array = &mut Array::::try_from(EXAMPLE_SLICE).unwrap(); + let (prefix, suffix) = array.split_ref_mut::(); + + assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..4]); + assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[4..]); +} + +#[test] +fn from_ref() { + let n = 42u64; + let array = Array::from_ref(&n); + assert_eq!(array[0], n); +} + +#[test] +fn from_mut() { + let mut n = 42u64; + let array = Array::from_mut(&mut n); + array[0] = 43; + assert_eq!(n, 43); +} + +#[test] +fn from_fn() { + let array = Array::::from_fn(|n| (n + 1) as u8); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +fn try_from_fn() { + let array = Array::::try_from_fn::<()>(|n| Ok((n + 1) as u8)).unwrap(); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); + + let err = Array::::try_from_fn::<&'static str>(|_| Err("err")) + .err() + .unwrap(); + + assert_eq!(err, "err"); +} + +#[test] +fn from_iterator_correct_size() { + let array: Array = EXAMPLE_SLICE.iter().copied().collect(); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +#[should_panic] +fn from_iterator_too_short() { + let _array: Array = EXAMPLE_SLICE.iter().copied().collect(); +} + +#[test] +#[should_panic] +fn from_iterator_too_long() { + let _array: Array = EXAMPLE_SLICE.iter().copied().collect(); +} + +#[test] +fn try_from_iterator_correct_size() { + let array = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()).unwrap(); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +fn try_from_iterator_too_short() { + let result = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()); + assert!(result.is_err()); +} + +#[test] +fn try_from_iterator_too_long() { + let result = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()); + assert!(result.is_err()); +} + +#[test] +fn maybe_uninit() { + let mut uninit_array = Array::, U6>::uninit(); + + for i in 0..6 { + uninit_array[i].write(EXAMPLE_SLICE[i]); + } + + let array = unsafe { uninit_array.assume_init() }; + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +fn map() { + let base = Array::::from([1, 2, 3, 4]); + let expected = Array::::from([2, 3, 4, 5]); + assert_eq!(base.map(|item| u16::from(item) + 1), expected); +} + +#[test] +#[allow(deprecated)] +fn clone_from_slice() { + let array = Array::::clone_from_slice(EXAMPLE_SLICE); + assert_eq!(array.as_slice(), EXAMPLE_SLICE); +} + +#[test] +fn slice_as_flattened() { + let slice: &mut [Array] = &mut [Array([1, 2, 3, 4]), Array([5, 6, 7, 8])]; + assert_eq!( + Array::slice_as_flattened_mut(slice), + &mut [1, 2, 3, 4, 5, 6, 7, 8] + ); + assert_eq!(Array::slice_as_flattened(slice), &[1, 2, 3, 4, 5, 6, 7, 8]); +} + +#[test] +#[cfg(feature = "zerocopy")] +#[allow(unused)] +fn zerocopy_traits() { + use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; + struct Check(T); + let ok: Check> = Check(Array([1, 2, 3, 4, 5])); + // let not_unaligned: Check::> = Check(Array([1, 2, 3, 4, 5])); +} diff --git a/hqc-kem/hybrid-array-patch/tests/subtle.rs b/hqc-kem/hybrid-array-patch/tests/subtle.rs new file mode 100644 index 0000000..ef87023 --- /dev/null +++ b/hqc-kem/hybrid-array-patch/tests/subtle.rs @@ -0,0 +1,29 @@ +//! Tests for `subtle` crate integration. + +#![cfg(feature = "subtle")] + +use hybrid_array::{Array, typenum::U3}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; + +#[test] +fn constant_time_eq() { + let a: Array = Array([0, 0, 0]); + let b: Array = Array([1, 2, 3]); + + assert!(bool::from(a.ct_eq(&a))); + assert!(!bool::from(a.ct_ne(&a))); + assert!(!bool::from(a.ct_eq(&b))); + assert!(bool::from(a.ct_ne(&b))); +} + +#[test] +fn conditional_select() { + let a: Array = Array([0, 0, 0]); + let b: Array = Array([1, 2, 3]); + + let c = Array::conditional_select(&a, &b, Choice::from(0)); + assert_eq!(a, c); + + let d = Array::conditional_select(&a, &b, Choice::from(1)); + assert_eq!(b, d); +} diff --git a/hqc-kem/kat/hqc-1.rsp b/hqc-kem/kat/hqc-1.rsp new file mode 100644 index 0000000..176f8ab --- /dev/null +++ b/hqc-kem/kat/hqc-1.rsp @@ -0,0 +1,9 @@ +# HQC-1 + +count = 0 +seed = 9EF877FDDBE8891C6E4E79EAF022E563DEFACA6B152161B9A423E8FE96A403E774B2D352CF74C934069C9DE74757F505 +pk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEBF6F02DD0EBAA1059A6E8944703770A74496DAF9E0B255E97A16B577C61B8FB99F3D11A8DB672C05AF484B70C354C56518AE0D0B093C0811F1134BBFD8358847F18051749AE26476DD2C304A4CA3FB5621D4CAB0407673819B4433AA0BBA0B3F9DFF1572B8EF8F0938CC83991246ACE5862584537C9F89BC93311CE076F91FDDB95AC619B8DF5F5DD415667BE349716FF91EAAABE6C828BFDF52E403F5C99FAA55C36FC8F3E2ACCCC39474C0EE8FF33E0EE55A19C14BEB031E14855CB4A56010A7A42486A042738D0FDF358C951F703C488B19AA52A30B894C176D639D5D7459727B9BB59BA73A88E560382D4A6FC55424E08A0DB8F88F9BF5809EC94C06258DBDFA25CC0419052603C446E33D9310EF040498B8C683C399C7569BBF265F7BBD587D8551DCC62EC1F147E996B73D867374F4FEE2036C637F68B9B81BBE8A69CB19F6FCA579FD1687EF949CB4B20771DEEB8451CCDD68BA97CE55DCECBE1B09D215265E77A73254B5ED2BAE89D05708AE98E170664D9F79D593FA01008AC65A8D74661337D6C1244A274A755877D4FE5CB38BB8F1A0863263099FF235B082AEE55A107EA54B06385F56662632EDD825109A55A08799E5E3210E862FEBF294F1DA84265887B8D068CC961E28D12B0860D9634D8CD285884FBB81B693B202592527E6E6A68FC012F06947AE85656D3E664140EFBDA95B0DDE925B02D8EE38653EC448D88199970E35C5CFC6D962EF7CB883C248505AA5CE7FEED89F7866CD00099428C7EA12B0B8493F21804AD44E6808F427E0931CE7F33C35D3990334C354835210319C5E1824AF471D6F7376143C5BC81F543D38909B3A5CB790CAF94CD48F00574EC44AECDB710D3E6BEF83AF8151B6CD669D520609D705C6597AE7D646D4EDABDB0A0E959EAFBFBB3E4186D0BFA41439D926189EA355E986C60F2DB8B83FC086820552ED756C63BB7B958CE401EF84C7D6E6404403A56D07D7BB74D727D603A4DC3442CAA79160F5FBDF3228BA401AC23E5E32B675540E195C1107CD76C5EB8962B1C5D574E8B5967E24D34B135AACF5CE1DF6EAA6CADC9EF561C521684B83E5B0DDBC355091C1C15952ADB88F0267A7904E0C7108071DCD1FD94317DE55AEBD9D85EC7A2F78A00C7F76F07164AEA4FE60332D5950D7838FED0E35A5D1146B4A92A2BC3B9CD8FE3F85070A85BCF8561229A55FB18BF0D2C0F40E3C8A1EA08F2A14D4B7204908D1330543660562188D25E6F8098556C152F5CB9E0D5F8D4F93FE7B2F9946AFF7847B9E49C78F6D1E83FEAFAE9323EB03E483AD52983F538A5B01A7B9D23A6178FC5288F8DDB61E7B284A55974BD71A05560AE8D72F2341E28A7009D2A27205524F44F7DBAD2C55BED9E8B9D54B9C0B48BD4BD9E1E18C5EC3BB1BD67E8AC4C3F7B61E7205112E1A8183AFB760BFD66603BB54CF9E8747CA990701A9581A25A798653F3AC2BC8B0958DCB3DEEB92AEB0844B1D1F00E9A85CF9E9C127B47FDB57B132DBF1086675307DCD6EE2C18C3E48B1509AB1E9B4F64741753D50A4EA6CA15ACF1CAF57A736C364894F8144133679EE7C285408F0DFA1DD6482F592A196E45AFA07A4557D28DB85C6F030E8FDD8353B65D50269B4C7F2E7F4C96344146453651CFE2BD144AE05672FEE5466804BDEC9438B518C2F8B8169A116886BD04365F37790775DC465E584F1E98C9CC85950041B5C658E771A8D40DE18A24F4B1FC1FDC402FDC9B91420A6A506CA9A452A9CC7C157B175E549D5BAA55C9614E9E2A05CB4215610E674F3FD5DABB65BAC2514EE120CB0C5AAB1616DE54B17863F312B48C826C05A54E41E2428794FB0288F24DCAA27C445044A2BD3757F017FB5A261078670DFE3F81647FD39DDACE082B0FBD772BBC2327C7480076465D715A0A6192784B14A30EE965914E3658BE0C72CC9DCAE96D13BB8AF0D55D59A58704B59860ADB8741C24A54980FC62362FAB74673F61DA346D01A852F43AE5C9A6AC908814910AD3542C76B2A5FB20C68287CC7BC9F87BAFC43D6CA9B6E3D90CD681FD18E73E5691A18AF2ADFF34F794F4EA2C48D6E9C315F62125C1D7C74C21B37A717EEEDA109F7F1D75E9C4F98E597EA61AE373D5C678BA6D258FB89A3E10AAD04DD931B97B8CC33A36959C67DCBD94549CAD38DF43DE03444399F8FEC5AF2C59ECA0603550A9627AD74C0F2EB5D52C5DE5FAF3737E512A7D81A51B9E683B33D091E5C970BA90CA0C6351E4A268DC30622B0C130AB67B2A156026BA6F0817CD134555895BD7F632C611DE487E9451DC9D64ED61558BB2294EC0C400CEDC9B502D0D959568CD7F3F07116ABE29B62FB4DC0347093B5B4473A4BBBD74776BCE46283C0C2ECD13E60318E6AB7EFF76E0F64CDAD56D53A0B1E7F88B57497B834E5837C49EC45F1916CAB67F9C3686DF77A974A0E88E29F352FF13D74F3A9AF7749DFE0AA4E7A089B096C162F9AA84A1183C1526FFA62EB3722CFE1FDD93AFE5DEF7CE5E12F37FF1C929484E7E25B23B22A47633D801272B8A8E6CFB9F63971E665A679E622F494C83463F45CC80C51303C008357ADAF12618A9964E6C12C6A13CB0530FBF03B030F82FB1D1D23913266B69E5305D199BCB32676875CA1287E1EAC976AA4148362BCF9CBE5BC6CD1AB5277A1FF1172D893317CE4050FE8D5830BBFD598767C5488665419600D4423ECF5A7C99CD6F1EA9D1FDDFCD49B6D9FB78C4EAB73CEE27755E66E0A529CDE6423B4C13F5160DE8639AC25BCE3F9A4AA2CCB5F6774CA6775636FC07BAEE42B18871CBBD25EFF40106C9CB66B39D226516E2AEC92FD1D840870E2DB8615FB5B7EDB8EB81F79907AD51320E06840943FDE537FA2FA756A40CB13C30439F0C98BE795C835280BDD29A32EBBBC1C03D54EFA161EC4C3ECAD5CC269C3C1CC5C85E8F60D49D3B22ACDA6E95E22A4573CA1095E4B59595E496A11AEF6484A54549B1C709697E2AAAF4EC5048B126EBBA1DA27E5AC4EC4AA04B2F25C11458FCF442E602270FEE03AF19335B24583B9595DDD9A6BEB642A92D85D61224619A198F500565F3F4D702E6AD3E93A107A9DCC0BA762322A6D578E2128C6418E8733B1167F94001E212A26FF940A5299D31BE7AA1BB9681807 +sk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEBF6F02DD0EBAA1059A6E8944703770A74496DAF9E0B255E97A16B577C61B8FB99F3D11A8DB672C05AF484B70C354C56518AE0D0B093C0811F1134BBFD8358847F18051749AE26476DD2C304A4CA3FB5621D4CAB0407673819B4433AA0BBA0B3F9DFF1572B8EF8F0938CC83991246ACE5862584537C9F89BC93311CE076F91FDDB95AC619B8DF5F5DD415667BE349716FF91EAAABE6C828BFDF52E403F5C99FAA55C36FC8F3E2ACCCC39474C0EE8FF33E0EE55A19C14BEB031E14855CB4A56010A7A42486A042738D0FDF358C951F703C488B19AA52A30B894C176D639D5D7459727B9BB59BA73A88E560382D4A6FC55424E08A0DB8F88F9BF5809EC94C06258DBDFA25CC0419052603C446E33D9310EF040498B8C683C399C7569BBF265F7BBD587D8551DCC62EC1F147E996B73D867374F4FEE2036C637F68B9B81BBE8A69CB19F6FCA579FD1687EF949CB4B20771DEEB8451CCDD68BA97CE55DCECBE1B09D215265E77A73254B5ED2BAE89D05708AE98E170664D9F79D593FA01008AC65A8D74661337D6C1244A274A755877D4FE5CB38BB8F1A0863263099FF235B082AEE55A107EA54B06385F56662632EDD825109A55A08799E5E3210E862FEBF294F1DA84265887B8D068CC961E28D12B0860D9634D8CD285884FBB81B693B202592527E6E6A68FC012F06947AE85656D3E664140EFBDA95B0DDE925B02D8EE38653EC448D88199970E35C5CFC6D962EF7CB883C248505AA5CE7FEED89F7866CD00099428C7EA12B0B8493F21804AD44E6808F427E0931CE7F33C35D3990334C354835210319C5E1824AF471D6F7376143C5BC81F543D38909B3A5CB790CAF94CD48F00574EC44AECDB710D3E6BEF83AF8151B6CD669D520609D705C6597AE7D646D4EDABDB0A0E959EAFBFBB3E4186D0BFA41439D926189EA355E986C60F2DB8B83FC086820552ED756C63BB7B958CE401EF84C7D6E6404403A56D07D7BB74D727D603A4DC3442CAA79160F5FBDF3228BA401AC23E5E32B675540E195C1107CD76C5EB8962B1C5D574E8B5967E24D34B135AACF5CE1DF6EAA6CADC9EF561C521684B83E5B0DDBC355091C1C15952ADB88F0267A7904E0C7108071DCD1FD94317DE55AEBD9D85EC7A2F78A00C7F76F07164AEA4FE60332D5950D7838FED0E35A5D1146B4A92A2BC3B9CD8FE3F85070A85BCF8561229A55FB18BF0D2C0F40E3C8A1EA08F2A14D4B7204908D1330543660562188D25E6F8098556C152F5CB9E0D5F8D4F93FE7B2F9946AFF7847B9E49C78F6D1E83FEAFAE9323EB03E483AD52983F538A5B01A7B9D23A6178FC5288F8DDB61E7B284A55974BD71A05560AE8D72F2341E28A7009D2A27205524F44F7DBAD2C55BED9E8B9D54B9C0B48BD4BD9E1E18C5EC3BB1BD67E8AC4C3F7B61E7205112E1A8183AFB760BFD66603BB54CF9E8747CA990701A9581A25A798653F3AC2BC8B0958DCB3DEEB92AEB0844B1D1F00E9A85CF9E9C127B47FDB57B132DBF1086675307DCD6EE2C18C3E48B1509AB1E9B4F64741753D50A4EA6CA15ACF1CAF57A736C364894F8144133679EE7C285408F0DFA1DD6482F592A196E45AFA07A4557D28DB85C6F030E8FDD8353B65D50269B4C7F2E7F4C96344146453651CFE2BD144AE05672FEE5466804BDEC9438B518C2F8B8169A116886BD04365F37790775DC465E584F1E98C9CC85950041B5C658E771A8D40DE18A24F4B1FC1FDC402FDC9B91420A6A506CA9A452A9CC7C157B175E549D5BAA55C9614E9E2A05CB4215610E674F3FD5DABB65BAC2514EE120CB0C5AAB1616DE54B17863F312B48C826C05A54E41E2428794FB0288F24DCAA27C445044A2BD3757F017FB5A261078670DFE3F81647FD39DDACE082B0FBD772BBC2327C7480076465D715A0A6192784B14A30EE965914E3658BE0C72CC9DCAE96D13BB8AF0D55D59A58704B59860ADB8741C24A54980FC62362FAB74673F61DA346D01A852F43AE5C9A6AC908814910AD3542C76B2A5FB20C68287CC7BC9F87BAFC43D6CA9B6E3D90CD681FD18E73E5691A18AF2ADFF34F794F4EA2C48D6E9C315F62125C1D7C74C21B37A717EEEDA109F7F1D75E9C4F98E597EA61AE373D5C678BA6D258FB89A3E10AAD04DD931B97B8CC33A36959C67DCBD94549CAD38DF43DE03444399F8FEC5AF2C59ECA0603550A9627AD74C0F2EB5D52C5DE5FAF3737E512A7D81A51B9E683B33D091E5C970BA90CA0C6351E4A268DC30622B0C130AB67B2A156026BA6F0817CD134555895BD7F632C611DE487E9451DC9D64ED61558BB2294EC0C400CEDC9B502D0D959568CD7F3F07116ABE29B62FB4DC0347093B5B4473A4BBBD74776BCE46283C0C2ECD13E60318E6AB7EFF76E0F64CDAD56D53A0B1E7F88B57497B834E5837C49EC45F1916CAB67F9C3686DF77A974A0E88E29F352FF13D74F3A9AF7749DFE0AA4E7A089B096C162F9AA84A1183C1526FFA62EB3722CFE1FDD93AFE5DEF7CE5E12F37FF1C929484E7E25B23B22A47633D801272B8A8E6CFB9F63971E665A679E622F494C83463F45CC80C51303C008357ADAF12618A9964E6C12C6A13CB0530FBF03B030F82FB1D1D23913266B69E5305D199BCB32676875CA1287E1EAC976AA4148362BCF9CBE5BC6CD1AB5277A1FF1172D893317CE4050FE8D5830BBFD598767C5488665419600D4423ECF5A7C99CD6F1EA9D1FDDFCD49B6D9FB78C4EAB73CEE27755E66E0A529CDE6423B4C13F5160DE8639AC25BCE3F9A4AA2CCB5F6774CA6775636FC07BAEE42B18871CBBD25EFF40106C9CB66B39D226516E2AEC92FD1D840870E2DB8615FB5B7EDB8EB81F79907AD51320E06840943FDE537FA2FA756A40CB13C30439F0C98BE795C835280BDD29A32EBBBC1C03D54EFA161EC4C3ECAD5CC269C3C1CC5C85E8F60D49D3B22ACDA6E95E22A4573CA1095E4B59595E496A11AEF6484A54549B1C709697E2AAAF4EC5048B126EBBA1DA27E5AC4EC4AA04B2F25C11458FCF442E602270FEE03AF19335B24583B9595DDD9A6BEB642A92D85D61224619A198F500565F3F4D702E6AD3E93A107A9DCC0BA762322A6D578E2128C6418E8733B1167F94001E212A26FF940A5299D31BE7AA1BB9681807374B10C73F79FA08D0731BE4F21356D191782EB1D10DEEA5929523B3B4D6D97BF397572E7CEAC24CD55009F822EBE800CEFC0D60050E04C3171859E54BA888D2F670E22EBE926B0B307A65264FBC08F8 +ct = B14D1AEEB2A88F27BFED21FF0E6C60F378663DED9C8B69A55488D0D140061E766EABAB01C583058B7A4468C13B4265560323B98192A492CF09B6B1DF57158E259B5BB7A813BEFDDE33B4AD6C90B430EFC74FFF2077A1CF2E4166F5BBCD56A516A5494458EEE1DF02FBF9AE2EB0D3BEB1FE86E4C97C281703F50A0113BD760C6EEEE362673A2629807710AB153125E4912F695C91B9137715AE9F6F036626D025FF60D1647424B20340BDFC5595AE5089594098BE4D1C028EBE9C55472FA395C7AE9E603CE09A470DA4BDE7496A49FBF1B6FCE5A37723D06E7A249E24051BE6E6AF8DFC018504110C45B2781A8E40D1B485605D7A0F01660107DBDABA620172F55898BCCD85FF420667CBBA901862E7C2D3F5288379D25DDC58F45E0DEC4610332CCAC9D1D06F32B6C73F94A314D4DBAF66AE9D66B5E3D3D7507BD3FD976502BEA5AF9F2DE26A69FF49DE36DB1495CD83EA3D74C436CDF9CB29E73BDECE670923FB8BC2EB762DA17EC2429B415A57DF0CD085F19E8B97A210102C766904D4CF99800EDB62EB416D2538309E14EB8913E4C72D68F27A8DAC93BFE1119E82BD7599EFD7FD7CA1F0FB2D24F7C46C60B244CE3E8941CEAAA6A0D39CA4F7A493F722ECB9BA1DC26783B858CA0BD4807C2A335AC14D2801B42351B9068BD9F216F21F7840F4D3E9E13A4FD2FD8DA0E67CA44CCA3A6839BAC423FEA0E8B1886677B5CA20B45DA3AD36F19E064714E4FE39CDFC1C4AC7AD8598ECA0ABBD4F8EB69BA125EFC2800978E11796A0C8F6A88F4D22ED1C59111F00C41EF11E9DD7E2DB892875148BF29D2DFBA92D93A16E07E721E1B02726799C09094EB43B2168B60AD630595E23BEF8197258187D5FA623AD18E1F482FFC30BB5707C4EAA266FB2156BCD034E340FB735AC0DDC60B7FC8F699338AC7D8954189582A7113854EE23062F05FA287941E71E66CCED457BE9B5FC36E9EE313A84F4750823D2354F71227FBD7CF746822F7FD4F580BA03CE49DE34BF70AD7BA08EB4898E8BC188F11D2918BEC122AC085C2CD2420242FAB9571F2B27F69B1291D6920CA1D455486FB1EB00896217AD6C5DAF33688B6EFF0D9DB3A2C7EFF1422AFE1F54C90DED4855F4933C0E8B8D9F022D50FB2720680C4E28A8DB4FCC8174CED5B2CCB5E608137DE71BADD62B90B576A12474DD9C9A7CF03CD47985B5CB45E18B09CE3C2D0D7A33F50A364E07339B2DD45E278B5C65FDAC41EF812A860A77939345EC148CF0CB54CD89E81C1A864584CDCD6B1BEE2C0B4D94E80680DE23F77FFC96F1EED5F7739B5ECF517EE64DC3314A1229A043835DF3E2B9F450D15BA860E6236560785C562D0F04D8C7BE5C5FC3BE2434486F213571C64BA069AD20D0C1D6C324E0746F6BFACC55CB529A5829CAE1100F48C21C86E045C30FBF4E1110599834111ABBF399E0EBB75B7B239DA19129EFCD1900A3B6F73491CFA9CEF71612AACA153073AA68A266C05F516759D64DD2AC8DB9CE2F3E4432662A298BD61714ECFB3981230462B82E5B45A37F28801ED06B52A73ADB616BBFE327FF1ED5FD5478EDC2990A1F494F4D3F241E0D3A2E4754CC2A321C51FAC80D2BE986FEFA4AA420047E8455165F3D03E49ED843C74C673C06C0FB036FD05372FD8EEC281AB0064CDE4A50B412E76F0737D608343FE7D7B80F4C5750D1BF1D97B5D981E21A0E233F7465AC0996554F74705B8BD3A7E19D3724892409056A1E20A8B0CA95D8C2E8D33D27900C2D476C5CBE9BBBF97FED0F87373FB7ACD81C4472DF6B869D7BBC56B39B840196B6EB03FA54B41BC64F47545A10327DF4DB89C3D4C46817F4920C96A8014C2F4AACD5D585A8A5F23A050FBA67DAA65AC80F63BED4DBE516C9DCDF99436BC60B25C2D39D4D3E302A42E4E308C1ECE1EB5181EF308BD1D04C0338FEC5CA54B20651AC9A8D9E01ED205699B0F038357C130E8143CBBC4A5D897BC347EE502DB881F2B41EA8C8B6C6E981BB6ED2D53C31FCAF062F5253E7A8B7325555DAB237DA8EAAE98A77938B77BC1F6C7F34CB1A228CA5CD07F2B785B5C2F6E40271CBDDF0755D6D16FFF8D515592F21B7B607F27882C844F328D3F46A7986E1495BBB383EAADC9B9298F2E01A9CB3B2047C10F3847F6E60E5CABBA37E09E3E39151FCCEA2A1393EDF12E284C855AB6B03844A352FA4F868EBB5F154B895302961ACB85CCA922608138F5242F36FEF0CD9FD0BF587C9ADC51A79DA89EBB803E9FF5214ABCE3B2DFB90942B432CA90B45EAA06915AF246B84A36A753D2DF6D26BD523580F1BA521DC0283AADEC31460B9C700248A8E731758243B1039B00E90FCF7137E1C6C4B8A1C1BF7374E267B9627010A10A4F7799CE56D7C846D0DEA0C54C119B8DEFBFF9C9F86A9C5D5E1F082E7A3C51C63E405CF4E376B60FB1FCA850ECAF4FE7184C50D94ED3E2ABABCBC1762D92E6F28CF2B0439DD1C1E5E492DFA88049B5F6F6108AD28F59F0E896340DA9E741FE223B81C1CC37BD53798D8F38E6BE469226732669C0C5B86C653505DC5051FE332B1D4EF72DEDA64154F34BC0FF2D8939BED418ADF323326D97BB8ACE6637E072890363F7BDCAACCB88B67280632E5FCA8DD5738D1A13351DEDD80BC59C3A953489AE4A891F6D26DE522BA54D4BC80DC379C042F5C583DE2BB2B477953CDD7A8EA89F7E8DD7F25CC319FF98117272C894DB6F72CED2BF7DB98D42BEDAD6A82A0D59DF702A7D8BE829B9F452218F6FFEDD902FD8D903C6BADCDCEF931D05B807E4623DCB02E2E97E16A02232FEC1CBF2443F13BB0F9162355D4A290AD677377A8DBA6A763C7448CAB87042EC1E6CD4B5209836D3EDF43919EE63231364A9655F2BBA0C272D1D1D19528EC330551DA74E57023985F8827098EBFE4A402DD75E5B8880E1AE6511CE2B9B9CA63F780CC8BC897E9AFBB0E44173827B0540E1AF007CF0DBE4D013786AB6397BB2F31334BE82352EA6E2A8C34EEE61C6700B16ED5E79336937EA6513CFFCA778BA194BA4E04DB49A516DE965FD2FE2568AF1F646201D5E0B395D732979F7BA6025CBAA9D4C10E7EDF56D20B119DBB7D3F8C82B546A00C83C9E66A320BCE37C08E49D0F47E9EAEEA037099905D8021EB0665B77B4F2D15BF14CE5A69C0667F31E3E4D7DEF49E3F478EAD3E059A16817B879A8FD64C081734B8C2A2755F407525DB0BACB1C989C6B806A2D979FBFA7902B03358AFAA70A33A54F3E5E8BB8A4346B0FB823CBD777CC65A73BFCF5A6799E157D9456668D378A97B090EE8063273B61AE1063ECD994FC6E5123F3BC1E013329E2E9C0403A08B35651A01352041EC596E072521EC7AD66CDC3B5ACEF02B2AF574618D4A41454D57337380E785ECD0C29A81D3C788EDB2021D31C0DA098741EFAA7834EFDC17BA704CA0217F65515B3FFAD1C326BB631147DA0EB5E8FA5ED739332CD4ADD8C209CC2C307E293AFD0ED4AE045912B154712CE703AA71980CE955EAFB9CDF105047ACCC96B256E81ACC6183F3651357C6F1037FEFC7992C0A0014D3B9A96F56E60F6AFFD0CAE4653F03D7BDDA35AB3E1AA8C5CCF1CDFD79457C4BF02262724C0F54A0F241FBB4AEA2E09D897694E50C0F67892A0EACFC6EA3FF9A6AB49AD6691DA620A4E49607D426E613329418EBFC0E325CE7516034EAEDEBBDD5D79327B395E4AB30CE3AA6FC707F528D7BF0D4CC972CD8B126FE3D499DB151E770DB7597BFB4AF658D3AC7BDCDC718DFF61F2D8E313AB592AD0180591D37AB135728FCAD836BB64B9089360436575AEF9C81B4C1F33BB80ABAB33F37CF4829E1FDDE8C42114BAE0BD5F0C96BB910C08E4AE423EB524EA714A6121E1F8176414096143975CA3B66BA6DEF555623414ED5A61C92175492070AABAE9230AD3569BF804DDC923CB2EE3E833B4B660E2D03E04D57AA92E2634082EA6865269B4BEF26A3045E29FCBF64349112E3FC52F0274AA466B851D142EF07A2A944A68805C1ABEBC214AA52CEB6BEB8C79477B1D50F0F4291337858416DE4F1ABB8ADB25418A23BB905DA64741FC7B7CB0E93933536D0E3FD351979FD1AE329C6F8B7BEB4C19202CA4B4EFC5E3B5C5C548C3972A6BF3BC714A1834662CDB43DB0B2D4F4CFF4A015AF422B4C3D304E1C1CA40AA65B13E795D4D30CB835E0D69232F97C59C57349A3EDBD2036A2E1642C571AB968D0191C4DBE6571CF003C66BF536B578C8840FD6887B38D2162DCC4F54FA3361C80BE4A546BDD94BD3958BA336A4D363D2B150C14F80948999AAFE651C3F4CA33B1ED689B121540B15E2D405C5C84AF68CAC084B0EED471B650FD7E5CFEFECDF01C796051D017ADEB5CAA6346FB15A5ECB8DA7565766456F8055248F1162D4EC866CE77677DFC58BCE8BE0A9A3F1AA948EB96D451FDBA5CC8D18F76C8A835BBA81AA6A6BE2851C65F0976C2D63E4532C2869AB171DE49341B36D973E0ADA34BBB733847331D138470E04E146535565D368C1D682FA5EB71F89AFAD5428E45D44EDB21C190089701106F0BF543B0E2D2B393FD57FF10E0ABEA665FD6BB8467C623FF57D7EFA0FC46F27AEFD5ECA19EBAB7E755AF26A666694827F0B8E4DCD3026A433697C96B46E01F591C8386E56495441830AEA1A7E855EB767A64F786C40539E5467E6AF6AF2095789382939F6B1984B62B62AB793C72564B935DAD7AD5B4B586D81F94639AFE742BE6091B19525C615A88E5223977A5CCBA652F5E9597988148F1A5E61FE3564F29CDAB14248FE3D91AF2AEF31FDCC2578016C7802D9D49F4561505FA9893483E077383D588624E764AB65ACB2F23247111A2FE294693AC0BFEEE913A28A56698BA8BD46353413B748EC53AE98FF995D6F676C8133DA2DD6188ABFAA8D8D81C945B1CE9ADB0320EEDF4B37432455C9C4D61D23985A903A8181939B042E3DF97764707F58F2C371FB0ED59A83D09CD1BEE88F02153F08DFDA6BA021E39F70BBFFB5E53110A753B74F7D40EF47071B6868A88DA0646C7B2FB1441B539C3AA8F43283D3A334C4C490683532B4D35B5D49CC80E265B6CBD1672F85B895539EB575546D06F2B763FCE4C89F7CCD2DACBEDC54E054286C919A6AB495F36E93FB6FA0F0E1EBBA3E38FC05A0426C88BCA22F942E345037510BEED4ECE7EFA9B9D2324B23C6BC8BD3E1D4C93FB8B612AE987D6D3971428375F545DD2567F79A7766B4909D762D3219B9151431F9899BD3D79C99A20530D99051A97438413F79F1FA74DD5A7B9865652DDEBAA04CD1BE40F32BA33B0D5ABAEE8C8D10E8E790A4F5D10606A104AF531AD494FB8CA234BE15463589A9C9D6DAAFF67394B62637954EB20D789D9734CEC924FFC713F62FBD234FF880D39E9F436E915F59E53473925408139AFA5CEE9815717DCA9A5141A3F020EDB9B5E4C5283E281024669BE396B8EB24E0B448F3E8440E4EC8A345913257A5631C64C78F00286E64212AAEC1E72997ADCCA88EB2492EDFBAB3FA83071A53D8D710186B262333A78C7CC2071509FAF3979A4477D0893D97D362965E67CEE4002E3035307D9BF9D2756F5B3CB8E92A9A54CA92CE93EFDBE107F92F125EEDC997C7F54F42D59D4E99A48D2873597A5A64B79A76E05EEC8815A38A8BE2BB63327D236D62A1A4090EDE6CDD26A2BC3743E81A46D344487284AEC95A6906A57CFAB88DE3B8C13842FC2145941B38810A83828B554DAD6669AD9776E97D32E443B14BB70087CBAA36B6301985B5BF868F13BCED348D906EC403409D1049C817918589BC19824E58999507F4B8F10E5953E23DFC48F61EF9FFC7E45CF9284CB4BAACE825405580974F099A8AA576B2CA9D6E4FA2E5EAE053716AF6EA71B5B57C873BF891DF5D3B843EB4685FDFC010B06C8D004C11BB5CA6CE70139C48FD2FCE2CE651340DC6ECA4685121676544C137A5CDAC718D69C88EDA376DD49605664339068B08FD445E5A96201390A4F268306EA049CE28251195F4E2F430745C22810CE3C0F0F4BE63ABFCF89B49829FFC7D52CA2C7FD45DC303B80C2EA6E83684A0BDE84BE62C1415B3BBF4FF37E2BD17DD0CB487373AFAF9B6B6EA16EDD44AC7A50F84B7FC5A4D98C316C3E4B67BA6E2FCD618AE51CD5E22552028BA8AE3848469B2BC875774BDF8C3EAA4470A9DF63DFBA37B39D827792B2BD47BC5A98CF462D73FF2906EFC3E37E29DB8EFEBDE5683D794568060AB7D5E19739868CC58BDB4E6C63D1C820AE33AEFA611EBD6E8560F76DB9E7157B2DCFE56AFE8C397CF9F5BDD0038DCB605116E74BB80684D826860C7515CE86E35571F5 +ss = 31D476B2A4D41B493246E055FB9D3088B3D3E4AE8D480477C66A271920C6C849 + diff --git a/hqc-kem/kat/hqc-3.rsp b/hqc-kem/kat/hqc-3.rsp new file mode 100644 index 0000000..411fd89 --- /dev/null +++ b/hqc-kem/kat/hqc-3.rsp @@ -0,0 +1,9 @@ +# HQC-3 + +count = 0 +seed = 9EF877FDDBE8891C6E4E79EAF022E563DEFACA6B152161B9A423E8FE96A403E774B2D352CF74C934069C9DE74757F505 +pk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEF3B1021BFEC31794714E6A9292AA08DD2487BE83C502127FB6218AFBA4E047C67EE8C5674181E692C9E5E79D5C6CD284618F911BD4A7B0D5BCD8C5A8DD64A4C588ED5570B88BA780149CD3F4AF573B90D7E683B8BEAAFF363FFF9F62911FDC2A8BD7E8265DB6079B9B36176DB31AB61744D6B9F1782063C5C6D1A14FAE23175AF6B11F82EB8C77DA0F9548432B62181C0C7C4CDA0C1C0E2EBDE07DFC5CF37CA9233101E5D66274E58F66E46F877BF3D84DD7328DD4FB87CEC269EA521E85122833BEFEE53F5E9F695424AB3108EA73C70B158777FA06C7AF2C6A42AE5BF0B73D602C95FE703D359F370DA17103358863134B8A730406F0077ABB2758A8A240C3545D564A0A2677A9A187FA6DCAEA1F25C77FD7D1353FAC494B4B1A03E433DA73F740562E0CC73C0838D525B8D1FCA73F4314ED02EB1F52770BB04FB32924488DCFB1A53A6D5949873DAA585C5371D3ED4B385DC28C5B2D2C1B0A6C1944E5AEA55C03A3CA10B78D5464D6D73F96E4FE6BF9C1EDFFF779A6F1435799B46A4C9B120FAC8F66FB3E5994AA02C2CC2572FEA373EAA5C4A81BBADAB2947731EE11E88834DDFB60F9C79BE6500C48FB3D49188DD02187BB37576BDB7400A1AB52FD3542DC36ADD213EDDDC69BB4CE5D7BB13F2BFB454AAA01C9A5EB9CC20FB4FF2745BF1D4EBFD3D0FD0FB5797F0C8940F2E8C2DC032C9CE8536F4EB785D2971F16C6B89B5BB5156C7083FB7695F9C76D3C08BB3E63A87D5D3CAEF1D1022F3D50489EF8FA2209C7EC1C1AA7ED6C34B04988E955AFDDBA5652D3347FEE8F6BE6991CE6870759F844E547977528AC837B67962682707B01AD2176F7965992AE98CFA1409753FEEA2B8EB9D99B96B2E8BDD598C088B53957D49FEBC8A7EFD54B0098D87430069570BC92F46BAEBF6A955F85ABF4085FCBDB0D24345DB579684CF74FE002A72C2F21A29114EC682CA4A06274DE884A92E417D1F2DFB23C11EC40C824A65D75E447857361942FAFFB82CDAA82856A3A09187AEEDEC6C686E3B132E25ADE8E9DA2FEEBDA93465838D1E6AA231D63F3368FFAC03D1F1E8C3AD2D16853A48C103C4408544EC719412A9345CEF0282D25FECE2C1FD158DE045D63E1FC8D4B1E7079F1379398F762C2924E51841E7F826D703DB2E645622CFF600C6FCD31F32A3943D5C8D34086FF84A6D7BACB902BEA94358C3DCEFB378F9188585F85DFF08D764BCE7D0F64C9364E394EA6976A9A7BC2EE949CC650965CC2321FFF2BB1BB77085E3242C90B1BEE992C10B4A41BEA5A8AB5A948412A6F5BCFDE95DF3E733BE325870E39CE1A069613448B12905CBD30D9ADBD5B36FDC82933C9652D647E3AB1E32AF89E9BEEB52CD2A719FF1FD31996E3A0EF89827CE3E59DFDF47E97BE4AA957FB2D4494D616D1E2453FCB5D840B28945F2AD82C3DB197194E7115E10CBFCD27C61B6A99BDFDEA390BED262677C8FF7271CC3C98C6F1833C8FF2E937617CB33E84D7197738E1B1BB4ECBFD734AF7C66BA51BB963E45D9BF796C810AD62D07A28149584A7CF37C718A0CA02EF2A425C30FBD475A133DD9AC34EE08D150CB9B094809C9FCD90C688D0FD749DBB87B8F0E5410537C64821909DB40C7592195E8E7FAD956596118CB902EC808B910928F2E8382BDC7E0BBE5FA617250E37EF82920DE4F9EEFB79731A23F3E9FE84482566F51CB6F49F8FEA8826182D03061717A4EC58561696B6AB06A284378338FF29CADC6A0655BA3E0FFEA739A6C8D8A4B72B55B2A99AAD8D628BCD28D10A53A54A6A30272991DBF109335B96ECEB4301A5584F57F35D5A470592FB20708C6686F40AF6FF9622AD87DFBED69B7FEA33D169E8A8837DC5AB33D30424C9CF2917ABC4458AA442A7B43F2586E567C1351E4A3488C62105EEBC93E0373D3F8B1F5CA2B704411058DC40F9C33E41B32B700D29099F99F98AE5B693976B39C1ADF2BAE1AE67047162D1337B6B4EE5111A9A3CE7AE1FA0B4F57553468B400AF5E37C1D1A27C2774F15F699905FF17A04A126C1C26090FBF695EBCE0CB9850F8647E9F1E156950BC6537061B70ED0BD7D9FED822F55757E8779645BE4AF8FC2EF8BE62575A3F86C596FEBA80134B9396ED5F4692C39606647F06FCFDD65B585320C9313E35C88BEBA32115889271E615D06299680692898908540B4BF840CD744A47BE305BF252F41BEF23CBEA46871F59858E806A0F1251B1E3253A0B7D9790BF5D76B5A16678C723B1CAF5F48B0B706164D02696A70C4019D34FC7E72A8BE73C0C025FD43FB2CBFA417FF759DF3EC8523069B528BD0409102761F37222FD485933B3EF898756FC3D82AF7592DBDADB3269F7F6EDECC9AB34A84DD50B0BE923618A442B4248C51B1640C5369FC162DF4238F8CD5E4D9A80055C9B498CD3B1B6CE8BB32DCA38D1AEC63A27150D8737CDB1E69126250F4179358D66875F8AB2A6659047917BDFEC6E0EA68725D56CEF9D0AB43B927D32F15E3418AD30AD02ABCB6FEB060C2F8FF4023A3A80F17816E2534619B72F9C8FC513375F66BC873408163040944625631309A3330D56C971E9C12A6FC56544C155363AC3BA72A4D64DEB72B0D380C5F66C5047425D2239B982406100BD7A020555DB54CB67F99E62C69927D4B908B4646D5B8975802988944EBBBED2566D07ADDCB6E01DBED97BAE32E86F6477C710423BD29EADF991649BA0A11BD793B7D91ED3D8B8A049A58762F8AE35A5C41A5D8F99876341B3B04E0CA40D11D71ABADE17690D3AE9A450181529C0DE6CF065B27C291CF72EA1F01280B81AE6C4F8DE7950D376E4A2556700914A18889B9D82BF83A7AC940288F48463F238AB67957BD9725C497A5AE7EA045E8E8CFFDA186CADC3B6F563C12695A5432BBDD905E2A61909F459352B9F542D85EEC2D1BE513E824BF8D5AB7D2C2C0B3A4C75586511D41A17BD09F5419E453D8DBB427FC1DA2B9B0B5361F72E885B022D787182D873899628446C6B1D16D49CE6527C13C21625E4561FB3491620A2594E904150FEC295FE1D74F711E74420A7015B9C8C557C0093083D15659475A2C1CAF99E8F150BA4E10F052AD153251E653C89B14B956294B428BCC7E465AAB050FF01D8D18B3078D2E683BB8CEC1AFC643426D85B12ED4E918E3C2189E4A739605D17EA9820CB02F8436F46E8FFE53F9ECD67F0E18C575D35ADC1DC08973113A32CF10E7872332F9E9EB3F147AAD59F93905302C3FF5A7432AC1DAE30A00ECF0E0A8BBDF775CC624E3C3DF99A74893BAC2522EA489F3C84323345D67699FB4FB721518581184B88C2DE7B46B0CFD7CAD7A6757C2F0D3A183CA67EC22E2AD19DD2E70C3F6EC7ACBAE1D3ADA166F766095318B5214BB1D1F56C853D18F4D3CCFA7D8690306E8A59D0BF3CAD0D85CC3335046011E14AB8B2C63636A5D2122633BA489FDAC878ABAB3DA3E8A4A3FC1EA0AC2FD7E788F54949BCDCFED4700BF361641BC37656887CD2F57C79075A9C775DCEFF4E30AF4195C7CE113E45BFC8F5A202ACB6FD44F2CCB2A9B780CC590BB3B36BD209E6CD306D4743F734347B041D1F716F6A4DB28F7346D27FC552EDA3096DC3E794444387F21B24DE6B2F417275BB1F5910803DC84AABA8D61E17C8FD676BE65A9D6E71FE245D88D48075CBAB15047EAA8E95D45140CCD870E3C4AFE32C13B99F5CCEA912350552259973F606B23C181CD61452CEE2EAD662E0FFA8865458B6279DD415091DEEF00347BFA624C400F4AB771948AE0F09845338BA5B643DC043C6DB014005AC6ACC149C08CDD8097928D5095FE6281AB042345BAF715750EE5B0CAE67E3D3EEEF82C032C07AD670FE6E04FE71315B698D17070DA615BA17F6349157B01CA7CBA4294CACA566AF8547FE1FBD6E066A6279D84D0A4A88533F3046CDAE52FE8AA8C81E61A1E614C4876460B6BE3DDF436F61E760F5233EC63B814111025037856462648866D608212B9F68EFC7341F8054E70202F833FE90DEA991269E9523B75566F46AA790AA412586AF6CABE4E8D53B17CED49D9D0116E73541BFE26F5B3B84292B6A6EBC46E8737092E760D640EFA09E0AD81537C1712F894196B30565C9439D8B78B36F0EB445A1C99FDA3C187EDDA2359D31D868700C2E0B9F2453DF691369009E5BCB14A95D89A3D7856D920417161F3DC3CAEFC80DF8F485492E615616284F4667ED60073D360B98963E4A83B145118D5C6308BC78F6582B3E258475AD1E5BEA67059C6BFFCDAED047829B5FC319112B9F243ECC363CE85053C445FADB5DE88318DAD27068253B2176B069B2985247BB6DC4D5D4FF550461741B92388142B6CF549B664160793D493BBB9E4AA729CF6721EC88F1B3D768DD6DBA755EFA3EBFDFAA55586257636EE767BE600127C1345502B24931D9933D1604A37AC0A1A437A6508708C9084B91047B2EC263E38629E32A1F8DDFEE588178257EFD2ADAB40C3264EDD4200BCADB8A13F2C68B9E8F6C84764BADF05AAF32CAD2E4E347F83D41B66080AB2AF01F76E512831CC08978568C7D2D75206E256194123F4C4981C6BB3BA9575937316969D2825C052515FAA7519A186FC9564C78F912B1B9B730AF07936585E64630A409DB1E370328063A3251F65771EECDFC087623D848C0B4C5A008719697425C4C0F13EB421E3770BA741AFB080B47C264DEE07F8891AB549A52C15B9D4D35E07A0FBC1F20BA6BF9F8B60E9D26F2F33973BA26530AEA8CD8CA259A1212D0A1E7E7675E8A792B4A3CCE5A5F0420BEB962D8B8D1D93CAFFFFC4BB8A72674CBD39F2B58A31F69FABC9ED3BF7EB8744E195E5FA02BF2F83B6EE5E4061AE86290FAF77443A175B9934846B09879B011448E75D248C338B6670C4B38F3566DE632002E1D8C09652C0BBCA8F0BACBC6E3E08AB001C06647CBCCCD2ED47FC92A48B4276E516794FDFB9F676EB2040E15E4DE78D8628FA2A8E89BB2CA517954D47C99800075A3C443BC511D061678CF7F06EEDE0FB7934CBA04893486E7CBC748E10C1E8B8ECEDE0520159DF7D0CD06D4750EBDC9105312D100A57D83C4D59A2225B31E6B006D90A554F452A74F4F01EC29983D428D5187468A5A58B54E128DA463201934D7EFB26F75C2446370E24AF9BD0B3029E72583F2A6A6C8946616B1966FC8C52C543EC480EB1FC6758502B1A0622BE55516CEFB71E0E19DC6983E03FE771D5DC0FCC91E06A4A70AE67FAF58878BC23CFB546692A11CFCD113D8702B9FE6B15B19D5D0844721184C99C84145EE2901CCA5419A03D0C552EA0FB871E6AFF64EEC57E969E9896D39824095E2CCE2D9CE0FC7F3F6E4CB5448CBB0958E4D63BF8BD8F25070BB6852887D827458747F245DC86B2E22F972683507E81993317F08757436FE3EE5280F79CEC1A99F9B2CEC7F7AD4AD742023882E869B546E4613C3CC33E08F62B89CCD60C99EA1ACBDDEE68A0E39C9271A52802B35B04D807889150180B08F593FEB90DEEC0EB99D49E2D8CF9059E8366A6D44A3D76A4EEC5D6328524ABD82BA11E8F850FA127B838C46A4EFA37227EF6D070F0D52F3123B1469E5EDE466EA7F0D80932BDB66F30E275D49406544616528EF68C30E3591A9BEE3B4ADCA7C7EA17938118654BB77EAA79677D9A881F0A50449D06B1BB0DEB3392ED451271BDC9618363779A5A1A14F8345F7A4F1EAC1ED9A59B0C7F602E408624341A12D4DBF7CFF403E72D653E67D959E63F14C022AB450B1817C2B7013C72A307D12EAF0391B8EAFF728239918FD46DF78696902FB31B8D66E53E11407FB2AE55A8F1406CFE3F27E41B0020527BB18010E4378F7EDA482423DACC496606FDBD949CF8474D243343C4CEA2A89A6ACD9CFC249D7C4C938B20C97264DE63EF93B0FE9EEC90E3994B424F39E739022FBE103E10E9687E663CA2AD1EA04198E6A78AC13B34EF69988373F2C1C378259F567FE6CEF739892B8B37CC0ED905A58F4F39763DC6BEB85A917AE99A3EA1BFF39793F2C176EE4C872354F61DD6E98F0C28E2094AB4259B617E4807BDA1A76FF9FFD2B33ED7BF8A15850A9D02A181D05036297C3F0BAA45DB973E2359F965A9FE0FA18997FEEEF34EA9F7EE701077992F2B41E87122DD784E23E2CCA5A8FA5F880B6E192D61E79F8FF3E5C20F2AA71A9696B1660093118DBDDB7D413D0002AD16BB49AE1B13AF8E05C643315D7A174B44D38A0FDEBB87B6D04F66848B08187A8C0735A4C5C29F9D75729250A8B856E57BEA623FBDD098FE2E185EA0343CBD105EB7C62BBDE70F28D3260D6343DD1C73D87E3E4ED3B85DD585707E4C08838FAA1D317A840269123189E4B607133364AD7EAB7427CC14E51AC0AB1A7C883A48AE7128093247615807 +sk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEF3B1021BFEC31794714E6A9292AA08DD2487BE83C502127FB6218AFBA4E047C67EE8C5674181E692C9E5E79D5C6CD284618F911BD4A7B0D5BCD8C5A8DD64A4C588ED5570B88BA780149CD3F4AF573B90D7E683B8BEAAFF363FFF9F62911FDC2A8BD7E8265DB6079B9B36176DB31AB61744D6B9F1782063C5C6D1A14FAE23175AF6B11F82EB8C77DA0F9548432B62181C0C7C4CDA0C1C0E2EBDE07DFC5CF37CA9233101E5D66274E58F66E46F877BF3D84DD7328DD4FB87CEC269EA521E85122833BEFEE53F5E9F695424AB3108EA73C70B158777FA06C7AF2C6A42AE5BF0B73D602C95FE703D359F370DA17103358863134B8A730406F0077ABB2758A8A240C3545D564A0A2677A9A187FA6DCAEA1F25C77FD7D1353FAC494B4B1A03E433DA73F740562E0CC73C0838D525B8D1FCA73F4314ED02EB1F52770BB04FB32924488DCFB1A53A6D5949873DAA585C5371D3ED4B385DC28C5B2D2C1B0A6C1944E5AEA55C03A3CA10B78D5464D6D73F96E4FE6BF9C1EDFFF779A6F1435799B46A4C9B120FAC8F66FB3E5994AA02C2CC2572FEA373EAA5C4A81BBADAB2947731EE11E88834DDFB60F9C79BE6500C48FB3D49188DD02187BB37576BDB7400A1AB52FD3542DC36ADD213EDDDC69BB4CE5D7BB13F2BFB454AAA01C9A5EB9CC20FB4FF2745BF1D4EBFD3D0FD0FB5797F0C8940F2E8C2DC032C9CE8536F4EB785D2971F16C6B89B5BB5156C7083FB7695F9C76D3C08BB3E63A87D5D3CAEF1D1022F3D50489EF8FA2209C7EC1C1AA7ED6C34B04988E955AFDDBA5652D3347FEE8F6BE6991CE6870759F844E547977528AC837B67962682707B01AD2176F7965992AE98CFA1409753FEEA2B8EB9D99B96B2E8BDD598C088B53957D49FEBC8A7EFD54B0098D87430069570BC92F46BAEBF6A955F85ABF4085FCBDB0D24345DB579684CF74FE002A72C2F21A29114EC682CA4A06274DE884A92E417D1F2DFB23C11EC40C824A65D75E447857361942FAFFB82CDAA82856A3A09187AEEDEC6C686E3B132E25ADE8E9DA2FEEBDA93465838D1E6AA231D63F3368FFAC03D1F1E8C3AD2D16853A48C103C4408544EC719412A9345CEF0282D25FECE2C1FD158DE045D63E1FC8D4B1E7079F1379398F762C2924E51841E7F826D703DB2E645622CFF600C6FCD31F32A3943D5C8D34086FF84A6D7BACB902BEA94358C3DCEFB378F9188585F85DFF08D764BCE7D0F64C9364E394EA6976A9A7BC2EE949CC650965CC2321FFF2BB1BB77085E3242C90B1BEE992C10B4A41BEA5A8AB5A948412A6F5BCFDE95DF3E733BE325870E39CE1A069613448B12905CBD30D9ADBD5B36FDC82933C9652D647E3AB1E32AF89E9BEEB52CD2A719FF1FD31996E3A0EF89827CE3E59DFDF47E97BE4AA957FB2D4494D616D1E2453FCB5D840B28945F2AD82C3DB197194E7115E10CBFCD27C61B6A99BDFDEA390BED262677C8FF7271CC3C98C6F1833C8FF2E937617CB33E84D7197738E1B1BB4ECBFD734AF7C66BA51BB963E45D9BF796C810AD62D07A28149584A7CF37C718A0CA02EF2A425C30FBD475A133DD9AC34EE08D150CB9B094809C9FCD90C688D0FD749DBB87B8F0E5410537C64821909DB40C7592195E8E7FAD956596118CB902EC808B910928F2E8382BDC7E0BBE5FA617250E37EF82920DE4F9EEFB79731A23F3E9FE84482566F51CB6F49F8FEA8826182D03061717A4EC58561696B6AB06A284378338FF29CADC6A0655BA3E0FFEA739A6C8D8A4B72B55B2A99AAD8D628BCD28D10A53A54A6A30272991DBF109335B96ECEB4301A5584F57F35D5A470592FB20708C6686F40AF6FF9622AD87DFBED69B7FEA33D169E8A8837DC5AB33D30424C9CF2917ABC4458AA442A7B43F2586E567C1351E4A3488C62105EEBC93E0373D3F8B1F5CA2B704411058DC40F9C33E41B32B700D29099F99F98AE5B693976B39C1ADF2BAE1AE67047162D1337B6B4EE5111A9A3CE7AE1FA0B4F57553468B400AF5E37C1D1A27C2774F15F699905FF17A04A126C1C26090FBF695EBCE0CB9850F8647E9F1E156950BC6537061B70ED0BD7D9FED822F55757E8779645BE4AF8FC2EF8BE62575A3F86C596FEBA80134B9396ED5F4692C39606647F06FCFDD65B585320C9313E35C88BEBA32115889271E615D06299680692898908540B4BF840CD744A47BE305BF252F41BEF23CBEA46871F59858E806A0F1251B1E3253A0B7D9790BF5D76B5A16678C723B1CAF5F48B0B706164D02696A70C4019D34FC7E72A8BE73C0C025FD43FB2CBFA417FF759DF3EC8523069B528BD0409102761F37222FD485933B3EF898756FC3D82AF7592DBDADB3269F7F6EDECC9AB34A84DD50B0BE923618A442B4248C51B1640C5369FC162DF4238F8CD5E4D9A80055C9B498CD3B1B6CE8BB32DCA38D1AEC63A27150D8737CDB1E69126250F4179358D66875F8AB2A6659047917BDFEC6E0EA68725D56CEF9D0AB43B927D32F15E3418AD30AD02ABCB6FEB060C2F8FF4023A3A80F17816E2534619B72F9C8FC513375F66BC873408163040944625631309A3330D56C971E9C12A6FC56544C155363AC3BA72A4D64DEB72B0D380C5F66C5047425D2239B982406100BD7A020555DB54CB67F99E62C69927D4B908B4646D5B8975802988944EBBBED2566D07ADDCB6E01DBED97BAE32E86F6477C710423BD29EADF991649BA0A11BD793B7D91ED3D8B8A049A58762F8AE35A5C41A5D8F99876341B3B04E0CA40D11D71ABADE17690D3AE9A450181529C0DE6CF065B27C291CF72EA1F01280B81AE6C4F8DE7950D376E4A2556700914A18889B9D82BF83A7AC940288F48463F238AB67957BD9725C497A5AE7EA045E8E8CFFDA186CADC3B6F563C12695A5432BBDD905E2A61909F459352B9F542D85EEC2D1BE513E824BF8D5AB7D2C2C0B3A4C75586511D41A17BD09F5419E453D8DBB427FC1DA2B9B0B5361F72E885B022D787182D873899628446C6B1D16D49CE6527C13C21625E4561FB3491620A2594E904150FEC295FE1D74F711E74420A7015B9C8C557C0093083D15659475A2C1CAF99E8F150BA4E10F052AD153251E653C89B14B956294B428BCC7E465AAB050FF01D8D18B3078D2E683BB8CEC1AFC643426D85B12ED4E918E3C2189E4A739605D17EA9820CB02F8436F46E8FFE53F9ECD67F0E18C575D35ADC1DC08973113A32CF10E7872332F9E9EB3F147AAD59F93905302C3FF5A7432AC1DAE30A00ECF0E0A8BBDF775CC624E3C3DF99A74893BAC2522EA489F3C84323345D67699FB4FB721518581184B88C2DE7B46B0CFD7CAD7A6757C2F0D3A183CA67EC22E2AD19DD2E70C3F6EC7ACBAE1D3ADA166F766095318B5214BB1D1F56C853D18F4D3CCFA7D8690306E8A59D0BF3CAD0D85CC3335046011E14AB8B2C63636A5D2122633BA489FDAC878ABAB3DA3E8A4A3FC1EA0AC2FD7E788F54949BCDCFED4700BF361641BC37656887CD2F57C79075A9C775DCEFF4E30AF4195C7CE113E45BFC8F5A202ACB6FD44F2CCB2A9B780CC590BB3B36BD209E6CD306D4743F734347B041D1F716F6A4DB28F7346D27FC552EDA3096DC3E794444387F21B24DE6B2F417275BB1F5910803DC84AABA8D61E17C8FD676BE65A9D6E71FE245D88D48075CBAB15047EAA8E95D45140CCD870E3C4AFE32C13B99F5CCEA912350552259973F606B23C181CD61452CEE2EAD662E0FFA8865458B6279DD415091DEEF00347BFA624C400F4AB771948AE0F09845338BA5B643DC043C6DB014005AC6ACC149C08CDD8097928D5095FE6281AB042345BAF715750EE5B0CAE67E3D3EEEF82C032C07AD670FE6E04FE71315B698D17070DA615BA17F6349157B01CA7CBA4294CACA566AF8547FE1FBD6E066A6279D84D0A4A88533F3046CDAE52FE8AA8C81E61A1E614C4876460B6BE3DDF436F61E760F5233EC63B814111025037856462648866D608212B9F68EFC7341F8054E70202F833FE90DEA991269E9523B75566F46AA790AA412586AF6CABE4E8D53B17CED49D9D0116E73541BFE26F5B3B84292B6A6EBC46E8737092E760D640EFA09E0AD81537C1712F894196B30565C9439D8B78B36F0EB445A1C99FDA3C187EDDA2359D31D868700C2E0B9F2453DF691369009E5BCB14A95D89A3D7856D920417161F3DC3CAEFC80DF8F485492E615616284F4667ED60073D360B98963E4A83B145118D5C6308BC78F6582B3E258475AD1E5BEA67059C6BFFCDAED047829B5FC319112B9F243ECC363CE85053C445FADB5DE88318DAD27068253B2176B069B2985247BB6DC4D5D4FF550461741B92388142B6CF549B664160793D493BBB9E4AA729CF6721EC88F1B3D768DD6DBA755EFA3EBFDFAA55586257636EE767BE600127C1345502B24931D9933D1604A37AC0A1A437A6508708C9084B91047B2EC263E38629E32A1F8DDFEE588178257EFD2ADAB40C3264EDD4200BCADB8A13F2C68B9E8F6C84764BADF05AAF32CAD2E4E347F83D41B66080AB2AF01F76E512831CC08978568C7D2D75206E256194123F4C4981C6BB3BA9575937316969D2825C052515FAA7519A186FC9564C78F912B1B9B730AF07936585E64630A409DB1E370328063A3251F65771EECDFC087623D848C0B4C5A008719697425C4C0F13EB421E3770BA741AFB080B47C264DEE07F8891AB549A52C15B9D4D35E07A0FBC1F20BA6BF9F8B60E9D26F2F33973BA26530AEA8CD8CA259A1212D0A1E7E7675E8A792B4A3CCE5A5F0420BEB962D8B8D1D93CAFFFFC4BB8A72674CBD39F2B58A31F69FABC9ED3BF7EB8744E195E5FA02BF2F83B6EE5E4061AE86290FAF77443A175B9934846B09879B011448E75D248C338B6670C4B38F3566DE632002E1D8C09652C0BBCA8F0BACBC6E3E08AB001C06647CBCCCD2ED47FC92A48B4276E516794FDFB9F676EB2040E15E4DE78D8628FA2A8E89BB2CA517954D47C99800075A3C443BC511D061678CF7F06EEDE0FB7934CBA04893486E7CBC748E10C1E8B8ECEDE0520159DF7D0CD06D4750EBDC9105312D100A57D83C4D59A2225B31E6B006D90A554F452A74F4F01EC29983D428D5187468A5A58B54E128DA463201934D7EFB26F75C2446370E24AF9BD0B3029E72583F2A6A6C8946616B1966FC8C52C543EC480EB1FC6758502B1A0622BE55516CEFB71E0E19DC6983E03FE771D5DC0FCC91E06A4A70AE67FAF58878BC23CFB546692A11CFCD113D8702B9FE6B15B19D5D0844721184C99C84145EE2901CCA5419A03D0C552EA0FB871E6AFF64EEC57E969E9896D39824095E2CCE2D9CE0FC7F3F6E4CB5448CBB0958E4D63BF8BD8F25070BB6852887D827458747F245DC86B2E22F972683507E81993317F08757436FE3EE5280F79CEC1A99F9B2CEC7F7AD4AD742023882E869B546E4613C3CC33E08F62B89CCD60C99EA1ACBDDEE68A0E39C9271A52802B35B04D807889150180B08F593FEB90DEEC0EB99D49E2D8CF9059E8366A6D44A3D76A4EEC5D6328524ABD82BA11E8F850FA127B838C46A4EFA37227EF6D070F0D52F3123B1469E5EDE466EA7F0D80932BDB66F30E275D49406544616528EF68C30E3591A9BEE3B4ADCA7C7EA17938118654BB77EAA79677D9A881F0A50449D06B1BB0DEB3392ED451271BDC9618363779A5A1A14F8345F7A4F1EAC1ED9A59B0C7F602E408624341A12D4DBF7CFF403E72D653E67D959E63F14C022AB450B1817C2B7013C72A307D12EAF0391B8EAFF728239918FD46DF78696902FB31B8D66E53E11407FB2AE55A8F1406CFE3F27E41B0020527BB18010E4378F7EDA482423DACC496606FDBD949CF8474D243343C4CEA2A89A6ACD9CFC249D7C4C938B20C97264DE63EF93B0FE9EEC90E3994B424F39E739022FBE103E10E9687E663CA2AD1EA04198E6A78AC13B34EF69988373F2C1C378259F567FE6CEF739892B8B37CC0ED905A58F4F39763DC6BEB85A917AE99A3EA1BFF39793F2C176EE4C872354F61DD6E98F0C28E2094AB4259B617E4807BDA1A76FF9FFD2B33ED7BF8A15850A9D02A181D05036297C3F0BAA45DB973E2359F965A9FE0FA18997FEEEF34EA9F7EE701077992F2B41E87122DD784E23E2CCA5A8FA5F880B6E192D61E79F8FF3E5C20F2AA71A9696B1660093118DBDDB7D413D0002AD16BB49AE1B13AF8E05C643315D7A174B44D38A0FDEBB87B6D04F66848B08187A8C0735A4C5C29F9D75729250A8B856E57BEA623FBDD098FE2E185EA0343CBD105EB7C62BBDE70F28D3260D6343DD1C73D87E3E4ED3B85DD585707E4C08838FAA1D317A840269123189E4B607133364AD7EAB7427CC14E51AC0AB1A7C883A48AE7128093247615807374B10C73F79FA08D0731BE4F21356D191782EB1D10DEEA5929523B3B4D6D97BF397572E7CEAC24CD55009F822EBE800A3231E1E1FA34A92CEFC0D60050E04C3171859E54BA888D2F670E22EBE926B0B307A65264FBC08F8 +ct = 116E1BB0F0C3FE6577D0B9D0461E66C6A97AB9238C52ACCB264E5625CAAFACB97B1F7518CF9E98302C3EFBA431433AD5C9B890584419DDB2D3CE392776DF7CA498D504240EA38ED5D3248796A050449D792E491E4982592DFE496D72E9C7A141B5DD106CB594D2D23DCB557BCBBD845F93F15E3FB99224A300BC4599EE8D0FF72A063C3B07EF396FDAE55800312CAE38DF437BAB9B7497A32D828CF39B022B2DE60330C697C4CC2427AC2CBB7E3867F6058604D969A940731E15D7BE6313617783FC641C5C36CA8BDF91533E23C89471836FEA65E761441535EABAF2E5D921ED05B20208592FD41CE89A9397BE23C999BB0E0B92A091F45C8740397E8F739886B40441E2396AACBEBFD26A60F5D10DB9D1C53DAB5A4691DCCA909087B824E48E38C702B56695C4D0DB02DCC654CD43FF4D87550E071A9A05EEA07A13347F4AEFF9658742C25C038F1A10F1611D95C1BC7996CDDBCC78F4B76F0F2D32C22D8DC1AF5077E4F43452595AF81ECCDB725D6B75E129CF268B2E48C6C2AEB3942F2626290C5A18AF70BEDA187247FD58D1F4C609F00B18675EF0012CE51FEA8706E9189C9EFF0CE743E8994B38BF192CDA8B7A4E743C0001F9769436A1CEEE9EEA170F6EFE7810CB8E139A0521CD5E9281FC8068C09153086E3EBFD4D66970F098ECEC8058FF39B914B0BECA12E18D97499D717908A3EA58E27260036847AC90C7BC20284F86C5BF5C9DF31BD021810FEFA6D909DBCF3C629128FFD28BE8BD8EA3FD2CBCF33C594C6359A351542FE414978ED8D75D62B5AD72ADD896F83EA50738174EF62D71BE942BBFB9DDD4E34149AD16B41B6276BE7A0AE1F793E7153B527FEBA4D7BB35FC85F05ACF06FBB53B7A1EF4B79E6E6F56DBAECEE153F1AC81BAC700DF257E7762788A79C5B809F30B30E7445B2F200BED7A8EC529C8D0EF77E1163B8F1A422AB782881490C6330F1FA74E49A1554F58907F550097F2B23EE7EDEAD32B60D4412819834CFD618E6CBB05656DC7CF655DC49C66DA4DF7ECB32A61196378726EA82DF97A5B69B2306B344BB4AD3F20AB5403279077FF5B9E148711BD1622ED819CA60735BAC3710F3E98B0C07D08C71F62A4E395FAD65610D55358F8DC3CD8A19472973FD2A620AF0AC982D8CAFF377F392F973D2AFB3B4B888B4C7CAD219641930863EA154D43AFE3DC55801FC78878258668B1CE63A1C6FD85D851D90293AACE975E59AA908AC82FF9B9C8F0CE2B0D4363AFCFF3D1FE0579480C463F9F0B0DD3039FD56316A729FC82FEF3565AB6B655C270AA2DEA67A2A8505ED0EBD209FA0A58414A6B39171685C2412DDB4066F1E635C78E95695C3BF7B9E2C967C09FFC369BFE7EDCFE738BE4A19CE341A53DCD525E7502574B86EE07DF0E2A898D66B09B9A14B0AF50159F72273D423BBA7E62FE8088CDF98EF89AE851429088B723C25C55600796C7FEAB7D2753CB0B7E0FE2FC78DDF84B6C276FA100B82D6886B12B7991926F6AF43AC46A29D34607532AE23F1674A19949E6D50AFA27A638794D2A8807108797BC15E4B4EB7C3CAF420A952772ABA1AA8CC43A8E5235558AD7B2B4B7DE8DE7ECDB4904864CCAB2B98C8BB877E3EE25F2748C93A6789165EAB655F4C086D2AE7D47F895414C2ABA2B7DECA110CA0B07C3D63439A4A720902832AFA39D0F2C51B851F657BA778FF74FC30502322A8F3921BEA908AB680CCB3757F40F17A74DBD75905367295DD9048F5CEA1DBDCA9E02368EC7C8D98F747A0EC82DE97F575D0443D7C0B42F00A394F9AEEE02AF9108FC83E8B29633BA765EEB13C77A2DE42596DD927B8F41D47E412C86A50935BC7CBCD12681C4376C17638B6B741C4E74137001F81457275F7F66660139CB0E000C45C80652B668BF8F5A0D8AAAE731E8FB148E5F784AB55BBD027CB5E2764C5C66AFD2BAD10433878946461E889364057FA30120EBC980A002C4342067960FB21546AE405D3090BC960454C48C2A667135A6F4701DD6014D6945239547D05804E3BAF939066FC76AD8ABF637E06018605D699461689712A8114C8D828748E186D2E0B00F8CC692BD10D97751593D4C5353C7A2E4CEDF8A831956ED0CC3FAB8C62A8F7D9F9ACA1B4931AEF7C08142E16E979FEDFDAB837A6B1F83E0B0E632CEA642779264DF88DCC87A8A5CF1E22BDF21114A6B6A9EAF8DA66889A314EFE6281CAEC0D9648F82EC4FEE7FD3EFC0D5E2D0264061C99E5481BE78679A3955D2292AE2AAAC0519A7905CD71386CF2B6E1BF447D1E46FA453A917129469B0A6708617DFFC782E9B62E647E4FB363DD69AB25E816FAF616BAD2B3ECE4ED04790838B293B2D55305F7FA7A5B4EE0961E459703CA49028D2AECB8DFB68DAA29BE3B241BE17ECEA0B92FF8321AE41E01A59C8C06A399BCC7F94685125097487DE5524FB41C6CEFC48AECCE1D49AE7CFCBAD8307F06355C0733F767477800D0E4475A0EAB8CC5123661C39C99BFDCF02877DD39F2031B5B5886AAEBD82AC51A9C2945F55A3C965D81FD62D69F29727234D1588C4A3963753AA13E823748931B53BD81B1C029DACF00ED1027F7870B2D2B6D44C9582F39ABED4F16AF223AE63D375B8FE84301B356A1F4E61D18AD6F07AAA30AE28E294CD09248651FAEEB85BF5E15965CFDF5459F957E9F518F8449F2B39CCC7A9D3E99BBC9C6A0F605B0181BF2CA40B81DB6C082B5E5F8983D348D0A4A49C1789C02B8C6D196ECEB366B83896D5057E20621B0B75F1BDE76C5F47714E4AFE3CF743C074BA11329B491BDB52CE07F77C8AD9CD746A938722A33B461BCAC7AFA5234C7DD23B9229F0CB89C671CBCA1F9543C821F281FF903F43A6D27D82B9FA7AB982E78BA6AFC6FC6A1BB6646CF65FA0D2F60B8767FA84989EFD61B949AD37C2494D8561DAE9F3ECC0D45740B91AA6CA3F2C11305D272551064888B7E36039967F39ACD91AE0FB21B17D30B84A0F113B0904CB9A8D93CEEB741B388568EE22EB7009CA985D4AFB47C86731AF92ECAAFA546FB81015DCADC73E15194B9DDA7F93824AB91160F11E4370EB3A735764AFAC9AEC1C132670E3C92614D37BBE2727D90E07A00505CA2183D554BA9C32870D746C0495D05644570724D2EFAC37F63C84DF3D44018ACA23A353701C9DAB191CD706312E5A764E1BAF43E2AF73A0AFC6486866EB7C315BB9BD28CCD1412D58FABB9854EEB4E84D99AC19BD52E88F8B2827D7701AD2BDA5A015341C8D35F0839E5CB4FC7831CF1CCA892DDD1454FBC35BBBFB7A1B79788352C65F1CF03A22461B19B5C38D900074F4D3BEC0FCE2737E04ED5B4A8890EF00DA64B2BC896B0698634F85C97BA78646BE1C751385BDBD3495B669CCE5FB878AADE761FE0CD1170B389BF46A1172CA4307F101A80931A99A94CD38C5E138782A8FC70EEDA3A67F07586578203CC8B374AD8C9C996D5080A846CD86E7D1E6AB8C9192BDA12D57973EEA543D25A215F85DEE4C3B4F161384BEE567862FE62B4076267B9D6D2CAA20FB7FC268BE702752C6D8E3F2E489D28A3BF5DDA2EF16750750D35AFE20E692D8861698562C585D959299638E21172E68879FE9FB75A9C6C8642B84A31E1163EA2A307D0CFCAA88BEE7A8BAA5308C5C6405DBF5991C3D4D9E94078687AE4D30BD2858B7F05403F1E5FB1D65C250DAC2E58B3299661EB1EF9BB94D01E83183EDD1A405F405E3F643BD8C2AD4DF680884BDBDA20F19540028CE818D32C3D4791C72CB22CA0DE21D92FFB7A214EECAFB7A4EDDB012C8965F66AE2C2387BE94DB86CFB00C742BE7A4B496EC13C5CDB4E32B6186630B6F34C876FB8A5C1A923F7DA870D3D29CFAB2509F9D7CE1E8F6DCF8F1C2E9B9BD54B56CDAFDA5D96BC42484FF3776C9A050257355518E5A4E19A1887416C515B6EB11724FDA244FB9471871CA0C44A40229EDB0F97979691B320037E8F963D64847E55842CD746302DDCA40C3FFF317EC887A7CF435F44AAFA7CCD94492A655261DD94513C9CE019C9D7EE0629D89C545E2ACEBA097E656558B7BA87BF7502018755275CEAA1233BFFCAEEF34A6E77E2132C0607B456D040E601A3CF62680E966AFB05B76E66982010C9DC158FD34C32C117EDC65884C14218F6764C0EFE2252BC2F67324FDB5F5CCB607F728B3F60C1A11D16CBAB8DD48261106DCD5D3F679641DA324E5AD5AFEFE8F8057A7EEA6A3B720D1F2DC5E02F367E3208874F048A761E056D93661C35308B412EA53DB2489280660F8063CDF70F08A02127BED194E06135D165D040C1C997E7358BB2888981C6D22D2D57FEAFC9D7CACC902A9C911C3DA0A3DAA14474A0DC57E28E788551F6ECB69E715C1A74E9AF79F47D1C44F2010CFD818B3276BF04F480E55726F79196E2F984B9A412471C89C4B2E282859923788581CCBE33594DEF825FC03E259B8E0B17AAB72B6897D297DD3553CF00D11D442CBEDD311D5940770A64A2AD9BA147D5712D07ABD4F59D151B371BD0016FF8A78AD7A69BFF6008832B73366D51A0FB272AA8D1A700DF88D2941EEB8FABC0AE31373F87B9CA7DD5C7A54DF1EF48632902DFE30B072CC1253E5BBECBFD31E207FB430CE1973333B804634E180AEDB24026B2C837D1972C379A6974EA8320103A7C4EAD8F62A466EDCD3E18B6655177549A50CA4C1CB0D809864285D13CA4E99EEE366E93FE9F35E47A75E32F496866808D930B7FDE453D5CD3639AC506ACDCA62A4D14AD8427F68CA610916D2E980E2F79413A29EA14D2176BD628506E6C653F1CD656855D07B4CF870778386BD02E827F412AB25779E63BFE49D0106770E3CFE126D073DFDC9338C7D4501A861B2BC7EC66ED19B39E10ACBFBAC6D78C1A946A6C424D2495DD5D9893AF4506CB855071B5CA8CF52B218BA0EECDC3FEA080B8176878978D8DFFE0EFC4835C49C5DEC189BBDAB5E9830B0628333A0FC1D7C12DC4697245D8F1E0FF631CDD21D54ECBCB5D3A8DD3FB1029C03E04E0228D79800397B2F860E2F2DDC531E00928C8CD4DB61607D6A080F596F965CA1293091DA84BDF9B1A9D5F524E569998DBF3CA7BBBC90315B5FD6BBAB6EA0A2FCF7C6D50C29BB5B6E2A64FF42D05646C82337F3D5E875894B363DEEC808C392E9548FD2E9216E1C36C28810AC07FF9C7C836C2FA4A8305DD3BC082CAAD541E5E82B5BEA1FE4EF51050705224D121A26D23044F4738D201081B429A4576AD17FD3A7D5F4858DEDCAD7493FBF7AA7D4CD2F1EB578FE8826B007974297D135AFEB286A08D5030585AB31E3DA6EDFCFC291C4E1CFC97007B6CF7A5A1038764486A76A167F50AB942A18CE0DB993FF36A33A228A60D044AB7B8934B2680DC1D0D2C9D5939460603FA76447C96099839513E44BE01F54F6471F7803925D36CFD48C699293FCF1DBC42801F185E43968676A0E6F37B261A882D46B0FE5873C8B8C8847A93610BA3868BDCF57B4CCE1197B3C75518F822C778D96026B59F84386497065CB77EAE0438B38A6B058DE7903049F5ACAF6E344B8F0610770915EB64FD2A3FDE751137A3287819DFE6B2D35361E294F09D65A364937E5C2873929AE7DB999BE752D5E86D3FF44602C299022CDF5FBFA9A3146222DBF66C396055383670C1876EC24102D34A57267A9A682120E5F5D8C465F886BB576548B1C8B138D50CA2322691E02A95D6E4B844B747EF3716BF00FAC0FAE13A8CC2EB6E29190AE74FCE890118031C0775B14E554B42D3B5F3B85BF0048D498A515A5C71E0298D42DBC35B5F6C55547F7240162BA453660233B60249233622E7785898231B7CC2BD05EF5D8303C77F5FFDAD9C0D1B68B45F91391DCA4F237EFE2C2FB2185A8334A41AF96996EBF568968827D98EC93A339C1D8131A113E6B18288FA24A921D38548768C6F77853A97AF96DAB4F62454D9E3C3E27396042CF712FCD47054D90BCB612AD15333C3C255E2409C70E1CAC66F567B46E0F2E919C5226D639E75BD557391CE18547BF1D941F62D40C376844100F4CCABEABB7F9C9D99E61A39C7654CC0DB1812B418A85A13FDE13935C4A3DBD9A6FF2662880809EF14901ED6EAD447D267A60E13BEBD999C6E39054F05694696FCC85704D0F888A9BB70FD37F18A264E4B888E5C8CF455508DE24834CC5A4AD3F26C4A334A5D2DF9CEBB85C60B6F51CAFED4A3AD2A53981757B00F2409CF92C682648AC0E84663D97F4D0004A3C1F041EAFFD9DCA2A07E79E18EEB5AAE5AD32F7D24361A5072C984B47A42E89710489D0321B01564D68A68A063D3C1641B6EC0EE49014463E6FBD70659A3B74AD15890FBC4C653A2BDEABC048AC27A003562BED82AD0EF00994981D03F9603450CE301F3464F66A80A986A9DF8B10044160158D1F5F35719B66DCEE42B105B747C6C860FFA5451DFEA407BBF744467BED6F91B03671AC6B80D6C64563A178EEF27C978283A7DD53D27BF3ECB68DA0D3A2F5D75BA86171258D889D2C3870B7C26ED74AF111FA0096B5A82E17A6AF2BD6D3807B685F4002D3AC5551D0FDAA1D4E85A330C246B944227C5860C99D8C621DDFB4FE6A914A1168F19498682111BC26D8AB6BD847FCF929E4DB87C5896860B274B13C037E5BB212D304176844CE955F4050F35974B813A677C448B41852229FF4993C9E9E6E82018A8B76FF1351E0C628F691B5397996E1ED6ACCE0BC2B0FF678C463AF62F3D79E847307261639408BED7572B4FD95E9F2A784DF106E2E4140F77498AF252FF306041ACFD9CBBF1DB10EB40102C6A26C62649E155D031A0DCE629E3D5E3CFC403C4C7B47E6FBDEAA23469C342A9EE6A3F573514392EFBC0724F5182FB9001B4BBFE5570B7E1885512AFE886F52DF5CA1339B1655AFCD4F0AFB058FEECA459CC1E72460266CE1FC754722A9B826F6A3D250676DA59AAB06924B81822834B031642B0DF077A884B4BBC8883D50BCB8CA27D2CB709EAE22D527CB374AE380C0CD28B2861C53F9E86C9D17AA1A1299055EA916D80F512253997DCDE1D59C2FDCB7541498B4BF4B17E2CFD9893FD992E7C8819C42D3306D855D8685EEBA9D3CDF196D1EDC63E02583C38727D744CF5E8E0FEAF1040AE4C43231D7FEC800AFA7285788DFDFE0F68A24BCBF769ED29DF04F3FFD86794090B7AEB8AD3032B8A0400DB6EE3E09114EE61809ACA14B39CBDFC050485FCD9C909CF0F8623D9A8C62C6658F1B3B295A94B5FFC4D9846070DD81407EECEC4CF2AFCF0D22007FA1FFE39B0D0F34CBF7A6D92B94C5426962D05081C65442EC4AADA819B0E907FEE8A4D68C7B4A6FA97A7B1DD1D0490525D427702397EA8F79C2359A02704D76C5F69252C2EDD8FBB4758B27A619C4CF7FFCD4F52D034C5CA1FDA8CF34D033AA91CF541159A76262EE48698765A51AD411D689B4B76D2D4A1D7079C7C4175FBBA39D1D5059DE334B7B37C32B745A30796DF949D607BA779B6C267B5E21BA48A2A8802DE617230D458B2642268CBAA5C9C971D785AF63AFEF502BA189AC97722652D8970488E1823468105D1C37C92AADB418B39977636FE466CDF71E88D7B645CDE41E38088C05A81E13FBEA89F3372576234338DBC34F367C20CF2B6CA0781CB8A941A5EA182B9E52E02FE8602FA814ADE29870AD8F7A1CD653B7171EAEA5B6218C2E69EA0FDE416E34F1447A482A98F83DA4D0B4E8CED036E16C17C9C685E05F678783FD577676BB4F8E9991F37524C3292E50F7934CD39FD2AC3499CFAB70AF52A7FE541B1836D0D452B8C416E5C70210DA557B9E91CFDCB73E5ECCA1DE69A0C15BE97711DC45DF088EA31B55D0F50B690C15733D5EACEA2837FAFA54DEF8035DE12B224891515D26F060F8C7A934D8D43EA689E182258B3819803E6C5B01F3A23CE2E504B29F06E0E2F9993A0A6B78C9C582E2D5F084D7FAF2D30DFC6C553BA33A07F0B1D1264FBB8640B1CABBFFDB8DD2B2C9375CA07D3F50FC03937B232D081ABD26716447CE5DD8634C1D7BAD7B9B6A54D30F697ACCBE48555B19321A4361858C0837E41C63ACEC25ABF8309C9F7651B4C2A8BC245EB0D0B196F92CFC48898787C0891FDD045B7894AFA34DFDA13BE0F820471A2B2EFF9883D910A3ACDB4EDCC021077E67954A92434923B132D3FB8DF3F8ACA164F49AE9563C2F54E1C0631F08C74265FF325C9CAA15F07D87BEE34A0011DDA64271D632A08B908ADC534F9DD0E5D2AC5BACB44BDBF601D8BC641E56BB69B938B8BB12157F6C8EB1FC5230DB235BC07A4BA21F389764ECE975B63858CF8617EC5A2B830B120FA89809FF725793E643E5F81FE7B040D9DC3DCFDF2F40B3E9C14D646ACE37DC8FC59FD91471FA6654D6F994E0A0320DAF8ABB5EACECF31330B68FE00B26E4BB80D4359F3F08DDFA868F1204BA3E2B8B44E16ECF4E3B4B12DF114BAF4FAA69173140E6066751E41764C21BC86514E2E8143232FDFFC8ACCC233ED580D2238FDE719DCA1B22C02DA0E7BD9EA288ACDD94E05317249ABECA4E13662C84A14CA99784DA0098EA51E2636B7187657D7CA2B2CA0E611D1BA5D4EA436F1BF041713F3FAF143A301C03A57892D2F4B3BAC61D3EA9D480869455F0DAAEB78E2BCBA7767ABB54A2706721BF13A454B7190747AE9E76E0B7EE4788402854FBC344D12804C15545EB3E77BA105EB5960AE1266CAF606FDFD5C759227084735596DD42C36529616C7347623ED809CC6C03CF06D22B1A36A9EDA821C59820D5F9BF857CC4E3188AD43A49659D3A349305DB33A79365B22C2BC6BB4E9E5535CF10EEA10FFCBF04977374B89EDBEB13EEE7448F9DF306518F6E01E0477CE4EF77F55016924ACBE6D47F08A8E869853FC49AEB2B23B5DD1B47BB5B7A2C56FA47E2704B82BA16F4B42BC9D4DD0AF7E231C2FE60F85BA003A3056A2B78000A105F7446E395C0E195608902EC156A66F126BFE565E7A1DE5EBB2BB4EC1C3F52717F6032C8DD1140061BF03B4477D3CDEDCA7073F398ADEE0F3BD38BCBD2BACDDDD28037FF966E2A08037CC6A1E49BC9390897D6F171BB63C2DE1B7C9D15482F7D633C5B6BD71587AB2ABE110B5392E9AFB07F1E3B8CCF166B831897D895621A9B5BF2DA16840254BF220AF991D1B1986C18BB301731EBC8DD1F0E86E074E2E34D5D6B4359420AAA8159FD3163D2A88EFE25B55B70ECB0182FAD533B12AD1A93A5A5D979968A38FA40BD582D051684AD2D7C5E4A7F5A969D8C0ABB072948D93C1EF74CA88A5BF7FFEF2F05BD774AC045DD59689A7B44EE9C91360338CE0ACB6A89654833F40B38B504E71C1A21D6D181EFC1EBFABE873FC523749894298B597ACDB5358CD50AE922EC4B0D4F513D275D0DBA66A14BB9DC37F1D982C6269A391C56AF8A2A06F2C5D4B034224E297CFB60AE6275AC966C66DAA7A49BACD8ECF5DE88493FEAB69ED161D3AD7F0510989B85DFD1BBC3E5599734469ABB12068AF658F658D54FF9F64932B3EE2CB94F6EF03480AD54F42759DA2305C39195F75A618B419FC9410302E0ED0B4A76CD2B796310D880AF178B7F5114ADED23C55F358201CDC65ED394A8C07FB7D9B51AD0FDB23A02A52B8C353BDD59DECC48BE016D9494F8BDA7C6981F5795DA83118A691D51DB128BA344C44A12B95EDA7F70A6909C44848106166F3ABE16491E4B1671C627ADCD5485252B24B26ED7CE56C54811E5EB2D0ACBE92E782426D3734E21B3F7E6134EEBEC2CA97EAAD2659CCC03325F93B56239343393037D902B881D0A65B5C1A0B189DF73280FF484A391F94F9E39A8C5DDBE23A62896CF27BCC6A5584765EACFA7A0EB5CA768E6A5AD27B6ED1581863571C8C50F066CEEE24493871574CE9DA0786C74D699D0FF99C957954A72E58AA14D35659BBFAF2068E9E3E676E917BF982F47117B148FDE36E9BCE3D3FB005B46C536A099F8DEEE0A4AA38FC81338180E2BF83845AD7A801B29B6AF2B6CBC82E59F1652A98A75E632B6150D0D07C524EFF2D0C561E97730CCE49C40320C6D83F4B4CB0DBF858DD0D723956D3B114975485A94487C0623B4C68796B06B4BEEE5885B079E63368E5249F58DD216699153FFBEA88B417A666BFBE8A6692EC9D6D64A1ABA4010785FC6EE3B7F41D3981F3867C385C65CE70596AC5769BA8609084107EF6B0BA9C752E1D38AEB5AFBAF105C4319B0897B09A000F59D6D6F89DBF9B36FE9BD84FECD661C50AA5B8DCFBC1ACB94FFBE5CD5AF1869EB85CBFAFB87EACDAE8B15902F6978FD2697E4451121FA6F7748AD25F0D906A6DB34D966663FB05484BDD8F2EB42E466465076D1636BA2904133A596C1B0FE855BED9F52BDB1C1DF2F6922075424060B3A00B0EE0197088229211FCB45252022418D2FEC26DD435AFF66BF2A148149506DB53AEAC83F916E7E9DBEEA816DFC9D760F1E2FD4A39EE9CEDF55CD0B3695E96F3F739EE32A9C7FD4A16DAE4A2637571C33B50B2EC225F2DB4710EF16C046EF2E92F162682693C698B9525F0E99FBD200DE9D1CCA1E5B7B898B139A897A25826D5FED0B9F48F490096332FCD06C97FC71A2BF2CA99BCC2D0415064D572EF5184491921F4C8D276449415E9FE540435413239252801E0575B8CD4800F0210CD7BC35A2B3C22AFB1AB294C10AFC290777438039CE37B122FCA90DCADCC6A284EFD924E05012BADA241F67BDF98F8DB6067CEE8891D9C6A1FFB3397702C7D9D2332EBEF530B4B3BFB04514C470C10523D04E92A70DFAF3B9BCFC6BAF2CFF62C329B228E17681735708C7B3F22A0BE34BD2D9E25305A9E1669EDCE32088DB1B1F7F38855611CD27FB98C49F1C0B4E627057A0489D166925C97C4D9F8B4224AC9DC92E22226042C99B4B390D0FEEBAB7AF8D1CE68CBA4A5AD3B95DB99C04E9956BDFAE052C02D56651501984A28932B3C97B520488A36FAE63F32838D74685D72C37A60BB23DBCF51D71C3A133E1AF23A7F3581A8F7BEAEA795339D6FD078CA176961435A8BD4FDC4207A793C5032001994946BE5700A38DC8B053FC94F3E4D5E6BCD7967106F9453B62984B875AF06C008C4BC05380AB2E5E7F9251AFB00B8F465473FDEED8FEBCA48C179131514AE88DD730533A87936D69DD732AEB9DC4E8B5BAE3D20B5E528C896A9BF68B3BBCE9C7BAF4BE9E1F5D15DDD4ED5912D5BD5DB6C98F2A477C436362895A28BA8485D50E199B01A21ECF805D2B5157C78C965171696D51B8CD945B31B6767E709544188D7096ABAF96E2C4658D9D99C05BF535AF2846C10C936ABB4DC7BBCD23551246DDFD598B479FE3534B64D17BAB73D7F6278F43C354B03781A30DF37857D1CC7A2EDBEC437C966A1B36ECC39031C254150D37D2DB4941F215D421CFD5BFF4AC283CDB8AE723C5DDA87DBC1E0AE37FB44A0710409F736517C43E37D118EB1752D57297787DE47013112ED268B0C24EACD1A8E86BBA51D3A91F966A4DB9D71A62D86A5BA7BCBF1EEA4D1D90A04712DF793DF42DC59DDC3281BA4E2FA9D7F7BC75A7B12097AA83C5BB78717165FE37D4BB1A9F18DCE1CD1A456C34AD5BF478E378ABC172E1B1C0AF003DA96A37D28D8A07425227E4C570CBFEB4B5A4BAFE870159843A046428EA0E3FCF6D194048DFD9687E950F4C178351B6D26143D79344FC5F581B82C4806E1600B379CE26382859655AB9AE31B26C479FECAF58EDC53F00F6D72C0289A9EFFE1E70966286A5BC37A5D7755AA9E875DA1101EBBCEDE20AE8F8BAE25BF874816A9DD847695A6DA23205C95842E9369EDED6C49C8F6E1AB8396DB9BEC2195B1B559E702CE070487E3E35F44830BA768E176025824DC6D7E3A77AED84620FB57F62BB6B95AD459DA86DC47F7EFBFA1E9A82B0C1F7CB5D07E29BC76924FB2A4E1DD00623A83EEB0C3A4758AD9D73347981849E45D0FBCC018E5926730C33B768B42D6CD24C776B3EAE888BB6550FE64D42620244022060C78C433B32249DB30C91611EC0BA79B091BFBD7F88E0C77C1FF5ADA7F4E5F6C23DFFD96145937FBC0A07717E37467D1A2A107044A506582B25D282B008B761C2AF2C5E8CD2635146205A613119883001D33D912F31EBEEBB76FBA3B22B1CF243069BF2271B7187616613671075C06F2FA8501669D6321E8C03B85A90BE27B77DABF76AB228217C3FA0DBDA6C7A98D524FDCD3C924B271536443C31D5663DBD13180C73AC781B6DA8F76780E292593A0341B72671C1DBE2DC4AF5D905A79828D9769E745D766FFEA4A2B971F34FE74B5CBBFDB5D7B9116A797618FB1DD0D9F35004627287936BDD7AFB693CEC6A054680D25B21E372746EB9A6883FA0492D890A6FC5BE1DB59BBEDC56DEA3912A3CE5A89C460A9FE8663038B13DF1367F194659F6E38FB84C7C0492DF7A62E3240FB8FEBF46FD8DF3A44BF03617DB741BE595A3AEACC1615B237F05B2DE3DFE7A82EDC376E6CDF7321E539BDF95AB5B800F6B441C33BBD1B97A8D1FAE3DAD8AA5EEC451C723E74EC89CFF17321FCF6BCEF4D6F3C289127D27CEDB765773E2B15BEE46594EE56B5AAAE7C49F482C117DE3FC15E2149969B9D392AE61B88F0E935DD24E8779796040FCCFBC2F40EDE62D6A21C6823257B046FB1CC39D3FE9AA724A64B1B223015460A8DA056E8A8F4B98B3C118DE3F3B9D7C89E5ED50696935E6EF440B4314B8892F23290BDFD627D666F2DB3B4A3EFE7A1B26BCD6B1D8B1F25C30971A0E429F015DB4920AFF0CDE77E5562A53690E53D5089A948D0278354026A04689800F9EE44139FC6C77C567FEC23741027515CE86E35571F5FFF32BA9C40BE676 +ss = 60EBD7334B5C208EE6483522049282390DA8701C938AE3D2BCF74AEB052B1939 + diff --git a/hqc-kem/kat/hqc-5.rsp b/hqc-kem/kat/hqc-5.rsp new file mode 100644 index 0000000..90a2840 --- /dev/null +++ b/hqc-kem/kat/hqc-5.rsp @@ -0,0 +1,9 @@ +# HQC-5 + +count = 0 +seed = 9EF877FDDBE8891C6E4E79EAF022E563DEFACA6B152161B9A423E8FE96A403E774B2D352CF74C934069C9DE74757F505 +pk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEB6CF9B91058293C99CED29F30BD7B76DFA03877BB9E6AAFC179DC638C8292EEDFA122AB384A60D18DA42AA426D7B08031564B1AD45FDD32956F77EDFBBDC012894A76CEBED542A941A60DB8885DA44CBDF284329CE3D59576E9DD444F16EE47062C95687A29F76815E884BB1CFABB86E49C0836818A3AB9D59D0ADCC6BD3E6C3ECF8478111BAC9D35E3754390DBBDC30D8DB134B5C28044B1C66D7238FC9FE354224466884A660268EB2717417B0D6F523C7D54DBC178080A88CC4113093F22FFA6BA5850F83C0B965FAD1581227C37C8C48C614BF81771867EC49427E82AFF14B352243F92610D8BC9677167ACF5FE133806D8F807480B33A1EDDC41D5E26B902EC4CC5B47A232B51C2843166FB2C499D3A3EE402F4CC950E6A190D016AAAC955071F5B9E982B80D6A21E22A5AB047C3566EBC32EC36DF0C738E269820A889C2A48DFE70BD7EFA6DB069436C09289BFE321219771C03ABD46014CF48B1F033BDF9D71FF5FA80566AA63CC8A1703B34216F81E22150647229F649C3072F323BC99D051E3940CCB76F71BBA1492C599D3FDBEAFA5E0BA7A7D5847F90D31108CB722A2A9823FC2FFD3DDA07CCA6869E13CACCDF1F19992BEF53901ED5367DD457F98982AF673574B129E3485F64D80C18CDDE3F81272E53504081F6417D5EAFA78E2ACB67956CD9582B0F96B72F8564414FEB6C13B6E86626F96E83AA93FC231E012D3F97259582A784961DACC44A5322EB76F3AB8F2E66BEE375B94C2088480A9B8B146D615E215D6F60E95C97E2D0884B77739C6CD853C1A0D7A0EA0C00E34350AC4407FC1018D7704AE5EA8509F2EB4F31B262E6774DB45BBE008C5C7218C30133238250BD3BC12CA990B99C571345D9A271BCBF775534D072717E1DFEBC4AE6CC86E81412414AA1576DFC2E435C7240D133F0C1FEAE87574AA6DAD6A78CBFAE3ED13C9927C6D23ACC2D55070A91F9B8B441FC6AA1A46CD292F0B914BE3A4F87AC9742056C7099AC5D4E3C4A79B164653B0A00E30B3AEF98C7B50AE93B79C6D06AF8C2DB47D2F97C0A50FAB23BB0F461BAD0E791EA3201850EF2B9BA8CDB0F8881AC95D9C34A0CCB63D85F31EEF3A3042BBC6CF97350456F4E791C0BC3B767751856202A719C066669C2710954857EA7FEA97C7191ACF2E5B5E6C8F57A8B3802CA3E8B97D0C52DB3B7B392D2A80B7883CC9C281BD87F7E4FFC6998E6D575B077D443EC4C51E3BE443741D364EACB1DEEE36F82E632091E384720BE26658337B5F108192FEA007115D69E57221DC4187D1157B4C72A2DA3406F384FACE5512C0878A086C1DD8B5D3B985CB75CB0C553CE5FCFCEB4271319397630166A2ADC96EAD14301EFD852DAD993D4D63757B27DB3DC9DA5D5869B90BA01FF0F3B045E7AA06C25F682FFEE3724ED8B80170270283DE04BB2FBB5F22CAEA06EFAED21DE0124451478A414D57E9561D905D891BF794E3823BB6336AF8A6A10AE3940C42A2FCDEDA2A2B69C223E3CBFC6D87211ED4D1137F122A00BD713F1EA50A9D9C4998313BB391CFD8E626983F306EBB92E2474106F0D8B5AD7ED0982EF4B2F80189A7A18E9B47704388BCAE76DA6CA3E5404AB617AE9913CE820F656EDF967D436CE5678A60839C3A8C8942AC52E402769C7C01920B5B1FF8A3E6160AA74B6163A4F0520478A1B70A32C71B4B7A4519AFEFC44A70E34706D57A14F6AC06DF09923800B63091FD41CF2CF7ACD2333D75692E751268E8DA2508F2BA8D889F684F248D259835EA274AA26AFABD6DBB714AF55D532D5B8345A58A3DBB8FB431EB0672D223A49DE20910CF27FCA95FEF5FD6780452787478410AAABC4C839F0A95D85BD2904058483C1F789A6E8ABEEF638225F2BEE9E228F76C6D2B5CB77C74E3EA35866760CE032DD95E17C6AA201E1CE2C230C4F66AA0DF96A0E82295745493D31F1103EE48A7D0B6296676F8247EBD02240784EFD81A8BAA2C7A0FDE2375821E477EFDF9CE41C3B7FC87ACFA6701B3EBFAFB88FA84761E72151C183E2D148F4106B2C3A855B1F36C28905A9D93251D6F9544CA17581DA9A1E5BEA14FD9B4DFE9D58A7975866F737CA16A93E1742BC6C1A84DACD9C5F80FE449D3382CFB8E461946403B5E484653C725CE9050F7128FD4AA53FA2AE0056CD0E5A353117CA6F815B450482BE2717010DFABA09F9F4DD998BC5035F7BCBA4A3B1F62F55C158185EE529BB985710CB176673A2E56501F65A3B92D05B8C8CED55C3B32C7EA6E83EB6C251CD24362E0541F19357726188A0E61F89209E821EC4A9003A20194D76F7AEF6B3121DB699931150AAAAEA8D1943726A567970D24BA5E69F3684B4CE75C125AFDFC58E9CFBAEB00146D5BF70C7AF01E43F633B5F457C9C6F8061434D6C3BDA84894E589FA9740FEFBA92DCED7E8537191482DF487D97F833DACBDC64AFCC7C33DF8B622F035CBA42CC4AB79E16A5ED2967A93703EED0761BACC87C5C814F55A3636404A2A1912D442EDBE2B9424865349C9FFD8C5E6B551895B55ED246BEDB8FED6D276A6032D8FEA8FDB07AA0187847652043DE3C10EA99DED4EA69A1066C14F5B202A2B15A9CEE5D6C073E759917FA30B71F7938D14711E4EA2151C6714CC1BA6FF8593A9961BB3706A7BA6E296FA08F2EF5FD813CD7EC3D6556B80A1C149831E002FBD98B0C3BBCCB681AA3237EBA288FC374C6E272377A8478507CFDCCB6FD4218FFEED286CF071401A0C7D94B23655D3222823D591609103ABA299FC9C135A12F1520B1A12318E7D3330BC3F5C8E6A9EEF0DB722F967A42F3A798F697AFC1FDDA7E08665ADCA283D9BD8AFCEFA3701C4FACFCEA36BB64A4E78FE901EB40A17D435FB9204C2C3DD6DC8AE9E6D4A894703030493F877F9C3C9954AB3C4E8BDBF76A6D60B607BB4A9EA22443A6FFAF6D641A477053ECBA67EA79A6AF0A16241A6CBB7649266695248BCF445E1CF4B131D052786E45C7869E8475D877E5ADE5F6B73BDFCA355F1E1123435FBFF4D4B21E41E0E70061DE8F770E1A50C79C449CF6301437BC78544D4FE99C9235260B2EF4F2AF70E0089218A954DD74BD0E90F243BB38DCA538E765F4BB426E0EF7F1280736CD830C1AA63A8DBA101BC0D5E831F941459D8991F1383B27E691902765EFF317AEAB47B18F08F136901273EA59D583179FE0656B14F15C494CD3E1696BF34C5AB0AE5E086B7C09CE77BC049EC522D4DB7A79AE6E595CD28766FC95A108211B4BFF3C5EC844C8B0F93F55E97BAED9336F13FFAD5708F005A1FC089DFBB74030AF7EB43D9D4C3A52AE26DE2AAC252777D0C96DF7743BB1F69845877EC09344AFBA503C976CCEFC4BA92554BDD0D5C3011B4DD7922B3308D651612FF4A803EA14DAC44697EB78DC76132A4AD33F6C702377F9A611DE8BAB8156429C65972FA59C439BF3CFD8A3916DBB5555A1C8C535516390B4DE591DB5A1A60576A1B07300F16CFF9A41A2FA6EBAEAA96999DD9A4BA0A57DF59F0912DD2F2A1D8554281AD094EBB29819AC682DE772504E697D647144F8A1BC68F7DA2BC4C5D5BCFD8B5A5578A0B433D53B2D992CECFB9FAFBA539A7C34664794EAA5634E0B467E7E5B60CA7C773F91B8C693F7358511270413564C889D1E8A3352938D8C026F53C384A780FFADE864C65974FB9B0BF5714E06BBFAC3BA23E6B4AE0FB804BE231A999430EA3D7098A60E83913875F19650E466F857EA162D744002B0A9BD26D8E446A3BA0461DD0B3DF51B65799578EFC5B2A4C02DE357BE921AD941B821263FA8A65F3F1DA06314F68AF374B523FC821A349ACC8A5A1CC6F25C84DAE143B4105F82BEA670E1B90A9A7EA0C88C363E0A281901BDDCC95494876C9024E2A83BDC50C0C1ED441C2D39F6F84DF041F463E6ABB79E01BD307122D700C9F05F79BD7AB38F245B068AA1B2B4B7BB05AEC88E0F433E82E44F1635DD94DB45C217C529C7A089E2C083480A6C7D94E53378E0F54436C33E1C560EA35D1F94CB948FA0CFEDEE0EC86B19664AD9F71AFE45E1D76289D66771857366BC3085A82190ED601C6C098FE396A831E59A64CA4B33BDC824DDB5FE352E8743BF7F3DB96EB68099EDDCB8BB4942F791B0DB10E12A3817A5804D84C50B34747687E6D1ABDCE737CB37A201C49A0E7195D3BBA3976C98CCE3A5517146AF80D236C1569E10698D6F7474E8F57A7BB0152846BA953906913153225DE94E5E614BF9C189A2375217402D1E367C1AE300043631A905594F77FCC929AFF2DEEB81D6463D9A336798BA10DB25484AAB2E2B524CD8CE8F897B819484487092A3C6595F5C508A5630CA57A5D5D2A0C38BFD74459EFA380D5714ED0E6E8583CCA9B4EEF45547D69D965804A131FEF72D210C2F698D24C96F35F2271C3075702E39B0CC3D7C2B876EB7ABFE903549057A13570B5C27DCAC9C7C817F657F3152EA3B83DE8CF80BF7F32E4FEE574205DE20FBC5003F5A4EE7FE481FE08BF1190ADEC145B3EAA3ADE14588863364DC56AD0BB71AB0F2054E63B9DB14A315210ACCD6CF4E773DD66E3CF9F06A512B50AB8F8012E50E9E8A0D22579C3D06EB03A98A272D1FBA4E8C9E7097957A71CA98E7825494949FFE56918B033CDAD8D2AB8E473B2659CAB2F16675036670A3DC32FA8799D8296A8B561B5B389EBE0BEC2AD84000C2BEAB96C1375FE90C45187C3C2B7EA362FE5EC8A61BD4AFED180E5CF1042F4A1B8C9B1C895B66167E383C17BF24CC1C57ACCA1749AD3F283E365B08B8931955F921B6B394706F358E68BC0D6BCC6BE2E014F4716F3EFE7B57D52D1188B1B687AD87B1595708CF51AA907D6D26B0A4C0A2BF653221AD2F45D1567D8102ECDFC76ED97EFC55E9D028F7A2A7A6146A1E817AB7A62DB2E7504ED390502DE61453A56A5E82455D97468ED45D5FCD90631EEB736B90D29210AF7B3F80E7C9528682A33811170C93C5E70D5C72DDC7D24CC03B829D637CC234B72FAB977AB5456A379BF294D905FA0A6D916213306B0C2D2685CC9B65BE53D3EA4B6685EA072E2A67120DAB1EF267072BEEA7D5CABBC5D4567B538234CF8B5CF8F52B04E173B8AF111E38E7CD41B302377D7D88A13CC233C7E8A11D95EB39933D32961ECB5DB0B711D6A943BDBBFCB62964CFCCC0F1C47390565F6C4036730B4E81D76C04577B466798A336BCA39018AA374E02E850EEE20D494D33B576108F3EF3175298E9E7624A907ECAD832CDE6114EF5B559263DE40AB5AC114137AFD367093BB745D75F3538BF91E97AB9D6B2B8A064E08FD30F5AA4AA4092C413D402EFA7E9C3B15B0EDBC4C1FAF296DC6719CB38A9DAD61A2B28C08B4FB681F3D9D5E09DA6BD3F69003DFAB8C73DC5EC81CDD9FDD25DE19B308FFD4EBF87370D2921063E7DC48E779B5F1B2BD82A4A4FAAD6F22D6C64379D564938F683579AABE88B7658BDE49650772C6510DF555D0C8BC0D8FF44A1B9237AEF8F9D3C25545FEB258376338B188B4356F8D647D2CC24D8B0156070865C2E43B770E589A036153ECDDA425006C76EC9A6B7AAED74ACA26EFCA60BAF0B601D4ED3B0F81B847557AD77C14AB01E1D8FFD3BE68C955A376AF2573F9F7CCEA1429A61D19EBD1BD15B672BACDDC76F22A3BBB2A4EC0674C0154C542EF0A3D070458E209420865945B2C87013672E8F760A5C6F2B7B0A403399D0A063B81D28DAD76B8EBF5BB03A006503EA34402906774C9503DDA4D78CAE11A81F4BAD2825C0F4E5278C39CC7CBF4316CC887796719EA660E08128C6F7C7F198A540D33A6825B0E806B4BC235DCC5B3A44AC9498B39A528EA433112F210B8728B274E1029CEFCF12830B53F941A84ED741EB0884FC3DFF3060CC778F38DEC5CB10B177CF73CBAAAFD7D73ED7F5CD47A4EF2228210EC8449DAEBD769D93843682EF4BAD23E3825BC81F9F15A490902C6392351275D3BC7C8E550F2D5C3705F377D4C2C113AA23BEDC7BF209EB3D80E698EED9A24A50705653EE02FB199A9A77EEA24DF2E4DD8ED1CA17396334C4EF723EF0C76910F375A31CC0F04384ADB4D163123DEB4BE93D0767B76EDDC700D68563EC320D5540B2B2CAA4213A3C290FD19C6880133FB6C82B5988B880C55F3B76F716C481C10B996A63A0CB15ADFE2A8119154F0CC888A16004DB825586A8D10E90F5D2F4B2F33478EE2BC678E0827845E8052AC52DB6DB1EA68980BB36B16607F1A9709F958ECA97BD4AEA4292C4D25FF781574E79400A0144902FC8DB2C23C3457ABCD179DCB1A0EF6F9BC7485F0170DC03452671E6EDD45AAEE9C1CB56F6240597DA6E7E075FE2E2ADE4BD3E5DDD3FCE08C2ED982DE5E5B0CCD5148E7DB4C5338518C1EDA36BE1AA1AD69FC949AFCACA5146B165B8DE0096F8A178C55AEEA19D1A5E72AF68195073E5C70C18AB50461ED8C92BA4E43CB41DAC5121DE6084B8E72FE24290EE2CF29AE8D575F3A649CBF22229259C20ED3DEF8896C757C5DEB9E8255415DE28B8007E1A096F081A54F9C51CC247067D70EDAAC29E3984534F8A434AAC57B41B916023157525C19CE498A6ABD85EA3869E3DFCB769C07EAE418FD56467B64F762F80B0DB1E6289996C0EE599A3C90D3FD68593E0DC53FF1DF7CFA6120D9E060DA6C39471545275B985D4F4A948CBC8176B1AD5586ABF279E6326F42007DD5D5547A50F6DD3CB31ECA2FB174B377034B3F4941904662144A08E942280EA72C8742CE4BC8BD8FDF31D0912764709AE9CACFAFF64273EFB9629C8A22311E074A210A97402CFD27AEAD871E725F010546F3312E4FAA102674A16381BE205D10F177477637ADA1679766F52CD54ACF9093F66BB68BBA5A4C51F50895E30B75BEA4BB34AE1A4902683937464BF9B30ADDAB245117B36ECDB4686B8863D1CE146A4695AB861934194B05EBAE540EA2E0F4D7A919D145DFC3F07355974E9D0E72B08917A8058BC90905D1C2A1DFC916DE4DBEA69629A91CA9686633328C11EE25199DE6730E129474E38199B16F860D4167CF84CBF244F036510D551E5F2421599226265DAEB98AA64EC90F707CC6B2E25917DFFE476FB677AA9B74F6F28DE2F1FE45D59FE7769E9456C81F1C0BECDEB72C34BE80C64A0DA7AB9A4DF25EA25E31235AD3922AB7701DD7B55377AE615740BF61FFB12345EB91AB9D1DA607437938CAC76EC3AEBFF837FD7E40DD41DF663B585B5064C535C413E2C2F2C17D94BD077F4FAC890489874474CFE6989562C0DD416D3CF0257C28E4EBA22C0D2A53ED8B06E0763A1F251351FFD06F1A214DBA6F8DF41FCF870369FDD6CC668D014E9052D534593BBEA2CDF43BE12614E469201431AEB7CEBEBB2574F05B9FF1F03A443C5ED23962E40D11C50D1A47DC1E9D3B8A4DE77FB2AF37A347D22F0CD43BD97D82A51EE916D00436018763DB9DBFB4AF020AA34800231F80C9C08C85287E875DB97FA8D0B392509EF400076C84E503F790AF571A43DDBE9A89D63B7FF09A1868772364008C9575DCFE829FBCAF17825447A14B1BD758D0AB860506675DCBB35CAFFE95D24291E32899B4B1C661D2122FDD61611EAC3B79C85AD52ACF7D1C46D59C3E1AE2C56893734DF302AB8A39A2B88F4664444DB4A02C3093152185B7E521F93F453B642221FFC864C48AA0C9829EE6607E641A566691341DDAB4821A1495124828F7A1C1B213B0722BBC141670BF2E7E17D0D8ECADC6F254D15942F22C97FDCF563403FE6B43F965EC1985AC0F2489BA8612354890D446D2C73BE0AF14BAAC3246D485987462C8526813700716E538024E7CB4F3678D4CAA7D0617E4318ED320F1CE22980DDBAFCF12B317766D560D55F25F8C1603933DF3F25C3AB99588B3B16AAF6F9E7506528C26DF309911903E6838152E0C723E189553DF533F8EA4DB2ED6301D02EBA3EDF34A089A1431F6BFA36F6193CF689EBD9DC6F32D2EF88D124AE0624CD99490FCB1ECB9BEC83CF2B3C9BEDC9B4BB329258E3D2C9A84428816E910C142EECD70476418F96CC0DE5C7BE7E5E16D547C5306C7E32EAF3548F0957D5F3ABDA684BF6D78BE48A485EB86F99DC117720C047F46EA1E64F7677B6B378B4BC16C75F5CF23D3B46A8AF0923D9B30CD68571BE49305C37A4C1E63D821C98493F32FC090897A4EA985A84286A4B8D038C6BC9318E51147619D5E80192DCF9F5A126316B74F7D7FBC717DA532C37E87F7D14B7004EBB73053C292A07F7CA2421A937FFA5353BC837786122491A9789B5B1F301BFBFF5E8710FFD371768A03D905A3FA8018BC4DEB039C2C4FF828F0FF855AC37D62EE35A1F5A3391E5BED826F77451C15361816501E16AF9C543A6CABC8CDB472F941E1533ADD5230DFC3DBDAAECD6EBAAA2134C117FC0B74F81842745135EDA7B7A10D7C8B6FA90B5192FE8F9C1CDDDF2CC9628689A38A5EDD032509683904D2183DC6CCDA2B85B8903C244B21922E22AB74E442056B33933496E5C14932805241C311DA2F511C3EF253F344B14783EB50645E4CC8E5CF85988A58F9F755B8E424358A47C32B953C563F20CE8FD661FF0C8ED19270259C5FB3E9B08B8A635749397B73C7D9C7CBCA49832391AF7A37DD73D43C8FA6A07568AF2A5E33210FC00F9F984A93CD50DA6F91EF031CC844DAD408CD14AF4464566B2DDAA3CED6EADF4E642252004F66EFC9802DAC13039328C4AE170F6353EAB75505BFDDA98D61E534F23DEC6874FD5BBDB349ACAB40FEF21B35C6A3A606C8983F34B374EC43C5D4CB8D78C346BBDBC309EDCF4BDF3DF96AA641CA4999C09DD106CD4CAE332A82930419A089E153C45C6B4DD06F0CA8F031D0FB60135D124FA78DB3E7441100175CE31B3E5A159521FE7747F6588534B6C09D93AFD2DAFFB940C523786CE9C7ABF215546C92AC2F7ACD52436F86A5888482B66558F61278094D7AB73998645AEC3DFA8C42A47F7687B8FED6EDFC3F82ADB5BA717A6E7FD4B55553A3F5622F87F8441AB1C6B23D7E304277DF1F25EC53C3B8AF42AA256F1BB06250A079257AB64281C5D7406145E243869E3736344715BEDD19CC7D59FCD961BBDB8AE9D9E8398C70DD5F16B5536245A0044BCF359124C810630CAFDD51A457B23A6A296B6F94F94FFD723D2A2576A486BFFB73D6A6182BC5105D51793AACA5BA9232411BDBF983DF76FB61002B222BCB970DD384ED14FE816BB8063557C3E0408D0E746CE07DF5C3A5492F9485BFD1A4E7F6CAA6D9A2F074EC3EC59402B688476E7E85E7A11EA6697245114575F27388B3D465763386E46C3A208D64B33D0764D6EDDD83BF307CFF7FB928EF99F742F78E5D4AA368968F8513AEDAE0279F1B8AA3F6AC8E722331F53622071A2D83C7869681A7C137576D4D20F427FC2460E664EA6503D6879E9DE6208F4DBF7F4D28402A56F304DCC0A7E21FE46AECA4ECA90AFFE04A94CB43D3073A4FCB9B53ADA5986BF33989BEEEF174AE118496CD98CFD7CBF54373111BB3A66CB22F44D11C2946E43716D30DC97DEFD05B288493CC56B2DE18457CA894931CB306460BAF84510C0C4753D34873607CC029DECBA131A37855F618B05BD4125C0DD84D5BC859D2F8271F06E8DBBB955AE5B790D623F0AECA36629EE87BBB92B08A7B68620895E63982AE8C290F33EE9A8E19775580B00D87D2D79022B27C5986109F4CA02B66A4152030558D5ECBE0DAAD033F82BF9C86BF54D43C3D5395E2557CF92E09DE0E446D95F89810A292E37A4D92DFCFDF4032C9563E22CB57D9CF24E5312B1B2BAC071EB20C1FA2882069A92D1AC19AE67B8D2A840D3C34A606F60192C273FD253F0DE9A2E1ADCC06064B23292DE3713C002DA40EB3139F91D32DEAD8D9F07D44154D2F67A502CD51AB2262B4AA00D93F65574C0D578E50606E6E20087531224FE372874B228B2671EC6B344FCC67DAD0C1B48690CE21870165C379FDE8D2352DCA9CB25AEC7460887EF7E500C6725154E000AF0C379547692D90C4CBCA78011DC71E32B2A23FC550A0F1BBF7DDBB23A398954D8DCD255B44D6B0DBCCF12CC9B980B14BFCD0832DD74D8EAFCD0806108BB43181CA6E4FE4039E4AAFD8D7C256DB131AD954BE164D3870505B54C20EEC74BA7A510FAC251386D697CB826C08AED1493D2A0006949C8DD711278EC798E9567B38EDE12E84BF0EBEE3C391778ADA2AD4FC8E5AF1DF7B2ACFE3ECC75F5BC95109B21829A835C003464EF564528DC0A6BECF8D939C9F7409F867988DC09DA5C70F96146DE1C8FFA4F54B7FA210 +sk = 4053237912EA281C51C4456A5096589EC9D20219651E00F9704178F0CF84F9AEB6CF9B91058293C99CED29F30BD7B76DFA03877BB9E6AAFC179DC638C8292EEDFA122AB384A60D18DA42AA426D7B08031564B1AD45FDD32956F77EDFBBDC012894A76CEBED542A941A60DB8885DA44CBDF284329CE3D59576E9DD444F16EE47062C95687A29F76815E884BB1CFABB86E49C0836818A3AB9D59D0ADCC6BD3E6C3ECF8478111BAC9D35E3754390DBBDC30D8DB134B5C28044B1C66D7238FC9FE354224466884A660268EB2717417B0D6F523C7D54DBC178080A88CC4113093F22FFA6BA5850F83C0B965FAD1581227C37C8C48C614BF81771867EC49427E82AFF14B352243F92610D8BC9677167ACF5FE133806D8F807480B33A1EDDC41D5E26B902EC4CC5B47A232B51C2843166FB2C499D3A3EE402F4CC950E6A190D016AAAC955071F5B9E982B80D6A21E22A5AB047C3566EBC32EC36DF0C738E269820A889C2A48DFE70BD7EFA6DB069436C09289BFE321219771C03ABD46014CF48B1F033BDF9D71FF5FA80566AA63CC8A1703B34216F81E22150647229F649C3072F323BC99D051E3940CCB76F71BBA1492C599D3FDBEAFA5E0BA7A7D5847F90D31108CB722A2A9823FC2FFD3DDA07CCA6869E13CACCDF1F19992BEF53901ED5367DD457F98982AF673574B129E3485F64D80C18CDDE3F81272E53504081F6417D5EAFA78E2ACB67956CD9582B0F96B72F8564414FEB6C13B6E86626F96E83AA93FC231E012D3F97259582A784961DACC44A5322EB76F3AB8F2E66BEE375B94C2088480A9B8B146D615E215D6F60E95C97E2D0884B77739C6CD853C1A0D7A0EA0C00E34350AC4407FC1018D7704AE5EA8509F2EB4F31B262E6774DB45BBE008C5C7218C30133238250BD3BC12CA990B99C571345D9A271BCBF775534D072717E1DFEBC4AE6CC86E81412414AA1576DFC2E435C7240D133F0C1FEAE87574AA6DAD6A78CBFAE3ED13C9927C6D23ACC2D55070A91F9B8B441FC6AA1A46CD292F0B914BE3A4F87AC9742056C7099AC5D4E3C4A79B164653B0A00E30B3AEF98C7B50AE93B79C6D06AF8C2DB47D2F97C0A50FAB23BB0F461BAD0E791EA3201850EF2B9BA8CDB0F8881AC95D9C34A0CCB63D85F31EEF3A3042BBC6CF97350456F4E791C0BC3B767751856202A719C066669C2710954857EA7FEA97C7191ACF2E5B5E6C8F57A8B3802CA3E8B97D0C52DB3B7B392D2A80B7883CC9C281BD87F7E4FFC6998E6D575B077D443EC4C51E3BE443741D364EACB1DEEE36F82E632091E384720BE26658337B5F108192FEA007115D69E57221DC4187D1157B4C72A2DA3406F384FACE5512C0878A086C1DD8B5D3B985CB75CB0C553CE5FCFCEB4271319397630166A2ADC96EAD14301EFD852DAD993D4D63757B27DB3DC9DA5D5869B90BA01FF0F3B045E7AA06C25F682FFEE3724ED8B80170270283DE04BB2FBB5F22CAEA06EFAED21DE0124451478A414D57E9561D905D891BF794E3823BB6336AF8A6A10AE3940C42A2FCDEDA2A2B69C223E3CBFC6D87211ED4D1137F122A00BD713F1EA50A9D9C4998313BB391CFD8E626983F306EBB92E2474106F0D8B5AD7ED0982EF4B2F80189A7A18E9B47704388BCAE76DA6CA3E5404AB617AE9913CE820F656EDF967D436CE5678A60839C3A8C8942AC52E402769C7C01920B5B1FF8A3E6160AA74B6163A4F0520478A1B70A32C71B4B7A4519AFEFC44A70E34706D57A14F6AC06DF09923800B63091FD41CF2CF7ACD2333D75692E751268E8DA2508F2BA8D889F684F248D259835EA274AA26AFABD6DBB714AF55D532D5B8345A58A3DBB8FB431EB0672D223A49DE20910CF27FCA95FEF5FD6780452787478410AAABC4C839F0A95D85BD2904058483C1F789A6E8ABEEF638225F2BEE9E228F76C6D2B5CB77C74E3EA35866760CE032DD95E17C6AA201E1CE2C230C4F66AA0DF96A0E82295745493D31F1103EE48A7D0B6296676F8247EBD02240784EFD81A8BAA2C7A0FDE2375821E477EFDF9CE41C3B7FC87ACFA6701B3EBFAFB88FA84761E72151C183E2D148F4106B2C3A855B1F36C28905A9D93251D6F9544CA17581DA9A1E5BEA14FD9B4DFE9D58A7975866F737CA16A93E1742BC6C1A84DACD9C5F80FE449D3382CFB8E461946403B5E484653C725CE9050F7128FD4AA53FA2AE0056CD0E5A353117CA6F815B450482BE2717010DFABA09F9F4DD998BC5035F7BCBA4A3B1F62F55C158185EE529BB985710CB176673A2E56501F65A3B92D05B8C8CED55C3B32C7EA6E83EB6C251CD24362E0541F19357726188A0E61F89209E821EC4A9003A20194D76F7AEF6B3121DB699931150AAAAEA8D1943726A567970D24BA5E69F3684B4CE75C125AFDFC58E9CFBAEB00146D5BF70C7AF01E43F633B5F457C9C6F8061434D6C3BDA84894E589FA9740FEFBA92DCED7E8537191482DF487D97F833DACBDC64AFCC7C33DF8B622F035CBA42CC4AB79E16A5ED2967A93703EED0761BACC87C5C814F55A3636404A2A1912D442EDBE2B9424865349C9FFD8C5E6B551895B55ED246BEDB8FED6D276A6032D8FEA8FDB07AA0187847652043DE3C10EA99DED4EA69A1066C14F5B202A2B15A9CEE5D6C073E759917FA30B71F7938D14711E4EA2151C6714CC1BA6FF8593A9961BB3706A7BA6E296FA08F2EF5FD813CD7EC3D6556B80A1C149831E002FBD98B0C3BBCCB681AA3237EBA288FC374C6E272377A8478507CFDCCB6FD4218FFEED286CF071401A0C7D94B23655D3222823D591609103ABA299FC9C135A12F1520B1A12318E7D3330BC3F5C8E6A9EEF0DB722F967A42F3A798F697AFC1FDDA7E08665ADCA283D9BD8AFCEFA3701C4FACFCEA36BB64A4E78FE901EB40A17D435FB9204C2C3DD6DC8AE9E6D4A894703030493F877F9C3C9954AB3C4E8BDBF76A6D60B607BB4A9EA22443A6FFAF6D641A477053ECBA67EA79A6AF0A16241A6CBB7649266695248BCF445E1CF4B131D052786E45C7869E8475D877E5ADE5F6B73BDFCA355F1E1123435FBFF4D4B21E41E0E70061DE8F770E1A50C79C449CF6301437BC78544D4FE99C9235260B2EF4F2AF70E0089218A954DD74BD0E90F243BB38DCA538E765F4BB426E0EF7F1280736CD830C1AA63A8DBA101BC0D5E831F941459D8991F1383B27E691902765EFF317AEAB47B18F08F136901273EA59D583179FE0656B14F15C494CD3E1696BF34C5AB0AE5E086B7C09CE77BC049EC522D4DB7A79AE6E595CD28766FC95A108211B4BFF3C5EC844C8B0F93F55E97BAED9336F13FFAD5708F005A1FC089DFBB74030AF7EB43D9D4C3A52AE26DE2AAC252777D0C96DF7743BB1F69845877EC09344AFBA503C976CCEFC4BA92554BDD0D5C3011B4DD7922B3308D651612FF4A803EA14DAC44697EB78DC76132A4AD33F6C702377F9A611DE8BAB8156429C65972FA59C439BF3CFD8A3916DBB5555A1C8C535516390B4DE591DB5A1A60576A1B07300F16CFF9A41A2FA6EBAEAA96999DD9A4BA0A57DF59F0912DD2F2A1D8554281AD094EBB29819AC682DE772504E697D647144F8A1BC68F7DA2BC4C5D5BCFD8B5A5578A0B433D53B2D992CECFB9FAFBA539A7C34664794EAA5634E0B467E7E5B60CA7C773F91B8C693F7358511270413564C889D1E8A3352938D8C026F53C384A780FFADE864C65974FB9B0BF5714E06BBFAC3BA23E6B4AE0FB804BE231A999430EA3D7098A60E83913875F19650E466F857EA162D744002B0A9BD26D8E446A3BA0461DD0B3DF51B65799578EFC5B2A4C02DE357BE921AD941B821263FA8A65F3F1DA06314F68AF374B523FC821A349ACC8A5A1CC6F25C84DAE143B4105F82BEA670E1B90A9A7EA0C88C363E0A281901BDDCC95494876C9024E2A83BDC50C0C1ED441C2D39F6F84DF041F463E6ABB79E01BD307122D700C9F05F79BD7AB38F245B068AA1B2B4B7BB05AEC88E0F433E82E44F1635DD94DB45C217C529C7A089E2C083480A6C7D94E53378E0F54436C33E1C560EA35D1F94CB948FA0CFEDEE0EC86B19664AD9F71AFE45E1D76289D66771857366BC3085A82190ED601C6C098FE396A831E59A64CA4B33BDC824DDB5FE352E8743BF7F3DB96EB68099EDDCB8BB4942F791B0DB10E12A3817A5804D84C50B34747687E6D1ABDCE737CB37A201C49A0E7195D3BBA3976C98CCE3A5517146AF80D236C1569E10698D6F7474E8F57A7BB0152846BA953906913153225DE94E5E614BF9C189A2375217402D1E367C1AE300043631A905594F77FCC929AFF2DEEB81D6463D9A336798BA10DB25484AAB2E2B524CD8CE8F897B819484487092A3C6595F5C508A5630CA57A5D5D2A0C38BFD74459EFA380D5714ED0E6E8583CCA9B4EEF45547D69D965804A131FEF72D210C2F698D24C96F35F2271C3075702E39B0CC3D7C2B876EB7ABFE903549057A13570B5C27DCAC9C7C817F657F3152EA3B83DE8CF80BF7F32E4FEE574205DE20FBC5003F5A4EE7FE481FE08BF1190ADEC145B3EAA3ADE14588863364DC56AD0BB71AB0F2054E63B9DB14A315210ACCD6CF4E773DD66E3CF9F06A512B50AB8F8012E50E9E8A0D22579C3D06EB03A98A272D1FBA4E8C9E7097957A71CA98E7825494949FFE56918B033CDAD8D2AB8E473B2659CAB2F16675036670A3DC32FA8799D8296A8B561B5B389EBE0BEC2AD84000C2BEAB96C1375FE90C45187C3C2B7EA362FE5EC8A61BD4AFED180E5CF1042F4A1B8C9B1C895B66167E383C17BF24CC1C57ACCA1749AD3F283E365B08B8931955F921B6B394706F358E68BC0D6BCC6BE2E014F4716F3EFE7B57D52D1188B1B687AD87B1595708CF51AA907D6D26B0A4C0A2BF653221AD2F45D1567D8102ECDFC76ED97EFC55E9D028F7A2A7A6146A1E817AB7A62DB2E7504ED390502DE61453A56A5E82455D97468ED45D5FCD90631EEB736B90D29210AF7B3F80E7C9528682A33811170C93C5E70D5C72DDC7D24CC03B829D637CC234B72FAB977AB5456A379BF294D905FA0A6D916213306B0C2D2685CC9B65BE53D3EA4B6685EA072E2A67120DAB1EF267072BEEA7D5CABBC5D4567B538234CF8B5CF8F52B04E173B8AF111E38E7CD41B302377D7D88A13CC233C7E8A11D95EB39933D32961ECB5DB0B711D6A943BDBBFCB62964CFCCC0F1C47390565F6C4036730B4E81D76C04577B466798A336BCA39018AA374E02E850EEE20D494D33B576108F3EF3175298E9E7624A907ECAD832CDE6114EF5B559263DE40AB5AC114137AFD367093BB745D75F3538BF91E97AB9D6B2B8A064E08FD30F5AA4AA4092C413D402EFA7E9C3B15B0EDBC4C1FAF296DC6719CB38A9DAD61A2B28C08B4FB681F3D9D5E09DA6BD3F69003DFAB8C73DC5EC81CDD9FDD25DE19B308FFD4EBF87370D2921063E7DC48E779B5F1B2BD82A4A4FAAD6F22D6C64379D564938F683579AABE88B7658BDE49650772C6510DF555D0C8BC0D8FF44A1B9237AEF8F9D3C25545FEB258376338B188B4356F8D647D2CC24D8B0156070865C2E43B770E589A036153ECDDA425006C76EC9A6B7AAED74ACA26EFCA60BAF0B601D4ED3B0F81B847557AD77C14AB01E1D8FFD3BE68C955A376AF2573F9F7CCEA1429A61D19EBD1BD15B672BACDDC76F22A3BBB2A4EC0674C0154C542EF0A3D070458E209420865945B2C87013672E8F760A5C6F2B7B0A403399D0A063B81D28DAD76B8EBF5BB03A006503EA34402906774C9503DDA4D78CAE11A81F4BAD2825C0F4E5278C39CC7CBF4316CC887796719EA660E08128C6F7C7F198A540D33A6825B0E806B4BC235DCC5B3A44AC9498B39A528EA433112F210B8728B274E1029CEFCF12830B53F941A84ED741EB0884FC3DFF3060CC778F38DEC5CB10B177CF73CBAAAFD7D73ED7F5CD47A4EF2228210EC8449DAEBD769D93843682EF4BAD23E3825BC81F9F15A490902C6392351275D3BC7C8E550F2D5C3705F377D4C2C113AA23BEDC7BF209EB3D80E698EED9A24A50705653EE02FB199A9A77EEA24DF2E4DD8ED1CA17396334C4EF723EF0C76910F375A31CC0F04384ADB4D163123DEB4BE93D0767B76EDDC700D68563EC320D5540B2B2CAA4213A3C290FD19C6880133FB6C82B5988B880C55F3B76F716C481C10B996A63A0CB15ADFE2A8119154F0CC888A16004DB825586A8D10E90F5D2F4B2F33478EE2BC678E0827845E8052AC52DB6DB1EA68980BB36B16607F1A9709F958ECA97BD4AEA4292C4D25FF781574E79400A0144902FC8DB2C23C3457ABCD179DCB1A0EF6F9BC7485F0170DC03452671E6EDD45AAEE9C1CB56F6240597DA6E7E075FE2E2ADE4BD3E5DDD3FCE08C2ED982DE5E5B0CCD5148E7DB4C5338518C1EDA36BE1AA1AD69FC949AFCACA5146B165B8DE0096F8A178C55AEEA19D1A5E72AF68195073E5C70C18AB50461ED8C92BA4E43CB41DAC5121DE6084B8E72FE24290EE2CF29AE8D575F3A649CBF22229259C20ED3DEF8896C757C5DEB9E8255415DE28B8007E1A096F081A54F9C51CC247067D70EDAAC29E3984534F8A434AAC57B41B916023157525C19CE498A6ABD85EA3869E3DFCB769C07EAE418FD56467B64F762F80B0DB1E6289996C0EE599A3C90D3FD68593E0DC53FF1DF7CFA6120D9E060DA6C39471545275B985D4F4A948CBC8176B1AD5586ABF279E6326F42007DD5D5547A50F6DD3CB31ECA2FB174B377034B3F4941904662144A08E942280EA72C8742CE4BC8BD8FDF31D0912764709AE9CACFAFF64273EFB9629C8A22311E074A210A97402CFD27AEAD871E725F010546F3312E4FAA102674A16381BE205D10F177477637ADA1679766F52CD54ACF9093F66BB68BBA5A4C51F50895E30B75BEA4BB34AE1A4902683937464BF9B30ADDAB245117B36ECDB4686B8863D1CE146A4695AB861934194B05EBAE540EA2E0F4D7A919D145DFC3F07355974E9D0E72B08917A8058BC90905D1C2A1DFC916DE4DBEA69629A91CA9686633328C11EE25199DE6730E129474E38199B16F860D4167CF84CBF244F036510D551E5F2421599226265DAEB98AA64EC90F707CC6B2E25917DFFE476FB677AA9B74F6F28DE2F1FE45D59FE7769E9456C81F1C0BECDEB72C34BE80C64A0DA7AB9A4DF25EA25E31235AD3922AB7701DD7B55377AE615740BF61FFB12345EB91AB9D1DA607437938CAC76EC3AEBFF837FD7E40DD41DF663B585B5064C535C413E2C2F2C17D94BD077F4FAC890489874474CFE6989562C0DD416D3CF0257C28E4EBA22C0D2A53ED8B06E0763A1F251351FFD06F1A214DBA6F8DF41FCF870369FDD6CC668D014E9052D534593BBEA2CDF43BE12614E469201431AEB7CEBEBB2574F05B9FF1F03A443C5ED23962E40D11C50D1A47DC1E9D3B8A4DE77FB2AF37A347D22F0CD43BD97D82A51EE916D00436018763DB9DBFB4AF020AA34800231F80C9C08C85287E875DB97FA8D0B392509EF400076C84E503F790AF571A43DDBE9A89D63B7FF09A1868772364008C9575DCFE829FBCAF17825447A14B1BD758D0AB860506675DCBB35CAFFE95D24291E32899B4B1C661D2122FDD61611EAC3B79C85AD52ACF7D1C46D59C3E1AE2C56893734DF302AB8A39A2B88F4664444DB4A02C3093152185B7E521F93F453B642221FFC864C48AA0C9829EE6607E641A566691341DDAB4821A1495124828F7A1C1B213B0722BBC141670BF2E7E17D0D8ECADC6F254D15942F22C97FDCF563403FE6B43F965EC1985AC0F2489BA8612354890D446D2C73BE0AF14BAAC3246D485987462C8526813700716E538024E7CB4F3678D4CAA7D0617E4318ED320F1CE22980DDBAFCF12B317766D560D55F25F8C1603933DF3F25C3AB99588B3B16AAF6F9E7506528C26DF309911903E6838152E0C723E189553DF533F8EA4DB2ED6301D02EBA3EDF34A089A1431F6BFA36F6193CF689EBD9DC6F32D2EF88D124AE0624CD99490FCB1ECB9BEC83CF2B3C9BEDC9B4BB329258E3D2C9A84428816E910C142EECD70476418F96CC0DE5C7BE7E5E16D547C5306C7E32EAF3548F0957D5F3ABDA684BF6D78BE48A485EB86F99DC117720C047F46EA1E64F7677B6B378B4BC16C75F5CF23D3B46A8AF0923D9B30CD68571BE49305C37A4C1E63D821C98493F32FC090897A4EA985A84286A4B8D038C6BC9318E51147619D5E80192DCF9F5A126316B74F7D7FBC717DA532C37E87F7D14B7004EBB73053C292A07F7CA2421A937FFA5353BC837786122491A9789B5B1F301BFBFF5E8710FFD371768A03D905A3FA8018BC4DEB039C2C4FF828F0FF855AC37D62EE35A1F5A3391E5BED826F77451C15361816501E16AF9C543A6CABC8CDB472F941E1533ADD5230DFC3DBDAAECD6EBAAA2134C117FC0B74F81842745135EDA7B7A10D7C8B6FA90B5192FE8F9C1CDDDF2CC9628689A38A5EDD032509683904D2183DC6CCDA2B85B8903C244B21922E22AB74E442056B33933496E5C14932805241C311DA2F511C3EF253F344B14783EB50645E4CC8E5CF85988A58F9F755B8E424358A47C32B953C563F20CE8FD661FF0C8ED19270259C5FB3E9B08B8A635749397B73C7D9C7CBCA49832391AF7A37DD73D43C8FA6A07568AF2A5E33210FC00F9F984A93CD50DA6F91EF031CC844DAD408CD14AF4464566B2DDAA3CED6EADF4E642252004F66EFC9802DAC13039328C4AE170F6353EAB75505BFDDA98D61E534F23DEC6874FD5BBDB349ACAB40FEF21B35C6A3A606C8983F34B374EC43C5D4CB8D78C346BBDBC309EDCF4BDF3DF96AA641CA4999C09DD106CD4CAE332A82930419A089E153C45C6B4DD06F0CA8F031D0FB60135D124FA78DB3E7441100175CE31B3E5A159521FE7747F6588534B6C09D93AFD2DAFFB940C523786CE9C7ABF215546C92AC2F7ACD52436F86A5888482B66558F61278094D7AB73998645AEC3DFA8C42A47F7687B8FED6EDFC3F82ADB5BA717A6E7FD4B55553A3F5622F87F8441AB1C6B23D7E304277DF1F25EC53C3B8AF42AA256F1BB06250A079257AB64281C5D7406145E243869E3736344715BEDD19CC7D59FCD961BBDB8AE9D9E8398C70DD5F16B5536245A0044BCF359124C810630CAFDD51A457B23A6A296B6F94F94FFD723D2A2576A486BFFB73D6A6182BC5105D51793AACA5BA9232411BDBF983DF76FB61002B222BCB970DD384ED14FE816BB8063557C3E0408D0E746CE07DF5C3A5492F9485BFD1A4E7F6CAA6D9A2F074EC3EC59402B688476E7E85E7A11EA6697245114575F27388B3D465763386E46C3A208D64B33D0764D6EDDD83BF307CFF7FB928EF99F742F78E5D4AA368968F8513AEDAE0279F1B8AA3F6AC8E722331F53622071A2D83C7869681A7C137576D4D20F427FC2460E664EA6503D6879E9DE6208F4DBF7F4D28402A56F304DCC0A7E21FE46AECA4ECA90AFFE04A94CB43D3073A4FCB9B53ADA5986BF33989BEEEF174AE118496CD98CFD7CBF54373111BB3A66CB22F44D11C2946E43716D30DC97DEFD05B288493CC56B2DE18457CA894931CB306460BAF84510C0C4753D34873607CC029DECBA131A37855F618B05BD4125C0DD84D5BC859D2F8271F06E8DBBB955AE5B790D623F0AECA36629EE87BBB92B08A7B68620895E63982AE8C290F33EE9A8E19775580B00D87D2D79022B27C5986109F4CA02B66A4152030558D5ECBE0DAAD033F82BF9C86BF54D43C3D5395E2557CF92E09DE0E446D95F89810A292E37A4D92DFCFDF4032C9563E22CB57D9CF24E5312B1B2BAC071EB20C1FA2882069A92D1AC19AE67B8D2A840D3C34A606F60192C273FD253F0DE9A2E1ADCC06064B23292DE3713C002DA40EB3139F91D32DEAD8D9F07D44154D2F67A502CD51AB2262B4AA00D93F65574C0D578E50606E6E20087531224FE372874B228B2671EC6B344FCC67DAD0C1B48690CE21870165C379FDE8D2352DCA9CB25AEC7460887EF7E500C6725154E000AF0C379547692D90C4CBCA78011DC71E32B2A23FC550A0F1BBF7DDBB23A398954D8DCD255B44D6B0DBCCF12CC9B980B14BFCD0832DD74D8EAFCD0806108BB43181CA6E4FE4039E4AAFD8D7C256DB131AD954BE164D3870505B54C20EEC74BA7A510FAC251386D697CB826C08AED1493D2A0006949C8DD711278EC798E9567B38EDE12E84BF0EBEE3C391778ADA2AD4FC8E5AF1DF7B2ACFE3ECC75F5BC95109B21829A835C003464EF564528DC0A6BECF8D939C9F7409F867988DC09DA5C70F96146DE1C8FFA4F54B7FA210374B10C73F79FA08D0731BE4F21356D191782EB1D10DEEA5929523B3B4D6D97BF397572E7CEAC24CD55009F822EBE800A3231E1E1FA34A924B899B5B85879FD0CEFC0D60050E04C3171859E54BA888D2F670E22EBE926B0B307A65264FBC08F8 +ct = 4D35734CE1A787632FCB6265044FF2A02747FD6BF7073B5ABC37DD3BB0C2C21E89823ABE75AFA6DD2AF344E79116D42FA852B0F27C1AA4FC157C3515CC3411C828591C27694007776A97BEA1125E4A9544FA0EBC6AD0E3D8DF7145C50B352C17FA988986F67D4AA13A68D56CD9F977BAD4B2868D1A42067B0B1E65C3932E49685E0C4482C78E8AA02DCC86E1333CF39A852CC2C277D07878BFC7EA178E0C8A7FD9D3FD060A495B71C697D9D97FAA6D5F3CCDBD5A9E8899C480066863BBFE0C2AE8726784D48F34D8E8D1C41816C28BFBE241046C784BBA9ECCB1E78E361F9DA657CE48C33B5686BBAB7380C6534F09C2453F0D09828AF76324148239AE89285C54CF2F799C2A1E06A55D119BB093F948182E9567D0521C7F10829213F02F5A26667C819DA17405B001C6BFF1016D5377D40018180356821704B88A3F44EEFC891E20BA0939926FF02E91138DF4EFDBBC74B5C60360267DB6750783018D0A43844E126DC46A3D4BACF5F77CC4B089338C8ED16E225B3229BB81348FD2B6ABDDB758493993C1383AD018F99AAC4FDFA321D7DBDD74A9126AD8812DA5C40B52BCFDABF24465B7C21A42336D187BCEC350EB173CFD736F58F4B491CF4734962257864459F29F1384DB996BB21EE0588EAE9CB32FCE579A8BE349E84EDF5315819E02CDE13CA3540E05D6EE13A0296973A1988D303FC77F0583C321771B33FC39251058C9E6FDCCD2EACD2190048533544B9E2A5A505B359B927E52F9A58AF57F4E7A34BE237776E164C4E81AA546A690411227D492C06B08B88F166B17EF7BC12AE6D02BF9B22628B62C7808785C93E41EC33F585F09CCF49F259BE72BB94A102E134FFC18BF5BD240349750625491F7955C39BF84B7A5948AD5B8FE8A5AEEC2C056BBC83D63238C0697171DD3C593E10EE1A41AB81E9BD08970C4B4423A70D4D719D1E5897C91E09497A7A3AEFC24BD8C7B97874C2A4669B8166434EA4F2975F0076DC390C7FB7C2514E91CD8F148C4336B59C196A9F867D66B1356134BFB5AF8A757349827E5FCEC2CC8B72D286286C9E41F65AC0036241F96E09E5AC176E2410B0734DB60ED82F2BD1AC6C299972804BCB742DDE92893285BE5A325E48740E96B1016F50270EBC9D69395B271F07DC30C2DC2A83906751AB0BEF0C26499DB9B0375F0612F4A054C5BB3CB7744815324C5B52A7ED969ED3475672D84A4EC7151596F586F05D99A7DB12FA15D586B79E3B5D15738D67CA7D223130D15ED9892DE3DBF67BE244FFDC948FE9F9B0E285883882E83E9719AA076BB62FC3DECCA44B7DDD8B65E4A6DEDEFC7DB13B77D77758A1D772672CAD78C369B89E584E42A47DDFD2834BAB0C383DC21FBA65ACCC737564D8099DF2747447404B438460FF4F77B4346026560911C6A0E5A53AD148D8E712D13D693676D41B894E335223AF1766806AB850EBFFA94022B0C2768D09FF78B6C5224571AC5009DD3A305260FB589E8B4B84334CB838070695E889A35BAB3024216A9EE6AE3E75B458C5E222245CBE01528646892252FB5F9F553CD21D2395511C832E854CF0C0E6E1DEFF26D30BB669E0C54C0FFBEF6B98A3D98E455E60155AF70E63DBA2141D4295E58CFDC953B7D444EF2641519445EA3F1E52736FDC8BF891481F20D4144E1E1E7FA4137EEDF0CC1EF6159F89B145A5DC652DF69DEFD86BA25857573DD2823D21D768E64A22374DB37D0BD9FBC7045A84642C05B8583551C3A92BAA3D80F7797F4831C6A87ED33BFEC80033DDF3BF3202D87B5A27D44E98D6AC431961941C9CA71CBDC27B55979554BFF9DAD2FDD8B87D90BC6F1198E572187FB00CA05ACFFA6E193058095ABD82E96FE5D0F774C1DD5D17A5544087D0001AC494B6B5E997AF1E1CA2E85EC0CD44A9620AA7BE79E5179361ED31B9A8F377E41209FBA0D889C24A120544CB3B1E051321B7F5F2382801036DAFF203181CDF5594B57B0FDB98BA65876FF55C350009FE1981CA32C12BCB6B3D6831B584828F17C4B2295FE33BE77C6D1B19D0550E5CDDC550179582614BCA8B924A829B06A330480902E070DFFA1518CC4FCFE3CF140F3EB0875C11DE28CF69225B7B437DEB9CECEE94FF7ECC13A180CD515EC8D8BF910A73E98D53607B4888CAD8BEDE55C5DD0A03AFF46EA6D14B3ADA863B322B7B226D6AFB149D9A4C3174010C365B9EED1E5559E8ED8A069484D4CEEB5E8313ABCE78BD371CDFE32CAB9638B7A035F7E70E4B58024A687CD40C6333EB3840A7F9CE1E0453C2740D09A15625824D7002AA82F0B8B54CF452043DC42B6D22FE7A69FE7A72805F752701B9947E431DFD7E6B69902FD949D121746AC671770081B7181E2F747BE4B871A6391EB2DC074FBA05F53E3BE34783EA60A8D696B405E279167CC2A5B4C808233197F0E63A4D2A1801A2CEED366C1FD891E5E858DF967BA376BE672E35ACFDF998063A76C163CBB3BFEAAF570C984643A7639BA010952633E3CD9F33F8C4C4EE2B2571B75878C7DE454BA106691269869FE351DBE454B82F945473FADEA63EB5111ECF52ADB73AA3C48410C59D39E0EF504AD8FFB16671DD222B0BC31F8CB6A58C3BC4C1379DDAD0112ED517EA2F8B5F962E7F4DA58D222B121B2E68D7826031213CFD91860BB0B6B2E963BD912EDD3176C009A949F4D9C674B5717EE0CC298B60267F8DD272C50635B5C5A51E64DDB8A60325A48805F5688AD93B2ACC0278D77C668E01E7C5C6E841258F25879E8780A856F5F8C9BB29E2A54774C770EA7EC96EE4DD300D62EF8B37C1C6A89006A4DF94D9A51CB6A0F24523251583614ECB4238225F8AD1977833456B3CF445BD2A20F6D600F98F93E425F0AA404F1945FB6870685D0A30F1672AB13DA3B319F54E2503B4FB9B03EE138B0A507F6DF5904802AB742E552A6EF1952DAF50AA19DEA0E0C68B15DDD0A9B9CC772FA418F5D7040652B57549A60924E9A49B9D88AE3C2EC0D7798C2CB7995B0FB1BE415A77E6C168EF1809C83A302886C6F24AC89CB1D45A5282438D7A3405F5E24A887154C83BE075724F666C96F5A09AD807F7E17E3966CCA4BFDFA3D6AA70343D560A50FABA9565196D88CCCA9945D90AA9B90AEF520E392CE444B028FA23C171F47F73419310ABD28253ED4B635A7DF0DFDD6AC1CDFF5BF4B7D3F6172BC6EFB5E2CF010EEB996D08EA1BEA7EBA58F0B349AD1FBE400285C6FC7233B03880AE757C9B15619AD09119454C70186DC69F9C775314787F3CA03884ADEE505966077E93ADCD328BA8EECD73D9E6E1E378D528F1E1BE8E4A231F5C519D57D9F61D8678C74B7AC63EB9DC8EFC71B9ABBAAA7F331F2AC8C7EA36BECE692567E82BA3A47BB9A346EDDE69A1C4E8DBD59C3D83FECBDEB9BBE2AF86C1B4E7A4ED52D638D5D9322D91F6B2D3F8CA604EDDBFCC776AD8FBFD8096B948EC9727D7973470A40F48FA95FBDAA6E8F576D21FAD136F928A7BA6C4DF7A516DDA569EF43570CDEEDF4157B452E3A81D21F896EEE704F0A83A1D24686A49C53760BA2A263A86FC939386D49D4173B2797B2F90E50627EACF1AE36F99C7F361415DC64129629ADB5B4CED8B2E6A76D7C3B8A2CF844258EE0C7345316119C3ECAFFFDAE88D96198CD4E07EA6A41545C0E2BC6951FC41182DE74A83DDE8F2F1B3C4502272FCFFA4650B4E65718137DBE561046BD56AF8A938F899B9C6413AB634531944FDDAB1AAFD7C8EFC784197EE2BAE1847A42A37A4D1D7DE068BF58B86ACFA199D00BE6CA520222B482759F0FF7ECD5CAC3C98A8E8664588D7D623C720336D9E5498FF88FBEA5274C388C8F3E2AD7ED632B58E1E8E6E8DAAAE4377AA3B4B9FFF7632C50B5B114AEF13887C7A68FF8BB989E2EAA2FBCB45FF80782314EB61C72DCD7B4CEB6200EDB316F1D041FCE9103EB12713D6FDAF56A2583A23FC94B6E3B12C3D5AF57D44046A283D1EE247553C35AF3BAE88BD12BCF85C7E16D6BE232BF489588D38DABC84287FBE604F39C15672FFD4FBA05A7D7F0032B16BA2E7D1E389B692324370E5A0905370E1A6C62BAB8CC8999A12CDF912CF1D001307AEDF40D7AD18418B7E1E83CB9852E0E03116869CAA207990C2D7D005621D6B914789287591C678611E4CD7684AD13A8C26A553BAB4B88BCA264F3DEDACABFC79BC08828F6DF5862A2567B7EEB5A6D1A647FABA1124EF1DE031DCFE2E021512BFA8F1AA96ACDAC0408919F3F421C015014195D7C369DD9F35BB7B33CD09651FF58B39311A13EB7F62D231724F397AE6D2574C7AB465096DC8AC8410C81E1162340F13D09DFC63F36979B343B41A44B20A67358D070C72ED1C61F50E068E9A7BFB2EA0C0DF4C640E798DA15E5F03364D42F011A9A049084A6BECD1AFEC926CAC45CE0C852A32F77E23D33CBA56399A56C89E5DEBBEB226E68C66C7DAA2EBDA4B2B130BF1C1EB5B1DE0A4578F56412D54984509A6D7E323FB7CC71D5841613348216709DC1271D7101B71DCCA19889F1BB78D4D99E3D195B92BC44B1527364167360FE8344D9834CA4B21035A81B2DD1D2921FB790874547917103D523D41A684320AF6A9259FA469AE2DA41DCD7862B095F17D5FF13A97E4FD9C5E304B78BD2004F042FD34F8F80788ED4DA2CB33AB2B973CDA41A6C86C10E63A7625D00DFAF7EAF91BBED43BC5E9A2C595BE6C128991FD55BC9C982F7F4B6763B02A2B86A418C17143193AA0B0486B3F8A9D0AD58F105A464AAFA51D6A1BE3B835FFF7CC22CCF530536C9226811909EE18311D3A72DEDA5577F6C3400781CE21035D25FF6AC696B478DFEF0DDA98ECC49ACA1A9F3AEE28237C717F43DF1C4E70541D2CE3F9D9A8AF570F9644FFF739878956DD8392B527D8C9D5F5715B79B1C8D1FCCA3A559423B250DCD12DDD2B0D3DE399CDCC4CD37D978AA37777350152447BC08F6A6A9E7FF871C3AACB9487B27E748517D615B30374E5A6427A26B2ED43E5D361382632AD8FE77DF4ACE67AAC1BE42324B9A8882852BD92A6721AF78BF5AB335D53DD1B0B6C783A79FF61814A970D2055F33B0155559F6694E9EA86AC2C15BC085E6300696B526A7C80B2E5BFFA0B8416285ED84EF5CCA0D4A8194BF0073EEC7F4BB53E24D6B2D692E93FC6226A37E992642208664BF5E8F0FFA7C2B46A2C458315B61AE08A1208E16969589A9F07DF35958DE00A3ABDF13D9C6FC0C96F81F78836A9B88A72581265D80A9529DBAF80067AE09ED6D43F32522C2D8D1A3A749C4531DDFCB8E8B5BC8A7550FD7921424D034229918D8C61FCDAF850FC14D055125DA5D3321A6BCBCE755F0CE5E21DDFA0D009BB09F69065314493BDCC7F4F5154500786538A93295E63FAAC41ABA7A1901136EE65EDF2C7D68A9587147926E7047C34EBA622DC733A96B0FE98EA7FD93CD5E50CA5D077E70AEB4E466AFF675AE09964BA82F84522AFD7AD876E81466D0419FAD5200D91D4B0F5792C7D96B29A6F9808C71ECD9AAB30B1EFB313176944D42523DC9D23BF8DDF54D2C883ED2C552DFBE5F1B4625A5679CE9747DF46C035AAA26E8463FBC096B682A37D18C54B0B27397B86CF76804E7BD459DD7C400360E5ACADAEEAFF47C21503AED2F65D7A23313BEB80CAFE9F7C1AE33940F75CF567F069654FFCB57D59595C5EC2EDD27E6B373A4158AAFE38DF31A1D2397539133F0DBFBD0CCD82EB4AC9FA886D6464868DB20DE9C89719CBCD0C1DE202864460D5B5D5B92F66D18A8ABEF3E61F3D7AD4DF1BDE44D460119BF0D981DA327901FD7D10E3BA47CF3998AE42E0008361C6D8594767E6BF8B63FED04B91B277C79D618EB552F398900618356E61878D9207BBE4D7831FD5FE669616DD9ED602ED66BB8D968A8FF524233C8584DE2A4976A8075515B52E6E114F4BFE2656A348E11A0DC78D9568B5D5C1DCD5C013F94FA3EA4A4DBEF9FC4CC48884B16A4699ACD5B8541BA20892510F7FB4EF1240BD41496F54D03AEAFE0765A65764099BBF78BEE6C4E7EB0920D0908F6AC634389B420D8F14A1B199F64790B79B101F70591163C5EA8614EB09462A40D6018C2064857F36CB8E131983ADEE5A6EB9B66F67BD149F7E6340CFE33B98847C9E4D5062997442ECC29851A7FC52066EC6FBBE9EE5ADD488267375DA6FE840CA1993538EB74AE45C85F2866BC2BB3A42237C3F414E5EB0FF7AA54C7ED7B27470F4A170D59FC8A0D3ECE42BB1CC7BF4DDE23D20F5C75E200A216C84F4C617CE31520BA3AE25C2185246FAAD3DC40206D235F4A8FE7C576442912229D93B7E9C87B5C8A136E78C0CA0BB85E97301A45CFC67D1970399B53D8C6F11BB43299788BE791A5FD43FCBB625FBF1FA4B7F99E19E04B61546C797534AD6959B634E9445871C5931521AE4FDF6A838009E3F4C3F60D8539AD945E51661021D1CEFCDC9814B14B8132F6A02FA6E4E2AEB13ED9E706C0178206DE6BBE02A538C3BC704D54C311C9F15F0EF9A217152912D524869969D2C31FAB22691284FF22CD3D4BA5645A45AD5BC301B848630BC5399996C62875D8BAAF3DD74AA65F651833B36437267F86202028EC8FFA914B68B528F7DFA60B7247A1F1ACC9CB0DD05B6B23676757DE5860A98460030D334A740585FCAAE9A08AC78D86DF7CBD50480CF60BF5C782A5DF1B0997054F0B08646A21BE55CBEC48FAB854E6F1B617E597DCCA678EFE4A078E4466B92B3B1EA32128915839B65B9E010479C9C0F06F860926F7D2B29287201AB4BA36C36DEE4FD1D75777353CFB5A36486EF8AAE15184F75E1CBFCA4E78D117C30AAB18852A89620E0D86BF64B76E09FA1F898DCCC5CE361AB1ABF34E02A6EF9B09932A0D61525C835518F5E37B12E58E58A0A37E2D4D80B567FB342B7544348805029F50CC3D62B5E187E1125DE1022C9CF846CA0998F5DFE00AABD16395A15998770B2FA49BB7114796EDBB3BAFD5C615730CAF527D3C4679873C0EA24C967686EDF30629A90F9CF130DC12AB17E7A5AEDAED9942F0F93CE2D6AA1B046A5B931476931C1DCDD97CB46B85D67AF16A08B9768EB096838B5604CDFA9F9ED9BDAED533F3271EBAADC7A046C2104B3E4998FEC2A08C5FE8FB8EE739C3F6B0EC767E7E0C15FEADB010F1CE6F0D57B90D83037080F99E667B1D1006E70B025E4A8E900B3C7E1D74963C3B39250C6516B1B4FE36FCAD4DD251A5C6AE13384ED5DB2C10ACF27974045A83567E5777961E1AE529A072778AC1B5F98BA1FE46EBF91C787945355677B76CA67B1E898D61F1AAE731A9DCCC199F805E381AC31D303129786CE7C87B496F36D9FE1C7471A15F5DD2977B74A56100CD8AB794171A33AFFE1B66F61AFF7957F5A29AF6ACF9C49CCBF09FAB46C4ED2154D96A626293C20E19344E4CA10D361CFEB6F1578CCE811065684CA650465AA4DB69284705A5D6D2E78AF4F81DF81F041A88519F5D22F99A8F85257B277E60B3924947B3D391EE4147BDAC8FA341F3AC951340FF0CCD405FF33E3EF202A4545A533134E73DD7C9CB4BF2B35F3A57305891BFEE68E8C5311811EBF51DBB530D9BD886F9E0E18CE1DF2ED2CD7DB42E4B95321870286B5C9FD3B91967AE50FC26AEBC6DEB9CECD70BC21D0096071FD84D3D334E38404C78484717EF361E57B03999AC514E5F45FAF6B005C79CD7A5039C9433958F07C8F4C138350179C77C5F02915D0FC3A245F0613DE4DFC5D484B177F4D5DB4FF4255156778CAE7C56C7A17DF031413432B571C7A6274C7ED7E45861636A5F28882F8398B829B75490E00A3F49C05B8C3CE5F962CE76CDD8739CFF99788B469F0D8D428779A492B459B21E2107AF31CDD8B8EA7515AF2068E70D56DB115EF18699F3C5BDB9AC382690359779C168A6D71CF3BFC08EB5ECF719C54E498176DDFDFA48DF8AD5381E192EDC6365A848C1B5636C2FF056C2ECF8D9B264E0EE1E5D94E5021E07C2AA9586FD821C200EDC0B7843349B3AC83E2E76EE3AAD3C4AF20CF8E189F74D13EAEF06C1F86F5F99D8529CF364024287706493CAFAE45D9336AABF9864BC40C49D960FC2D29128F936CBAEBCAEF5AD1E2E52C7DAE9DB09A5D0A42FCE4430BB76456B7AEB70F7091FEA99C84E3CBD057539FEB816CFAE197BE9D6584B4536270BFE4154080A10A2F5E62EEBD61065F7755A22BC9BF005F688221E42C6BCD26130D832348F103BD39D49EE836F993A56637E38D0440797E949CBA59BF6BF0D7C3D5F82CF7840B5862C771A7C9EE6E28CD9E7A5095054DF827341AEA359193477B9618639A0B2922F843BC882990D33AA29C29D404BCA15E5985DA98AADF3B21E6A4EA5E8A2D025AB092DA5EC3795DF6053F1EE5C16E471F92DBA466655D495DA084212FB810986C827EC4EB623A0720AABC9EE8E04CA21BF824CC53C5FA9E495744560749886FE2A8F61F5006F7F8EBA255416302E08456B5E3376B33BFF4EC5E9019B4082EDD50B4DBED313F72EE69170469DE5F15B62AF6D89B0AB20E4D365C02E9BA611D1CCC32C8231C3B478C5F11E4A417B8E7DA38CAE00CCB2897DA507034E88AE9172A5F6DB5DB488BA7643CE82698200C4638FE02AC9290D0591EFDEE581BCF94213A3347B66A4B55E9E05A639E3CE7F448971CF092BB7EF68398EDA63C86D64B0355BA67969655B2C3F23D31FDBC2CB765B6D3CE210F10FE3A0338C3F8B64B190804A4E4AD6D3E56122CAFF7075C2EDC95FC9387D44ACA431FEFBBAAA3B38F1EAFE6AEE334F6578D59BA2F75EB3E927AB4EE5572A1CF7E0A6E784ECD6104F4C722F7111344C5ABE15EF385BC137A55D38CF63F8FA5638EDCC18DE2CB50E3EF13BA69285AA97D019FF341C4B1BAF64C850AEEADE0B0D16E5509AE93E095AB52DF7A341D8603233CCA11416AC687283365CB9A3BBD61DE4FB35073900BDF11CA6F4A65813469F78B337FD70988F8BC0332FFD007745A18B225A5E2ABCEC74C1028A890957A9CA6B24393D17FC4C708191C60D5B6AF1ABE5FEE64A7CA8B078EC1EBBA5A9F77A7BE62FCC408C46033B926B6E13A8E73E5D475AF9C06C360D9190AB458A13ACC7807090E7DAA7D4B52C7CB7F5F16711F902184E2FBA69718C060C67C6B34F03402A3EA47932C353D6E7BA403810DC0CFF1A3722BD7CB61C4EEC545351C90EA6C1836694D5E1ED55277EE75485D8F11F65A758C07A8C31CE4B93900B8258886559DA6CDE1C942A35DE813BDA8283AFF30040E5C54C508CE065FD3D71990717B1C671C9116A43240D344973E02DC13559BFEC430B149EB6074F4AF45D8DA57DE719175F983A31ABDFFD5EEBB5178576CEEB55FA7B88594AAFE5328408EBCB5D9C895488AE1D3BAA376BA021615D7CB9660C93FAC925619B2249DD9E08BB7F8C2C52054F107EAD823CF6776E792C5769EDD260B9E5F7800E0E395D6DBB24C3D463803D7A0D301597873D3E2E5791275D7E27ED6038E2420427960022C71C161F4658F2679705074EDC8BAA4186B9F3E4B9CDFEBD450660E7808E1A50FF19F65D757A76A6C2B4EF2B0C478BA79E2058D2845E02B17BD261CE638226867975EC7C1A1026A77E772706E79CA977F996A5F4B703DD2243862D7D769A98DFC3880D4631698340D77A31F6722738650D2C4EC125B3229659DC118F2F9E98C4542613718E71C0E6FA0C950101C2802C7D70E82399E98C5F62F7165E0E533997A5997EA617EB8659376E6E6707274A83266EB9710B233FE96D2006BD8CAEBCF77ABFCD9E5D6D226A6E5536866B69F937EDB3565F562B4963C86F0F5A21298BAF88F24F8887CB050D028C2818ECC388552623E27D05F049C0DDD5A52B1EE9168E95DEDB51574866846535FBCDC410393F7457428D7D1D31A62AABFBB5FADA1AB70CBE74CBF3913EB648FC7E1CA3AB1D4CC2D0C9150884858B5143FD5507CE28B629CC264D2DAF36954894FD69320533A3C50A56964244CB2C63BFD01299F52DA45A6DF88A59AD543A6EB73064C4985641BBFD0B084C77A2CA36A7A13A6DBF8A6241FD8B39496D7133B2FBCCE956CB30FFB5297CCF95664216C5FE7748618ACD5BD801029E5914BDBAFC8367791D69C31ACF9BFFAEF646547D544DBCAFC3A66F84241F99BB90BDF05C27060B72BF14860322C67F6D220B4F0CAE56AC8D59AEC614C72D8815C3CB0C2864BBE98CFD375051030A90ADAE737844BFA0C870C0C30BFC2CB65117D78421E2F8D9F84CE49BD34542169B06496250700C6DA624C5FD1840430A46AA7930350897A675AD742B7D0B80404C75E4792B7417E16825550633B5A2FF8D7BB80D468A6EE7F1E65BB86F257E93775E55FEEFEC37C8C9139159948B4B059C753930B167B78FAD6FB8EE340BA1E892A2D4506AE5314846B8B78F0AF59ED33CA700A43B083C784DC4B8E8B270A8D55AEA97C9CBA5836AEF4B51828ACDE4E176DFB988D1D543BB318A9B4CDA620E145B188025F9E5236AF934D51D0323C60D4AF89C2489EE368CEB1F8D6ACE5272A1C946B1E7289751ADC536257CBFFD140A1802E6AE29A1D36732BAFC333FC3078107CFA33CC6193551AFEDFA2805E5181B24D55CA8379B16E494042C1FBF4BCCD411E67630A0F6629827CC8D1D73BF8697B9325686357FF6CF6357D554ADAD15707745914A3DDC046C17C1EFE0A8D35E0B69E2A31B78B1057C7D04B985CEDE7194B3F1EEDC24707E0A83F7A9327F88FA905A5FF961BB9502C007D6765FAB715AE12C92095DA89DD8A47F904D970AC50D20D5C6CF874B97B14FB996EE090AE749B572167DBEBA6E0565FD106541151AF8F43659EF3265AC6A8A55A1173EB396A3BD6A6C862ABDEF3998B61583CE999E5F5FE687349C57A4F2FB24E2CA71CBB368EC70D944BCBC8807C018F07B967B7ABAEEE53777A2F095339CC2DCBC1FE6BB7CECDC2F7C78023745B662DA3F5B6254005985EFC1019B8E669BC105DE6B682FC7F7F69C7172B6003177079A098C50912D953F0A69554FC88015E5267712999C6897350D6402C695B8183D5BB4DBDE586D8CF5C7E4503434007365897651FB48B55F1839F058EC4557561753EC7731DABA93AF0437E184B5B4790874316786FB0222BBA896640354FEFE45FA40EB71C33E465D66A55F95AFF304977F091BB468FAF9F4CD34FD1E531FE8DDBDAD2C722BE88DF5DD629C12E18C07F4FCB9119AD5B4DD955DC81F241E16F76F20B9E49D30D3816B44BC655B31BFD53A6BCF1B5993E6279A626B241E037E85474A4473EADD155FCB35B8F9DF75504451C193F37DAC34C974E1AE41CF8EBD633E39426E0CDAE0D62E663CC16F18665BB85BDC4508580F0B311063F93512D4D209E9B8779915DCC14C1A3D2C1162C6DAC2233A702C22561A330FBE2D546865C695218A3FF9F5B4978AA073C9CDA91E478FB0E1D7894227B4D4D44DC31D2E63E955732DC170111A3AFF771A82E8BCCCBC6BBE6154E79C8EBD7F08E7DA97490861E6EFFAEDE7BBDF490FA3515BEF8CAE522F08DCECD9CEE36E55E2D3EBC82C346A2F248E79C525D888BDCF43D4BFB884A33AA1F04DA527DBF100616BA8B35F286C2EBD60F94E332DA80697070B4026BDE6CC5E64A9F85C0FE4EEE1D069ADEB21BACCA1C2C753CF952FDC6287AD5DC4604CAFDA5032AB3923002C91956B16C8D4C4AE7B81CEE8D2031DFA35CB19E744A8BAB7B56C55C379C6A50966D915FC78551EBF3723DED7F80616AABA7B11EF0172D33D87C352FAE89C4AD91FC3290FF3D9CE078EDE683C52C163268D7525C4C4934D6CC2D4E6D8FF0DE261AA06C048039D9AA4F294DCE923E228A6D5D5C5907BC6497448372BD09BC7AD1BEAC251DBC352051E8E07F7A184FDB1A098E2D45DF3277818A3CC24B07D3A442087463D6A2BC309BF576AD03B39B6CF60893446F4C5A2D2A64A5BA383FB5A148B4DCB368B0EE7E9B3640267F50E0C3AF0786B78559D761EA3F27D566AD4C836B5116C1A4DB2A3454767D3B5DBF75C80BD2583ECC18496A1240921BAB673C47893AF38A72A37C9E1BDC2556B95FF7C5DD4AFE0A37CEC60A607F348BE73A65E157355EE190A414ADE954232CCC77951589E21640AA84C5BD3DE83E11700CDDB08DB9D89C6B7CA90A166DDA1C916F31B855EBB0CB2D6C37948FD99302238D630A4BB0B10F19D6887E1B4602771453EC1DA4E7A4982B222F585418EEA3CCD74AFD66B7BA8E76D3E168B409F71E80F7A16678FC43BF7E75A08E6F3D6C4DCC13E66265DD3E5BB8A68A4307635BB29F0FDB43D191EAA1AF6A79E2FA0EE8318045347BA182C8E4B533F927698D001B541C3435F45EDC65AC43D443298DA09800C84BD8EF72053E76285F1F1FF9AD39D6BF511CED62A8C175533AF24CCE931212110582DF82ADE9EFC0EFFC64F0A6058165E92438F322055FC3AD682EC6EE1E8B57791E8510138854DB5238D2364451A2C88376BBCE2B9DA0104878C8EEF920E6A0A9215BDF10428A1A6557613A5866154BD552BE01E607EEDA05F50CABBE920F149A38477A50B6D3439AC21C9214D6E9E8145CBEF62B77E0F2D193DDF449ACEBED2E89493D0E1F4FA4A2057A766935868C69C1C397EAE3A926D90BB9BB0D5FAE780A847185C81A07CF724F24F2EA9C534615E1FB6C2FA6C4980E8EB561CD25C5C1657F19980B6981EFA2F447F644766567BC7ADE89ADC0241C8077FB705EEB2FE568C2BC03ADE978B416BAD43DB87878CA519427A921055294BDC6DDCCC800700D53404AD34FBE6766D4EB5FD1C4805412BDAD507F2B64CB457DB79CE6A16AE9CBED0165C1A33C5D6D5CA12202339E7DE75760A756266C52FB518FB048C9812EA794413F77C6AAE13E3E864584145B855A6905164343A64115757BAB251B139EDC55915788C3E7FE71243BAF7ABA56B76BBD03064C67D1C6615022BF234E125DA32F314441D9B1F9E07AB3571EA51B4A9DFB83AA01F4B721FD58E282FE6EBEAC244AA7EBDE775BCD3B398D18D0C8CCC2776D92062E62D77043983419D42566570EEC9BA24079BEC109CF13D2386A9BA053F542B12BC1AEBD1EC411CE9C1B38BC7EA2F74C2EBF7F791F0D2C89A8BD0B7BBEB2A67AF262902A6B43110A6DF73F72CEB1FE9F80AD2E6C8B1487BE1E342FE4E9C6034D2FA48FFC34EBF088BC2C3D3ECD5A93DAC6B2E25B729D05B50B79FCFAE7261C4521E85EBC6112CF7C0E30F0552C6867EBBFCD24E7F6A9A0B0E7CDD93BCD6EF419D23A7C1F5D3DE7242AFC462CF0EDD7A1A3521E4AA69BB9086CEE2AA5E995F37BEAEF696331EDFC7EDE4A69575F7037FBB19C7CB411DB81DF3A329904F0E19577C4FBC9B5615C4B158B2D6E610F37B93EB5277EAF3D71CC4B2BAB26AAED0FC9C4F6049B89FF7D3AEA0D74C5FC4F84F9DA183A5438C535FECBD54CBC1D0437A3046694391496A453FEF185BE999720B31C095DE5E0E5489715E5577042A111060B7B2161C4DE0117091E25DEADAFABA57D1E76AA6E665736E34160D2233D060CC4D0D46E438736B1918BE5842114D15A0C9F9F4FE63C8DCA69259BCB00D33AAD05BB1304665E7B2C579AF396B511964C7ED1D0374A82096923C1508221107B12B74C60EFB58C03460D9653A56DCD0FA80E72D678697D4BF8868176D3DAD434C5649CEFFC8115D1608F36AEBD71BBED2772A4A964A32EDE455D96D061DCD4E14EE2370F30A16F08078B440D817831190E9DDEB3539C179EC227B9001C1E33DFD8DA21F68ED1E08CBB0173E0D1967A5FFB8732DCE7E53C33D6D5CCD4F84C2F57A8F9C99A5C91B2C883175888E6EE5304104A24F9303B2EF4F5A3B4EEF6ADA2FEE49CC6EDB05FC69BA83FD96BA585E6C6A080FA2935AF888304C7A52FEF3EE3ED1E0741115F55B01F3D789501233314574F6FB1DA26F4C519F9EE58E80C2EDA280B1823A11A02D547EAF72D7F1DEDA47657DE9D3038BCF4783316C2925CBF14D8D5E8C7E86BD3584AACAD5172897D1B4517CF02EDB78B87DDE00B06EBF45FD903CADE69C09647F867FC80CD6E2F101A86A065761EAB89ACFAA2B88FFCBE3EE3D95F88D2922D38A156ACB86D175970B0251F282216BB59B0DA1994A27C606A111D6208B44AFC9C3776577EB85930B7107E36A9358C0A780CF9E7BEB561BD5005D8BE48652E4ECF83279BDA2AD527E34B53234803FFE7D344E421480610C1B5CBFE24E423C49785AA73F237DFE6BA815B08179702E58FE61A72F360028D80C5E1579FE0DC415C3C017F27039C69448FCE5B1B72A0EAEC50BAAD2FB62B53ECFB800C7BE9989268F32B36F2E455C231617C02B12EBB21EB1C68DDD48E0E6244D31A36B04FFD7504485688F95459A395719A27A0A7EE1DD64837916084D7519A6581EFCCCF159A5B31C56BF0747AEAF39E08025D45A1F6FD6433F96F2E6E91B69BD786FEE4D47C00FF3D9873BDA6A4047C61880BF55993E413413D69E9B99AF394AD8683B09DDDC0DA71D2267A723EA8BA8170FCDC91F4D2649F6FBF33E2FF67B6C1B3A6A7F29AA7BCFC28861360C8BAB1D8619DDD6251935418B52E3D4553B7063E30DFAC474C6029FC5FF26D4B77E267E2D3AE7D0D2044737808211EC14A1A6DD4A40752BCD3C8DFC33F9A281F4C63493B8CDC48281DFD1C7D6E7B94B575F4A0B686ECA951173BBE32F3CE7380B2E0B2511EF53E8FEBFAA664D70646E73F733A6D7FB029A0E32B1FB7491A2EF3D7BCD48D3D214BC82E720174DA68282400A75B834B41E6702F1431414984B7E2E1F9E29868DA1F85D6DEC3C53F467A9BAD4D6678B2B56EB591C86B091F1F294A940D5C1503A9E6359B5B210372C58351FB8A81A2D8FF7F84888B2D970CBD49256B8C1FDB93ABAA6CCE68FF054B4A084D12AC45A4D4AA9324399CDFB6E4F2B0AA363A8E9B47AE2097E5B36DF09A3360AB3D5FFB8552DDC3BA75FA59D3DDB6B0A66C3EFA528C58B1E069CE31F13C75EE76F0CC86190EFB0738793B057A822F3427D8A77F039CF2ED63E84FDC4C123ADC7EFE9C5EE9E9E57C1A9DF382C01CC401B04C72E874B3A3CCA874C6086A5E259845B2B1DCE6E7670FEC2D7D227A8BD4C56C226F5BD6ED3E07CC4346A79C7683ABD5CE354D1B240738D6DF1DF611B72C11E6D29BD984DBE02B2AC91C34034F8F899CC0D7085A097736B5C549DA27586E5DD0E34A4E52C686A49B3636C0B3396A68D5D65DA47DBE29A760BD17FE6B796264ED57EFD0CB9FAAC1A1F091255D1295B14CC3954FF755B6091E4C8E462C468D29A45CB210EC6B2F20C7CA7A75BC5D0F584A1BB81D018A0C0FCBA19DA09FEC794882070110612DF90B04E0AB182CDEF62343FDF9677D17BCF34A80C9999855912FEE8C3E494B1E9B2EADB1D5ECA058ABF3783F80ABE40D3925129601E51D55CA2B9241869723AE9EDC29240BCCFC201705632BF7AC3C43E2FCDE41DC0363D4DB5C0F678AB5A0623E82D6023BF63852307AAD680FC894EC985785CEE023BAF4812DFB3A495CEE2C7C4493D2EA60354879CC8BED8F298AE83E7EEA6221CE43B635DBA3D6C6D8D7D62403983C190582310858317205F2C689F31360FE82AF02B3943471E5DB72BAD4190933DDA53B2C888C2ED8CA1EE42AD8FC4C32E5DB0E6819A99889E7DDE5FD084724D3D71D53F09AF59EDE317C22420A8DA41AC4098A0662B931B1188F79C2CEF852F7B62DAF30FE01FD68438288A6C970C2FC9DE77BEBBA498E2FAD0A860C35248E64E93201B4649374716290966C0C8BF0939A5FA86246145714A35D75471BB1C4DCD49220EFCF2DB8D3320C8E76DBCAD82E03E799DDEFFC5F03FCD39ED48A66CBC22CAA746B6B601852EBA0F2F32BAD505FC2671BF8B9A27EFAECF3B1DA63ED88A4AF978A3B089B6F3942ACA28A166BA137EEFB359E0FA1E197529A45BA9A68E85675554CCBE9F319A21A63B68BEFD142EAAE624C8E0FB394FB30F830D1457F33831DA0355A338CE2B79333F5DF5158609EE66A49D41704D479880C542C7A5287989AD32FE8224B65AC10E799493C11E56BF0516E38CA89147AF841B62441C581C7C685BEF9E5D855ACA721312190F396E5DBE94A7FE8E78381722D37566D11405D2A310EC61EB78406B5DBE1F42C193398820C27C4F72D3CF014273850F0135B68C09038968FF136453A35D0DF9E3134F359DD33C4861C03CAE705798C260F7AC9BBFD053D2FF3D4DCFEE36C26E3A54C0FCCEBA159F0048F6F37519878A92A75E9981D63526D691B87ABBB4B4EC08AA359D9F66E6AE7A52CCE5954A6B56D08361974F533F36879B4B8D60D9B682CC1677B0D7001B18C7EF8A947EBABAF4035D276205C9CE6C7836AB268FF4005C14623DF6D555CDC29D03F737D851F81CC020C6B2532EF5D643AFFD446C8A2F162CCF12FC45D9115BFDA37FAA030EDB3EC75AB5F0528F6812A93FA78617393ACE595E3D2351DBC9EB495C1B3160C1023E25B6FA8DEDF99BDA1053F38CE35943F4BE274BC5D07EB6E6AB06BA13E677195829FF2B37DD6B59D35FF15E1259B842BEAA327F0BDBB1F732416674C2BBDD2632D7143B7186B209F1F46FF1EF123F91D9192F429DA1204C12C8FA86DFC31450D5CB3B0C43DEE5878D1F2D3519B7460A65A7066A5D70550E4B5BBD9BB457BCDCFEB458285EBEDB5F72665230B62B2D8AA4A658B4814EB4549A6D0AAE9423CF75A4253E045C24078DEDE774EB7A95DA4685F19F8F854A5E0C2D31924936B3DD8CE0E3B0A242C4E485F240EE956B429439274E0A7CBE109272F33B8BFF3FB8BE6D0DABB5A190AC909A31727A4C1A022FEF3955746DE9FF6EE43BA4DFD324BF9FB5E6A345D4CC35019DD961B34C233EEEAAE8B7B0F1676315A951E5DEB573E05C233D8F26F629DD2A5271AF294027ADB5ABA624C0DF20EF6136158B5151D72E9BDFDD289ABD70C904F317A28352EE0776C1AF7B8989D0014F33BE935DFD919EFDBE5552252A42D84AAB7FF95F315FFF75F764E0F27F27E74258D414C01B908CDE6D0A80E87B75C3F3DE7BDE1292DC94CEE362B9EC7EB9E64FF1AD962887998F4FD170921F1111CE49FC0446E6E189228219369730DCA874853CA58B44A9F06327F4B41883CD299B416E6F230975E8628A4961C6081AB20554D37109D1C65200A0407FB1EF2EE72FA25DA0CFED95BC5F33404BF17FCDE84D9DD40F520688CA0DE2D2548A850988627B9F281E38F46356B00DEFB9978C90F33949D4094D4530F0C9FFD9AAB25E6687523D0F43B77D8DEF65F94D847918E077291CAC7B5C0A55E74515280A24E9EAA7BE55FB67398100A001BD68F6E30A250C71AC9670729A73E75A6AB20849A361F008261D4FB3622D75B63CC7D8D93A50917093EECA446F1E894FE54CCAC51F3F3EA63D707F15465EB7E8C23569C65156FA5089247C23C1C7C6F1FB5AC3DA689E8C6CB684A64FDA63184CBC37077707C6E2EC775F542FB64EB2A69E596796B72ECB2BFE27068DB48F0AA0AFF335CA0F90F504C59A22A7B1DD4DCA2B6BF9375E3115CE6819A484358BC2605193688EB9BF87A3BF36616425CA72B94E45B8D8FC9487253EF2B034190B120D33FAD28A20E7C35307BFED50F7C3FB2A3AB5502BD940FAC4D745B3C412D08767507F73386CE7FF00ED735043445D20CE0C854FA7B75A60DB582F68D09888336943371A42EDF1AA0AC706FA16AE9BBA77A2955DBF9F1735856021F8FE108A650B8678A554BA0FD393F9EDEF4F89ECDCC8A764761FF259F1149D481ACAC7B01827BE66270840C52454E36DCB6E664155F564E4538FD80465B5FAE444D0B51C841BCA924EF78039C9F35A73A2402CFAC7ECBB1C30D2DE41DE303C7E2694A3C5C229CF25157A962113AFE15FDBA474A7F7F2A3BEFCE6240423A620C6DA3EF85BFE709AC3A7152182F1CC504A69998BFF864CB7159B2BC39FC295C2D3E3ACDBB82D91B86C919335007897C119EE5815E9611E1DA30CAA9392297E5EB2EC4CCFDE44892BF68EC65AC5089FE56CD06D7FBEC40D1A5DBC72D19F93D2A734A907D98F27D0633655AD92DB938D63151E620BA4919B6EFF89D3D1377D5424194031BB2496481100DFB62D14D51DFB05103F91DA5824E8782308E0AA52894061854872A7B9B63B1D72AA727C1AF6FB366950B6C0BE8D932363F6882FE2C955041520AB6A9B9DBAD817D2AFAC3A3C3478433A65FEC082188882E3A7260D3E62D5B9890120FB74E1EE8FAD5A83E20678517F9D1BD0B4FED7F07A43C1B1456114CC25C1FCBF4A6300B446CABA8E1F42479AAD99C351D4B76926CE7C31047FC83929F574B1CAC75B968164F8ABF65D4302A30A5BC5BD545B7C1CD540B9C530D40778835260A7187BBA1662EFB23AA11707FCE1D18E2A39DF87802B6F2CCC74048FB44C9B65FB6BB672846579F564D8E173245F61039172FD1E43CE187A0E9E673481E3102C6B242B472191FF9C32D5124CE840424229BF77F00EF94C224A4CABECEA72B5D9A31157940C15D30677974BDC6A58B51EAB9426E71449EF68F608675BE69350D2CE1777FA1DA393C835FA944ABB87CB1F060F87090EF365B21DB4117D0A2C0E1EB1D13A305217AE9241CE53A8E50319F6197C6EB6D0F964E02D2C85974610C4BB08376223A90BC7C123E5567974EDB2CA416FA92053BDAEE9805029BC56DDECAE14807B1170B148BF5CE31623FE6A981962ECB837BD5FB90863C7153D2A6C98810AE109B19B43F1C5C835318B2E54B6368DE19C8C55EBAC5FCF400DABF7536D6050BBC9763F410A8426AEF1AF3839162BFABC159D54027480AD06BA7EEA9D39ACE76332B0C8819B7E5AED882406F20C43A23D04795173BE9BFC816CE74E7B257DD73E21ECEB8CC6F8C62B90DA6214290D3AEE2F1D5964BBC760F9C522E214088003F87ED820EFD0D69AE28748C71E84CC3BB7E1BFBF810A1BFBEA535608A44F772ACF575320380ED27D5C8474711F23021F7BE762159B8F348FD70C5B71EBFCA3443ED1F4C2ECF24D5F09776E7D07F92D0760BB32DC4AD7D72EF951AC9440BEC6812FCA14DAD0E5FB8B126499A5C49133D1D734FF4FF5554FB238153908B16C05437BF208ED210F26A8092A54897B5500ADFA29E51FA6DAA827881514BBE28A0746005EC037B3AA5F844DFF16FA52664C10C5F6E6D6598CD105226D2D60B89088E29AD1DA46371C901007CB3AF95F857D6764625B5F53E6A6D5F7D4BC7173D89C5B7612790E07C5EC36C7D0C14F767B86D736F25D3BA575BD7D5BA679B76F1528B7BAD86A3FE57D854C5F8FC3D36E718A0A031004195F3CE3DB503C11698608FC1ECCDE0F2FA88DCA0002D9DA63F7382F97211EFE9E4CE472E1E14FE753FA513557684CD3DE7B968A2F574F45910129B674C8A6132215D9AA2AEC706495D0682D2EE56CEC3118D4C3E2CB9DA4F6E9D7BC6D4D785D44F3E11A10A57C6C95D94C818B710FB649BE3FB2D5754F62AACCA6428F947AAC712D3384BAAF6E3621D8FBFF82722E3576A21A604D8F11FCE626E5C26818255CA140CC887EEF50CD3EB378B25C0CCAF176086325128DA9F20AF8129F0C6B6C421397543A7DAA813F776798F964D5F78AEFCEECB13F6587BEBCF52128B04F8F3132EAAA2F8786F99E8749893B09821F1A0E9410E535687409B61A29B3FBDB4DE68B8B16110AB935C0C371C65C820DD7C4408530728FA79A10EBA688562BE0C5D86091FF867268E5E446A0BD7D9DCCFCC9708CFD1BFAC4551446E85D3C319F1BBD3893FAF55F3FC180A23A121418197A985E1E6B9A69BC6C2BFD53B83750355CAA9C2E4A3245E4DCB72ED15B7AD53BD4121AADB33BCA4948F27BBEA9B0CC9B5581A2D21B343173EB8ABBB51BB76A6E6F04C5DF065BAAE371DCE42084523689C9B9DEFAF07D86F70A1DB785337BC0763B26D6F4F6F2C0AB2593F7777B50E5F5D81031DE9EF67A0366F0826E12F87ABBF7D115065D640AAF2C49D6534B35C46E8C7D22C61B52E908EF5041AE3742BD9DBEACA79E8A2D05B6D2ECA2E3DBAEA122AB551A76D95872E574CA921D562B7DD665298EAE112DB663E2771AA459E33FE43CCD0F33054F55E3911674D7EBFDC2BC7CF7E5502665C40F4F1666230B8B19946FF88F733EE8D5EBB4F16B3CB8C679081F1439B761B7F134B1F862E1EDC7BE6ACA2D5FA94DA5964235ABD0993D21280F6175218C5BCEA4133C26DFFAFA5C870124679272A20A07AD423E4D81DE7BAE2F7963064728A156DBB58CD458C338BCE3924129F7908A06916454AD9B52F871407FB60315B23D601042D5E2D8446A260534668AE84AD8AC78F47FA0B3F9A861E2A9655D011F0449E1B4E045FCEF8A9CFCBE8EB541200BFF13806F131E5EC03EFEC816151BF4456225417BF544DE545B7EC2DFF43A682C3A2EAF4B6DFA5C4EFF2AC19CC6D28F8E067A872742A7DD675825E0B232EE518458238F79381BCD77EE60839126C5ADB83F9840DF68BAA47A7D4C5980EFB8EE1D94531F6B4ADC03E4A51E2940DBC8BF5D985884402D5B4801817CAA2A0F4452E8F13FAAD56B490D39334C8FEBAC8812CA29024AE2295788C460CA373F1CFFF32BA9C40BE67657B1F25319C6E2AF +ss = E1FAA1034599A2694F72603451FBECD55FCABF135238A5D3D9BD97CE2F1BBB73 + diff --git a/hqc-kem/src/code.rs b/hqc-kem/src/code.rs new file mode 100644 index 0000000..22c2b26 --- /dev/null +++ b/hqc-kem/src/code.rs @@ -0,0 +1,18 @@ +/// Concatenated code: Reed-Solomon then Reed-Muller. +use crate::params::HqcParameters; +use crate::reed_muller; +use crate::reed_solomon; + +/// Encode message: RS encode then RM encode. +pub(crate) fn code_encode(em: &mut [u64], m: &[u8], p: &HqcParameters) { + let mut tmp = vec![0u8; p.n1]; + reed_solomon::reed_solomon_encode(&mut tmp, m, p); + reed_muller::reed_muller_encode(em, &tmp, p); +} + +/// Decode codeword: RM decode then RS decode. +pub(crate) fn code_decode(m: &mut [u8], em: &[u64], p: &HqcParameters) { + let mut tmp = vec![0u8; p.n1]; + reed_muller::reed_muller_decode(&mut tmp, em, p); + reed_solomon::reed_solomon_decode(m, &mut tmp, p); +} diff --git a/hqc-kem/src/error.rs b/hqc-kem/src/error.rs new file mode 100644 index 0000000..ae363c7 --- /dev/null +++ b/hqc-kem/src/error.rs @@ -0,0 +1,38 @@ +//! Error types for HQC-KEM operations. + +/// Errors that can occur during HQC operations. +#[derive(Debug, thiserror::Error)] +pub enum Error { + /// Invalid public key size. + #[error("invalid public key size: expected {expected}, got {got}")] + InvalidPublicKeySize { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, + /// Invalid secret key size. + #[error("invalid secret key size: expected {expected}, got {got}")] + InvalidSecretKeySize { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, + /// Invalid ciphertext size. + #[error("invalid ciphertext size: expected {expected}, got {got}")] + InvalidCiphertextSize { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, + /// Invalid message size for deterministic encapsulation. + #[error("invalid message size: expected {expected}, got {got}")] + InvalidMessageSize { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, +} diff --git a/hqc-kem/src/fft.rs b/hqc-kem/src/fft.rs new file mode 100644 index 0000000..d77899b --- /dev/null +++ b/hqc-kem/src/fft.rs @@ -0,0 +1,255 @@ +/// Additive FFT for GF(2^8) polynomial evaluation. +/// +/// Used by Reed-Solomon decoding to find roots of the error locator polynomial. +/// Based on Gao-Mateer with Bernstein-Chou-Schwabe improvements. +use crate::gf256::{GF_LOG, gf_inverse, gf_mul, gf_square}; +use crate::params::PARAM_M; + +/// Compute FFT betas (basis elements). +fn compute_fft_betas(betas: &mut [u16]) { + for (i, beta) in betas.iter_mut().enumerate().take(PARAM_M - 1) { + *beta = 1 << (PARAM_M - 1 - i); + } +} + +/// Compute subset sums of a set. +fn compute_subset_sums(subset_sums: &mut [u16], set: &[u16], set_size: usize) { + subset_sums[0] = 0; + for i in 0..set_size { + for j in 0..(1 << i) { + subset_sums[(1 << i) + j] = set[i] ^ subset_sums[j]; + } + } +} + +/// Radix conversion for small sizes (hardcoded cases). +fn radix(f0: &mut [u16], f1: &mut [u16], f: &[u16], m_f: usize) { + match m_f { + 1 => { + f0[0] = f[0]; + f1[0] = f[1]; + } + 2 => { + f0[0] = f[0]; + f0[1] = f[2] ^ f[3]; + f1[0] = f[1] ^ f0[1]; + f1[1] = f[3]; + } + 3 => { + f0[0] = f[0]; + f0[2] = f[4] ^ f[6]; + f0[3] = f[6] ^ f[7]; + f1[1] = f[3] ^ f[5] ^ f[7]; + f1[2] = f[5] ^ f[6]; + f1[3] = f[7]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + } + 4 => { + f0[4] = f[8] ^ f[12]; + f0[6] = f[12] ^ f[14]; + f0[7] = f[14] ^ f[15]; + f1[5] = f[11] ^ f[13]; + f1[6] = f[13] ^ f[14]; + f1[7] = f[15]; + f0[5] = f[10] ^ f[12] ^ f1[5]; + f1[4] = f[9] ^ f[13] ^ f0[5]; + + f0[0] = f[0]; + f1[3] = f[7] ^ f[11] ^ f[15]; + f0[3] = f[6] ^ f[10] ^ f[14] ^ f1[3]; + f0[2] = f[4] ^ f0[4] ^ f0[3] ^ f1[3]; + f1[1] = f[3] ^ f[5] ^ f[9] ^ f[13] ^ f1[3]; + f1[2] = f[3] ^ f1[1] ^ f0[3]; + f0[1] = f[2] ^ f0[2] ^ f1[1]; + f1[0] = f[1] ^ f0[1]; + } + _ => { + radix_big(f0, f1, f, m_f); + } + } +} + +/// Radix conversion for larger sizes. +fn radix_big(f0: &mut [u16], f1: &mut [u16], f: &[u16], m_f: usize) { + let n = 1usize << (m_f - 2); + let mut q = vec![0u16; 2 * n + 1]; + let mut r = vec![0u16; 4 * n]; + let mut q0 = vec![0u16; n]; + let mut q1 = vec![0u16; n]; + let mut r0 = vec![0u16; n]; + let mut r1 = vec![0u16; n]; + + q[..n].copy_from_slice(&f[3 * n..4 * n]); + q[n..2 * n].copy_from_slice(&f[3 * n..4 * n]); + r[..4 * n].copy_from_slice(&f[..4 * n]); + + for i in 0..n { + q[i] ^= f[2 * n + i]; + r[n + i] ^= q[i]; + } + + radix(&mut q0, &mut q1, &q, m_f - 1); + radix(&mut r0, &mut r1, &r, m_f - 1); + + f0[..n].copy_from_slice(&r0[..n]); + f0[n..2 * n].copy_from_slice(&q0[..n]); + f1[..n].copy_from_slice(&r1[..n]); + f1[n..2 * n].copy_from_slice(&q1[..n]); +} + +/// Recursive FFT evaluation. +fn fft_rec(w: &mut [u16], f: &mut [u16], f_coeffs: usize, m: usize, m_f: usize, betas: &[u16]) { + if m_f == 1 { + let mut tmp = [0u16; PARAM_M]; + for i in 0..m { + tmp[i] = gf_mul(betas[i], f[1]); + } + w[0] = f[0]; + let mut x = 1usize; + for tmp_j in tmp.iter().take(m) { + for k in 0..x { + w[x + k] = w[k] ^ tmp_j; + } + x <<= 1; + } + return; + } + + let half_size = 1 << (m_f - 1); + let mut f0 = vec![0u16; half_size]; + let mut f1 = vec![0u16; half_size]; + + // Step 2: twist by beta_m + if betas[m - 1] != 1 { + let mut beta_pow = 1u16; + let x = 1usize << m_f; + for i in 1..x.min(f.len()) { + beta_pow = gf_mul(beta_pow, betas[m - 1]); + f[i] = gf_mul(beta_pow, f[i]); + } + } + + // Step 3: radix + radix(&mut f0, &mut f1, f, m_f); + + // Step 4: compute gammas and deltas + let mut gammas = vec![0u16; m]; + let mut deltas = vec![0u16; m]; + let inv_beta_m = gf_inverse(betas[m - 1]); + for i in 0..m - 1 { + gammas[i] = gf_mul(betas[i], inv_beta_m); + deltas[i] = gf_square(gammas[i]) ^ gammas[i]; + } + + // Compute gamma sums + let mut gammas_sums = vec![0u16; 1 << (m - 1)]; + compute_subset_sums(&mut gammas_sums, &gammas, m - 1); + + // Step 5: recurse + let k = 1usize << (m - 1); + let mut u = vec![0u16; k]; + fft_rec(&mut u, &mut f0, f_coeffs.div_ceil(2), m - 1, m_f - 1, &deltas); + + if f_coeffs <= 3 { + // f1 is constant + w[0] = u[0]; + w[k] = u[0] ^ f1[0]; + for i in 1..k { + w[i] = u[i] ^ gf_mul(gammas_sums[i], f1[0]); + w[k + i] = w[i] ^ f1[0]; + } + } else { + let mut v = vec![0u16; k]; + fft_rec(&mut v, &mut f1, f_coeffs / 2, m - 1, m_f - 1, &deltas); + + // Step 6: combine + w[k..k + k].copy_from_slice(&v[..k]); + w[0] = u[0]; + w[k] ^= u[0]; + for i in 1..k { + w[i] = u[i] ^ gf_mul(gammas_sums[i], v[i]); + w[k + i] ^= w[i]; + } + } +} + +/// Evaluate polynomial at all 2^M field elements using additive FFT. +/// +/// `f` has `f_coeffs` coefficients. `w` receives 2^M evaluations. +pub(crate) fn fft(w: &mut [u16; 256], f: &[u16], f_coeffs: usize, fft_param: usize) { + let mut betas = [0u16; PARAM_M - 1]; + compute_fft_betas(&mut betas); + + let mut betas_sums = [0u16; 1 << (PARAM_M - 1)]; + compute_subset_sums(&mut betas_sums, &betas, PARAM_M - 1); + + let fft_size = 1 << fft_param; + let mut f_padded = vec![0u16; fft_size]; + f_padded[..f_coeffs.min(fft_size)].copy_from_slice(&f[..f_coeffs.min(fft_size)]); + + let half = fft_size >> 1; + let mut f0 = vec![0u16; half]; + let mut f1 = vec![0u16; half]; + radix(&mut f0, &mut f1, &f_padded, fft_param); + + let mut deltas = [0u16; PARAM_M - 1]; + for i in 0..(PARAM_M - 1) { + deltas[i] = gf_square(betas[i]) ^ betas[i]; + } + + let k = 1usize << (PARAM_M - 1); + let mut u = vec![0u16; k]; + let mut v = vec![0u16; k]; + + fft_rec( + &mut u, + &mut f0, + f_coeffs.div_ceil(2), + PARAM_M - 1, + fft_param - 1, + &deltas, + ); + fft_rec( + &mut v, + &mut f1, + f_coeffs / 2, + PARAM_M - 1, + fft_param - 1, + &deltas, + ); + + w[k..k + k].copy_from_slice(&v[..k]); + w[0] = u[0]; + w[k] ^= u[0]; + for i in 1..k { + w[i] = u[i] ^ gf_mul(betas_sums[i], v[i]); + w[k + i] ^= w[i]; + } +} + +/// Retrieve error polynomial from FFT evaluations. +/// +/// `error[i] = 1` if `w[alpha^(-i)] == 0`, i.e., alpha^(-i) is a root of sigma. +pub(crate) fn fft_retrieve_error_poly(error: &mut [u8; 256], w: &[u16; 256]) { + let mut gammas = [0u16; PARAM_M - 1]; + compute_fft_betas(&mut gammas); + + let mut gammas_sums = [0u16; 1 << (PARAM_M - 1)]; + compute_subset_sums(&mut gammas_sums, &gammas, PARAM_M - 1); + + let k = 1usize << (PARAM_M - 1); + + // Check if 0 is root + error[0] ^= 1 ^ ((0u16.wrapping_sub(w[0])) >> 15) as u8; + // Check if 1 is root + error[0] ^= 1 ^ ((0u16.wrapping_sub(w[k])) >> 15) as u8; + + for i in 1..k { + let idx1 = 255 - GF_LOG[gammas_sums[i] as usize] as usize; + error[idx1] ^= 1 ^ ((0u16.wrapping_sub(w[i])) >> 15) as u8; + + let idx2 = 255 - GF_LOG[(gammas_sums[i] ^ 1) as usize] as usize; + error[idx2] ^= 1 ^ ((0u16.wrapping_sub(w[k + i])) >> 15) as u8; + } +} diff --git a/hqc-kem/src/gf256.rs b/hqc-kem/src/gf256.rs new file mode 100644 index 0000000..c3e7336 --- /dev/null +++ b/hqc-kem/src/gf256.rs @@ -0,0 +1,77 @@ +//! GF(2^8) arithmetic with irreducible polynomial x^8 + x^4 + x^3 + x^2 + 1 (0x11D). +//! +//! Generator (alpha) = 2. + +/// Powers of alpha: gf_exp[i] = alpha^i. Extended to 258 entries to avoid +/// bounds checking in multiplication. +pub(crate) static GF_EXP: [u16; 258] = [ + 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, + 234, 201, 143, 3, 6, 12, 24, 48, 96, 192, 157, 39, 78, 156, 37, 74, 148, 53, 106, 212, 181, + 119, 238, 193, 159, 35, 70, 140, 5, 10, 20, 40, 80, 160, 93, 186, 105, 210, 185, 111, 222, 161, + 95, 190, 97, 194, 153, 47, 94, 188, 101, 202, 137, 15, 30, 60, 120, 240, 253, 231, 211, 187, + 107, 214, 177, 127, 254, 225, 223, 163, 91, 182, 113, 226, 217, 175, 67, 134, 17, 34, 68, 136, + 13, 26, 52, 104, 208, 189, 103, 206, 129, 31, 62, 124, 248, 237, 199, 147, 59, 118, 236, 197, + 151, 51, 102, 204, 133, 23, 46, 92, 184, 109, 218, 169, 79, 158, 33, 66, 132, 21, 42, 84, 168, + 77, 154, 41, 82, 164, 85, 170, 73, 146, 57, 114, 228, 213, 183, 115, 230, 209, 191, 99, 198, + 145, 63, 126, 252, 229, 215, 179, 123, 246, 241, 255, 227, 219, 171, 75, 150, 49, 98, 196, 149, + 55, 110, 220, 165, 87, 174, 65, 130, 25, 50, 100, 200, 141, 7, 14, 28, 56, 112, 224, 221, 167, + 83, 166, 81, 162, 89, 178, 121, 242, 249, 239, 195, 155, 43, 86, 172, 69, 138, 9, 18, 36, 72, + 144, 61, 122, 244, 245, 247, 243, 251, 235, 203, 139, 11, 22, 44, 88, 176, 125, 250, 233, 207, + 131, 27, 54, 108, 216, 173, 71, 142, 1, 2, 4, +]; + +/// Logarithm table: gf_log[v] = i where alpha^i = v. gf_log[0] = 0 by convention. +pub(crate) static GF_LOG: [u16; 256] = [ + 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, + 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, + 240, 18, 130, 69, 29, 181, 194, 125, 106, 39, 249, 185, 201, 154, 9, 120, 77, 228, 114, 166, 6, + 191, 139, 98, 102, 221, 48, 253, 226, 152, 37, 179, 16, 145, 34, 136, 54, 208, 148, 206, 143, + 150, 219, 189, 241, 210, 19, 92, 131, 56, 70, 64, 30, 66, 182, 163, 195, 72, 126, 110, 107, 58, + 40, 84, 250, 133, 186, 61, 202, 94, 155, 159, 10, 21, 121, 43, 78, 212, 229, 172, 115, 243, + 167, 87, 7, 112, 192, 247, 140, 128, 99, 13, 103, 74, 222, 237, 49, 197, 254, 24, 227, 165, + 153, 119, 38, 184, 180, 124, 17, 68, 146, 217, 35, 32, 137, 46, 55, 63, 209, 91, 149, 188, 207, + 205, 144, 135, 151, 178, 220, 252, 190, 97, 242, 86, 211, 171, 20, 42, 93, 158, 132, 60, 57, + 83, 71, 109, 65, 162, 31, 45, 67, 216, 183, 123, 164, 118, 196, 23, 73, 236, 127, 12, 111, 246, + 108, 161, 59, 82, 41, 157, 85, 170, 251, 96, 134, 177, 187, 204, 62, 90, 203, 89, 95, 176, 156, + 169, 160, 81, 11, 245, 22, 235, 122, 117, 44, 215, 79, 174, 213, 233, 230, 231, 173, 232, 116, + 214, 244, 234, 168, 80, 88, 175, +]; + +/// Constant-time GF(2^8) multiplication. +/// +/// Uses bitwise carry-less multiplication with modular reduction by 0x11D. +#[inline] +pub(crate) fn gf_mul(a: u16, b: u16) -> u16 { + let mut a = a; + let mut r = a & (0u16.wrapping_sub(b & 1)); + for i in 1..8 { + a = (a << 1) ^ ((0u16.wrapping_sub(a >> 7)) & 0x11D); + r ^= a & (0u16.wrapping_sub((b >> i) & 1)); + } + r +} + +/// GF(2^8) squaring: returns a^2. +#[inline] +pub(crate) fn gf_square(a: u16) -> u16 { + gf_mul(a, a) +} + +/// GF(2^8) multiplicative inverse via the addition chain 1,2,3,4,7,11,15,30,60,120,127,254. +/// +/// Returns 0 if a = 0. +#[inline] +pub(crate) fn gf_inverse(a: u16) -> u16 { + let inv = gf_square(a); // a^2 + let tmp1 = gf_mul(inv, a); // a^3 + let inv = gf_square(inv); // a^4 + let tmp2 = gf_mul(inv, tmp1); // a^7 + let tmp1 = gf_mul(inv, tmp2); // a^11 + let mut inv = gf_mul(tmp1, inv); // a^15 + inv = gf_square(inv); // a^30 + inv = gf_square(inv); // a^60 + inv = gf_square(inv); // a^120 + inv = gf_mul(inv, tmp2); // a^127 + inv = gf_square(inv); // a^254 + inv +} diff --git a/hqc-kem/src/kem.rs b/hqc-kem/src/kem.rs new file mode 100644 index 0000000..1ca6119 --- /dev/null +++ b/hqc-kem/src/kem.rs @@ -0,0 +1,188 @@ +/// HQC Key Encapsulation Mechanism with Fujisaki-Okamoto transform. +/// +/// v5.0.0 KEM: +/// - keygen: XOF(seed_kem) → seed_pke, sigma; pke_keygen(seed_pke) +/// - encaps: m, salt from RNG; H(ek), G(H||m||salt) → kk, theta; encrypt(ek, m, theta) +/// - decaps: decrypt → m'; re-encrypt; constant-time comparison; implicit rejection +use crate::params::{HqcParameters, SALT_BYTES, SEED_BYTES, SS_BYTES}; +use crate::pke; +use crate::poly; +use crate::shake::{self, SeedExpander}; +use rand::CryptoRng; +use zeroize::Zeroize; + +/// Deterministic KEM key generation from a 32-byte seed. +/// +/// Returns (pk_bytes, sk_bytes) where: +/// - pk = ek_pke (seed_pke_ek || s_bytes) +/// - sk = ek_pke || dk_pke || sigma || seed_kem +#[cfg(feature = "kgen")] +pub(crate) fn keygen_deterministic( + seed_kem: &[u8; SEED_BYTES], + p: &HqcParameters, +) -> (Vec, Vec) { + // XOF(seed_kem || 0x01) → raw read seed_pke + sigma + let mut ctx_kem = SeedExpander::new(seed_kem); + let mut seed_pke = [0u8; SEED_BYTES]; + let mut sigma = vec![0u8; p.k]; + ctx_kem.read_raw(&mut seed_pke); + ctx_kem.read_raw(&mut sigma); + + // PKE keygen + let (ek_pke, dk_pke) = pke::pke_keygen(&seed_pke, p); + + // Zeroize intermediate seed_pke — no longer needed after pke_keygen + seed_pke.zeroize(); + + // pk = ek_pke + let pk = ek_pke.clone(); + + // sk = ek_pke || dk_pke || sigma || seed_kem + let mut sk = Vec::with_capacity(p.sk_bytes); + sk.extend_from_slice(&ek_pke); + sk.extend_from_slice(&dk_pke); + sk.extend_from_slice(&sigma); + sk.extend_from_slice(seed_kem); + + // Zeroize sigma intermediate — now copied into sk + sigma.zeroize(); + + (pk, sk) +} + +/// KEM key generation. +/// +/// Samples a random seed, then delegates to [`keygen_deterministic`]. +#[cfg(feature = "kgen")] +pub(crate) fn keygen(p: &HqcParameters, rng: &mut impl CryptoRng) -> (Vec, Vec) { + let mut seed_kem = [0u8; SEED_BYTES]; + rng.fill_bytes(&mut seed_kem); + let result = keygen_deterministic(&seed_kem, p); + seed_kem.zeroize(); + result +} + +/// Deterministic KEM encapsulation from a message and salt. +/// +/// Returns (shared_secret, ciphertext). +#[cfg(feature = "ecap")] +pub(crate) fn encaps_deterministic( + ek_kem: &[u8], + m: &[u8], + salt: &[u8; SALT_BYTES], + p: &HqcParameters, +) -> (Vec, Vec) { + // H(ek_kem || 0x01) → 32-byte hash of public key + let tmp_h = shake::hash_h(ek_kem); + + // G(H || m || salt || 0x00) → 64 bytes: kk || theta + let mut g_input = Vec::with_capacity(32 + m.len() + SALT_BYTES); + g_input.extend_from_slice(&tmp_h); + g_input.extend_from_slice(m); + g_input.extend_from_slice(salt); + let mut tmp_g = shake::hash_g(&g_input); + g_input.zeroize(); + + let kk = tmp_g[..SS_BYTES].to_vec(); + let theta = tmp_g[SS_BYTES..2 * SS_BYTES].to_vec(); + + // Encrypt + let c_pke = pke::pke_encrypt(ek_kem, m, &theta, p); + + // Zeroize intermediates + tmp_g.zeroize(); + + // Ciphertext = c_pke || salt + let mut ct = Vec::with_capacity(p.ct_bytes); + ct.extend_from_slice(&c_pke); + ct.extend_from_slice(salt); + + (kk, ct) +} + +/// KEM encapsulation. +/// +/// Samples random message and salt, then delegates to [`encaps_deterministic`]. +#[cfg(feature = "ecap")] +pub(crate) fn encaps( + ek_kem: &[u8], + p: &HqcParameters, + rng: &mut (impl CryptoRng + ?Sized), +) -> (Vec, Vec) { + let mut m = vec![0u8; p.k]; + let mut salt = [0u8; SALT_BYTES]; + rng.fill_bytes(&mut m); + rng.fill_bytes(&mut salt); + let result = encaps_deterministic(ek_kem, &m, &salt, p); + m.zeroize(); + salt.zeroize(); + result +} + +/// KEM decapsulation with implicit rejection. +/// +/// Returns shared secret (SS_BYTES). +#[cfg(feature = "dcap")] +pub(crate) fn decaps(dk_kem: &[u8], c_kem: &[u8], p: &HqcParameters) -> Vec { + // Parse secret key: ek_kem || dk_pke || sigma || seed_kem + let ek_kem = &dk_kem[..p.pk_bytes]; + let dk_pke = &dk_kem[p.pk_bytes..p.pk_bytes + SEED_BYTES]; + let sigma = &dk_kem[p.pk_bytes + SEED_BYTES..p.pk_bytes + SEED_BYTES + p.k]; + + // Parse ciphertext: c_pke || salt + let c_pke_len = p.n_bytes + p.n1n2_bytes; + let c_pke = &c_kem[..c_pke_len]; + let salt = &c_kem[c_pke_len..c_pke_len + SALT_BYTES]; + + // Decrypt + let mut m_prime = pke::pke_decrypt(dk_pke, c_pke, p); + + // Re-derive theta + let tmp_h = shake::hash_h(ek_kem); + + let mut g_input = Vec::with_capacity(32 + p.k + SALT_BYTES); + g_input.extend_from_slice(&tmp_h); + g_input.extend_from_slice(&m_prime); + g_input.extend_from_slice(salt); + let mut tmp_g = shake::hash_g(&g_input); + g_input.zeroize(); + + let kk_prime = &tmp_g[..SS_BYTES]; + let theta_prime = &tmp_g[SS_BYTES..2 * SS_BYTES]; + + // Re-encrypt + let c_pke_prime = pke::pke_encrypt(ek_kem, &m_prime, theta_prime, p); + + // Zeroize decrypted message — no longer needed + m_prime.zeroize(); + + // Ciphertext for comparison: c_pke_prime || salt + let mut c_kem_prime = Vec::with_capacity(p.ct_bytes); + c_kem_prime.extend_from_slice(&c_pke_prime); + c_kem_prime.extend_from_slice(salt); + + // J function for rejection key: SHA3-256(H || sigma || c_kem || 0x03) + let mut j_input = Vec::with_capacity(32 + p.k + p.ct_bytes); + j_input.extend_from_slice(&tmp_h); + j_input.extend_from_slice(sigma); + j_input.extend_from_slice(c_kem); + let mut k_rej = shake::hash_j(&j_input); + j_input.zeroize(); + + // Constant-time comparison of ciphertexts + let cmp_result = poly::vect_compare(&c_kem[..c_pke_len + SALT_BYTES], &c_kem_prime); + + // Select between kk_prime and k_rej in constant time + // cmp_result is 0 if equal (use kk_prime), non-zero if different (use k_rej) + let mask = 0u8.wrapping_sub(cmp_result); // 0xFF if mismatch, 0x00 if match + let mut ss = vec![0u8; SS_BYTES]; + for i in 0..SS_BYTES { + ss[i] = (kk_prime[i] & !mask) | (k_rej[i] & mask); + } + + // Zeroize remaining sensitive intermediates + tmp_g.zeroize(); + k_rej.zeroize(); + + ss +} diff --git a/hqc-kem/src/kem_impl.rs b/hqc-kem/src/kem_impl.rs new file mode 100644 index 0000000..7c4313b --- /dev/null +++ b/hqc-kem/src/kem_impl.rs @@ -0,0 +1,232 @@ +//! Implementation of the [`kem`](kem_traits) crate traits for HQC-KEM. + +use crate::params::HqcParams; +use crate::sizes; +use crate::types::{DecapsulationKey, EncapsulationKey}; +use hybrid_array::Array; + + +macro_rules! impl_hqc_kem { + ($params:ty, $pk_size:ty, $ct_size:ty) => { + // -- Kem on the parameter marker type -- + impl kem_traits::Kem for $params { + type DecapsulationKey = DecapsulationKey<$params>; + type EncapsulationKey = EncapsulationKey<$params>; + type SharedKeySize = typenum::consts::U32; + type CiphertextSize = $ct_size; + + fn generate_keypair_from_rng( + rng: &mut R, + ) -> ( + kem_traits::DecapsulationKey, + kem_traits::EncapsulationKey, + ) { + let (pk, sk) = crate::kem::keygen(<$params>::params(), rng); + let ek = EncapsulationKey::<$params>::from_vec(pk); + let dk = DecapsulationKey::<$params>::from_vec(sk); + (dk, ek) + } + } + + // -- EncapsulationKey: KeySizeUser -- + impl kem_traits::common::KeySizeUser for EncapsulationKey<$params> { + type KeySize = $pk_size; + } + + // -- EncapsulationKey: TryKeyInit -- + impl kem_traits::common::TryKeyInit for EncapsulationKey<$params> { + fn new( + key: &kem_traits::Key, + ) -> Result { + let bytes = key.as_slice(); + if bytes.len() != <$params>::PK_BYTES { + return Err(kem_traits::InvalidKey); + } + Ok(EncapsulationKey::<$params>::from_vec(bytes.to_vec())) + } + } + + // -- EncapsulationKey: KeyExport -- + impl kem_traits::common::KeyExport for EncapsulationKey<$params> { + fn to_bytes(&self) -> kem_traits::Key { + let mut arr = Array::::default(); + arr.as_mut_slice().copy_from_slice(self.as_ref()); + arr + } + } + + // -- EncapsulationKey: Encapsulate -- + impl kem_traits::Encapsulate for EncapsulationKey<$params> { + type Kem = $params; + + fn encapsulate_with_rng( + &self, + rng: &mut R, + ) -> ( + kem_traits::Ciphertext, + kem_traits::SharedKey, + ) + where + R: rand::CryptoRng + ?Sized, + { + let (ss_vec, ct_vec) = + crate::kem::encaps(self.as_ref(), <$params>::params(), rng); + + let mut ct = Array::::default(); + ct.as_mut_slice().copy_from_slice(&ct_vec); + + let mut ss = Array::::default(); + ss.as_mut_slice().copy_from_slice(&ss_vec); + + (ct, ss) + } + } + + // -- DecapsulationKey: KeySizeUser (seed = 32 bytes) -- + impl kem_traits::common::KeySizeUser for DecapsulationKey<$params> { + type KeySize = typenum::consts::U32; + } + + // -- DecapsulationKey: KeyInit (from 32-byte seed) -- + impl kem_traits::common::KeyInit for DecapsulationKey<$params> { + fn new(seed: &kem_traits::Key) -> Self { + let seed_arr: [u8; 32] = seed + .as_slice() + .try_into() + .expect("seed is exactly 32 bytes"); + let (pk, sk) = + crate::kem::keygen_deterministic(&seed_arr, <$params>::params()); + let _ = pk; // pk is embedded in sk + DecapsulationKey::<$params>::from_vec(sk) + } + } + + // -- DecapsulationKey: KeyExport (returns 32-byte seed) -- + impl kem_traits::common::KeyExport for DecapsulationKey<$params> { + fn to_bytes(&self) -> kem_traits::Key { + let sk = self.as_ref(); + let seed_start = sk.len() - crate::params::SEED_BYTES; + let mut arr = Array::::default(); + arr.as_mut_slice() + .copy_from_slice(&sk[seed_start..]); + arr + } + } + + // -- DecapsulationKey: Generate -- + impl kem_traits::common::Generate for DecapsulationKey<$params> { + fn try_generate_from_rng( + rng: &mut R, + ) -> Result::Error> + where + R: rand::TryCryptoRng + ?Sized, + { + let mut seed = [0u8; 32]; + rng.try_fill_bytes(&mut seed)?; + let seed_arr = Array::::from(seed); + Ok(::new(&seed_arr)) + } + } + + // -- DecapsulationKey: Decapsulator -- + impl kem_traits::Decapsulator for DecapsulationKey<$params> { + type Kem = $params; + + fn encapsulation_key( + &self, + ) -> &kem_traits::EncapsulationKey { + self.encapsulation_key() + } + } + + // -- DecapsulationKey: Decapsulate -- + impl kem_traits::Decapsulate for DecapsulationKey<$params> { + fn decapsulate( + &self, + ct: &kem_traits::Ciphertext, + ) -> kem_traits::SharedKey { + let ss_vec = + crate::kem::decaps(self.as_ref(), ct.as_slice(), <$params>::params()); + let mut ss = Array::::default(); + ss.as_mut_slice().copy_from_slice(&ss_vec); + ss + } + } + }; +} + +impl_hqc_kem!( + crate::params::Hqc128Params, + sizes::U2241, + sizes::U4433 +); +impl_hqc_kem!( + crate::params::Hqc192Params, + sizes::U4514, + sizes::U8978 +); +impl_hqc_kem!( + crate::params::Hqc256Params, + sizes::U7237, + sizes::U14421 +); + +#[cfg(test)] +mod tests { + use super::*; + use kem_traits::common::{Generate, KeyExport, KeyInit}; + use kem_traits::{Decapsulate, Encapsulate, Kem}; + + macro_rules! kem_roundtrip_test { + ($name:ident, $params:ty) => { + #[test] + fn $name() { + // Generate via kem trait + let mut rng = rand::rng(); + let (dk, ek) = <$params>::generate_keypair_from_rng(&mut rng); + + // Encapsulate + let (ct, ss1) = ek.encapsulate_with_rng(&mut rng); + + // Decapsulate (use UFCS to call trait method, not inherent) + let ss2 = <_ as Decapsulate>::decapsulate(&dk, &ct); + assert_eq!(ss1, ss2); + + // Verify Decapsulator returns the right EK + let ek_ref = kem_traits::Decapsulator::encapsulation_key(&dk); + assert_eq!(&ek, ek_ref); + } + }; + } + + kem_roundtrip_test!(kem_roundtrip_128, crate::params::Hqc128Params); + kem_roundtrip_test!(kem_roundtrip_192, crate::params::Hqc192Params); + kem_roundtrip_test!(kem_roundtrip_256, crate::params::Hqc256Params); + + macro_rules! from_seed_test { + ($name:ident, $params:ty) => { + #[test] + fn $name() { + // Generate DK, export seed, re-import, verify deterministic + let dk = DecapsulationKey::<$params>::generate_from_rng(&mut rand::rng()); + let seed = dk.to_bytes(); + let dk2 = DecapsulationKey::<$params>::new(&seed); + + // Both should produce the same EK + let ek1 = kem_traits::Decapsulator::encapsulation_key(&dk); + let ek2 = kem_traits::Decapsulator::encapsulation_key(&dk2); + assert_eq!(ek1, ek2); + + // Both should produce the same shared secret + let mut rng = rand::rng(); + let (ct, ss1) = ek1.encapsulate_with_rng(&mut rng); + let ss2 = <_ as Decapsulate>::decapsulate(&dk2, &ct); + assert_eq!(ss1, ss2); + } + }; + } + + from_seed_test!(from_seed_128, crate::params::Hqc128Params); + from_seed_test!(from_seed_192, crate::params::Hqc192Params); + from_seed_test!(from_seed_256, crate::params::Hqc256Params); +} diff --git a/hqc-kem/src/lib.rs b/hqc-kem/src/lib.rs new file mode 100644 index 0000000..1687826 --- /dev/null +++ b/hqc-kem/src/lib.rs @@ -0,0 +1,176 @@ +//! Pure Rust implementation of HQC-KEM (NIST FIPS 207). +//! +//! HQC is a code-based Key Encapsulation Mechanism using quasi-cyclic codes +//! over the ring Z_2\[X\]/(X^n-1). Uses concatenated Reed-Solomon + Reed-Muller +//! error correction with Fujisaki-Okamoto transform for IND-CCA2 security. +//! +//! # Usage +//! +//! ```rust +//! use hqc_kem::{Hqc128, HqcKem}; +//! +//! let mut rng = rand::rng(); +//! let (ek, dk) = Hqc128::generate_key(&mut rng); +//! let (ct, ss1) = ek.encapsulate(&mut rng); +//! let ss2 = dk.decapsulate(&ct); +//! assert_eq!(ss1, ss2); +//! ``` +//! +//! # Security Levels +//! +//! - [`Hqc128`] / [`hqc128`]: NIST Level 1 (128-bit security) +//! - [`Hqc192`] / [`hqc192`]: NIST Level 3 (192-bit security) +//! - [`Hqc256`] / [`hqc256`]: NIST Level 5 (256-bit security) +//! +//! # Features +//! +//! - `kgen`: Key generation (default) +//! - `ecap`: Encapsulation (default) +//! - `dcap`: Decapsulation (default) +//! - `kem`: RustCrypto [`kem`](https://crates.io/crates/kem) 0.3 trait implementations +//! - `pkcs8`: PKCS#8 key encoding/decoding +//! - `pem`: PEM encoding (enables `pkcs8/pem`) +//! - `alloc`: Enables PKCS#8 encoding (requires `alloc`) +//! - `serde`: Serde serialization support + +mod code; +mod error; +mod fft; +mod gf256; +mod kem; +#[cfg(feature = "kem")] +mod kem_impl; +mod params; +#[cfg(feature = "pkcs8")] +mod pkcs8_impl; +mod pke; +mod poly; +mod reed_muller; +mod reed_solomon; +mod sampling; +mod shake; +#[cfg(feature = "kem")] +mod sizes; +mod types; + +pub use error::Error; +pub use params::{Hqc128Params, Hqc192Params, Hqc256Params, HqcParams}; +pub use types::{Ciphertext, DecapsulationKey, EncapsulationKey, HqcKem, SharedSecret}; + +/// HQC-128 KEM (NIST Level 1). +pub type Hqc128 = HqcKem; +/// HQC-192 KEM (NIST Level 3). +pub type Hqc192 = HqcKem; +/// HQC-256 KEM (NIST Level 5). +pub type Hqc256 = HqcKem; + +/// HQC-128: NIST Level 1 security (128-bit). +pub mod hqc128 { + /// Public key size in bytes. + pub const PUBLIC_KEY_SIZE: usize = 2241; + /// Secret key size in bytes. + pub const SECRET_KEY_SIZE: usize = 2321; + /// Ciphertext size in bytes. + pub const CIPHERTEXT_SIZE: usize = 4433; + /// Shared secret size in bytes. + pub const SHARED_SECRET_SIZE: usize = crate::params::SS_BYTES; + /// Message size in bytes (for deterministic encapsulation). + pub const MESSAGE_SIZE: usize = 16; + /// Salt size in bytes (for deterministic encapsulation). + pub const SALT_SIZE: usize = crate::params::SALT_BYTES; + + /// HQC-128 encapsulation key. + pub type EncapsulationKey = crate::EncapsulationKey; + /// HQC-128 decapsulation key. + pub type DecapsulationKey = crate::DecapsulationKey; + /// HQC-128 ciphertext. + pub type Ciphertext = crate::Ciphertext; + /// HQC-128 shared secret. + pub type SharedSecret = crate::SharedSecret; + + /// Generate an HQC-128 key pair. + #[cfg(feature = "kgen")] + pub fn generate_key(rng: &mut impl rand::CryptoRng) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc128::generate_key(rng) + } + + /// Generate an HQC-128 key pair deterministically from a 32-byte seed. + #[cfg(feature = "kgen")] + pub fn generate_key_deterministic(seed: &[u8; 32]) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc128::generate_key_deterministic(seed) + } +} + +/// HQC-192: NIST Level 3 security (192-bit). +pub mod hqc192 { + /// Public key size in bytes. + pub const PUBLIC_KEY_SIZE: usize = 4514; + /// Secret key size in bytes. + pub const SECRET_KEY_SIZE: usize = 4602; + /// Ciphertext size in bytes. + pub const CIPHERTEXT_SIZE: usize = 8978; + /// Shared secret size in bytes. + pub const SHARED_SECRET_SIZE: usize = crate::params::SS_BYTES; + /// Message size in bytes (for deterministic encapsulation). + pub const MESSAGE_SIZE: usize = 24; + /// Salt size in bytes (for deterministic encapsulation). + pub const SALT_SIZE: usize = crate::params::SALT_BYTES; + + /// HQC-192 encapsulation key. + pub type EncapsulationKey = crate::EncapsulationKey; + /// HQC-192 decapsulation key. + pub type DecapsulationKey = crate::DecapsulationKey; + /// HQC-192 ciphertext. + pub type Ciphertext = crate::Ciphertext; + /// HQC-192 shared secret. + pub type SharedSecret = crate::SharedSecret; + + /// Generate an HQC-192 key pair. + #[cfg(feature = "kgen")] + pub fn generate_key(rng: &mut impl rand::CryptoRng) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc192::generate_key(rng) + } + + /// Generate an HQC-192 key pair deterministically from a 32-byte seed. + #[cfg(feature = "kgen")] + pub fn generate_key_deterministic(seed: &[u8; 32]) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc192::generate_key_deterministic(seed) + } +} + +/// HQC-256: NIST Level 5 security (256-bit). +pub mod hqc256 { + /// Public key size in bytes. + pub const PUBLIC_KEY_SIZE: usize = 7237; + /// Secret key size in bytes. + pub const SECRET_KEY_SIZE: usize = 7333; + /// Ciphertext size in bytes. + pub const CIPHERTEXT_SIZE: usize = 14421; + /// Shared secret size in bytes. + pub const SHARED_SECRET_SIZE: usize = crate::params::SS_BYTES; + /// Message size in bytes (for deterministic encapsulation). + pub const MESSAGE_SIZE: usize = 32; + /// Salt size in bytes (for deterministic encapsulation). + pub const SALT_SIZE: usize = crate::params::SALT_BYTES; + + /// HQC-256 encapsulation key. + pub type EncapsulationKey = crate::EncapsulationKey; + /// HQC-256 decapsulation key. + pub type DecapsulationKey = crate::DecapsulationKey; + /// HQC-256 ciphertext. + pub type Ciphertext = crate::Ciphertext; + /// HQC-256 shared secret. + pub type SharedSecret = crate::SharedSecret; + + /// Generate an HQC-256 key pair. + #[cfg(feature = "kgen")] + pub fn generate_key(rng: &mut impl rand::CryptoRng) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc256::generate_key(rng) + } + + /// Generate an HQC-256 key pair deterministically from a 32-byte seed. + #[cfg(feature = "kgen")] + pub fn generate_key_deterministic(seed: &[u8; 32]) -> (EncapsulationKey, DecapsulationKey) { + crate::Hqc256::generate_key_deterministic(seed) + } +} diff --git a/hqc-kem/src/params.rs b/hqc-kem/src/params.rs new file mode 100644 index 0000000..9ffa37d --- /dev/null +++ b/hqc-kem/src/params.rs @@ -0,0 +1,225 @@ +//! HQC parameter definitions for all security levels. + +/// Seed size in bytes. +pub(crate) const SEED_BYTES: usize = 32; +/// Salt size in bytes. +pub(crate) const SALT_BYTES: usize = 16; +/// Shared secret size in bytes. +pub(crate) const SS_BYTES: usize = 32; +/// GF(2^M) parameter M. +pub(crate) const PARAM_M: usize = 8; + +/// Internal runtime parameter set. +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct HqcParameters { + /// Ring dimension. + pub n: usize, + /// RS code length. + pub n1: usize, + /// RM code length. + pub n2: usize, + /// Concatenated code length n1*n2. + pub n1n2: usize, + /// Message length in bytes. + pub k: usize, + /// RS error-correction capability. + pub delta: usize, + /// Secret key Hamming weight. + pub w: usize, + /// Error vector Hamming weight. + pub w_e: usize, + /// Random vector Hamming weight. + pub w_r: usize, + /// FFT parameter: 2^fft >= delta+1. + pub fft: usize, + /// Byte size of n-bit vector: ceil(n/8). + pub n_bytes: usize, + /// Byte size of n1n2-bit vector: ceil(n1n2/8). + pub n1n2_bytes: usize, + /// u64 array size for n-bit vector: ceil(n/64). + pub vec_n_size_64: usize, + /// u64 array size for n1n2-bit vector: ceil(n1n2/64). + pub vec_n1n2_size_64: usize, + /// Public key size in bytes: SEED_BYTES + n_bytes. + pub pk_bytes: usize, + /// Secret key size in bytes: pk_bytes + SEED_BYTES + k + SEED_BYTES. + pub sk_bytes: usize, + /// Ciphertext size in bytes: n_bytes + n1n2_bytes + SALT_BYTES. + pub ct_bytes: usize, + /// Reduction mask for top u64 word. + pub red_mask: u64, + /// Barrett reciprocal for constant-time modular reduction: floor(2^32 / n). + pub barrett_recip: u32, + /// RS generator polynomial coefficients (ascending degree, monic). + pub rs_poly: &'static [u8], +} + +/// RS generator polynomial for HQC-128 (degree 30, delta=15). +static RS_POLY_128: [u8; 31] = [ + 89, 69, 153, 116, 176, 117, 111, 75, 73, 233, 242, 233, 65, 210, 21, 139, 103, 173, 67, 118, + 105, 210, 174, 110, 74, 69, 228, 82, 255, 181, 1, +]; + +/// RS generator polynomial for HQC-192 (degree 32, delta=16). +static RS_POLY_192: [u8; 33] = [ + 45, 216, 239, 24, 253, 104, 27, 40, 107, 50, 163, 210, 227, 134, 224, 158, 119, 13, 158, 1, + 238, 164, 82, 43, 15, 232, 246, 142, 50, 189, 29, 232, 1, +]; + +/// RS generator polynomial for HQC-256 (degree 58, delta=29). +static RS_POLY_256: [u8; 59] = [ + 49, 167, 49, 39, 200, 121, 124, 91, 240, 63, 148, 71, 150, 123, 87, 101, 32, 215, 159, 71, 201, + 115, 97, 210, 186, 183, 141, 217, 123, 12, 31, 243, 180, 219, 152, 239, 99, 141, 4, 246, 191, + 144, 8, 232, 47, 27, 141, 178, 130, 64, 124, 47, 39, 188, 216, 48, 199, 187, 1, +]; + +/// HQC-128 parameters. +pub(crate) const HQC_128: HqcParameters = HqcParameters { + n: 17669, + n1: 46, + n2: 384, + n1n2: 17664, + k: 16, + delta: 15, + w: 66, + w_e: 75, + w_r: 75, + fft: 4, + n_bytes: 2209, + n1n2_bytes: 2208, + vec_n_size_64: 277, + vec_n1n2_size_64: 276, + pk_bytes: 2241, + sk_bytes: 2321, + ct_bytes: 4433, + red_mask: 0x1f, + barrett_recip: 243079, // floor(2^32 / 17669) + rs_poly: &RS_POLY_128, +}; + +/// HQC-192 parameters. +pub(crate) const HQC_192: HqcParameters = HqcParameters { + n: 35851, + n1: 56, + n2: 640, + n1n2: 35840, + k: 24, + delta: 16, + w: 100, + w_e: 114, + w_r: 114, + fft: 5, + n_bytes: 4482, + n1n2_bytes: 4480, + vec_n_size_64: 561, + vec_n1n2_size_64: 560, + pk_bytes: 4514, + sk_bytes: 4602, + ct_bytes: 8978, + red_mask: 0x7ff, + barrett_recip: 119800, // floor(2^32 / 35851) + rs_poly: &RS_POLY_192, +}; + +/// HQC-256 parameters. +pub(crate) const HQC_256: HqcParameters = HqcParameters { + n: 57637, + n1: 90, + n2: 640, + n1n2: 57600, + k: 32, + delta: 29, + w: 131, + w_e: 149, + w_r: 149, + fft: 5, + n_bytes: 7205, + n1n2_bytes: 7200, + vec_n_size_64: 901, + vec_n1n2_size_64: 900, + pk_bytes: 7237, + sk_bytes: 7333, + ct_bytes: 14421, + red_mask: (1u64 << 37) - 1, + barrett_recip: 74517, // floor(2^32 / 57637) + rs_poly: &RS_POLY_256, +}; + +// Maximum sizes for stack-allocated arrays. +pub(crate) const MAX_N1: usize = 90; +pub(crate) const MAX_DELTA: usize = 29; + +mod sealed { + /// Sealed trait preventing external implementations of [`HqcParams`](super::HqcParams). + pub trait Sealed {} +} + +/// Trait defining an HQC parameter set. +/// +/// Sealed — cannot be implemented outside this crate. Use one of the provided +/// marker types: [`Hqc128Params`], [`Hqc192Params`], [`Hqc256Params`]. +pub trait HqcParams: sealed::Sealed + 'static { + /// Human-readable name (e.g. `"hqc128"`). + const NAME: &'static str; + /// Public key size in bytes. + const PK_BYTES: usize; + /// Secret key size in bytes. + const SK_BYTES: usize; + /// Ciphertext size in bytes. + const CT_BYTES: usize; + /// Shared secret size in bytes (always 32). + const SS_BYTES: usize = SS_BYTES; + + /// Runtime parameter struct for internal operations. + #[doc(hidden)] + fn params() -> &'static HqcParameters; +} + +/// HQC-128 parameter marker (NIST Level 1, 128-bit security). +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Hqc128Params; + +impl sealed::Sealed for Hqc128Params {} + +impl HqcParams for Hqc128Params { + const NAME: &'static str = "hqc128"; + const PK_BYTES: usize = 2241; + const SK_BYTES: usize = 2321; + const CT_BYTES: usize = 4433; + fn params() -> &'static HqcParameters { + &HQC_128 + } +} + +/// HQC-192 parameter marker (NIST Level 3, 192-bit security). +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Hqc192Params; + +impl sealed::Sealed for Hqc192Params {} + +impl HqcParams for Hqc192Params { + const NAME: &'static str = "hqc192"; + const PK_BYTES: usize = 4514; + const SK_BYTES: usize = 4602; + const CT_BYTES: usize = 8978; + fn params() -> &'static HqcParameters { + &HQC_192 + } +} + +/// HQC-256 parameter marker (NIST Level 5, 256-bit security). +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)] +pub struct Hqc256Params; + +impl sealed::Sealed for Hqc256Params {} + +impl HqcParams for Hqc256Params { + const NAME: &'static str = "hqc256"; + const PK_BYTES: usize = 7237; + const SK_BYTES: usize = 7333; + const CT_BYTES: usize = 14421; + fn params() -> &'static HqcParameters { + &HQC_256 + } +} diff --git a/hqc-kem/src/pkcs8_impl.rs b/hqc-kem/src/pkcs8_impl.rs new file mode 100644 index 0000000..e901738 --- /dev/null +++ b/hqc-kem/src/pkcs8_impl.rs @@ -0,0 +1,260 @@ +//! PKCS#8 encoding support for HQC-KEM keys. +//! +//! When the `pkcs8` feature is enabled, [`DecodePrivateKey`] is impl'd for +//! [`DecapsulationKey`], and [`DecodePublicKey`] is impl'd for [`EncapsulationKey`]. +//! +//! When both `pkcs8` and `alloc` features are enabled, [`EncodePrivateKey`] is impl'd +//! for [`DecapsulationKey`], and [`EncodePublicKey`] is impl'd for [`EncapsulationKey`]. + +pub use ::pkcs8::spki::AssociatedAlgorithmIdentifier; +pub use const_oid::AssociatedOid; + +use crate::{ + params::{HqcParams, Hqc128Params, Hqc192Params, Hqc256Params, SEED_BYTES}, + types::{DecapsulationKey, EncapsulationKey}, +}; +use ::pkcs8::{ + der::{ + AnyRef, Reader, SliceReader, TagNumber, + asn1::{ContextSpecific, OctetStringRef}, + }, + spki, +}; + +#[cfg(feature = "alloc")] +use ::pkcs8::der::{Encode, TagMode, asn1::BitStringRef}; + +#[cfg(feature = "alloc")] +use ::pkcs8::{EncodePrivateKey, EncodePublicKey}; + +/// Tag number for the seed value (matches ml-kem convention). +const SEED_TAG_NUMBER: TagNumber = TagNumber(0); + +/// HQC seed serialized as ASN.1. +type SeedString<'a> = ContextSpecific<&'a OctetStringRef>; + +// --------------------------------------------------------------------------- +// Provisional OIDs for HQC-KEM +// +// FIPS 207 does not yet have assigned OIDs. These are provisional placeholders +// in the NIST KEM arc (2.16.840.1.101.3.4.4.x). ML-KEM uses .1/.2/.3. +// These WILL change when NIST assigns official OIDs. +// --------------------------------------------------------------------------- + +impl AssociatedOid for Hqc128Params { + const OID: ::pkcs8::ObjectIdentifier = + ::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.4.4"); +} + +impl AssociatedOid for Hqc192Params { + const OID: ::pkcs8::ObjectIdentifier = + ::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.4.5"); +} + +impl AssociatedOid for Hqc256Params { + const OID: ::pkcs8::ObjectIdentifier = + ::pkcs8::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.4.6"); +} + +// AssociatedAlgorithmIdentifier for parameter types + +impl AssociatedAlgorithmIdentifier for Hqc128Params { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier = + spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + +impl AssociatedAlgorithmIdentifier for Hqc192Params { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier = + spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + +impl AssociatedAlgorithmIdentifier for Hqc256Params { + type Params = AnyRef<'static>; + const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier = + spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + +// AssociatedAlgorithmIdentifier for key types (delegating to P) + +impl

AssociatedAlgorithmIdentifier for EncapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + type Params = P::Params; + const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier = P::ALGORITHM_IDENTIFIER; +} + +impl

AssociatedAlgorithmIdentifier for DecapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + type Params = P::Params; + const ALGORITHM_IDENTIFIER: spki::AlgorithmIdentifier = P::ALGORITHM_IDENTIFIER; +} + +// --------------------------------------------------------------------------- +// EncodePublicKey (requires alloc) +// --------------------------------------------------------------------------- + +#[cfg(feature = "alloc")] +impl

EncodePublicKey for EncapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + fn to_public_key_der(&self) -> spki::Result { + let public_key = self.as_ref(); + let subject_public_key = BitStringRef::new(0, public_key)?; + + ::pkcs8::SubjectPublicKeyInfo { + algorithm: P::ALGORITHM_IDENTIFIER, + subject_public_key, + } + .try_into() + } +} + +// --------------------------------------------------------------------------- +// DecodePublicKey (via TryFrom) +// --------------------------------------------------------------------------- + +impl

TryFrom<::pkcs8::SubjectPublicKeyInfoRef<'_>> for EncapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + type Error = spki::Error; + + fn try_from(spki: ::pkcs8::SubjectPublicKeyInfoRef<'_>) -> Result { + if spki.algorithm.oid != P::ALGORITHM_IDENTIFIER.oid { + return Err(spki::Error::OidUnknown { + oid: P::ALGORITHM_IDENTIFIER.oid, + }); + } + + let bytes = spki + .subject_public_key + .as_bytes() + .ok_or(spki::Error::KeyMalformed)?; + + if bytes.len() != P::PK_BYTES { + return Err(spki::Error::KeyMalformed); + } + + Ok(EncapsulationKey::from_vec(bytes.to_vec())) + } +} + +// --------------------------------------------------------------------------- +// EncodePrivateKey (requires alloc) +// --------------------------------------------------------------------------- + +#[cfg(feature = "alloc")] +impl

EncodePrivateKey for DecapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + fn to_pkcs8_der(&self) -> ::pkcs8::Result { + let sk = self.as_ref(); + let seed = &sk[sk.len() - SEED_BYTES..]; + + let seed_der = SeedString { + tag_mode: TagMode::Implicit, + tag_number: SEED_TAG_NUMBER, + value: OctetStringRef::new(seed)?, + } + .to_der()?; + + let private_key = OctetStringRef::new(&seed_der)?; + let private_key_info = + pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, private_key); + pkcs8::SecretDocument::encode_msg(&private_key_info).map_err(pkcs8::Error::Asn1) + } +} + +// --------------------------------------------------------------------------- +// DecodePrivateKey (via TryFrom) +// --------------------------------------------------------------------------- + +impl

TryFrom<::pkcs8::PrivateKeyInfoRef<'_>> for DecapsulationKey

+where + P: HqcParams + AssociatedAlgorithmIdentifier>, +{ + type Error = ::pkcs8::Error; + + fn try_from( + private_key_info_ref: ::pkcs8::PrivateKeyInfoRef<'_>, + ) -> Result { + let _ = private_key_info_ref + .algorithm + .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?; + + let mut reader = SliceReader::new(private_key_info_ref.private_key.as_bytes())?; + let seed_string = SeedString::decode_implicit(&mut reader, SEED_TAG_NUMBER)? + .ok_or(pkcs8::Error::KeyMalformed)?; + let seed: [u8; SEED_BYTES] = seed_string + .value + .as_bytes() + .try_into() + .map_err(|_| pkcs8::Error::KeyMalformed)?; + reader.finish()?; + + let (_pk, sk) = crate::kem::keygen_deterministic(&seed, P::params()); + Ok(DecapsulationKey::from_vec(sk)) + } +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +#[cfg(all(test, feature = "alloc"))] +mod tests { + use super::*; + use ::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}; + + macro_rules! pkcs8_roundtrip_test { + ($name:ident, $params:ty) => { + #[test] + fn $name() { + let mut rng = rand::rng(); + let (ek, dk) = crate::HqcKem::<$params>::generate_key(&mut rng); + + // Public key roundtrip + let pk_der = ek.to_public_key_der().expect("encode public key"); + let ek2 = EncapsulationKey::<$params>::from_public_key_der(pk_der.as_bytes()) + .expect("decode public key"); + assert_eq!(ek.as_ref(), ek2.as_ref()); + + // Private key roundtrip + let sk_der = dk.to_pkcs8_der().expect("encode private key"); + let dk2 = DecapsulationKey::<$params>::from_pkcs8_der(sk_der.as_bytes()) + .expect("decode private key"); + + // Verify deterministic reconstruction: same EK + assert_eq!( + dk.encapsulation_key().as_ref(), + dk2.encapsulation_key().as_ref() + ); + + // Verify encaps/decaps works with reconstructed keys + let (ct, ss1) = ek2.encapsulate(&mut rng); + let ss2 = dk2.decapsulate(&ct); + assert_eq!(ss1, ss2); + } + }; + } + + pkcs8_roundtrip_test!(pkcs8_roundtrip_128, Hqc128Params); + pkcs8_roundtrip_test!(pkcs8_roundtrip_192, Hqc192Params); + pkcs8_roundtrip_test!(pkcs8_roundtrip_256, Hqc256Params); +} diff --git a/hqc-kem/src/pke.rs b/hqc-kem/src/pke.rs new file mode 100644 index 0000000..ae97f1f --- /dev/null +++ b/hqc-kem/src/pke.rs @@ -0,0 +1,154 @@ +/// HQC Public Key Encryption (PKE) operations. +/// +/// v5.0.0 PKE: +/// - keygen: I(seed_pke) → (seed_dk, seed_ek), sample y,x via rej, s = x + h*y +/// - encrypt: sample r2,e,r1 via mod from theta, u = r1 + h*r2, v = encode(m) + s*r2 + e +/// - decrypt: y from seed_dk, v - u*y, decode +use crate::code; +use crate::params::{HqcParameters, SEED_BYTES}; +use crate::poly; +use crate::sampling; +use crate::shake::{self, SeedExpander}; +use zeroize::Zeroize; + +/// PKE key generation from a PKE seed. +/// +/// Returns (ek_pke, dk_pke) where: +/// - ek_pke = seed_pke_ek || s_bytes (pk_bytes total) +/// - dk_pke = seed_pke_dk (SEED_BYTES) +pub(crate) fn pke_keygen(seed_pke: &[u8], p: &HqcParameters) -> (Vec, Vec) { + // I function: SHA3-512(seed_pke || 0x02) → 64 bytes + let mut i_res = shake::hash_i(seed_pke); + let seed_pke_dk = &i_res[..SEED_BYTES]; + let seed_pke_ek = &i_res[SEED_BYTES..2 * SEED_BYTES]; + + // Sample y, x from dk seed using rejection sampling + let mut ctx_dk = SeedExpander::new(seed_pke_dk); + let mut y = vec![0u64; p.vec_n_size_64]; + let mut x = vec![0u64; p.vec_n_size_64]; + sampling::sample_fixed_wt_rej(&mut ctx_dk, &mut y, p.w, p); + sampling::sample_fixed_wt_rej(&mut ctx_dk, &mut x, p.w, p); + + // Sample h from ek seed + let mut ctx_ek = SeedExpander::new(seed_pke_ek); + let mut h = vec![0u64; p.vec_n_size_64]; + sampling::sample_vect(&mut ctx_ek, &mut h, p); + + // s = x + h*y + let mut s = vec![0u64; p.vec_n_size_64]; + let mut tmp = vec![0u64; p.vec_n_size_64]; + poly::vect_mul(&mut tmp, &y, &h, p); + poly::vect_add(&mut s, &x, &tmp, p.vec_n_size_64); + + // Zeroize secret vectors — no longer needed + y.zeroize(); + x.zeroize(); + + // ek_pke = seed_pke_ek || s_bytes + let mut ek_pke = vec![0u8; p.pk_bytes]; + ek_pke[..SEED_BYTES].copy_from_slice(seed_pke_ek); + poly::store8_arr(&mut ek_pke[SEED_BYTES..], &s); + + // dk_pke = seed_pke_dk + let dk_pke = seed_pke_dk.to_vec(); + + // Zeroize I function result (contains secret dk seed) + i_res.zeroize(); + + (ek_pke, dk_pke) +} + +/// PKE encryption. +/// +/// Returns ciphertext bytes: u_bytes || v_bytes (n_bytes + n1n2_bytes). +pub(crate) fn pke_encrypt(ek_pke: &[u8], m: &[u8], theta: &[u8], p: &HqcParameters) -> Vec { + // Parse ek_pke + let seed_pke_ek = &ek_pke[..SEED_BYTES]; + + // Regenerate h from seed + let mut ctx_ek = SeedExpander::new(seed_pke_ek); + let mut h = vec![0u64; p.vec_n_size_64]; + sampling::sample_vect(&mut ctx_ek, &mut h, p); + + // Load s from ek_pke + let mut s = vec![0u64; p.vec_n_size_64]; + poly::load8_arr(&mut s, &ek_pke[SEED_BYTES..]); + + // Sample r2, e, r1 from theta using mod sampling + let mut ctx_th = SeedExpander::new(theta); + let mut r2 = vec![0u64; p.vec_n_size_64]; + let mut e = vec![0u64; p.vec_n_size_64]; + let mut r1 = vec![0u64; p.vec_n_size_64]; + sampling::sample_fixed_wt_mod(&mut ctx_th, &mut r2, p.w_r, p); + sampling::sample_fixed_wt_mod(&mut ctx_th, &mut e, p.w_e, p); + sampling::sample_fixed_wt_mod(&mut ctx_th, &mut r1, p.w_r, p); + + // u = r1 + h*r2 + let mut u = vec![0u64; p.vec_n_size_64]; + let mut tmp = vec![0u64; p.vec_n_size_64]; + poly::vect_mul(&mut tmp, &r2, &h, p); + poly::vect_add(&mut u, &r1, &tmp, p.vec_n_size_64); + + // cm = encode(m) - encoded into n1n2 bits + let mut cm = vec![0u64; p.vec_n1n2_size_64]; + code::code_encode(&mut cm, m, p); + + // v_full = cm + s*r2 + e (in n-bit space, then truncate to n1n2) + let mut sr2 = vec![0u64; p.vec_n_size_64]; + poly::vect_mul(&mut sr2, &r2, &s, p); + + // Work in n-bit space: extend cm and add + let mut v_full = vec![0u64; p.vec_n_size_64]; + // Copy cm into v_full (n1n2 <= n) + poly::vect_resize(&mut v_full, p.n, &cm, p.n1n2); + poly::vect_add_assign(&mut v_full, &sr2, p.vec_n_size_64); + poly::vect_add_assign(&mut v_full, &e, p.vec_n_size_64); + + // Truncate v to n1n2 bits + let mut v = vec![0u64; p.vec_n1n2_size_64]; + poly::vect_resize(&mut v, p.n1n2, &v_full, p.n); + + // Serialize: u_bytes || v_bytes + let mut ct = vec![0u8; p.n_bytes + p.n1n2_bytes]; + poly::store8_arr(&mut ct[..p.n_bytes], &u); + poly::store8_arr(&mut ct[p.n_bytes..], &v); + + ct +} + +/// PKE decryption. +/// +/// Returns decrypted message bytes (k bytes). +pub(crate) fn pke_decrypt(dk_pke: &[u8], c_pke: &[u8], p: &HqcParameters) -> Vec { + // Regenerate y from dk seed + let mut ctx_dk = SeedExpander::new(&dk_pke[..SEED_BYTES]); + let mut y = vec![0u64; p.vec_n_size_64]; + sampling::sample_fixed_wt_rej(&mut ctx_dk, &mut y, p.w, p); + + // Parse ciphertext + let mut u = vec![0u64; p.vec_n_size_64]; + let mut v = vec![0u64; p.vec_n1n2_size_64]; + poly::load8_arr(&mut u, &c_pke[..p.n_bytes]); + poly::load8_arr(&mut v, &c_pke[p.n_bytes..p.n_bytes + p.n1n2_bytes]); + + // Compute v - u*y (XOR in GF(2)) + let mut uy = vec![0u64; p.vec_n_size_64]; + poly::vect_mul(&mut uy, &y, &u, p); + + // Zeroize secret vector y — no longer needed + y.zeroize(); + + // Extend v to n bits, XOR with uy, truncate back + let mut v_full = vec![0u64; p.vec_n_size_64]; + poly::vect_resize(&mut v_full, p.n, &v, p.n1n2); + poly::vect_add_assign(&mut v_full, &uy, p.vec_n_size_64); + + // Truncate to n1n2 bits for decoding + let mut cm = vec![0u64; p.vec_n1n2_size_64]; + poly::vect_resize(&mut cm, p.n1n2, &v_full, p.n); + + // Decode + let mut m = vec![0u8; p.k]; + code::code_decode(&mut m, &cm, p); + m +} diff --git a/hqc-kem/src/poly.rs b/hqc-kem/src/poly.rs new file mode 100644 index 0000000..b802f35 --- /dev/null +++ b/hqc-kem/src/poly.rs @@ -0,0 +1,504 @@ +/// Binary polynomial operations in Z_2[X]/(X^n - 1). +/// +/// Polynomials are stored as arrays of u64, where each bit is a coefficient. +/// Uses Karatsuba multiplication for efficiency. +use crate::params::HqcParameters; + +/// Polynomial addition: o = v1 XOR v2. +#[inline] +pub(crate) fn vect_add(o: &mut [u64], v1: &[u64], v2: &[u64], size: usize) { + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx2") { + // Safety: AVX2 detected; pointers valid for `size` elements. + unsafe { + return vect_add_avx2(o, v1, v2, size); + } + } + for i in 0..size { + o[i] = v1[i] ^ v2[i]; + } +} + +/// In-place polynomial addition: v ^= rhs. +#[inline] +pub(crate) fn vect_add_assign(v: &mut [u64], rhs: &[u64], size: usize) { + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx2") { + unsafe { + return vect_add_assign_avx2(v, rhs, size); + } + } + for i in 0..size { + v[i] ^= rhs[i]; + } +} + +/// Constant-time byte comparison. Returns 0 if equal, non-zero otherwise. +pub(crate) fn vect_compare(v1: &[u8], v2: &[u8]) -> u8 { + let mut r: u16 = 0x0100; + for i in 0..v1.len().min(v2.len()) { + r |= (v1[i] ^ v2[i]) as u16; + } + ((r.wrapping_sub(1)) >> 8) as u8 +} + +/// Carry-less multiplication of two 64-bit words. +/// +/// Uses PCLMULQDQ on x86-64 when available, otherwise constant-time software fallback. +#[inline] +fn base_mul(a: u64, b: u64) -> [u64; 2] { + #[cfg(target_arch = "x86_64")] + { + if std::is_x86_feature_detected!("pclmulqdq") { + return unsafe { base_mul_pclmul(a, b) }; + } + } + base_mul_soft(a, b) +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "pclmulqdq")] +#[inline] +unsafe fn base_mul_pclmul(a: u64, b: u64) -> [u64; 2] { + use std::arch::x86_64::*; + let va = _mm_set_epi64x(0, a as i64); + let vb = _mm_set_epi64x(0, b as i64); + let r = _mm_clmulepi64_si128(va, vb, 0x00); + let mut result = [0u64; 2]; + _mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, r); + result +} + +/// Software carry-less multiplication (constant-time). +#[inline] +fn base_mul_soft(a: u64, b: u64) -> [u64; 2] { + let mut h: u64 = 0; + let mut l: u64; + let mut g: u64; + let mut u = [0u64; 16]; + + // Precompute small multiples of b (with top 4 bits masked) + u[0] = 0; + u[1] = b & ((1u64 << 60) - 1); + u[2] = u[1] << 1; + u[3] = u[2] ^ u[1]; + u[4] = u[2] << 1; + u[5] = u[4] ^ u[1]; + u[6] = u[3] << 1; + u[7] = u[6] ^ u[1]; + u[8] = u[4] << 1; + u[9] = u[8] ^ u[1]; + u[10] = u[5] << 1; + u[11] = u[10] ^ u[1]; + u[12] = u[6] << 1; + u[13] = u[12] ^ u[1]; + u[14] = u[7] << 1; + u[15] = u[14] ^ u[1]; + + // First nibble + g = 0; + let tmp1 = a & 0x0f; + for i in 0..16u64 { + let tmp2 = tmp1.wrapping_sub(i); + let mask = 0u64.wrapping_sub(1u64.wrapping_sub((tmp2 | tmp2.wrapping_neg()) >> 63)); + g ^= u[i as usize] & mask; + } + l = g; + + // Remaining nibbles + for shift in (4..64).step_by(4) { + g = 0; + let tmp1 = (a >> shift) & 0x0f; + for j in 0..16u64 { + let tmp2 = tmp1.wrapping_sub(j); + let mask = 0u64.wrapping_sub(1u64.wrapping_sub((tmp2 | tmp2.wrapping_neg()) >> 63)); + g ^= u[j as usize] & mask; + } + l ^= g << shift; + h ^= g >> (64 - shift); + } + + // Handle top 4 bits of b + let masks = [ + 0u64.wrapping_sub((b >> 60) & 1), + 0u64.wrapping_sub((b >> 61) & 1), + 0u64.wrapping_sub((b >> 62) & 1), + 0u64.wrapping_sub((b >> 63) & 1), + ]; + + l ^= (a << 60) & masks[0]; + h ^= (a >> 4) & masks[0]; + l ^= (a << 61) & masks[1]; + h ^= (a >> 3) & masks[1]; + l ^= (a << 62) & masks[2]; + h ^= (a >> 2) & masks[2]; + l ^= (a << 63) & masks[3]; + h ^= (a >> 1) & masks[3]; + + [l, h] +} + +#[inline] +fn karatsuba_add1( + alh: &mut [u64], + blh: &mut [u64], + a: &[u64], + b: &[u64], + size_l: usize, + size_h: usize, +) { + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx2") { + unsafe { + return karatsuba_add1_avx2(alh, blh, a, b, size_l, size_h); + } + } + for i in 0..size_h { + alh[i] = a[i] ^ a[i + size_l]; + blh[i] = b[i] ^ b[i + size_l]; + } + if size_h < size_l { + alh[size_h] = a[size_h]; + blh[size_h] = b[size_h]; + } +} + +#[inline] +fn karatsuba_add2(o: &mut [u64], tmp1: &mut [u64], tmp2: &[u64], size_l: usize, size_h: usize) { + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx2") { + unsafe { + return karatsuba_add2_avx2(o, tmp1, tmp2, size_l, size_h); + } + } + for i in 0..(2 * size_l) { + tmp1[i] ^= o[i]; + } + for i in 0..(2 * size_h) { + tmp1[i] ^= tmp2[i]; + } + for i in 0..(2 * size_l) { + o[i + size_l] ^= tmp1[i]; + } +} + +/// Recursive Karatsuba multiplication. +/// +/// Stack layout per level: [alh(size_l) | blh(size_l) | tmp1(2*size_l) | copies(2*size_l) | deeper...] +/// Total local = 6*size_l per level. The 8*vec_n pre-allocation is sufficient for all levels. +fn karatsuba(o: &mut [u64], a: &[u64], b: &[u64], size: usize, stack: &mut [u64]) { + if size == 1 { + let c = base_mul(a[0], b[0]); + o[0] = c[0]; + o[1] = c[1]; + return; + } + + let size_h = size / 2; + let size_l = size.div_ceil(2); + + // Split stack: 6*size_l for this level, rest for recursion + let (local, stack_rest) = stack.split_at_mut(6 * size_l); + + // local layout: [alh | blh | tmp1 | copies] + let (alh_blh, tmp1_copies) = local.split_at_mut(2 * size_l); + let (tmp1_part, copies_part) = tmp1_copies.split_at_mut(2 * size_l); + + karatsuba(o, a, b, size_l, stack_rest); + karatsuba( + &mut o[2 * size_l..], + &a[size_l..], + &b[size_l..], + size_h, + stack_rest, + ); + + { + let (alh_part, blh_part) = alh_blh.split_at_mut(size_l); + karatsuba_add1(alh_part, blh_part, a, b, size_l, size_h); + } + + // Copy alh/blh into copies region so we can pass tmp1 as mutable output + let (alh, blh) = alh_blh.split_at(size_l); + let (alh_copy, blh_copy) = copies_part.split_at_mut(size_l); + alh_copy[..size_l].copy_from_slice(alh); + blh_copy[..size_l].copy_from_slice(&blh[..size_l]); + + // Clear tmp1 before writing + for v in tmp1_part[..2 * size_l].iter_mut() { + *v = 0; + } + karatsuba( + tmp1_part, + &alh_copy[..size_l], + &blh_copy[..size_l], + size_l, + stack_rest, + ); + + // Copy tmp2 (in o[2*size_l..]) into copies region to avoid aliasing + let tmp2_len = 2 * size_h; + copies_part[..tmp2_len].copy_from_slice(&o[2 * size_l..2 * size_l + tmp2_len]); + + karatsuba_add2(o, tmp1_part, &copies_part[..tmp2_len], size_l, size_h); +} + +/// Reduce polynomial modulo X^n - 1. +fn reduce(o: &mut [u64], a: &[u64], n: usize, vec_n_size_64: usize) { + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx2") { + unsafe { + return reduce_avx2(o, a, n, vec_n_size_64); + } + } + let shift = n & 0x3f; + for i in 0..vec_n_size_64 { + let r = a[i + vec_n_size_64 - 1] >> shift; + let carry = if i + vec_n_size_64 < a.len() { + a[i + vec_n_size_64] << (64 - shift) + } else { + 0 + }; + o[i] = a[i] ^ r ^ carry; + } +} + +/// Multiply two polynomials modulo X^n - 1. +pub(crate) fn vect_mul(o: &mut [u64], v1: &[u64], v2: &[u64], p: &HqcParameters) { + let vec_n = p.vec_n_size_64; + let mut stack = vec![0u64; vec_n << 3]; + let mut o_karat = vec![0u64; vec_n << 1]; + + karatsuba(&mut o_karat, &v1[..vec_n], &v2[..vec_n], vec_n, &mut stack); + reduce(o, &o_karat, p.n, vec_n); + o[vec_n - 1] &= p.red_mask; +} + +/// Load byte array into u64 array (little-endian). +#[inline] +pub(crate) fn load8_arr(out: &mut [u64], inp: &[u8]) { + let full_chunks = inp.len() / 8; + let count = full_chunks.min(out.len()); + for i in 0..count { + let mut buf = [0u8; 8]; + buf.copy_from_slice(&inp[i * 8..(i + 1) * 8]); + out[i] = u64::from_le_bytes(buf); + } + let rem = inp.len() - count * 8; + if rem > 0 && count < out.len() { + let mut buf = [0u8; 8]; + buf[..rem].copy_from_slice(&inp[count * 8..]); + out[count] = u64::from_le_bytes(buf); + } +} + +/// Store u64 array into byte array (little-endian). +#[inline] +pub(crate) fn store8_arr(out: &mut [u8], inp: &[u64]) { + let full_chunks = out.len() / 8; + let count = full_chunks.min(inp.len()); + for i in 0..count { + out[i * 8..(i + 1) * 8].copy_from_slice(&inp[i].to_le_bytes()); + } + let rem = out.len() - count * 8; + if rem > 0 && count < inp.len() { + let bytes = inp[count].to_le_bytes(); + out[count * 8..].copy_from_slice(&bytes[..rem]); + } +} + +// ---- AVX2 SIMD acceleration (x86-64 only) ---- + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn vect_add_avx2(o: &mut [u64], v1: &[u64], v2: &[u64], size: usize) { + use std::arch::x86_64::*; + let chunks = size / 4; + let p1 = v1.as_ptr() as *const __m256i; + let p2 = v2.as_ptr() as *const __m256i; + let po = o.as_mut_ptr() as *mut __m256i; + for i in 0..chunks { + _mm256_storeu_si256( + po.add(i), + _mm256_xor_si256(_mm256_loadu_si256(p1.add(i)), _mm256_loadu_si256(p2.add(i))), + ); + } + for i in (chunks * 4)..size { + o[i] = v1[i] ^ v2[i]; + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn vect_add_assign_avx2(v: &mut [u64], rhs: &[u64], size: usize) { + use std::arch::x86_64::*; + let chunks = size / 4; + let pv = v.as_mut_ptr() as *mut __m256i; + let pr = rhs.as_ptr() as *const __m256i; + for i in 0..chunks { + _mm256_storeu_si256( + pv.add(i), + _mm256_xor_si256( + _mm256_loadu_si256(pv.add(i) as *const __m256i), + _mm256_loadu_si256(pr.add(i)), + ), + ); + } + for i in (chunks * 4)..size { + v[i] ^= rhs[i]; + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn karatsuba_add1_avx2( + alh: &mut [u64], + blh: &mut [u64], + a: &[u64], + b: &[u64], + size_l: usize, + size_h: usize, +) { + use std::arch::x86_64::*; + let chunks = size_h / 4; + for i in 0..chunks { + let idx = i * 4; + let a_lo = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); + let a_hi = _mm256_loadu_si256(a.as_ptr().add(idx + size_l) as *const __m256i); + _mm256_storeu_si256( + alh.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(a_lo, a_hi), + ); + let b_lo = _mm256_loadu_si256(b.as_ptr().add(idx) as *const __m256i); + let b_hi = _mm256_loadu_si256(b.as_ptr().add(idx + size_l) as *const __m256i); + _mm256_storeu_si256( + blh.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(b_lo, b_hi), + ); + } + for i in (chunks * 4)..size_h { + alh[i] = a[i] ^ a[i + size_l]; + blh[i] = b[i] ^ b[i + size_l]; + } + if size_h < size_l { + alh[size_h] = a[size_h]; + blh[size_h] = b[size_h]; + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +#[allow(clippy::cast_ptr_alignment)] +unsafe fn karatsuba_add2_avx2( + o: &mut [u64], + tmp1: &mut [u64], + tmp2: &[u64], + size_l: usize, + size_h: usize, +) { + use std::arch::x86_64::*; + let len1 = 2 * size_l; + let len2 = 2 * size_h; + + // tmp1[i] ^= o[i] for i in 0..2*size_l + let chunks1 = len1 / 4; + for i in 0..chunks1 { + let idx = i * 4; + let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; + _mm256_storeu_si256( + pt, + _mm256_xor_si256( + _mm256_loadu_si256(pt as *const __m256i), + _mm256_loadu_si256(o.as_ptr().add(idx) as *const __m256i), + ), + ); + } + for i in (chunks1 * 4)..len1 { + tmp1[i] ^= o[i]; + } + + // tmp1[i] ^= tmp2[i] for i in 0..2*size_h + let chunks2 = len2 / 4; + for i in 0..chunks2 { + let idx = i * 4; + let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; + _mm256_storeu_si256( + pt, + _mm256_xor_si256( + _mm256_loadu_si256(pt as *const __m256i), + _mm256_loadu_si256(tmp2.as_ptr().add(idx) as *const __m256i), + ), + ); + } + for i in (chunks2 * 4)..len2 { + tmp1[i] ^= tmp2[i]; + } + + // o[i + size_l] ^= tmp1[i] for i in 0..2*size_l + for i in 0..chunks1 { + let idx = i * 4; + let po = o.as_mut_ptr().add(idx + size_l) as *mut __m256i; + _mm256_storeu_si256( + po, + _mm256_xor_si256( + _mm256_loadu_si256(po as *const __m256i), + _mm256_loadu_si256(tmp1.as_ptr().add(idx) as *const __m256i), + ), + ); + } + for i in (chunks1 * 4)..len1 { + o[i + size_l] ^= tmp1[i]; + } +} + +#[cfg(target_arch = "x86_64")] +#[target_feature(enable = "avx2")] +unsafe fn reduce_avx2(o: &mut [u64], a: &[u64], n: usize, vec_n: usize) { + use std::arch::x86_64::*; + let shift = (n & 0x3f) as i64; + let inv_shift = 64 - shift; + let shift_v = _mm_set_epi64x(0, shift); + let inv_shift_v = _mm_set_epi64x(0, inv_shift); + let chunks = vec_n / 4; + + for i in 0..chunks { + let idx = i * 4; + let base = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); + let hi_prev = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n - 1) as *const __m256i); + let hi_next = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n) as *const __m256i); + let r = _mm256_srl_epi64(hi_prev, shift_v); + let carry = _mm256_sll_epi64(hi_next, inv_shift_v); + _mm256_storeu_si256( + o.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(base, _mm256_xor_si256(r, carry)), + ); + } + // Scalar remainder + let shift_s = n & 0x3f; + for i in (chunks * 4)..vec_n { + let r = a[i + vec_n - 1] >> shift_s; + let carry = if i + vec_n < a.len() { + a[i + vec_n] << (64 - shift_s) + } else { + 0 + }; + o[i] = a[i] ^ r ^ carry; + } +} + +/// Resize vector: copy and potentially truncate. +pub(crate) fn vect_resize(o: &mut [u64], size_o: usize, v: &[u64], size_v: usize) { + if size_o < size_v { + let n1n2_64 = size_o.div_ceil(64); + let copy_words = n1n2_64.min(o.len()).min(v.len()); + o[..copy_words].copy_from_slice(&v[..copy_words]); + let bits_in_last = size_o % 64; + if bits_in_last != 0 && n1n2_64 > 0 && n1n2_64 - 1 < o.len() { + o[n1n2_64 - 1] &= (1u64 << bits_in_last) - 1; + } + } else { + let words = size_v.div_ceil(64); + let copy_words = words.min(o.len()).min(v.len()); + o[..copy_words].copy_from_slice(&v[..copy_words]); + } +} diff --git a/hqc-kem/src/reed_muller.rs b/hqc-kem/src/reed_muller.rs new file mode 100644 index 0000000..46be489 --- /dev/null +++ b/hqc-kem/src/reed_muller.rs @@ -0,0 +1,149 @@ +/// Reed-Muller RM(1,7) encoding and decoding with repetition. +/// +/// Each byte is encoded into a 128-bit RM(1,7) codeword, then repeated +/// `mult` times to fill `n2` bits per symbol. +use crate::params::HqcParameters; + +/// Number of repetitions for a given parameter set. +fn multiplicity(p: &HqcParameters) -> usize { + p.n2.div_ceil(128) +} + +/// Copy bit 0 of x into all 32 bits. +#[inline] +fn bit0mask(x: u32) -> u32 { + 0u32.wrapping_sub(x & 1) +} + +/// Encode a single byte into a 128-bit RM(1,7) codeword (two u64s). +fn encode_single(message: u8) -> [u64; 2] { + // bit 7 flips all bits + let mut first_word: u32 = bit0mask((message >> 7) as u32); + first_word ^= bit0mask(message as u32) & 0xAAAAAAAA; + first_word ^= bit0mask((message >> 1) as u32) & 0xCCCCCCCC; + first_word ^= bit0mask((message >> 2) as u32) & 0xF0F0F0F0; + first_word ^= bit0mask((message >> 3) as u32) & 0xFF00FF00; + first_word ^= bit0mask((message >> 4) as u32) & 0xFFFF0000; + + let mut cword = [0u64; 2]; + cword[0] = first_word as u64; + + // bit 5 flips entries 1 and 3; bit 6 flips entries 2 and 3 + first_word ^= bit0mask((message >> 5) as u32); + cword[0] |= (first_word as u64) << 32; + + first_word ^= bit0mask((message >> 6) as u32); + cword[1] = (first_word as u64) << 32; + + first_word ^= bit0mask((message >> 5) as u32); + cword[1] |= first_word as u64; + + cword +} + +/// Hadamard transform using a flag to track which buffer has the result. +fn hadamard_transform(expanded: &mut [i16; 128], transform: &mut [i16; 128]) { + // Copy expanded to a working buffer + let mut buf_a = *expanded; + let mut buf_b = [0i16; 128]; + + let mut src = &mut buf_a; + let mut dst = &mut buf_b; + + for _pass in 0..7 { + for i in 0..64 { + dst[i] = src[2 * i].wrapping_add(src[2 * i + 1]); + dst[i + 64] = src[2 * i].wrapping_sub(src[2 * i + 1]); + } + core::mem::swap(&mut src, &mut dst); + } + // After 7 passes, result is in `src` + transform.copy_from_slice(src); +} + +/// Sum repeated codewords into 128 accumulators. +fn expand_and_sum(dest: &mut [i16; 128], src: &[u64], mult: usize) { + // First copy + for part in 0..2 { + for bit in 0..64 { + dest[part * 64 + bit] = ((src[part] >> bit) & 1) as i16; + } + } + // Sum remaining copies + for copy in 1..mult { + for part in 0..2 { + if 2 * copy + part < src.len() { + for bit in 0..64 { + dest[part * 64 + bit] += ((src[2 * copy + part] >> bit) & 1) as i16; + } + } + } + } +} + +/// Find the peak in the Hadamard transform (constant-time). +fn find_peaks(transform: &[i16; 128], _mult: usize) -> u8 { + let mut peak_abs: u16 = 0; + let mut peak: i16 = 0; + let mut pos: u16 = 0; + + for i in 0..128u16 { + let t = transform[i as usize]; + // Branchless absolute value: avoids timing leak on Hadamard coefficients + let mask = t >> 15; // arithmetic right shift: -1 if negative, 0 if non-negative + let abs_t = ((t ^ mask).wrapping_sub(mask)) as u16; + + // Update if this abs is strictly greater (constant time) + let mask = 0u16.wrapping_sub((peak_abs.wrapping_sub(abs_t)) >> 15); // mask = 0xFFFF if peak_abs < abs_t + peak = (peak & !(mask as i16)) | (t & (mask as i16)); + pos = (pos & !mask) | (i & mask); + peak_abs = (peak_abs & !mask) | (abs_t & mask); + } + + // Set bit 7 if peak is positive (>=0) + let positive = 0u16.wrapping_sub(1u16.wrapping_sub((peak as u16) >> 15)); // 0xFFFF if positive + pos |= 128 & positive; + + pos as u8 +} + +/// Encode message bytes into concatenated RM codewords. +/// +/// Each byte of `msg` (length n1) is RM-encoded into `mult` repetitions of +/// 128-bit codewords, producing n1*n2 bits total in `cdw`. +pub(crate) fn reed_muller_encode(cdw: &mut [u64], msg: &[u8], p: &HqcParameters) { + let mult = multiplicity(p); + for (i, &byte) in msg.iter().enumerate().take(p.n1) { + let cword = encode_single(byte); + // Write first codeword + let base = 2 * i * mult; + if base + 1 < cdw.len() { + cdw[base] = cword[0]; + cdw[base + 1] = cword[1]; + } + // Copy to remaining repetitions + for copy in 1..mult { + let dst = base + 2 * copy; + if dst + 1 < cdw.len() { + cdw[dst] = cword[0]; + cdw[dst + 1] = cword[1]; + } + } + } +} + +/// Decode concatenated RM codewords into message bytes. +pub(crate) fn reed_muller_decode(msg: &mut [u8], cdw: &[u64], p: &HqcParameters) { + let mult = multiplicity(p); + let mut expanded = [0i16; 128]; + let mut transform = [0i16; 128]; + + for (i, byte) in msg.iter_mut().enumerate().take(p.n1) { + let base = 2 * i * mult; + expand_and_sum(&mut expanded, &cdw[base..], mult); + hadamard_transform(&mut expanded, &mut transform); + // Fix first entry: subtract 64 * mult + transform[0] = transform[0].wrapping_sub((64 * mult) as i16); + *byte = find_peaks(&transform, mult); + } +} diff --git a/hqc-kem/src/reed_solomon.rs b/hqc-kem/src/reed_solomon.rs new file mode 100644 index 0000000..7466f41 --- /dev/null +++ b/hqc-kem/src/reed_solomon.rs @@ -0,0 +1,233 @@ +/// Reed-Solomon encoding and decoding over GF(2^8). +/// +/// Systematic RS(n1, k) with error correction capability delta = (n1-k)/2. +/// Generator polynomial roots: alpha^1, ..., alpha^(2*delta). +use crate::fft; +use crate::gf256::{GF_EXP, gf_inverse, gf_mul}; +use crate::params::{HqcParameters, MAX_DELTA, MAX_N1}; + +/// Encode message into RS codeword (systematic). +/// +/// Message bytes are placed at positions [n1-k..n1-1] (high end). +/// Parity bytes at positions [0..n1-k-1] (low end). +pub(crate) fn reed_solomon_encode(cdw: &mut [u8], msg: &[u8], p: &HqcParameters) { + // Clear output + cdw[..p.n1].fill(0); + + for i in 0..p.k { + let gate_value = msg[p.k - 1 - i] ^ cdw[p.n1 - p.k - 1]; + + // Shift register: shift right by 1 and XOR with gate_value * g[k] + for k_idx in (1..p.n1 - p.k).rev() { + cdw[k_idx] = + cdw[k_idx - 1] ^ (gf_mul(gate_value as u16, p.rs_poly[k_idx] as u16) as u8); + } + cdw[0] = gf_mul(gate_value as u16, p.rs_poly[0] as u16) as u8; + } + + // Copy message to high positions + cdw[p.n1 - p.k..p.n1].copy_from_slice(&msg[..p.k]); +} + +/// Compute 2*delta syndromes. +#[allow(clippy::needless_range_loop)] // indices used in arithmetic matching FIPS spec +fn compute_syndromes(syndromes: &mut [u16], cdw: &[u8], p: &HqcParameters) { + for i in 0..(2 * p.delta) { + syndromes[i] = 0; + for j in 1..p.n1 { + // alpha^((i+1)*j) + let pow = ((i + 1) * j) % 255; + syndromes[i] ^= gf_mul(cdw[j] as u16, GF_EXP[pow]); + } + syndromes[i] ^= cdw[0] as u16; + } +} + +/// Berlekamp algorithm: compute error locator polynomial sigma. +/// +/// Constant-time implementation using mask-based updates. +/// Returns degree of sigma. +fn compute_elp(sigma: &mut [u16], syndromes: &[u16], p: &HqcParameters) -> u16 { + let max_delta = p.delta; + let mut deg_sigma: u16 = 0; + let mut deg_sigma_p: u16 = 0; + let mut sigma_copy = [0u16; MAX_DELTA + 1]; + let mut x_sigma_p = [0u16; MAX_DELTA + 1]; + x_sigma_p[1] = 1; + let mut pp: u16 = 0u16.wrapping_sub(1); // -1 in u16 + let mut d_p: u16 = 1; + let mut d: u16 = syndromes[0]; + + sigma[0] = 1; + + for mu in 0..(2 * max_delta) { + // Save sigma + let deg_sigma_copy_val = deg_sigma; + sigma_copy[..max_delta].copy_from_slice(&sigma[..max_delta]); + + let dd = gf_mul(d, gf_inverse(d_p)); + + for i in 1..=(mu + 1).min(max_delta) { + sigma[i] ^= gf_mul(dd, x_sigma_p[i]); + } + + let deg_x = (mu as u16).wrapping_sub(pp); + let deg_x_sigma_p = deg_x.wrapping_add(deg_sigma_p); + + // mask1 = 0xFFFF if d != 0 + let mask1 = 0u16.wrapping_sub((0u16.wrapping_sub(d)) >> 15); + // mask2 = 0xFFFF if deg_x_sigma_p > deg_sigma + let mask2 = 0u16.wrapping_sub(deg_sigma.wrapping_sub(deg_x_sigma_p) >> 15); + let mask12 = mask1 & mask2; + + deg_sigma ^= mask12 & (deg_x_sigma_p ^ deg_sigma); + + if mu == 2 * max_delta - 1 { + break; + } + + pp ^= mask12 & ((mu as u16) ^ pp); + d_p ^= mask12 & (d ^ d_p); + + for i in (1..=max_delta).rev() { + x_sigma_p[i] = (mask12 & sigma_copy[i - 1]) ^ (!mask12 & x_sigma_p[i - 1]); + } + x_sigma_p[0] = 0; + + deg_sigma_p ^= mask12 & (deg_sigma_copy_val ^ deg_sigma_p); + + d = syndromes[mu + 1]; + for i in 1..=(mu + 1).min(max_delta) { + d ^= gf_mul(sigma[i], syndromes[mu + 1 - i]); + } + } + + deg_sigma +} + +/// Compute z polynomial for error value computation. +fn compute_z_poly(z: &mut [u16], sigma: &[u16], degree: u16, syndromes: &[u16], delta: usize) { + z[0] = 1; + + for i in 1..=delta { + let mask = 0u16.wrapping_sub(((i as u16).wrapping_sub(degree.wrapping_add(1))) >> 15); + z[i] = mask & sigma[i]; + } + + z[1] ^= syndromes[0]; + + for i in 2..=delta { + let mask = 0u16.wrapping_sub(((i as u16).wrapping_sub(degree.wrapping_add(1))) >> 15); + z[i] ^= mask & syndromes[i - 1]; + for j in 1..i { + z[i] ^= mask & gf_mul(sigma[j], syndromes[i - j - 1]); + } + } +} + +/// Compute error values using Forney's algorithm. +#[allow(clippy::needless_range_loop)] // constant-time mask operations indexed across multiple arrays +fn compute_error_values(error_values: &mut [u16], z: &[u16], error: &[u8; 256], p: &HqcParameters) { + let mut beta_j = [0u16; MAX_DELTA]; + let mut e_j = [0u16; MAX_DELTA]; + + // Compute beta values (error locator field elements) + let mut delta_counter: u16 = 0; + for i in 0..p.n1 { + let found_mask = + 0u16.wrapping_sub(((0i32.wrapping_sub(error[i] as i32)) as u32 >> 31) as u16); + let mut local_found: u16 = 0; + + for j in 0..p.delta { + // Proper constant-time eq: both are u16 + let diff = (j as u16) ^ delta_counter; + let zero_mask = + 0u16.wrapping_sub(((diff as u32 | diff.wrapping_neg() as u32) >> 31) as u16); + let eq_mask2 = !zero_mask; // 0xFFFF if j == delta_counter + + beta_j[j] ^= found_mask & eq_mask2 & GF_EXP[i]; + local_found = local_found.wrapping_add(found_mask & eq_mask2 & 1); + } + delta_counter = delta_counter.wrapping_add(local_found); + } + let delta_real_value = delta_counter; + + // Compute error values + for i in 0..p.delta { + let mut tmp1: u16 = 1; + let mut tmp2: u16 = 1; + let inverse = gf_inverse(beta_j[i]); + let mut inverse_power_j: u16 = 1; + + for j in 1..=p.delta { + inverse_power_j = gf_mul(inverse_power_j, inverse); + tmp1 ^= gf_mul(inverse_power_j, z[j]); + } + + for k in 1..p.delta { + let idx = (i + k) % p.delta; + tmp2 = gf_mul(tmp2, 1 ^ gf_mul(inverse, beta_j[idx])); + } + + // mask1 = 0xFFFF if i < delta_real_value + let mask1 = 0u16.wrapping_sub(((i as u16).wrapping_sub(delta_real_value)) >> 15); + e_j[i] = mask1 & gf_mul(tmp1, gf_inverse(tmp2)); + } + + // Place error values at correct positions + delta_counter = 0; + for i in 0..p.n1 { + error_values[i] = 0; + let found_mask = + 0u16.wrapping_sub(((0i32.wrapping_sub(error[i] as i32)) as u32 >> 31) as u16); + let mut local_found: u16 = 0; + + for j in 0..p.delta { + let diff = (j as u16) ^ delta_counter; + let zero_mask = + 0u16.wrapping_sub(((diff as u32 | diff.wrapping_neg() as u32) >> 31) as u16); + let eq_mask = !zero_mask; + + error_values[i] ^= found_mask & eq_mask & e_j[j]; + local_found = local_found.wrapping_add(found_mask & eq_mask & 1); + } + delta_counter = delta_counter.wrapping_add(local_found); + } +} + +/// Decode RS codeword, correcting up to delta errors. +/// +/// Modifies `cdw` in place, then extracts message from positions [2*delta..n1-1]. +pub(crate) fn reed_solomon_decode(msg: &mut [u8], cdw: &mut [u8], p: &HqcParameters) { + let mut syndromes = [0u16; 2 * MAX_DELTA]; + let mut sigma = [0u16; 1 << 5]; // 2^MAX_FFT + let mut error = [0u8; 256]; // 2^PARAM_M + let mut z = [0u16; MAX_N1]; + let mut error_values = [0u16; MAX_N1]; + + // Compute syndromes + compute_syndromes(&mut syndromes, cdw, p); + + // Compute error locator polynomial + let deg = compute_elp(&mut sigma, &syndromes, p); + + // Find roots via FFT + let mut w = [0u16; 256]; + fft::fft(&mut w, &sigma, p.delta + 1, p.fft); + fft::fft_retrieve_error_poly(&mut error, &w); + + // Compute z polynomial + compute_z_poly(&mut z, &sigma, deg, &syndromes, p.delta); + + // Compute error values + compute_error_values(&mut error_values, &z, &error, p); + + // Correct errors + for i in 0..p.n1 { + cdw[i] ^= error_values[i] as u8; + } + + // Extract message (positions after parity bytes) + let parity_len = 2 * p.delta; + msg[..p.k].copy_from_slice(&cdw[parity_len..parity_len + p.k]); +} diff --git a/hqc-kem/src/sampling.rs b/hqc-kem/src/sampling.rs new file mode 100644 index 0000000..f10e3f2 --- /dev/null +++ b/hqc-kem/src/sampling.rs @@ -0,0 +1,146 @@ +/// Fixed-weight vector sampling. +/// +/// Two methods per v5.0.0: +/// - `sample_fixed_wt_mod`: modular mapping, used in encrypt (r2, e, r1). +/// - `sample_fixed_wt_rej`: rejection sampling, used in keygen (y, x). +/// - `sample_vect`: uniform random vector via XOF. +use crate::params::HqcParameters; +use crate::shake::SeedExpander; + +/// Sample a random binary vector of n bits via XOF. +pub(crate) fn sample_vect(xof: &mut SeedExpander, v: &mut [u64], p: &HqcParameters) { + let mut rand_bytes = vec![0u8; p.n_bytes]; + xof.get_bytes(&mut rand_bytes); + crate::poly::load8_arr(v, &rand_bytes); + if p.vec_n_size_64 > 0 { + v[p.vec_n_size_64 - 1] &= p.red_mask; + } +} + +/// Sample a fixed-weight vector using modular mapping (v5.0.0 encrypt). +/// +/// Reads 4*weight bytes from XOF (with alignment waste), maps each u32 +/// to position via multiplication-based reduction, deduplicates. +pub(crate) fn sample_fixed_wt_mod( + xof: &mut SeedExpander, + v: &mut [u64], + weight: usize, + p: &HqcParameters, +) { + let mut rand_bytes = vec![0u8; 4 * weight]; + xof.get_bytes(&mut rand_bytes); + + let mut pos = vec![0u32; weight]; + for i in 0..weight { + let u = u32::from_le_bytes([ + rand_bytes[4 * i], + rand_bytes[4 * i + 1], + rand_bytes[4 * i + 2], + rand_bytes[4 * i + 3], + ]); + // v5.0.0 modular mapping: pos = ((u * (n-i)) >> 32) + i + let n_minus_i = (p.n - i) as u64; + pos[i] = (((u as u64 * n_minus_i) >> 32) + i as u64) as u32; + } + + // Dedup backwards (v5.0.0 style): for each i from wt-1 down to 1, + // check if any j < i has the same position. If so, set pos[i] = i. + for i in (1..weight).rev() { + let mut found = 0u32; + for j in 0..i { + // Constant-time equality check + let diff = pos[j] ^ pos[i]; + let is_zero = (diff as u64 | (diff as u64).wrapping_neg()) >> 63; // 1 if non-zero + found |= 1u32.wrapping_sub(is_zero as u32); // 1 if zero (equal) + } + let mask = 0u32.wrapping_sub(found & 1); + pos[i] = (pos[i] & !mask) | (i as u32 & mask); + } + + // Set bits in output vector + for i in 0..p.vec_n_size_64.min(v.len()) { + v[i] = 0; + } + for &position in pos.iter().take(weight) { + let idx = position as usize >> 6; + let bit = position as usize & 0x3f; + if idx < v.len() { + v[idx] |= 1u64 << bit; + } + } +} + +/// Barrett reduction: val mod n using precomputed reciprocal. +/// +/// Avoids variable-time `div` instruction. Uses `floor(2^32 / n)` as reciprocal. +/// Requires val < n * (2^32 / n), which holds for val < 2^24 and n < 2^17. +#[inline] +fn barrett_reduce(val: u32, n: u32, reciprocal: u32) -> u32 { + let q = ((val as u64 * reciprocal as u64) >> 32) as u32; + let mut r = val.wrapping_sub(q.wrapping_mul(n)); + // At most one correction needed: if r >= n, subtract n. + // Constant-time: mask is all-ones if r >= n, all-zeros otherwise. + let correction = n & 0u32.wrapping_sub((r >= n) as u32); + r = r.wrapping_sub(correction); + r +} + +/// Sample a fixed-weight vector using rejection sampling (v5.0.0 keygen). +/// +/// Reads 3*weight bytes per XOF chunk (matching reference implementation), +/// parses as big-endian 3-byte values, rejects if >= n_rej or duplicate. +/// Uses Barrett reduction instead of modulo to avoid variable-time division. +pub(crate) fn sample_fixed_wt_rej( + xof: &mut SeedExpander, + v: &mut [u64], + weight: usize, + p: &HqcParameters, +) { + let n = p.n as u32; + let n_rej = ((1u32 << 24) / n) * n; + let chunk_size = 3 * weight; + + // Clear output vector + for i in 0..p.vec_n_size_64.min(v.len()) { + v[i] = 0; + } + + let mut count = 0usize; + let mut rand_bytes = vec![0u8; chunk_size]; + let mut j = chunk_size; // Start at chunk_size to trigger first read + + // Maximum iteration bound to prevent potential DoS from degenerate XOF output. + // Expected iterations ≈ weight * (2^24 / n_rej). Bound at 16x weight. + let max_iters = 16 * weight; + let mut iters = 0usize; + + while count < weight && iters < max_iters { + if j >= chunk_size { + xof.get_bytes(&mut rand_bytes); + j = 0; + } + + // Big-endian 3-byte read + let val = ((rand_bytes[j] as u32) << 16) + | ((rand_bytes[j + 1] as u32) << 8) + | (rand_bytes[j + 2] as u32); + j += 3; + iters += 1; + + if val < n_rej { + // Barrett reduction: constant-time modular reduction (no div instruction) + let pos = barrett_reduce(val, n, p.barrett_recip) as usize; + let idx = pos >> 6; + let bit = pos & 0x3f; + if idx < v.len() { + let bit_mask = 1u64 << bit; + // Branchless duplicate check: only set bit and increment count + // if this position is not already set. + let already_set = (v[idx] >> bit) & 1; + let is_new = 1u64.wrapping_sub(already_set); // 1 if new, 0 if dup + v[idx] |= bit_mask; // harmless if already set + count += is_new as usize; + } + } + } +} diff --git a/hqc-kem/src/shake.rs b/hqc-kem/src/shake.rs new file mode 100644 index 0000000..f71610e --- /dev/null +++ b/hqc-kem/src/shake.rs @@ -0,0 +1,95 @@ +/// SHAKE256-based seed expander and domain-separated hash functions. +/// +/// v5.0.0 domain bytes: +/// - 0x00: G function (SHA3-512), KAT PRNG +/// - 0x01: H function (SHA3-256), XOF seed expander +/// - 0x02: I function (SHA3-512, PKE keygen) +/// - 0x03: J function (SHA3-256, rejection key) +use sha3::digest::{ExtendableOutput, Update, XofReader}; +use sha3::{Digest, Sha3_256, Sha3_512, Shake256}; + +/// Domain separation bytes. +pub(crate) const DOMAIN_G: u8 = 0x00; +pub(crate) const DOMAIN_H: u8 = 0x01; +pub(crate) const DOMAIN_I: u8 = 0x02; +pub(crate) const DOMAIN_J: u8 = 0x03; +pub(crate) const DOMAIN_XOF: u8 = 0x01; + +/// SHAKE256-based seed expander with 8-byte aligned reads. +pub(crate) struct SeedExpander { + reader: ::Reader, +} + +impl SeedExpander { + /// Initialize: SHAKE256(seed || domain_byte), then finalize for squeezing. + pub(crate) fn new(seed: &[u8]) -> Self { + let mut hasher = Shake256::default(); + hasher.update(seed); + hasher.update(&[DOMAIN_XOF]); + Self { + reader: hasher.finalize_xof(), + } + } + + /// Read `sz` bytes with 8-byte alignment waste. + /// + /// After reading `sz` bytes, discards `(8 - sz%8) % 8` bytes to maintain + /// 8-byte alignment of the XOF stream. This matches the v5.0.0 `xof_get_bytes`. + pub(crate) fn get_bytes(&mut self, output: &mut [u8]) { + self.reader.read(output); + let remainder = output.len() % 8; + if remainder != 0 { + let mut waste = [0u8; 8]; + self.reader.read(&mut waste[..8 - remainder]); + } + } + + /// Raw squeeze without alignment waste. Used in KEM keygen for seed_pke and sigma. + pub(crate) fn read_raw(&mut self, output: &mut [u8]) { + self.reader.read(output); + } +} + +/// G function: SHA3-512(data || 0x00). Returns 64 bytes. +pub(crate) fn hash_g(data: &[u8]) -> [u8; 64] { + let mut hasher = Sha3_512::default(); + Update::update(&mut hasher, data); + Update::update(&mut hasher, &[DOMAIN_G]); + let result = hasher.finalize(); + let mut out = [0u8; 64]; + out.copy_from_slice(&result); + out +} + +/// H function: SHA3-256(data || 0x01). Returns 32 bytes. +pub(crate) fn hash_h(data: &[u8]) -> [u8; 32] { + let mut hasher = Sha3_256::default(); + Update::update(&mut hasher, data); + Update::update(&mut hasher, &[DOMAIN_H]); + let result = hasher.finalize(); + let mut out = [0u8; 32]; + out.copy_from_slice(&result); + out +} + +/// I function: SHA3-512(data || 0x02). Returns 64 bytes. +pub(crate) fn hash_i(data: &[u8]) -> [u8; 64] { + let mut hasher = Sha3_512::default(); + Update::update(&mut hasher, data); + Update::update(&mut hasher, &[DOMAIN_I]); + let result = hasher.finalize(); + let mut out = [0u8; 64]; + out.copy_from_slice(&result); + out +} + +/// J function: SHA3-256(data || 0x03). Returns 32 bytes. +pub(crate) fn hash_j(data: &[u8]) -> [u8; 32] { + let mut hasher = Sha3_256::default(); + Update::update(&mut hasher, data); + Update::update(&mut hasher, &[DOMAIN_J]); + let result = hasher.finalize(); + let mut out = [0u8; 32]; + out.copy_from_slice(&result); + out +} diff --git a/hqc-kem/src/sizes.rs b/hqc-kem/src/sizes.rs new file mode 100644 index 0000000..7972b6a --- /dev/null +++ b/hqc-kem/src/sizes.rs @@ -0,0 +1,13 @@ +//! Type-level size aliases for HQC key and ciphertext sizes. +//! +//! These are provided by the `hybrid-array` crate's `extra-sizes` feature +//! (with HQC sizes added upstream). + +pub use hybrid_array::sizes::{ + U2241, // HQC-128 public key + U4433, // HQC-128 ciphertext + U4514, // HQC-192 public key + U7237, // HQC-256 public key + U8978, // HQC-192 ciphertext + U14421, // HQC-256 ciphertext +}; diff --git a/hqc-kem/src/types.rs b/hqc-kem/src/types.rs new file mode 100644 index 0000000..017c55b --- /dev/null +++ b/hqc-kem/src/types.rs @@ -0,0 +1,433 @@ +//! Generic HQC-KEM types parameterized by security level. + +use crate::error::Error; +use crate::params::HqcParams; +use core::marker::PhantomData; +use subtle::ConstantTimeEq; +use zeroize::Zeroize; + +/// HQC encapsulation key (public key). +#[derive(Clone)] +pub struct EncapsulationKey { + bytes: Vec, + _marker: PhantomData

, +} + +/// HQC decapsulation key (secret key). +#[derive(Clone)] +pub struct DecapsulationKey { + bytes: Vec, + ek: EncapsulationKey

, + _marker: PhantomData

, +} + +/// HQC ciphertext. +#[derive(Clone)] +pub struct Ciphertext { + bytes: Vec, + _marker: PhantomData

, +} + +/// HQC shared secret. +#[derive(Clone)] +pub struct SharedSecret { + bytes: Vec, + _marker: PhantomData

, +} + +/// HQC Key Encapsulation Mechanism parameterized by security level. +/// +/// Zero-sized marker type providing [`generate_key`](HqcKem::generate_key). +/// Use the type aliases [`Hqc128`](crate::Hqc128), +/// [`Hqc192`](crate::Hqc192), [`Hqc256`](crate::Hqc256). +#[derive(Debug, Clone, Copy)] +pub struct HqcKem(PhantomData

); + +// --------------------------------------------------------------------------- +// Internal constructors (crate-only) +// --------------------------------------------------------------------------- + +impl EncapsulationKey

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::PK_BYTES); + Self { + bytes, + _marker: PhantomData, + } + } +} + +impl DecapsulationKey

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::SK_BYTES); + let ek = EncapsulationKey::from_vec(bytes[..P::PK_BYTES].to_vec()); + Self { + bytes, + ek, + _marker: PhantomData, + } + } + + /// Get the encapsulation (public) key corresponding to this decapsulation key. + pub fn encapsulation_key(&self) -> &EncapsulationKey

{ + &self.ek + } +} + +impl Ciphertext

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::CT_BYTES); + Self { + bytes, + _marker: PhantomData, + } + } +} + +impl SharedSecret

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::SS_BYTES); + Self { + bytes, + _marker: PhantomData, + } + } +} + +// --------------------------------------------------------------------------- +// Debug +// --------------------------------------------------------------------------- + +impl core::fmt::Debug for EncapsulationKey

{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let name: String = format!("{}::EncapsulationKey", P::NAME); + f.debug_struct(&name) + .field("len", &P::PK_BYTES) + .field("bytes", &hex::encode(&self.bytes)) + .finish() + } +} + +impl core::fmt::Debug for DecapsulationKey

{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let name: String = format!("{}::DecapsulationKey", P::NAME); + f.debug_struct(&name).finish() + } +} + +impl core::fmt::Debug for Ciphertext

{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let name: String = format!("{}::Ciphertext", P::NAME); + f.debug_struct(&name) + .field("len", &P::CT_BYTES) + .field("bytes", &hex::encode(&self.bytes)) + .finish() + } +} + +impl core::fmt::Debug for SharedSecret

{ + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + let name: String = format!("{}::SharedSecret", P::NAME); + f.debug_struct(&name).finish() + } +} + +// --------------------------------------------------------------------------- +// AsRef<[u8]> +// --------------------------------------------------------------------------- + +impl AsRef<[u8]> for EncapsulationKey

{ + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl AsRef<[u8]> for DecapsulationKey

{ + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl AsRef<[u8]> for Ciphertext

{ + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +impl AsRef<[u8]> for SharedSecret

{ + fn as_ref(&self) -> &[u8] { + &self.bytes + } +} + +// --------------------------------------------------------------------------- +// TryFrom<&[u8]> +// --------------------------------------------------------------------------- + +impl TryFrom<&[u8]> for EncapsulationKey

{ + type Error = Error; + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != P::PK_BYTES { + return Err(Error::InvalidPublicKeySize { + expected: P::PK_BYTES, + got: bytes.len(), + }); + } + Ok(Self { + bytes: bytes.to_vec(), + _marker: PhantomData, + }) + } +} + +impl TryFrom<&[u8]> for DecapsulationKey

{ + type Error = Error; + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != P::SK_BYTES { + return Err(Error::InvalidSecretKeySize { + expected: P::SK_BYTES, + got: bytes.len(), + }); + } + let ek = EncapsulationKey::from_vec(bytes[..P::PK_BYTES].to_vec()); + Ok(Self { + bytes: bytes.to_vec(), + ek, + _marker: PhantomData, + }) + } +} + +impl TryFrom<&[u8]> for Ciphertext

{ + type Error = Error; + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != P::CT_BYTES { + return Err(Error::InvalidCiphertextSize { + expected: P::CT_BYTES, + got: bytes.len(), + }); + } + Ok(Self { + bytes: bytes.to_vec(), + _marker: PhantomData, + }) + } +} + +// --------------------------------------------------------------------------- +// PartialEq / Eq (EncapsulationKey, Ciphertext) +// --------------------------------------------------------------------------- + +impl PartialEq for EncapsulationKey

{ + fn eq(&self, other: &Self) -> bool { + self.bytes == other.bytes + } +} + +impl Eq for EncapsulationKey

{} + +impl PartialEq for Ciphertext

{ + fn eq(&self, other: &Self) -> bool { + self.bytes == other.bytes + } +} + +impl Eq for Ciphertext

{} + +// --------------------------------------------------------------------------- +// ConstantTimeEq / PartialEq / Eq (SharedSecret only) +// --------------------------------------------------------------------------- + +impl ConstantTimeEq for SharedSecret

{ + fn ct_eq(&self, other: &Self) -> subtle::Choice { + self.bytes.as_slice().ct_eq(other.bytes.as_slice()) + } +} + +impl PartialEq for SharedSecret

{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for SharedSecret

{} + +// --------------------------------------------------------------------------- +// Zeroize + Drop (secret types) +// --------------------------------------------------------------------------- + +impl Zeroize for DecapsulationKey

{ + fn zeroize(&mut self) { + self.bytes.zeroize(); + } +} + +impl Drop for DecapsulationKey

{ + fn drop(&mut self) { + self.zeroize(); + } +} + +impl Zeroize for SharedSecret

{ + fn zeroize(&mut self) { + self.bytes.zeroize(); + } +} + +impl Drop for SharedSecret

{ + fn drop(&mut self) { + self.zeroize(); + } +} + +// --------------------------------------------------------------------------- +// KEM operations (feature-gated) +// --------------------------------------------------------------------------- + +#[cfg(feature = "kgen")] +impl HqcKem

{ + /// Generate an HQC key pair. + pub fn generate_key( + rng: &mut impl rand::CryptoRng, + ) -> (EncapsulationKey

, DecapsulationKey

) { + let (pk, sk) = crate::kem::keygen(P::params(), rng); + ( + EncapsulationKey::from_vec(pk), + DecapsulationKey::from_vec(sk), + ) + } + + /// Generate an HQC key pair deterministically from a 32-byte seed. + /// + /// The seed is expanded via SHAKE256 to derive the PKE key pair and sigma. + /// Identical seeds always produce identical key pairs. + pub fn generate_key_deterministic( + seed: &[u8; 32], + ) -> (EncapsulationKey

, DecapsulationKey

) { + let (pk, sk) = crate::kem::keygen_deterministic(seed, P::params()); + ( + EncapsulationKey::from_vec(pk), + DecapsulationKey::from_vec(sk), + ) + } +} + +#[cfg(feature = "ecap")] +impl EncapsulationKey

{ + /// Encapsulate: produce a ciphertext and shared secret. + pub fn encapsulate(&self, rng: &mut impl rand::CryptoRng) -> (Ciphertext

, SharedSecret

) { + let (ss, ct) = crate::kem::encaps(&self.bytes, P::params(), rng); + (Ciphertext::from_vec(ct), SharedSecret::from_vec(ss)) + } + + /// Encapsulate deterministically from a message and salt. + /// + /// `m` must be exactly the message size for this security level + /// (16 bytes for HQC-128, 24 for HQC-192, 32 for HQC-256). + /// `salt` is always 16 bytes. + /// + /// Identical inputs always produce identical ciphertext and shared secret. + pub fn encapsulate_deterministic( + &self, + m: &[u8], + salt: &[u8; 16], + ) -> Result<(Ciphertext

, SharedSecret

), Error> { + let p = P::params(); + if m.len() != p.k { + return Err(Error::InvalidMessageSize { + expected: p.k, + got: m.len(), + }); + } + let (ss, ct) = crate::kem::encaps_deterministic(&self.bytes, m, salt, p); + Ok((Ciphertext::from_vec(ct), SharedSecret::from_vec(ss))) + } +} + +#[cfg(feature = "dcap")] +impl DecapsulationKey

{ + /// Decapsulate: recover shared secret from ciphertext. + pub fn decapsulate(&self, ct: &Ciphertext

) -> SharedSecret

{ + let ss = crate::kem::decaps(&self.bytes, &ct.bytes, P::params()); + SharedSecret::from_vec(ss) + } +} + +// --------------------------------------------------------------------------- +// Serde (feature-gated) +// --------------------------------------------------------------------------- + +#[cfg(feature = "serde")] +mod serde_impl { + use super::*; + + impl serde::Serialize for EncapsulationKey

{ + fn serialize(&self, s: S) -> Result { + serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) + } + } + + impl<'de, P: HqcParams> serde::Deserialize<'de> for EncapsulationKey

{ + fn deserialize>(d: D) -> Result { + let mut buf = vec![0u8; P::PK_BYTES]; + let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + Ok(Self { + bytes: buf, + _marker: PhantomData, + }) + } + } + + impl serde::Serialize for DecapsulationKey

{ + fn serialize(&self, s: S) -> Result { + serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) + } + } + + impl<'de, P: HqcParams> serde::Deserialize<'de> for DecapsulationKey

{ + fn deserialize>(d: D) -> Result { + let mut buf = vec![0u8; P::SK_BYTES]; + let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + let ek = EncapsulationKey::from_vec(buf[..P::PK_BYTES].to_vec()); + Ok(Self { + bytes: buf, + ek, + _marker: PhantomData, + }) + } + } + + impl serde::Serialize for Ciphertext

{ + fn serialize(&self, s: S) -> Result { + serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) + } + } + + impl<'de, P: HqcParams> serde::Deserialize<'de> for Ciphertext

{ + fn deserialize>(d: D) -> Result { + let mut buf = vec![0u8; P::CT_BYTES]; + let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + Ok(Self { + bytes: buf, + _marker: PhantomData, + }) + } + } + + impl serde::Serialize for SharedSecret

{ + fn serialize(&self, s: S) -> Result { + serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) + } + } + + impl<'de, P: HqcParams> serde::Deserialize<'de> for SharedSecret

{ + fn deserialize>(d: D) -> Result { + let mut buf = vec![0u8; P::SS_BYTES]; + let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + Ok(Self { + bytes: buf, + _marker: PhantomData, + }) + } + } +} diff --git a/hqc-kem/tests/kat.rs b/hqc-kem/tests/kat.rs new file mode 100644 index 0000000..146b2d3 --- /dev/null +++ b/hqc-kem/tests/kat.rs @@ -0,0 +1,174 @@ +//! Known Answer Tests for HQC-KEM. +//! +//! Tests use the KAT PRNG (SHAKE256 with domain byte 0x00) to generate +//! deterministic randomness matching the v5.0.0 reference implementation. + +use hqc_kem::{hqc128, hqc192, hqc256}; + +/// KAT PRNG: wraps the internal SHAKE256-based PRNG. +/// Implements rand TryRng + TryCryptoRng (rand 0.10) for use with the API. +struct KatRng { + reader: sha3::digest::core_api::XofReaderCoreWrapper, +} + +impl KatRng { + fn new(seed: &[u8]) -> Self { + use sha3::digest::{ExtendableOutput, Update}; + let mut hasher = sha3::Shake256::default(); + hasher.update(seed); + hasher.update(&[0x00]); // KAT PRNG domain byte + Self { + reader: hasher.finalize_xof(), + } + } +} + +impl rand::TryRng for KatRng { + type Error = core::convert::Infallible; + + fn try_next_u32(&mut self) -> Result { + let mut buf = [0u8; 4]; + self.try_fill_bytes(&mut buf)?; + Ok(u32::from_le_bytes(buf)) + } + + fn try_next_u64(&mut self) -> Result { + let mut buf = [0u8; 8]; + self.try_fill_bytes(&mut buf)?; + Ok(u64::from_le_bytes(buf)) + } + + fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), Self::Error> { + use sha3::digest::XofReader; + self.reader.read(dest); + Ok(()) + } +} + +impl rand::TryCryptoRng for KatRng {} + +/// Parse a KAT .rsp file and extract the first test vector. +fn parse_kat(content: &str) -> (Vec, Vec, Vec, Vec, Vec) { + let mut seed = Vec::new(); + let mut pk = Vec::new(); + let mut sk = Vec::new(); + let mut ct = Vec::new(); + let mut ss = Vec::new(); + + for line in content.lines() { + let line = line.trim(); + if line.starts_with("seed = ") { + seed = hex::decode(&line[7..]).expect("invalid hex in seed"); + } else if line.starts_with("pk = ") { + pk = hex::decode(&line[5..]).expect("invalid hex in pk"); + } else if line.starts_with("sk = ") { + sk = hex::decode(&line[5..]).expect("invalid hex in sk"); + } else if line.starts_with("ct = ") { + ct = hex::decode(&line[5..]).expect("invalid hex in ct"); + } else if line.starts_with("ss = ") { + ss = hex::decode(&line[5..]).expect("invalid hex in ss"); + } + } + + (seed, pk, sk, ct, ss) +} + +#[test] +fn test_hqc128_kat() { + let content = include_str!("../kat/hqc-1.rsp"); + let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); + + // Initialize KAT PRNG with the seed + let mut rng = KatRng::new(&seed); + + // Key generation + let (ek, dk) = hqc128::generate_key(&mut rng); + + // Check public key + assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-128 public key mismatch"); + + // Check secret key + assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-128 secret key mismatch"); + + // Encapsulation (uses same RNG) + let (ct, ss) = ek.encapsulate(&mut rng); + + // Check ciphertext + assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-128 ciphertext mismatch"); + + // Check shared secret + assert_eq!( + ss.as_ref(), + &expected_ss[..], + "HQC-128 shared secret mismatch" + ); + + // Decapsulation + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-128 decapsulation mismatch"); +} + +#[test] +fn test_hqc192_kat() { + let content = include_str!("../kat/hqc-3.rsp"); + let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); + let mut rng = KatRng::new(&seed); + let (ek, dk) = hqc192::generate_key(&mut rng); + assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-192 public key mismatch"); + assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-192 secret key mismatch"); + let (ct, ss) = ek.encapsulate(&mut rng); + assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-192 ciphertext mismatch"); + assert_eq!( + ss.as_ref(), + &expected_ss[..], + "HQC-192 shared secret mismatch" + ); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-192 decapsulation mismatch"); +} + +#[test] +fn test_hqc256_kat() { + let content = include_str!("../kat/hqc-5.rsp"); + let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); + let mut rng = KatRng::new(&seed); + let (ek, dk) = hqc256::generate_key(&mut rng); + assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-256 public key mismatch"); + assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-256 secret key mismatch"); + let (ct, ss) = ek.encapsulate(&mut rng); + assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-256 ciphertext mismatch"); + assert_eq!( + ss.as_ref(), + &expected_ss[..], + "HQC-256 shared secret mismatch" + ); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-256 decapsulation mismatch"); +} + +#[test] +fn test_hqc128_roundtrip() { + let mut rng = rand::rng(); + let (ek, dk) = hqc128::generate_key(&mut rng); + let (ct, ss1) = ek.encapsulate(&mut rng); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss1, ss2, "HQC-128 roundtrip failed"); +} + +#[test] +fn test_hqc192_roundtrip() { + let mut rng = rand::rng(); + let (ek, dk) = hqc192::generate_key(&mut rng); + let (ct, ss1) = ek.encapsulate(&mut rng); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss1, ss2, "HQC-192 roundtrip failed"); +} + +#[test] +fn test_hqc256_roundtrip() { + let mut rng = rand::rng(); + let (ek, dk) = hqc256::generate_key(&mut rng); + let (ct, ss1) = ek.encapsulate(&mut rng); + let ss2 = dk.decapsulate(&ct); + assert_eq!(ss1, ss2, "HQC-256 roundtrip failed"); +} From e121afeb43572389f26169f3269481e4ce13b6c6 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 09:22:56 -0600 Subject: [PATCH 2/8] fix CI issues Signed-off-by: Mike Lodder --- Cargo.lock | 231 ++++++++++++++++++++++++++++++++++---- Cargo.toml | 1 + hqc-kem/src/fft.rs | 9 +- hqc-kem/src/gf256.rs | 4 +- hqc-kem/src/kem_impl.rs | 43 ++----- hqc-kem/src/pkcs8_impl.rs | 13 +-- hqc-kem/tests/kat.rs | 101 ++++++++--------- 7 files changed, 277 insertions(+), 125 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d1a8519..28d661e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,6 +22,15 @@ 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" @@ -73,6 +82,15 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "block-buffer" version = "0.12.0" @@ -118,13 +136,13 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chacha20" -version = "0.10.0-rc.10" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c536927023d1c432e6e23a25ef45f6756094eac2ab460db5fb17a772acdfd312" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cipher", - "cpufeatures 0.2.17", + "cpufeatures 0.3.0", "rand_core", ] @@ -161,8 +179,8 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e34d8227fe1ba289043aeb13792056ff80fd6de1a9f49137a5f499de8e8c78ea" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.12.0", + "crypto-common 0.2.1", "inout", ] @@ -246,7 +264,7 @@ dependencies = [ "cast", "ciborium", "clap", - "criterion-plot", + "criterion-plot 0.6.0", "itertools", "num-traits", "oorandom", @@ -259,6 +277,31 @@ dependencies = [ "walkdir", ] +[[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 0.8.2", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + [[package]] name = "criterion-plot" version = "0.6.0" @@ -269,6 +312,16 @@ dependencies = [ "itertools", ] +[[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 = "critical-section" version = "1.2.0" @@ -321,6 +374,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-common" version = "0.2.1" @@ -398,15 +461,25 @@ dependencies = [ "zeroize", ] +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + [[package]] name = "digest" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4850db49bf08e663084f7fb5c87d202ef91a3907271aff24a94eb97ff039153c" dependencies = [ - "block-buffer", + "block-buffer 0.12.0", "const-oid", - "crypto-common", + "crypto-common 0.2.1", "ctutils", ] @@ -424,8 +497,8 @@ checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978" dependencies = [ "base16ct", "crypto-bigint", - "crypto-common", - "digest", + "crypto-common 0.2.1", + "digest 0.11.2", "hkdf", "hybrid-array", "rand_core", @@ -478,7 +551,7 @@ version = "0.1.0-pre.3" dependencies = [ "aes", "chacha20", - "criterion", + "criterion 0.7.0", "getrandom", "hex", "hybrid-array", @@ -492,7 +565,7 @@ dependencies = [ "serde_json", "serde_yaml", "serdect", - "sha3", + "sha3 0.11.0-rc.9", "subtle", "thiserror", "toml", @@ -542,6 +615,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.4.1" @@ -653,14 +736,32 @@ version = "0.13.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60017b071c523c9e5a55dd1253582bff6150c5e96a7e8511e419de1ab5ee97f9" dependencies = [ - "digest", + "digest 0.11.2", +] + +[[package]] +name = "hqc-kem" +version = "0.1.0" +dependencies = [ + "const-oid", + "criterion 0.8.2", + "hex", + "hybrid-array", + "kem", + "pkcs8", + "rand", + "serde", + "serdect", + "sha3 0.10.8", + "subtle", + "thiserror", + "typenum", + "zeroize", ] [[package]] name = "hybrid-array" -version = "0.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8655f91cd07f2b9d0c24137bd650fe69617773435ee5ec83022377777ce65ef1" +version = "0.4.9" dependencies = [ "ctutils", "subtle", @@ -730,6 +831,15 @@ dependencies = [ "elliptic-curve", ] +[[package]] +name = "keccak" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb26cec98cce3a3d96cbb7bced3c4b16e3d13f27ec56dbd62cbc8f39cfb9d653" +dependencies = [ + "cpufeatures 0.2.17", +] + [[package]] name = "keccak" version = "0.2.0" @@ -746,7 +856,7 @@ version = "0.3.0-rc.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3ae2c3347ff4a7af4f679a9e397c2c7e6034a00b773dd2dd3c001d7f40897c9" dependencies = [ - "crypto-common", + "crypto-common 0.2.1", "rand_core", ] @@ -788,7 +898,7 @@ name = "ml-kem" version = "0.3.0-rc.1" dependencies = [ "const-oid", - "criterion", + "criterion 0.7.0", "getrandom", "hex", "hex-literal", @@ -800,7 +910,7 @@ dependencies = [ "rand_core", "serde", "serde_json", - "sha3", + "sha3 0.11.0-rc.9", "zeroize", ] @@ -913,6 +1023,16 @@ dependencies = [ "primeorder", ] +[[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 = "pem-rfc7468" version = "1.0.0" @@ -1008,7 +1128,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93401c13cc7ff24684571cfca9d3cf9ebabfaf3d4b7b9963ade41ec54da196b5" dependencies = [ "crypto-bigint", - "crypto-common", + "crypto-common 0.2.1", "rand_core", "rustcrypto-ff", "subtle", @@ -1057,6 +1177,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +dependencies = [ + "chacha20", + "getrandom", + "rand_core", +] + [[package]] name = "rand_core" version = "0.10.0" @@ -1326,7 +1457,17 @@ checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "digest", + "digest 0.11.2", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest 0.10.7", + "keccak 0.1.6", ] [[package]] @@ -1335,8 +1476,8 @@ version = "0.11.0-rc.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b233a7d59d7bfc027208506a33ffc9532b2acb24ddc61fe7e758dc2250db431" dependencies = [ - "digest", - "keccak", + "digest 0.11.2", + "keccak 0.2.0", ] [[package]] @@ -1504,6 +1645,12 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "walkdir" version = "2.5.0" @@ -1621,6 +1768,22 @@ dependencies = [ "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" @@ -1630,6 +1793,12 @@ 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" @@ -1759,7 +1928,7 @@ dependencies = [ "rand_core", "serde", "serde_json", - "sha3", + "sha3 0.11.0-rc.9", "x25519-dalek", "zeroize", ] @@ -1800,6 +1969,20 @@ name = "zeroize" version = "1.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] [[package]] name = "zmij" diff --git a/Cargo.toml b/Cargo.toml index bc13478..2d77965 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,3 +59,4 @@ debug = true [patch.crates-io] ml-kem = { path = "./ml-kem" } module-lattice = { path = "./module-lattice" } +hybrid-array = { path = "./hqc-kem/hybrid-array-patch" } diff --git a/hqc-kem/src/fft.rs b/hqc-kem/src/fft.rs index d77899b..69e8d3e 100644 --- a/hqc-kem/src/fft.rs +++ b/hqc-kem/src/fft.rs @@ -149,7 +149,14 @@ fn fft_rec(w: &mut [u16], f: &mut [u16], f_coeffs: usize, m: usize, m_f: usize, // Step 5: recurse let k = 1usize << (m - 1); let mut u = vec![0u16; k]; - fft_rec(&mut u, &mut f0, f_coeffs.div_ceil(2), m - 1, m_f - 1, &deltas); + fft_rec( + &mut u, + &mut f0, + f_coeffs.div_ceil(2), + m - 1, + m_f - 1, + &deltas, + ); if f_coeffs <= 3 { // f1 is constant diff --git a/hqc-kem/src/gf256.rs b/hqc-kem/src/gf256.rs index c3e7336..df693b6 100644 --- a/hqc-kem/src/gf256.rs +++ b/hqc-kem/src/gf256.rs @@ -2,7 +2,7 @@ //! //! Generator (alpha) = 2. -/// Powers of alpha: gf_exp[i] = alpha^i. Extended to 258 entries to avoid +/// Powers of alpha: `gf_exp[i] = alpha^i`. Extended to 258 entries to avoid /// bounds checking in multiplication. pub(crate) static GF_EXP: [u16; 258] = [ 1, 2, 4, 8, 16, 32, 64, 128, 29, 58, 116, 232, 205, 135, 19, 38, 76, 152, 45, 90, 180, 117, @@ -20,7 +20,7 @@ pub(crate) static GF_EXP: [u16; 258] = [ 131, 27, 54, 108, 216, 173, 71, 142, 1, 2, 4, ]; -/// Logarithm table: gf_log[v] = i where alpha^i = v. gf_log[0] = 0 by convention. +/// Logarithm table: `gf_log[v] = i` where `alpha^i = v`. `gf_log[0] = 0` by convention. pub(crate) static GF_LOG: [u16; 256] = [ 0, 0, 1, 25, 2, 50, 26, 198, 3, 223, 51, 238, 27, 104, 199, 75, 4, 100, 224, 14, 52, 141, 239, 129, 28, 193, 105, 248, 200, 8, 76, 113, 5, 138, 101, 47, 225, 36, 15, 33, 53, 147, 142, 218, diff --git a/hqc-kem/src/kem_impl.rs b/hqc-kem/src/kem_impl.rs index 7c4313b..a24c9d7 100644 --- a/hqc-kem/src/kem_impl.rs +++ b/hqc-kem/src/kem_impl.rs @@ -5,7 +5,6 @@ use crate::sizes; use crate::types::{DecapsulationKey, EncapsulationKey}; use hybrid_array::Array; - macro_rules! impl_hqc_kem { ($params:ty, $pk_size:ty, $ct_size:ty) => { // -- Kem on the parameter marker type -- @@ -35,9 +34,7 @@ macro_rules! impl_hqc_kem { // -- EncapsulationKey: TryKeyInit -- impl kem_traits::common::TryKeyInit for EncapsulationKey<$params> { - fn new( - key: &kem_traits::Key, - ) -> Result { + fn new(key: &kem_traits::Key) -> Result { let bytes = key.as_slice(); if bytes.len() != <$params>::PK_BYTES { return Err(kem_traits::InvalidKey); @@ -69,8 +66,7 @@ macro_rules! impl_hqc_kem { where R: rand::CryptoRng + ?Sized, { - let (ss_vec, ct_vec) = - crate::kem::encaps(self.as_ref(), <$params>::params(), rng); + let (ss_vec, ct_vec) = crate::kem::encaps(self.as_ref(), <$params>::params(), rng); let mut ct = Array::::default(); ct.as_mut_slice().copy_from_slice(&ct_vec); @@ -94,8 +90,7 @@ macro_rules! impl_hqc_kem { .as_slice() .try_into() .expect("seed is exactly 32 bytes"); - let (pk, sk) = - crate::kem::keygen_deterministic(&seed_arr, <$params>::params()); + let (pk, sk) = crate::kem::keygen_deterministic(&seed_arr, <$params>::params()); let _ = pk; // pk is embedded in sk DecapsulationKey::<$params>::from_vec(sk) } @@ -107,17 +102,14 @@ macro_rules! impl_hqc_kem { let sk = self.as_ref(); let seed_start = sk.len() - crate::params::SEED_BYTES; let mut arr = Array::::default(); - arr.as_mut_slice() - .copy_from_slice(&sk[seed_start..]); + arr.as_mut_slice().copy_from_slice(&sk[seed_start..]); arr } } // -- DecapsulationKey: Generate -- impl kem_traits::common::Generate for DecapsulationKey<$params> { - fn try_generate_from_rng( - rng: &mut R, - ) -> Result::Error> + fn try_generate_from_rng(rng: &mut R) -> Result::Error> where R: rand::TryCryptoRng + ?Sized, { @@ -132,9 +124,7 @@ macro_rules! impl_hqc_kem { impl kem_traits::Decapsulator for DecapsulationKey<$params> { type Kem = $params; - fn encapsulation_key( - &self, - ) -> &kem_traits::EncapsulationKey { + fn encapsulation_key(&self) -> &kem_traits::EncapsulationKey { self.encapsulation_key() } } @@ -145,8 +135,7 @@ macro_rules! impl_hqc_kem { &self, ct: &kem_traits::Ciphertext, ) -> kem_traits::SharedKey { - let ss_vec = - crate::kem::decaps(self.as_ref(), ct.as_slice(), <$params>::params()); + let ss_vec = crate::kem::decaps(self.as_ref(), ct.as_slice(), <$params>::params()); let mut ss = Array::::default(); ss.as_mut_slice().copy_from_slice(&ss_vec); ss @@ -155,21 +144,9 @@ macro_rules! impl_hqc_kem { }; } -impl_hqc_kem!( - crate::params::Hqc128Params, - sizes::U2241, - sizes::U4433 -); -impl_hqc_kem!( - crate::params::Hqc192Params, - sizes::U4514, - sizes::U8978 -); -impl_hqc_kem!( - crate::params::Hqc256Params, - sizes::U7237, - sizes::U14421 -); +impl_hqc_kem!(crate::params::Hqc128Params, sizes::U2241, sizes::U4433); +impl_hqc_kem!(crate::params::Hqc192Params, sizes::U4514, sizes::U8978); +impl_hqc_kem!(crate::params::Hqc256Params, sizes::U7237, sizes::U14421); #[cfg(test)] mod tests { diff --git a/hqc-kem/src/pkcs8_impl.rs b/hqc-kem/src/pkcs8_impl.rs index e901738..304c806 100644 --- a/hqc-kem/src/pkcs8_impl.rs +++ b/hqc-kem/src/pkcs8_impl.rs @@ -1,7 +1,7 @@ //! PKCS#8 encoding support for HQC-KEM keys. //! -//! When the `pkcs8` feature is enabled, [`DecodePrivateKey`] is impl'd for -//! [`DecapsulationKey`], and [`DecodePublicKey`] is impl'd for [`EncapsulationKey`]. +//! When the `pkcs8` feature is enabled, [`pkcs8::DecodePrivateKey`] is impl'd for +//! [`DecapsulationKey`], and [`pkcs8::DecodePublicKey`] is impl'd for [`EncapsulationKey`]. //! //! When both `pkcs8` and `alloc` features are enabled, [`EncodePrivateKey`] is impl'd //! for [`DecapsulationKey`], and [`EncodePublicKey`] is impl'd for [`EncapsulationKey`]. @@ -10,7 +10,7 @@ pub use ::pkcs8::spki::AssociatedAlgorithmIdentifier; pub use const_oid::AssociatedOid; use crate::{ - params::{HqcParams, Hqc128Params, Hqc192Params, Hqc256Params, SEED_BYTES}, + params::{Hqc128Params, Hqc192Params, Hqc256Params, HqcParams, SEED_BYTES}, types::{DecapsulationKey, EncapsulationKey}, }; use ::pkcs8::{ @@ -175,8 +175,7 @@ where .to_der()?; let private_key = OctetStringRef::new(&seed_der)?; - let private_key_info = - pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, private_key); + let private_key_info = pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, private_key); pkcs8::SecretDocument::encode_msg(&private_key_info).map_err(pkcs8::Error::Asn1) } } @@ -191,9 +190,7 @@ where { type Error = ::pkcs8::Error; - fn try_from( - private_key_info_ref: ::pkcs8::PrivateKeyInfoRef<'_>, - ) -> Result { + fn try_from(private_key_info_ref: ::pkcs8::PrivateKeyInfoRef<'_>) -> Result { let _ = private_key_info_ref .algorithm .assert_algorithm_oid(P::ALGORITHM_IDENTIFIER.oid)?; diff --git a/hqc-kem/tests/kat.rs b/hqc-kem/tests/kat.rs index 146b2d3..748d786 100644 --- a/hqc-kem/tests/kat.rs +++ b/hqc-kem/tests/kat.rs @@ -47,8 +47,16 @@ impl rand::TryRng for KatRng { impl rand::TryCryptoRng for KatRng {} +struct KatVector { + seed: Vec, + pk: Vec, + sk: Vec, + ct: Vec, + ss: Vec, +} + /// Parse a KAT .rsp file and extract the first test vector. -fn parse_kat(content: &str) -> (Vec, Vec, Vec, Vec, Vec) { +fn parse_kat(content: &str) -> KatVector { let mut seed = Vec::new(); let mut pk = Vec::new(); let mut sk = Vec::new(); @@ -57,53 +65,40 @@ fn parse_kat(content: &str) -> (Vec, Vec, Vec, Vec, Vec) { for line in content.lines() { let line = line.trim(); - if line.starts_with("seed = ") { - seed = hex::decode(&line[7..]).expect("invalid hex in seed"); - } else if line.starts_with("pk = ") { - pk = hex::decode(&line[5..]).expect("invalid hex in pk"); - } else if line.starts_with("sk = ") { - sk = hex::decode(&line[5..]).expect("invalid hex in sk"); - } else if line.starts_with("ct = ") { - ct = hex::decode(&line[5..]).expect("invalid hex in ct"); - } else if line.starts_with("ss = ") { - ss = hex::decode(&line[5..]).expect("invalid hex in ss"); + if let Some(val) = line.strip_prefix("seed = ") { + seed = hex::decode(val).expect("invalid hex in seed"); + } else if let Some(val) = line.strip_prefix("pk = ") { + pk = hex::decode(val).expect("invalid hex in pk"); + } else if let Some(val) = line.strip_prefix("sk = ") { + sk = hex::decode(val).expect("invalid hex in sk"); + } else if let Some(val) = line.strip_prefix("ct = ") { + ct = hex::decode(val).expect("invalid hex in ct"); + } else if let Some(val) = line.strip_prefix("ss = ") { + ss = hex::decode(val).expect("invalid hex in ss"); } } - (seed, pk, sk, ct, ss) + KatVector { + seed, + pk, + sk, + ct, + ss, + } } #[test] fn test_hqc128_kat() { let content = include_str!("../kat/hqc-1.rsp"); - let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); + let kat = parse_kat(content); - // Initialize KAT PRNG with the seed - let mut rng = KatRng::new(&seed); - - // Key generation + let mut rng = KatRng::new(&kat.seed); let (ek, dk) = hqc128::generate_key(&mut rng); - - // Check public key - assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-128 public key mismatch"); - - // Check secret key - assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-128 secret key mismatch"); - - // Encapsulation (uses same RNG) + assert_eq!(ek.as_ref(), &kat.pk[..], "HQC-128 public key mismatch"); + assert_eq!(dk.as_ref(), &kat.sk[..], "HQC-128 secret key mismatch"); let (ct, ss) = ek.encapsulate(&mut rng); - - // Check ciphertext - assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-128 ciphertext mismatch"); - - // Check shared secret - assert_eq!( - ss.as_ref(), - &expected_ss[..], - "HQC-128 shared secret mismatch" - ); - - // Decapsulation + assert_eq!(ct.as_ref(), &kat.ct[..], "HQC-128 ciphertext mismatch"); + assert_eq!(ss.as_ref(), &kat.ss[..], "HQC-128 shared secret mismatch"); let ss2 = dk.decapsulate(&ct); assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-128 decapsulation mismatch"); } @@ -111,18 +106,14 @@ fn test_hqc128_kat() { #[test] fn test_hqc192_kat() { let content = include_str!("../kat/hqc-3.rsp"); - let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); - let mut rng = KatRng::new(&seed); + let kat = parse_kat(content); + let mut rng = KatRng::new(&kat.seed); let (ek, dk) = hqc192::generate_key(&mut rng); - assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-192 public key mismatch"); - assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-192 secret key mismatch"); + assert_eq!(ek.as_ref(), &kat.pk[..], "HQC-192 public key mismatch"); + assert_eq!(dk.as_ref(), &kat.sk[..], "HQC-192 secret key mismatch"); let (ct, ss) = ek.encapsulate(&mut rng); - assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-192 ciphertext mismatch"); - assert_eq!( - ss.as_ref(), - &expected_ss[..], - "HQC-192 shared secret mismatch" - ); + assert_eq!(ct.as_ref(), &kat.ct[..], "HQC-192 ciphertext mismatch"); + assert_eq!(ss.as_ref(), &kat.ss[..], "HQC-192 shared secret mismatch"); let ss2 = dk.decapsulate(&ct); assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-192 decapsulation mismatch"); } @@ -130,18 +121,14 @@ fn test_hqc192_kat() { #[test] fn test_hqc256_kat() { let content = include_str!("../kat/hqc-5.rsp"); - let (seed, expected_pk, expected_sk, expected_ct, expected_ss) = parse_kat(content); - let mut rng = KatRng::new(&seed); + let kat = parse_kat(content); + let mut rng = KatRng::new(&kat.seed); let (ek, dk) = hqc256::generate_key(&mut rng); - assert_eq!(ek.as_ref(), &expected_pk[..], "HQC-256 public key mismatch"); - assert_eq!(dk.as_ref(), &expected_sk[..], "HQC-256 secret key mismatch"); + assert_eq!(ek.as_ref(), &kat.pk[..], "HQC-256 public key mismatch"); + assert_eq!(dk.as_ref(), &kat.sk[..], "HQC-256 secret key mismatch"); let (ct, ss) = ek.encapsulate(&mut rng); - assert_eq!(ct.as_ref(), &expected_ct[..], "HQC-256 ciphertext mismatch"); - assert_eq!( - ss.as_ref(), - &expected_ss[..], - "HQC-256 shared secret mismatch" - ); + assert_eq!(ct.as_ref(), &kat.ct[..], "HQC-256 ciphertext mismatch"); + assert_eq!(ss.as_ref(), &kat.ss[..], "HQC-256 shared secret mismatch"); let ss2 = dk.decapsulate(&ct); assert_eq!(ss.as_ref(), ss2.as_ref(), "HQC-256 decapsulation mismatch"); } From 5c81bf3be8245164277fe7434cceb18dbf0b8220 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 09:30:56 -0600 Subject: [PATCH 3/8] fix clippy Signed-off-by: Mike Lodder --- hqc-kem/src/poly.rs | 163 ++++++++++++++++++++++++-------------------- 1 file changed, 90 insertions(+), 73 deletions(-) diff --git a/hqc-kem/src/poly.rs b/hqc-kem/src/poly.rs index b802f35..dcb50b1 100644 --- a/hqc-kem/src/poly.rs +++ b/hqc-kem/src/poly.rs @@ -61,12 +61,14 @@ fn base_mul(a: u64, b: u64) -> [u64; 2] { #[inline] unsafe fn base_mul_pclmul(a: u64, b: u64) -> [u64; 2] { use std::arch::x86_64::*; - let va = _mm_set_epi64x(0, a as i64); - let vb = _mm_set_epi64x(0, b as i64); - let r = _mm_clmulepi64_si128(va, vb, 0x00); - let mut result = [0u64; 2]; - _mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, r); - result + unsafe { + let va = _mm_set_epi64x(0, a as i64); + let vb = _mm_set_epi64x(0, b as i64); + let r = _mm_clmulepi64_si128(va, vb, 0x00); + let mut result = [0u64; 2]; + _mm_storeu_si128(result.as_mut_ptr() as *mut __m128i, r); + result + } } /// Software carry-less multiplication (constant-time). @@ -314,14 +316,16 @@ pub(crate) fn store8_arr(out: &mut [u8], inp: &[u64]) { unsafe fn vect_add_avx2(o: &mut [u64], v1: &[u64], v2: &[u64], size: usize) { use std::arch::x86_64::*; let chunks = size / 4; - let p1 = v1.as_ptr() as *const __m256i; - let p2 = v2.as_ptr() as *const __m256i; - let po = o.as_mut_ptr() as *mut __m256i; - for i in 0..chunks { - _mm256_storeu_si256( - po.add(i), - _mm256_xor_si256(_mm256_loadu_si256(p1.add(i)), _mm256_loadu_si256(p2.add(i))), - ); + unsafe { + let p1 = v1.as_ptr() as *const __m256i; + let p2 = v2.as_ptr() as *const __m256i; + let po = o.as_mut_ptr() as *mut __m256i; + for i in 0..chunks { + _mm256_storeu_si256( + po.add(i), + _mm256_xor_si256(_mm256_loadu_si256(p1.add(i)), _mm256_loadu_si256(p2.add(i))), + ); + } } for i in (chunks * 4)..size { o[i] = v1[i] ^ v2[i]; @@ -333,16 +337,18 @@ unsafe fn vect_add_avx2(o: &mut [u64], v1: &[u64], v2: &[u64], size: usize) { unsafe fn vect_add_assign_avx2(v: &mut [u64], rhs: &[u64], size: usize) { use std::arch::x86_64::*; let chunks = size / 4; - let pv = v.as_mut_ptr() as *mut __m256i; - let pr = rhs.as_ptr() as *const __m256i; - for i in 0..chunks { - _mm256_storeu_si256( - pv.add(i), - _mm256_xor_si256( - _mm256_loadu_si256(pv.add(i) as *const __m256i), - _mm256_loadu_si256(pr.add(i)), - ), - ); + unsafe { + let pv = v.as_mut_ptr() as *mut __m256i; + let pr = rhs.as_ptr() as *const __m256i; + for i in 0..chunks { + _mm256_storeu_si256( + pv.add(i), + _mm256_xor_si256( + _mm256_loadu_si256(pv.add(i) as *const __m256i), + _mm256_loadu_si256(pr.add(i)), + ), + ); + } } for i in (chunks * 4)..size { v[i] ^= rhs[i]; @@ -363,18 +369,20 @@ unsafe fn karatsuba_add1_avx2( let chunks = size_h / 4; for i in 0..chunks { let idx = i * 4; - let a_lo = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); - let a_hi = _mm256_loadu_si256(a.as_ptr().add(idx + size_l) as *const __m256i); - _mm256_storeu_si256( - alh.as_mut_ptr().add(idx) as *mut __m256i, - _mm256_xor_si256(a_lo, a_hi), - ); - let b_lo = _mm256_loadu_si256(b.as_ptr().add(idx) as *const __m256i); - let b_hi = _mm256_loadu_si256(b.as_ptr().add(idx + size_l) as *const __m256i); - _mm256_storeu_si256( - blh.as_mut_ptr().add(idx) as *mut __m256i, - _mm256_xor_si256(b_lo, b_hi), - ); + unsafe { + let a_lo = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); + let a_hi = _mm256_loadu_si256(a.as_ptr().add(idx + size_l) as *const __m256i); + _mm256_storeu_si256( + alh.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(a_lo, a_hi), + ); + let b_lo = _mm256_loadu_si256(b.as_ptr().add(idx) as *const __m256i); + let b_hi = _mm256_loadu_si256(b.as_ptr().add(idx + size_l) as *const __m256i); + _mm256_storeu_si256( + blh.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(b_lo, b_hi), + ); + } } for i in (chunks * 4)..size_h { alh[i] = a[i] ^ a[i + size_l]; @@ -404,14 +412,16 @@ unsafe fn karatsuba_add2_avx2( let chunks1 = len1 / 4; for i in 0..chunks1 { let idx = i * 4; - let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; - _mm256_storeu_si256( - pt, - _mm256_xor_si256( - _mm256_loadu_si256(pt as *const __m256i), - _mm256_loadu_si256(o.as_ptr().add(idx) as *const __m256i), - ), - ); + unsafe { + let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; + _mm256_storeu_si256( + pt, + _mm256_xor_si256( + _mm256_loadu_si256(pt as *const __m256i), + _mm256_loadu_si256(o.as_ptr().add(idx) as *const __m256i), + ), + ); + } } for i in (chunks1 * 4)..len1 { tmp1[i] ^= o[i]; @@ -421,14 +431,16 @@ unsafe fn karatsuba_add2_avx2( let chunks2 = len2 / 4; for i in 0..chunks2 { let idx = i * 4; - let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; - _mm256_storeu_si256( - pt, - _mm256_xor_si256( - _mm256_loadu_si256(pt as *const __m256i), - _mm256_loadu_si256(tmp2.as_ptr().add(idx) as *const __m256i), - ), - ); + unsafe { + let pt = tmp1.as_mut_ptr().add(idx) as *mut __m256i; + _mm256_storeu_si256( + pt, + _mm256_xor_si256( + _mm256_loadu_si256(pt as *const __m256i), + _mm256_loadu_si256(tmp2.as_ptr().add(idx) as *const __m256i), + ), + ); + } } for i in (chunks2 * 4)..len2 { tmp1[i] ^= tmp2[i]; @@ -437,14 +449,16 @@ unsafe fn karatsuba_add2_avx2( // o[i + size_l] ^= tmp1[i] for i in 0..2*size_l for i in 0..chunks1 { let idx = i * 4; - let po = o.as_mut_ptr().add(idx + size_l) as *mut __m256i; - _mm256_storeu_si256( - po, - _mm256_xor_si256( - _mm256_loadu_si256(po as *const __m256i), - _mm256_loadu_si256(tmp1.as_ptr().add(idx) as *const __m256i), - ), - ); + unsafe { + let po = o.as_mut_ptr().add(idx + size_l) as *mut __m256i; + _mm256_storeu_si256( + po, + _mm256_xor_si256( + _mm256_loadu_si256(po as *const __m256i), + _mm256_loadu_si256(tmp1.as_ptr().add(idx) as *const __m256i), + ), + ); + } } for i in (chunks1 * 4)..len1 { o[i + size_l] ^= tmp1[i]; @@ -457,21 +471,24 @@ unsafe fn reduce_avx2(o: &mut [u64], a: &[u64], n: usize, vec_n: usize) { use std::arch::x86_64::*; let shift = (n & 0x3f) as i64; let inv_shift = 64 - shift; - let shift_v = _mm_set_epi64x(0, shift); - let inv_shift_v = _mm_set_epi64x(0, inv_shift); let chunks = vec_n / 4; - for i in 0..chunks { - let idx = i * 4; - let base = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); - let hi_prev = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n - 1) as *const __m256i); - let hi_next = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n) as *const __m256i); - let r = _mm256_srl_epi64(hi_prev, shift_v); - let carry = _mm256_sll_epi64(hi_next, inv_shift_v); - _mm256_storeu_si256( - o.as_mut_ptr().add(idx) as *mut __m256i, - _mm256_xor_si256(base, _mm256_xor_si256(r, carry)), - ); + unsafe { + let shift_v = _mm_set_epi64x(0, shift); + let inv_shift_v = _mm_set_epi64x(0, inv_shift); + + for i in 0..chunks { + let idx = i * 4; + let base = _mm256_loadu_si256(a.as_ptr().add(idx) as *const __m256i); + let hi_prev = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n - 1) as *const __m256i); + let hi_next = _mm256_loadu_si256(a.as_ptr().add(idx + vec_n) as *const __m256i); + let r = _mm256_srl_epi64(hi_prev, shift_v); + let carry = _mm256_sll_epi64(hi_next, inv_shift_v); + _mm256_storeu_si256( + o.as_mut_ptr().add(idx) as *mut __m256i, + _mm256_xor_si256(base, _mm256_xor_si256(r, carry)), + ); + } } // Scalar remainder let shift_s = n & 0x3f; From 17bf6f7a77dfb884a68455592ff304ec3cc4de31 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 09:33:20 -0600 Subject: [PATCH 4/8] add tests for hqc-kem Signed-off-by: Mike Lodder --- .github/workflows/hqc-kem.yml | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 .github/workflows/hqc-kem.yml diff --git a/.github/workflows/hqc-kem.yml b/.github/workflows/hqc-kem.yml new file mode 100644 index 0000000..9a19955 --- /dev/null +++ b/.github/workflows/hqc-kem.yml @@ -0,0 +1,78 @@ +name: hqc-kem + +on: + pull_request: + paths: + - ".github/workflows/hqc-kem.yml" + - "hqc-kem/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: hqc-kem + +env: + RUSTFLAGS: "-Dwarnings" + CARGO_INCREMENTAL: 0 + +# Cancels CI jobs when new commits are pushed to a PR branch +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + set-msrv: + uses: RustCrypto/actions/.github/workflows/set-msrv.yml@master + with: + msrv: 1.85.0 + + minimal-versions: + if: false + uses: RustCrypto/actions/.github/workflows/minimal-versions.yml@master + with: + working-directory: ${{ github.workflow }} + + test: + needs: set-msrv + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - ${{needs.set-msrv.outputs.msrv}} + - stable + steps: + - uses: actions/checkout@v6.0.2 + with: + submodules: recursive + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + - run: cargo build --benches + - run: cargo build --benches --all-features + - run: cargo test --no-default-features + - run: cargo test + - run: cargo test --all-features + + cross: + needs: set-msrv + strategy: + matrix: + include: + - target: powerpc-unknown-linux-gnu + rust: ${{needs.set-msrv.outputs.msrv}} + - target: powerpc-unknown-linux-gnu + rust: stable + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6.0.2 + with: + submodules: recursive + - uses: dtolnay/rust-toolchain@master + with: + toolchain: ${{ matrix.rust }} + targets: ${{ matrix.target }} + - uses: RustCrypto/actions/cross-install@master + - run: cross test --release --target ${{ matrix.target }} --all-features From 640982a05183e02755e4859a7621d7ef28c43151 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 09:43:10 -0600 Subject: [PATCH 5/8] more CI fixes Signed-off-by: Mike Lodder --- Cargo.lock | 84 ++---------------------------------------- hqc-kem/Cargo.toml | 2 +- hqc-kem/benches/kem.rs | 1 + hqc-kem/src/lib.rs | 3 ++ hqc-kem/tests/kat.rs | 1 + 5 files changed, 10 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 28d661e..39aff77 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,15 +22,6 @@ 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" @@ -264,35 +255,10 @@ dependencies = [ "cast", "ciborium", "clap", - "criterion-plot 0.6.0", - "itertools", - "num-traits", - "oorandom", - "plotters", - "rayon", - "regex", - "serde", - "serde_json", - "tinytemplate", - "walkdir", -] - -[[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 0.8.2", + "criterion-plot", "itertools", "num-traits", "oorandom", - "page_size", "plotters", "rayon", "regex", @@ -312,16 +278,6 @@ dependencies = [ "itertools", ] -[[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 = "critical-section" version = "1.2.0" @@ -551,7 +507,7 @@ version = "0.1.0-pre.3" dependencies = [ "aes", "chacha20", - "criterion 0.7.0", + "criterion", "getrandom", "hex", "hybrid-array", @@ -744,7 +700,7 @@ name = "hqc-kem" version = "0.1.0" dependencies = [ "const-oid", - "criterion 0.8.2", + "criterion", "hex", "hybrid-array", "kem", @@ -898,7 +854,7 @@ name = "ml-kem" version = "0.3.0-rc.1" dependencies = [ "const-oid", - "criterion 0.7.0", + "criterion", "getrandom", "hex", "hex-literal", @@ -1023,16 +979,6 @@ dependencies = [ "primeorder", ] -[[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 = "pem-rfc7468" version = "1.0.0" @@ -1768,22 +1714,6 @@ dependencies = [ "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" @@ -1793,12 +1723,6 @@ 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" diff --git a/hqc-kem/Cargo.toml b/hqc-kem/Cargo.toml index 3fa2614..9588e2f 100644 --- a/hqc-kem/Cargo.toml +++ b/hqc-kem/Cargo.toml @@ -37,7 +37,7 @@ typenum = { version = "1", optional = true } zeroize = { version = "1", features = ["derive"] } [dev-dependencies] -criterion = { version = "0.8", features = ["html_reports"] } +criterion = "0.7" hex = "0.4" rand = { version = "0.10", features = ["thread_rng"] } diff --git a/hqc-kem/benches/kem.rs b/hqc-kem/benches/kem.rs index 40311e0..21ac9a0 100644 --- a/hqc-kem/benches/kem.rs +++ b/hqc-kem/benches/kem.rs @@ -1,4 +1,5 @@ #![allow(missing_docs, unused_results)] +#![cfg(all(feature = "kgen", feature = "ecap", feature = "dcap"))] use criterion::{Criterion, criterion_group, criterion_main}; use hqc_kem::{hqc128, hqc192, hqc256}; diff --git a/hqc-kem/src/lib.rs b/hqc-kem/src/lib.rs index 1687826..73dd6d1 100644 --- a/hqc-kem/src/lib.rs +++ b/hqc-kem/src/lib.rs @@ -7,6 +7,8 @@ //! # Usage //! //! ```rust +//! # #[cfg(all(feature = "kgen", feature = "ecap", feature = "dcap"))] +//! # { //! use hqc_kem::{Hqc128, HqcKem}; //! //! let mut rng = rand::rng(); @@ -14,6 +16,7 @@ //! let (ct, ss1) = ek.encapsulate(&mut rng); //! let ss2 = dk.decapsulate(&ct); //! assert_eq!(ss1, ss2); +//! # } //! ``` //! //! # Security Levels diff --git a/hqc-kem/tests/kat.rs b/hqc-kem/tests/kat.rs index 748d786..a9332f4 100644 --- a/hqc-kem/tests/kat.rs +++ b/hqc-kem/tests/kat.rs @@ -2,6 +2,7 @@ //! //! Tests use the KAT PRNG (SHAKE256 with domain byte 0x00) to generate //! deterministic randomness matching the v5.0.0 reference implementation. +#![cfg(all(feature = "kgen", feature = "ecap", feature = "dcap"))] use hqc_kem::{hqc128, hqc192, hqc256}; From c50948cb5ae11996fa41aaa5e68ce3c92b91c4d1 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 10:15:34 -0600 Subject: [PATCH 6/8] simplify Signed-off-by: Mike Lodder --- hqc-kem/src/error.rs | 8 + hqc-kem/src/types.rs | 338 ++++++++++++++++++++----------------------- 2 files changed, 167 insertions(+), 179 deletions(-) diff --git a/hqc-kem/src/error.rs b/hqc-kem/src/error.rs index ae363c7..4668b07 100644 --- a/hqc-kem/src/error.rs +++ b/hqc-kem/src/error.rs @@ -27,6 +27,14 @@ pub enum Error { /// Actual size. got: usize, }, + /// Invalid shared secret size. + #[error("invalid shared secret size: expected {expected}, got {got}")] + InvalidSharedSecretSize { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, /// Invalid message size for deterministic encapsulation. #[error("invalid message size: expected {expected}, got {got}")] InvalidMessageSize { diff --git a/hqc-kem/src/types.rs b/hqc-kem/src/types.rs index 017c55b..cbdd267 100644 --- a/hqc-kem/src/types.rs +++ b/hqc-kem/src/types.rs @@ -6,6 +6,63 @@ use core::marker::PhantomData; use subtle::ConstantTimeEq; use zeroize::Zeroize; +macro_rules! from_bytes { + ($name:ident, $bytes:expr, $err:ident) => { + impl TryFrom<&[u8]> for $name

{ + type Error = Error; + + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() == $bytes { + return Err(Error::$err { + expected: $bytes, + got: bytes.len(), + }); + } + Ok(Self { + bytes: bytes.to_vec(), + _marker: PhantomData, + }) + } + } + + basic_bytes!($name, $bytes, $err); + }; +} + +macro_rules! basic_bytes { + ($name:ident, $bytes:expr, $err:ident) => { + impl TryFrom> for $name

{ + type Error = Error; + + fn try_from(bytes: Vec) -> Result { + Self::try_from(bytes.as_slice()) + } + } + + impl TryFrom<&Vec> for $name

{ + type Error = Error; + + fn try_from(bytes: &Vec) -> Result { + Self::try_from(bytes.as_slice()) + } + } + + impl TryFrom> for $name

{ + type Error = Error; + + fn try_from(bytes: Box<[u8]>) -> Result { + Self::try_from(bytes.as_ref()) + } + } + + impl AsRef<[u8]> for $name

{ + fn as_ref(&self) -> &[u8] { + &self.bytes + } + } + }; +} + /// HQC encapsulation key (public key). #[derive(Clone)] pub struct EncapsulationKey { @@ -35,6 +92,10 @@ pub struct SharedSecret { _marker: PhantomData

, } +from_bytes!(EncapsulationKey, P::PK_BYTES, InvalidPublicKeySize); +from_bytes!(Ciphertext, P::CT_BYTES, InvalidCiphertextSize); +from_bytes!(SharedSecret, P::SS_BYTES, InvalidSharedSecretSize); + /// HQC Key Encapsulation Mechanism parameterized by security level. /// /// Zero-sized marker type providing [`generate_key`](HqcKem::generate_key). @@ -43,57 +104,33 @@ pub struct SharedSecret { #[derive(Debug, Clone, Copy)] pub struct HqcKem(PhantomData

); -// --------------------------------------------------------------------------- -// Internal constructors (crate-only) -// --------------------------------------------------------------------------- - -impl EncapsulationKey

{ - pub(crate) fn from_vec(bytes: Vec) -> Self { - debug_assert_eq!(bytes.len(), P::PK_BYTES); - Self { - bytes, - _marker: PhantomData, - } - } -} - impl DecapsulationKey

{ - pub(crate) fn from_vec(bytes: Vec) -> Self { - debug_assert_eq!(bytes.len(), P::SK_BYTES); - let ek = EncapsulationKey::from_vec(bytes[..P::PK_BYTES].to_vec()); - Self { - bytes, - ek, - _marker: PhantomData, - } - } - /// Get the encapsulation (public) key corresponding to this decapsulation key. pub fn encapsulation_key(&self) -> &EncapsulationKey

{ &self.ek } } -impl Ciphertext

{ - pub(crate) fn from_vec(bytes: Vec) -> Self { - debug_assert_eq!(bytes.len(), P::CT_BYTES); - Self { - bytes, - _marker: PhantomData, - } - } -} +impl TryFrom<&[u8]> for DecapsulationKey

{ + type Error = Error; -impl SharedSecret

{ - pub(crate) fn from_vec(bytes: Vec) -> Self { - debug_assert_eq!(bytes.len(), P::SS_BYTES); - Self { - bytes, - _marker: PhantomData, + fn try_from(bytes: &[u8]) -> Result { + if bytes.len() != P::SK_BYTES { + return Err(Error::InvalidSecretKeySize { + expected: P::SK_BYTES, + got: bytes.len(), + }); } + Ok(Self { + bytes: bytes.to_vec(), + ek: EncapsulationKey::try_from(&bytes[..P::PK_BYTES])?, + _marker: PhantomData, + }) } } +basic_bytes!(DecapsulationKey, P::SK_BYTES, InvalidSecretKeySize); + // --------------------------------------------------------------------------- // Debug // --------------------------------------------------------------------------- @@ -132,88 +169,6 @@ impl core::fmt::Debug for SharedSecret

{ } } -// --------------------------------------------------------------------------- -// AsRef<[u8]> -// --------------------------------------------------------------------------- - -impl AsRef<[u8]> for EncapsulationKey

{ - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl AsRef<[u8]> for DecapsulationKey

{ - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl AsRef<[u8]> for Ciphertext

{ - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -impl AsRef<[u8]> for SharedSecret

{ - fn as_ref(&self) -> &[u8] { - &self.bytes - } -} - -// --------------------------------------------------------------------------- -// TryFrom<&[u8]> -// --------------------------------------------------------------------------- - -impl TryFrom<&[u8]> for EncapsulationKey

{ - type Error = Error; - fn try_from(bytes: &[u8]) -> Result { - if bytes.len() != P::PK_BYTES { - return Err(Error::InvalidPublicKeySize { - expected: P::PK_BYTES, - got: bytes.len(), - }); - } - Ok(Self { - bytes: bytes.to_vec(), - _marker: PhantomData, - }) - } -} - -impl TryFrom<&[u8]> for DecapsulationKey

{ - type Error = Error; - fn try_from(bytes: &[u8]) -> Result { - if bytes.len() != P::SK_BYTES { - return Err(Error::InvalidSecretKeySize { - expected: P::SK_BYTES, - got: bytes.len(), - }); - } - let ek = EncapsulationKey::from_vec(bytes[..P::PK_BYTES].to_vec()); - Ok(Self { - bytes: bytes.to_vec(), - ek, - _marker: PhantomData, - }) - } -} - -impl TryFrom<&[u8]> for Ciphertext

{ - type Error = Error; - fn try_from(bytes: &[u8]) -> Result { - if bytes.len() != P::CT_BYTES { - return Err(Error::InvalidCiphertextSize { - expected: P::CT_BYTES, - got: bytes.len(), - }); - } - Ok(Self { - bytes: bytes.to_vec(), - _marker: PhantomData, - }) - } -} - // --------------------------------------------------------------------------- // PartialEq / Eq (EncapsulationKey, Ciphertext) // --------------------------------------------------------------------------- @@ -291,9 +246,20 @@ impl HqcKem

{ rng: &mut impl rand::CryptoRng, ) -> (EncapsulationKey

, DecapsulationKey

) { let (pk, sk) = crate::kem::keygen(P::params(), rng); + let ek = EncapsulationKey { + bytes: pk.clone(), + _marker: PhantomData, + }; ( - EncapsulationKey::from_vec(pk), - DecapsulationKey::from_vec(sk), + EncapsulationKey { + bytes: pk, + _marker: PhantomData, + }, + DecapsulationKey { + bytes: sk, + ek, + _marker: PhantomData, + }, ) } @@ -305,9 +271,20 @@ impl HqcKem

{ seed: &[u8; 32], ) -> (EncapsulationKey

, DecapsulationKey

) { let (pk, sk) = crate::kem::keygen_deterministic(seed, P::params()); + let ek = EncapsulationKey { + bytes: pk.clone(), + _marker: PhantomData, + }; ( - EncapsulationKey::from_vec(pk), - DecapsulationKey::from_vec(sk), + EncapsulationKey { + bytes: pk, + _marker: PhantomData, + }, + DecapsulationKey { + bytes: sk, + ek, + _marker: PhantomData, + }, ) } } @@ -317,7 +294,16 @@ impl EncapsulationKey

{ /// Encapsulate: produce a ciphertext and shared secret. pub fn encapsulate(&self, rng: &mut impl rand::CryptoRng) -> (Ciphertext

, SharedSecret

) { let (ss, ct) = crate::kem::encaps(&self.bytes, P::params(), rng); - (Ciphertext::from_vec(ct), SharedSecret::from_vec(ss)) + ( + Ciphertext { + bytes: ct, + _marker: PhantomData, + }, + SharedSecret { + bytes: ss, + _marker: PhantomData, + }, + ) } /// Encapsulate deterministically from a message and salt. @@ -340,7 +326,16 @@ impl EncapsulationKey

{ }); } let (ss, ct) = crate::kem::encaps_deterministic(&self.bytes, m, salt, p); - Ok((Ciphertext::from_vec(ct), SharedSecret::from_vec(ss))) + Ok(( + Ciphertext { + bytes: ct, + _marker: PhantomData, + }, + SharedSecret { + bytes: ss, + _marker: PhantomData, + }, + )) } } @@ -349,7 +344,10 @@ impl DecapsulationKey

{ /// Decapsulate: recover shared secret from ciphertext. pub fn decapsulate(&self, ct: &Ciphertext

) -> SharedSecret

{ let ss = crate::kem::decaps(&self.bytes, &ct.bytes, P::params()); - SharedSecret::from_vec(ss) + SharedSecret { + bytes: ss, + _marker: PhantomData, + } } } @@ -361,73 +359,55 @@ impl DecapsulationKey

{ mod serde_impl { use super::*; - impl serde::Serialize for EncapsulationKey

{ - fn serialize(&self, s: S) -> Result { - serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) - } + macro_rules! ser_impl { + ($name:ident, ) => { + impl serde::Serialize for $name

{ + fn serialize(&self, s: S) -> Result { + serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) + } + } + }; } - impl<'de, P: HqcParams> serde::Deserialize<'de> for EncapsulationKey

{ - fn deserialize>(d: D) -> Result { - let mut buf = vec![0u8; P::PK_BYTES]; - let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; - Ok(Self { - bytes: buf, - _marker: PhantomData, - }) - } + macro_rules! deser_impl { + ($name:ident, $bytes:expr) => { + impl<'de, P: HqcParams> serde::Deserialize<'de> for $name

{ + fn deserialize>(d: D) -> Result { + let mut buf = vec![0u8; $bytes]; + let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + Ok(Self { + bytes: buf, + _marker: PhantomData, + }) + } + } + }; } - impl serde::Serialize for DecapsulationKey

{ - fn serialize(&self, s: S) -> Result { - serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) - } - } + ser_impl!(EncapsulationKey); + deser_impl!(EncapsulationKey, P::PK_BYTES); + + ser_impl!(DecapsulationKey); impl<'de, P: HqcParams> serde::Deserialize<'de> for DecapsulationKey

{ fn deserialize>(d: D) -> Result { let mut buf = vec![0u8; P::SK_BYTES]; let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; - let ek = EncapsulationKey::from_vec(buf[..P::PK_BYTES].to_vec()); - Ok(Self { - bytes: buf, - ek, + let ek = EncapsulationKey { + bytes: buf[..P::PK_BYTES].to_vec(), _marker: PhantomData, - }) - } - } - - impl serde::Serialize for Ciphertext

{ - fn serialize(&self, s: S) -> Result { - serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) - } - } - - impl<'de, P: HqcParams> serde::Deserialize<'de> for Ciphertext

{ - fn deserialize>(d: D) -> Result { - let mut buf = vec![0u8; P::CT_BYTES]; - let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; + }; Ok(Self { bytes: buf, + ek, _marker: PhantomData, }) } } - impl serde::Serialize for SharedSecret

{ - fn serialize(&self, s: S) -> Result { - serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) - } - } + ser_impl!(Ciphertext); + deser_impl!(Ciphertext, P::CT_BYTES); - impl<'de, P: HqcParams> serde::Deserialize<'de> for SharedSecret

{ - fn deserialize>(d: D) -> Result { - let mut buf = vec![0u8; P::SS_BYTES]; - let _ = serdect::slice::deserialize_hex_or_bin(&mut buf, d)?; - Ok(Self { - bytes: buf, - _marker: PhantomData, - }) - } - } + ser_impl!(SharedSecret); + deser_impl!(SharedSecret, P::SS_BYTES); } From 29def2ee284268474db20e5701b508376d1f22a6 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 10:25:03 -0600 Subject: [PATCH 7/8] more CI fixes Signed-off-by: Mike Lodder --- hqc-kem/src/lib.rs | 10 +++++++++ hqc-kem/src/params.rs | 10 +++++++++ hqc-kem/src/types.rs | 48 ++++++++++++++++++++++++++++++++++--------- 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/hqc-kem/src/lib.rs b/hqc-kem/src/lib.rs index 73dd6d1..d0d2a27 100644 --- a/hqc-kem/src/lib.rs +++ b/hqc-kem/src/lib.rs @@ -36,21 +36,31 @@ //! - `alloc`: Enables PKCS#8 encoding (requires `alloc`) //! - `serde`: Serde serialization support +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod code; mod error; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod fft; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod gf256; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod kem; #[cfg(feature = "kem")] mod kem_impl; mod params; #[cfg(feature = "pkcs8")] mod pkcs8_impl; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod pke; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod poly; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod reed_muller; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod reed_solomon; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod sampling; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] mod shake; #[cfg(feature = "kem")] mod sizes; diff --git a/hqc-kem/src/params.rs b/hqc-kem/src/params.rs index 9ffa37d..3b2fa33 100644 --- a/hqc-kem/src/params.rs +++ b/hqc-kem/src/params.rs @@ -1,12 +1,20 @@ //! HQC parameter definitions for all security levels. /// Seed size in bytes. +#[cfg(any( + feature = "kgen", + feature = "ecap", + feature = "dcap", + feature = "pkcs8", + feature = "kem" +))] pub(crate) const SEED_BYTES: usize = 32; /// Salt size in bytes. pub(crate) const SALT_BYTES: usize = 16; /// Shared secret size in bytes. pub(crate) const SS_BYTES: usize = 32; /// GF(2^M) parameter M. +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] pub(crate) const PARAM_M: usize = 8; /// Internal runtime parameter set. @@ -147,7 +155,9 @@ pub(crate) const HQC_256: HqcParameters = HqcParameters { }; // Maximum sizes for stack-allocated arrays. +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] pub(crate) const MAX_N1: usize = 90; +#[cfg(any(feature = "kgen", feature = "ecap", feature = "dcap"))] pub(crate) const MAX_DELTA: usize = 29; mod sealed { diff --git a/hqc-kem/src/types.rs b/hqc-kem/src/types.rs index cbdd267..7f913ea 100644 --- a/hqc-kem/src/types.rs +++ b/hqc-kem/src/types.rs @@ -66,36 +66,64 @@ macro_rules! basic_bytes { /// HQC encapsulation key (public key). #[derive(Clone)] pub struct EncapsulationKey { - bytes: Vec, - _marker: PhantomData

, + pub(crate) bytes: Vec, + pub(crate) _marker: PhantomData

, } /// HQC decapsulation key (secret key). #[derive(Clone)] pub struct DecapsulationKey { - bytes: Vec, - ek: EncapsulationKey

, - _marker: PhantomData

, + pub(crate) bytes: Vec, + pub(crate) ek: EncapsulationKey

, + pub(crate) _marker: PhantomData

, } /// HQC ciphertext. #[derive(Clone)] pub struct Ciphertext { - bytes: Vec, - _marker: PhantomData

, + pub(crate) bytes: Vec, + pub(crate) _marker: PhantomData

, } /// HQC shared secret. #[derive(Clone)] pub struct SharedSecret { - bytes: Vec, - _marker: PhantomData

, + pub(crate) bytes: Vec, + pub(crate) _marker: PhantomData

, } from_bytes!(EncapsulationKey, P::PK_BYTES, InvalidPublicKeySize); from_bytes!(Ciphertext, P::CT_BYTES, InvalidCiphertextSize); from_bytes!(SharedSecret, P::SS_BYTES, InvalidSharedSecretSize); +// --------------------------------------------------------------------------- +// Internal constructors (crate-only) +// --------------------------------------------------------------------------- + +#[cfg(any(feature = "kem", feature = "pkcs8"))] +impl EncapsulationKey

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::PK_BYTES); + Self { + bytes, + _marker: PhantomData, + } + } +} + +#[cfg(any(feature = "kem", feature = "pkcs8"))] +impl DecapsulationKey

{ + pub(crate) fn from_vec(bytes: Vec) -> Self { + debug_assert_eq!(bytes.len(), P::SK_BYTES); + let ek = EncapsulationKey::from_vec(bytes[..P::PK_BYTES].to_vec()); + Self { + bytes, + ek, + _marker: PhantomData, + } + } +} + /// HQC Key Encapsulation Mechanism parameterized by security level. /// /// Zero-sized marker type providing [`generate_key`](HqcKem::generate_key). @@ -360,7 +388,7 @@ mod serde_impl { use super::*; macro_rules! ser_impl { - ($name:ident, ) => { + ($name:ident) => { impl serde::Serialize for $name

{ fn serialize(&self, s: S) -> Result { serdect::slice::serialize_hex_lower_or_bin(&self.bytes, s) From 7b7e80e7a6274982888bbf56f030ec3b886bf007 Mon Sep 17 00:00:00 2001 From: Mike Lodder Date: Tue, 31 Mar 2026 10:48:29 -0600 Subject: [PATCH 8/8] remove patch, use new published crate Signed-off-by: Mike Lodder --- Cargo.lock | 294 ++-- Cargo.toml | 1 - hqc-kem/Cargo.toml | 5 +- hqc-kem/hybrid-array-patch/.cargo-ok | 1 - .../hybrid-array-patch/.cargo_vcs_info.json | 6 - .../hybrid-array-patch/.github/dependabot.yml | 12 - .../.github/workflows/hybrid-array.yml | 109 -- .../.github/workflows/publish.yml | 26 - hqc-kem/hybrid-array-patch/.gitignore | 10 - hqc-kem/hybrid-array-patch/CHANGELOG.md | 194 --- hqc-kem/hybrid-array-patch/Cargo.lock | 146 -- hqc-kem/hybrid-array-patch/Cargo.toml | 138 -- hqc-kem/hybrid-array-patch/Cargo.toml.orig | 77 -- hqc-kem/hybrid-array-patch/LICENSE-APACHE | 201 --- hqc-kem/hybrid-array-patch/LICENSE-MIT | 25 - hqc-kem/hybrid-array-patch/README.md | 68 - hqc-kem/hybrid-array-patch/src/flatten.rs | 137 -- hqc-kem/hybrid-array-patch/src/from_fn.rs | 106 -- hqc-kem/hybrid-array-patch/src/iter.rs | 107 -- hqc-kem/hybrid-array-patch/src/lib.rs | 1217 ----------------- hqc-kem/hybrid-array-patch/src/serde.rs | 71 - hqc-kem/hybrid-array-patch/src/sizes.rs | 1164 ---------------- hqc-kem/hybrid-array-patch/src/traits.rs | 199 --- hqc-kem/hybrid-array-patch/tests/ctutils.rs | 42 - hqc-kem/hybrid-array-patch/tests/mod.rs | 201 --- hqc-kem/hybrid-array-patch/tests/subtle.rs | 29 - 26 files changed, 150 insertions(+), 4436 deletions(-) delete mode 100644 hqc-kem/hybrid-array-patch/.cargo-ok delete mode 100644 hqc-kem/hybrid-array-patch/.cargo_vcs_info.json delete mode 100644 hqc-kem/hybrid-array-patch/.github/dependabot.yml delete mode 100644 hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml delete mode 100644 hqc-kem/hybrid-array-patch/.github/workflows/publish.yml delete mode 100644 hqc-kem/hybrid-array-patch/.gitignore delete mode 100644 hqc-kem/hybrid-array-patch/CHANGELOG.md delete mode 100644 hqc-kem/hybrid-array-patch/Cargo.lock delete mode 100644 hqc-kem/hybrid-array-patch/Cargo.toml delete mode 100644 hqc-kem/hybrid-array-patch/Cargo.toml.orig delete mode 100644 hqc-kem/hybrid-array-patch/LICENSE-APACHE delete mode 100644 hqc-kem/hybrid-array-patch/LICENSE-MIT delete mode 100644 hqc-kem/hybrid-array-patch/README.md delete mode 100644 hqc-kem/hybrid-array-patch/src/flatten.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/from_fn.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/iter.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/lib.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/serde.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/sizes.rs delete mode 100644 hqc-kem/hybrid-array-patch/src/traits.rs delete mode 100644 hqc-kem/hybrid-array-patch/tests/ctutils.rs delete mode 100644 hqc-kem/hybrid-array-patch/tests/mod.rs delete mode 100644 hqc-kem/hybrid-array-patch/tests/subtle.rs diff --git a/Cargo.lock b/Cargo.lock index 39aff77..9fc8ccf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -30,15 +30,15 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "atomic-polyfill" @@ -63,15 +63,15 @@ checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" [[package]] name = "base64ct" -version = "1.8.2" +version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d809780667f4410e7c41b07f52439b94d2bdf8528eeedc287fa38d3b7f95d82" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "block-buffer" @@ -93,9 +93,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "byteorder" @@ -111,9 +111,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.52" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "shlex", @@ -177,18 +177,18 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.54" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" -version = "4.5.54" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstyle", "clap_lex", @@ -196,9 +196,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "cmov" @@ -317,9 +317,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "crypto-bigint" -version = "0.7.0-rc.25" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cba9eeeb213f7fd29353032f71f7c173e5f6d95d85151cb3a47197b0ea7e8be7" +checksum = "42a0d26b245348befa0c121944541476763dcc46ede886c88f9d12e1697d27c3" dependencies = [ "cpubits", "ctutils", @@ -447,9 +447,9 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "elliptic-curve" -version = "0.14.0-rc.28" +version = "0.14.0-rc.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978" +checksum = "e84043d573efd4ac9d2d125817979a379204bf7e328b25a4a30487e8d100e618" dependencies = [ "base16ct", "crypto-bigint", @@ -491,9 +491,9 @@ checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" [[package]] name = "find-msvc-tools" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "foldhash" @@ -530,15 +530,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -547,9 +547,9 @@ dependencies = [ [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-timer" @@ -559,15 +559,14 @@ checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-core", "futures-macro", "futures-task", "pin-project-lite", - "pin-utils", "slab", ] @@ -583,9 +582,9 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", @@ -679,18 +678,18 @@ checksum = "e712f64ec3850b98572bffac52e2c6f282b29fe6c5fa6d42334b30be438d95c1" [[package]] name = "hkdf" -version = "0.13.0-rc.5" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbb55385998ae66b8d2d5143c05c94b9025ab863966f0c94ce7a5fde30105092" +checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018" dependencies = [ "hmac", ] [[package]] name = "hmac" -version = "0.13.0-rc.6" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60017b071c523c9e5a55dd1253582bff6150c5e96a7e8511e419de1ab5ee97f9" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" dependencies = [ "digest 0.11.2", ] @@ -717,7 +716,9 @@ dependencies = [ [[package]] name = "hybrid-array" -version = "0.4.9" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3944cf8cf766b40e2a1a333ee5e9b563f854d5fa49d6a8ca2764e97c6eddb214" dependencies = [ "ctutils", "subtle", @@ -763,15 +764,15 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.83" +version = "0.3.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +checksum = "797146bb2677299a1eb6b7b50a890f4c361b29ef967addf5b2fa45dae1bb6d7d" dependencies = [ "once_cell", "wasm-bindgen", @@ -779,9 +780,9 @@ dependencies = [ [[package]] name = "k256" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83da23da11f0b5db6f23d9280a84b3a33a746aa43ebb9270d6b445991da9cee3" +checksum = "f7d2c6c227649d5ec80eaae541f1736232641a0bcdb3062a52b34edb42054158" dependencies = [ "cpubits", "elliptic-curve", @@ -824,9 +825,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "lock_api" @@ -845,9 +846,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "ml-kem" @@ -922,9 +923,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "oorandom" @@ -934,9 +935,9 @@ checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" [[package]] name = "openssl-sys" -version = "0.9.111" +version = "0.9.112" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +checksum = "57d55af3b3e226502be1526dfdba67ab0e9c96fc293004e79576b2b9edb0dbdb" dependencies = [ "cc", "libc", @@ -946,9 +947,9 @@ dependencies = [ [[package]] name = "p256" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "018bfbb86e05fd70a83e985921241035ee09fcd369c4a2c3680b389a01d2ad28" +checksum = "44f0a10fe314869359cb2901342b045f4e5a962ef9febc006f03d2a8c848fe4c" dependencies = [ "elliptic-curve", "primefield", @@ -957,9 +958,9 @@ dependencies = [ [[package]] name = "p384" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c91df688211f5957dbe2ab599dcbcaade8d6d3cdc15c5b350d350d7d07ce423" +checksum = "b079e66810c55ab3d6ba424e056dc4aefcdb8046c8c3f3816142edbdd7af7721" dependencies = [ "elliptic-curve", "fiat-crypto", @@ -969,9 +970,9 @@ dependencies = [ [[package]] name = "p521" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de6cd9451de522549d36cc78a1b45a699a3d55a872e8ea0c8f0318e502d99e2c" +checksum = "9eecc34c4c6e6596d5271fecf90ac4f16593fa198e77282214d0c22736aa9266" dependencies = [ "base16ct", "elliptic-curve", @@ -990,15 +991,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" - -[[package]] -name = "pin-utils" -version = "0.1.0" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "pkcs8" @@ -1069,9 +1064,9 @@ dependencies = [ [[package]] name = "primefield" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93401c13cc7ff24684571cfca9d3cf9ebabfaf3d4b7b9963ade41ec54da196b5" +checksum = "c6543f5eec854fbf74ba5ef651fbdc9408919b47c3e1526623687135c16d12e9" dependencies = [ "crypto-bigint", "crypto-common 0.2.1", @@ -1083,45 +1078,45 @@ dependencies = [ [[package]] name = "primeorder" -version = "0.14.0-rc.7" +version = "0.14.0-rc.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c5c8a39bcd764bfedf456e8d55e115fe86dda3e0f555371849f2a41cbc9706" +checksum = "569d9ad6ef822bb0322c7e7d84e5e286244050bd5246cac4c013535ae91c2c90" dependencies = [ "elliptic-curve", ] [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.43" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" @@ -1162,9 +1157,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1174,9 +1169,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1185,9 +1180,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "relative-path" @@ -1235,9 +1230,9 @@ dependencies = [ [[package]] name = "rustcrypto-ff" -version = "0.14.0-rc.0" +version = "0.14.0-rc.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5db129183b2c139d7d87d08be57cba626c715789db17aec65c8866bfd767d1f" +checksum = "fd2a8adb347447693cd2ba0d218c4b66c62da9b0a5672b17b981e4291ec65ff6" dependencies = [ "rand_core", "subtle", @@ -1262,9 +1257,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "same-file" @@ -1283,9 +1278,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sec1" -version = "0.8.0-rc.13" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2400ed44a13193820aa528a19f376c3843141a8ce96ff34b11104cc79763f2" +checksum = "f46b9a5ab87780a3189a1d704766579517a04ad59de653b7aad7d38e8a15f7dc" dependencies = [ "base16ct", "ctutils", @@ -1365,9 +1360,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" dependencies = [ "serde_core", ] @@ -1434,9 +1429,9 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slab" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "spin" @@ -1471,9 +1466,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" -version = "2.0.114" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1482,18 +1477,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1512,17 +1507,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.11+spec-1.1.0" +version = "0.9.12+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3afc9a848309fe1aaffaed6e1546a7a14de1f935dc9d89d32afd9a44bab7c46" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -1534,32 +1529,41 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.8+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 1.1.0+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.0.6+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" dependencies = [ - "winnow", + "winnow 1.0.1", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.0+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" [[package]] name = "typenum" @@ -1569,9 +1573,9 @@ checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-xid" @@ -1609,11 +1613,11 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "wit-bindgen 0.46.0", + "wit-bindgen", ] [[package]] @@ -1622,14 +1626,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen 0.51.0", + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.106" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +checksum = "7dc0882f7b5bb01ae8c5215a1230832694481c1a4be062fd410e12ea3da5b631" dependencies = [ "cfg-if", "once_cell", @@ -1640,9 +1644,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.106" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +checksum = "75973d3066e01d035dbedaad2864c398df42f8dd7b1ea057c35b8407c015b537" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1650,9 +1654,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.106" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +checksum = "91af5e4be765819e0bcfee7322c14374dc821e35e72fa663a830bbc7dc199eac" dependencies = [ "bumpalo", "proc-macro2", @@ -1663,9 +1667,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.106" +version = "0.2.116" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +checksum = "c9bf0406a78f02f336bf1e451799cca198e8acde4ffa278f0fb20487b150a633" dependencies = [ "unicode-ident", ] @@ -1706,9 +1710,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.83" +version = "0.3.93" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +checksum = "749466a37ee189057f54748b200186b59a03417a117267baf3fd89cecc9fb837" dependencies = [ "js-sys", "wasm-bindgen", @@ -1740,18 +1744,18 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -dependencies = [ - "memchr", -] +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] -name = "wit-bindgen" -version = "0.46.0" +name = "winnow" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" +dependencies = [ + "memchr", +] [[package]] name = "wit-bindgen" @@ -1870,18 +1874,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.33" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.33" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -1910,6 +1914,6 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.12" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fc5a66a20078bf1251bde995aa2fdcc4b800c70b5d92dd2c62abc5c60f679f8" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/Cargo.toml b/Cargo.toml index 2d77965..bc13478 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,4 +59,3 @@ debug = true [patch.crates-io] ml-kem = { path = "./ml-kem" } module-lattice = { path = "./module-lattice" } -hybrid-array = { path = "./hqc-kem/hybrid-array-patch" } diff --git a/hqc-kem/Cargo.toml b/hqc-kem/Cargo.toml index 9588e2f..e4e293b 100644 --- a/hqc-kem/Cargo.toml +++ b/hqc-kem/Cargo.toml @@ -24,7 +24,7 @@ serde = ["dep:serdect", "dep:serde"] [dependencies] const-oid = { version = "0.10", optional = true } hex = "0.4" -hybrid-array = { version = "0.4.7", optional = true, features = ["extra-sizes"] } +hybrid-array = { version = "0.4.10", optional = true, features = ["extra-sizes"] } kem_traits = { package = "kem", version = "0.3.0-rc.6", optional = true } pkcs8 = { version = "0.11.0-rc.11", optional = true, default-features = false } rand = "0.10" @@ -45,9 +45,6 @@ rand = { version = "0.10", features = ["thread_rng"] } name = "kem" harness = false -[patch.crates-io] -hybrid-array = { path = "hybrid-array-patch" } - [lints.rust] missing_docs = "deny" missing_debug_implementations = "deny" diff --git a/hqc-kem/hybrid-array-patch/.cargo-ok b/hqc-kem/hybrid-array-patch/.cargo-ok deleted file mode 100644 index 5f8b795..0000000 --- a/hqc-kem/hybrid-array-patch/.cargo-ok +++ /dev/null @@ -1 +0,0 @@ -{"v":1} \ No newline at end of file diff --git a/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json b/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json deleted file mode 100644 index d20bb7c..0000000 --- a/hqc-kem/hybrid-array-patch/.cargo_vcs_info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "git": { - "sha1": "1cccda21548ebb317e6c3a0b47dbd70c746a0f84" - }, - "path_in_vcs": "" -} \ No newline at end of file diff --git a/hqc-kem/hybrid-array-patch/.github/dependabot.yml b/hqc-kem/hybrid-array-patch/.github/dependabot.yml deleted file mode 100644 index 397bdaa..0000000 --- a/hqc-kem/hybrid-array-patch/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -version: 2 -updates: -- package-ecosystem: cargo - directory: "/" - schedule: - interval: daily - open-pull-requests-limit: 10 -- package-ecosystem: github-actions - directory: "/" - schedule: - interval: weekly - open-pull-requests-limit: 10 diff --git a/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml b/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml deleted file mode 100644 index 70df483..0000000 --- a/hqc-kem/hybrid-array-patch/.github/workflows/hybrid-array.yml +++ /dev/null @@ -1,109 +0,0 @@ -name: hybrid-array - -on: - pull_request: - paths-ignore: - - README.md - push: - branches: master - paths-ignore: - - README.md - -permissions: - contents: read - -env: - CARGO_INCREMENTAL: 0 - RUSTFLAGS: "-D warnings" - RUSTDOCFLAGS: "-D warnings" - -jobs: - build: - runs-on: ubuntu-latest - strategy: - matrix: - rust: - - 1.85.0 # MSRV - - stable - target: - - armv7a-none-eabi - - thumbv7em-none-eabi - - wasm32-unknown-unknown - steps: - - uses: actions/checkout@v6 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.rust }} - targets: ${{ matrix.target }} - - uses: RustCrypto/actions/cargo-hack-install@master - - run: cargo hack build --target ${{ matrix.target }} --feature-powerset --exclude-all-features --optional-deps bytemuck,ctutils,serde,subtle,zeroize - - careful: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@nightly - - run: cargo install cargo-careful - - run: cargo careful test --all-features - - clippy: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: 1.85.0 - components: clippy - - run: cargo clippy --all-targets --all-features -- -D warnings - - doc: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - - run: cargo doc --all-features - - miri: - runs-on: ubuntu-latest - env: - MIRIFLAGS: "-Zmiri-symbolic-alignment-check -Zmiri-strict-provenance" - strategy: - matrix: - target: - - x86_64-unknown-linux-gnu - - s390x-unknown-linux-gnu - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@nightly - - run: rustup component add miri && cargo miri setup - - run: cargo miri test --target ${{ matrix.target }} --all-features - - rustfmt: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: dtolnay/rust-toolchain@master - with: - toolchain: stable - components: rustfmt - - run: cargo fmt --all -- --check - - test: - strategy: - matrix: - toolchain: - - 1.85.0 # MSRV - - stable - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v6 - - uses: RustCrypto/actions/cargo-cache@master - - uses: dtolnay/rust-toolchain@master - with: - toolchain: ${{ matrix.toolchain }} - - uses: RustCrypto/actions/cargo-hack-install@master - - run: cargo hack test --feature-powerset --optional-deps arbitrary,bytemuck,serde,subtle,zeroize --group-features arbitrary,bytemuck,ctutils,serde,subtle,zeroize - - run: cargo test --all-features --release diff --git a/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml b/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml deleted file mode 100644 index 1ac4433..0000000 --- a/hqc-kem/hybrid-array-patch/.github/workflows/publish.yml +++ /dev/null @@ -1,26 +0,0 @@ -name: Publish to crates.io -on: - push: - tags: [ 'v*' ] - -jobs: - publish: - runs-on: ubuntu-latest - environment: publish - permissions: - id-token: write - steps: - - uses: actions/checkout@v6 - - uses: rust-lang/crates-io-auth-action@v1 - id: auth - - - name: Check crate version - run: | - CRATE_VERSION=v$(grep -m 1 "^version =" Cargo.toml | cut -d'"' -f2) - echo $CRATE_VERSION ${{ github.ref_name }} - [[ $CRATE_VERSION == ${{ github.ref_name }} ]] - - - name: Publish - env: - CARGO_REGISTRY_TOKEN: ${{ steps.auth.outputs.token }} - run: cargo publish diff --git a/hqc-kem/hybrid-array-patch/.gitignore b/hqc-kem/hybrid-array-patch/.gitignore deleted file mode 100644 index 73fab07..0000000 --- a/hqc-kem/hybrid-array-patch/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -# Generated by Cargo -# will have compiled files and executables -debug/ -target/ - -# These are backup files generated by rustfmt -**/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information -*.pdb diff --git a/hqc-kem/hybrid-array-patch/CHANGELOG.md b/hqc-kem/hybrid-array-patch/CHANGELOG.md deleted file mode 100644 index 7a421ae..0000000 --- a/hqc-kem/hybrid-array-patch/CHANGELOG.md +++ /dev/null @@ -1,194 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). - -## 0.4.9 (2026-03-30) -### Added -- Functions for casting from core references ([#181]) - - `Array::cast_from_core`: `const fn` equivalent of `From<[T; N]>` - - `Array::cast_from_core_mut`: `mut` equivalent of the above - - `Array::from_ref`: cast `&T` to `&Array` - - `Array::from_mut`: `mut` equivalent of the above - -[#181]: https://github.com/RustCrypto/hybrid-array/pull/181 - -## 0.4.8 (2026-03-08) -### Added -- `ctutils` support ([#177]) - -[#177]: https://github.com/RustCrypto/hybrid-array/pull/177 - -## 0.4.7 (2026-02-03) -### Added -- `Flatten` and `Unflatten` traits ([#170]) -- `Array::slice_as_(mut_)array` ([#171]) -- `SliceExt` ([#172]) - -[#170]: https://github.com/RustCrypto/hybrid-array/pull/170 -[#171]: https://github.com/RustCrypto/hybrid-array/pull/171 -[#172]: https://github.com/RustCrypto/hybrid-array/pull/172 - -## 0.4.6 (2026-01-22) -### Added -- Optional dependency on `zerocopy` with impls for `Array` ([#162]) -- `Debug` bound to `ArraySize` ([#165]) - -[#162]: https://github.com/RustCrypto/hybrid-array/pull/162 -[#165]: https://github.com/RustCrypto/hybrid-array/pull/165 - -## 0.4.5 (2025-09-29) -### Added -- Impl `arbitrary::Arbitrary` for `Array` ([#153]) - -### Changed -- Switch from `doc_auto_cfg` to `doc_cfg` ([#154]) - -[#153]: https://github.com/RustCrypto/hybrid-array/pull/153 -[#154]: https://github.com/RustCrypto/hybrid-array/pull/154 - -## 0.4.4 (2025-09-24) -### Added -- Enable the `subtle/const-generics` feature ([#149]) - -[#149]: https://github.com/RustCrypto/hybrid-array/pull/149 - -## 0.4.3 (2025-09-23) -### Added -- `Array::as_(mut_)ptr` ([#147]) - -### Changed -- Remove bounds on `Array::slice_as_flattened(_mut)`; make `const fn` ([#144]) -- Make `Array::as_(mut_)slice` a `const fn` ([#147]) -- Make `Array::::as_flattened(_mut)` a `const fn` ([#147]) - -[#144]: https://github.com/RustCrypto/hybrid-array/pull/144 -[#147]: https://github.com/RustCrypto/hybrid-array/pull/147 - -## 0.4.2 (2025-09-21) [YANKED] -### Added -- `Array::slice_as_flattened(_mut)` ([#142]) - -[#142]: https://github.com/RustCrypto/hybrid-array/pull/142 - -## 0.4.1 (2025-09-10) -### Changed -- Make slice conversions `const fn` ([#140]) - -[#140]: https://github.com/RustCrypto/hybrid-array/pull/140 - -## 0.4.0 (2025-09-01) -### Added -- `ArraySize` impls for `U536` and `U568` ([#128]) -- `AsArrayRef`/`AsArrayMut` traits with impls on `[T; N]` and `Array` ([#135]) -- `alloc` feature with `Box`/`Vec` conversions to/from `Array` ([#136], [#138]) - -### Removed -- `AsRef`/`AsMut` impls on `[T; N]` ([#133]) - -[#128]: https://github.com/RustCrypto/hybrid-array/pull/128 -[#133]: https://github.com/RustCrypto/hybrid-array/pull/133 -[#135]: https://github.com/RustCrypto/hybrid-array/pull/135 -[#136]: https://github.com/RustCrypto/hybrid-array/pull/136 -[#138]: https://github.com/RustCrypto/hybrid-array/pull/138 - -## 0.3.1 (2025-03-30) -### Added -- `subtle` feature ([#126]) - -[#126]: https://github.com/RustCrypto/hybrid-array/pull/126 - -## 0.3.0 (2025-02-21) -### Changed -- Bump edition to 2024; MSRV 1.85 ([#116]) - -### Removed -- `U3293` as an unused ML-DSA size ([#117]) - -[#116]: https://github.com/RustCrypto/hybrid-array/pull/116 -[#117]: https://github.com/RustCrypto/hybrid-array/pull/117 - -## 0.2.3 (2024-12-07) -### Added -- Additional ML-DSA sizes ([#108]) - -[#108]: https://github.com/RustCrypto/hybrid-array/pull/108 - -## 0.2.2 (2024-11-11) -### Added -- FrodoKEM sizes ([#104]) - -[#104]: https://github.com/RustCrypto/hybrid-array/pull/104 - -## 0.2.1 (2024-10-20) -### Fixed -- MSRV badge ([9d47c798](https://github.com/RustCrypto/hybrid-array/commit/9d47c79861057b3a04bb19cb2dfaa1f75cbf9ddd)) - -## 0.2.0 (2024-10-19) -### Added -- Reference conversion support from core arrays ([utils#904]) -- Impl `Default` for `Array` ([utils#905]) -- `Deref`/`DerefMut` impls for `Array` ([utils#908], [utils#913]) -- Impl `From>` for `[T; N]` ([utils#945]) -- Impl `IntoIterator` for all `ArraySize`s ([utils#956]) -- Impl `IntoIterator` for references to all `ArraySize`s ([utils#957]) -- Concat and split methods ([utils#958]) -- `slice_as_chunks(_mut)` methods ([utils#974]) -- Impl `Zeroize`/`ZeroizeOnDrop` for `Array` ([utils#984]) -- `AssocArraySize` trait ([utils#1006], [#40]) -- `sizes` submodule ([utils#1014], [#68]) -- `ArrayN` type alias ([utils#1017]) -- Impl `FromIterator` ([utils#1039]) -- `Array::try_from_iter` ([#4]) -- Helper functions for `Array, U>` ([#8]) -- `Send` and `Sync` impls for `Array` ([#15]) -- `Array::map` ([#61]) -- Support all array sizes up to `U512` ([#67]) -- `Array>::as_flattened{_mut}()` ([#86]) -- `serde` support ([#88]) -- `bytemuck` support ([#99]) - -### Changed -- Use GATs for `ArraySize` ([utils#893]) -- Make `ArraySize` an `unsafe trait` ([utils#914]) -- MSRV 1.81 ([#85]) - -### Removed -- `ByteArray` type alias ([utils#995]) -- `ArrayOps` trait ([#30]) -- `std` feature ([#85]) - -[utils#893]: https://github.com/RustCrypto/utils/pull/893 -[utils#904]: https://github.com/RustCrypto/utils/pull/904 -[utils#905]: https://github.com/RustCrypto/utils/pull/905 -[utils#908]: https://github.com/RustCrypto/utils/pull/908 -[utils#913]: https://github.com/RustCrypto/utils/pull/913 -[utils#914]: https://github.com/RustCrypto/utils/pull/914 -[utils#945]: https://github.com/RustCrypto/utils/pull/945 -[utils#956]: https://github.com/RustCrypto/utils/pull/956 -[utils#957]: https://github.com/RustCrypto/utils/pull/957 -[utils#958]: https://github.com/RustCrypto/utils/pull/958 -[utils#974]: https://github.com/RustCrypto/utils/pull/974 -[utils#984]: https://github.com/RustCrypto/utils/pull/984 -[utils#995]: https://github.com/RustCrypto/utils/pull/995 -[utils#1006]: https://github.com/RustCrypto/utils/pull/1006 -[utils#1014]: https://github.com/RustCrypto/utils/pull/1014 -[utils#1017]: https://github.com/RustCrypto/utils/pull/1017 -[utils#1039]: https://github.com/RustCrypto/utils/pull/1039 -[#4]: https://github.com/RustCrypto/hybrid-array/pull/4 -[#8]: https://github.com/RustCrypto/hybrid-array/pull/8 -[#15]: https://github.com/RustCrypto/hybrid-array/pull/15 -[#30]: https://github.com/RustCrypto/hybrid-array/pull/30 -[#40]: https://github.com/RustCrypto/hybrid-array/pull/40 -[#61]: https://github.com/RustCrypto/hybrid-array/pull/61 -[#67]: https://github.com/RustCrypto/hybrid-array/pull/67 -[#68]: https://github.com/RustCrypto/hybrid-array/pull/68 -[#85]: https://github.com/RustCrypto/hybrid-array/pull/85 -[#86]: https://github.com/RustCrypto/hybrid-array/pull/86 -[#88]: https://github.com/RustCrypto/hybrid-array/pull/88 -[#99]: https://github.com/RustCrypto/hybrid-array/pull/99 - -## 0.1.0 (2022-05-07) -- Initial release diff --git a/hqc-kem/hybrid-array-patch/Cargo.lock b/hqc-kem/hybrid-array-patch/Cargo.lock deleted file mode 100644 index ee8d9c4..0000000 --- a/hqc-kem/hybrid-array-patch/Cargo.lock +++ /dev/null @@ -1,146 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 4 - -[[package]] -name = "arbitrary" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" - -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - -[[package]] -name = "cmov" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3" - -[[package]] -name = "ctutils" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1005a6d4446f5120ef475ad3d2af2b30c49c2c9c6904258e3bb30219bebed5e4" -dependencies = [ - "cmov", -] - -[[package]] -name = "hybrid-array" -version = "0.4.9" -dependencies = [ - "arbitrary", - "bytemuck", - "ctutils", - "serde", - "subtle", - "typenum", - "zerocopy", - "zeroize", -] - -[[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.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", -] - -[[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 = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "2.0.114" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "unicode-ident" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" - -[[package]] -name = "zerocopy" -version = "0.8.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7456cf00f0685ad319c5b1693f291a650eaf345e941d082fc4e03df8a03996ac" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.8.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1328722bbf2115db7e19d69ebcc15e795719e2d66b60827c6a69a117365e37a0" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "zeroize" -version = "1.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" diff --git a/hqc-kem/hybrid-array-patch/Cargo.toml b/hqc-kem/hybrid-array-patch/Cargo.toml deleted file mode 100644 index c04416a..0000000 --- a/hqc-kem/hybrid-array-patch/Cargo.toml +++ /dev/null @@ -1,138 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2024" -rust-version = "1.85" -name = "hybrid-array" -version = "0.4.9" -authors = ["RustCrypto Developers"] -build = false -autolib = false -autobins = false -autoexamples = false -autotests = false -autobenches = false -description = """ -Hybrid typenum-based and const generic array types designed to provide the -flexibility of typenum-based expressions while also allowing interoperability -and a transition path to const generics -""" -documentation = "https://docs.rs/hybrid-array" -readme = "README.md" -keywords = ["generic-array"] -categories = [ - "no-std", - "data-structures", -] -license = "MIT OR Apache-2.0" -repository = "https://github.com/RustCrypto/hybrid-array" - -[package.metadata.docs.rs] -all-features = true - -[features] -alloc = [] -extra-sizes = [] - -[lib] -name = "hybrid_array" -path = "src/lib.rs" - -[[test]] -name = "ctutils" -path = "tests/ctutils.rs" - -[[test]] -name = "mod" -path = "tests/mod.rs" - -[[test]] -name = "subtle" -path = "tests/subtle.rs" - -[dependencies.arbitrary] -version = "1" -optional = true - -[dependencies.bytemuck] -version = "1" -optional = true -default-features = false - -[dependencies.ctutils] -version = "0.4" -optional = true - -[dependencies.serde] -version = "1" -optional = true -default-features = false - -[dependencies.subtle] -version = "2" -features = ["const-generics"] -optional = true -default-features = false - -[dependencies.typenum] -version = "1.17" -features = ["const-generics"] - -[dependencies.zerocopy] -version = "0.8" -features = ["derive"] -optional = true - -[dependencies.zeroize] -version = "1.8" -optional = true -default-features = false - -[lints.clippy] -arithmetic_side_effects = "warn" -borrow_as_ptr = "warn" -cast_lossless = "warn" -cast_possible_truncation = "warn" -cast_possible_wrap = "warn" -cast_precision_loss = "warn" -cast_sign_loss = "warn" -checked_conversions = "warn" -doc_markdown = "warn" -from_iter_instead_of_collect = "warn" -implicit_saturating_sub = "warn" -manual_assert = "warn" -map_unwrap_or = "warn" -missing_errors_doc = "warn" -missing_panics_doc = "warn" -mod_module_files = "warn" -must_use_candidate = "warn" -panic = "warn" -panic_in_result_fn = "warn" -ptr_as_ptr = "warn" -redundant_closure_for_method_calls = "warn" -ref_as_ptr = "warn" -return_self_not_must_use = "warn" -semicolon_if_nothing_returned = "warn" -std_instead_of_alloc = "warn" -std_instead_of_core = "warn" -trivially_copy_pass_by_ref = "warn" -unwrap_in_result = "warn" -unwrap_used = "warn" - -[lints.rust] -missing_copy_implementations = "warn" -missing_debug_implementations = "warn" -missing_docs = "warn" -trivial_casts = "warn" -trivial_numeric_casts = "warn" -unused_lifetimes = "warn" -unused_qualifications = "warn" diff --git a/hqc-kem/hybrid-array-patch/Cargo.toml.orig b/hqc-kem/hybrid-array-patch/Cargo.toml.orig deleted file mode 100644 index 186674c..0000000 --- a/hqc-kem/hybrid-array-patch/Cargo.toml.orig +++ /dev/null @@ -1,77 +0,0 @@ -[package] -name = "hybrid-array" -version = "0.4.9" -description = """ -Hybrid typenum-based and const generic array types designed to provide the -flexibility of typenum-based expressions while also allowing interoperability -and a transition path to const generics -""" -authors = ["RustCrypto Developers"] -license = "MIT OR Apache-2.0" -documentation = "https://docs.rs/hybrid-array" -repository = "https://github.com/RustCrypto/hybrid-array" -categories = ["no-std", "data-structures"] -keywords = ["generic-array"] -readme = "README.md" -edition = "2024" -rust-version = "1.85" - -[dependencies] -typenum = { version = "1.17", features = ["const-generics"] } - -# optional dependencies -arbitrary = { version = "1", optional = true } -bytemuck = { version = "1", optional = true, default-features = false } -ctutils = { version = "0.4", optional = true } -serde = { version = "1", optional = true, default-features = false } -subtle = { version = "2", optional = true, default-features = false, features = ["const-generics"] } -zeroize = { version = "1.8", optional = true, default-features = false } -zerocopy = { version = "0.8", optional = true, features = ["derive"] } - -[features] -alloc = [] -extra-sizes = [] - -[lints.clippy] -arithmetic_side_effects = "warn" -borrow_as_ptr = "warn" -cast_lossless = "warn" -cast_possible_truncation = "warn" -cast_possible_wrap = "warn" -cast_precision_loss = "warn" -cast_sign_loss = "warn" -checked_conversions = "warn" -doc_markdown = "warn" -from_iter_instead_of_collect = "warn" -manual_assert = "warn" -map_unwrap_or = "warn" -missing_errors_doc = "warn" -missing_panics_doc = "warn" -mod_module_files = "warn" -must_use_candidate = "warn" -implicit_saturating_sub = "warn" -panic = "warn" -panic_in_result_fn = "warn" -ptr_as_ptr = "warn" -redundant_closure_for_method_calls = "warn" -ref_as_ptr = "warn" -return_self_not_must_use = "warn" -semicolon_if_nothing_returned = "warn" -trivially_copy_pass_by_ref = "warn" -std_instead_of_alloc = "warn" -std_instead_of_core = "warn" -# undocumented_unsafe_blocks = "warn" TODO -unwrap_in_result = "warn" -unwrap_used = "warn" - -[lints.rust] -missing_copy_implementations = "warn" -missing_debug_implementations = "warn" -missing_docs = "warn" -trivial_casts = "warn" -trivial_numeric_casts = "warn" -unused_lifetimes = "warn" -unused_qualifications = "warn" - -[package.metadata.docs.rs] -all-features = true diff --git a/hqc-kem/hybrid-array-patch/LICENSE-APACHE b/hqc-kem/hybrid-array-patch/LICENSE-APACHE deleted file mode 100644 index 78173fa..0000000 --- a/hqc-kem/hybrid-array-patch/LICENSE-APACHE +++ /dev/null @@ -1,201 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - -Copyright [yyyy] [name of copyright owner] - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/hqc-kem/hybrid-array-patch/LICENSE-MIT b/hqc-kem/hybrid-array-patch/LICENSE-MIT deleted file mode 100644 index 5b37ba5..0000000 --- a/hqc-kem/hybrid-array-patch/LICENSE-MIT +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2022-2026 The RustCrypto Project Developers - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. diff --git a/hqc-kem/hybrid-array-patch/README.md b/hqc-kem/hybrid-array-patch/README.md deleted file mode 100644 index c65e596..0000000 --- a/hqc-kem/hybrid-array-patch/README.md +++ /dev/null @@ -1,68 +0,0 @@ -# [RustCrypto]: Hybrid Const Generic / Typenum Arrays - -[![crate][crate-image]][crate-link] -[![Docs][docs-image]][docs-link] -[![Build Status][build-image]][build-link] -![Apache2/MIT licensed][license-image] -![Rust Version][rustc-image] -[![Project Chat][chat-image]][chat-link] - -Hybrid array type combining const generics with the expressiveness of -[`typenum`]-based constraints, providing an alternative to [`generic-array`] -and a incremental transition path to const generics. - -## About - -This crate uses `typenum` to enable the following features which aren't yet -possible with the stable implementation of const generics: - -- [#60551: Associated constants in traits can not be used in const generics][rust-issue-60551] -- [#76560: Complex generic constants: `feature(generic_const_exprs)`][rust-issue-76560] - -Internally the crate is built on const generics and provides traits which make -it possible to convert between const generic types and `typenum` types. - -## Minimum Supported Rust Version (MSRV) Policy - -MSRV increases are not considered breaking changes and can happen in patch -releases. - -The crate MSRV accounts for all supported targets and crate feature -combinations, excluding explicitly unstable features. - -## License - -Licensed under either of: - -- [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0) -- [MIT license](http://opensource.org/licenses/MIT) - -at your option. - -### Contribution - -Unless you explicitly state otherwise, any contribution intentionally submitted -for inclusion in the work by you, as defined in the Apache-2.0 license, shall be -dual licensed as above, without any additional terms or conditions. - -[//]: # (badges) - -[crate-image]: https://img.shields.io/crates/v/hybrid-array?logo=rust -[crate-link]: https://crates.io/crates/hybrid-array -[docs-image]: https://docs.rs/hybrid-array/badge.svg -[docs-link]: https://docs.rs/hybrid-array/ -[build-image]: https://github.com/RustCrypto/hybrid-array/actions/workflows/hybrid-array.yml/badge.svg?branch=master -[build-link]: https://github.com/RustCrypto/hybrid-array/actions/workflows/hybrid-array.yml?query=branch:master -[license-image]: https://img.shields.io/badge/license-Apache2.0/MIT-blue.svg -[rustc-image]: https://img.shields.io/badge/rustc-1.85+-blue.svg -[chat-image]: https://img.shields.io/badge/zulip-join_chat-blue.svg -[chat-link]: https://rustcrypto.zulipchat.com/#narrow/stream/260052-utils - -[//]: # (links) - -[RustCrypto]: https://github.com/rustcrypto -[RustCrypto/utils#378]: https://github.com/RustCrypto/utils/issues/378 -[`typenum`]: https://github.com/paholg/typenum -[`generic-array`]: https://github.com/fizyk20/generic-array -[rust-issue-60551]: https://github.com/rust-lang/rust/issues/60551 -[rust-issue-76560]: https://github.com/rust-lang/rust/issues/76560 diff --git a/hqc-kem/hybrid-array-patch/src/flatten.rs b/hqc-kem/hybrid-array-patch/src/flatten.rs deleted file mode 100644 index f3159b4..0000000 --- a/hqc-kem/hybrid-array-patch/src/flatten.rs +++ /dev/null @@ -1,137 +0,0 @@ -use crate::{ - Array, ArraySize, - typenum::{Prod, Quot, U0, Unsigned}, -}; -use core::{ - mem::ManuallyDrop, - ops::{Div, Mul, Rem}, - ptr, -}; - -/// Defines a sequence of sequences that can be merged into a bigger overall sequence. -pub trait Flatten { - /// Size of the output array. - type OutputSize: ArraySize; - - /// Flatten array. - fn flatten(self) -> Array; -} - -impl Flatten> for Array, N> -where - N: ArraySize, - M: ArraySize + Mul, - Prod: ArraySize, -{ - type OutputSize = Prod; - - // SAFETY: this is the reverse transmute between [T; K*N] and [[T; K], M], which is guaranteed - // to be safe by the Rust memory layout of these types. - fn flatten(self) -> Array { - let whole = ManuallyDrop::new(self); - unsafe { ptr::read(whole.as_ptr().cast()) } - } -} - -/// Defines a sequence that can be split into a sequence of smaller sequences of uniform size. -pub trait Unflatten -where - M: ArraySize, -{ - /// Part of the array we're decomposing into. - type Part; - - /// Unflatten array into `Self::Part` chunks. - fn unflatten(self) -> Array; -} - -impl Unflatten for Array -where - N: ArraySize + Div + Rem, - M: ArraySize, - Quot: ArraySize, -{ - type Part = Array>; - - // SAFETY: this is doing the same thing as what is done in `Array::split`. - // Basically, this is doing transmute between [T; K*N] and [[T; K], M], which is guaranteed to - // be safe by the Rust memory layout of these types. - fn unflatten(self) -> Array { - let part_size = Quot::::USIZE; - let whole = ManuallyDrop::new(self); - Array::from_fn(|i| unsafe { - let offset = i.checked_mul(part_size).expect("overflow"); - ptr::read(whole.as_ptr().add(offset).cast()) - }) - } -} - -impl<'a, T, N, M> Unflatten for &'a Array -where - N: ArraySize + Div + Rem, - M: ArraySize, - Quot: ArraySize, -{ - type Part = &'a Array>; - - // SAFETY: this is doing the same thing as what is done in `Array::split`. - // Basically, this is doing transmute between [T; K*N] and [[T; K], M], which is guaranteed to - // be safe by the Rust memory layout of these types. - fn unflatten(self) -> Array { - let part_size = Quot::::USIZE; - let mut ptr: *const T = self.as_ptr(); - Array::from_fn(|_i| unsafe { - let part = &*(ptr.cast()); - ptr = ptr.add(part_size); - part - }) - } -} - -#[cfg(test)] -mod test { - use super::*; - use crate::{ - Array, - sizes::{U2, U5}, - }; - - #[test] - fn flatten() { - let flat: Array = Array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]); - let unflat2: Array, _> = Array([ - Array([1, 2]), - Array([3, 4]), - Array([5, 6]), - Array([7, 8]), - Array([9, 10]), - ]); - let unflat5: Array, _> = - Array([Array([1, 2, 3, 4, 5]), Array([6, 7, 8, 9, 10])]); - - // Flatten - let actual = unflat2.flatten(); - assert_eq!(flat, actual); - - let actual = unflat5.flatten(); - assert_eq!(flat, actual); - - // Unflatten - let actual: Array, U5> = flat.unflatten(); - assert_eq!(unflat2, actual); - - let actual: Array, U2> = flat.unflatten(); - assert_eq!(unflat5, actual); - - // Unflatten on references - let actual: Array<&Array, U5> = (&flat).unflatten(); - for (i, part) in actual.iter().enumerate() { - assert_eq!(&unflat2[i], *part); - } - - let actual: Array<&Array, U2> = (&flat).unflatten(); - for (i, part) in actual.iter().enumerate() { - assert_eq!(&unflat5[i], *part); - } - } -} diff --git a/hqc-kem/hybrid-array-patch/src/from_fn.rs b/hqc-kem/hybrid-array-patch/src/from_fn.rs deleted file mode 100644 index 8a507c6..0000000 --- a/hqc-kem/hybrid-array-patch/src/from_fn.rs +++ /dev/null @@ -1,106 +0,0 @@ -//! Support for constructing arrays using a provided generator function. - -use crate::{Array, ArraySize}; -use core::{ - convert::Infallible, - mem::{self, MaybeUninit}, - ptr, -}; - -impl Array -where - U: ArraySize, -{ - /// Create array where each array element `T` is returned by the `f` call. - #[inline] - pub fn from_fn(mut f: impl FnMut(usize) -> T) -> Self { - let Ok(ret) = Self::try_from_fn::(|n| Ok(f(n))); - ret - } - - /// Create array fallibly where each array element `T` is returned by the `f` call, or return - /// an error if any are encountered. - /// - /// # Errors - /// - /// Propagates the `E` type returned from the provided `F` in the event of error. - pub fn try_from_fn(f: impl FnMut(usize) -> Result) -> Result { - let mut array = Array::, U>::uninit(); - try_from_fn_erased(array.0.as_mut(), f)?; - - // SAFETY: if we got here, every element of the array was initialized - Ok(unsafe { array.assume_init() }) - } -} - -/// Fills a `MaybeUninit` slice using the given fallible generator function. -/// -/// Using a slice avoids monomorphizing for each array size. -#[inline] -fn try_from_fn_erased(buffer: &mut [MaybeUninit], mut f: F) -> Result<(), E> -where - F: FnMut(usize) -> Result, -{ - let mut guard = Guard { - array_mut: buffer, - initialized: 0, - }; - - while guard.initialized < guard.array_mut.len() { - let item = f(guard.initialized)?; - - // SAFETY: the loop's condition ensures we won't push too many items - unsafe { guard.push_unchecked(item) }; - } - - mem::forget(guard); - Ok(()) -} - -/// Drop guard which tracks the total number of initialized items, and handles dropping them in -/// the event a panic occurs. -/// -/// Use `mem::forget` when the array has been fully constructed. -struct Guard<'a, T> { - /// Array being constructed. - array_mut: &'a mut [MaybeUninit], - - /// Number of items in the array which have been initialized. - initialized: usize, -} - -impl Guard<'_, T> { - /// Push an item onto the guard, writing to its `MaybeUninit` slot and incrementing the - /// counter of the number of initialized items. - /// - /// # Safety - /// - /// This can only be called n-times for as many elements are in the slice. - #[inline] - pub unsafe fn push_unchecked(&mut self, item: T) { - // SAFETY: the `initialized` counter tracks the number of initialized items, so as long as - // this is called the correct number of times for the array size writes will always be - // in-bounds and to an uninitialized slot in the array. - unsafe { - self.array_mut - .get_unchecked_mut(self.initialized) - .write(item); - self.initialized = self.initialized.saturating_add(1); - } - } -} - -impl Drop for Guard<'_, T> { - fn drop(&mut self) { - debug_assert!(self.initialized <= self.array_mut.len()); - - // SAFETY: the loop only iterates over initialized items - unsafe { - let p: *mut T = self.array_mut.as_mut_ptr().cast(); - - for i in 0..self.initialized { - ptr::drop_in_place(p.add(i)); - } - } - } -} diff --git a/hqc-kem/hybrid-array-patch/src/iter.rs b/hqc-kem/hybrid-array-patch/src/iter.rs deleted file mode 100644 index 281bab1..0000000 --- a/hqc-kem/hybrid-array-patch/src/iter.rs +++ /dev/null @@ -1,107 +0,0 @@ -//! Support for constructing arrays using a provided iterator function and other iterator-related -//! functionality. - -use crate::{Array, ArraySize}; -use core::{ - fmt, - slice::{Iter, IterMut}, -}; - -/// Couldn't construct an array from an iterator because the number of items in the iterator -/// didn't match the array size. -#[derive(Clone, Copy, Debug)] -pub struct TryFromIteratorError; - -impl fmt::Display for TryFromIteratorError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str("iterator did not contain the correct number of items for the array size") - } -} - -impl core::error::Error for TryFromIteratorError {} - -impl Array -where - U: ArraySize, -{ - /// Construct an array from the given iterator, returning [`TryFromIteratorError`] in the event - /// that the number of items in the iterator does not match the array size. - /// - /// # Errors - /// - /// Returns [`TryFromIteratorError`] in the event the iterator does not return a number of - /// items which is exactly equal to the array size. - pub fn try_from_iter>(iter: I) -> Result { - let mut iter = iter.into_iter(); - let ret = Self::try_from_fn(|_| iter.next().ok_or(TryFromIteratorError))?; - - match iter.next() { - None => Ok(ret), - Some(_) => Err(TryFromIteratorError), - } - } -} - -impl FromIterator for Array -where - U: ArraySize, -{ - fn from_iter>(iter: I) -> Self { - let mut iter = iter.into_iter(); - let ret = Self::from_fn(|_| { - iter.next() - .expect("iterator should have enough items to fill array") - }); - - assert!( - iter.next().is_none(), - "too many items in iterator to fit in array" - ); - - ret - } -} - -impl IntoIterator for Array -where - U: ArraySize, -{ - type Item = T; - type IntoIter = as IntoIterator>::IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out of the array (from - /// start to end). - /// - /// The array cannot be used after calling this unless `T` implements `Copy`, so the whole - /// array is copied. - #[inline] - fn into_iter(self) -> Self::IntoIter { - self.0.into_iter() - } -} - -impl<'a, T, U> IntoIterator for &'a Array -where - U: ArraySize, -{ - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - #[inline] - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -impl<'a, T, U> IntoIterator for &'a mut Array -where - U: ArraySize, -{ - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - #[inline] - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} diff --git a/hqc-kem/hybrid-array-patch/src/lib.rs b/hqc-kem/hybrid-array-patch/src/lib.rs deleted file mode 100644 index afa3a03..0000000 --- a/hqc-kem/hybrid-array-patch/src/lib.rs +++ /dev/null @@ -1,1217 +0,0 @@ -#![no_std] -#![cfg_attr(docsrs, feature(doc_cfg))] -#![doc = include_str!("../README.md")] -#![doc( - html_logo_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg", - html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/meta/master/logo.svg" -)] - -//! ## Features -//! -//! This crate exposes the following feature flags. The default is NO features. -//! -//! - `bytemuck`: impls the `Pod` and `Zeroable` traits -//! - `serde`: impls the `Deserialize` and `Serialize` traits for `Array` -//! - `zeroize`: impls [`Zeroize`](https://docs.rs/zeroize/latest/zeroize/trait.Zeroize.html) for `Array` -//! -//! ## Usage -//! -//! The two core types in this crate are as follows: -//! -//! - [`Array`]: wrapper for `[T; N]` where `U` is an [`ArraySize`] provided by [`typenum`] -//! whose associated [`ArraySize::ArrayType`] determines the inner array size. -//! - [`ArrayN`]: type alias for [`Array`] which is const generic around `const N: usize`. -//! This provides a linkage between const generics and [`typenum`]. -//! -//! The [`Array`] type has an inner `pub [T; N]` field, which means writing a literal can be -//! expressed as follows: -//! -//! ``` -//! use hybrid_array::{Array, sizes::U4}; -//! -//! let arr: Array = Array([1, 2, 3, 4]); -//! ``` -//! -//! ### About [`typenum`] -//! -//! The [`typenum`] crate provides a type-level implementation of numbers and arithmetic operations. -//! -//! While [`typenum`] can be used to express arbitrary integers using the type system, the -//! `hybrid-array` crate is limited to the array sizes in the [`sizes`] module, which have -//! names like [`U0`][`sizes::U0`], [`U1`][`sizes::U1`], [`U2`][`sizes::U2`], [`U3`][`sizes::U3`], -//! etc. All supported sizes will have an impl of [`ArraySize`], which is the trait providing -//! linkage between [`typenum`]-based types and core arrays / const generics. -//! -//! [`ArraySize`] bounds on the [`typenum::Unsigned`] trait, which can be used to obtain integer -//! sizes of arrays via associated constants. For example, to obtain the size of an `ArraySize` as -//! a `usize`, use the associated [`typenum::Unsigned::USIZE`] constant. -//! -//! ### [`AsArrayRef`] and [`AsArrayMut`] traits -//! -//! These traits simplify obtaining references to [`Array`] and are impl'd for both [`Array`] -//! and `[T; N]`. They're analogous to traits like [`AsRef`] and [`AsMut`]. -//! -//! They make it possible to write code which uses `[T; N]` or `&[T; N]` in the external facing -//! API which can obtain references to `&Array` and call other functions which accept such -//! references, without the caller having to use `Array` in their code and while still supporting -//! generic sizes. -//! -//! For more information and a code example, see [`AsArrayRef`]. -//! -//! ## Relationship with `generic-array` -//! -//! `hybrid-array` is directly inspired by the [`generic-array`] crate. -//! -//! However, where `generic-array` predates const generics and uses a core which is built -//! on `unsafe` code, `hybrid-array`'s core implementation is built on safe code and const -//! generic implementations. This allows the inner `[T; N]` field of an `Array` to be `pub` as -//! noted above, and in general for the implementation to be significantly simpler, easier-to-audit, -//! and with significantly less use of `unsafe`. -//! -//! The only places `hybrid-array` uses unsafe are where it is absolutely necessary, primarily -//! for reference conversions between `Array` and `[T; N]`, and also to provide features -//! which are not yet stable in `core`/`std`, such as [`Array::try_from_fn`]. -//! -//! [`generic-array`]: https://docs.rs/generic-array -//! -//! ## Migrating from `generic-array` -//! -//! *NOTE: this guide assumes a migration from `generic-array` v0.14* -//! -//! `hybrid-array` has been designed to largely be a drop-in replacement for -//! `generic-array`, albeit with a public inner array type and significantly less -//! `unsafe` code. -//! -//! The bulk of the migration work can be accomplished by making the following find/replace-style -//! substitutions in your `.rs` files: -//! -//! - Replace `generic_array` with `hybrid_array` -//! - Replace `GenericArray` with `Array` -//! - Replace `ArrayLength` with `ArraySize` -//! - Replace usages of the `Concat` and `Split` traits with [`Array::concat`] and [`Array::split`] -//! - Replace `>::ArrayType` with `::ArrayType` -//! - Replace usages of the `arr![N; A, B, C]` macro with `Array([A, B, C])` -//! -//! If you have any questions, please -//! [start a discussion](https://github.com/RustCrypto/hybrid-array/discussions). - -#[cfg(feature = "alloc")] -extern crate alloc; - -pub mod sizes; - -mod flatten; -mod from_fn; -mod iter; -mod traits; - -#[cfg(feature = "serde")] -mod serde; - -pub use crate::{ - flatten::{Flatten, Unflatten}, - iter::TryFromIteratorError, - traits::*, -}; -pub use typenum; - -use core::{ - array::TryFromSliceError, - borrow::{Borrow, BorrowMut}, - cmp::Ordering, - fmt::{self, Debug}, - hash::{Hash, Hasher}, - mem::{self, ManuallyDrop, MaybeUninit}, - ops::{Add, Deref, DerefMut, Index, IndexMut, Sub}, - ptr, - slice::{self, Iter, IterMut}, -}; -use typenum::{Diff, Sum, U1}; - -#[cfg(feature = "arbitrary")] -use arbitrary::Arbitrary; - -#[cfg(feature = "bytemuck")] -use bytemuck::{Pod, Zeroable}; - -#[cfg(feature = "zeroize")] -use zeroize::{Zeroize, ZeroizeOnDrop}; - -#[cfg(feature = "zerocopy")] -use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; - -/// Type alias for [`Array`] which is const generic around a size `N`, ala `[T; N]`. -pub type ArrayN = Array::Size>; - -/// [`Array`] is a newtype for an inner `[T; N]` array where `N` is determined by a generic -/// [`ArraySize`] parameter, which is a marker trait for a numeric value determined by ZSTs that -/// impl the [`typenum::Unsigned`] trait. -/// -/// The inner `[T; N]` field is `pub` which means it's possible to write [`Array`] literals like: -/// -/// [`Array`] is defined as `repr(transparent)`, meaning it can be used anywhere an appropriately -/// sized `[T; N]` type is used in unsafe code / FFI. -/// -/// ``` -/// use hybrid_array::{Array, sizes::U3}; -/// -/// let arr: Array = Array([1, 2, 3]); -/// ``` -#[cfg_attr( - feature = "zerocopy", - derive(IntoBytes, FromBytes, Immutable, Unaligned, KnownLayout) -)] -#[repr(transparent)] -pub struct Array(pub U::ArrayType); - -type SplitResult = (Array, Array>); -type SplitRefResult<'a, T, U, N> = (&'a Array, &'a Array>); -type SplitRefMutResult<'a, T, U, N> = (&'a mut Array, &'a mut Array>); - -impl Array -where - U: ArraySize, -{ - /// Returns a slice containing the entire array. Equivalent to `&s[..]`. - #[inline] - pub const fn as_slice(&self) -> &[T] { - // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` - // newtype for `[T; N]`. - unsafe { slice::from_raw_parts(self.as_ptr(), U::USIZE) } - } - - /// Returns a mutable slice containing the entire array. Equivalent to `&mut s[..]`. - #[inline] - pub const fn as_mut_slice(&mut self) -> &mut [T] { - // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` - // newtype for `[T; N]`. - unsafe { slice::from_raw_parts_mut(self.as_mut_ptr(), U::USIZE) } - } - - /// Returns a pointer to the start of the array. - pub const fn as_ptr(&self) -> *const T { - ptr::from_ref::(self).cast::() - } - - /// Returns a mutable pointer to the start of the array. - pub const fn as_mut_ptr(&mut self) -> *mut T { - ptr::from_mut::(self).cast::() - } - - /// Returns an iterator over the array. - #[inline] - pub fn iter(&self) -> Iter<'_, T> { - self.as_slice().iter() - } - - /// Returns an iterator that allows modifying each value. - #[inline] - pub fn iter_mut(&mut self) -> IterMut<'_, T> { - self.as_mut().iter_mut() - } - - /// Returns an array of the same size as `self`, with function `f` applied to each element in - /// order. - pub fn map(self, f: F) -> Array - where - F: FnMut(T) -> O, - { - self.into_iter().map(f).collect() - } - - /// Concatenates `self` with `other`. - #[inline] - pub fn concat(self, other: Array) -> Array> - where - N: ArraySize, - U: Add, - Sum: ArraySize, - { - let mut c = Array::uninit(); - let (left, right) = c.split_at_mut(self.len()); - for (val, dst) in self.into_iter().zip(left) { - dst.write(val); - } - for (val, dst) in other.into_iter().zip(right) { - dst.write(val); - } - // SAFETY: We wrote to every element of `c`. - unsafe { c.assume_init() } - } - - /// Splits `self` at index `N` in two arrays. - /// - /// New arrays hold the original memory from `self`. - #[inline] - pub fn split(self) -> SplitResult - where - U: Sub, - N: ArraySize, - Diff: ArraySize, - { - unsafe { - let array = ManuallyDrop::new(self); - let head = ptr::read(array.as_ptr().cast()); - let tail = ptr::read(array.as_ptr().add(N::USIZE).cast()); - (head, tail) - } - } - - /// Splits `&self` at index `N` in two array references. - #[inline] - pub fn split_ref(&self) -> SplitRefResult<'_, T, U, N> - where - U: Sub, - N: ArraySize, - Diff: ArraySize, - { - unsafe { - let array_ptr = self.as_ptr(); - let head = &*array_ptr.cast(); - let tail = &*array_ptr.add(N::USIZE).cast(); - (head, tail) - } - } - - /// Splits `&mut self` at index `N` in two mutable array references. - #[inline] - pub fn split_ref_mut(&mut self) -> SplitRefMutResult<'_, T, U, N> - where - U: Sub, - N: ArraySize, - Diff: ArraySize, - { - unsafe { - let array_ptr = self.as_mut_ptr(); - let head = &mut *array_ptr.cast(); - let tail = &mut *array_ptr.add(N::USIZE).cast(); - (head, tail) - } - } - - /// Get a reference to an array from a slice, if the slice is exactly the size of the array. - /// - /// Returns `None` if the slice's length is not exactly equal to the array size. - #[inline] - #[must_use] - pub const fn slice_as_array(slice: &[T]) -> Option<&Self> { - if slice.len() == U::USIZE { - // SAFETY: `Self` is ensured to be layout-identical to `[T; U::USIZE]`, and immediately - // above we validated that `slice` is also layout-identical to `[T; U::USIZE]`, - // therefore the cast is valid. - unsafe { Some(&*slice.as_ptr().cast()) } - } else { - None - } - } - - /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the - /// array. - /// - /// Returns `None` if the slice's length is not exactly equal to the array size. - #[inline] - #[must_use] - pub const fn slice_as_mut_array(slice: &mut [T]) -> Option<&mut Self> { - if slice.len() == U::USIZE { - // SAFETY: `Self` is ensured to be layout-identical to `[T; U::USIZE]`, and immediately - // above we validated that `slice` is also layout-identical to `[T; U::USIZE]`, - // therefore the cast is valid. - unsafe { Some(&mut *slice.as_mut_ptr().cast()) } - } else { - None - } - } - - /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning - /// of the slice, and a remainder slice with length strictly less than `U`. - /// - /// # Panics - /// Panics if `U` is 0. - #[allow(clippy::arithmetic_side_effects)] - #[inline] - pub const fn slice_as_chunks(buf: &[T]) -> (&[Self], &[T]) { - assert!(U::USIZE != 0, "chunk size must be non-zero"); - // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus - // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, - // thus overflow on multiplication and underflow on substraction are impossible. - let chunks_len = buf.len() / U::USIZE; - let tail_pos = U::USIZE * chunks_len; - let tail_len = buf.len() - tail_pos; - unsafe { - let ptr = buf.as_ptr(); - let chunks = slice::from_raw_parts(ptr.cast(), chunks_len); - let tail = slice::from_raw_parts(ptr.add(tail_pos), tail_len); - (chunks, tail) - } - } - - /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning - /// of the slice, and a remainder slice with length strictly less than `U`. - /// - /// # Panics - /// Panics if `U` is 0. - #[allow(clippy::arithmetic_side_effects)] - #[inline] - pub const fn slice_as_chunks_mut(buf: &mut [T]) -> (&mut [Self], &mut [T]) { - assert!(U::USIZE != 0, "chunk size must be non-zero"); - // Arithmetic safety: we have checked that `N::USIZE` is not zero, thus - // division always returns correct result. `tail_pos` can not be bigger than `buf.len()`, - // thus overflow on multiplication and underflow on substraction are impossible. - let chunks_len = buf.len() / U::USIZE; - let tail_pos = U::USIZE * chunks_len; - let tail_len = buf.len() - tail_pos; - unsafe { - let ptr = buf.as_mut_ptr(); - let chunks = slice::from_raw_parts_mut(ptr.cast(), chunks_len); - let tail = slice::from_raw_parts_mut(ptr.add(tail_pos), tail_len); - (chunks, tail) - } - } - - /// Obtain a flattened slice from a slice of array chunks. - /// - /// # Panics - /// - if the length calculation for the flattened slice overflows - #[inline] - pub const fn slice_as_flattened(slice: &[Self]) -> &[T] { - let len = slice - .len() - .checked_mul(U::USIZE) - .expect("slice len overflow"); - - // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` - // newtype for `[T; N]`. - unsafe { slice::from_raw_parts(slice.as_ptr().cast(), len) } - } - - /// Obtain a mutable flattened slice from a mutable slice of array chunks. - /// - /// # Panics - /// - if the length calculation for the flattened slice overflows - #[inline] - pub const fn slice_as_flattened_mut(slice: &mut [Self]) -> &mut [T] { - let len = slice - .len() - .checked_mul(U::USIZE) - .expect("slice len overflow"); - - // SAFETY: `[T]` is layout-identical to `Array`, which is a `repr(transparent)` - // newtype for `[T; N]`. - unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), len) } - } -} - -impl Array { - /// Convert a reference to `T` into a reference to an [`Array`] of length [`U1`]. - pub const fn from_ref(r: &T) -> &Self { - Self::cast_from_core(core::array::from_ref(r)) - } - - /// Converts a mutable reference to `T` into a mutable reference to an [`Array`] of - /// length [`U1`]. - pub const fn from_mut(r: &mut T) -> &mut Self { - Self::cast_from_core_mut(core::array::from_mut(r)) - } -} - -impl Array, V> -where - U: ArraySize, - V: ArraySize, -{ - /// Takes a `&Array, >>`, and flattens it to a `&[T]`. - /// - /// # Panics - /// - /// This panics if the length of the resulting slice would overflow a `usize`. - /// - /// This is only possible when flattening a slice of arrays of zero-sized - /// types, and thus tends to be irrelevant in practice. If - /// `size_of::() > 0`, this will never panic. - /// - /// # Examples - /// - /// ``` - /// use hybrid_array::{Array, typenum::{U0, U2, U3, U5, U10}}; - /// - /// let a: Array, U2> = Array([Array([1, 2, 3]), Array([4, 5, 6])]); - /// assert_eq!(a.as_flattened(), &[1, 2, 3, 4, 5, 6]); - /// - /// let b: Array, U3> = Array([Array([1, 2]), Array([3, 4]), Array([5, 6])]); - /// assert_eq!(a.as_flattened(), b.as_flattened()); - /// - /// let c: Array<[usize; 2], U3> = Array([[1, 2], [3, 4], [5, 6]]); - /// assert_eq!(a.as_flattened(), c.as_flattened()); - /// - /// let slice_of_empty_arrays: &Array, U0> = &Array::from_fn(|_| Array([1, 2, 3, 4, 5])); - /// assert!(slice_of_empty_arrays.as_flattened().is_empty()); - /// - /// let empty_slice_of_arrays: &Array, U0> = &Array([]); - /// assert!(empty_slice_of_arrays.as_flattened().is_empty()); - /// ``` - pub const fn as_flattened(&self) -> &[T] { - Array::slice_as_flattened(self.as_slice()) - } - - /// Takes a `&mut Array,M>`, and flattens it to a `&mut [T]`. - /// - /// # Panics - /// - /// This panics if the length of the resulting slice would overflow a `usize`. - /// - /// This is only possible when flattening a slice of arrays of zero-sized - /// types, and thus tends to be irrelevant in practice. If - /// `size_of::() > 0`, this will never panic. - /// - /// # Examples - /// - /// ``` - /// use hybrid_array::{Array, typenum::U3}; - /// - /// fn add_5_to_all(slice: &mut [i32]) { - /// for i in slice { - /// *i += 5; - /// } - /// } - /// - /// let mut array: Array, U3> = Array([Array([1_i32, 2, 3]), Array([4, 5, 6]), Array([7, 8, 9])]); - /// add_5_to_all(array.as_flattened_mut()); - /// assert_eq!(array, Array([Array([6, 7, 8]), Array([9, 10, 11]), Array([12, 13, 14])])); - /// ``` - pub const fn as_flattened_mut(&mut self) -> &mut [T] { - Array::slice_as_flattened_mut(self.as_mut_slice()) - } -} - -// Impls which depend on the inner array type being `[T; N]`. -impl Array -where - U: ArraySize = [T; N]>, -{ - /// Cast a reference to a core array to an [`Array`] reference. - #[inline] - pub const fn cast_from_core(array_ref: &[T; N]) -> &Self { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` - unsafe { &*array_ref.as_ptr().cast() } - } - - /// Cast a mutable reference to a core array to an [`Array`] reference. - #[inline] - pub const fn cast_from_core_mut(array_ref: &mut [T; N]) -> &mut Self { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; 1]` - unsafe { &mut *array_ref.as_mut_ptr().cast() } - } - - /// Transform slice to slice of core array type. - #[inline] - pub const fn cast_slice_from_core(slice: &[[T; N]]) -> &[Self] { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` - unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len()) } - } - - /// Transform mutable slice to mutable slice of core array type. - #[inline] - pub const fn cast_slice_from_core_mut(slice: &mut [[T; N]]) -> &mut [Self] { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` - unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) } - } - - /// Transform slice to slice of core array type. - #[inline] - pub const fn cast_slice_to_core(slice: &[Self]) -> &[[T; N]] { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` - unsafe { slice::from_raw_parts(slice.as_ptr().cast(), slice.len()) } - } - - /// Transform mutable slice to mutable slice of core array type. - #[inline] - pub const fn cast_slice_to_core_mut(slice: &mut [Self]) -> &mut [[T; N]] { - // SAFETY: `Self` is a `repr(transparent)` newtype for `[T; N]` - unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr().cast(), slice.len()) } - } -} - -impl Array, U> -where - U: ArraySize, -{ - /// Create an uninitialized array of [`MaybeUninit`]s for the given type. - #[must_use] - pub const fn uninit() -> Array, U> { - // SAFETY: `Array` is a `repr(transparent)` newtype for `[MaybeUninit, N]`, i.e. an - // array of uninitialized memory mediated via the `MaybeUninit` interface, where the inner - // type is constrained by `ArraySize` impls which can only be added by this crate. - // - // Calling `uninit().assume_init()` triggers the `clippy::uninit_assumed_init` lint, but - // as just mentioned the inner type we're "assuming init" for is `[MaybeUninit, N]`, - // i.e. an array of uninitialized memory, which is always valid because definitionally no - // initialization is required of uninitialized memory. - #[allow(clippy::uninit_assumed_init)] - Self(unsafe { MaybeUninit::uninit().assume_init() }) - } - - /// Extract the values from an array of `MaybeUninit` containers. - /// - /// # Safety - /// - /// It is up to the caller to guarantee that all elements of the array are in an initialized - /// state. - #[inline] - pub unsafe fn assume_init(self) -> Array { - unsafe { - // `Array` is a `repr(transparent)` newtype for a generic inner type which is constrained to - // be `[T; N]` by the `ArraySize` impls in this crate. - // - // Since we're working with a type-erased inner type and ultimately trying to convert - // `[MaybeUninit; N]` to `[T; N]`, we can't use simpler approaches like a pointer cast - // or `transmute`, since the compiler can't prove to itself that the size will be the same. - // - // We've taken unique ownership of `self`, which is a `MaybeUninit` array, and as such we - // don't need to worry about `Drop` impls because `MaybeUninit` does not impl `Drop`. - // Since we have unique ownership of `self`, it's okay to make a copy because we're throwing - // the original away (and this should all get optimized to a noop by the compiler, anyway). - mem::transmute_copy(&self) - } - } -} - -impl AsRef> for Array -where - U: ArraySize, -{ - #[inline] - fn as_ref(&self) -> &Self { - self - } -} - -impl AsRef<[T]> for Array -where - U: ArraySize, -{ - #[inline] - fn as_ref(&self) -> &[T] { - self.0.as_ref() - } -} - -impl AsRef<[T; N]> for Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn as_ref(&self) -> &[T; N] { - &self.0 - } -} - -impl AsMut<[T]> for Array -where - U: ArraySize, -{ - #[inline] - fn as_mut(&mut self) -> &mut [T] { - self.0.as_mut() - } -} - -impl AsMut<[T; N]> for Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn as_mut(&mut self) -> &mut [T; N] { - &mut self.0 - } -} - -impl Borrow<[T]> for Array -where - U: ArraySize, -{ - #[inline] - fn borrow(&self) -> &[T] { - self.0.as_ref() - } -} - -impl Borrow<[T; N]> for Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn borrow(&self) -> &[T; N] { - &self.0 - } -} - -impl BorrowMut<[T]> for Array -where - U: ArraySize, -{ - #[inline] - fn borrow_mut(&mut self) -> &mut [T] { - self.0.as_mut() - } -} - -impl BorrowMut<[T; N]> for Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn borrow_mut(&mut self) -> &mut [T; N] { - &mut self.0 - } -} - -impl Clone for Array -where - T: Clone, - U: ArraySize, -{ - #[inline] - fn clone(&self) -> Self { - Self::from_fn(|n| self.0.as_ref()[n].clone()) - } -} - -impl Copy for Array -where - T: Copy, - U: ArraySize, - U::ArrayType: Copy, -{ -} - -impl Debug for Array -where - T: Debug, - U: ArraySize, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Array").field(&self.0.as_ref()).finish() - } -} - -impl Default for Array -where - T: Default, - U: ArraySize, -{ - #[inline] - fn default() -> Self { - Self::from_fn(|_| Default::default()) - } -} - -impl Deref for Array -where - U: ArraySize, -{ - type Target = [T]; - - #[inline] - fn deref(&self) -> &[T] { - self.0.as_ref() - } -} - -impl DerefMut for Array -where - U: ArraySize, -{ - #[inline] - fn deref_mut(&mut self) -> &mut [T] { - self.0.as_mut() - } -} - -impl Eq for Array -where - T: Eq, - U: ArraySize, -{ -} - -impl From<[T; N]> for Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(arr: [T; N]) -> Array { - Array(arr) - } -} - -impl From> for [T; N] -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(arr: Array) -> [T; N] { - arr.0 - } -} - -impl<'a, T, U, const N: usize> From<&'a [T; N]> for &'a Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(array_ref: &'a [T; N]) -> &'a Array { - Array::cast_from_core(array_ref) - } -} - -impl<'a, T, U, const N: usize> From<&'a Array> for &'a [T; N] -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(array_ref: &'a Array) -> &'a [T; N] { - array_ref.as_ref() - } -} - -impl<'a, T, U, const N: usize> From<&'a mut [T; N]> for &'a mut Array -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(array_ref: &'a mut [T; N]) -> &'a mut Array { - Array::cast_from_core_mut(array_ref) - } -} - -impl<'a, T, U, const N: usize> From<&'a mut Array> for &'a mut [T; N] -where - U: ArraySize = [T; N]>, -{ - #[inline] - fn from(array_ref: &'a mut Array) -> &'a mut [T; N] { - array_ref.as_mut() - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::boxed::Box<[T]> -where - U: ArraySize, -{ - #[inline] - fn from(array: Array) -> alloc::boxed::Box<[T]> { - array.into_iter().collect() - } -} - -#[cfg(feature = "alloc")] -impl From<&Array> for alloc::boxed::Box<[T]> -where - T: Clone, - U: ArraySize, -{ - #[inline] - fn from(array: &Array) -> alloc::boxed::Box<[T]> { - array.as_slice().into() - } -} - -#[cfg(feature = "alloc")] -impl From> for alloc::vec::Vec -where - U: ArraySize, -{ - #[inline] - fn from(array: Array) -> alloc::vec::Vec { - array.into_iter().collect() - } -} - -#[cfg(feature = "alloc")] -impl From<&Array> for alloc::vec::Vec -where - T: Clone, - U: ArraySize, -{ - #[inline] - fn from(array: &Array) -> alloc::vec::Vec { - array.as_slice().into() - } -} - -impl Hash for Array -where - T: Hash, - U: ArraySize, -{ - #[inline] - fn hash(&self, state: &mut H) { - self.0.as_ref().hash(state); - } -} - -impl Index for Array -where - [T]: Index, - U: ArraySize, -{ - type Output = <[T] as Index>::Output; - - #[inline] - fn index(&self, index: I) -> &Self::Output { - Index::index(self.as_slice(), index) - } -} - -impl IndexMut for Array -where - [T]: IndexMut, - U: ArraySize, -{ - #[inline] - fn index_mut(&mut self, index: I) -> &mut Self::Output { - IndexMut::index_mut(self.as_mut_slice(), index) - } -} - -impl PartialEq for Array -where - T: PartialEq, - U: ArraySize, -{ - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.as_ref().eq(other.0.as_ref()) - } -} - -impl PartialEq<[T; N]> for Array -where - T: PartialEq, - U: ArraySize = [T; N]>, -{ - #[inline] - fn eq(&self, other: &[T; N]) -> bool { - self.0.eq(other) - } -} - -impl PartialEq> for [T; N] -where - T: PartialEq, - U: ArraySize = [T; N]>, -{ - #[inline] - fn eq(&self, other: &Array) -> bool { - self.eq(&other.0) - } -} - -impl PartialOrd for Array -where - T: PartialOrd, - U: ArraySize, -{ - #[inline] - fn partial_cmp(&self, other: &Self) -> Option { - self.0.as_ref().partial_cmp(other.0.as_ref()) - } -} - -impl Ord for Array -where - T: Ord, - U: ArraySize, -{ - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.0.as_ref().cmp(other.0.as_ref()) - } -} - -/// SAFETY: `Array` is a `repr(transparent)` newtype for `[T; N]`, so as long as `T: Send` it should -/// also be `Send`. -unsafe impl Send for Array where T: Send {} - -/// SAFETY: `Array` is a `repr(transparent)` newtype for `[T; N]`, so as long as `T: Sync` it should -/// also be `Sync`. -unsafe impl Sync for Array where T: Sync {} - -impl<'a, T, U> TryFrom<&'a [T]> for &'a Array -where - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(slice: &'a [T]) -> Result { - check_slice_length::(slice)?; - - // SAFETY: `Array` is a `repr(transparent)` newtype for a core - // array with length checked above. - Ok(unsafe { &*slice.as_ptr().cast() }) - } -} - -impl<'a, T, U> TryFrom<&'a mut [T]> for &'a mut Array -where - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(slice: &'a mut [T]) -> Result { - check_slice_length::(slice)?; - - // SAFETY: `Array` is a `repr(transparent)` newtype for a core - // array with length checked above. - Ok(unsafe { &mut *slice.as_mut_ptr().cast() }) - } -} - -impl<'a, T, U> TryFrom<&'a [T]> for Array -where - Self: Clone, - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(slice: &'a [T]) -> Result, TryFromSliceError> { - <&'a Self>::try_from(slice).cloned() - } -} - -#[cfg(feature = "alloc")] -impl TryFrom> for Array -where - Self: Clone, - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(b: alloc::boxed::Box<[T]>) -> Result { - Self::try_from(&*b) - } -} - -#[cfg(feature = "alloc")] -impl<'a, T, U> TryFrom<&'a alloc::boxed::Box<[T]>> for Array -where - Self: Clone, - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(b: &'a alloc::boxed::Box<[T]>) -> Result { - Self::try_from(&**b) - } -} - -#[cfg(feature = "alloc")] -impl TryFrom> for Array -where - Self: Clone, - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(v: alloc::vec::Vec) -> Result { - Self::try_from(v.as_slice()) - } -} - -#[cfg(feature = "alloc")] -impl<'a, T, U> TryFrom<&'a alloc::vec::Vec> for Array -where - Self: Clone, - U: ArraySize, -{ - type Error = TryFromSliceError; - - #[inline] - fn try_from(v: &'a alloc::vec::Vec) -> Result { - Self::try_from(v.as_slice()) - } -} - -// Deprecated legacy methods to ease migrations from `generic-array` -impl Array -where - U: ArraySize, -{ - /// Convert the given slice into a reference to a hybrid array. - /// - /// # Panics - /// - /// Panics if the slice's length doesn't match the array type. - #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] - #[inline] - pub fn from_slice(slice: &[T]) -> &Self { - slice.try_into().expect("slice length mismatch") - } - - /// Convert the given mutable slice to a mutable reference to a hybrid array. - /// - /// # Panics - /// - /// Panics if the slice's length doesn't match the array type. - #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] - #[inline] - pub fn from_mut_slice(slice: &mut [T]) -> &mut Self { - slice.try_into().expect("slice length mismatch") - } - - /// Clone the contents of the slice as a new hybrid array. - /// - /// # Panics - /// - /// Panics if the slice's length doesn't match the array type. - #[deprecated(since = "0.2.0", note = "use `TryFrom` instead")] - #[inline] - pub fn clone_from_slice(slice: &[T]) -> Self - where - Self: Clone, - { - slice.try_into().expect("slice length mismatch") - } -} - -#[cfg(feature = "arbitrary")] -impl<'a, T, U> Arbitrary<'a> for Array -where - T: Arbitrary<'a>, - U: ArraySize, -{ - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result { - Self::try_from_fn(|_n| Arbitrary::arbitrary(u)) - } -} - -#[cfg(feature = "bytemuck")] -unsafe impl Pod for Array -where - T: Pod, - U: ArraySize, - U::ArrayType: Copy, -{ -} - -#[cfg(feature = "bytemuck")] -unsafe impl Zeroable for Array -where - T: Zeroable, - U: ArraySize, -{ -} - -#[cfg(feature = "ctutils")] -impl ctutils::CtAssign for Array -where - [T]: ctutils::CtAssign, - U: ArraySize, -{ - #[inline] - fn ct_assign(&mut self, other: &Self, choice: ctutils::Choice) { - self.as_mut_slice().ct_assign(other.as_slice(), choice); - } -} - -#[cfg(feature = "ctutils")] -impl ctutils::CtSelect for Array -where - U: ArraySize, - U::ArrayType: ctutils::CtSelect, -{ - #[inline] - fn ct_select(&self, other: &Self, choice: ctutils::Choice) -> Self { - Self(self.0.ct_select(&other.0, choice)) - } -} - -#[cfg(feature = "ctutils")] -impl ctutils::CtEq for Array -where - U: ArraySize, - U::ArrayType: ctutils::CtEq, -{ - #[inline] - fn ct_eq(&self, other: &Self) -> ctutils::Choice { - self.0.ct_eq(&other.0) - } -} - -#[cfg(feature = "subtle")] -impl subtle::ConditionallySelectable for Array -where - Self: Copy, - T: subtle::ConditionallySelectable, - U: ArraySize, -{ - #[inline] - fn conditional_select(a: &Self, b: &Self, choice: subtle::Choice) -> Self { - let mut output = *a; - output.conditional_assign(b, choice); - output - } - - fn conditional_assign(&mut self, other: &Self, choice: subtle::Choice) { - for (a_i, b_i) in self.iter_mut().zip(other) { - a_i.conditional_assign(b_i, choice); - } - } -} - -#[cfg(feature = "subtle")] -impl subtle::ConstantTimeEq for Array -where - T: subtle::ConstantTimeEq, - U: ArraySize, -{ - #[inline] - fn ct_eq(&self, other: &Self) -> subtle::Choice { - self.iter() - .zip(other.iter()) - .fold(subtle::Choice::from(1), |acc, (a, b)| acc & a.ct_eq(b)) - } -} - -#[cfg(feature = "zeroize")] -impl Zeroize for Array -where - T: Zeroize, - U: ArraySize, -{ - #[inline] - fn zeroize(&mut self) { - self.0.as_mut().iter_mut().zeroize(); - } -} - -#[cfg(feature = "zeroize")] -impl ZeroizeOnDrop for Array -where - T: ZeroizeOnDrop, - U: ArraySize, -{ -} - -/// Generate a [`TryFromSliceError`] if the slice doesn't match the given length. -#[cfg_attr(debug_assertions, allow(clippy::panic_in_result_fn))] -fn check_slice_length(slice: &[T]) -> Result<(), TryFromSliceError> { - debug_assert_eq!(Array::<(), U>::default().len(), U::USIZE); - - if slice.len() != U::USIZE { - // Hack: `TryFromSliceError` lacks a public constructor - <&[T; 1]>::try_from([].as_slice())?; - - #[cfg(debug_assertions)] - unreachable!(); - } - - Ok(()) -} diff --git a/hqc-kem/hybrid-array-patch/src/serde.rs b/hqc-kem/hybrid-array-patch/src/serde.rs deleted file mode 100644 index 4647581..0000000 --- a/hqc-kem/hybrid-array-patch/src/serde.rs +++ /dev/null @@ -1,71 +0,0 @@ -//! Support for serializing and deserializing `Array` using `serde`. - -use crate::{Array, ArraySize}; -use core::{fmt, marker::PhantomData}; -use serde::{ - de::{self, Deserialize, Deserializer, SeqAccess, Visitor}, - ser::{Serialize, SerializeTuple, Serializer}, -}; - -impl<'de, T, U> Deserialize<'de> for Array -where - T: Deserialize<'de>, - U: ArraySize, -{ - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - T: Deserialize<'de>, - { - struct ArrayVisitor { - element: PhantomData, - } - - impl<'de, T, U> Visitor<'de> for ArrayVisitor> - where - T: Deserialize<'de>, - U: ArraySize, - { - type Value = Array; - - fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(formatter, "an array of length {}", U::USIZE) - } - - fn visit_seq(self, mut seq: A) -> Result, A::Error> - where - A: SeqAccess<'de>, - { - Array::::try_from_fn(|i| { - seq.next_element()? - .ok_or_else(|| de::Error::invalid_length(i, &self)) - }) - } - } - - let visitor = ArrayVisitor { - element: PhantomData, - }; - - deserializer.deserialize_tuple(U::USIZE, visitor) - } -} - -impl Serialize for Array -where - T: Serialize, - U: ArraySize, -{ - fn serialize(&self, serializer: S) -> Result - where - S: Serializer, - { - let mut seq = serializer.serialize_tuple(U::USIZE)?; - - for elem in self { - seq.serialize_element(elem)?; - } - - seq.end() - } -} diff --git a/hqc-kem/hybrid-array-patch/src/sizes.rs b/hqc-kem/hybrid-array-patch/src/sizes.rs deleted file mode 100644 index d42ba6d..0000000 --- a/hqc-kem/hybrid-array-patch/src/sizes.rs +++ /dev/null @@ -1,1164 +0,0 @@ -//! Supported array sizes: [`typenum::Unsigned`] types with an [`ArraySize`] impl. -//! -//! We support the following array sizes by default: -//! -//! - 0-512 -//! - 528-1024 (multiples of 16) -//! - 2048, 4096, 8192 -//! -//! When the `extra-sizes` feature is enabled: 1040-4064 (multiples of 32) - -use super::{ArraySize, AssocArraySize}; - -#[cfg(feature = "extra-sizes")] -pub use extra_sizes::*; - -/// Implement the `ArraySize` and `AssocArraySize` traits for a given list of `N => UN, ...` -/// mappings. -/// -/// `N` is used over `UN::USIZE` in order to improve compile times (avoids associated constant -/// resolution) -macro_rules! impl_array_sizes { - ($testname:ident, $($len:expr => $ty:ident),+ $(,)?) => { - $( - // SAFETY: we depend on `<$ty as Unsigned>::USIZE == $len` for unsafe pointer casts. - // We ensure this property holds by having the macro write a test for that below. - unsafe impl ArraySize for $ty { - type ArrayType = [T; $len]; - } - - impl AssocArraySize for [T; $len] { - type Size = $ty; - } - )+ - - #[test] - fn $testname() { - use typenum::Unsigned; - $( - assert_eq!($len, $ty::USIZE); - )+ - } - }; -} - -/// Implement array sizes, also importing the relevant constants. -macro_rules! impl_array_sizes_with_import { - ($testname:ident, $($len:expr => $ty:ident),+ $(,)?) => { - $( - pub use typenum::consts::$ty; - )+ - impl_array_sizes!($testname, $($len => $ty),+); - }; -} - -impl_array_sizes_with_import! { - base, - 0 => U0, - 1 => U1, - 2 => U2, - 3 => U3, - 4 => U4, - 5 => U5, - 6 => U6, - 7 => U7, - 8 => U8, - 9 => U9, - 10 => U10, - 11 => U11, - 12 => U12, - 13 => U13, - 14 => U14, - 15 => U15, - 16 => U16, - 17 => U17, - 18 => U18, - 19 => U19, - 20 => U20, - 21 => U21, - 22 => U22, - 23 => U23, - 24 => U24, - 25 => U25, - 26 => U26, - 27 => U27, - 28 => U28, - 29 => U29, - 30 => U30, - 31 => U31, - 32 => U32, - 33 => U33, - 34 => U34, - 35 => U35, - 36 => U36, - 37 => U37, - 38 => U38, - 39 => U39, - 40 => U40, - 41 => U41, - 42 => U42, - 43 => U43, - 44 => U44, - 45 => U45, - 46 => U46, - 47 => U47, - 48 => U48, - 49 => U49, - 50 => U50, - 51 => U51, - 52 => U52, - 53 => U53, - 54 => U54, - 55 => U55, - 56 => U56, - 57 => U57, - 58 => U58, - 59 => U59, - 60 => U60, - 61 => U61, - 62 => U62, - 63 => U63, - 64 => U64, - 65 => U65, - 66 => U66, - 67 => U67, - 68 => U68, - 69 => U69, - 70 => U70, - 71 => U71, - 72 => U72, - 73 => U73, - 74 => U74, - 75 => U75, - 76 => U76, - 77 => U77, - 78 => U78, - 79 => U79, - 80 => U80, - 81 => U81, - 82 => U82, - 83 => U83, - 84 => U84, - 85 => U85, - 86 => U86, - 87 => U87, - 88 => U88, - 89 => U89, - 90 => U90, - 91 => U91, - 92 => U92, - 93 => U93, - 94 => U94, - 95 => U95, - 96 => U96, - 97 => U97, - 98 => U98, - 99 => U99, - 100 => U100, - 101 => U101, - 102 => U102, - 103 => U103, - 104 => U104, - 105 => U105, - 106 => U106, - 107 => U107, - 108 => U108, - 109 => U109, - 110 => U110, - 111 => U111, - 112 => U112, - 113 => U113, - 114 => U114, - 115 => U115, - 116 => U116, - 117 => U117, - 118 => U118, - 119 => U119, - 120 => U120, - 121 => U121, - 122 => U122, - 123 => U123, - 124 => U124, - 125 => U125, - 126 => U126, - 127 => U127, - 128 => U128, - 129 => U129, - 130 => U130, - 131 => U131, - 132 => U132, - 133 => U133, - 134 => U134, - 135 => U135, - 136 => U136, - 137 => U137, - 138 => U138, - 139 => U139, - 140 => U140, - 141 => U141, - 142 => U142, - 143 => U143, - 144 => U144, - 145 => U145, - 146 => U146, - 147 => U147, - 148 => U148, - 149 => U149, - 150 => U150, - 151 => U151, - 152 => U152, - 153 => U153, - 154 => U154, - 155 => U155, - 156 => U156, - 157 => U157, - 158 => U158, - 159 => U159, - 160 => U160, - 161 => U161, - 162 => U162, - 163 => U163, - 164 => U164, - 165 => U165, - 166 => U166, - 167 => U167, - 168 => U168, - 169 => U169, - 170 => U170, - 171 => U171, - 172 => U172, - 173 => U173, - 174 => U174, - 175 => U175, - 176 => U176, - 177 => U177, - 178 => U178, - 179 => U179, - 180 => U180, - 181 => U181, - 182 => U182, - 183 => U183, - 184 => U184, - 185 => U185, - 186 => U186, - 187 => U187, - 188 => U188, - 189 => U189, - 190 => U190, - 191 => U191, - 192 => U192, - 193 => U193, - 194 => U194, - 195 => U195, - 196 => U196, - 197 => U197, - 198 => U198, - 199 => U199, - 200 => U200, - 201 => U201, - 202 => U202, - 203 => U203, - 204 => U204, - 205 => U205, - 206 => U206, - 207 => U207, - 208 => U208, - 209 => U209, - 210 => U210, - 211 => U211, - 212 => U212, - 213 => U213, - 214 => U214, - 215 => U215, - 216 => U216, - 217 => U217, - 218 => U218, - 219 => U219, - 220 => U220, - 221 => U221, - 222 => U222, - 223 => U223, - 224 => U224, - 225 => U225, - 226 => U226, - 227 => U227, - 228 => U228, - 229 => U229, - 230 => U230, - 231 => U231, - 232 => U232, - 233 => U233, - 234 => U234, - 235 => U235, - 236 => U236, - 237 => U237, - 238 => U238, - 239 => U239, - 240 => U240, - 241 => U241, - 242 => U242, - 243 => U243, - 244 => U244, - 245 => U245, - 246 => U246, - 247 => U247, - 248 => U248, - 249 => U249, - 250 => U250, - 251 => U251, - 252 => U252, - 253 => U253, - 254 => U254, - 255 => U255, - 256 => U256, - 257 => U257, - 258 => U258, - 259 => U259, - 260 => U260, - 261 => U261, - 262 => U262, - 263 => U263, - 264 => U264, - 265 => U265, - 266 => U266, - 267 => U267, - 268 => U268, - 269 => U269, - 270 => U270, - 271 => U271, - 272 => U272, - 273 => U273, - 274 => U274, - 275 => U275, - 276 => U276, - 277 => U277, - 278 => U278, - 279 => U279, - 280 => U280, - 281 => U281, - 282 => U282, - 283 => U283, - 284 => U284, - 285 => U285, - 286 => U286, - 287 => U287, - 288 => U288, - 289 => U289, - 290 => U290, - 291 => U291, - 292 => U292, - 293 => U293, - 294 => U294, - 295 => U295, - 296 => U296, - 297 => U297, - 298 => U298, - 299 => U299, - 300 => U300, - 301 => U301, - 302 => U302, - 303 => U303, - 304 => U304, - 305 => U305, - 306 => U306, - 307 => U307, - 308 => U308, - 309 => U309, - 310 => U310, - 311 => U311, - 312 => U312, - 313 => U313, - 314 => U314, - 315 => U315, - 316 => U316, - 317 => U317, - 318 => U318, - 319 => U319, - 320 => U320, - 321 => U321, - 322 => U322, - 323 => U323, - 324 => U324, - 325 => U325, - 326 => U326, - 327 => U327, - 328 => U328, - 329 => U329, - 330 => U330, - 331 => U331, - 332 => U332, - 333 => U333, - 334 => U334, - 335 => U335, - 336 => U336, - 337 => U337, - 338 => U338, - 339 => U339, - 340 => U340, - 341 => U341, - 342 => U342, - 343 => U343, - 344 => U344, - 345 => U345, - 346 => U346, - 347 => U347, - 348 => U348, - 349 => U349, - 350 => U350, - 351 => U351, - 352 => U352, - 353 => U353, - 354 => U354, - 355 => U355, - 356 => U356, - 357 => U357, - 358 => U358, - 359 => U359, - 360 => U360, - 361 => U361, - 362 => U362, - 363 => U363, - 364 => U364, - 365 => U365, - 366 => U366, - 367 => U367, - 368 => U368, - 369 => U369, - 370 => U370, - 371 => U371, - 372 => U372, - 373 => U373, - 374 => U374, - 375 => U375, - 376 => U376, - 377 => U377, - 378 => U378, - 379 => U379, - 380 => U380, - 381 => U381, - 382 => U382, - 383 => U383, - 384 => U384, - 385 => U385, - 386 => U386, - 387 => U387, - 388 => U388, - 389 => U389, - 390 => U390, - 391 => U391, - 392 => U392, - 393 => U393, - 394 => U394, - 395 => U395, - 396 => U396, - 397 => U397, - 398 => U398, - 399 => U399, - 400 => U400, - 401 => U401, - 402 => U402, - 403 => U403, - 404 => U404, - 405 => U405, - 406 => U406, - 407 => U407, - 408 => U408, - 409 => U409, - 410 => U410, - 411 => U411, - 412 => U412, - 413 => U413, - 414 => U414, - 415 => U415, - 416 => U416, - 417 => U417, - 418 => U418, - 419 => U419, - 420 => U420, - 421 => U421, - 422 => U422, - 423 => U423, - 424 => U424, - 425 => U425, - 426 => U426, - 427 => U427, - 428 => U428, - 429 => U429, - 430 => U430, - 431 => U431, - 432 => U432, - 433 => U433, - 434 => U434, - 435 => U435, - 436 => U436, - 437 => U437, - 438 => U438, - 439 => U439, - 440 => U440, - 441 => U441, - 442 => U442, - 443 => U443, - 444 => U444, - 445 => U445, - 446 => U446, - 447 => U447, - 448 => U448, - 449 => U449, - 450 => U450, - 451 => U451, - 452 => U452, - 453 => U453, - 454 => U454, - 455 => U455, - 456 => U456, - 457 => U457, - 458 => U458, - 459 => U459, - 460 => U460, - 461 => U461, - 462 => U462, - 463 => U463, - 464 => U464, - 465 => U465, - 466 => U466, - 467 => U467, - 468 => U468, - 469 => U469, - 470 => U470, - 471 => U471, - 472 => U472, - 473 => U473, - 474 => U474, - 475 => U475, - 476 => U476, - 477 => U477, - 478 => U478, - 479 => U479, - 480 => U480, - 481 => U481, - 482 => U482, - 483 => U483, - 484 => U484, - 485 => U485, - 486 => U486, - 487 => U487, - 488 => U488, - 489 => U489, - 490 => U490, - 491 => U491, - 492 => U492, - 493 => U493, - 494 => U494, - 495 => U495, - 496 => U496, - 497 => U497, - 498 => U498, - 499 => U499, - 500 => U500, - 501 => U501, - 502 => U502, - 503 => U503, - 504 => U504, - 505 => U505, - 506 => U506, - 507 => U507, - 508 => U508, - 509 => U509, - 510 => U510, - 511 => U511, - 512 => U512, - 528 => U528, - 536 => U536, - 544 => U544, - 560 => U560, - 568 => U568, - 576 => U576, - 592 => U592, - 608 => U608, - 624 => U624, - 640 => U640, - 656 => U656, - 672 => U672, - 688 => U688, - 704 => U704, - 720 => U720, - 736 => U736, - 752 => U752, - 768 => U768, - 784 => U784, - 800 => U800, - 816 => U816, - 832 => U832, - 848 => U848, - 864 => U864, - 880 => U880, - 896 => U896, - 912 => U912, - 928 => U928, - 944 => U944, - 960 => U960, - 976 => U976, - 992 => U992, - 1008 => U1008, - 1024 => U1024, - 2048 => U2048, - 4096 => U4096, - 8192 => U8192, -} - -/// Additional typenum size aliases beyond what are normally provided. -/// -/// These are defined using their component bits rather than `Add` to avoid conflicting impls. -#[cfg(feature = "extra-sizes")] -#[allow(missing_docs)] -mod extra_sizes { - use super::{ArraySize, AssocArraySize}; - use typenum::{ - UInt, UTerm, - consts::{B0, B1}, - }; - - // This macro constructs a UInt type from a sequence of bits. The bits are interpreted as the - // little-endian representation of the integer in question. For example, uint!(1 1 0 1 0 0 1) is - // U75 (not U105). - macro_rules! uint { - () => { UTerm }; - (0 $($bs:tt)*) => { UInt< uint!($($bs)*), B0 > }; - (1 $($bs:tt)*) => { UInt< uint!($($bs)*), B1 > }; - } - - pub type U1040 = uint!(0 0 0 0 1 0 0 0 0 0 1); - pub type U1056 = uint!(0 0 0 0 0 1 0 0 0 0 1); - pub type U1072 = uint!(0 0 0 0 1 1 0 0 0 0 1); - pub type U1088 = uint!(0 0 0 0 0 0 1 0 0 0 1); - pub type U1104 = uint!(0 0 0 0 1 0 1 0 0 0 1); - pub type U1120 = uint!(0 0 0 0 0 1 1 0 0 0 1); - pub type U1136 = uint!(0 0 0 0 1 1 1 0 0 0 1); - pub type U1152 = uint!(0 0 0 0 0 0 0 1 0 0 1); - pub type U1168 = uint!(0 0 0 0 1 0 0 1 0 0 1); - pub type U1184 = uint!(0 0 0 0 0 1 0 1 0 0 1); - pub type U1200 = uint!(0 0 0 0 1 1 0 1 0 0 1); - pub type U1216 = uint!(0 0 0 0 0 0 1 1 0 0 1); - pub type U1232 = uint!(0 0 0 0 1 0 1 1 0 0 1); - pub type U1248 = uint!(0 0 0 0 0 1 1 1 0 0 1); - pub type U1264 = uint!(0 0 0 0 1 1 1 1 0 0 1); - pub type U1280 = uint!(0 0 0 0 0 0 0 0 1 0 1); - pub type U1296 = uint!(0 0 0 0 1 0 0 0 1 0 1); - pub type U1312 = uint!(0 0 0 0 0 1 0 0 1 0 1); - pub type U1328 = uint!(0 0 0 0 1 1 0 0 1 0 1); - pub type U1344 = uint!(0 0 0 0 0 0 1 0 1 0 1); - pub type U1360 = uint!(0 0 0 0 1 0 1 0 1 0 1); - pub type U1376 = uint!(0 0 0 0 0 1 1 0 1 0 1); - pub type U1392 = uint!(0 0 0 0 1 1 1 0 1 0 1); - pub type U1408 = uint!(0 0 0 0 0 0 0 1 1 0 1); - pub type U1424 = uint!(0 0 0 0 1 0 0 1 1 0 1); - pub type U1440 = uint!(0 0 0 0 0 1 0 1 1 0 1); - pub type U1456 = uint!(0 0 0 0 1 1 0 1 1 0 1); - pub type U1472 = uint!(0 0 0 0 0 0 1 1 1 0 1); - pub type U1488 = uint!(0 0 0 0 1 0 1 1 1 0 1); - pub type U1504 = uint!(0 0 0 0 0 1 1 1 1 0 1); - pub type U1520 = uint!(0 0 0 0 1 1 1 1 1 0 1); - pub type U1536 = uint!(0 0 0 0 0 0 0 0 0 1 1); - pub type U1552 = uint!(0 0 0 0 1 0 0 0 0 1 1); - pub type U1568 = uint!(0 0 0 0 0 1 0 0 0 1 1); - pub type U1584 = uint!(0 0 0 0 1 1 0 0 0 1 1); - pub type U1600 = uint!(0 0 0 0 0 0 1 0 0 1 1); - pub type U1616 = uint!(0 0 0 0 1 0 1 0 0 1 1); - pub type U1632 = uint!(0 0 0 0 0 1 1 0 0 1 1); - pub type U1648 = uint!(0 0 0 0 1 1 1 0 0 1 1); - pub type U1664 = uint!(0 0 0 0 0 0 0 1 0 1 1); - pub type U1680 = uint!(0 0 0 0 1 0 0 1 0 1 1); - pub type U1696 = uint!(0 0 0 0 0 1 0 1 0 1 1); - pub type U1712 = uint!(0 0 0 0 1 1 0 1 0 1 1); - pub type U1728 = uint!(0 0 0 0 0 0 1 1 0 1 1); - pub type U1744 = uint!(0 0 0 0 1 0 1 1 0 1 1); - pub type U1760 = uint!(0 0 0 0 0 1 1 1 0 1 1); - pub type U1776 = uint!(0 0 0 0 1 1 1 1 0 1 1); - pub type U1792 = uint!(0 0 0 0 0 0 0 0 1 1 1); - pub type U1808 = uint!(0 0 0 0 1 0 0 0 1 1 1); - pub type U1824 = uint!(0 0 0 0 0 1 0 0 1 1 1); - pub type U1840 = uint!(0 0 0 0 1 1 0 0 1 1 1); - pub type U1856 = uint!(0 0 0 0 0 0 1 0 1 1 1); - pub type U1872 = uint!(0 0 0 0 1 0 1 0 1 1 1); - pub type U1888 = uint!(0 0 0 0 0 1 1 0 1 1 1); - pub type U1904 = uint!(0 0 0 0 1 1 1 0 1 1 1); - pub type U1920 = uint!(0 0 0 0 0 0 0 1 1 1 1); - pub type U1936 = uint!(0 0 0 0 1 0 0 1 1 1 1); - pub type U1952 = uint!(0 0 0 0 0 1 0 1 1 1 1); - pub type U1968 = uint!(0 0 0 0 1 1 0 1 1 1 1); - pub type U1984 = uint!(0 0 0 0 0 0 1 1 1 1 1); - pub type U2000 = uint!(0 0 0 0 1 0 1 1 1 1 1); - pub type U2016 = uint!(0 0 0 0 0 1 1 1 1 1 1); - pub type U2032 = uint!(0 0 0 0 1 1 1 1 1 1 1); - pub type U2064 = uint!(0 0 0 0 1 0 0 0 0 0 0 1); - pub type U2080 = uint!(0 0 0 0 0 1 0 0 0 0 0 1); - pub type U2096 = uint!(0 0 0 0 1 1 0 0 0 0 0 1); - pub type U2112 = uint!(0 0 0 0 0 0 1 0 0 0 0 1); - pub type U2128 = uint!(0 0 0 0 1 0 1 0 0 0 0 1); - pub type U2144 = uint!(0 0 0 0 0 1 1 0 0 0 0 1); - pub type U2160 = uint!(0 0 0 0 1 1 1 0 0 0 0 1); - pub type U2176 = uint!(0 0 0 0 0 0 0 1 0 0 0 1); - pub type U2192 = uint!(0 0 0 0 1 0 0 1 0 0 0 1); - pub type U2208 = uint!(0 0 0 0 0 1 0 1 0 0 0 1); - pub type U2224 = uint!(0 0 0 0 1 1 0 1 0 0 0 1); - pub type U2240 = uint!(0 0 0 0 0 0 1 1 0 0 0 1); - pub type U2256 = uint!(0 0 0 0 1 0 1 1 0 0 0 1); - pub type U2272 = uint!(0 0 0 0 0 1 1 1 0 0 0 1); - pub type U2288 = uint!(0 0 0 0 1 1 1 1 0 0 0 1); - pub type U2304 = uint!(0 0 0 0 0 0 0 0 1 0 0 1); - pub type U2320 = uint!(0 0 0 0 1 0 0 0 1 0 0 1); - pub type U2336 = uint!(0 0 0 0 0 1 0 0 1 0 0 1); - pub type U2352 = uint!(0 0 0 0 1 1 0 0 1 0 0 1); - pub type U2368 = uint!(0 0 0 0 0 0 1 0 1 0 0 1); - pub type U2384 = uint!(0 0 0 0 1 0 1 0 1 0 0 1); - pub type U2400 = uint!(0 0 0 0 0 1 1 0 1 0 0 1); - pub type U2416 = uint!(0 0 0 0 1 1 1 0 1 0 0 1); - pub type U2432 = uint!(0 0 0 0 0 0 0 1 1 0 0 1); - pub type U2448 = uint!(0 0 0 0 1 0 0 1 1 0 0 1); - pub type U2464 = uint!(0 0 0 0 0 1 0 1 1 0 0 1); - pub type U2480 = uint!(0 0 0 0 1 1 0 1 1 0 0 1); - pub type U2496 = uint!(0 0 0 0 0 0 1 1 1 0 0 1); - pub type U2512 = uint!(0 0 0 0 1 0 1 1 1 0 0 1); - pub type U2528 = uint!(0 0 0 0 0 1 1 1 1 0 0 1); - pub type U2544 = uint!(0 0 0 0 1 1 1 1 1 0 0 1); - pub type U2560 = uint!(0 0 0 0 0 0 0 0 0 1 0 1); - pub type U2576 = uint!(0 0 0 0 1 0 0 0 0 1 0 1); - pub type U2592 = uint!(0 0 0 0 0 1 0 0 0 1 0 1); - pub type U2608 = uint!(0 0 0 0 1 1 0 0 0 1 0 1); - pub type U2624 = uint!(0 0 0 0 0 0 1 0 0 1 0 1); - pub type U2640 = uint!(0 0 0 0 1 0 1 0 0 1 0 1); - pub type U2656 = uint!(0 0 0 0 0 1 1 0 0 1 0 1); - pub type U2672 = uint!(0 0 0 0 1 1 1 0 0 1 0 1); - pub type U2688 = uint!(0 0 0 0 0 0 0 1 0 1 0 1); - pub type U2704 = uint!(0 0 0 0 1 0 0 1 0 1 0 1); - pub type U2720 = uint!(0 0 0 0 0 1 0 1 0 1 0 1); - pub type U2736 = uint!(0 0 0 0 1 1 0 1 0 1 0 1); - pub type U2752 = uint!(0 0 0 0 0 0 1 1 0 1 0 1); - pub type U2768 = uint!(0 0 0 0 1 0 1 1 0 1 0 1); - pub type U2784 = uint!(0 0 0 0 0 1 1 1 0 1 0 1); - pub type U2800 = uint!(0 0 0 0 1 1 1 1 0 1 0 1); - pub type U2816 = uint!(0 0 0 0 0 0 0 0 1 1 0 1); - pub type U2832 = uint!(0 0 0 0 1 0 0 0 1 1 0 1); - pub type U2848 = uint!(0 0 0 0 0 1 0 0 1 1 0 1); - pub type U2864 = uint!(0 0 0 0 1 1 0 0 1 1 0 1); - pub type U2880 = uint!(0 0 0 0 0 0 1 0 1 1 0 1); - pub type U2896 = uint!(0 0 0 0 1 0 1 0 1 1 0 1); - pub type U2912 = uint!(0 0 0 0 0 1 1 0 1 1 0 1); - pub type U2928 = uint!(0 0 0 0 1 1 1 0 1 1 0 1); - pub type U2944 = uint!(0 0 0 0 0 0 0 1 1 1 0 1); - pub type U2960 = uint!(0 0 0 0 1 0 0 1 1 1 0 1); - pub type U2976 = uint!(0 0 0 0 0 1 0 1 1 1 0 1); - pub type U2992 = uint!(0 0 0 0 1 1 0 1 1 1 0 1); - pub type U3008 = uint!(0 0 0 0 0 0 1 1 1 1 0 1); - pub type U3024 = uint!(0 0 0 0 1 0 1 1 1 1 0 1); - pub type U3040 = uint!(0 0 0 0 0 1 1 1 1 1 0 1); - pub type U3056 = uint!(0 0 0 0 1 1 1 1 1 1 0 1); - pub type U3072 = uint!(0 0 0 0 0 0 0 0 0 0 1 1); - pub type U3088 = uint!(0 0 0 0 1 0 0 0 0 0 1 1); - pub type U3104 = uint!(0 0 0 0 0 1 0 0 0 0 1 1); - pub type U3120 = uint!(0 0 0 0 1 1 0 0 0 0 1 1); - pub type U3136 = uint!(0 0 0 0 0 0 1 0 0 0 1 1); - pub type U3152 = uint!(0 0 0 0 1 0 1 0 0 0 1 1); - pub type U3168 = uint!(0 0 0 0 0 1 1 0 0 0 1 1); - pub type U3184 = uint!(0 0 0 0 1 1 1 0 0 0 1 1); - pub type U3200 = uint!(0 0 0 0 0 0 0 1 0 0 1 1); - pub type U3216 = uint!(0 0 0 0 1 0 0 1 0 0 1 1); - pub type U3232 = uint!(0 0 0 0 0 1 0 1 0 0 1 1); - pub type U3248 = uint!(0 0 0 0 1 1 0 1 0 0 1 1); - pub type U3264 = uint!(0 0 0 0 0 0 1 1 0 0 1 1); - pub type U3280 = uint!(0 0 0 0 1 0 1 1 0 0 1 1); - pub type U3296 = uint!(0 0 0 0 0 1 1 1 0 0 1 1); - pub type U3312 = uint!(0 0 0 0 1 1 1 1 0 0 1 1); - pub type U3328 = uint!(0 0 0 0 0 0 0 0 1 0 1 1); - pub type U3344 = uint!(0 0 0 0 1 0 0 0 1 0 1 1); - pub type U3360 = uint!(0 0 0 0 0 1 0 0 1 0 1 1); - pub type U3376 = uint!(0 0 0 0 1 1 0 0 1 0 1 1); - pub type U3392 = uint!(0 0 0 0 0 0 1 0 1 0 1 1); - pub type U3408 = uint!(0 0 0 0 1 0 1 0 1 0 1 1); - pub type U3424 = uint!(0 0 0 0 0 1 1 0 1 0 1 1); - pub type U3440 = uint!(0 0 0 0 1 1 1 0 1 0 1 1); - pub type U3456 = uint!(0 0 0 0 0 0 0 1 1 0 1 1); - pub type U3472 = uint!(0 0 0 0 1 0 0 1 1 0 1 1); - pub type U3488 = uint!(0 0 0 0 0 1 0 1 1 0 1 1); - pub type U3504 = uint!(0 0 0 0 1 1 0 1 1 0 1 1); - pub type U3520 = uint!(0 0 0 0 0 0 1 1 1 0 1 1); - pub type U3536 = uint!(0 0 0 0 1 0 1 1 1 0 1 1); - pub type U3552 = uint!(0 0 0 0 0 1 1 1 1 0 1 1); - pub type U3568 = uint!(0 0 0 0 1 1 1 1 1 0 1 1); - pub type U3584 = uint!(0 0 0 0 0 0 0 0 0 1 1 1); - pub type U3600 = uint!(0 0 0 0 1 0 0 0 0 1 1 1); - pub type U3616 = uint!(0 0 0 0 0 1 0 0 0 1 1 1); - pub type U3632 = uint!(0 0 0 0 1 1 0 0 0 1 1 1); - pub type U3648 = uint!(0 0 0 0 0 0 1 0 0 1 1 1); - pub type U3664 = uint!(0 0 0 0 1 0 1 0 0 1 1 1); - pub type U3680 = uint!(0 0 0 0 0 1 1 0 0 1 1 1); - pub type U3696 = uint!(0 0 0 0 1 1 1 0 0 1 1 1); - pub type U3712 = uint!(0 0 0 0 0 0 0 1 0 1 1 1); - pub type U3728 = uint!(0 0 0 0 1 0 0 1 0 1 1 1); - pub type U3744 = uint!(0 0 0 0 0 1 0 1 0 1 1 1); - pub type U3760 = uint!(0 0 0 0 1 1 0 1 0 1 1 1); - pub type U3776 = uint!(0 0 0 0 0 0 1 1 0 1 1 1); - pub type U3792 = uint!(0 0 0 0 1 0 1 1 0 1 1 1); - pub type U3808 = uint!(0 0 0 0 0 1 1 1 0 1 1 1); - pub type U3824 = uint!(0 0 0 0 1 1 1 1 0 1 1 1); - pub type U3840 = uint!(0 0 0 0 0 0 0 0 1 1 1 1); - pub type U3856 = uint!(0 0 0 0 1 0 0 0 1 1 1 1); - pub type U3872 = uint!(0 0 0 0 0 1 0 0 1 1 1 1); - pub type U3888 = uint!(0 0 0 0 1 1 0 0 1 1 1 1); - pub type U3904 = uint!(0 0 0 0 0 0 1 0 1 1 1 1); - pub type U3920 = uint!(0 0 0 0 1 0 1 0 1 1 1 1); - pub type U3936 = uint!(0 0 0 0 0 1 1 0 1 1 1 1); - pub type U3952 = uint!(0 0 0 0 1 1 1 0 1 1 1 1); - pub type U3968 = uint!(0 0 0 0 0 0 0 1 1 1 1 1); - pub type U3984 = uint!(0 0 0 0 1 0 0 1 1 1 1 1); - pub type U4000 = uint!(0 0 0 0 0 1 0 1 1 1 1 1); - pub type U4016 = uint!(0 0 0 0 1 1 0 1 1 1 1 1); - pub type U4032 = uint!(0 0 0 0 0 0 1 1 1 1 1 1); - pub type U4048 = uint!(0 0 0 0 1 0 1 1 1 1 1 1); - pub type U4064 = uint!(0 0 0 0 0 1 1 1 1 1 1 1); - pub type U4080 = uint!(0 0 0 0 1 1 1 1 1 1 1 1); - - // ML-DSA sizes - // - // Includes the public key, private key, and signature sizes not covered elsewhere, as well as - // some intermediate value sizes. - pub type U2420 = uint!(0 0 1 0 1 1 1 0 1 0 0 1); - pub type U3309 = uint!(1 0 1 1 0 1 1 1 0 0 1 1); - pub type U4480 = uint!(0 0 0 0 0 0 0 1 1 0 0 0 1); - pub type U4544 = uint!(0 0 0 0 0 0 1 1 1 0 0 0 1); - pub type U4595 = uint!(1 1 0 0 1 1 1 1 1 0 0 0 1); - pub type U4627 = uint!(1 1 0 0 1 0 0 0 0 1 0 0 1); - pub type U4896 = uint!(0 0 0 0 0 1 0 0 1 1 0 0 1); - - // SLH-DSA sizes - pub type U7856 = uint!(0 0 0 0 1 1 0 1 0 1 1 1 1); - pub type U16224 = uint!(0 0 0 0 0 1 1 0 1 1 1 1 1 1); - pub type U17088 = uint!(0 0 0 0 0 0 1 1 0 1 0 0 0 0 1); - pub type U29792 = uint!(0 0 0 0 0 1 1 0 0 0 1 0 1 1 1); - pub type U35664 = uint!(0 0 0 0 1 0 1 0 1 1 0 1 0 0 0 1); - pub type U49856 = uint!(0 0 0 0 0 0 1 1 0 1 0 0 0 0 1 1); - - // Kemeleon ML-KEM Encoding sizes - pub type U749 = uint!(1 0 1 1 0 1 1 1 0 1); - pub type U781 = uint!(1 0 1 1 0 0 0 0 1 1); - pub type U877 = uint!(1 0 1 1 0 1 1 0 1 1); - pub type U1124 = uint!(0 0 1 0 0 1 1 0 0 0 1); - pub type U1156 = uint!(0 0 1 0 0 0 0 1 0 0 1); - pub type U1252 = uint!(0 0 1 0 0 1 1 1 0 0 1); - pub type U1498 = uint!(0 1 0 1 1 0 1 1 1 0 1); - pub type U1530 = uint!(0 1 0 1 1 1 1 1 1 0 1); - pub type U1658 = uint!(0 1 0 1 1 1 1 0 0 1 1); - - // LMS sizes - pub type U2047 = uint!(1 1 1 1 1 1 1 1 1 1 1); - pub type U2180 = uint!(0 0 1 0 0 0 0 1 0 0 0 1); - pub type U4292 = uint!(0 0 1 0 0 0 1 1 0 0 0 0 1); - pub type U8516 = uint!(0 0 1 0 0 0 1 0 1 0 0 0 0 1); - - // FrodoKEM640 sizes - - pub type U9616 = uint!(0 0 0 0 1 0 0 1 1 0 1 0 0 1); - pub type U19888 = uint!(0 0 0 0 1 1 0 1 1 0 1 1 0 0 1); - pub type U9720 = uint!(0 0 0 1 1 1 1 1 1 0 1 0 0 1); - pub type U9752 = uint!(0 0 0 1 1 0 0 0 0 1 1 0 0 1); - - // FrodoKEM976 sizes - pub type U15632 = uint!(0 0 0 0 1 0 0 0 1 0 1 1 1 1); - pub type U31296 = uint!(0 0 0 0 0 0 1 0 0 1 0 1 1 1 1); - pub type U15744 = uint!(0 0 0 0 0 0 0 1 1 0 1 1 1 1); - pub type U15792 = uint!(0 0 0 0 1 1 0 1 1 0 1 1 1 1); - - // FrodoKEM1344 sizes - pub type U21520 = uint!(0 0 0 0 1 0 0 0 0 0 1 0 1 0 1); - pub type U43088 = uint!(0 0 0 0 1 0 1 0 0 0 0 1 0 1 0 1); - pub type U21632 = uint!(0 0 0 0 0 0 0 1 0 0 1 0 1 0 1); - pub type U21696 = uint!(0 0 0 0 0 0 1 1 0 0 1 0 1 0 1); - - // HKDF-Expand common max output sizes - pub type U8160 = uint!(0 0 0 0 0 1 1 1 1 1 1 1 1); - pub type U12240 = uint!(0 0 0 0 1 0 1 1 1 1 1 1 0 1); - pub type U16320 = uint!(0 0 0 0 0 0 1 1 1 1 1 1 1 1); - - impl_array_sizes! { - base_extra, - 1040 => U1040, - 1056 => U1056, - 1072 => U1072, - 1088 => U1088, - 1104 => U1104, - 1120 => U1120, - 1136 => U1136, - 1152 => U1152, - 1168 => U1168, - 1184 => U1184, - 1200 => U1200, - 1216 => U1216, - 1232 => U1232, - 1248 => U1248, - 1264 => U1264, - 1280 => U1280, - 1296 => U1296, - 1312 => U1312, - 1328 => U1328, - 1344 => U1344, - 1360 => U1360, - 1376 => U1376, - 1392 => U1392, - 1408 => U1408, - 1424 => U1424, - 1440 => U1440, - 1456 => U1456, - 1472 => U1472, - 1488 => U1488, - 1504 => U1504, - 1520 => U1520, - 1536 => U1536, - 1552 => U1552, - 1568 => U1568, - 1584 => U1584, - 1600 => U1600, - 1616 => U1616, - 1632 => U1632, - 1648 => U1648, - 1664 => U1664, - 1680 => U1680, - 1696 => U1696, - 1712 => U1712, - 1728 => U1728, - 1744 => U1744, - 1760 => U1760, - 1776 => U1776, - 1792 => U1792, - 1808 => U1808, - 1824 => U1824, - 1840 => U1840, - 1856 => U1856, - 1872 => U1872, - 1888 => U1888, - 1904 => U1904, - 1920 => U1920, - 1936 => U1936, - 1952 => U1952, - 1968 => U1968, - 1984 => U1984, - 2000 => U2000, - 2016 => U2016, - 2032 => U2032, - 2064 => U2064, - 2080 => U2080, - 2096 => U2096, - 2112 => U2112, - 2128 => U2128, - 2144 => U2144, - 2160 => U2160, - 2176 => U2176, - 2192 => U2192, - 2208 => U2208, - 2224 => U2224, - 2240 => U2240, - 2256 => U2256, - 2272 => U2272, - 2288 => U2288, - 2304 => U2304, - 2320 => U2320, - 2336 => U2336, - 2352 => U2352, - 2368 => U2368, - 2384 => U2384, - 2400 => U2400, - 2416 => U2416, - 2432 => U2432, - 2448 => U2448, - 2464 => U2464, - 2480 => U2480, - 2496 => U2496, - 2512 => U2512, - 2528 => U2528, - 2544 => U2544, - 2560 => U2560, - 2576 => U2576, - 2592 => U2592, - 2608 => U2608, - 2624 => U2624, - 2640 => U2640, - 2656 => U2656, - 2672 => U2672, - 2688 => U2688, - 2704 => U2704, - 2720 => U2720, - 2736 => U2736, - 2752 => U2752, - 2768 => U2768, - 2784 => U2784, - 2800 => U2800, - 2816 => U2816, - 2832 => U2832, - 2848 => U2848, - 2864 => U2864, - 2880 => U2880, - 2896 => U2896, - 2912 => U2912, - 2928 => U2928, - 2944 => U2944, - 2960 => U2960, - 2976 => U2976, - 2992 => U2992, - 3008 => U3008, - 3024 => U3024, - 3040 => U3040, - 3056 => U3056, - 3072 => U3072, - 3088 => U3088, - 3104 => U3104, - 3120 => U3120, - 3136 => U3136, - 3152 => U3152, - 3168 => U3168, - 3184 => U3184, - 3200 => U3200, - 3216 => U3216, - 3232 => U3232, - 3248 => U3248, - 3264 => U3264, - 3280 => U3280, - 3296 => U3296, - 3312 => U3312, - 3328 => U3328, - 3344 => U3344, - 3360 => U3360, - 3376 => U3376, - 3392 => U3392, - 3408 => U3408, - 3424 => U3424, - 3440 => U3440, - 3456 => U3456, - 3472 => U3472, - 3488 => U3488, - 3504 => U3504, - 3520 => U3520, - 3536 => U3536, - 3552 => U3552, - 3568 => U3568, - 3584 => U3584, - 3600 => U3600, - 3616 => U3616, - 3632 => U3632, - 3648 => U3648, - 3664 => U3664, - 3680 => U3680, - 3696 => U3696, - 3712 => U3712, - 3728 => U3728, - 3744 => U3744, - 3760 => U3760, - 3776 => U3776, - 3792 => U3792, - 3808 => U3808, - 3824 => U3824, - 3840 => U3840, - 3856 => U3856, - 3872 => U3872, - 3888 => U3888, - 3904 => U3904, - 3920 => U3920, - 3936 => U3936, - 3952 => U3952, - 3968 => U3968, - 3984 => U3984, - 4000 => U4000, - 4016 => U4016, - 4032 => U4032, - 4048 => U4048, - 4064 => U4064, - 4080 => U4080, - } - - // ML-DSA sizes - impl_array_sizes! { - ml_dsa, - 2420 => U2420, - 3309 => U3309, - 4480 => U4480, - 4544 => U4544, - 4595 => U4595, - 4627 => U4627, - 4896 => U4896, - } - - // SLH-DSA sizes - impl_array_sizes! { - slh_dsa, - 7856 => U7856, - 16224 => U16224, - 17088 => U17088, - 29792 => U29792, - 35664 => U35664, - 49856 => U49856, - } - - // Kemeleon ML-KEM Encoding sizes - impl_array_sizes! { - kemeleon, - 749 => U749, - 781 => U781, - 877 => U877, - 1124 => U1124, - 1156 => U1156, - 1252 => U1252, - 1498 => U1498, - 1530 => U1530, - 1658 => U1658, - } - - // LMS sizes - impl_array_sizes! { - lms, - 2047 => U2047, - 2180 => U2180, - 4292 => U4292, - 8516 => U8516, - } - - // Frodo sizes - impl_array_sizes! { - frodokem, - 9616 => U9616, - 19888 => U19888, - 9720 => U9720, - 9752 => U9752, - 15632 => U15632, - 31296 => U31296, - 15744 => U15744, - 15792 => U15792, - 21520 => U21520, - 43088 => U43088, - 21632 => U21632, - 21696 => U21696, - } - - // HKDF-Expand common max output sizes - impl_array_sizes! { - hkdf_expand_max_output, - 8160 => U8160, - 12240 => U12240, - 16320 => U16320, - } - - // HQC-KEM sizes (FIPS 207) - pub type U2241 = uint!(1 0 0 0 0 0 1 1 0 0 0 1); - pub type U4433 = uint!(1 0 0 0 1 0 1 0 1 0 0 0 1); - pub type U4514 = uint!(0 1 0 0 0 1 0 1 1 0 0 0 1); - pub type U7237 = uint!(1 0 1 0 0 0 1 0 0 0 1 1 1); - pub type U8978 = uint!(0 1 0 0 1 0 0 0 1 1 0 0 0 1); - pub type U14421 = uint!(1 0 1 0 1 0 1 0 0 0 0 1 1 1); - - impl_array_sizes! { - hqc, - 2241 => U2241, - 4433 => U4433, - 4514 => U4514, - 7237 => U7237, - 8978 => U8978, - 14421 => U14421, - } -} diff --git a/hqc-kem/hybrid-array-patch/src/traits.rs b/hqc-kem/hybrid-array-patch/src/traits.rs deleted file mode 100644 index 7c9f183..0000000 --- a/hqc-kem/hybrid-array-patch/src/traits.rs +++ /dev/null @@ -1,199 +0,0 @@ -//! Trait definitions. - -use crate::Array; -use core::{ - borrow::{Borrow, BorrowMut}, - fmt::Debug, - ops::{Index, IndexMut, Range}, -}; -use typenum::Unsigned; - -/// Trait which associates a [`usize`] size and `ArrayType` with a -/// `typenum`-provided [`Unsigned`] integer. -/// -/// # Safety -/// -/// `ArrayType` MUST be an array with a number of elements exactly equal to -/// [`Unsigned::USIZE`]. Breaking this requirement will cause undefined behavior. -/// -/// NOTE: This trait is effectively sealed and can not be implemented by third-party crates. -/// It is implemented only for a number of types defined in [`typenum::consts`]. -pub unsafe trait ArraySize: Unsigned + Debug { - /// Array type which corresponds to this size. - /// - /// This is always defined to be `[T; N]` where `N` is the same as - /// [`ArraySize::USIZE`][`typenum::Unsigned::USIZE`]. - type ArrayType: AssocArraySize - + AsRef<[T]> - + AsMut<[T]> - + Borrow<[T]> - + BorrowMut<[T]> - + From> - + Index - + Index> - + IndexMut - + IndexMut> - + Into> - + IntoIterator; -} - -/// Associates an [`ArraySize`] with a given type. Can be used to accept `[T; N]` const generic -/// arguments and convert to [`Array`] internally. -/// -/// This trait is also the magic glue that makes the [`ArrayN`][`crate::ArrayN`] type alias work. -/// -/// # Example -/// -/// ``` -/// use hybrid_array::{ArrayN, AssocArraySize}; -/// -/// pub fn example(bytes: &[u8; N]) -/// where -/// [u8; N]: AssocArraySize + AsRef> -/// { -/// // _arrayn is ArrayN -/// let _arrayn = bytes.as_ref(); -/// } -/// ``` -pub trait AssocArraySize: Sized { - /// Size of an array type, expressed as a [`typenum`]-based [`ArraySize`]. - type Size: ArraySize; -} - -impl AssocArraySize for Array -where - U: ArraySize, -{ - type Size = U; -} - -/// Obtain an `&Array` reference for a given type. -/// -/// This provides functionality equivalent to `AsRef` or `Borrow`, but is deliberately -/// implemented as its own trait both so it can leverage [`AssocArraySize`] to determine the -/// array size, and also to avoid inference problems that occur when third party impls of traits -/// like [`AsRef`] and [`Borrow`] are added to `[T; N]`. -/// -/// # Usage with `[T; N]` -/// -/// ``` -/// use hybrid_array::{Array, ArraySize, AsArrayRef}; -/// -/// pub fn getn_hybrid(arr: &Array, n: usize) -> &T { -/// &arr[2] -/// } -/// -/// pub fn getn_generic(arr: &[T; N], n: usize) -> &T -/// where -/// [T; N]: AsArrayRef -/// { -/// getn_hybrid(arr.as_array_ref(), n) -/// } -/// -/// let array = [0u8, 1, 2, 3]; -/// let x = getn_generic(&array, 2); -/// assert_eq!(x, &2); -/// ``` -pub trait AsArrayRef: AssocArraySize { - /// Converts this type into an immutable [`Array`] reference. - fn as_array_ref(&self) -> &Array; -} - -/// Obtain a `&mut Array` reference for a given type. -/// -/// Companion trait to [`AsArrayRef`] for mutable references, equivalent to [`AsMut`] or -/// [`BorrowMut`]. -pub trait AsArrayMut: AsArrayRef { - /// Converts this type into a mutable [`Array`] reference. - fn as_array_mut(&mut self) -> &mut Array; -} - -impl AsArrayRef for Array -where - U: ArraySize, -{ - fn as_array_ref(&self) -> &Self { - self - } -} - -impl AsArrayMut for Array -where - U: ArraySize, -{ - fn as_array_mut(&mut self) -> &mut Self { - self - } -} - -impl AsArrayRef for [T; N] -where - Self: AssocArraySize, - U: ArraySize = Self>, -{ - fn as_array_ref(&self) -> &Array { - self.into() - } -} - -impl AsArrayMut for [T; N] -where - Self: AssocArraySize, - U: ArraySize = Self>, -{ - fn as_array_mut(&mut self) -> &mut Array { - self.into() - } -} - -/// Extension trait for `[T]` providing methods for working with [`Array`]. -pub trait SliceExt: sealed::Sealed { - /// Get a reference to an array from a slice, if the slice is exactly the size of the array. - /// - /// Returns `None` if the slice's length is not exactly equal to the array size. - fn as_hybrid_array(&self) -> Option<&Array>; - - /// Get a mutable reference to an array from a slice, if the slice is exactly the size of the - /// array. - /// - /// Returns `None` if the slice's length is not exactly equal to the array size. - fn as_mut_hybrid_array(&mut self) -> Option<&mut Array>; - - /// Splits the shared slice into a slice of `U`-element arrays, starting at the beginning - /// of the slice, and a remainder slice with length strictly less than `U`. - /// - /// # Panics - /// If `U` is 0. - fn as_hybrid_chunks(&self) -> (&[Array], &[T]); - - /// Splits the exclusive slice into a slice of `U`-element arrays, starting at the beginning - /// of the slice, and a remainder slice with length strictly less than `U`. - /// - /// # Panics - /// If `U` is 0. - fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]); -} - -impl SliceExt for [T] { - fn as_hybrid_array(&self) -> Option<&Array> { - Array::slice_as_array(self) - } - - fn as_mut_hybrid_array(&mut self) -> Option<&mut Array> { - Array::slice_as_mut_array(self) - } - - fn as_hybrid_chunks(&self) -> (&[Array], &[T]) { - Array::slice_as_chunks(self) - } - - fn as_hybrid_chunks_mut(&mut self) -> (&mut [Array], &mut [T]) { - Array::slice_as_chunks_mut(self) - } -} - -impl sealed::Sealed for [T] {} - -mod sealed { - pub trait Sealed {} -} diff --git a/hqc-kem/hybrid-array-patch/tests/ctutils.rs b/hqc-kem/hybrid-array-patch/tests/ctutils.rs deleted file mode 100644 index 86896b0..0000000 --- a/hqc-kem/hybrid-array-patch/tests/ctutils.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! Tests for `ctutils` integration. - -#![cfg(feature = "ctutils")] - -use ctutils::{Choice, CtAssign, CtEq, CtSelect}; -use hybrid_array::{Array, typenum::U3}; - -#[test] -fn ct_assign() { - let a: Array = Array([0, 0, 0]); - let b: Array = Array([1, 2, 3]); - let mut c = a; - - c.ct_assign(&b, Choice::FALSE); - assert_eq!(a, c); - - c.ct_assign(&b, Choice::TRUE); - assert_eq!(b, c); -} - -#[test] -fn ct_eq() { - let a: Array = Array([0, 0, 0]); - let b: Array = Array([1, 2, 3]); - - assert!(a.ct_eq(&a).to_bool()); - assert!(!a.ct_ne(&a).to_bool()); - assert!(!a.ct_eq(&b).to_bool()); - assert!(a.ct_ne(&b).to_bool()); -} - -#[test] -fn ct_select() { - let a: Array = Array([0, 0, 0]); - let b: Array = Array([1, 2, 3]); - - let c = a.ct_select(&b, Choice::FALSE); - assert_eq!(a, c); - - let d = a.ct_select(&b, Choice::TRUE); - assert_eq!(b, d); -} diff --git a/hqc-kem/hybrid-array-patch/tests/mod.rs b/hqc-kem/hybrid-array-patch/tests/mod.rs deleted file mode 100644 index 0973d98..0000000 --- a/hqc-kem/hybrid-array-patch/tests/mod.rs +++ /dev/null @@ -1,201 +0,0 @@ -#![allow(missing_docs, clippy::cast_possible_truncation, clippy::unwrap_used)] - -use core::mem::MaybeUninit; -use hybrid_array::{Array, ArrayN}; -use typenum::{U0, U2, U3, U4, U5, U6, U7}; - -const EXAMPLE_SLICE: &[u8] = &[1, 2, 3, 4, 5, 6]; - -/// Ensure `ArrayN` works as expected. -const _FOO: ArrayN = Array([1, 2, 3, 4]); - -#[test] -fn tryfrom_slice_for_clonable_array() { - assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); - assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); - - let array_ref = Array::::try_from(EXAMPLE_SLICE).expect("slice contains 6 bytes"); - assert_eq!(&*array_ref, EXAMPLE_SLICE); - - assert!(Array::::try_from(EXAMPLE_SLICE).is_err()); -} - -#[test] -fn tryfrom_slice_for_array_ref() { - assert!(<&Array>::try_from(EXAMPLE_SLICE).is_err()); - assert!(<&Array::>::try_from(EXAMPLE_SLICE).is_err()); - - let array_ref = <&Array>::try_from(EXAMPLE_SLICE).expect("slice contains 6 bytes"); - assert_eq!(array_ref.as_slice(), EXAMPLE_SLICE); - - assert!(<&Array::>::try_from(EXAMPLE_SLICE).is_err()); -} - -#[test] -fn slice_as_array() { - type A = Array; - assert_eq!(A::slice_as_array(&[]), None); - assert_eq!(A::slice_as_array(&[1]), None); - assert_eq!(A::slice_as_array(&[1, 2]), Some(&Array([1, 2]))); - assert_eq!(A::slice_as_array(&[1, 2, 3]), None); -} - -#[test] -fn slice_as_mut_array() { - type A = Array; - assert_eq!(A::slice_as_mut_array(&mut []), None); - assert_eq!(A::slice_as_mut_array(&mut [1]), None); - assert_eq!(A::slice_as_mut_array(&mut [1, 2]), Some(&mut Array([1, 2]))); - assert_eq!(A::slice_as_mut_array(&mut [1, 2, 3]), None); -} - -#[test] -fn concat() { - let prefix = Array::::try_from(&EXAMPLE_SLICE[..2]).unwrap(); - let suffix = Array::::try_from(&EXAMPLE_SLICE[2..]).unwrap(); - - let array = prefix.concat(suffix); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -fn split() { - let array = Array::::try_from(EXAMPLE_SLICE).unwrap(); - let (prefix, suffix) = array.split::(); - - assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..2]); - assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[2..]); -} - -#[test] -fn split_ref() { - let array = Array::::try_from(EXAMPLE_SLICE).unwrap(); - let (prefix, suffix) = array.split_ref::(); - - assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..3]); - assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[3..]); -} - -#[test] -fn split_ref_mut() { - let array = &mut Array::::try_from(EXAMPLE_SLICE).unwrap(); - let (prefix, suffix) = array.split_ref_mut::(); - - assert_eq!(prefix.as_slice(), &EXAMPLE_SLICE[..4]); - assert_eq!(suffix.as_slice(), &EXAMPLE_SLICE[4..]); -} - -#[test] -fn from_ref() { - let n = 42u64; - let array = Array::from_ref(&n); - assert_eq!(array[0], n); -} - -#[test] -fn from_mut() { - let mut n = 42u64; - let array = Array::from_mut(&mut n); - array[0] = 43; - assert_eq!(n, 43); -} - -#[test] -fn from_fn() { - let array = Array::::from_fn(|n| (n + 1) as u8); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -fn try_from_fn() { - let array = Array::::try_from_fn::<()>(|n| Ok((n + 1) as u8)).unwrap(); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); - - let err = Array::::try_from_fn::<&'static str>(|_| Err("err")) - .err() - .unwrap(); - - assert_eq!(err, "err"); -} - -#[test] -fn from_iterator_correct_size() { - let array: Array = EXAMPLE_SLICE.iter().copied().collect(); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -#[should_panic] -fn from_iterator_too_short() { - let _array: Array = EXAMPLE_SLICE.iter().copied().collect(); -} - -#[test] -#[should_panic] -fn from_iterator_too_long() { - let _array: Array = EXAMPLE_SLICE.iter().copied().collect(); -} - -#[test] -fn try_from_iterator_correct_size() { - let array = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()).unwrap(); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -fn try_from_iterator_too_short() { - let result = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()); - assert!(result.is_err()); -} - -#[test] -fn try_from_iterator_too_long() { - let result = Array::::try_from_iter(EXAMPLE_SLICE.iter().copied()); - assert!(result.is_err()); -} - -#[test] -fn maybe_uninit() { - let mut uninit_array = Array::, U6>::uninit(); - - for i in 0..6 { - uninit_array[i].write(EXAMPLE_SLICE[i]); - } - - let array = unsafe { uninit_array.assume_init() }; - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -fn map() { - let base = Array::::from([1, 2, 3, 4]); - let expected = Array::::from([2, 3, 4, 5]); - assert_eq!(base.map(|item| u16::from(item) + 1), expected); -} - -#[test] -#[allow(deprecated)] -fn clone_from_slice() { - let array = Array::::clone_from_slice(EXAMPLE_SLICE); - assert_eq!(array.as_slice(), EXAMPLE_SLICE); -} - -#[test] -fn slice_as_flattened() { - let slice: &mut [Array] = &mut [Array([1, 2, 3, 4]), Array([5, 6, 7, 8])]; - assert_eq!( - Array::slice_as_flattened_mut(slice), - &mut [1, 2, 3, 4, 5, 6, 7, 8] - ); - assert_eq!(Array::slice_as_flattened(slice), &[1, 2, 3, 4, 5, 6, 7, 8]); -} - -#[test] -#[cfg(feature = "zerocopy")] -#[allow(unused)] -fn zerocopy_traits() { - use zerocopy::{FromBytes, Immutable, IntoBytes, KnownLayout, Unaligned}; - struct Check(T); - let ok: Check> = Check(Array([1, 2, 3, 4, 5])); - // let not_unaligned: Check::> = Check(Array([1, 2, 3, 4, 5])); -} diff --git a/hqc-kem/hybrid-array-patch/tests/subtle.rs b/hqc-kem/hybrid-array-patch/tests/subtle.rs deleted file mode 100644 index ef87023..0000000 --- a/hqc-kem/hybrid-array-patch/tests/subtle.rs +++ /dev/null @@ -1,29 +0,0 @@ -//! Tests for `subtle` crate integration. - -#![cfg(feature = "subtle")] - -use hybrid_array::{Array, typenum::U3}; -use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; - -#[test] -fn constant_time_eq() { - let a: Array = Array([0, 0, 0]); - let b: Array = Array([1, 2, 3]); - - assert!(bool::from(a.ct_eq(&a))); - assert!(!bool::from(a.ct_ne(&a))); - assert!(!bool::from(a.ct_eq(&b))); - assert!(bool::from(a.ct_ne(&b))); -} - -#[test] -fn conditional_select() { - let a: Array = Array([0, 0, 0]); - let b: Array = Array([1, 2, 3]); - - let c = Array::conditional_select(&a, &b, Choice::from(0)); - assert_eq!(a, c); - - let d = Array::conditional_select(&a, &b, Choice::from(1)); - assert_eq!(b, d); -}