mirror of
https://github.com/RustCrypto/signatures
synced 2026-06-21 13:45:42 +00:00
feat(xmss): Add XMSS (#1228)
This commit is contained in:
@@ -0,0 +1,41 @@
|
||||
name: xmss
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/xmss.yml"
|
||||
- "xmss/**"
|
||||
- "Cargo.*"
|
||||
push:
|
||||
branches: master
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: xmss
|
||||
|
||||
env:
|
||||
CARGO_INCREMENTAL: 0
|
||||
RUSTFLAGS: "-Dwarnings"
|
||||
|
||||
# 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:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
rust:
|
||||
- 1.85.0 # MSRV
|
||||
- stable
|
||||
steps:
|
||||
- uses: actions/checkout@v6.0.2
|
||||
- uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: ${{ matrix.rust }}
|
||||
- run: cargo build --benches
|
||||
- run: cargo build --benches --all-features
|
||||
- run: cargo test --release
|
||||
- run: cargo test --all-features --release
|
||||
- run: cargo test --no-default-features --release
|
||||
Generated
+394
-108
@@ -36,9 +36,18 @@ checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||
|
||||
[[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"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
@@ -75,9 +84,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7"
|
||||
|
||||
[[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 = "blobby"
|
||||
@@ -85,20 +94,35 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89af0b093cc13baa4e51e64e65ec2422f7e73aea0e612e5ad3872986671622f1"
|
||||
|
||||
[[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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
]
|
||||
|
||||
[[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"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cast"
|
||||
@@ -157,25 +181,25 @@ 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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.54"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394"
|
||||
checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.54"
|
||||
version = "4.5.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00"
|
||||
checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876"
|
||||
dependencies = [
|
||||
"anstyle",
|
||||
"clap_lex",
|
||||
@@ -183,9 +207,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_lex"
|
||||
version = "0.7.7"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32"
|
||||
checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831"
|
||||
|
||||
[[package]]
|
||||
name = "cmov"
|
||||
@@ -193,6 +217,21 @@ version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de0758edba32d61d1fd9f4d69491b47604b91ee2f7e6b33de7e54ca4ebe55dc3"
|
||||
|
||||
[[package]]
|
||||
name = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.10.2"
|
||||
@@ -256,6 +295,12 @@ dependencies = [
|
||||
"itertools",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
@@ -296,7 +341,7 @@ dependencies = [
|
||||
"cpubits",
|
||||
"ctutils",
|
||||
"getrandom 0.4.1",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"num-traits",
|
||||
"rand_core 0.10.0",
|
||||
"serdect",
|
||||
@@ -304,6 +349,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"
|
||||
@@ -311,7 +366,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710"
|
||||
dependencies = [
|
||||
"getrandom 0.4.1",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
|
||||
@@ -345,13 +400,23 @@ dependencies = [
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid 0.9.6",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "der"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"const-oid 0.10.2",
|
||||
"der_derive",
|
||||
"pem-rfc7468",
|
||||
"zeroize",
|
||||
@@ -368,6 +433,16 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[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.1"
|
||||
@@ -375,9 +450,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "285743a676ccb6b3e116bc14cc69319b957867930ae9c4822f8e0f54509d7243"
|
||||
dependencies = [
|
||||
"blobby",
|
||||
"block-buffer",
|
||||
"const-oid",
|
||||
"crypto-common",
|
||||
"block-buffer 0.12.0",
|
||||
"const-oid 0.10.2",
|
||||
"crypto-common 0.2.1",
|
||||
"ctutils",
|
||||
]
|
||||
|
||||
@@ -388,18 +463,18 @@ dependencies = [
|
||||
"chacha20",
|
||||
"crypto-bigint",
|
||||
"crypto-primes",
|
||||
"der",
|
||||
"digest",
|
||||
"der 0.8.0",
|
||||
"digest 0.11.1",
|
||||
"getrandom 0.4.1",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"proptest",
|
||||
"rand_core 0.10.0",
|
||||
"rfc6979",
|
||||
"sha1",
|
||||
"sha2",
|
||||
"signature",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"signature 3.0.0-rc.10",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -407,15 +482,15 @@ dependencies = [
|
||||
name = "ecdsa"
|
||||
version = "0.17.0-rc.16"
|
||||
dependencies = [
|
||||
"der",
|
||||
"digest",
|
||||
"der 0.8.0",
|
||||
"digest 0.11.1",
|
||||
"elliptic-curve",
|
||||
"hex-literal",
|
||||
"rfc6979",
|
||||
"serdect",
|
||||
"sha2",
|
||||
"signature",
|
||||
"spki",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"signature 3.0.0-rc.10",
|
||||
"spki 0.8.0-rc.4",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -424,11 +499,11 @@ name = "ed25519"
|
||||
version = "3.0.0-rc.4"
|
||||
dependencies = [
|
||||
"hex-literal",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"rand_core 0.9.5",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"signature",
|
||||
"signature 3.0.0-rc.10",
|
||||
"zerocopy",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -438,10 +513,10 @@ name = "ed448"
|
||||
version = "0.5.0-rc.5"
|
||||
dependencies = [
|
||||
"hex-literal",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"serde",
|
||||
"serde_bytes",
|
||||
"signature",
|
||||
"signature 3.0.0-rc.10",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -459,13 +534,13 @@ checksum = "bde7860544606d222fd6bd6d9f9a0773321bf78072a637e1d560a058c0031978"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"crypto-bigint",
|
||||
"crypto-common",
|
||||
"digest",
|
||||
"crypto-common 0.2.1",
|
||||
"digest 0.11.1",
|
||||
"hex-literal",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"once_cell",
|
||||
"pem-rfc7468",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"rand_core 0.10.0",
|
||||
"rustcrypto-ff",
|
||||
"rustcrypto-group",
|
||||
@@ -475,6 +550,18 @@ dependencies = [
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
@@ -509,6 +596,16 @@ 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.3.4"
|
||||
@@ -546,6 +643,15 @@ dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.15.5"
|
||||
@@ -561,6 +667,20 @@ version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
|
||||
[[package]]
|
||||
name = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
@@ -588,7 +708,17 @@ version = "0.13.0-rc.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef451d73f36d8a3f93ad32c332ea01146c9650e1ec821a9b0e46c01277d544f8"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -626,7 +756,7 @@ version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -646,14 +776,23 @@ checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3"
|
||||
checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c"
|
||||
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 = "keccak"
|
||||
version = "0.2.0-rc.2"
|
||||
@@ -671,9 +810,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
@@ -683,27 +822,36 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
|
||||
|
||||
[[package]]
|
||||
name = "linux-raw-sys"
|
||||
version = "0.11.0"
|
||||
version = "0.12.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
|
||||
checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
|
||||
|
||||
[[package]]
|
||||
name = "lms-signature"
|
||||
version = "0.1.0-rc.2"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
"getrandom 0.4.1",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"rand_core 0.10.0",
|
||||
"sha2",
|
||||
"signature",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"signature 3.0.0-rc.10",
|
||||
"static_assertions",
|
||||
"typenum",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
@@ -712,28 +860,28 @@ 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-dsa"
|
||||
version = "0.1.0-rc.7"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"const-oid 0.10.2",
|
||||
"criterion",
|
||||
"getrandom 0.4.1",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"module-lattice",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"proptest",
|
||||
"rand_core 0.10.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha3",
|
||||
"signature",
|
||||
"sha3 0.11.0-rc.8",
|
||||
"signature 3.0.0-rc.10",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
@@ -743,7 +891,7 @@ version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6dfecc750073acc09af2f8899b2342d520d570392ba1c3aed53eeb0d84ca4103"
|
||||
dependencies = [
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"num-traits",
|
||||
"zeroize",
|
||||
]
|
||||
@@ -803,14 +951,24 @@ dependencies = [
|
||||
"base64ct",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der 0.7.10",
|
||||
"spki 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pkcs8"
|
||||
version = "0.11.0-rc.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12922b6296c06eb741b02d7b5161e3aaa22864af38dfa025a1a3ba3f68c84577"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
"der 0.8.0",
|
||||
"spki 0.8.0-rc.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -841,6 +999,19 @@ dependencies = [
|
||||
"plotters-backend",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
@@ -986,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",
|
||||
@@ -998,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",
|
||||
@@ -1009,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 = "rfc6979"
|
||||
@@ -1019,10 +1190,19 @@ version = "0.5.0-rc.5"
|
||||
dependencies = [
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"sha2",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rustcrypto-ff"
|
||||
version = "0.14.0-rc.0"
|
||||
@@ -1046,9 +1226,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustix"
|
||||
version = "1.1.3"
|
||||
version = "1.1.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34"
|
||||
checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"errno",
|
||||
@@ -1084,6 +1264,12 @@ dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[package]]
|
||||
name = "sec1"
|
||||
version = "0.8.0-rc.13"
|
||||
@@ -1092,8 +1278,8 @@ checksum = "7a2400ed44a13193820aa528a19f376c3843141a8ce96ff34b11104cc79763f2"
|
||||
dependencies = [
|
||||
"base16ct",
|
||||
"ctutils",
|
||||
"der",
|
||||
"hybrid-array",
|
||||
"der 0.8.0",
|
||||
"hybrid-array 0.4.7",
|
||||
"serdect",
|
||||
"subtle",
|
||||
"zeroize",
|
||||
@@ -1176,7 +1362,18 @@ checksum = "3b167252f3c126be0d8926639c4c4706950f01445900c4b3db0fd7e89fcb750a"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest 0.10.7",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1187,7 +1384,17 @@ checksum = "7c5f3b1e2dc8aad28310d8410bd4d7e180eca65fca176c52ab00d364475d0024"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
]
|
||||
|
||||
[[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]]
|
||||
@@ -1196,17 +1403,23 @@ version = "0.11.0-rc.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95f78cd62cc39ece5aefbeb6caaa2ea44f70b4815d4b85f7e150ac685ada2bb5"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"keccak",
|
||||
"digest 0.11.1",
|
||||
"keccak 0.2.0-rc.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "3.0.0-rc.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f1880df446116126965eeec169136b2e0251dba37c6223bcc819569550edea3"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
"rand_core 0.10.0",
|
||||
]
|
||||
|
||||
@@ -1216,30 +1429,49 @@ version = "0.2.0-rc.4"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"cipher",
|
||||
"const-oid",
|
||||
"const-oid 0.10.2",
|
||||
"criterion",
|
||||
"ctr",
|
||||
"digest",
|
||||
"digest 0.11.1",
|
||||
"hex",
|
||||
"hex-literal",
|
||||
"hmac",
|
||||
"hybrid-array",
|
||||
"hybrid-array 0.4.7",
|
||||
"num-bigint",
|
||||
"paste",
|
||||
"pkcs8",
|
||||
"pkcs8 0.11.0-rc.11",
|
||||
"proptest",
|
||||
"rand 0.10.0",
|
||||
"rand_core 0.10.0",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"signature",
|
||||
"sha2 0.11.0-rc.5",
|
||||
"sha3 0.11.0-rc.8",
|
||||
"signature 3.0.0-rc.10",
|
||||
"typenum",
|
||||
"zerocopy",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der 0.7.10",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.8.0-rc.4"
|
||||
@@ -1247,9 +1479,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
"der 0.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "static_assertions"
|
||||
version = "1.1.0"
|
||||
@@ -1264,9 +1502,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",
|
||||
@@ -1275,17 +1513,37 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.24.0"
|
||||
version = "3.26.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "655da9c7eb6305c55742045d5a8d2037996d61d8de95806335c7c86ce0f82e9c"
|
||||
checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0"
|
||||
dependencies = [
|
||||
"fastrand",
|
||||
"getrandom 0.3.4",
|
||||
"getrandom 0.4.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[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"
|
||||
@@ -1310,9 +1568,9 @@ checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94"
|
||||
|
||||
[[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"
|
||||
@@ -1320,6 +1578,12 @@ 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 = "wait-timeout"
|
||||
version = "0.2.1"
|
||||
@@ -1359,9 +1623,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566"
|
||||
checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"once_cell",
|
||||
@@ -1372,9 +1636,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608"
|
||||
checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1382,9 +1646,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55"
|
||||
checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"proc-macro2",
|
||||
@@ -1395,9 +1659,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.108"
|
||||
version = "0.2.114"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12"
|
||||
checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
@@ -1438,9 +1702,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.85"
|
||||
version = "0.3.91"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "312e32e551d92129218ea9a2452120f4aabc03529ef03e4d0d82fb2780608598"
|
||||
checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@@ -1558,20 +1822,42 @@ dependencies = [
|
||||
"wasmparser",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xmss-signatures"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"const-oid 0.9.6",
|
||||
"der 0.7.10",
|
||||
"digest 0.10.7",
|
||||
"hybrid-array 0.2.3",
|
||||
"pkcs8 0.10.2",
|
||||
"postcard",
|
||||
"rand 0.10.0",
|
||||
"serde_json",
|
||||
"serdect",
|
||||
"sha2 0.10.9",
|
||||
"sha3 0.10.8",
|
||||
"signature 2.2.0",
|
||||
"spki 0.7.3",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.34"
|
||||
version = "0.8.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "71ddd76bcebeed25db614f82bf31a9f4222d3fbba300e6fb6c00afa26cbd4d9d"
|
||||
checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.34"
|
||||
version = "0.8.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8187381b52e32220d50b255276aa16a084ec0a9017a0ca2152a1f55c539758d"
|
||||
checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1586,6 +1872,6 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.17"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02aae0f83f69aafc94776e879363e9771d7ecbffe2c7fbb6c14c5e00dfe88439"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
|
||||
@@ -9,6 +9,7 @@ members = [
|
||||
"ml-dsa",
|
||||
"rfc6979",
|
||||
"slh-dsa",
|
||||
"xmss",
|
||||
]
|
||||
|
||||
[profile.dev]
|
||||
@@ -25,3 +26,4 @@ lms-signature = { path = "./lms" }
|
||||
ml-dsa = { path = "./ml-dsa" }
|
||||
rfc6979 = { path = "./rfc6979" }
|
||||
slh-dsa = { path = "./slh-dsa" }
|
||||
xmss-signatures = { path = "./xmss" }
|
||||
|
||||
@@ -21,6 +21,7 @@ and can be easily used for bare-metal or lightweight WebAssembly programming.
|
||||
| [`ml-dsa`] | [Module Lattice DSA](https://csrc.nist.gov/pubs/fips/204/final) | [](https://crates.io/crates/ml-dsa) | [](https://docs.rs/ml-dsa) | [](https://github.com/RustCrypto/signatures/actions/workflows/ml-dsa.yml)
|
||||
| [`rfc6979`] | [Deterministic (EC)DSA Signatures](https://datatracker.ietf.org/doc/html/rfc6979) | [](https://crates.io/crates/rfc6979) | [](https://docs.rs/rfc6979) | [](https://github.com/RustCrypto/signatures/actions/workflows/rfc6979.yml)
|
||||
| [`slh-dsa`] | [Stateless Hash-Based Signature](https://csrc.nist.gov/pubs/fips/205/final) | [](https://crates.io/crates/slh-dsa) | [](https://docs.rs/slh-dsa) | [](https://github.com/RustCrypto/signatures/actions/workflows/slh-dsa.yml)
|
||||
| [`xmss`] | [eXtended Merkle Signature Scheme](https://datatracker.ietf.org/doc/html/rfc8391) | [](https://crates.io/crates/xmss-signatures) | [](https://docs.rs/xmss-signatures) | [](https://github.com/RustCrypto/signatures/actions/workflows/xmss.yml)
|
||||
|
||||
NOTE: for RSA signatures see <https://github.com/RustCrypto/RSA>
|
||||
|
||||
@@ -61,6 +62,7 @@ dual licensed as above, without any additional terms or conditions.
|
||||
[`ml-dsa`]: ./ml-dsa
|
||||
[`rfc6979`]: ./rfc6979
|
||||
[`slh-dsa`]: ./slh-dsa
|
||||
[`xmss`]: ./xmss
|
||||
|
||||
[//]: # (general links)
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
@@ -0,0 +1,9 @@
|
||||
# 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.1.0 (2026-02-20)
|
||||
- Initial release
|
||||
Generated
+740
@@ -0,0 +1,740 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.101"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea"
|
||||
|
||||
[[package]]
|
||||
name = "atomic-polyfill"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cf2bce30dfe09ef0bfaef228b9d414faaf7e563035494d7fe092dba54b300f4"
|
||||
dependencies = [
|
||||
"critical-section",
|
||||
]
|
||||
|
||||
[[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 = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[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 = "cobs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa961b519f0b462e3a3b4a34b64d119eeaca1d59af726fe450bbba07a9fc0a1"
|
||||
dependencies = [
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "const-oid"
|
||||
version = "0.9.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
|
||||
|
||||
[[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 = "critical-section"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b"
|
||||
|
||||
[[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 = "der"
|
||||
version = "0.7.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "embedded-io"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[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.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"rand_core",
|
||||
"wasip2",
|
||||
"wasip3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hash32"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[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 = "heapless"
|
||||
version = "0.7.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdc6457c0eb62c71aac4bc17216026d8410337c4126773b9c5daba343f17964f"
|
||||
dependencies = [
|
||||
"atomic-polyfill",
|
||||
"hash32",
|
||||
"rustc_version",
|
||||
"serde",
|
||||
"spin",
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hybrid-array"
|
||||
version = "0.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f2d35805454dc9f8662a98d6d61886ffe26bd465f5960e0e55345c70d5c0d2a9"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[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 = "itoa"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2"
|
||||
|
||||
[[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 = "leb128fmt"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.182"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112"
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[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 = "pkcs8"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
|
||||
dependencies = [
|
||||
"der",
|
||||
"spki",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "postcard"
|
||||
version = "1.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
|
||||
dependencies = [
|
||||
"cobs",
|
||||
"embedded-io 0.4.0",
|
||||
"embedded-io 0.6.1",
|
||||
"heapless",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[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.44"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
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"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba"
|
||||
|
||||
[[package]]
|
||||
name = "rustc_version"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||
dependencies = [
|
||||
"semver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||
|
||||
[[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 = "sha2"
|
||||
version = "0.10.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures 0.2.17",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha3"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60"
|
||||
dependencies = [
|
||||
"digest",
|
||||
"keccak",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spki"
|
||||
version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"der",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[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.116"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3df424c70518695237746f84cede799c9c58fcb37450d7b23716568cc8bc69cb"
|
||||
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 = "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 = "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-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 = "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 = "xmss-signatures"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"const-oid",
|
||||
"der",
|
||||
"digest",
|
||||
"hybrid-array",
|
||||
"pkcs8",
|
||||
"postcard",
|
||||
"rand",
|
||||
"serde_json",
|
||||
"serdect",
|
||||
"sha2",
|
||||
"sha3",
|
||||
"signature",
|
||||
"spki",
|
||||
"subtle",
|
||||
"thiserror",
|
||||
"zeroize",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
|
||||
|
||||
[[package]]
|
||||
name = "zmij"
|
||||
version = "1.0.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
||||
@@ -0,0 +1,64 @@
|
||||
[package]
|
||||
name = "xmss-signatures"
|
||||
authors = ["Michael Lodder <redmike7@gmail.com>"]
|
||||
category = ["cryptography"]
|
||||
description = """
|
||||
Pure Rust implementation of XMSS (eXtended Merkle Signature Scheme)
|
||||
as described in RFC 8391 and SP 800-208
|
||||
"""
|
||||
edition = "2024"
|
||||
homepage = "https://github.com/RustCrypto/signature/tree/master/xmss"
|
||||
repository = "https://github.com/RustCrypto/signatures"
|
||||
readme = "README.md"
|
||||
keywords = ["xmss", "signature", "crypto", "cryptography", "rfc8391"]
|
||||
license = "MIT/Apache-2.0"
|
||||
version = "0.1.0"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
serde = ["dep:serdect"]
|
||||
pkcs8 = ["dep:pkcs8", "dep:spki", "dep:der", "dep:const-oid"]
|
||||
|
||||
[dependencies]
|
||||
const-oid = { version = "0.9", optional = true }
|
||||
der = { version = "0.7", optional = true, default-features = false, features = ["alloc"] }
|
||||
digest = "0.10"
|
||||
hybrid-array = { version = "0.2", features = ["zeroize"] }
|
||||
pkcs8 = { version = "0.10", optional = true, default-features = false, features = ["alloc"] }
|
||||
rand = "0.10"
|
||||
sha2 = "0.10"
|
||||
sha3 = "0.10"
|
||||
serdect = { version = "0.4", features = ["alloc"], optional = true }
|
||||
signature = "2"
|
||||
spki = { version = "0.7", optional = true, default-features = false, features = ["alloc"] }
|
||||
subtle = "2.6"
|
||||
thiserror = "2"
|
||||
zeroize = "1"
|
||||
|
||||
[dev-dependencies]
|
||||
postcard = { version = "1", features = ["alloc"] }
|
||||
serde_json = "1"
|
||||
|
||||
[lints.rust]
|
||||
missing_docs = "deny"
|
||||
missing_debug_implementations = "deny"
|
||||
trivial_casts = "deny"
|
||||
trivial_numeric_casts = "deny"
|
||||
unsafe_code = "deny"
|
||||
unstable_features = "deny"
|
||||
unused_import_braces = "deny"
|
||||
unused_parens = "deny"
|
||||
unused_lifetimes = "deny"
|
||||
unused_qualifications = "deny"
|
||||
unused_extern_crates = "deny"
|
||||
|
||||
[lints.clippy]
|
||||
unwrap_used = "deny"
|
||||
cast_precision_loss = "warn"
|
||||
cast_possible_truncation = "warn"
|
||||
cast_possible_wrap = "warn"
|
||||
cast_sign_loss = "warn"
|
||||
checked_conversions = "warn"
|
||||
mod_module_files = "warn"
|
||||
panic = "warn"
|
||||
panic_in_result_fn = "warn"
|
||||
@@ -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.
|
||||
@@ -0,0 +1,23 @@
|
||||
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.
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
# [RustCrypto]: XMSS
|
||||
|
||||
[![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]
|
||||
|
||||
Pure Rust implementation of the XMSS (eXtended Merkle Signature Scheme)
|
||||
and XMSS^MT (Multi-Tree) signature schemes as described in [RFC 8391] and
|
||||
[NIST SP 800-208].
|
||||
|
||||
## ⚠️ Security Warning
|
||||
|
||||
The implementation contained in this crate has never been independently audited!
|
||||
|
||||
USE AT YOUR OWN RISK!
|
||||
|
||||
## About
|
||||
|
||||
XMSS is a stateful hash-based digital signature scheme that is believed to be
|
||||
resistant to attacks by quantum computers. It is standardized in [RFC 8391] and
|
||||
approved by NIST in [SP 800-208].
|
||||
|
||||
This crate provides:
|
||||
|
||||
- XMSS (single-tree) and XMSS^MT (multi-tree) signature schemes
|
||||
- SHA-256, SHA-512, SHAKE128, and SHAKE256 hash function support
|
||||
- 93 parameter sets covering tree heights of 10, 16, 20, 40, and 60
|
||||
- Hash output sizes of 192, 256, and 512 bits
|
||||
- Optional `serde` support for serialization/deserialization
|
||||
- Optional `pkcs8` support for PKCS#8/SPKI key encoding
|
||||
- `no_unsafe` code — zero `unsafe` blocks
|
||||
- Constant-time operations for signature verification
|
||||
- Automatic zeroization of secret key material on drop
|
||||
|
||||
## Usage
|
||||
|
||||
```rust
|
||||
use xmss_signatures::{KeyPair, XmssSha2_10_256};
|
||||
|
||||
// Generate a key pair
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
// Sign a message
|
||||
let message = b"test message";
|
||||
let signature = kp.signing_key().sign(message).unwrap();
|
||||
|
||||
// Verify the signature and recover the message
|
||||
let recovered = kp.verifying_key().verify(&signature).unwrap();
|
||||
assert_eq!(recovered, message);
|
||||
|
||||
// Detached signatures are also supported
|
||||
let signature = kp.signing_key().sign_detached(message).unwrap();
|
||||
kp.verifying_key().verify_detached(&signature, message).unwrap();
|
||||
```
|
||||
|
||||
## Supported Parameter Sets
|
||||
|
||||
### XMSS (Single-Tree)
|
||||
|
||||
| Parameter Set | Hash | n (bytes) | Tree Height | Max Signatures |
|
||||
|---|---|---|---|---|
|
||||
| `XmssSha2_10_256` | SHA-256 | 32 | 10 | 1,024 |
|
||||
| `XmssSha2_16_256` | SHA-256 | 32 | 16 | 65,536 |
|
||||
| `XmssSha2_20_256` | SHA-256 | 32 | 20 | 1,048,576 |
|
||||
| `XmssSha2_10_512` | SHA-512 | 64 | 10 | 1,024 |
|
||||
| `XmssSha2_16_512` | SHA-512 | 64 | 16 | 65,536 |
|
||||
| `XmssSha2_20_512` | SHA-512 | 64 | 20 | 1,048,576 |
|
||||
| `XmssSha2_10_192` | SHA-256 | 24 | 10 | 1,024 |
|
||||
| `XmssSha2_16_192` | SHA-256 | 24 | 16 | 65,536 |
|
||||
| `XmssSha2_20_192` | SHA-256 | 24 | 20 | 1,048,576 |
|
||||
| `XmssShake_10_256` | SHAKE128 | 32 | 10 | 1,024 |
|
||||
| `XmssShake_16_256` | SHAKE128 | 32 | 16 | 65,536 |
|
||||
| `XmssShake_20_256` | SHAKE128 | 32 | 20 | 1,048,576 |
|
||||
| `XmssShake_10_512` | SHAKE128 | 64 | 10 | 1,024 |
|
||||
| `XmssShake_16_512` | SHAKE128 | 64 | 16 | 65,536 |
|
||||
| `XmssShake_20_512` | SHAKE128 | 64 | 20 | 1,048,576 |
|
||||
| `XmssShake256_10_256` | SHAKE256 | 32 | 10 | 1,024 |
|
||||
| `XmssShake256_16_256` | SHAKE256 | 32 | 16 | 65,536 |
|
||||
| `XmssShake256_20_256` | SHAKE256 | 32 | 20 | 1,048,576 |
|
||||
| `XmssShake256_10_192` | SHAKE256 | 24 | 10 | 1,024 |
|
||||
| `XmssShake256_16_192` | SHAKE256 | 24 | 16 | 65,536 |
|
||||
| `XmssShake256_20_192` | SHAKE256 | 24 | 20 | 1,048,576 |
|
||||
|
||||
### XMSS^MT (Multi-Tree)
|
||||
|
||||
Multi-tree parameter sets follow the naming convention
|
||||
`Xmssmt[Hash]_[TotalHeight]_[Depth]_[Bits]`, e.g. `XmssmtSha2_20_2_256`.
|
||||
|
||||
Total tree heights of 20, 40, and 60 are supported with depths of 2, 4, 8, 3,
|
||||
6, and 12 (where applicable), across SHA-256, SHA-512, SHAKE128, and SHAKE256
|
||||
hash functions.
|
||||
|
||||
See the [API documentation][docs-link] for a complete list of all 72 XMSS^MT
|
||||
parameter sets.
|
||||
|
||||
## Features
|
||||
|
||||
| Feature | Description |
|
||||
|---|---|
|
||||
| `serde` | Enables `serde` serialization/deserialization via `serdect` |
|
||||
| `pkcs8` | Enables PKCS#8 and SPKI key encoding/decoding |
|
||||
|
||||
## Minimum Supported Rust Version
|
||||
|
||||
This crate requires **Rust 2024 edition**.
|
||||
|
||||
## License
|
||||
|
||||
All crates licensed under either of
|
||||
|
||||
* [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0)
|
||||
* [MIT license](https://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/xmss-signatures?logo=rust
|
||||
[crate-link]: https://crates.io/crates/xmss-signatures
|
||||
[docs-image]: https://docs.rs/xmss-signatures/badge.svg
|
||||
[docs-link]: https://docs.rs/xmss-signatures/
|
||||
[build-image]: https://github.com/RustCrypto/signatures/actions/workflows/xmss.yml/badge.svg
|
||||
[build-link]: https://github.com/RustCrypto/signatures/actions/workflows/xmss.yml
|
||||
[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/260048-signatures
|
||||
|
||||
[//]: # (links)
|
||||
|
||||
[RustCrypto]: https://github.com/RustCrypto
|
||||
[RFC 8391]: https://www.rfc-editor.org/rfc/rfc8391
|
||||
[NIST SP 800-208]: https://csrc.nist.gov/pubs/sp/800/208/final
|
||||
@@ -0,0 +1,54 @@
|
||||
/// Errors used throughout this crate.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
/// The OID value is not recognized.
|
||||
#[error("Invalid OID: 0x{0:08x}")]
|
||||
InvalidOid(u32),
|
||||
/// The parameter set name is not recognized.
|
||||
#[error("Invalid parameter set: {0}")]
|
||||
InvalidParameterSet(String),
|
||||
/// The Winternitz parameter value is not supported.
|
||||
#[error("Invalid parameters: unsupported Winternitz parameter w={0}")]
|
||||
InvalidParams(u32),
|
||||
/// All one-time signatures have been used.
|
||||
#[error("Key exhausted: all one-time signatures have been used")]
|
||||
KeyExhausted,
|
||||
/// The provided seed has an incorrect length.
|
||||
#[error("Invalid seed length: expected {expected}, got {got}")]
|
||||
InvalidSeedLength {
|
||||
/// Expected seed length in bytes.
|
||||
expected: usize,
|
||||
/// Actual seed length in bytes.
|
||||
got: usize,
|
||||
},
|
||||
/// Signature verification failed.
|
||||
#[error("Signature verification failed")]
|
||||
VerificationFailed,
|
||||
/// The hash function configuration is not supported.
|
||||
#[error("Hash function error: unsupported n={n} with func={func}")]
|
||||
Hash {
|
||||
/// The hash output length parameter.
|
||||
n: u32,
|
||||
/// The hash function identifier.
|
||||
func: u32,
|
||||
},
|
||||
/// The provided key has an incorrect length.
|
||||
#[error("Invalid key length: expected {expected}, got {got}")]
|
||||
InvalidKeyLength {
|
||||
/// Expected key length in bytes.
|
||||
expected: usize,
|
||||
/// Actual key length in bytes.
|
||||
got: usize,
|
||||
},
|
||||
/// The provided signature has an incorrect length.
|
||||
#[error("Invalid signature length: expected {expected}, got {got}")]
|
||||
InvalidSignatureLength {
|
||||
/// Expected signature length in bytes.
|
||||
expected: usize,
|
||||
/// Actual signature length in bytes.
|
||||
got: usize,
|
||||
},
|
||||
}
|
||||
|
||||
/// Result type used by this crate.
|
||||
pub type XmssResult<T> = Result<T, Error>;
|
||||
@@ -0,0 +1,210 @@
|
||||
use sha2::{Digest, Sha256, Sha512};
|
||||
use sha3::{
|
||||
Shake128, Shake256,
|
||||
digest::{ExtendableOutput, Update, XofReader},
|
||||
};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::error::{Error, XmssResult};
|
||||
use crate::hash_address::set_key_and_mask;
|
||||
use crate::params::{XMSS_SHA2, XMSS_SHAKE128, XMSS_SHAKE256, XmssParams};
|
||||
use crate::utils::ull_to_bytes;
|
||||
|
||||
const XMSS_HASH_PADDING_F: u64 = 0;
|
||||
const XMSS_HASH_PADDING_H: u64 = 1;
|
||||
const XMSS_HASH_PADDING_HASH: u64 = 2;
|
||||
const XMSS_HASH_PADDING_PRF: u64 = 3;
|
||||
const XMSS_HASH_PADDING_PRF_KEYGEN: u64 = 4;
|
||||
|
||||
pub(crate) fn addr_to_bytes(bytes: &mut [u8], addr: &[u32; 8]) {
|
||||
for i in 0..8 {
|
||||
ull_to_bytes(&mut bytes[i * 4..i * 4 + 4], addr[i] as u64);
|
||||
}
|
||||
}
|
||||
|
||||
fn core_hash(params: &XmssParams, out: &mut [u8], input: &[u8]) -> XmssResult<()> {
|
||||
if params.n == 24 && params.func == XMSS_SHA2 {
|
||||
let result = Sha256::digest(input);
|
||||
out[..24].copy_from_slice(&result[..24]);
|
||||
} else if params.n == 24 && params.func == XMSS_SHAKE256 {
|
||||
let mut hasher = Shake256::default();
|
||||
hasher.update(input);
|
||||
let mut reader = hasher.finalize_xof();
|
||||
reader.read(&mut out[..24]);
|
||||
} else if params.n == 32 && params.func == XMSS_SHA2 {
|
||||
let result = Sha256::digest(input);
|
||||
out[..32].copy_from_slice(&result);
|
||||
} else if params.n == 32 && params.func == XMSS_SHAKE128 {
|
||||
let mut hasher = Shake128::default();
|
||||
hasher.update(input);
|
||||
let mut reader = hasher.finalize_xof();
|
||||
reader.read(&mut out[..32]);
|
||||
} else if params.n == 32 && params.func == XMSS_SHAKE256 {
|
||||
let mut hasher = Shake256::default();
|
||||
hasher.update(input);
|
||||
let mut reader = hasher.finalize_xof();
|
||||
reader.read(&mut out[..32]);
|
||||
} else if params.n == 64 && params.func == XMSS_SHA2 {
|
||||
let result = Sha512::digest(input);
|
||||
out[..64].copy_from_slice(&result);
|
||||
} else if params.n == 64 && params.func == XMSS_SHAKE256 {
|
||||
let mut hasher = Shake256::default();
|
||||
hasher.update(input);
|
||||
let mut reader = hasher.finalize_xof();
|
||||
reader.read(&mut out[..64]);
|
||||
} else {
|
||||
return Err(Error::Hash {
|
||||
n: params.n,
|
||||
func: params.func,
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes PRF(key, in), for a key of params.n bytes, and a 32-byte input.
|
||||
pub(crate) fn prf(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
input: &[u8; 32],
|
||||
key: &[u8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let padding_len = params.padding_len as usize;
|
||||
let buf_len = padding_len + n + 32;
|
||||
let mut buf = vec![0u8; buf_len];
|
||||
|
||||
ull_to_bytes(&mut buf[..padding_len], XMSS_HASH_PADDING_PRF);
|
||||
buf[padding_len..padding_len + n].copy_from_slice(&key[..n]);
|
||||
buf[padding_len + n..padding_len + n + 32].copy_from_slice(input);
|
||||
|
||||
let result = core_hash(params, out, &buf);
|
||||
buf.zeroize();
|
||||
result
|
||||
}
|
||||
|
||||
/// Computes PRF_keygen(key, in), for a key of params.n bytes,
|
||||
/// and an input of 32 + params.n bytes.
|
||||
pub(crate) fn prf_keygen(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
input: &[u8],
|
||||
key: &[u8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let padding_len = params.padding_len as usize;
|
||||
let buf_len = padding_len + 2 * n + 32;
|
||||
let mut buf = vec![0u8; buf_len];
|
||||
|
||||
ull_to_bytes(&mut buf[..padding_len], XMSS_HASH_PADDING_PRF_KEYGEN);
|
||||
buf[padding_len..padding_len + n].copy_from_slice(&key[..n]);
|
||||
buf[padding_len + n..padding_len + n + n + 32].copy_from_slice(&input[..n + 32]);
|
||||
|
||||
let result = core_hash(params, out, &buf);
|
||||
buf.zeroize();
|
||||
result
|
||||
}
|
||||
|
||||
/// Computes the message hash using R, the public root, the index of the leaf
|
||||
/// node, and the message.
|
||||
pub(crate) fn hash_message(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
r: &[u8],
|
||||
root: &[u8],
|
||||
idx: u64,
|
||||
m_with_prefix: &mut [u8],
|
||||
mlen: u64,
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let padding_len = params.padding_len as usize;
|
||||
|
||||
ull_to_bytes(&mut m_with_prefix[..padding_len], XMSS_HASH_PADDING_HASH);
|
||||
m_with_prefix[padding_len..padding_len + n].copy_from_slice(&r[..n]);
|
||||
m_with_prefix[padding_len + n..padding_len + 2 * n].copy_from_slice(&root[..n]);
|
||||
ull_to_bytes(
|
||||
&mut m_with_prefix[padding_len + 2 * n..padding_len + 3 * n],
|
||||
idx,
|
||||
);
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let total_len = mlen as usize + padding_len + 3 * n;
|
||||
core_hash(params, out, &m_with_prefix[..total_len])
|
||||
}
|
||||
|
||||
/// Tree hash function for internal nodes (two n-byte inputs).
|
||||
pub(crate) fn thash_h(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
input: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let padding_len = params.padding_len as usize;
|
||||
let buf_len = padding_len + 3 * n;
|
||||
let mut buf = vec![0u8; buf_len];
|
||||
let mut bitmask = vec![0u8; 2 * n];
|
||||
let mut addr_as_bytes = [0u8; 32];
|
||||
|
||||
ull_to_bytes(&mut buf[..padding_len], XMSS_HASH_PADDING_H);
|
||||
|
||||
set_key_and_mask(addr, 0);
|
||||
addr_to_bytes(&mut addr_as_bytes, addr);
|
||||
prf(
|
||||
params,
|
||||
&mut buf[padding_len..padding_len + n],
|
||||
&addr_as_bytes,
|
||||
pub_seed,
|
||||
)?;
|
||||
|
||||
set_key_and_mask(addr, 1);
|
||||
addr_to_bytes(&mut addr_as_bytes, addr);
|
||||
prf(params, &mut bitmask[..n], &addr_as_bytes, pub_seed)?;
|
||||
|
||||
set_key_and_mask(addr, 2);
|
||||
addr_to_bytes(&mut addr_as_bytes, addr);
|
||||
prf(params, &mut bitmask[n..2 * n], &addr_as_bytes, pub_seed)?;
|
||||
|
||||
for i in 0..2 * n {
|
||||
buf[padding_len + n + i] = input[i] ^ bitmask[i];
|
||||
}
|
||||
|
||||
core_hash(params, out, &buf)
|
||||
}
|
||||
|
||||
/// Tree hash function for WOTS chains (single n-byte input).
|
||||
pub(crate) fn thash_f(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
input: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let padding_len = params.padding_len as usize;
|
||||
let buf_len = padding_len + 2 * n;
|
||||
let mut buf = vec![0u8; buf_len];
|
||||
let mut bitmask = vec![0u8; n];
|
||||
let mut addr_as_bytes = [0u8; 32];
|
||||
|
||||
ull_to_bytes(&mut buf[..padding_len], XMSS_HASH_PADDING_F);
|
||||
|
||||
set_key_and_mask(addr, 0);
|
||||
addr_to_bytes(&mut addr_as_bytes, addr);
|
||||
prf(
|
||||
params,
|
||||
&mut buf[padding_len..padding_len + n],
|
||||
&addr_as_bytes,
|
||||
pub_seed,
|
||||
)?;
|
||||
|
||||
set_key_and_mask(addr, 1);
|
||||
addr_to_bytes(&mut addr_as_bytes, addr);
|
||||
prf(params, &mut bitmask, &addr_as_bytes, pub_seed)?;
|
||||
|
||||
for i in 0..n {
|
||||
buf[padding_len + n + i] = input[i] ^ bitmask[i];
|
||||
}
|
||||
|
||||
core_hash(params, out, &buf)
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
pub const XMSS_ADDR_TYPE_OTS: u32 = 0;
|
||||
pub const XMSS_ADDR_TYPE_LTREE: u32 = 1;
|
||||
pub const XMSS_ADDR_TYPE_HASHTREE: u32 = 2;
|
||||
|
||||
#[inline]
|
||||
pub fn set_layer_addr(addr: &mut [u32; 8], layer: u32) {
|
||||
addr[0] = layer;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_tree_addr(addr: &mut [u32; 8], tree: u64) {
|
||||
addr[1] = (tree >> 32) as u32;
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
{
|
||||
addr[2] = tree as u32;
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_type(addr: &mut [u32; 8], type_val: u32) {
|
||||
addr[3] = type_val;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_key_and_mask(addr: &mut [u32; 8], key_and_mask: u32) {
|
||||
addr[7] = key_and_mask;
|
||||
}
|
||||
|
||||
/// Copies the layer and tree part of one address into the other.
|
||||
#[inline]
|
||||
pub fn copy_subtree_addr(out: &mut [u32; 8], input: &[u32; 8]) {
|
||||
out[0] = input[0];
|
||||
out[1] = input[1];
|
||||
out[2] = input[2];
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ots_addr(addr: &mut [u32; 8], ots: u32) {
|
||||
addr[4] = ots;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_chain_addr(addr: &mut [u32; 8], chain: u32) {
|
||||
addr[5] = chain;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_hash_addr(addr: &mut [u32; 8], hash: u32) {
|
||||
addr[6] = hash;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_ltree_addr(addr: &mut [u32; 8], ltree: u32) {
|
||||
addr[4] = ltree;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_tree_height(addr: &mut [u32; 8], tree_height: u32) {
|
||||
addr[5] = tree_height;
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn set_tree_index(addr: &mut [u32; 8], tree_index: u32) {
|
||||
addr[6] = tree_index;
|
||||
}
|
||||
+441
@@ -0,0 +1,441 @@
|
||||
//! XMSS (eXtended Merkle Signature Scheme) implementation in Rust.
|
||||
//!
|
||||
//! Implements RFC 8391 XMSS and XMSSMT hash-based signature schemes.
|
||||
|
||||
mod error;
|
||||
mod hash;
|
||||
mod hash_address;
|
||||
mod params;
|
||||
#[cfg(feature = "pkcs8")]
|
||||
mod pkcs8;
|
||||
mod utils;
|
||||
mod wots;
|
||||
mod xmss;
|
||||
mod xmss_commons;
|
||||
mod xmss_core;
|
||||
|
||||
pub use error::{Error, XmssResult};
|
||||
|
||||
pub use params::{
|
||||
XmssMtSha2_20_2_192,
|
||||
// XMSSMT multi-tree parameter sets
|
||||
XmssMtSha2_20_2_256,
|
||||
XmssMtSha2_20_2_512,
|
||||
XmssMtSha2_20_4_192,
|
||||
XmssMtSha2_20_4_256,
|
||||
XmssMtSha2_20_4_512,
|
||||
XmssMtSha2_40_2_192,
|
||||
XmssMtSha2_40_2_256,
|
||||
XmssMtSha2_40_2_512,
|
||||
XmssMtSha2_40_4_192,
|
||||
XmssMtSha2_40_4_256,
|
||||
XmssMtSha2_40_4_512,
|
||||
XmssMtSha2_40_8_192,
|
||||
XmssMtSha2_40_8_256,
|
||||
XmssMtSha2_40_8_512,
|
||||
XmssMtSha2_60_3_192,
|
||||
XmssMtSha2_60_3_256,
|
||||
XmssMtSha2_60_3_512,
|
||||
XmssMtSha2_60_6_192,
|
||||
XmssMtSha2_60_6_256,
|
||||
XmssMtSha2_60_6_512,
|
||||
XmssMtSha2_60_12_192,
|
||||
XmssMtSha2_60_12_256,
|
||||
XmssMtSha2_60_12_512,
|
||||
XmssMtShake_20_2_256,
|
||||
XmssMtShake_20_2_512,
|
||||
XmssMtShake_20_4_256,
|
||||
XmssMtShake_20_4_512,
|
||||
XmssMtShake_40_2_256,
|
||||
XmssMtShake_40_2_512,
|
||||
XmssMtShake_40_4_256,
|
||||
XmssMtShake_40_4_512,
|
||||
XmssMtShake_40_8_256,
|
||||
XmssMtShake_40_8_512,
|
||||
XmssMtShake_60_3_256,
|
||||
XmssMtShake_60_3_512,
|
||||
XmssMtShake_60_6_256,
|
||||
XmssMtShake_60_6_512,
|
||||
XmssMtShake_60_12_256,
|
||||
XmssMtShake_60_12_512,
|
||||
XmssMtShake256_20_2_192,
|
||||
XmssMtShake256_20_2_256,
|
||||
XmssMtShake256_20_4_192,
|
||||
XmssMtShake256_20_4_256,
|
||||
XmssMtShake256_40_2_192,
|
||||
XmssMtShake256_40_2_256,
|
||||
XmssMtShake256_40_4_192,
|
||||
XmssMtShake256_40_4_256,
|
||||
XmssMtShake256_40_8_192,
|
||||
XmssMtShake256_40_8_256,
|
||||
XmssMtShake256_60_3_192,
|
||||
XmssMtShake256_60_3_256,
|
||||
XmssMtShake256_60_6_192,
|
||||
XmssMtShake256_60_6_256,
|
||||
XmssMtShake256_60_12_192,
|
||||
XmssMtShake256_60_12_256,
|
||||
XmssParameter,
|
||||
// XMSS single-tree parameter sets
|
||||
XmssSha2_10_192,
|
||||
XmssSha2_10_256,
|
||||
XmssSha2_10_512,
|
||||
XmssSha2_16_192,
|
||||
XmssSha2_16_256,
|
||||
XmssSha2_16_512,
|
||||
XmssSha2_20_192,
|
||||
XmssSha2_20_256,
|
||||
XmssSha2_20_512,
|
||||
XmssShake_10_256,
|
||||
XmssShake_10_512,
|
||||
XmssShake_16_256,
|
||||
XmssShake_16_512,
|
||||
XmssShake_20_256,
|
||||
XmssShake_20_512,
|
||||
XmssShake256_10_192,
|
||||
XmssShake256_10_256,
|
||||
XmssShake256_16_192,
|
||||
XmssShake256_16_256,
|
||||
XmssShake256_20_192,
|
||||
XmssShake256_20_256,
|
||||
};
|
||||
|
||||
pub use xmss::{DetachedSignature, KeyPair, Signature, SigningKey, VerifyingKey};
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_xmss_sha2_10_256_sign_verify() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
let message = b"test message";
|
||||
let sig = kp.signing_key().sign(message).unwrap();
|
||||
|
||||
let recovered = kp.verifying_key().verify(&sig).unwrap();
|
||||
assert_eq!(recovered, message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xmss_sha2_10_256_bad_signature() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
let message = b"test message";
|
||||
let sig = kp.signing_key().sign(message).unwrap();
|
||||
|
||||
// Corrupt the signature
|
||||
let mut sig_bytes = sig.as_ref().to_vec();
|
||||
sig_bytes[10] ^= 0xFF;
|
||||
let bad_sig = Signature::<XmssSha2_10_256>::try_from(sig_bytes).unwrap();
|
||||
|
||||
let result = kp.verifying_key().verify(&bad_sig);
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xmssmt_sha2_20_2_256_sign_verify() {
|
||||
let mut kp = KeyPair::<XmssMtSha2_20_2_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
let message = b"test message for xmssmt";
|
||||
let sig = kp.signing_key().sign(message).unwrap();
|
||||
|
||||
let recovered = kp.verifying_key().verify(&sig).unwrap();
|
||||
assert_eq!(recovered, message);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_multiple_signatures() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
for i in 0..3 {
|
||||
let msg = format!("message {}", i);
|
||||
let sig = kp.signing_key().sign(msg.as_bytes()).unwrap();
|
||||
let recovered = kp.verifying_key().verify(&sig).unwrap();
|
||||
assert_eq!(recovered, msg.as_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xmss_sign_detached_verify() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
let message = b"detached test message";
|
||||
let sig = kp.signing_key().sign_detached(message).unwrap();
|
||||
|
||||
// Detached signature should not contain the message
|
||||
let full_sig = kp.signing_key().sign(b"another").unwrap();
|
||||
assert!(sig.as_ref().len() < full_sig.as_ref().len());
|
||||
|
||||
kp.verifying_key().verify_detached(&sig, message).unwrap();
|
||||
|
||||
// Wrong message should fail
|
||||
assert!(
|
||||
kp.verifying_key()
|
||||
.verify_detached(&sig, b"wrong message")
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_xmss_verify_truncated_signature() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
let sig = kp.signing_key().sign(b"test message").unwrap();
|
||||
|
||||
// Truncate the signature to be too short
|
||||
let short_bytes = &sig.as_ref()[..sig.as_ref().len() / 2];
|
||||
let short_sig = Signature::<XmssSha2_10_256>::try_from(short_bytes).unwrap();
|
||||
|
||||
assert!(kp.verifying_key().verify(&short_sig).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_key_exhaustion() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
// Modify the index to be at the last valid position (2^10 - 1 = 1023).
|
||||
let mut sk_bytes = kp.signing_key().as_ref().to_vec();
|
||||
// Index is at bytes[4..8] (after OID), big-endian.
|
||||
sk_bytes[4] = 0x00;
|
||||
sk_bytes[5] = 0x00;
|
||||
sk_bytes[6] = 0x03;
|
||||
sk_bytes[7] = 0xFF; // 1023
|
||||
let mut last_sk = SigningKey::<XmssSha2_10_256>::try_from(sk_bytes).unwrap();
|
||||
|
||||
// Signing at the last index should succeed.
|
||||
let sig = last_sk.sign(b"last message").unwrap();
|
||||
let recovered = kp.verifying_key().verify(&sig).unwrap();
|
||||
assert_eq!(recovered, b"last message");
|
||||
|
||||
// Signing again should fail with KeyExhausted.
|
||||
let result = last_sk.sign(b"one more");
|
||||
assert!(result.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_deterministic_keygen() {
|
||||
// Sequential seed pattern: SK_SEED || SK_PRF || PUB_SEED
|
||||
let seed: Vec<u8> = (0u8..96).collect();
|
||||
|
||||
let kp1 = KeyPair::<XmssSha2_10_256>::from_seed(&seed).unwrap();
|
||||
let mut kp2 = KeyPair::<XmssSha2_10_256>::from_seed(&seed).unwrap();
|
||||
|
||||
// Same seed must produce identical keys.
|
||||
assert_eq!(kp1.verifying_key(), kp2.verifying_key());
|
||||
|
||||
// Sign with one, verify with the other's public key.
|
||||
let sig = kp2.signing_key().sign(b"deterministic test").unwrap();
|
||||
let recovered = kp1.verifying_key().verify(&sig).unwrap();
|
||||
assert_eq!(recovered, b"deterministic test");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verifying_key_from_signing_key() {
|
||||
let kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
|
||||
// Derive verifying key from signing key.
|
||||
let derived_pk = VerifyingKey::from(kp.signing_key_ref());
|
||||
assert_eq!(kp.verifying_key(), &derived_pk);
|
||||
}
|
||||
|
||||
/// Decodes a hex string to bytes. Panics on invalid input (test-only).
|
||||
fn hex_decode(s: &str) -> Vec<u8> {
|
||||
assert!(s.len() % 2 == 0, "hex string must have even length");
|
||||
(0..s.len())
|
||||
.step_by(2)
|
||||
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("invalid hex"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// KAT verification test using liboqs XMSS-SHA2_10_256 known-answer test vectors.
|
||||
#[test]
|
||||
fn test_kat_xmss_sha2_10_256_verify() {
|
||||
let pk_hex = "00000001B901B8D9332FE458EB6DE87AF74655D0B5AD936A66FDB6AC9D1B8CF25BB6DB8404562AD35E8ECAFAAFDA16981CDAA147606BEEA62801342AF13C8B5535F72F94";
|
||||
let msg_hex = "B338DD755D5618C464AB331F14DE3DD4A358BBA00D28FB35236741E902F7B248CE";
|
||||
let sm_hex = concat!(
|
||||
"00000000404DFF9B9F3931FE6158FFF355A8EE715C9BC6A87FE6627928F3CA10",
|
||||
"55FA7010C534B0D4C6FFDF4DBFE00E72405EFE83BBCF19AA2030A8CB16380848",
|
||||
"2B6376FF8CE01FB8090F4842896A1EA5E9282F35CACD245A4B9DE9FE84E93158",
|
||||
"51D68A72B3ECB9F440937C8BA4AC3F0429246CBC2777E8B92D84F4BA49FAB894",
|
||||
"65FCB0FC8017E582746F531B4697925154A22E2D6A0F1B81913438000C295153",
|
||||
"D7ADCA8F852C50D360F65F887479E9631A2CA30FE3AD92E7BF648643835F4F8C",
|
||||
"C081A6C951B83B77608A08C021821DA61962CFCC8E97D75441921D39C5AD5375",
|
||||
"43EFBAF0345DC70826E6E950929570C72E51619600C58D932A72657B19AF163E",
|
||||
"0B8F7AAF2949A5EB26C517909E0E663E36753491182975206009107509DFFC89",
|
||||
"8D308B903E84A8B29718BF7125397AFF5467D53CF8F36EB945B6B98D48E81C01",
|
||||
"74A0E03541D24369CF8EDDA4288FFA615D16FBC7355CFC0966BA9256E5B8A44D",
|
||||
"A95760DFB61301B10FD3E82436E267DB089773E43B984297D1E0D395DCC77FCF",
|
||||
"ECCEFEBD4B80B3F241872EA251DA466CA6C5324346F4B5E6886654A86592641A",
|
||||
"8C32AC554261B2D9130462C976B039E593F873AD1712820FF3E723FE57F13775",
|
||||
"1AB3CA8B5B20D28D1B9384DF1D710AC39FAF699989418B7856C2034C695A693E",
|
||||
"CC336EB472DE5049C743089529695B028F2F72BE0893E59169E9A2376C64BC5C",
|
||||
"CAC5482E5A6E9C88D710A3FF8F23C206B09D314BF50568228B1BACF1CE330D52",
|
||||
"9BD3793D7C7CD9EC770C111D9681D6F1B97D908CBBD436444853FEB47F234D31",
|
||||
"F5E92B9E0465D67AC0FE48859126BEFA7F7D121A67C2C2970B37B8081B4E73C5",
|
||||
"A21A41F60160A61FAFBD48649A3D2032C1679A67F348E3E25275FCD9AF650937",
|
||||
"FEB0A30F25878CEED7D6CA693518B5A2F5418135EA9316EFFDECDB1DFFC9EE3A",
|
||||
"62EFF0E66F3D05BD9D5F8679B536BB6D39792B28DF2481A6EECB9BEE40B11A10",
|
||||
"D39A90EA1AAC47BF956FBFE9B0427B599B9BC024F326515E71615419423FEC3F",
|
||||
"19F621D49B6EED59F129A6B1411B7B1AFCF073095D57B03F25A16F946ED716BF",
|
||||
"705F567A151BE85B8E8195CC2F070BFD482702182B8A4A43ED942F6BD3CBF9DE",
|
||||
"7E8AEB17C41E1C009C94FF4A2050E3731088B75474B38DC52BADF53C7DCD3FB9",
|
||||
"8D023649FC4799CE060ADDACEC7CD4E656074E631C1CB8AEF88EFEE0817C2E3D",
|
||||
"79E287F4510E48DFB7E23CB49D6FCA39A1E0F471F16A8BB65AF02150D059036D",
|
||||
"00386DD287BEA4D52FB263B57AE5ADD901CADE838B1D7347D9E47EAF6456148C",
|
||||
"6C4E44B0FA3DFCF5C9CEC2D80AD509A65AEF0E3E663B7F31BCA437311BA799D",
|
||||
"4C2ACC138F85D73CB40792FF03F8F20427D951444990CA3976A71368A7DC1455",
|
||||
"E880722F06F02163BC712E852A914F22E5675EB9B1C6C8B7FD20A8880AD2EEF9",
|
||||
"7982C065C937BD3639357E4C7450CBDA0B51CCA8E3E078DC760FD99EBF646B82",
|
||||
"369576539B2BD5B2C866ED5AE94423A5CE18C685352398D01C983F080D7BEB8A",
|
||||
"9243AAA9AC1DDCC1B058B92BEAD301E8F3B8F5EF71EEE7966302B44D2E26D2A0",
|
||||
"2393713E5D4D3FEF42196FAA368274C78C2932D22840ECA6018CE7D16B19A072",
|
||||
"7CB1966EB28B57D137C5264CC2E627F24A3BAD50EA4F75C7BD8998709C01ED5A",
|
||||
"CFFF0891934E94DA2CACCA212FB48BE3F9EAA310547E73C388D881F36AE21EFE",
|
||||
"DD23744F6B07C5D6D2776C191ED41E607316F61BBEF7A20E1A03150AE833D189",
|
||||
"52AE35188FBFDFA55C12A388836717BB2BDD97E89121C56C3B53E8198242315C",
|
||||
"9E438512E0C8354A3E599CB7217AE688647A72985606BBD0720F6FA5C5B6F70E",
|
||||
"88234EE54C6DB0A41106C866564650829FE4B232635B06B18240C9F86369C75B",
|
||||
"2F7D237211A380C43F95D362E0680D9EA2CA47E1DC8C49703E22650B765F847A",
|
||||
"D86BE25A3B7630D640A0097632DF13F600E8A025DD9A1FC67B0EB09C1CA9FA39",
|
||||
"23896927DEE1E3CC0C81F4B82E43B89CACC69C9B8ADCA1670F7D4E50DB7BCD94",
|
||||
"C2115E75F2BFD2336DA5A304D0F3455927360BF5040E95D1454106F2A8A7CD27",
|
||||
"D5510E7B5BE7B5B9EDEFDC3D4249D655C51F4C1DBA0F359BE4769AB66EDBC802",
|
||||
"824E9AB866E8EEAA2FEB1CC855F0A745AAC84A610DF0238112C6519F8E7346C4",
|
||||
"5331A6036F84D5B6250F4B5BC0A2A6A31DAF9C60EB13C20CC649A18E27A6C98B",
|
||||
"82F08E21706A8BDF338CC69C1679D25ECFF733A721211C1F6DD28091AAA9C93B",
|
||||
"047EFCD2C8A55F2DA65E616F07DCC0F44081D4E359C1688A00F062EC925D2443",
|
||||
"2862B547BB70F2AF126A3DABA5C918B224DE444B8733E6FA601B3D349307E945",
|
||||
"83D0EC976AEDA2B90972324B3ACE8C7B79A67723AEA037E12DA9EFA9CA9668A4",
|
||||
"F5FDADFB9EEE13398921F5023E354A6894825431DBA7317E6A6F69F0E77294BC",
|
||||
"D02D7616E75AC31EC528FC070B8C34027C4E9CD0672903412FCA6B723650D56A",
|
||||
"F562069312FC7EF1891A77E1A3F29D810C205EE212E75863F3B8B1ED216DF888",
|
||||
"ADD07AFF45F1B5C01196329311414797CD5F67FFC54AAD04C803FF7E83C2E8BA",
|
||||
"224CE83695BB7916AC42B1861F5CB527FDBCD82DBFA31C5ACF981D841420383750",
|
||||
"4263C96A0015841FBCC721F96D50A86D6E096AB54AF9980F06CEE6341C78D658",
|
||||
"3F6BAE8081B3C44B0F10FB7300874B5011FF0F97C52F975A31355884C2F12B6F",
|
||||
"FEE20E8371D38183C9D04977BFA037C9BD4DD7F7CE203FD7FAD3852B3C2AE9D0",
|
||||
"78ADEC70DB1A7140EF1114EBB03E8DE03237E0A27FF510015AC76FCEFE4EBD4C",
|
||||
"3A1B6C67DB2A82FE2B1BF18723DB0F29FE4AD47B2EEF22AC3C6661CFA7DA747",
|
||||
"6D23B470FA2E0441B6473EBD291791F09B4ADA70A5286EB05167BD59BFD8C464",
|
||||
"27413D60692382EFB7882F60DC53AAAFDF2014CA7D27F8FA93C187A8371B4179",
|
||||
"6557AE739912E5991C713532E81FA57F9BA562E1D3026D2D2D7373D99871BC62",
|
||||
"768AD70D3DB184EABED83E30C11C9BC62F3340923A0082B987EC45CC7BD1DB4B",
|
||||
"2B15E8AD3EAD74E96D8C20D85617BBEDC0BDAF8ED48B7EE8D7C42990028EC066",
|
||||
"9AFC0861C22F2E9109F9BB35426BDDB4A69EB8F45CD5B226F92E8026F1E62DE1",
|
||||
"DE435A4FC0CAEDA91C38A88F0037BDB296CD7B07FF040B1E08F02711E946B307",
|
||||
"A5A38487F53070985B8E28BE6CCE809F34100F0CA780996CD38E91BA7773BB63",
|
||||
"2D0BE7978F3AF3A92B961BD3A8759590726D6C1811F9E0BCA87377334E7C1F12",
|
||||
"FE37401CA0200823938C816ED98981521470F7F2CCDD69D85E7530EBF39E3A59",
|
||||
"2B1C09BC6C352C3FDB108FB26E7ACD3D5A4FC0442962E2C09651AC0D026E370F",
|
||||
"1EE1A8219C4833D70793D6E581FD25B0E95FAB1EDA67232C2FA12C4E379A6627",
|
||||
"E75AD408C1D2526005F2567CED8608E88CF53064FCDC58007198ADFA860F9FED",
|
||||
"1DF80EFACC768A0A063E1AFEE6DF1BE3483105B1C45EB50BF7863B4278422CEB",
|
||||
"A9001EA00299AC0415BF28A9C49CC2E92FC15565B547538A027886C6EB0D83B7",
|
||||
"1138CE1A",
|
||||
);
|
||||
|
||||
let pk_bytes = hex_decode(pk_hex);
|
||||
let msg_bytes = hex_decode(msg_hex);
|
||||
let sm_bytes = hex_decode(sm_hex);
|
||||
|
||||
assert_eq!(pk_bytes.len(), 68); // 4 OID + 32 root + 32 PUB_SEED
|
||||
assert_eq!(msg_bytes.len(), 33);
|
||||
assert_eq!(sm_bytes.len(), 2500);
|
||||
|
||||
let pk = VerifyingKey::<XmssSha2_10_256>::try_from(pk_bytes.as_slice())
|
||||
.expect("failed to parse KAT public key");
|
||||
let sig = DetachedSignature::<XmssSha2_10_256>::try_from(sm_bytes.as_slice())
|
||||
.expect("failed to parse KAT signature");
|
||||
|
||||
pk.verify_detached(&sig, &msg_bytes)
|
||||
.expect("KAT verification failed — signature should be valid");
|
||||
|
||||
// Also verify that a corrupted message fails.
|
||||
let mut bad_msg = msg_bytes.clone();
|
||||
bad_msg[0] ^= 0xFF;
|
||||
assert!(
|
||||
pk.verify_detached(&sig, &bad_msg).is_err(),
|
||||
"KAT verification should fail with corrupted message"
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
mod serde_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_signing_key_serde_json_roundtrip() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let sk = kp.signing_key();
|
||||
|
||||
let json = serde_json::to_string(&*sk).unwrap();
|
||||
let sk2: SigningKey<XmssSha2_10_256> = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(*sk, sk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verifying_key_serde_json_roundtrip() {
|
||||
let kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let pk = kp.verifying_key();
|
||||
|
||||
let json = serde_json::to_string(pk).unwrap();
|
||||
let pk2: VerifyingKey<XmssSha2_10_256> = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(*pk, pk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_serde_json_roundtrip() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let sig = kp.signing_key().sign(b"test message").unwrap();
|
||||
|
||||
let json = serde_json::to_string(&sig).unwrap();
|
||||
let sig2: Signature<XmssSha2_10_256> = serde_json::from_str(&json).unwrap();
|
||||
assert_eq!(sig, sig2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signing_key_postcard_roundtrip() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let sk = kp.signing_key();
|
||||
|
||||
let bytes = postcard::to_allocvec(&*sk).unwrap();
|
||||
let sk2: SigningKey<XmssSha2_10_256> = postcard::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(*sk, sk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_verifying_key_postcard_roundtrip() {
|
||||
let kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let pk = kp.verifying_key();
|
||||
|
||||
let bytes = postcard::to_allocvec(pk).unwrap();
|
||||
let pk2: VerifyingKey<XmssSha2_10_256> = postcard::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(*pk, pk2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_signature_postcard_roundtrip() {
|
||||
let mut kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let sig = kp.signing_key().sign(b"test message").unwrap();
|
||||
|
||||
let bytes = postcard::to_allocvec(&sig).unwrap();
|
||||
let sig2: Signature<XmssSha2_10_256> = postcard::from_bytes(&bytes).unwrap();
|
||||
assert_eq!(sig, sig2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "pkcs8")]
|
||||
mod pkcs8_tests {
|
||||
use super::*;
|
||||
use ::pkcs8::EncodePrivateKey;
|
||||
|
||||
#[test]
|
||||
fn test_pkcs8_roundtrip() {
|
||||
let kp = KeyPair::<XmssSha2_10_256>::generate(&mut rand::rng()).unwrap();
|
||||
let der = kp.to_pkcs8_der().expect("PKCS#8 encode failed");
|
||||
let kp2 = KeyPair::<XmssSha2_10_256>::from_pkcs8_der(der.as_bytes())
|
||||
.expect("PKCS#8 decode failed");
|
||||
assert_eq!(kp.verifying_key(), kp2.verifying_key());
|
||||
}
|
||||
}
|
||||
}
|
||||
+1465
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,103 @@
|
||||
//! PKCS#8 encoding/decoding support for XMSS keys and signatures.
|
||||
|
||||
use const_oid::ObjectIdentifier;
|
||||
use der::asn1::BitStringRef;
|
||||
use pkcs8::{AlgorithmIdentifierRef, EncodePrivateKey, PrivateKeyInfo};
|
||||
use spki::{EncodePublicKey, SubjectPublicKeyInfoRef};
|
||||
|
||||
use crate::error::Error;
|
||||
use crate::params::XmssParameter;
|
||||
use crate::xmss::{KeyPair, SigningKey, VerifyingKey};
|
||||
|
||||
/// OID for XMSS hash-based signatures: `id-alg-xmss-hashsig`
|
||||
/// 0.4.0.127.0.15.1.1.13.0
|
||||
const XMSS_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.15.1.1.13.0");
|
||||
|
||||
/// OID for XMSSMT hash-based signatures: `id-alg-xmssmt-hashsig`
|
||||
/// 0.4.0.127.0.15.1.1.14.0
|
||||
const XMSSMT_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("0.4.0.127.0.15.1.1.14.0");
|
||||
|
||||
/// Returns the appropriate ASN.1 OID for the given parameter set.
|
||||
fn algorithm_oid<P: XmssParameter>() -> ObjectIdentifier {
|
||||
let oid = P::oid();
|
||||
if oid.is_xmss() { XMSS_OID } else { XMSSMT_OID }
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> EncodePublicKey for VerifyingKey<P> {
|
||||
fn to_public_key_der(&self) -> spki::Result<der::Document> {
|
||||
use der::Encode;
|
||||
|
||||
let algorithm = AlgorithmIdentifierRef {
|
||||
oid: algorithm_oid::<P>(),
|
||||
parameters: None,
|
||||
};
|
||||
let pk_bytes = self.as_ref();
|
||||
let subject_public_key =
|
||||
BitStringRef::from_bytes(pk_bytes).map_err(|_| spki::Error::KeyMalformed)?;
|
||||
let spki_ref = SubjectPublicKeyInfoRef {
|
||||
algorithm,
|
||||
subject_public_key,
|
||||
};
|
||||
let der_bytes = spki_ref.to_der().map_err(|_| spki::Error::KeyMalformed)?;
|
||||
der::Document::try_from(der_bytes.as_slice()).map_err(|_| spki::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<SubjectPublicKeyInfoRef<'_>> for VerifyingKey<P> {
|
||||
type Error = spki::Error;
|
||||
|
||||
fn try_from(spki: SubjectPublicKeyInfoRef<'_>) -> Result<Self, Self::Error> {
|
||||
let expected_oid = algorithm_oid::<P>();
|
||||
if spki.algorithm.oid != expected_oid {
|
||||
return Err(spki::Error::OidUnknown {
|
||||
oid: spki.algorithm.oid,
|
||||
});
|
||||
}
|
||||
let pk_bytes = spki
|
||||
.subject_public_key
|
||||
.as_bytes()
|
||||
.ok_or(spki::Error::KeyMalformed)?;
|
||||
VerifyingKey::<P>::try_from(pk_bytes).map_err(|_| spki::Error::KeyMalformed)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> EncodePrivateKey for KeyPair<P> {
|
||||
fn to_pkcs8_der(&self) -> pkcs8::Result<der::SecretDocument> {
|
||||
let algo = AlgorithmIdentifierRef {
|
||||
oid: algorithm_oid::<P>(),
|
||||
parameters: None,
|
||||
};
|
||||
let sk_bytes = self.signing_key_ref().as_ref();
|
||||
let pk_bytes = self.verifying_key().as_ref();
|
||||
let pki = PrivateKeyInfo {
|
||||
algorithm: algo,
|
||||
private_key: sk_bytes,
|
||||
public_key: Some(pk_bytes),
|
||||
};
|
||||
pki.try_into()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> KeyPair<P> {
|
||||
/// Decodes a key pair from PKCS#8 DER bytes.
|
||||
pub fn from_pkcs8_der(der_bytes: &[u8]) -> crate::error::XmssResult<Self> {
|
||||
let pk_info = PrivateKeyInfo::try_from(der_bytes).map_err(|_| Error::InvalidKeyLength {
|
||||
expected: 0,
|
||||
got: der_bytes.len(),
|
||||
})?;
|
||||
|
||||
let expected_oid = algorithm_oid::<P>();
|
||||
if pk_info.algorithm.oid != expected_oid {
|
||||
return Err(Error::InvalidOid(0));
|
||||
}
|
||||
|
||||
let signing_key = SigningKey::<P>::try_from(pk_info.private_key)?;
|
||||
let verifying_key = if let Some(pk_bytes) = pk_info.public_key {
|
||||
VerifyingKey::<P>::try_from(pk_bytes)?
|
||||
} else {
|
||||
VerifyingKey::from(&signing_key)
|
||||
};
|
||||
|
||||
Ok(KeyPair::new(signing_key, verifying_key))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/// Converts the value of `val` to `outlen` bytes in big-endian byte order.
|
||||
pub(crate) fn ull_to_bytes(out: &mut [u8], val: u64) {
|
||||
let outlen = out.len();
|
||||
let mut v = val;
|
||||
for i in (0..outlen).rev() {
|
||||
out[i] = (v & 0xff) as u8;
|
||||
v >>= 8;
|
||||
}
|
||||
}
|
||||
|
||||
/// Converts the bytes in `input` from big-endian byte order to an integer.
|
||||
/// If `input` is longer than 8 bytes, only the first 8 bytes are used.
|
||||
pub(crate) fn bytes_to_ull(input: &[u8]) -> u64 {
|
||||
let mut retval: u64 = 0;
|
||||
for &byte in input.iter().take(8) {
|
||||
retval = (retval << 8) | u64::from(byte);
|
||||
}
|
||||
retval
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
use crate::error::XmssResult;
|
||||
use crate::hash::{addr_to_bytes, prf_keygen, thash_f};
|
||||
use crate::hash_address::{set_chain_addr, set_hash_addr, set_key_and_mask};
|
||||
use crate::params::XmssParams;
|
||||
use crate::utils::ull_to_bytes;
|
||||
|
||||
/// Expands an n-byte seed into a wots_len*n byte array using prf_keygen.
|
||||
fn expand_seed(
|
||||
params: &XmssParams,
|
||||
outseeds: &mut [u8],
|
||||
inseed: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut buf = vec![0u8; n + 32];
|
||||
|
||||
set_hash_addr(addr, 0);
|
||||
set_key_and_mask(addr, 0);
|
||||
buf[..n].copy_from_slice(&pub_seed[..n]);
|
||||
|
||||
for i in 0..params.wots_len {
|
||||
set_chain_addr(addr, i);
|
||||
addr_to_bytes(&mut buf[n..n + 32], addr);
|
||||
prf_keygen(
|
||||
params,
|
||||
&mut outseeds[i as usize * n..(i as usize + 1) * n],
|
||||
&buf,
|
||||
inseed,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes the chaining function.
|
||||
/// Interprets `input` as start-th value of the chain.
|
||||
fn gen_chain(
|
||||
params: &XmssParams,
|
||||
out: &mut [u8],
|
||||
input: &[u8],
|
||||
start: u32,
|
||||
steps: u32,
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
|
||||
out[..n].copy_from_slice(&input[..n]);
|
||||
|
||||
let mut i = start;
|
||||
while i < start + steps && i < params.wots_w {
|
||||
set_hash_addr(addr, i);
|
||||
let mut tmp = vec![0u8; n];
|
||||
tmp.copy_from_slice(&out[..n]);
|
||||
thash_f(params, out, &tmp, pub_seed, addr)?;
|
||||
i += 1;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// base_w algorithm as described in draft.
|
||||
/// Interprets an array of bytes as integers in base w.
|
||||
fn base_w(params: &XmssParams, output: &mut [u32], input: &[u8]) {
|
||||
let out_len = output.len();
|
||||
let mut in_idx = 0;
|
||||
let mut total: u8 = 0;
|
||||
let mut bits: u32 = 0;
|
||||
|
||||
for out_val in output.iter_mut().take(out_len) {
|
||||
if bits == 0 {
|
||||
total = input[in_idx];
|
||||
in_idx += 1;
|
||||
bits += 8;
|
||||
}
|
||||
bits -= params.wots_log_w;
|
||||
// wots_w is always a power of 2 <= 256, so (wots_w - 1) fits in u8.
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let mask = (params.wots_w - 1) as u8;
|
||||
*out_val = u32::from((total >> bits) & mask);
|
||||
}
|
||||
}
|
||||
|
||||
/// Computes the WOTS+ checksum over a message (in base_w).
|
||||
fn wots_checksum(params: &XmssParams, csum_base_w: &mut [u32], msg_base_w: &[u32]) {
|
||||
let mut csum: u32 = 0;
|
||||
|
||||
for val in msg_base_w.iter().take(params.wots_len1 as usize) {
|
||||
csum += params.wots_w - 1 - val;
|
||||
}
|
||||
|
||||
csum <<= 8 - ((params.wots_len2 * params.wots_log_w) % 8);
|
||||
let csum_bytes_len = (params.wots_len2 * params.wots_log_w).div_ceil(8) as usize;
|
||||
let mut csum_bytes = vec![0u8; csum_bytes_len];
|
||||
ull_to_bytes(&mut csum_bytes, u64::from(csum));
|
||||
base_w(params, csum_base_w, &csum_bytes);
|
||||
}
|
||||
|
||||
/// Takes a message and derives the matching chain lengths.
|
||||
fn chain_lengths(params: &XmssParams, lengths: &mut [u32], msg: &[u8]) {
|
||||
let len1 = params.wots_len1 as usize;
|
||||
base_w(params, &mut lengths[..len1], msg);
|
||||
let (msg_part, csum_part) = lengths.split_at_mut(len1);
|
||||
wots_checksum(params, csum_part, msg_part);
|
||||
}
|
||||
|
||||
/// WOTS key generation. Takes a 32 byte seed for the private key, expands it to
|
||||
/// a full WOTS private key and computes the corresponding public key.
|
||||
pub fn wots_pkgen(
|
||||
params: &XmssParams,
|
||||
pk: &mut [u8],
|
||||
seed: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
|
||||
expand_seed(params, pk, seed, pub_seed, addr)?;
|
||||
|
||||
for i in 0..params.wots_len as usize {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
set_chain_addr(addr, i as u32);
|
||||
let mut tmp = vec![0u8; n];
|
||||
tmp.copy_from_slice(&pk[i * n..(i + 1) * n]);
|
||||
gen_chain(
|
||||
params,
|
||||
&mut pk[i * n..],
|
||||
&tmp,
|
||||
0,
|
||||
params.wots_w - 1,
|
||||
pub_seed,
|
||||
addr,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes a n-byte message and the 32-byte seed for the private key to compute a
|
||||
/// signature that is placed at 'sig'.
|
||||
pub fn wots_sign(
|
||||
params: &XmssParams,
|
||||
sig: &mut [u8],
|
||||
msg: &[u8],
|
||||
seed: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut lengths = vec![0u32; params.wots_len as usize];
|
||||
|
||||
chain_lengths(params, &mut lengths, msg);
|
||||
|
||||
expand_seed(params, sig, seed, pub_seed, addr)?;
|
||||
|
||||
for i in 0..params.wots_len as usize {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
set_chain_addr(addr, i as u32);
|
||||
let mut tmp = vec![0u8; n];
|
||||
tmp.copy_from_slice(&sig[i * n..(i + 1) * n]);
|
||||
gen_chain(
|
||||
params,
|
||||
&mut sig[i * n..],
|
||||
&tmp,
|
||||
0,
|
||||
lengths[i],
|
||||
pub_seed,
|
||||
addr,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Takes a WOTS signature and an n-byte message, computes a WOTS public key.
|
||||
pub fn wots_pk_from_sig(
|
||||
params: &XmssParams,
|
||||
pk: &mut [u8],
|
||||
sig: &[u8],
|
||||
msg: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut lengths = vec![0u32; params.wots_len as usize];
|
||||
|
||||
chain_lengths(params, &mut lengths, msg);
|
||||
|
||||
for i in 0..params.wots_len as usize {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
set_chain_addr(addr, i as u32);
|
||||
gen_chain(
|
||||
params,
|
||||
&mut pk[i * n..],
|
||||
&sig[i * n..],
|
||||
lengths[i],
|
||||
params.wots_w - 1 - lengths[i],
|
||||
pub_seed,
|
||||
addr,
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,662 @@
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use hybrid_array::Array;
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::error::{Error, XmssResult};
|
||||
use crate::params::{XMSS_OID_LEN, XmssOid, XmssParameter, XmssParams};
|
||||
use crate::xmss_commons;
|
||||
use crate::xmss_core;
|
||||
|
||||
/// Parses the OID and initializes params from the first bytes of a key.
|
||||
/// Tries XMSS OIDs first, then falls back to XMSSMT.
|
||||
fn parse_oid_and_params(bytes: &[u8]) -> XmssResult<(XmssOid, XmssParams)> {
|
||||
if bytes.len() < XMSS_OID_LEN {
|
||||
return Err(Error::InvalidOid(0));
|
||||
}
|
||||
let mut raw_oid: u32 = 0;
|
||||
for i in 0..XMSS_OID_LEN {
|
||||
raw_oid |= (bytes[XMSS_OID_LEN - i - 1] as u32) << (i * 8);
|
||||
}
|
||||
let oid = XmssOid::try_from(raw_oid).or_else(|_| XmssOid::from_xmssmt_raw_oid(raw_oid))?;
|
||||
let mut params = XmssParams::default();
|
||||
oid.initialize(&mut params)?;
|
||||
Ok((oid, params))
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// SigningKey<P>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// An XMSS signing key (secret key).
|
||||
#[derive(Clone)]
|
||||
pub struct SigningKey<P: XmssParameter> {
|
||||
bytes: Array<u8, P::SkLen>,
|
||||
params: XmssParams,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> core::fmt::Debug for SigningKey<P> {
|
||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||
f.debug_struct("SigningKey")
|
||||
.field("parameter_set", &P::NAME)
|
||||
.field("bytes", &"[REDACTED]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> Drop for SigningKey<P> {
|
||||
fn drop(&mut self) {
|
||||
self.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> Zeroize for SigningKey<P> {
|
||||
fn zeroize(&mut self) {
|
||||
self.bytes.zeroize();
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> AsRef<[u8]> for SigningKey<P> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.bytes.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&[u8]> for SigningKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
let (oid, params) = parse_oid_and_params(value)?;
|
||||
let expected_oid = P::oid();
|
||||
if oid != expected_oid {
|
||||
return Err(Error::InvalidOid(oid.raw_oid()));
|
||||
}
|
||||
if value.len() != P::SK_LEN {
|
||||
return Err(Error::InvalidKeyLength {
|
||||
expected: P::SK_LEN,
|
||||
got: value.len(),
|
||||
});
|
||||
}
|
||||
let bytes = Array::try_from(value).map_err(|_| Error::InvalidKeyLength {
|
||||
expected: P::SK_LEN,
|
||||
got: value.len(),
|
||||
})?;
|
||||
Ok(Self {
|
||||
bytes,
|
||||
params,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Vec<u8>> for SigningKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
SigningKey::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&Vec<u8>> for SigningKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
SigningKey::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Box<[u8]>> for SigningKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
|
||||
SigningKey::<P>::try_from(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<P: XmssParameter> PartialEq for SigningKey<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.bytes[..] == other.bytes[..]
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<P: XmssParameter> Eq for SigningKey<P> {}
|
||||
|
||||
#[cfg(test)]
|
||||
impl<P: XmssParameter> core::hash::Hash for SigningKey<P> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.bytes[..].hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<P: XmssParameter> serdect::serde::Serialize for SigningKey<P> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serdect::serde::Serializer,
|
||||
{
|
||||
serdect::slice::serialize_hex_lower_or_bin(&self.bytes, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, P: XmssParameter> serdect::serde::Deserialize<'de> for SigningKey<P> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serdect::serde::Deserializer<'de>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
|
||||
Self::try_from(bytes).map_err(serdect::serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> SigningKey<P> {
|
||||
pub(crate) fn new(bytes: Array<u8, P::SkLen>, params: XmssParams) -> Self {
|
||||
Self {
|
||||
bytes,
|
||||
params,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Signs a message. Returns the signature followed by the message and updates
|
||||
/// the internal state (one-time key index is advanced).
|
||||
pub fn sign(&mut self, m: &[u8]) -> XmssResult<Signature<P>> {
|
||||
xmss_core::xmssmt_core_sign(&self.params, &mut self.bytes[XMSS_OID_LEN..], m).map(|bytes| {
|
||||
Signature {
|
||||
bytes,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Signs a message, returning only the detached signature (without the message appended).
|
||||
pub fn sign_detached(&mut self, m: &[u8]) -> XmssResult<DetachedSignature<P>> {
|
||||
let mut sm = xmss_core::xmssmt_core_sign(&self.params, &mut self.bytes[XMSS_OID_LEN..], m)?;
|
||||
let sig_bytes = sm.len() - m.len();
|
||||
sm.truncate(sig_bytes);
|
||||
Ok(DetachedSignature {
|
||||
bytes: sm.into_boxed_slice(),
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> signature::SignerMut<DetachedSignature<P>> for SigningKey<P> {
|
||||
fn try_sign(&mut self, msg: &[u8]) -> Result<DetachedSignature<P>, signature::Error> {
|
||||
self.sign_detached(msg).map_err(|_| signature::Error::new())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> signature::Keypair for SigningKey<P> {
|
||||
type VerifyingKey = VerifyingKey<P>;
|
||||
|
||||
fn verifying_key(&self) -> Self::VerifyingKey {
|
||||
VerifyingKey::from(self)
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// VerifyingKey<P>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// An XMSS verifying key (public key).
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct VerifyingKey<P: XmssParameter> {
|
||||
bytes: Array<u8, P::VkLen>,
|
||||
params: XmssParams,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> AsRef<[u8]> for VerifyingKey<P> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.bytes.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&[u8]> for VerifyingKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
let (oid, params) = parse_oid_and_params(value)?;
|
||||
let expected_oid = P::oid();
|
||||
if oid != expected_oid {
|
||||
return Err(Error::InvalidOid(oid.raw_oid()));
|
||||
}
|
||||
if value.len() != P::VK_LEN {
|
||||
return Err(Error::InvalidKeyLength {
|
||||
expected: P::VK_LEN,
|
||||
got: value.len(),
|
||||
});
|
||||
}
|
||||
let bytes = Array::try_from(value).map_err(|_| Error::InvalidKeyLength {
|
||||
expected: P::VK_LEN,
|
||||
got: value.len(),
|
||||
})?;
|
||||
Ok(Self {
|
||||
bytes,
|
||||
params,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Vec<u8>> for VerifyingKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
VerifyingKey::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&Vec<u8>> for VerifyingKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
VerifyingKey::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Box<[u8]>> for VerifyingKey<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
|
||||
VerifyingKey::<P>::try_from(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> PartialEq for VerifyingKey<P> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.bytes[..] == other.bytes[..]
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> Eq for VerifyingKey<P> {}
|
||||
|
||||
impl<P: XmssParameter> core::hash::Hash for VerifyingKey<P> {
|
||||
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||
self.bytes[..].hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<P: XmssParameter> serdect::serde::Serialize for VerifyingKey<P> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serdect::serde::Serializer,
|
||||
{
|
||||
serdect::slice::serialize_hex_lower_or_bin(&self.bytes, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, P: XmssParameter> serdect::serde::Deserialize<'de> for VerifyingKey<P> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serdect::serde::Deserializer<'de>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
|
||||
Self::try_from(bytes).map_err(serdect::serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> VerifyingKey<P> {
|
||||
pub(crate) fn new(bytes: Array<u8, P::VkLen>, params: XmssParams) -> Self {
|
||||
Self {
|
||||
bytes,
|
||||
params,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Verifies a given message signature pair.
|
||||
/// Returns the verified message on success.
|
||||
pub fn verify(&self, signature: &Signature<P>) -> XmssResult<Vec<u8>> {
|
||||
let mut m = Vec::new();
|
||||
xmss_commons::xmssmt_core_sign_open(
|
||||
&self.params,
|
||||
&mut m,
|
||||
&signature.bytes,
|
||||
&self.bytes[XMSS_OID_LEN..],
|
||||
)?;
|
||||
Ok(m)
|
||||
}
|
||||
|
||||
/// Verifies a detached signature against the provided message.
|
||||
pub fn verify_detached(&self, signature: &DetachedSignature<P>, m: &[u8]) -> XmssResult<()> {
|
||||
let mut sm = Vec::with_capacity(signature.bytes.len() + m.len());
|
||||
sm.extend_from_slice(&signature.bytes);
|
||||
sm.extend_from_slice(m);
|
||||
let mut msg = Vec::new();
|
||||
xmss_commons::xmssmt_core_sign_open(
|
||||
&self.params,
|
||||
&mut msg,
|
||||
&sm,
|
||||
&self.bytes[XMSS_OID_LEN..],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> From<&SigningKey<P>> for VerifyingKey<P> {
|
||||
fn from(sk: &SigningKey<P>) -> Self {
|
||||
let n = sk.params.n as usize;
|
||||
let idx_bytes = sk.params.index_bytes as usize;
|
||||
|
||||
// sk after OID: [idx || SK_SEED || SK_PRF || root || PUB_SEED]
|
||||
let root_start = XMSS_OID_LEN + idx_bytes + 2 * n;
|
||||
|
||||
// pk: [OID || root || PUB_SEED]
|
||||
let mut pk = Array::<u8, P::VkLen>::default();
|
||||
pk[..XMSS_OID_LEN].copy_from_slice(&sk.bytes[..XMSS_OID_LEN]);
|
||||
pk[XMSS_OID_LEN..].copy_from_slice(&sk.bytes[root_start..root_start + 2 * n]);
|
||||
|
||||
VerifyingKey {
|
||||
bytes: pk,
|
||||
params: sk.params,
|
||||
_marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> signature::Verifier<DetachedSignature<P>> for VerifyingKey<P> {
|
||||
fn verify(&self, msg: &[u8], signature: &DetachedSignature<P>) -> Result<(), signature::Error> {
|
||||
self.verify_detached(signature, msg)
|
||||
.map_err(|_| signature::Error::new())
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Signature<P>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// An XMSS signature (signature + message, variable length).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct Signature<P: XmssParameter> {
|
||||
bytes: Vec<u8>,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> AsRef<[u8]> for Signature<P> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&[u8]> for Signature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
Ok(Signature {
|
||||
bytes: value.to_vec(),
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Vec<u8>> for Signature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Ok(Signature {
|
||||
bytes: value,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&Vec<u8>> for Signature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
Signature::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Box<[u8]>> for Signature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
|
||||
Signature::<P>::try_from(value.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> From<Signature<P>> for Vec<u8> {
|
||||
fn from(sig: Signature<P>) -> Vec<u8> {
|
||||
sig.bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<P: XmssParameter> serdect::serde::Serialize for Signature<P> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serdect::serde::Serializer,
|
||||
{
|
||||
serdect::slice::serialize_hex_lower_or_bin(&self.bytes, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, P: XmssParameter> serdect::serde::Deserialize<'de> for Signature<P> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serdect::serde::Deserializer<'de>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
|
||||
Self::try_from(bytes).map_err(serdect::serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> signature::SignatureEncoding for Signature<P> {
|
||||
type Repr = Vec<u8>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// DetachedSignature<P>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// A fixed-size detached XMSS signature (without appended message).
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub struct DetachedSignature<P: XmssParameter> {
|
||||
bytes: Box<[u8]>,
|
||||
_marker: PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> AsRef<[u8]> for DetachedSignature<P> {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.bytes
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&[u8]> for DetachedSignature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
if value.len() != P::SIG_LEN {
|
||||
return Err(Error::InvalidSignatureLength {
|
||||
expected: P::SIG_LEN,
|
||||
got: value.len(),
|
||||
});
|
||||
}
|
||||
Ok(DetachedSignature {
|
||||
bytes: value.into(),
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Vec<u8>> for DetachedSignature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
|
||||
if value.len() != P::SIG_LEN {
|
||||
return Err(Error::InvalidSignatureLength {
|
||||
expected: P::SIG_LEN,
|
||||
got: value.len(),
|
||||
});
|
||||
}
|
||||
Ok(DetachedSignature {
|
||||
bytes: value.into_boxed_slice(),
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<&Vec<u8>> for DetachedSignature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
|
||||
DetachedSignature::<P>::try_from(value.as_slice())
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> TryFrom<Box<[u8]>> for DetachedSignature<P> {
|
||||
type Error = Error;
|
||||
|
||||
fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
|
||||
if value.len() != P::SIG_LEN {
|
||||
return Err(Error::InvalidSignatureLength {
|
||||
expected: P::SIG_LEN,
|
||||
got: value.len(),
|
||||
});
|
||||
}
|
||||
Ok(DetachedSignature {
|
||||
bytes: value,
|
||||
_marker: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> From<DetachedSignature<P>> for Vec<u8> {
|
||||
fn from(sig: DetachedSignature<P>) -> Vec<u8> {
|
||||
sig.bytes.into_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> From<DetachedSignature<P>> for Box<[u8]> {
|
||||
fn from(sig: DetachedSignature<P>) -> Box<[u8]> {
|
||||
sig.bytes
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<P: XmssParameter> serdect::serde::Serialize for DetachedSignature<P> {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||
where
|
||||
S: serdect::serde::Serializer,
|
||||
{
|
||||
serdect::slice::serialize_hex_lower_or_bin(&self.bytes, serializer)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "serde")]
|
||||
impl<'de, P: XmssParameter> serdect::serde::Deserialize<'de> for DetachedSignature<P> {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serdect::serde::Deserializer<'de>,
|
||||
{
|
||||
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
|
||||
Self::try_from(bytes).map_err(serdect::serde::de::Error::custom)
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> signature::SignatureEncoding for DetachedSignature<P> {
|
||||
type Repr = Box<[u8]>;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// KeyPair<P>
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// An XMSS key pair containing both signing and verifying keys.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct KeyPair<P: XmssParameter> {
|
||||
signing_key: SigningKey<P>,
|
||||
verifying_key: VerifyingKey<P>,
|
||||
}
|
||||
|
||||
impl<P: XmssParameter> KeyPair<P> {
|
||||
#[cfg(feature = "pkcs8")]
|
||||
pub(crate) fn new(signing_key: SigningKey<P>, verifying_key: VerifyingKey<P>) -> Self {
|
||||
Self {
|
||||
signing_key,
|
||||
verifying_key,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generates a random key pair for the parameter set `P`.
|
||||
pub fn generate<R: rand::CryptoRng>(rng: &mut R) -> XmssResult<Self> {
|
||||
let oid = P::oid();
|
||||
let (params, mut pk, mut sk) = oid.init_keypair_buffers(None)?;
|
||||
xmss_core::xmssmt_core_keypair(
|
||||
¶ms,
|
||||
&mut pk[XMSS_OID_LEN..],
|
||||
&mut sk[XMSS_OID_LEN..],
|
||||
rng,
|
||||
)?;
|
||||
let pk_array = Array::<u8, P::VkLen>::try_from(pk.as_slice()).map_err(|_| {
|
||||
Error::InvalidKeyLength {
|
||||
expected: P::VK_LEN,
|
||||
got: pk.len(),
|
||||
}
|
||||
})?;
|
||||
let sk_array = Array::<u8, P::SkLen>::try_from(sk.as_slice()).map_err(|_| {
|
||||
Error::InvalidKeyLength {
|
||||
expected: P::SK_LEN,
|
||||
got: sk.len(),
|
||||
}
|
||||
})?;
|
||||
Ok(Self {
|
||||
verifying_key: VerifyingKey::new(pk_array, params),
|
||||
signing_key: SigningKey::new(sk_array, params),
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a key pair from a deterministic seed.
|
||||
/// Seed must be `P::SEED_LEN` bytes (3*n).
|
||||
pub fn from_seed(seed: &[u8]) -> XmssResult<Self> {
|
||||
let oid = P::oid();
|
||||
let (params, mut pk, mut sk) = oid.init_keypair_buffers(Some(seed))?;
|
||||
xmss_core::xmssmt_core_seed_keypair(
|
||||
¶ms,
|
||||
&mut pk[XMSS_OID_LEN..],
|
||||
&mut sk[XMSS_OID_LEN..],
|
||||
seed,
|
||||
)?;
|
||||
let pk_array = Array::<u8, P::VkLen>::try_from(pk.as_slice()).map_err(|_| {
|
||||
Error::InvalidKeyLength {
|
||||
expected: P::VK_LEN,
|
||||
got: pk.len(),
|
||||
}
|
||||
})?;
|
||||
let sk_array = Array::<u8, P::SkLen>::try_from(sk.as_slice()).map_err(|_| {
|
||||
Error::InvalidKeyLength {
|
||||
expected: P::SK_LEN,
|
||||
got: sk.len(),
|
||||
}
|
||||
})?;
|
||||
Ok(Self {
|
||||
verifying_key: VerifyingKey::new(pk_array, params),
|
||||
signing_key: SigningKey::new(sk_array, params),
|
||||
})
|
||||
}
|
||||
|
||||
/// Returns a mutable reference to the signing key.
|
||||
pub fn signing_key(&mut self) -> &mut SigningKey<P> {
|
||||
&mut self.signing_key
|
||||
}
|
||||
|
||||
/// Returns a reference to the verifying key.
|
||||
pub fn verifying_key(&self) -> &VerifyingKey<P> {
|
||||
&self.verifying_key
|
||||
}
|
||||
|
||||
/// Returns a shared reference to the signing key (non-mut).
|
||||
#[cfg(any(feature = "pkcs8", test))]
|
||||
pub(crate) fn signing_key_ref(&self) -> &SigningKey<P> {
|
||||
&self.signing_key
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,218 @@
|
||||
use subtle::ConstantTimeEq;
|
||||
|
||||
use crate::error::{Error, XmssResult};
|
||||
use crate::hash::{hash_message, thash_h};
|
||||
use crate::hash_address::*;
|
||||
use crate::params::XmssParams;
|
||||
use crate::utils::bytes_to_ull;
|
||||
use crate::wots::{wots_pk_from_sig, wots_pkgen};
|
||||
|
||||
/// Computes a leaf node from a WOTS public key using an L-tree.
|
||||
/// Note that this destroys the used WOTS public key.
|
||||
fn l_tree(
|
||||
params: &XmssParams,
|
||||
leaf: &mut [u8],
|
||||
wots_pk: &mut [u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut l = params.wots_len as usize;
|
||||
let mut height: u32 = 0;
|
||||
|
||||
set_tree_height(addr, height);
|
||||
|
||||
while l > 1 {
|
||||
let parent_nodes = l >> 1;
|
||||
for i in 0..parent_nodes {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
set_tree_index(addr, i as u32);
|
||||
let mut tmp = vec![0u8; 2 * n];
|
||||
tmp.copy_from_slice(&wots_pk[i * 2 * n..(i * 2 + 2) * n]);
|
||||
thash_h(
|
||||
params,
|
||||
&mut wots_pk[i * n..(i + 1) * n],
|
||||
&tmp,
|
||||
pub_seed,
|
||||
addr,
|
||||
)?;
|
||||
}
|
||||
if l & 1 != 0 {
|
||||
let src_start = (l - 1) * n;
|
||||
let dst_start = (l >> 1) * n;
|
||||
let mut tmp = vec![0u8; n];
|
||||
tmp.copy_from_slice(&wots_pk[src_start..src_start + n]);
|
||||
wots_pk[dst_start..dst_start + n].copy_from_slice(&tmp);
|
||||
l = (l >> 1) + 1;
|
||||
} else {
|
||||
l >>= 1;
|
||||
}
|
||||
height += 1;
|
||||
set_tree_height(addr, height);
|
||||
}
|
||||
leaf[..n].copy_from_slice(&wots_pk[..n]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Computes a root node given a leaf and an auth path.
|
||||
fn compute_root(
|
||||
params: &XmssParams,
|
||||
root: &mut [u8],
|
||||
leaf: &[u8],
|
||||
mut leafidx: u32,
|
||||
auth_path: &[u8],
|
||||
pub_seed: &[u8],
|
||||
addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut buffer = vec![0u8; 2 * n];
|
||||
let mut auth_offset = 0usize;
|
||||
|
||||
if leafidx & 1 != 0 {
|
||||
buffer[n..2 * n].copy_from_slice(&leaf[..n]);
|
||||
buffer[..n].copy_from_slice(&auth_path[..n]);
|
||||
} else {
|
||||
buffer[..n].copy_from_slice(&leaf[..n]);
|
||||
buffer[n..2 * n].copy_from_slice(&auth_path[..n]);
|
||||
}
|
||||
auth_offset += n;
|
||||
|
||||
for i in 0..params.tree_height - 1 {
|
||||
set_tree_height(addr, i);
|
||||
leafidx >>= 1;
|
||||
set_tree_index(addr, leafidx);
|
||||
|
||||
if leafidx & 1 != 0 {
|
||||
let tmp = buffer.clone();
|
||||
thash_h(params, &mut buffer[n..2 * n], &tmp, pub_seed, addr)?;
|
||||
buffer[..n].copy_from_slice(&auth_path[auth_offset..auth_offset + n]);
|
||||
} else {
|
||||
let tmp = buffer.clone();
|
||||
thash_h(params, &mut buffer[..n], &tmp, pub_seed, addr)?;
|
||||
buffer[n..2 * n].copy_from_slice(&auth_path[auth_offset..auth_offset + n]);
|
||||
}
|
||||
auth_offset += n;
|
||||
}
|
||||
|
||||
set_tree_height(addr, params.tree_height - 1);
|
||||
leafidx >>= 1;
|
||||
set_tree_index(addr, leafidx);
|
||||
thash_h(params, root, &buffer, pub_seed, addr)
|
||||
}
|
||||
|
||||
/// Computes the leaf at a given address. First generates the WOTS key pair,
|
||||
/// then computes leaf using l_tree.
|
||||
pub fn gen_leaf_wots(
|
||||
params: &XmssParams,
|
||||
leaf: &mut [u8],
|
||||
sk_seed: &[u8],
|
||||
pub_seed: &[u8],
|
||||
ltree_addr: &mut [u32; 8],
|
||||
ots_addr: &mut [u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let mut pk = vec![0u8; params.wots_sig_bytes as usize];
|
||||
|
||||
wots_pkgen(params, &mut pk, sk_seed, pub_seed, ots_addr)?;
|
||||
l_tree(params, leaf, &mut pk, pub_seed, ltree_addr)
|
||||
}
|
||||
|
||||
/// Verifies a given message signature pair under a given public key.
|
||||
/// Note that this assumes a pk without an OID, i.e. [root || PUB_SEED].
|
||||
pub fn xmssmt_core_sign_open(
|
||||
params: &XmssParams,
|
||||
m: &mut Vec<u8>,
|
||||
sm: &[u8],
|
||||
pk: &[u8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let pub_root = &pk[..n];
|
||||
let pub_seed = &pk[n..2 * n];
|
||||
|
||||
let smlen = sm.len();
|
||||
if smlen < params.sig_bytes as usize {
|
||||
return Err(Error::VerificationFailed);
|
||||
}
|
||||
let mlen = smlen - params.sig_bytes as usize;
|
||||
|
||||
let mut wots_pk = vec![0u8; params.wots_sig_bytes as usize];
|
||||
let mut leaf = vec![0u8; n];
|
||||
let mut root = vec![0u8; n];
|
||||
|
||||
let mut ots_addr = [0u32; 8];
|
||||
let mut ltree_addr = [0u32; 8];
|
||||
let mut node_addr = [0u32; 8];
|
||||
|
||||
set_type(&mut ots_addr, XMSS_ADDR_TYPE_OTS);
|
||||
set_type(&mut ltree_addr, XMSS_ADDR_TYPE_LTREE);
|
||||
set_type(&mut node_addr, XMSS_ADDR_TYPE_HASHTREE);
|
||||
|
||||
let idx = bytes_to_ull(&sm[..params.index_bytes as usize]);
|
||||
|
||||
let prefix_len = params.padding_len as usize + 3 * n;
|
||||
m.resize(params.sig_bytes as usize + mlen, 0);
|
||||
m[params.sig_bytes as usize..].copy_from_slice(&sm[params.sig_bytes as usize..]);
|
||||
|
||||
let mhash = &mut root;
|
||||
let prefix_start = params.sig_bytes as usize - prefix_len;
|
||||
hash_message(
|
||||
params,
|
||||
mhash,
|
||||
&sm[params.index_bytes as usize..],
|
||||
pk,
|
||||
idx,
|
||||
&mut m[prefix_start..],
|
||||
mlen as u64,
|
||||
)?;
|
||||
|
||||
let mut sm_offset = params.index_bytes as usize + n;
|
||||
|
||||
for i in 0..params.d {
|
||||
#[allow(clippy::cast_possible_truncation)] // masked to tree_height bits, always fits u32
|
||||
let idx_leaf =
|
||||
((idx >> (params.tree_height * i)) & ((1u64 << params.tree_height) - 1)) as u32;
|
||||
let tree_idx = idx >> (params.tree_height * (i + 1));
|
||||
|
||||
set_layer_addr(&mut ots_addr, i);
|
||||
set_layer_addr(&mut ltree_addr, i);
|
||||
set_layer_addr(&mut node_addr, i);
|
||||
|
||||
set_tree_addr(&mut ltree_addr, tree_idx);
|
||||
set_tree_addr(&mut ots_addr, tree_idx);
|
||||
set_tree_addr(&mut node_addr, tree_idx);
|
||||
|
||||
set_ots_addr(&mut ots_addr, idx_leaf);
|
||||
wots_pk_from_sig(
|
||||
params,
|
||||
&mut wots_pk,
|
||||
&sm[sm_offset..],
|
||||
&root,
|
||||
pub_seed,
|
||||
&mut ots_addr,
|
||||
)?;
|
||||
sm_offset += params.wots_sig_bytes as usize;
|
||||
|
||||
set_ltree_addr(&mut ltree_addr, idx_leaf);
|
||||
l_tree(params, &mut leaf, &mut wots_pk, pub_seed, &mut ltree_addr)?;
|
||||
|
||||
compute_root(
|
||||
params,
|
||||
&mut root,
|
||||
&leaf,
|
||||
idx_leaf,
|
||||
&sm[sm_offset..],
|
||||
pub_seed,
|
||||
&mut node_addr,
|
||||
)?;
|
||||
sm_offset += params.tree_height as usize * n;
|
||||
}
|
||||
|
||||
if !bool::from(root.ct_eq(pub_root)) {
|
||||
m.clear();
|
||||
return Err(Error::VerificationFailed);
|
||||
}
|
||||
|
||||
let msg = sm[params.sig_bytes as usize..].to_vec();
|
||||
*m = msg;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,274 @@
|
||||
use zeroize::Zeroize;
|
||||
|
||||
use crate::error::{Error, XmssResult};
|
||||
use crate::hash::{hash_message, prf, thash_h};
|
||||
use crate::hash_address::*;
|
||||
use crate::params::XmssParams;
|
||||
use crate::utils::{bytes_to_ull, ull_to_bytes};
|
||||
use crate::wots::wots_sign;
|
||||
use crate::xmss_commons::gen_leaf_wots;
|
||||
|
||||
/// For a given leaf index, computes the authentication path and the resulting
|
||||
/// root node using Merkle's TreeHash algorithm.
|
||||
fn treehash(
|
||||
params: &XmssParams,
|
||||
root: &mut [u8],
|
||||
auth_path: &mut [u8],
|
||||
sk_seed: &[u8],
|
||||
pub_seed: &[u8],
|
||||
leaf_idx: u32,
|
||||
subtree_addr: &[u32; 8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let tree_height = params.tree_height as usize;
|
||||
let mut stack = vec![0u8; (tree_height + 1) * n];
|
||||
let mut heights = vec![0u32; tree_height + 1];
|
||||
let mut offset: usize = 0;
|
||||
|
||||
let mut ots_addr = [0u32; 8];
|
||||
let mut ltree_addr = [0u32; 8];
|
||||
let mut node_addr = [0u32; 8];
|
||||
|
||||
copy_subtree_addr(&mut ots_addr, subtree_addr);
|
||||
copy_subtree_addr(&mut ltree_addr, subtree_addr);
|
||||
copy_subtree_addr(&mut node_addr, subtree_addr);
|
||||
|
||||
set_type(&mut ots_addr, XMSS_ADDR_TYPE_OTS);
|
||||
set_type(&mut ltree_addr, XMSS_ADDR_TYPE_LTREE);
|
||||
set_type(&mut node_addr, XMSS_ADDR_TYPE_HASHTREE);
|
||||
|
||||
let num_leaves: u32 = 1 << params.tree_height;
|
||||
for idx in 0..num_leaves {
|
||||
set_ltree_addr(&mut ltree_addr, idx);
|
||||
set_ots_addr(&mut ots_addr, idx);
|
||||
gen_leaf_wots(
|
||||
params,
|
||||
&mut stack[offset * n..(offset + 1) * n],
|
||||
sk_seed,
|
||||
pub_seed,
|
||||
&mut ltree_addr,
|
||||
&mut ots_addr,
|
||||
)?;
|
||||
offset += 1;
|
||||
heights[offset - 1] = 0;
|
||||
|
||||
if (leaf_idx ^ 0x1) == idx {
|
||||
auth_path[..n].copy_from_slice(&stack[(offset - 1) * n..offset * n]);
|
||||
}
|
||||
|
||||
while offset >= 2 && heights[offset - 1] == heights[offset - 2] {
|
||||
let tree_idx = idx >> (heights[offset - 1] + 1);
|
||||
|
||||
set_tree_height(&mut node_addr, heights[offset - 1]);
|
||||
set_tree_index(&mut node_addr, tree_idx);
|
||||
let tmp = stack[(offset - 2) * n..offset * n].to_vec();
|
||||
thash_h(
|
||||
params,
|
||||
&mut stack[(offset - 2) * n..(offset - 1) * n],
|
||||
&tmp,
|
||||
pub_seed,
|
||||
&mut node_addr,
|
||||
)?;
|
||||
offset -= 1;
|
||||
heights[offset - 1] += 1;
|
||||
|
||||
if ((leaf_idx >> heights[offset - 1]) ^ 0x1) == tree_idx {
|
||||
let h = heights[offset - 1] as usize;
|
||||
auth_path[h * n..(h + 1) * n].copy_from_slice(&stack[(offset - 1) * n..offset * n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
root[..n].copy_from_slice(&stack[..n]);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Given a set of parameters, this function returns the size of the secret key.
|
||||
pub fn xmss_xmssmt_core_sk_bytes(params: &XmssParams) -> u64 {
|
||||
params.index_bytes as u64 + 4 * params.n as u64
|
||||
}
|
||||
|
||||
/// Derives a XMSSMT key pair from a given seed.
|
||||
/// Seed must be 3*n long.
|
||||
/// Format sk: [(ceil(h/8) bit) index || SK_SEED || SK_PRF || root || PUB_SEED]
|
||||
/// Format pk: [root || PUB_SEED] omitting algorithm OID.
|
||||
pub fn xmssmt_core_seed_keypair(
|
||||
params: &XmssParams,
|
||||
pk: &mut [u8],
|
||||
sk: &mut [u8],
|
||||
seed: &[u8],
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let idx_bytes = params.index_bytes as usize;
|
||||
let tree_height = params.tree_height as usize;
|
||||
let mut auth_path = vec![0u8; tree_height * n];
|
||||
let mut top_tree_addr = [0u32; 8];
|
||||
set_layer_addr(&mut top_tree_addr, params.d - 1);
|
||||
|
||||
for b in sk[..idx_bytes].iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
|
||||
sk[idx_bytes..idx_bytes + 2 * n].copy_from_slice(&seed[..2 * n]);
|
||||
|
||||
sk[idx_bytes + 3 * n..idx_bytes + 4 * n].copy_from_slice(&seed[2 * n..3 * n]);
|
||||
pk[n..2 * n].copy_from_slice(&sk[idx_bytes + 3 * n..idx_bytes + 4 * n]);
|
||||
|
||||
// Copy pub_seed since pk is mutably borrowed by treehash.
|
||||
let pub_seed_copy = pk[n..2 * n].to_vec();
|
||||
treehash(
|
||||
params,
|
||||
pk,
|
||||
&mut auth_path,
|
||||
&sk[idx_bytes..],
|
||||
&pub_seed_copy,
|
||||
0,
|
||||
&top_tree_addr,
|
||||
)?;
|
||||
sk[idx_bytes + 2 * n..idx_bytes + 3 * n].copy_from_slice(&pk[..n]);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Generates a XMSSMT key pair for a given parameter set.
|
||||
/// Format sk: [(ceil(h/8) bit) index || SK_SEED || SK_PRF || root || PUB_SEED]
|
||||
/// Format pk: [root || PUB_SEED] omitting algorithm OID.
|
||||
pub fn xmssmt_core_keypair<R: rand::CryptoRng>(
|
||||
params: &XmssParams,
|
||||
pk: &mut [u8],
|
||||
sk: &mut [u8],
|
||||
rng: &mut R,
|
||||
) -> XmssResult<()> {
|
||||
let n = params.n as usize;
|
||||
let mut seed = vec![0u8; 3 * n];
|
||||
|
||||
rng.fill_bytes(&mut seed[..]);
|
||||
let result = xmssmt_core_seed_keypair(params, pk, sk, &seed);
|
||||
seed.zeroize();
|
||||
result
|
||||
}
|
||||
|
||||
/// Signs a message. Returns the signature followed by the message
|
||||
/// and an updated secret key.
|
||||
pub fn xmssmt_core_sign(params: &XmssParams, sk: &mut [u8], m: &[u8]) -> XmssResult<Vec<u8>> {
|
||||
let n = params.n as usize;
|
||||
let idx_bytes = params.index_bytes as usize;
|
||||
let mlen = m.len();
|
||||
let sig_bytes = params.sig_bytes as usize;
|
||||
|
||||
let sk_seed_start = idx_bytes;
|
||||
let sk_prf_start = idx_bytes + n;
|
||||
let pub_root_start = idx_bytes + 2 * n;
|
||||
let pub_seed_start = idx_bytes + 3 * n;
|
||||
|
||||
let idx = bytes_to_ull(&sk[..idx_bytes]);
|
||||
|
||||
// Check if key is exhausted before doing anything.
|
||||
let max_idx = if params.full_height >= 64 {
|
||||
u64::MAX
|
||||
} else {
|
||||
(1u64 << params.full_height) - 1
|
||||
};
|
||||
if idx > max_idx {
|
||||
return Err(Error::KeyExhausted);
|
||||
}
|
||||
|
||||
// Copy secret values out before mutating sk.
|
||||
let mut sk_seed = sk[sk_seed_start..sk_seed_start + n].to_vec();
|
||||
let mut sk_prf = sk[sk_prf_start..sk_prf_start + n].to_vec();
|
||||
let pub_root = sk[pub_root_start..pub_root_start + n].to_vec();
|
||||
let pub_seed = sk[pub_seed_start..pub_seed_start + n].to_vec();
|
||||
|
||||
let mut sm = vec![0u8; sig_bytes + mlen];
|
||||
|
||||
let mut ots_addr = [0u32; 8];
|
||||
set_type(&mut ots_addr, XMSS_ADDR_TYPE_OTS);
|
||||
|
||||
sm[sig_bytes..].copy_from_slice(m);
|
||||
|
||||
// Write index into signature.
|
||||
sm[..idx_bytes].copy_from_slice(&sk[..idx_bytes]);
|
||||
|
||||
// Advance the index in sk.
|
||||
if idx == max_idx {
|
||||
// Last valid index — mark as exhausted for next call.
|
||||
for b in sk[..idx_bytes].iter_mut() {
|
||||
*b = 0xFF;
|
||||
}
|
||||
} else {
|
||||
ull_to_bytes(&mut sk[..idx_bytes], idx + 1);
|
||||
}
|
||||
|
||||
// Compute R (randomness for message hashing).
|
||||
let mut idx_bytes_32 = [0u8; 32];
|
||||
ull_to_bytes(&mut idx_bytes_32, idx);
|
||||
prf(
|
||||
params,
|
||||
&mut sm[idx_bytes..idx_bytes + n],
|
||||
&idx_bytes_32,
|
||||
&sk_prf,
|
||||
)?;
|
||||
|
||||
let mut root = vec![0u8; n];
|
||||
let prefix_len = params.padding_len as usize + 3 * n;
|
||||
let prefix_start = sig_bytes - prefix_len;
|
||||
// Copy R out to avoid borrow conflict (sm is both read for R and mutated for prefix).
|
||||
let r_val = sm[idx_bytes..idx_bytes + n].to_vec();
|
||||
hash_message(
|
||||
params,
|
||||
&mut root,
|
||||
&r_val,
|
||||
&pub_root,
|
||||
idx,
|
||||
&mut sm[prefix_start..],
|
||||
mlen as u64,
|
||||
)?;
|
||||
|
||||
let mut sm_offset = idx_bytes + n;
|
||||
|
||||
for i in 0..params.d {
|
||||
let idx_leaf = (idx >> (params.tree_height * i)) & ((1u64 << params.tree_height) - 1);
|
||||
#[allow(clippy::cast_possible_truncation)] // masked to tree_height bits, always fits u32
|
||||
let idx_leaf = idx_leaf as u32;
|
||||
let tree_idx = idx >> (params.tree_height * (i + 1));
|
||||
|
||||
set_layer_addr(&mut ots_addr, i);
|
||||
set_tree_addr(&mut ots_addr, tree_idx);
|
||||
set_ots_addr(&mut ots_addr, idx_leaf);
|
||||
|
||||
wots_sign(
|
||||
params,
|
||||
&mut sm[sm_offset..],
|
||||
&root,
|
||||
&sk_seed,
|
||||
&pub_seed,
|
||||
&mut ots_addr,
|
||||
)?;
|
||||
sm_offset += params.wots_sig_bytes as usize;
|
||||
|
||||
treehash(
|
||||
params,
|
||||
&mut root,
|
||||
&mut sm[sm_offset..],
|
||||
&sk_seed,
|
||||
&pub_seed,
|
||||
idx_leaf,
|
||||
&ots_addr,
|
||||
)?;
|
||||
sm_offset += params.tree_height as usize * n;
|
||||
}
|
||||
|
||||
// Zeroize secret copies.
|
||||
sk_seed.zeroize();
|
||||
sk_prf.zeroize();
|
||||
|
||||
// If this was the last valid index, zero the secret key material in sk.
|
||||
if idx == max_idx {
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
let sk_bytes_len = params.sk_bytes as usize;
|
||||
for b in sk[idx_bytes..sk_bytes_len].iter_mut() {
|
||||
*b = 0;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(sm)
|
||||
}
|
||||
Reference in New Issue
Block a user