diff --git a/.github/workflows/publish_doc_benches_to_ghpages.yaml b/.github/workflows/publish_doc_benches_to_ghpages.yaml index 80e7d87..819803b 100644 --- a/.github/workflows/publish_doc_benches_to_ghpages.yaml +++ b/.github/workflows/publish_doc_benches_to_ghpages.yaml @@ -37,21 +37,22 @@ jobs: with: name: code_quality_stats path: ./code_stats.txt - run_benches: - runs-on: ubuntu-latest - if: github.ref == 'refs/heads/main' - steps: - - name: Checkout code - uses: actions/checkout@v4 - - run: cargo bench --all - - name: Save artifacts - uses: actions/upload-artifact@v4 - with: - name: bc-rust-benches - path: ./target/criterion + # the benches run crazy slow on the github agent. So there's really no point because it's not useful data. + # run_benches: + # runs-on: ubuntu-latest + # if: github.ref == 'refs/heads/main' + # steps: + # - name: Checkout code + # uses: actions/checkout@v4 + # - run: cargo bench --all + # - name: Save artifacts + # uses: actions/upload-artifact@v4 + # with: + # name: bc-rust-benches + # path: ./target/criterion collect_ghpages: if: github.ref == 'refs/heads/main' - needs: [build_docs, code_stats, run_benches] + needs: [build_docs, code_stats] runs-on: ubuntu-latest steps: - run: mkdir ./gh-pages @@ -65,11 +66,11 @@ jobs: with: name: code_quality_stats path: ./gh-pages/ - - name: Get benches from previous job - uses: actions/download-artifact@v4 - with: - name: bc-rust-benches - path: ./gh-pages/benches + # - name: Get benches from previous job + # uses: actions/download-artifact@v4 + # with: + # name: bc-rust-benches + # path: ./gh-pages/benches - name: Archive Compatibility Matrix For Download uses: actions/upload-pages-artifact@v3 with: diff --git a/.github/workflows/rust-style.yml b/.github/workflows/rust-style.yml new file mode 100644 index 0000000..4ca5776 --- /dev/null +++ b/.github/workflows/rust-style.yml @@ -0,0 +1,28 @@ +name: Rust Style + +on: + pull_request: + +permissions: + contents: read + +concurrency: + group: rust-style-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + rustfmt: + if: github.repository == 'bcgit/bc-rust' + name: rustfmt + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install nightly rustfmt + run: | + rustup toolchain install nightly --profile minimal --component rustfmt + rustup override set nightly + + - name: Check formatting + run: cargo fmt --all --check diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 104f2ff..d402afd 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -20,6 +20,23 @@ before posting anything public. See [Security Policy](SECURITY.md). If a related discussion or issue doesn't exist, and the issue is not security related, you can [open a new issue](https://github.com/bcgit/bc-java/issues/new). An issue can be converted into a discussion if regarded as one. +## Coding philosophy + +> Slow is smooth, smooth is fast. + +There is a time and a place for "Move fast and break things", but the source code of a crypto library is not one of them. + +This project takes the philosophy that taking the time to do things right pays off in the long run, both in terms of +the runtime and memory footprint of the code, and it terms of the time required for a future maintainer to get up to speed with the code +and avoid introducing bugs due to the code being hard to understand. + +Some specifics: + +* Respect that the innovative process sometimes requires exploring several dead-ends before you find the most elegant solution. +* Public APIs of a library should be both ergonomic and expressive. When defining a new trait or public function, ask yourself whether a programmer who is new to cryptography is likely to use this in a way that will get them into trouble. +* Variables should be well-named, well-structured, and well-commented (a comment-to-code ration of 1:1 is a goal to be strived for!). Think about memory footprint and, where possible, use unnamed scopes to allow the compiler to pop intermediate value variables off the stack as soon as they are no longer needed. +* Always run your code through `cargo mutants` and get the issue count as low as your can. As a first pass, this forces you to write thorough unit tests. As a second pass, this draws your attention to bits of your code that cannot be tested from the outside. Often this means that the code can be simplified without affecting functionality (as defined by your set of unit tests) -- "simpler code" usually means faster runtime and easier future maintenance. + ## Contribute to the code For substantial, non-trivial contributions, you may be asked to sign a contributor assignment agreement. Optionally, you can also have your name and contact information listed in [Contributors](https://www.bouncycastle.org/contributors.html). @@ -56,5 +73,5 @@ Don't forget to self-review. Please follow these simple guidelines: #### Your pull request is merged -For acceptance, pull requests need to meet specific quality criteria, including tests for anything substantial. Someone on the Bouncy Castle core team will review the pull request when there is time, and let you know if something is missing or suggest improvements. If it is a useful and generic feature it will be integrated in Bouncy Castle to be available in a later release. +Someone on the Bouncy Castle core team will review the pull request when there is time, and let you know if something is missing or suggest improvements. If it is a useful and generic feature it will be integrated in Bouncy Castle to be available in a later release. diff --git a/Cargo.toml b/Cargo.toml index ecf3f90..9ce118b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = [ "cli", "crypto/*", "mem_usage_benches" ] +members = ["cli", "crypto/*", "mem_usage_benches"] [workspace.package] edition = "2024" @@ -8,21 +8,24 @@ edition = "2024" # *** Internal Dependencies *** bouncycastle = { path = "./", version = "0.1.1" } -bouncycastle-base64 = { path = "./crypto/base64", version = "0.1.1"} -bouncycastle-core = { path = "crypto/core", version = "0.1.1" } -bouncycastle-core-test-framework = { path = "./crypto/core-test-framework", version = "0.1.1"} -bouncycastle-factory = { path = "./crypto/factory", version = "0.1.1"} +bouncycastle-base64 = { path = "./crypto/base64", version = "0.1.1" } +# `default-features = false` here (rather than on each member) is required so that individual +# crates can opt out of the `alloc` feature for `no_std` builds; members re-enable it through +# their own `alloc` feature. Building the whole workspace keeps `alloc` on via feature unification. +bouncycastle-core = { path = "crypto/core", version = "0.1.1", default-features = false } +bouncycastle-core-test-framework = { path = "./crypto/core-test-framework", version = "0.1.1" } +bouncycastle-factory = { path = "./crypto/factory", version = "0.1.1" } bouncycastle-hex = { path = "./crypto/hex", version = "0.1.1" } -bouncycastle-hkdf = { path = "./crypto/hkdf", version = "0.1.1"} -bouncycastle-hmac = { path = "./crypto/hmac", version = "0.1.1"} +bouncycastle-hkdf = { path = "./crypto/hkdf", version = "0.1.1" } +bouncycastle-hmac = { path = "./crypto/hmac", version = "0.1.1", default-features = false } bouncycastle-mlkem = { path = "./crypto/mlkem", version = "0.1.2" } -bouncycastle-mlkem-lowmemory = { path = "./crypto/mlkem_lowmemory", version = "0.1.2" } +bouncycastle-mlkem-lowmemory = { path = "./crypto/mlkem-lowmemory", version = "0.1.2" } bouncycastle-mldsa = { path = "./crypto/mldsa", version = "0.1.2" } -bouncycastle-mldsa-lowmemory = { path = "./crypto/mldsa_lowmemory", version = "0.1.2" } +bouncycastle-mldsa-lowmemory = { path = "./crypto/mldsa-lowmemory", version = "0.1.2" } bouncycastle-rng = { path = "./crypto/rng", version = "0.1.1" } -bouncycastle-sha2 = { path = "./crypto/sha2", version = "0.1.1"} -bouncycastle-sha3 = { path = "./crypto/sha3", version = "0.1.1"} -bouncycastle-utils = { path = "./crypto/utils", version = "0.1.1"} +bouncycastle-sha2 = { path = "./crypto/sha2", version = "0.1.1", default-features = false } +bouncycastle-sha3 = { path = "./crypto/sha3", version = "0.1.1", default-features = false } +bouncycastle-utils = { path = "./crypto/utils", version = "0.1.1" } # *** External Dependencies *** @@ -39,17 +42,20 @@ name = "bouncycastle" version = "0.1.2" edition.workspace = true +# The umbrella crate re-exports the whole library for downstream users who want everything in one +# dependency, so it enables `alloc` on the primitive crates whose workspace deps default it off. +# (base64/hex/hkdf/rng/factory already default `alloc` on.) [dependencies] bouncycastle-base64.workspace = true -bouncycastle-core.workspace = true +bouncycastle-core = { workspace = true, features = ["alloc"] } bouncycastle-factory.workspace = true bouncycastle-hex.workspace = true bouncycastle-hkdf.workspace = true -bouncycastle-hmac.workspace = true +bouncycastle-hmac = { workspace = true, features = ["alloc"] } bouncycastle-mldsa.workspace = true bouncycastle-mldsa-lowmemory.workspace = true bouncycastle-mlkem.workspace = true bouncycastle-mlkem-lowmemory.workspace = true bouncycastle-rng.workspace = true -bouncycastle-sha2.workspace = true -bouncycastle-sha3.workspace = true \ No newline at end of file +bouncycastle-sha2 = { workspace = true, features = ["alloc"] } +bouncycastle-sha3 = { workspace = true, features = ["alloc"] } \ No newline at end of file diff --git a/INTRODUCTION.md b/INTRODUCTION.md index a6cc415..13d816b 100644 --- a/INTRODUCTION.md +++ b/INTRODUCTION.md @@ -123,7 +123,7 @@ While the `KeyMaterial` is fundamentally just a buffer of bytes, it tracks many The `KeyMaterial` object is used consistently across the library and any functions that manipulate a key material object will properly update the metadata to track any changes made to the key's entropy or security strength. For example, a `KeyMaterial512{ key_type: MACKey, security_strength: _256bit}` will have its security strength downgraded to 128 bit if you pass it through a SHA256-based KDF, indicating that it is no longer sufficient to generate a full-strength AES256 or ML-DSA-87. -Of course, there will always be things developers need to do that the library did not provide a utility function for, for example, you may actually need an all-zero MACKey in order to implement certain standardized MAC algorithms. To the end, the library will allow you to, for example, force a key type to any full-entropy key type and security strength, or even get a direct immutable or mutable reference to the underlying buffer via `.ref_to_bytes()` and `mut_ref_to_bytes()`, but only with use of the `allow_hazardous_operations` flag: +Of course, there will always be things developers need to do that the library did not provide a utility function for, for example, you may actually need an all-zero MACKey in order to implement certain standardized MAC algorithms. To the end, the library will allow you to, for example, force a key type to any full-entropy key type and security strength, or even get a direct immutable or mutable reference to the underlying buffer via `.ref_to_bytes()` and `ref_to_bytes_mut()`, but only with use of the `allow_hazardous_operations` flag: ```rust key.allow_hazardous_operations(); @@ -202,4 +202,4 @@ As this is an alpha release, we're eagerly looking for feedback from the communi You can reach us at Sincerely, -Mike Ounsworth (lead maintainer of BC-Rust), on behalf of the Legion of the Bouncy Castle and the entire Bouncy Castle community \ No newline at end of file +Mike Ounsworth (lead maintainer of BC-Rust), on behalf of the Legion of the Bouncy Castle and the entire Bouncy Castle community diff --git a/QUALITY_AND_STYLE.md b/QUALITY_AND_STYLE.md index 94036d0..77e9c08 100644 --- a/QUALITY_AND_STYLE.md +++ b/QUALITY_AND_STYLE.md @@ -1,75 +1,123 @@ This document lists general quality and style guidelines used across the library. Hint: ask an AI to help review your PR against this style guide. - # Architecture The Bounce Castle Rust project should be broken up into individual modular crates named `bouncycastle_*`. -The project aims to be completely self-contained with zero external dependencies in the runtime code. External dependencies are ok in test or benchmarking code. +The project aims to be completely self-contained with zero external dependencies in the runtime code. External +dependencies are ok in test or benchmarking code. lib.rs for all crates needs to contain: `#![forbid(missing_docs)]`, `#![no_std]`. - All primitives must be accompanied by a CLI in `/cli`. +All algorithms with a state that is exercised across multiple API calls -- typically through a `do_update()`, +`do_final()` pattern -- should implement SerializableState so that the user can pause the execution of this algorithm to +a cache and resume later. # Quality ## Tests -All crates must have tests in `src/tests`. Part of writing code that treats future maintainers as malicious is that all functions that form part of the public interface should have their expected behaviour fully constrained with tests. In other words, any behaviour change of the library that could cause a change in a calling application should also cause a test in bc-rust to fail. An excellent tool for achieving this is `cargo mutants` which must be run on every crate and each failed mutant must be investigated. We do not require `cargo mutants` to be clean because it's reasonably common, especially in low-level crypto code, that there are multiple correct ways to write the same code; for example where swapping an OR for an XOR results in functionally equivalent code. +All crates must have tests in `src/tests`. Part of writing code that treats future maintainers as malicious is that all +functions that form part of the public interface should have their expected behaviour fully constrained with tests. In +other words, any behaviour change of the library that could cause a change in a calling application should also cause a +test in bc-rust to fail. An excellent tool for achieving this is `cargo mutants` which must be run on every crate and +each failed mutant must be investigated. We do not require `cargo mutants` to be clean because it's reasonably common, +especially in low-level crypto code, that there are multiple correct ways to write the same code; for example where +swapping an OR for an XOR results in functionally equivalent code. -Where the behaviour of a function is critical to test but cannot be tested from outside the crate because it is on a private function, in-line tests in the source file should be used. +Where the behaviour of a function is critical to test but cannot be tested from outside the crate because it is on a +private function, in-line tests in the source file should be used. -All traits in `bouncycastle-core` must have corresponding tests in `bouncycastle-core-test-framework` that exercise all behaviours and error conditions that are comment to all implementations of that trait. +All traits in `bouncycastle-core` must have corresponding tests in `bouncycastle-core-test-framework` that exercise all +behaviours and error conditions that are comment to all implementations of that trait. +All crypto algorithms must have tests against the bc-test-data repo and against wycheproof. ## Performance Benchmarks -Any crate that contains an algorithm were runtime matters must have cargo-compatible performance benchmarks in a `/benches` folder. +Any crate that contains an algorithm were runtime matters must have cargo-compatible performance benchmarks in a +`/benches` folder. -The benches must cover all algorithms. If there are multiple variants of an algorithm with different performance characteristics (such as with pre-expanded keys), then these must each be benchmarked separately. Separate benchmarks should not be written for different APIs for accessing the same underlying implementation; such as one-shot and streaming APIs that use the same core algorithm implementation. +The benches must cover all algorithms. If there are multiple variants of an algorithm with different performance +characteristics (such as with pre-expanded keys), then these must each be benchmarked separately. Separate benchmarks +should not be written for different APIs for accessing the same underlying implementation; such as one-shot and +streaming APIs that use the same core algorithm implementation. ## Stack Usage Benchmarks -Bouncy Castle Rust cares about the peak stack memory usage of its algorithms. Crates should be accompanied by a memory usage test harness in `/mem_usage_benches`. +Bouncy Castle Rust cares about the peak stack memory usage of its algorithms. Crates should be accompanied by a memory +usage test harness in `/mem_usage_benches`. # Style -Part of writing code that treats future maintainers as malicious is good inline comments. Anything even remotely tricky, or where naive modification would put it out of alignment with, for example, sample code in an RFC or FIPS spec should be commented line-by-line with the corresponding lines from the spec. This also helps with code review and certification. Any deviations from the spec should be noted and explained / justified. A good rule-of-thumb is to ask yourself whether this function would take 6-months-from-now-you more than 10 minutes to understand thoroughly, and are there comments you could add that would help future you get back up to speed faster about what this code is doing and which parts were done for a very specific reason and should not be changed on a whim. - +Part of writing code that treats future maintainers as malicious is good inline comments. Anything even remotely tricky, +or where naive modification would put it out of alignment with, for example, sample code in an RFC or FIPS spec should +be commented line-by-line with the corresponding lines from the spec. This also helps with code review and +certification. Any deviations from the spec should be noted and explained / justified. A good rule-of-thumb is to ask +yourself whether this function would take 6-months-from-now-you more than 10 minutes to understand thoroughly, and are +there comments you could add that would help future you get back up to speed faster about what this code is doing and +which parts were done for a very specific reason and should not be changed on a whim. ## APIs -Where possible, primitives should expose "one-shot APIs" that simply take data and return a result as a static member function that does not require object instantiation. +Where possible, primitives should expose "one-shot APIs" that simply take data and return a result as a static member +function that does not require object instantiation. -Other version of Bouncy Castle have a design pattern where stateful objects follow a pattern of new() -> init() -> do_update() -> do_final(), and then optionally reset() that sets the object back to an unitialized state. Instead, bc-rust does not have init() functions (moving this logic into new() or from() as appropriate), and consequently it also does not have reset(). Also, we take advantage of the rust borrow checker's syntax so that all do_final() functions are actually final, in other words they must take ownership of self `do_final(self, ...)` so that no subsequent calls can be made to this object (as opposed to the usual pattern of taking a ref to self as in `do_update(&self, ...)`). These tricks go a long way to reducing fallibility since now in general there is no (or very very little) object state to track and return errors about. +Other version of Bouncy Castle have a design pattern where stateful objects follow a pattern of new() -> init() -> +do_update() -> do_final(), and then optionally reset() that sets the object back to an unitialized state. Instead, +bc-rust does not have init() functions (moving this logic into new() or from() as appropriate), and consequently it also +does not have reset(). Also, we take advantage of the rust borrow checker's syntax so that all do_final() functions are +actually final, in other words they must take ownership of self `do_final(self, ...)` so that no subsequent calls can be +made to this object (as opposed to the usual pattern of taking a ref to self as in `do_update(&self, ...)`). These +tricks go a long way to reducing fallibility since now in general there is no (or very very little) object state to +track and return errors about. Any struct that holds sensitive data must impl the `core::Secret` trait and all associated super-traits. ## Fallibility -As much as humanly possible, Result and unwrap() should be used for "Bad input data" type things and not "Programmer didn't read the docs" type things. +As much as humanly possible, Result and unwrap() should be used for "Bad input data" type things and not "Programmer +didn't read the docs" type things. -`.unwrap()` causes system crashes. The use of `.unwrap()` should always be preceeded by testing that we're in a state where we know the call will succeed, or else there should be an inline comment explaining why the `.unwrap()` will always succeed. +`.unwrap()` causes system crashes. The use of `.unwrap()` should always be preceeded by testing that we're in a state +where we know the call will succeed, or else there should be an inline comment explaining why the `.unwrap()` will +always succeed. -Also, we want to avoid forcing users of the library from needing excessive amounts of `.unwrap()`. To this end, any function that returns a `Result` should be inspected closely to ensure that +Also, we want to avoid forcing users of the library from needing excessive amounts of `.unwrap()`. To this end, any +function that returns a `Result` should be inspected closely to ensure that -Therefore, public APIs should aim to avoid the use of Result if it is not strictly needed. This generally means that returning a `Result` is only used for instances where bad data was handed in to the function, or where something unrecoverable happened, like the internal RNG failed to initialize. `Result` must never be thrown out of convenience to the maintainer of bc-rust -- instead, get creative about how to check for and resolve error conditions within the function so that valid input will always produce valid output. Also, the rust language has a lot of features for turning runtime error conditions into compile-time error conditions. For example, if you find yourself taking in a reference to bytes `in: &[u8]` and then checking its length `if in.len() != LEN { return Err() }`, stop and instead change the function signature to `in: &[u8; LEN]` so that it is simply impossible for the caller to hand you data of the wrong length (this also has a small performance benefit since you don't need to do that if-check). In other contexts it might be possible to use rust typing system to track state change of an object instead of carrying a member variable that tracks it. +Therefore, public APIs should aim to avoid the use of Result if it is not strictly needed. This generally means that +returning a `Result` is only used for instances where bad data was handed in to the function, or where something +unrecoverable happened, like the internal RNG failed to initialize. `Result` must never be thrown out of convenience to +the maintainer of bc-rust -- instead, get creative about how to check for and resolve error conditions within the +function so that valid input will always produce valid output. Also, the rust language has a lot of features for turning +runtime error conditions into compile-time error conditions. For example, if you find yourself taking in a reference to +bytes `in: &[u8]` and then checking its length `if in.len() != LEN { return Err() }`, stop and instead change the +function signature to `in: &[u8; LEN]` so that it is simply impossible for the caller to hand you data of the wrong +length (this also has a small performance benefit since you don't need to do that if-check). In other contexts it might +be possible to use rust typing system to track state change of an object instead of carrying a member variable that +tracks it. -Use `./dev_scripts/quality_stats.sh` to see the fallibility metrics for the crate you're working on and try to get those numbers down. +Use `./dev_scripts/quality_stats.sh` to see the fallibility metrics for the crate you're working on and try to get those +numbers down. # Docs ## Usage Examples -The crate docs needs a section "Usage Examples" with sample code for all the major usage patterns of the primitives in the crate. +The crate docs needs a section "Usage Examples" with sample code for all the major usage patterns of the primitives in +the crate. ## Memory Usage -The crate docs needs a section "Memory Usage" with a table of the stack memory usage of each algorithm or primitive in the crate. +The crate docs needs a section "Memory Usage" with a table of the stack memory usage of each algorithm or primitive in +the crate. ## Security Considerations -Most crates should have a "Security Considerations" section that documents any footguns where the user of this crate could undermine their own security; for example where providing a seed or a nonce that is not truly random would completely undermine the algorithm. \ No newline at end of file +Most crates should have a "Security Considerations" section that documents any footguns where the user of this crate +could undermine their own security; for example where providing a seed or a nonce that is not truly random would +completely undermine the algorithm. \ No newline at end of file diff --git a/alpha_0.1.2_release_notes.md b/alpha_0.1.2_release_notes.md index 652f08f..327f19c 100644 --- a/alpha_0.1.2_release_notes.md +++ b/alpha_0.1.2_release_notes.md @@ -2,56 +2,63 @@ [remove this section before publication] -[ ] EdDSA -[ ] ML-DSA & ML-KEM -* Look a close look at Kris' ICMC slides on low_memory -* lowmemory: Play with packing Polynomials into 12 / 23 bits. -* Polynomial (and maybe Matrix and Vec) might want custom indexing -https://doc.rust-lang.org/core/ops/trait.Index.html -https://doc.rust-lang.org/core/ops/trait.IndexMut.html -* Check the crate release checklist -* Run Crucible testing -* Run Wycheproof tests -[ ] Check out Megan's email May 13: "I was wondering if there might be scope for a closure based approach that could -guarantee encapsulation of the state change from safe to hazardous back to safe again." -[ ] Anywhere that you have an `_out(.. out: &mut [u8])`, start by zeroizing it with .fill(0); .. a good task for Claude? -[ ] Go back to previous algs and apply memory optimization tricks like unnamed scopes. And add a docs section "Memory -Usage" that measures with valgrind. -[ ] Ensure that all crates have `#![forbid(missing_docs)]` -[ ] Apply Secret trait consistently across the library --> study the `Zeroize` trait in RustCrypto -[ ] Change all "[u8;0]" to "[]" throughout the code and docs ... or better yet, change the APIs to take an Option<> -[ ] Change all `-> Vec` to `-> [u8; CONST_LEN]`, and the `output: &mut [u8]` to `output: &mut [u8; CONST_LEN]` where -appropriate. -* After doing that, do I even need the _out() versions? Since everything is no_std now. +* ML-DSA & ML-KEM + * Check the crate release checklist and run claude against the style guide (maybe Francis could cross-check me) + * Run Crucible testing + * Add factories for ML-DSA and ML-KEM (if we are keeping factories, see below) + * After merging the Signer/Verifier, Encrypter/Decrypter split, check if the keygen_from_rng() is still on the right + trait. +* Split the Signature trait into a Signer and a Verifier so that, for example, we can implement the verifier for MTC in + a different struct from the signer; or so that you can get FIPS compliance on old algorithms that are currently only + FIPS-allowed for verification of existing signatures but not for creation of new ones. +* Check out Megan's email May 13 about KeyMaterial: "I was wondering if there might be scope for a closure based + approach that could guarantee encapsulation of the state change from safe to hazardous back to safe again." +* Go back to previous algs and apply memory optimization tricks like internal functions. And add a docs section "Memory + Usage" that measures with valgrind. +* Ensure that all crates have `#![forbid(missing_docs)]` +* Apply Secret trait consistently across the library --> study the `Zeroize` trait in RustCrypto +* Change all "[u8;0]" to "[]" throughout the code and docs ... or better yet, change the APIs to take an Option<> +* Change all `-> Vec` to `-> [u8; CONST_LEN]`, and the `output: &mut [u8]` to `output: &mut [u8; CONST_LEN]` where + appropriate. * Probably it makes sense to leave Hex and Base64 as requiring std; ... or maybe add a no_std version that uses -fixed-sized blocks? -[ ] For all higher-level algorithms, put a cargo #[cfg(feature='rng')] around the keygen that takes an rng so that the -dependency on bouncycastle_rng is optional. -[ ] Enhance the default HashDRBG instantiation to take in NIST-compatible CPU jitter entropy -[ ] Get an opinion from Bob Beck or Dennis about the factories ... Are they worth it? Michael Richardson says Very Yes. - -* Add factories for ML-DSA and ML-KEM - [ ] Add back the Memoable trait from nursery (maybe under a different name) that lets you serialize out the - intermediate state, especially important for SHA2, SHA3, and HMAC because TLS needs to be able to fork a state, - finalize() a copy and then keep feeding the other copy. -* Add unit tests. - [ ] Make crypto one crate - [ ] Do some science about perf impacts of acting on a local hard-copy vs acting in-place on some specific bit of - memory -* Bob suggests: feed a function in question to GodBolt. - [ ] Change the tone of the documentation (both the crate docs and the inline comments) to be less individual ("I" - statements) and be more factual ("it is", or "the project", or "the bc-rust library" as appropriate). - [ ] Relax the requirement on XOF that once you start squeezing, you can't absorb anymore. ... this might need to be - specifically forbidden in FIPS mode. - [ ] Do a pass over KeyMaterial, taking into account Dennis Jackson's input (maybe ping him for a phone call?) - [ ] Need a rust expert: I use a bunch of #![feature(_)]'s that are only available in nightly. ... what should I do - about that? - [ ] Deal with as many of the inline TODOs as possible - [ ] Open github issues + fixed-sized blocks? +* Make this build on the stable compiler. IE Remove the rust-toolchain.toml file that builds with nightly. Will require + some refactoring. +* Create a cargo feature #[cfg(feature='rng')] and put it around things like keygen that takes an rng so that the build + dependency on bouncycastle_rng is optional. +* Factories ... Are they worth it? Michael Richardson says Very Yes. If we are keeping them, then we need a serious + re-engineering of them because I really dislike that currently they make it hard for the underlying primitive to have + static one-shot APIs. +* Deal with as many of the inline TODOs as possible +* Close all open github issues and document them in this file. +* After everything is merged, circle back to crucible, and make sure that the harness still works (and maybe remove the + nightly build toolchain) +* Search for all the uses of .unwrap() in non-test code and replace each with either a comment or an expect with a + meaningful error string. # 0.1.2 Features / Changelog -* ML-DSA -* Low-Memory ML-DSA -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with only minor performance impact. +## Major features + +* New algorithms added to crypto/ : + * mldsa (FIPS 204) + * mldsa-lowmemory -- runs in about 1/10th of the usual memory (~ 30 kb of stack) with comparable performance impact. + * mlkem (FIPS 203) + * mlkem-lowmemory -- runs in about 1/4th of the usual memory (~ 12 kb of stack) with comparable performance impact. +* New traits [Suspendable] and [SuspendableKeyed] allow algorithms with a streaming API (`do_update()` -> + `do_final()`) to be suspended to a small byte array and then resumed later, potentially from a different host and + potentially across versions of the library. The intended use case is if you are processing a large input that depends + on one or more network round-trips and you wish to suspend to a cache and potentially transfer to a new host while + waiting for network IO. + +## Minor features / bug fixes + +* All public `*_out(.., out: &mut [u8])` functions now begin by zeroizing the entire output buffer with `.fill(0)`, + preventing exposure of stale data in oversized output buffers or on early error returns. +* Reworked the way KeyMaterial hazardous operations work; instead of a stateful .allow_hazardous_operations() / + .drop_hazardous_operations(), it now uses a closure-based do_hazardous_operations(). Github issue #39. +* Renamed KeyMaterial::KeyType's and deleted KeyMaterial::concatenate in order to give a better intuition and + FIPS-alignment. * Github issues resolved: - * #2, or whatever \ No newline at end of file + * #6: https://github.com/bcgit/bc-rust/issues/6, thanks to Q. T. Felix (github: @Quant-TheodoreFelix) + * #10: https://github.com/bcgit/bc-rust/issues/10, thanks to Nicola Tuveri (github: @romen) \ No newline at end of file diff --git a/cli/src/encoders_cmd.rs b/cli/src/encoders_cmd.rs index 1de9d59..c3b6d0b 100644 --- a/cli/src/encoders_cmd.rs +++ b/cli/src/encoders_cmd.rs @@ -1,17 +1,17 @@ use std::io; use std::io::{Read, Write}; -use bouncycastle::hex; use bouncycastle::base64; +use bouncycastle::hex; pub(crate) fn hex_encode_cmd() { // Stream from stdin to stdout in chunks of 1 kb let mut buf: [u8; 1024] = [0u8; 1024]; let mut bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); while bytes_read != 0 { - io::stdout().write_all( - hex::encode(&buf[..bytes_read]).as_bytes() - ).expect("Failed to write to stdout"); + io::stdout() + .write_all(hex::encode(&buf[..bytes_read]).as_bytes()) + .expect("Failed to write to stdout"); bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); } @@ -22,13 +22,12 @@ pub(crate) fn hex_decode_cmd() { let mut buf: [u8; 1024] = [0u8; 1024]; let mut bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); while bytes_read != 0 { - let chunk_str: String = String::from_utf8( - Vec::from(&buf[..bytes_read]) - ).expect("Input was not valid utf8."); + let chunk_str: String = + String::from_utf8(Vec::from(&buf[..bytes_read])).expect("Input was not valid utf8."); - io::stdout().write_all( - &*hex::decode(chunk_str.as_str()).expect("Input was not valid hex.") - ).expect("Failed to write to stdout"); + io::stdout() + .write_all(&*hex::decode(chunk_str.as_str()).expect("Input was not valid hex.")) + .expect("Failed to write to stdout"); bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); } @@ -40,9 +39,9 @@ pub(crate) fn base64_encode_cmd() { let mut buf: [u8; 1024] = [0u8; 1024]; let mut bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); while bytes_read != 0 { - io::stdout().write_all( - encoder.do_update(&buf[..bytes_read]).as_bytes() - ).expect("Failed to write to stdout"); + io::stdout() + .write_all(encoder.do_update(&buf[..bytes_read]).as_bytes()) + .expect("Failed to write to stdout"); bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); } @@ -54,14 +53,18 @@ pub(crate) fn base64_decode_cmd() { let mut decoder = base64::Base64Decoder::new(true); let mut bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); while bytes_read != 0 { - let chunk_str: String = String::from_utf8( - Vec::from(&buf[..bytes_read]) - ).expect("Input was not valid utf8."); + let chunk_str: String = + String::from_utf8(Vec::from(&buf[..bytes_read])).expect("Input was not valid utf8."); - io::stdout().write_all( - decoder.do_update(chunk_str.as_str()).expect("Input was not valid base64.").as_slice() - ).expect("Failed to write to stdout"); + io::stdout() + .write_all( + decoder + .do_update(chunk_str.as_str()) + .expect("Input was not valid base64.") + .as_slice(), + ) + .expect("Failed to write to stdout"); bytes_read = io::stdin().read(&mut buf).expect("Failed to read from stdin"); } -} \ No newline at end of file +} diff --git a/cli/src/helpers.rs b/cli/src/helpers.rs index 7fa4adf..43993a3 100644 --- a/cli/src/helpers.rs +++ b/cli/src/helpers.rs @@ -1,10 +1,12 @@ -use std::{io}; +use bouncycastle::core::key_material::{ + KeyMaterial, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; +use bouncycastle::core::traits::SecurityStrength; +use bouncycastle::hex; use std::fs::File; +use std::io; use std::io::{Read, Write}; use std::process::exit; -use bouncycastle::core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; -use bouncycastle::core::traits::{SecurityStrength}; -use bouncycastle::hex; /// Reads either bin or hex pub(crate) fn read_from_file(filename: &str) -> Vec { @@ -12,20 +14,20 @@ pub(crate) fn read_from_file(filename: &str) -> Vec { if file.is_ok() { let mut buf = Vec::::new(); match file.unwrap().read_to_end(&mut buf) { - Ok(_bytes_read) => { + Ok(_bytes_read) => { // try hex decoding it match hex::decode(&buf) { - Ok(decoded) => { decoded }, + Ok(decoded) => decoded, Err(_) => { // well, it's not hex, so return it raw buf - }, + } } - }, + } Err(_) => { eprintln!("Error: couldn't open file '{}'", &filename); exit(-1); - }, + } } } else { eprintln!("Error: couldn't open file '{}'", &filename); @@ -35,22 +37,21 @@ pub(crate) fn read_from_file(filename: &str) -> Vec { /// Reads either bin or hex pub(crate) fn read_from_file_or_stdin(filename: &Option) -> Vec { - if filename.is_some() { // This already reads either bin or hex return read_from_file(filename.as_ref().unwrap()); } - + let mut buf = Vec::::new(); io::stdin().read_to_end(&mut buf).expect("Failed to read from stdin"); // try hex decoding it match hex::decode(&buf) { - Ok(decoded) => { decoded }, + Ok(decoded) => decoded, Err(_) => { // well, it's not hex, so return it raw buf - }, + } } } @@ -84,7 +85,7 @@ pub(crate) fn parse_seed(bytes: &[u8]) -> Result { - if decoded_bytes.len() < SEED_LEN || decoded_bytes.len() > SEED_LEN +1 { + if decoded_bytes.len() < SEED_LEN || decoded_bytes.len() > SEED_LEN + 1 { // it was valid hex, but the wrong length return Err(()); } @@ -92,7 +93,7 @@ pub(crate) fn parse_seed(bytes: &[u8]) -> Result { // it's not hex, so take the fist SEED_LEN bytes of the raw binary - if bytes.len() < SEED_LEN || bytes.len() > SEED_LEN +1 { + if bytes.len() < SEED_LEN || bytes.len() > SEED_LEN + 1 { return Err(()); } bytes[..SEED_LEN].try_into().unwrap() @@ -107,10 +108,11 @@ pub(crate) fn parse_seed(bytes: &[u8]) -> Result, - salt_file: &Option, - ikm: &Option, - ikm_file: &Option, - additional_input: &Option, - additional_input_file: &Option, - len: usize, - output_hex: bool ) { +pub(crate) fn hkdf_cmd( + hkdfname: &str, + salt: &Option, + salt_file: &Option, + ikm: &Option, + ikm_file: &Option, + additional_input: &Option, + additional_input_file: &Option, + len: usize, + output_hex: bool, +) { let salt_bytes: Vec; let ikm_bytes: Vec; let additional_input_bytes: Vec; @@ -41,8 +45,8 @@ pub(crate) fn hkdf_cmd(hkdfname: &str, } let mut salt_key = KeyMaterial::<1024>::from_bytes(&salt_bytes).unwrap(); // force it just so the CLI behaves properly even with all-zero or zero-length keys - salt_key.allow_hazardous_operations(); - salt_key.convert_key_type(KeyType::MACKey).unwrap(); + do_hazardous_operations(&mut salt_key, |salt_key| salt_key.set_key_type(KeyType::MACKey)) + .unwrap(); ikm_bytes = if ikm.is_some() { hex::decode(ikm.as_ref().unwrap()).unwrap() @@ -53,7 +57,6 @@ pub(crate) fn hkdf_cmd(hkdfname: &str, exit(-1) }; - additional_input_bytes = if additional_input.is_some() { hex::decode(additional_input.as_ref().unwrap()).unwrap() } else if additional_input.is_some() { @@ -72,21 +75,24 @@ pub(crate) fn hkdf_cmd(hkdfname: &str, h.do_extract_update_bytes(ikm_bytes.as_slice()).unwrap(); h.do_extract_update_bytes(additional_input_bytes.as_slice()).unwrap(); h.do_extract_final_out(&mut out_key).unwrap(); - }, + } "HKDF-SHA512" => { let mut h = hkdf::HKDF_SHA512::new(); h.do_extract_init(&salt_key).unwrap(); h.do_extract_update_bytes(ikm_bytes.as_slice()).unwrap(); h.do_extract_update_bytes(additional_input_bytes.as_slice()).unwrap(); h.do_extract_final_out(&mut out_key).unwrap(); - }, - _ => { panic!("{} is not a supported HKDF variant.", hkdfname); } + } + _ => { + panic!("{} is not a supported HKDF variant.", hkdfname); + } } - if output_hex { for b in out_key.ref_to_bytes().iter() { print!("{b:02x}"); } - } else { io::stdout().write(&out_key.ref_to_bytes()).unwrap(); } -} \ No newline at end of file + } else { + io::stdout().write(&out_key.ref_to_bytes()).unwrap(); + } +} diff --git a/cli/src/mac_cmd.rs b/cli/src/mac_cmd.rs index 3a4e98d..bb7aafc 100644 --- a/cli/src/mac_cmd.rs +++ b/cli/src/mac_cmd.rs @@ -2,7 +2,9 @@ use std::io::{Read, Write}; use std::process::exit; use std::{fs, io}; -use bouncycastle::core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; +use bouncycastle::core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; use bouncycastle::core::traits::MAC; use bouncycastle::hex; use bouncycastle::hmac::{HMAC_SHA256, HMAC_SHA512}; @@ -34,16 +36,14 @@ pub(crate) fn mac_cmd( exit(-1); } let mut key = KeyMaterial512::from_bytes(&key_bytes).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::MACKey).unwrap(); - + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::MACKey)).unwrap(); // instantiate the MAC object and call do_mac() match hmac_variant { HMACVariant::SHA256 => { let mac = HMAC_SHA256::new_allow_weak_key(&key).unwrap(); do_mac(mac, verify_val, output_hex); - }, + } HMACVariant::SHA512 => { let mac = HMAC_SHA512::new_allow_weak_key(&key).unwrap(); do_mac(mac, verify_val, output_hex); @@ -51,7 +51,6 @@ pub(crate) fn mac_cmd( } } - fn do_mac(mut mac: impl MAC, verify_val: &Option, output_hex: bool) { // read the content to be MAC'd from stdin let mut buf: [u8; 1024] = [0u8; 1024]; diff --git a/cli/src/main.rs b/cli/src/main.rs index 3296160..45205bf 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -1,21 +1,20 @@ -mod sha3_cmd; mod encoders_cmd; -mod sha2_cmd; -mod mac_cmd; +mod helpers; mod hkdf_cmd; -mod rng_cmd; +mod mac_cmd; mod mldsa_cmd; mod mlkem_cmd; -mod helpers; +mod rng_cmd; +mod sha2_cmd; +mod sha3_cmd; -use clap::{Parser, Subcommand}; use crate::mac_cmd::HMACVariant; use crate::mldsa_cmd::MLDSAAction; +use clap::{Parser, Subcommand}; #[derive(Parser)] #[command(version, about, long_about=None, arg_required_else_help=true)] struct Cli { - #[command(subcommand)] subcommands: Option, } @@ -30,7 +29,7 @@ enum Subcommands { /// Decode base64 data from stdin to binary. /// Supports streaming for low memory footprint and continuous processing from stdin to stdout. HexDecode, - + /// Encode binary data from stdin to base64. /// Supports streaming for low memory footprint and continuous processing from stdin to stdout. Base64Encode, @@ -163,7 +162,7 @@ enum Subcommands { /// If both key and key_file options are provided, the file will be used. #[arg(short, long)] key_file: Option, - + /// A MAC value to be verified. /// The command will output either 0 for success or -1 for verification failure. #[arg(short, long)] @@ -174,7 +173,6 @@ enum Subcommands { x: bool, }, - /// Perform HMAC-SHA256 of the content provided on stdin. /// HKDF.extract_and_expand(salt, ikm, additional_info, L) /// Note: in production uses, secrets should not be passed on the command-line because they get @@ -218,7 +216,6 @@ enum Subcommands { x: bool, }, - /// Perform HMAC-SHA512 of the content provided on stdin. /// HKDF.extract_and_expand(salt, ikm, additional_info, L) /// Note: in production uses, secrets should not be passed on the command-line because they get @@ -285,7 +282,7 @@ enum Subcommands { #[arg(long)] /// The public key file (in hex or binary) for encaps pkfile: Option, - + #[arg(long)] /// The ciphertext value file (in hex or binary) either for encaps to output to, or for decaps to read from. ctfile: Option, @@ -432,7 +429,6 @@ enum Subcommands { /// The signature value file (in hex or binary) for verifying sigfile: Option, - #[arg(short)] /// Output in hex format. x: bool, @@ -458,7 +454,6 @@ enum Subcommands { /// The signature value file (in hex or binary) for verifying sigfile: Option, - #[arg(short)] /// Output in hex format. x: bool, @@ -484,46 +479,120 @@ enum Subcommands { /// The signature value file (in hex or binary) for verifying sigfile: Option, - #[arg(short)] /// Output in hex format. x: bool, }, } - fn main() { let cli = Cli::parse(); match &cli.subcommands { - Some(Subcommands::HexEncode) => { encoders_cmd::hex_encode_cmd(); } - Some(Subcommands::HexDecode) => { encoders_cmd::hex_decode_cmd(); } - Some(Subcommands::Base64Encode) => { encoders_cmd::base64_encode_cmd(); } - Some(Subcommands::Base64Decode) => { encoders_cmd::base64_decode_cmd(); } - Some(Subcommands::SHA224 { x}) => { sha2_cmd::sha2_cmd(224, *x); }, - Some(Subcommands::SHA256 { x}) => { sha2_cmd::sha2_cmd(256, *x); }, - Some(Subcommands::SHA384 { x}) => { sha2_cmd::sha2_cmd(384, *x); }, - Some(Subcommands::SHA512 { x}) => { sha2_cmd::sha2_cmd(512, *x); }, - Some(Subcommands::SHA3_224 { x}) => { sha3_cmd::sha3_cmd(224, *x); }, - Some(Subcommands::SHA3_256 { x}) => { sha3_cmd::sha3_cmd(256, *x); }, - Some(Subcommands::SHA3_384 { x}) => { sha3_cmd::sha3_cmd(384, *x); }, - Some(Subcommands::SHA3_512 { x}) => { sha3_cmd::sha3_cmd(512, *x); }, - Some(Subcommands::SHAKE128 { length, x}) => { sha3_cmd::shake_cmd(128, *length, *x); }, - Some(Subcommands::SHAKE256 { length, x}) => { sha3_cmd::shake_cmd(256, *length, *x); }, - Some(Subcommands::HMAC_SHA256 { key, key_file, verify, x}) => { mac_cmd::mac_cmd(HMACVariant::SHA256, key, key_file, verify, *x)}, - Some(Subcommands::HMAC_SHA512 { key, key_file, verify, x}) => { mac_cmd::mac_cmd(HMACVariant::SHA512, key, key_file, verify, *x)}, - Some(Subcommands::HKDF_SHA256 { salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, len, x}) => { hkdf_cmd::hkdf_cmd("HKDF-SHA256", salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, *len, *x)}, - Some(Subcommands::HKDF_SHA512 { salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, len, x}) => { hkdf_cmd::hkdf_cmd("HKDF-SHA512", salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, *len, *x)}, - Some(Subcommands::RNG { len, x}) => { rng_cmd::rng_cmd(*len, *x)}, - Some(Subcommands::MLKEM512 { action, skfile, pkfile, ctfile, x }) => { mlkem_cmd::mlkem512_cmd(action, skfile, pkfile, ctfile, *x); } - Some(Subcommands::MLKEM768 { action, skfile, pkfile, ctfile, x }) => { mlkem_cmd::mlkem768_cmd(action, skfile, pkfile, ctfile, *x); } - Some(Subcommands::MLKEM1024 { action, skfile, pkfile, ctfile, x }) => { mlkem_cmd::mlkem1024_cmd(action, skfile, pkfile, ctfile, *x); } - Some(Subcommands::MLDSA44 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::mldsa44_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - Some(Subcommands::MLDSA65 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::mldsa65_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - Some(Subcommands::MLDSA87 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::mldsa87_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - Some(Subcommands::HashMLDSA44 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::hash_mldsa44_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - Some(Subcommands::HashMLDSA65 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::hash_mldsa65_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - Some(Subcommands::HashMLDSA87 { action, ctxfile, skfile, pkfile, sigfile, x }) => { mldsa_cmd::hash_mldsa87_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); } - None => { eprintln!("No command provided. See -h") }, + Some(Subcommands::HexEncode) => { + encoders_cmd::hex_encode_cmd(); + } + Some(Subcommands::HexDecode) => { + encoders_cmd::hex_decode_cmd(); + } + Some(Subcommands::Base64Encode) => { + encoders_cmd::base64_encode_cmd(); + } + Some(Subcommands::Base64Decode) => { + encoders_cmd::base64_decode_cmd(); + } + Some(Subcommands::SHA224 { x }) => { + sha2_cmd::sha2_cmd(224, *x); + } + Some(Subcommands::SHA256 { x }) => { + sha2_cmd::sha2_cmd(256, *x); + } + Some(Subcommands::SHA384 { x }) => { + sha2_cmd::sha2_cmd(384, *x); + } + Some(Subcommands::SHA512 { x }) => { + sha2_cmd::sha2_cmd(512, *x); + } + Some(Subcommands::SHA3_224 { x }) => { + sha3_cmd::sha3_cmd(224, *x); + } + Some(Subcommands::SHA3_256 { x }) => { + sha3_cmd::sha3_cmd(256, *x); + } + Some(Subcommands::SHA3_384 { x }) => { + sha3_cmd::sha3_cmd(384, *x); + } + Some(Subcommands::SHA3_512 { x }) => { + sha3_cmd::sha3_cmd(512, *x); + } + Some(Subcommands::SHAKE128 { length, x }) => { + sha3_cmd::shake_cmd(128, *length, *x); + } + Some(Subcommands::SHAKE256 { length, x }) => { + sha3_cmd::shake_cmd(256, *length, *x); + } + Some(Subcommands::HMAC_SHA256 { key, key_file, verify, x }) => { + mac_cmd::mac_cmd(HMACVariant::SHA256, key, key_file, verify, *x) + } + Some(Subcommands::HMAC_SHA512 { key, key_file, verify, x }) => { + mac_cmd::mac_cmd(HMACVariant::SHA512, key, key_file, verify, *x) + } + Some(Subcommands::HKDF_SHA256 { + salt, + salt_file, + ikm, + ikm_file, + additional_input, + additional_input_file, + len, + x, + }) => hkdf_cmd::hkdf_cmd( + "HKDF-SHA256", salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, + *len, *x, + ), + Some(Subcommands::HKDF_SHA512 { + salt, + salt_file, + ikm, + ikm_file, + additional_input, + additional_input_file, + len, + x, + }) => hkdf_cmd::hkdf_cmd( + "HKDF-SHA512", salt, salt_file, ikm, ikm_file, additional_input, additional_input_file, + *len, *x, + ), + Some(Subcommands::RNG { len, x }) => rng_cmd::rng_cmd(*len, *x), + Some(Subcommands::MLKEM512 { action, skfile, pkfile, ctfile, x }) => { + mlkem_cmd::mlkem512_cmd(action, skfile, pkfile, ctfile, *x); + } + Some(Subcommands::MLKEM768 { action, skfile, pkfile, ctfile, x }) => { + mlkem_cmd::mlkem768_cmd(action, skfile, pkfile, ctfile, *x); + } + Some(Subcommands::MLKEM1024 { action, skfile, pkfile, ctfile, x }) => { + mlkem_cmd::mlkem1024_cmd(action, skfile, pkfile, ctfile, *x); + } + Some(Subcommands::MLDSA44 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::mldsa44_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + Some(Subcommands::MLDSA65 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::mldsa65_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + Some(Subcommands::MLDSA87 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::mldsa87_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + Some(Subcommands::HashMLDSA44 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::hash_mldsa44_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + Some(Subcommands::HashMLDSA65 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::hash_mldsa65_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + Some(Subcommands::HashMLDSA87 { action, ctxfile, skfile, pkfile, sigfile, x }) => { + mldsa_cmd::hash_mldsa87_sha512_cmd(action, ctxfile, skfile, pkfile, sigfile, *x); + } + None => { + eprintln!("No command provided. See -h") + } } } diff --git a/cli/src/mldsa_cmd.rs b/cli/src/mldsa_cmd.rs index 79ba941..cfb99bd 100644 --- a/cli/src/mldsa_cmd.rs +++ b/cli/src/mldsa_cmd.rs @@ -2,13 +2,20 @@ //! by using generics or macros. I just, haven't ... yet. use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex}; -use bouncycastle::core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey}; +use bouncycastle::core::traits::{ + SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, Signer, +}; use bouncycastle::hex; -use bouncycastle::mldsa::{MLDSA_SEED_LEN, MLDSA44, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSA87_SK_LEN, MLDSAPrivateKeyTrait, MLDSATrait, MLDSA44PublicKey, MLDSA44_PK_LEN, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSA65, MLDSA65PublicKey, MLDSA65_PK_LEN, MLDSA87PrivateKey, MLDSA87, MLDSA87PublicKey, MLDSA87_PK_LEN, HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512}; -use std::{io}; -use std::io::{Read}; -use std::process::exit; +use bouncycastle::mldsa::{ + HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA_SEED_LEN, + MLDSA44, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, + MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87, MLDSA87_PK_LEN, + MLDSA87_SK_LEN, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSATrait, +}; use clap::ValueEnum; +use std::io; +use std::io::Read; +use std::process::exit; #[derive(ValueEnum, Clone, Debug)] pub(crate) enum MLDSAAction { @@ -48,7 +55,10 @@ pub(crate) fn mldsa44_cmd( let seed = match parse_seed::(&buf) { Ok(seed) => seed, Err(()) => { - eprintln!("Error: input could not be parsed as a {} byte seed in either hex or bin", MLDSA_SEED_LEN); + eprintln!( + "Error: input could not be parsed as a {} byte seed in either hex or bin", + MLDSA_SEED_LEN + ); exit(-1); } }; @@ -94,7 +104,9 @@ pub(crate) fn mldsa44_cmd( }; match MLDSA44::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -113,7 +125,9 @@ pub(crate) fn mldsa44_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa44_sk(&sk_bytes) { @@ -156,7 +170,9 @@ pub(crate) fn mldsa44_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -185,7 +201,7 @@ pub(crate) fn mldsa44_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } @@ -247,7 +263,9 @@ pub(crate) fn mldsa65_cmd( }; match MLDSA65::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -266,7 +284,9 @@ pub(crate) fn mldsa65_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa65_sk(&sk_bytes) { @@ -309,7 +329,9 @@ pub(crate) fn mldsa65_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -338,7 +360,7 @@ pub(crate) fn mldsa65_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } pub(crate) fn mldsa87_cmd( @@ -383,7 +405,6 @@ pub(crate) fn mldsa87_cmd( } }; - // first, read the pk let pk_bytes = if pkfile.is_some() { read_from_file(pkfile.as_ref().unwrap()) @@ -400,7 +421,9 @@ pub(crate) fn mldsa87_cmd( }; match MLDSA87::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -419,7 +442,9 @@ pub(crate) fn mldsa87_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa87_sk(&sk_bytes) { @@ -462,7 +487,9 @@ pub(crate) fn mldsa87_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -491,7 +518,7 @@ pub(crate) fn mldsa87_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } @@ -553,7 +580,9 @@ pub(crate) fn hash_mldsa44_sha512_cmd( }; match MLDSA44::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -572,7 +601,9 @@ pub(crate) fn hash_mldsa44_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa44_sk(&sk_bytes) { @@ -615,7 +646,9 @@ pub(crate) fn hash_mldsa44_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -644,7 +677,7 @@ pub(crate) fn hash_mldsa44_sha512_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } @@ -706,7 +739,9 @@ pub(crate) fn hash_mldsa65_sha512_cmd( }; match MLDSA65::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -725,7 +760,9 @@ pub(crate) fn hash_mldsa65_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa65_sk(&sk_bytes) { @@ -768,7 +805,9 @@ pub(crate) fn hash_mldsa65_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -797,7 +836,7 @@ pub(crate) fn hash_mldsa65_sha512_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } pub(crate) fn hash_mldsa87_sha512_cmd( @@ -858,7 +897,9 @@ pub(crate) fn hash_mldsa87_sha512_cmd( }; match MLDSA87::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); @@ -877,7 +918,9 @@ pub(crate) fn hash_mldsa87_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // and now sign, streaming the message from stdin let sk = match parse_mldsa87_sk(&sk_bytes) { @@ -920,7 +963,9 @@ pub(crate) fn hash_mldsa87_sha512_cmd( // then read ctx let ctx = if ctxfile.is_some() { read_from_file(ctxfile.as_ref().unwrap()) - } else { vec![0u8;0] }; + } else { + vec![0u8; 0] + }; // then read the sig let sig = if sigfile.is_some() { @@ -949,7 +994,7 @@ pub(crate) fn hash_mldsa87_sha512_cmd( eprintln!("Signature is invalid."); exit(-1); } - }, + } } } @@ -1137,4 +1182,4 @@ fn parse_mldsa87_pk(bytes: &[u8]) -> Result { } // else: we're out of things to try Err("Error: couldn't parse the input as a valid ML-DSA-87 public key.") -} \ No newline at end of file +} diff --git a/cli/src/mlkem_cmd.rs b/cli/src/mlkem_cmd.rs index de56238..67214da 100644 --- a/cli/src/mlkem_cmd.rs +++ b/cli/src/mlkem_cmd.rs @@ -1,13 +1,21 @@ //! Yup, this file is as absolutely atrocious mess of duplicate code that could be much improved //! by using generics or macros. I just, haven't ... yet. -use std::process::exit; -use clap::ValueEnum; +use crate::helpers::{ + parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex, + write_bytes_or_hex_to_file, +}; use bouncycastle::core::key_material::KeyMaterialTrait; -use bouncycastle::core::traits::{KEMPrivateKey, KEMPublicKey, KEM}; +use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey}; use bouncycastle::hex; -use bouncycastle::mlkem::{MLKEM512, MLKEMTrait, MLKEM512PrivateKey, MLKEM512_SK_LEN, MLKEM512PublicKey, MLKEM512_PK_LEN, MLKEMPrivateKeyTrait, MLKEM512_CT_LEN, MLKEM768PrivateKey, MLKEM768_SK_LEN, MLKEM768, MLKEM768PublicKey, MLKEM768_PK_LEN, MLKEM1024PrivateKey, MLKEM1024_SK_LEN, MLKEM1024, MLKEM1024_PK_LEN, MLKEM1024PublicKey, MLKEM768_CT_LEN, MLKEM1024_CT_LEN}; -use crate::helpers::{parse_seed, read_from_file, read_from_file_or_stdin, write_bytes_or_hex, write_bytes_or_hex_to_file}; +use bouncycastle::mlkem::{ + MLKEM512, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512PrivateKey, + MLKEM512PublicKey, MLKEM768, MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, + MLKEM768PrivateKey, MLKEM768PublicKey, MLKEM1024, MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, + MLKEM1024_SK_LEN, MLKEM1024PrivateKey, MLKEM1024PublicKey, MLKEMPrivateKeyTrait, MLKEMTrait, +}; +use clap::ValueEnum; +use std::process::exit; #[derive(ValueEnum, Clone, Debug)] pub(crate) enum MLKEMAction { @@ -94,13 +102,15 @@ pub(crate) fn mlkem512_cmd( }; match MLKEM512::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); } } - }, + } MLKEMAction::Encaps => { // first, read the pk let pk_bytes = read_from_file_or_stdin(pkfile); @@ -124,8 +134,7 @@ pub(crate) fn mlkem512_cmd( println!(); write_bytes_or_hex(ss.ref_to_bytes(), true); } - - }, + } MLKEMAction::Decaps => { // first, read the sk let sk_bytes = read_from_file_or_stdin(skfile); @@ -216,13 +225,15 @@ pub(crate) fn mlkem768_cmd( }; match MLKEM768::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); } } - }, + } MLKEMAction::Encaps => { // first, read the pk let pk_bytes = read_from_file_or_stdin(pkfile); @@ -246,7 +257,7 @@ pub(crate) fn mlkem768_cmd( println!(); write_bytes_or_hex(ss.ref_to_bytes(), true); } - }, + } MLKEMAction::Decaps => { // first, read the sk let sk_bytes = read_from_file_or_stdin(skfile); @@ -337,13 +348,15 @@ pub(crate) fn mlkem1024_cmd( }; match MLKEM1024::keypair_consistency_check(&pk, &sk) { - Ok(_) => { println!("SUCCESS: pk and sk match."); } + Ok(_) => { + println!("SUCCESS: pk and sk match."); + } Err(_) => { eprintln!("FAILURE: pk and sk do not match."); exit(-1); } } - }, + } MLKEMAction::Encaps => { // first, read the pk let pk_bytes = read_from_file_or_stdin(pkfile); @@ -367,7 +380,7 @@ pub(crate) fn mlkem1024_cmd( println!(); write_bytes_or_hex(ss.ref_to_bytes(), true); } - }, + } MLKEMAction::Decaps => { // first, read the sk let sk_bytes = read_from_file_or_stdin(skfile); @@ -401,7 +414,6 @@ pub(crate) fn mlkem1024_cmd( } } - fn parse_mlkem512_sk(bytes: &[u8]) -> Result { // try it in Biggest -> Smallest order @@ -459,7 +471,7 @@ fn parse_mlkem512_pk(bytes: &[u8]) -> Result { if pk.is_ok() { return Ok(pk.unwrap()); } - } // else: we're out of things to try + } // else: we're out of things to try Err("Error: couldn't parse the input as a valid ML-KEM-768 public key.") } @@ -521,7 +533,7 @@ fn parse_mlkem768_pk(bytes: &[u8]) -> Result { if pk.is_ok() { return Ok(pk.unwrap()); } - } // else: we're out of things to try + } // else: we're out of things to try Err("Error: couldn't parse the input as a valid ML-KEM-768 public key.") } @@ -583,8 +595,7 @@ fn parse_mlkem1024_pk(bytes: &[u8]) -> Result if pk.is_ok() { return Ok(pk.unwrap()); } - } // else: we're out of things to try + } // else: we're out of things to try Err("Error: couldn't parse the input as a valid ML-KEM-1024 public key.") } - diff --git a/cli/src/rng_cmd.rs b/cli/src/rng_cmd.rs index 7fabca8..e7a28e5 100644 --- a/cli/src/rng_cmd.rs +++ b/cli/src/rng_cmd.rs @@ -1,4 +1,4 @@ -use bouncycastle::core::traits::{RNG}; +use bouncycastle::core::traits::RNG; use bouncycastle::factory::AlgorithmFactory; use bouncycastle::factory::rng_factory::RNGFactory; @@ -13,9 +13,11 @@ pub(crate) fn rng_cmd(len: Option, output_hex: bool) { while loop_forever || bytes_left_to_write > 0 { rng.next_bytes_out(&mut buf).unwrap(); - if bytes_left_to_write < buf.len() { buf.truncate(bytes_left_to_write); } + if bytes_left_to_write < buf.len() { + buf.truncate(bytes_left_to_write); + } write_bytes_or_hex(&buf, output_hex); bytes_left_to_write -= buf.len(); } println!(); -} \ No newline at end of file +} diff --git a/cli/src/sha2_cmd.rs b/cli/src/sha2_cmd.rs index 4007a13..3551c9d 100644 --- a/cli/src/sha2_cmd.rs +++ b/cli/src/sha2_cmd.rs @@ -1,4 +1,4 @@ -use bouncycastle::core::traits::{Hash}; +use bouncycastle::core::traits::Hash; use std::io; use std::io::{Read, Write}; @@ -10,7 +10,7 @@ pub(crate) fn sha2_cmd(bit_len: usize, output_hex: bool) { 256 => do_sha2(SHA256::new(), output_hex), 384 => do_sha2(SHA384::new(), output_hex), 512 => do_sha2(SHA512::new(), output_hex), - _ => panic!("Unsupported algorithm: SHA{}", bit_len) + _ => panic!("Unsupported algorithm: SHA{}", bit_len), } } @@ -30,6 +30,8 @@ fn do_sha2(mut sha2: impl Hash, output_hex: bool) { for b in out.iter() { print!("{b:02x}"); } - } else { io::stdout().write(&out).unwrap(); } + } else { + io::stdout().write(&out).unwrap(); + } println!(); -} \ No newline at end of file +} diff --git a/cli/src/sha3_cmd.rs b/cli/src/sha3_cmd.rs index 3efa6a7..27d5feb 100644 --- a/cli/src/sha3_cmd.rs +++ b/cli/src/sha3_cmd.rs @@ -2,7 +2,6 @@ use bouncycastle::core::traits::{Hash, XOF}; use std::io; use std::io::{Read, Write}; - use bouncycastle::sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE128, SHAKE256}; pub(crate) fn sha3_cmd(bit_len: usize, output_hex: bool) { @@ -11,7 +10,7 @@ pub(crate) fn sha3_cmd(bit_len: usize, output_hex: bool) { 256 => do_sha3(SHA3_256::new(), output_hex), 384 => do_sha3(SHA3_384::new(), output_hex), 512 => do_sha3(SHA3_512::new(), output_hex), - _ => panic!("Unsupported algorithm: SHA3-{}", bit_len) + _ => panic!("Unsupported algorithm: SHA3-{}", bit_len), } } @@ -31,7 +30,9 @@ fn do_sha3(mut sha3: impl Hash, output_hex: bool) { for b in out.iter() { print!("{b:02x}"); } - } else { io::stdout().write(&out).unwrap(); } + } else { + io::stdout().write(&out).unwrap(); + } println!(); } @@ -39,7 +40,7 @@ pub(crate) fn shake_cmd(bit_len: usize, output_len: usize, output_hex: bool) { match bit_len { 128 => do_shake(SHAKE128::new(), output_len, output_hex), 256 => do_shake(SHAKE256::new(), output_len, output_hex), - _ => panic!("Unsupported algorithm: SHAKE-{}", bit_len) + _ => panic!("Unsupported algorithm: SHAKE-{}", bit_len), } } @@ -57,6 +58,8 @@ fn do_shake(mut shake: impl XOF, output_len: usize, output_hex: bool) { for b in out.iter() { print!("{b:02x}"); } - } else { io::stdout().write(&out).unwrap(); } + } else { + io::stdout().write(&out).unwrap(); + } println!(); -} \ No newline at end of file +} diff --git a/crypto/base64/Cargo.toml b/crypto/base64/Cargo.toml index 13411b9..2da0a8a 100644 --- a/crypto/base64/Cargo.toml +++ b/crypto/base64/Cargo.toml @@ -3,6 +3,12 @@ name = "bouncycastle-base64" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the (entire) `String`/`Vec`-returning encoder/decoder API. On by default; a +# `no_std` build without it exposes only the type definitions (there are no streaming `_out` variants). +default = ["alloc"] +alloc = [] + [dependencies] bouncycastle-core.workspace = true bouncycastle-utils.workspace = true diff --git a/crypto/base64/benches/base64_benches.rs b/crypto/base64/benches/base64_benches.rs index 914d671..efe84d0 100644 --- a/crypto/base64/benches/base64_benches.rs +++ b/crypto/base64/benches/base64_benches.rs @@ -1,8 +1,8 @@ -use std::hint::black_box; -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; -use bouncycastle_rng as rng; +use bouncycastle_base64::{Base64Decoder, Base64Encoder}; use bouncycastle_core::traits::RNG; -use bouncycastle_base64::{Base64Encoder, Base64Decoder}; +use bouncycastle_rng as rng; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_base64_encode(c: &mut Criterion) { const INPUT_SIZE: usize = 16 * 1024; @@ -37,7 +37,7 @@ fn bench_base64_decode(c: &mut Criterion) { // Generate some base65-encoded data. let mut encoder = Base64Encoder::new(); - let input: String = encoder.do_update(&data); // will be 1024 * 4 / 3 + 2 = 1368 bytes long. + let input: String = encoder.do_update(&data); // will be 1024 * 4 / 3 + 2 = 1368 bytes long. let mut output = vec![0u8; INPUT_SIZE]; @@ -47,16 +47,21 @@ fn bench_base64_decode(c: &mut Criterion) { b.iter(|| { let mut decoder = Base64Decoder::new(false); for _ in 0..16 { - output.extend_from_slice(&*decoder.do_update(black_box(&input)).expect("TODO: panic message")); + output.extend_from_slice( + &*decoder.do_update(black_box(&input)).expect("TODO: panic message"), + ); } - output.extend_from_slice(decoder.do_final(&str::from_utf8(&[0u8; 0]).expect("TODO: panic message")) - .expect("TODO: panic message").as_slice()); + output.extend_from_slice( + decoder + .do_final(&str::from_utf8(&[0u8; 0]).expect("TODO: panic message")) + .expect("TODO: panic message") + .as_slice(), + ); black_box(&output); }) }); group.finish(); } - criterion_group!(benches, bench_base64_encode, bench_base64_decode); -criterion_main!(benches); \ No newline at end of file +criterion_main!(benches); diff --git a/crypto/base64/src/lib.rs b/crypto/base64/src/lib.rs index 8b7cfeb..2f55313 100644 --- a/crypto/base64/src/lib.rs +++ b/crypto/base64/src/lib.rs @@ -81,15 +81,27 @@ // /// "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=" // URLSafe, +#![cfg_attr(not(test), no_std)] +// The entire base64 encoder/decoder API returns `String`/`Vec`, so it lives behind the +// default-on `alloc` feature. base64 is inherently an allocating codec (there are currently no +// streaming `_out` variants), so a `no_std` build without `alloc` exposes only the type definitions. +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "alloc")] +use alloc::{string::String, vec, vec::Vec}; + +#[cfg(feature = "alloc")] use bouncycastle_utils::ct::Condition; /// One-shot encode from bytes to a base64-encoded string using a constant-time implementation. +#[cfg(feature = "alloc")] pub fn encode>(input: T) -> String { Base64Encoder::new().do_final(input) } /// One-shot decode from a base64-encoded string to bytes using a constant-time implementation. +#[cfg(feature = "alloc")] pub fn decode>(input: T) -> Result, Base64Error> { Base64Decoder::new(true).do_final(input) } @@ -108,11 +120,13 @@ pub enum Base64Error { } /// The stateful base64 encoder that supports streaming. +#[cfg(feature = "alloc")] pub struct Base64Encoder { buf: [u8; 3], vals_in_buf: usize, } +#[cfg(feature = "alloc")] impl Base64Encoder { /// Create a new instance. pub fn new() -> Self { @@ -183,7 +197,7 @@ impl Base64Encoder { if self.vals_in_buf == 1 { out_buf[2] = b'='; } - out.push_str(std::str::from_utf8(&out_buf).unwrap()); + out.push_str(core::str::from_utf8(&out_buf).unwrap()); } out } @@ -193,6 +207,8 @@ impl Base64Encoder { assert!(inref.len() >= 3); assert!(out.len() >= 4); + out.fill(0); + out[0] = Self::ct_bin_to_b64(inref[0] >> 2); out[1] = Self::ct_bin_to_b64(((inref[0] & 0x03) << 4) | inref[1] >> 4); out[2] = Self::ct_bin_to_b64(((inref[1] & 0x0F) << 2) | inref[2] >> 6); @@ -201,12 +217,14 @@ impl Base64Encoder { } /// The stateful base64 decoder that supports streaming. +#[cfg(feature = "alloc")] pub struct Base64Decoder { buf: [u8; 4], vals_in_buf: usize, skip_whitespace: bool, } +#[cfg(feature = "alloc")] impl Base64Decoder { /// Create a new instance. pub fn new(skip_whitespace: bool) -> Self { @@ -229,7 +247,7 @@ impl Base64Decoder { #[allow(non_snake_case)] let c_AZ: i64 = b as i64 - 'A' as i64; let c_az: i64 = b as i64 - 'a' as i64 + 26; - let c_09: i64 = b as i64 - '0' as i64 + 2*26; + let c_09: i64 = b as i64 - '0' as i64 + 2 * 26; let mut ret: i64 = 0xFFi64; @@ -307,7 +325,9 @@ impl Base64Decoder { let mut out = match self.decode_internal(input, false) { Ok(out) => out, Err(Base64Error::PaddingEnconteredDuringDoUpdate) => { - panic!("rollback_if_padding = false should not produce a Base64Error::PaddingEnconteredDuringDoUpdate"); + panic!( + "rollback_if_padding = false should not produce a Base64Error::PaddingEnconteredDuringDoUpdate" + ); } Err(e) => return Err(e), }; diff --git a/crypto/base64/tests/base64_tests.rs b/crypto/base64/tests/base64_tests.rs index 607356e..eba9036 100644 --- a/crypto/base64/tests/base64_tests.rs +++ b/crypto/base64/tests/base64_tests.rs @@ -1,7 +1,7 @@ extern crate core; use bouncycastle_base64 as base64; -use bouncycastle_base64::{Base64Encoder, Base64Decoder}; +use bouncycastle_base64::{Base64Decoder, Base64Encoder}; const LOREM_IPSUM: &[u8] = b"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."; const LOREM_IPSUM_B64: &str = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCwgc2VkIGRvIGVpdXNtb2QgdGVtcG9yIGluY2lkaWR1bnQgdXQgbGFib3JlIGV0IGRvbG9yZSBtYWduYSBhbGlxdWEuIFV0IGVuaW0gYWQgbWluaW0gdmVuaWFtLCBxdWlzIG5vc3RydWQgZXhlcmNpdGF0aW9uIHVsbGFtY28gbGFib3JpcyBuaXNpIHV0IGFsaXF1aXAgZXggZWEgY29tbW9kbyBjb25zZXF1YXQuIER1aXMgYXV0ZSBpcnVyZSBkb2xvciBpbiByZXByZWhlbmRlcml0IGluIHZvbHVwdGF0ZSB2ZWxpdCBlc3NlIGNpbGx1bSBkb2xvcmUgZXUgZnVnaWF0IG51bGxhIHBhcmlhdHVyLiBFeGNlcHRldXIgc2ludCBvY2NhZWNhdCBjdXBpZGF0YXQgbm9uIHByb2lkZW50LCBzdW50IGluIGN1bHBhIHF1aSBvZmZpY2lhIGRlc2VydW50IG1vbGxpdCBhbmltIGlkIGVzdCBsYWJvcnVtLg=="; @@ -9,7 +9,7 @@ const LOREM_IPSUM_B64: &str = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3Rld #[cfg(test)] mod ctbase64_test { use super::*; - + #[test] fn test_base64_encode() { assert_eq!(base64::encode(b"\x00"), "AA=="); @@ -40,20 +40,29 @@ mod ctbase64_test { assert_eq!(base64::decode("SGVsbG8sIFdvcmxkIQ==").unwrap(), b"Hello, World!"); assert_eq!(base64::decode("AAECAwQFBg==").unwrap(), b"\x00\x01\x02\x03\x04\x05\x06"); assert_eq!(base64::decode("AAECAwQFBgc=").unwrap(), b"\x00\x01\x02\x03\x04\x05\x06\x07"); - assert_eq!(base64::decode("AAECAwQFBgcI").unwrap(), b"\x00\x01\x02\x03\x04\x05\x06\x07\x08"); + assert_eq!( + base64::decode("AAECAwQFBgcI").unwrap(), + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08" + ); // test some whitespace // failure case - let decoder = Base64Decoder::new(/*skip_whitespace=*/false); + let decoder = Base64Decoder::new(/*skip_whitespace=*/ false); match decoder.do_final(" AAE CA wQF \nBgcI") { Ok(_) => panic!("expected decode to fail"), Err(_) => {} } // success case - let decoder = Base64Decoder::new(/*skip_whitespace=*/true); - assert_eq!(decoder.do_final(" AAE CA wQF BgcI").unwrap(), b"\x00\x01\x02\x03\x04\x05\x06\x07\x08"); - assert_eq!(base64::decode(" AAE CA wQF BgcI").unwrap(), b"\x00\x01\x02\x03\x04\x05\x06\x07\x08"); + let decoder = Base64Decoder::new(/*skip_whitespace=*/ true); + assert_eq!( + decoder.do_final(" AAE CA wQF BgcI").unwrap(), + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08" + ); + assert_eq!( + base64::decode(" AAE CA wQF BgcI").unwrap(), + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08" + ); // test invalid base64 match base64::decode("AAECAwQF&?nBgcI") { @@ -73,10 +82,10 @@ mod ctbase64_test { let mut i: usize = 0; while i < LOREM_IPSUM_B64.len() - 10 { - out.extend(decoder.do_update(&LOREM_IPSUM_B64[i..i+10]).unwrap()); + out.extend(decoder.do_update(&LOREM_IPSUM_B64[i..i + 10]).unwrap()); i += 10; } out.extend(decoder.do_final(&LOREM_IPSUM_B64[i..]).unwrap()); assert_eq!(LOREM_IPSUM, out); } -} \ No newline at end of file +} diff --git a/crypto/core-test-framework/Cargo.toml b/crypto/core-test-framework/Cargo.toml index 49ae93b..37161c0 100644 --- a/crypto/core-test-framework/Cargo.toml +++ b/crypto/core-test-framework/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.1" edition.workspace = true [dependencies] -bouncycastle-core.workspace = true +# The KAT test framework exercises the `Vec`/`Box`-returning trait APIs, so it always needs `alloc`. +bouncycastle-core = { workspace = true, features = ["alloc"] } [dev-dependencies] \ No newline at end of file diff --git a/crypto/core-test-framework/src/fixed_seed_rng.rs b/crypto/core-test-framework/src/fixed_seed_rng.rs new file mode 100644 index 0000000..944487d --- /dev/null +++ b/crypto/core-test-framework/src/fixed_seed_rng.rs @@ -0,0 +1,95 @@ +//! A deterministic fake [RNG] for reproducible tests. + +use bouncycastle_core::errors::{KeyMaterialError, RNGError}; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterialTrait, KeyType}; +use bouncycastle_core::traits::{RNG, SecurityStrength}; + +/// A test-only fake [RNG] that produces a fixed, fully deterministic byte stream. +/// +/// The stream is the `SEED_LEN`-byte seed repeated indefinitely. A single internal counter is +/// shared across every [RNG] method, so each byte handed out — whether through +/// [RNG::next_bytes_out], [RNG::next_bytes], [RNG::next_int], or [RNG::fill_keymaterial_out] — +/// advances the same stream. Two instances built from the same seed therefore emit identical +/// streams, which is what makes RNG-driven operations reproducible (and comparable against their +/// seed/`m`-driven internal counterparts) in tests. +/// +/// This is a deterministic stub for tests only; it is in no way a secure RNG. +pub struct FixedSeedRNG { + seed: [u8; SEED_LEN], + counter: usize, + security_strength: SecurityStrength, +} + +impl FixedSeedRNG { + /// Create an instance that emits `seed` repeated indefinitely, starting from its first byte. + pub fn new(seed: [u8; SEED_LEN]) -> Self { + Self { seed, counter: 0, security_strength: SecurityStrength::_256bit } + } + + /// Pull the next byte from the deterministic stream and advance the counter. + fn next_byte(&mut self) -> u8 { + let b = self.seed[self.counter % SEED_LEN]; + self.counter += 1; + b + } + + /// For testing purposes, set the security strength that this RNG will report + pub fn set_security_strength(&mut self, security_strength: SecurityStrength) { + self.security_strength = security_strength; + } +} + +impl RNG for FixedSeedRNG { + /// No-op: this fake RNG ignores reseeding, since its stream is fixed by construction. + fn add_seed_keymaterial( + &mut self, + _additional_seed: &dyn KeyMaterialTrait, + ) -> Result<(), RNGError> { + Ok(()) + } + + fn next_int(&mut self) -> Result { + let mut buf = [0u8; 4]; + for slot in buf.iter_mut() { + *slot = self.next_byte(); + } + Ok(u32::from_le_bytes(buf)) + } + + fn next_bytes(&mut self, len: usize) -> Result, RNGError> { + let mut out = vec![0u8; len]; + for slot in out.iter_mut() { + *slot = self.next_byte(); + } + Ok(out) + } + + fn next_bytes_out(&mut self, out: &mut [u8]) -> Result { + for slot in out.iter_mut() { + *slot = self.next_byte(); + } + Ok(out.len()) + } + + /// Fill `out` to capacity from the stream and mark it as a full-entropy 256-bit seed, + /// mirroring what a real DRBG's `generate_keymaterial_out` produces. A 256-bit security + /// strength is enough for every ML-KEM / ML-DSA parameter set. + fn fill_keymaterial_out(&mut self, out: &mut dyn KeyMaterialTrait) -> Result { + let mut len = 0; + key_material::do_hazardous_operations(out, |out| { + len = self + .next_bytes_out(out.ref_to_bytes_mut()?) + .map_err(|_| KeyMaterialError::GenericError("RNG failed to acquire next bytes."))?; + out.set_key_len(len)?; + out.set_key_type(KeyType::Seed)?; + out.set_security_strength(SecurityStrength::_256bit) + })?; + + Ok(len) + } + + fn security_strength(&self) -> SecurityStrength { + self.security_strength.clone() + } +} diff --git a/crypto/core-test-framework/src/hash.rs b/crypto/core-test-framework/src/hash.rs index 8bcc000..337d8d8 100644 --- a/crypto/core-test-framework/src/hash.rs +++ b/crypto/core-test-framework/src/hash.rs @@ -30,6 +30,20 @@ impl TestFrameworkHash { H::default().hash_out(input, &mut output_buf); assert_eq!(output_buf, expected_output); + /*** fn hash_array(self, data: &[u8]) -> [u8; N] (no_std alternative) ***/ + // Use N = 64, the maximum output length across all hashes; hash_array zero-pads the tail + // beyond output_len, so the digest lands in the first OUTPUT_LEN bytes. + let arr: [u8; 64] = H::default().hash_array(input); + assert_eq!(&arr[..H::OUTPUT_LEN], expected_output, "hash_array digest mismatch"); + assert!(arr[H::OUTPUT_LEN..].iter().all(|&b| b == 0), "hash_array tail not zero-padded"); + + /*** fn do_final_array(self) -> [u8; N] (no_std alternative) ***/ + let mut message_digest = H::default(); + message_digest.do_update(input); + let arr: [u8; 64] = message_digest.do_final_array(); + assert_eq!(&arr[..H::OUTPUT_LEN], expected_output, "do_final_array digest mismatch"); + assert!(arr[H::OUTPUT_LEN..].iter().all(|&b| b == 0), "do_final_array tail not zero-padded"); + /*** fn do_update(&mut self, data: &[u8]) -> Result<(), HashError> ***/ /*** fn do_final(self) -> Result, HashError> **/ diff --git a/crypto/core-test-framework/src/kdf.rs b/crypto/core-test-framework/src/kdf.rs index 35b429a..cac4081 100644 --- a/crypto/core-test-framework/src/kdf.rs +++ b/crypto/core-test-framework/src/kdf.rs @@ -1,4 +1,6 @@ -use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait}; +use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, +}; use bouncycastle_core::traits::{KDF, SecurityStrength}; pub struct TestFrameworkKDF {} @@ -28,7 +30,7 @@ impl TestFrameworkKDF { // account for the fact that XOF style KDFs will will the provided buffer. assert!(bytes_written >= expected_output.key_len()); assert_eq!(output.key_len(), bytes_written); - output.truncate(expected_output.key_len()).unwrap(); + output.set_key_len(expected_output.key_len()).unwrap(); // truncates should be infallible assert_eq!(output.key_len(), expected_output.key_len()); assert_eq!(output.ref_to_bytes(), expected_output.ref_to_bytes()); @@ -57,31 +59,31 @@ impl TestFrameworkKDF { assert_eq!(zeroized_key.key_type(), KeyType::Zeroized); let out_key = H::default().derive_key(&zeroized_key, &[0u8; 10]).unwrap(); // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesLowEntropy -> BytesLowEntropy let low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap(); - assert_eq!(low_entropy_key.key_type(), KeyType::BytesLowEntropy); + KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::Unknown).unwrap(); + assert_eq!(low_entropy_key.key_type(), KeyType::Unknown); let out_key = H::default().derive_key(&low_entropy_key, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesFullEntropy -> BytesLowEntropy if not enough to fill the hash block let low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::BytesFullEntropy).unwrap(); - assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy); + KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::CryptographicRandom).unwrap(); + assert_eq!(low_entropy_key.key_type(), KeyType::CryptographicRandom); let out_key = H::default().derive_key(&low_entropy_key, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesFullEntropy -> BytesFullEntropy let full_entropy_key = - KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap(); - assert_eq!(full_entropy_key.key_type(), KeyType::BytesFullEntropy); + KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap(); + assert_eq!(full_entropy_key.key_type(), KeyType::CryptographicRandom); let out_key = H::default().derive_key(&full_entropy_key, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(out_key.key_type(), KeyType::CryptographicRandom); assert!(out_key.security_strength() > SecurityStrength::None); } @@ -97,7 +99,7 @@ impl TestFrameworkKDF { let output = kdf.derive_key_from_multiple(keys, additional_input).unwrap(); // This is sortof a hack since the rust language won't easily allow me to make the KeyMaterials the same length if output.key_len() < expected_output.key_len() { - expected_output.truncate(output.key_len()).unwrap(); + expected_output.set_key_len(output.key_len()).unwrap(); // truncates should be infallible } assert_eq!(output.key_len(), expected_output.key_len()); assert_eq!(output.ref_to_bytes(), expected_output.ref_to_bytes()); @@ -110,7 +112,7 @@ impl TestFrameworkKDF { // account for the fact that XOF style KDFs will will the provided buffer. assert!(bytes_written >= expected_output.key_len()); assert_eq!(output.key_len(), bytes_written); - output.truncate(expected_output.key_len()).unwrap(); + output.set_key_len(expected_output.key_len()).unwrap(); // truncates should be infallible assert_eq!(output.key_len(), expected_output.key_len()); assert_eq!(output.ref_to_bytes(), expected_output.ref_to_bytes()); @@ -139,35 +141,35 @@ impl TestFrameworkKDF { assert_eq!(zeroized_key.security_strength(), SecurityStrength::None); let keys = [&zeroized_key, &zeroized_key]; let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesLowEntropy -> BytesLowEntropy let low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap(); - assert_eq!(low_entropy_key.key_type(), KeyType::BytesLowEntropy); + KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::Unknown).unwrap(); + assert_eq!(low_entropy_key.key_type(), KeyType::Unknown); let keys = [&zeroized_key, &low_entropy_key]; let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesFullEntropy -> BytesLowEntropy if not enough to fill the hash block let low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::BytesFullEntropy).unwrap(); - assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy); + KeyMaterial256::from_bytes_as_type(&[1u8; 6], KeyType::CryptographicRandom).unwrap(); + assert_eq!(low_entropy_key.key_type(), KeyType::CryptographicRandom); let keys = [&zeroized_key, &low_entropy_key]; let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(out_key.key_type(), KeyType::Unknown); assert_eq!(out_key.security_strength(), SecurityStrength::None); // BytesFullEntropy -> BytesFullEntropy let zeroized64_key = KeyMaterial512::new(); let full_entropy_key = - KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap(); - assert_eq!(full_entropy_key.key_type(), KeyType::BytesFullEntropy); + KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap(); + assert_eq!(full_entropy_key.key_type(), KeyType::CryptographicRandom); let keys = [&zeroized64_key, &full_entropy_key]; let out_key = H::default().derive_key_from_multiple(&keys, &[0u8; 10]).unwrap(); - assert_eq!(out_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(out_key.key_type(), KeyType::CryptographicRandom); assert!(out_key.security_strength() > SecurityStrength::None); } } diff --git a/crypto/core-test-framework/src/kem.rs b/crypto/core-test-framework/src/kem.rs index 785d499..170c688 100644 --- a/crypto/core-test-framework/src/kem.rs +++ b/crypto/core-test-framework/src/kem.rs @@ -1,5 +1,8 @@ +use crate::FixedSeedRNG; use bouncycastle_core::errors::KEMError; -use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, KEM}; +use bouncycastle_core::traits::{ + KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey, RNG, SecurityStrength, +}; pub struct TestFrameworkKEM { // Put any config options here @@ -16,26 +19,48 @@ impl TestFrameworkKEM { Self { alg_is_deterministic, is_implicitly_rejecting } } - /// Test all the members of trait Hash against the given input-output pair. + /// Test all the members of traits [KEMEncapsulator] and [KEMDecapsulator] against the given input-output pair. /// This gives good baseline test coverage, but is not exhaustive. + /// + /// Since key generation is not part of either KEM trait, the caller supplies a + /// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct). pub fn test_kem< PK: KEMPublicKey, SK: KEMPrivateKey, - KEMAlg: KEM, + KEMAlg: KEMEncapsulator + KEMDecapsulator, const PK_LEN: usize, const SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, >( &self, + keygen: fn() -> Result<(PK, SK), KEMError>, run_full_bitflipping_tests: bool, ) { // Basic test - let (pk, sk) = KEMAlg::keygen().unwrap(); + let (pk, sk) = keygen().unwrap(); let (ss, ct) = KEMAlg::encaps(&pk).unwrap(); let ss1 = KEMAlg::decaps(&sk, &ct).unwrap(); assert_eq!(ss, ss1); + // Test that encaps_rng is deterministic in its RNG input: two encapsulations against the + // same public key, each fed an RNG that emits identical bytes, must produce the same + // shared secret and ciphertext. + { + let mut rng_a = FixedSeedRNG::new([0x5A; 64]); + let mut rng_b = FixedSeedRNG::new([0x5A; 64]); + let (ss_a, ct_a) = KEMAlg::encaps_rng(&pk, &mut rng_a).unwrap(); + let (ss_b, ct_b) = KEMAlg::encaps_rng(&pk, &mut rng_b).unwrap(); + assert_eq!( + ss_a, ss_b, + "encaps_rng shared secret must be deterministic given fixed RNG output" + ); + assert_eq!( + ct_a, ct_b, + "encaps_rng ciphertext must be deterministic given fixed RNG output" + ); + } + // Test non-determinism if !self.alg_is_deterministic { let (ss1, ct1) = KEMAlg::encaps(&pk).unwrap(); @@ -45,7 +70,7 @@ impl TestFrameworkKEM { } // Test that decaps fails for broken ct value - let (pk, sk) = KEMAlg::keygen().unwrap(); + let (pk, sk) = keygen().unwrap(); let (ss, mut ct) = KEMAlg::encaps(&pk).unwrap(); ct[17] ^= 0xFF; if self.is_implicitly_rejecting { @@ -53,12 +78,15 @@ impl TestFrameworkKEM { assert_ne!(ss, ss2); } else { match KEMAlg::decaps(&sk, &ct) { - Err(KEMError::DecapsulationFailed) => /* good */ (), + Err(KEMError::DecapsulationFailed) => + /* good */ + { + () + } _ => panic!("This should have thrown an error but it didn't."), } } - // test flipping every bit ... this will take some time to run if run_full_bitflipping_tests { for i in 0..ct.len() { @@ -72,66 +100,78 @@ impl TestFrameworkKEM { assert_ne!(ss, ss2); } else { match KEMAlg::decaps(&sk, &ct) { - Err(KEMError::DecapsulationFailed) => /* good */ (), + Err(KEMError::DecapsulationFailed) => + /* good */ + { + () + } _ => panic!("This should have thrown an error but it didn't."), } } } } } - // test ct the wrong length - let (pk, sk) = KEMAlg::keygen().unwrap(); + let (pk, sk) = keygen().unwrap(); let (_ss, ct) = KEMAlg::encaps(&pk).unwrap(); - // too short - match KEMAlg::decaps(&sk, &ct[..CT_LEN-1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + match KEMAlg::decaps(&sk, &ct[..CT_LEN - 1]) { + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("This should have thrown an error but it didn't."), }; - + // too long let mut long_ct = vec![1u8; CT_LEN + 2]; long_ct.as_mut_slice()[..CT_LEN].copy_from_slice(&ct); match KEMAlg::decaps(&sk, &long_ct) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("This should have thrown an error but it didn't."), }; + + // encaps_rng should reject an RNG at a lower security level than the KEM + let mut no_security_rng = FixedSeedRNG::new([0x00; 64]); + no_security_rng.set_security_strength(SecurityStrength::None); + assert_eq!(no_security_rng.security_strength(), SecurityStrength::None); + match KEMAlg::encaps_rng(&pk, &mut no_security_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("This should have thrown an error but it didn't."), + } } } pub struct TestFrameworkKEMKeys {} impl TestFrameworkKEMKeys { - pub fn new() -> Self { - Self { } + Self {} } + /// Since key generation is not part of either KEM trait, the caller supplies a + /// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct). pub fn test_keys< PK: KEMPublicKey, SK: KEMPrivateKey, - KEMAlg: KEM, const PK_LEN: usize, const SK_LEN: usize, - const CT_LEN: usize, - const SS_LEN: usize, - >(&self) { - self.test_boundary_conditions::(); + >( + &self, + keygen: fn() -> Result<(PK, SK), KEMError>, + ) { + self.test_boundary_conditions::(keygen); } /// Tests the correct behaviour on buffers too large / too small. fn test_boundary_conditions< PK: KEMPublicKey, SK: KEMPrivateKey, - KEMAlg: KEM, const PK_LEN: usize, const SK_LEN: usize, - const CT_LEN: usize, - const SS_LEN: usize, - >(&self) { - let (pk, sk) = KEMAlg::keygen().unwrap(); + >( + &self, + keygen: fn() -> Result<(PK, SK), KEMError>, + ) { + let (pk, sk) = keygen().unwrap(); let pk_bytes = pk.encode(); assert_eq!(pk_bytes.len(), PK_LEN); @@ -149,7 +189,6 @@ impl TestFrameworkKEMKeys { _ => panic!("Should have failed"), } - let sk_bytes = sk.encode(); assert_eq!(sk_bytes.len(), SK_LEN); // too short diff --git a/crypto/core-test-framework/src/lib.rs b/crypto/core-test-framework/src/lib.rs index 92f7215..52ccc87 100644 --- a/crypto/core-test-framework/src/lib.rs +++ b/crypto/core-test-framework/src/lib.rs @@ -14,6 +14,10 @@ pub mod kdf; pub mod kem; pub mod mac; pub mod signature; +pub mod suspendable_state; + +mod fixed_seed_rng; +pub use fixed_seed_rng::FixedSeedRNG; pub const DUMMY_SEED_512: &[u8; 512] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; diff --git a/crypto/core-test-framework/src/mac.rs b/crypto/core-test-framework/src/mac.rs index 728e35c..c8f7809 100644 --- a/crypto/core-test-framework/src/mac.rs +++ b/crypto/core-test-framework/src/mac.rs @@ -1,8 +1,10 @@ use crate::DUMMY_SEED_512; use bouncycastle_core::errors::{KeyMaterialError, MACError}; -use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait}; +use bouncycastle_core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; use bouncycastle_core::traits::MAC; -use bouncycastle_core::traits::{SecurityStrength}; +use bouncycastle_core::traits::SecurityStrength; pub struct TestFrameworkMAC { // Put any config options here @@ -58,6 +60,22 @@ impl TestFrameworkMAC { // Test .output_len() assert_eq!(output_len, out.len()); + // Test ::mac_array() and ::do_final_array() (no_std alternatives). + // N = 64 is >= every supported MAC output length (and >= the FIPS minimum), so the tag lands + // in the first output_len bytes with a zero-padded tail. + let arr: [u8; 64] = M::new_allow_weak_key(key).unwrap().mac_array(input).unwrap(); + assert_eq!(&arr[..expected_output.len()], expected_output, "mac_array digest mismatch"); + assert!(arr[expected_output.len()..].iter().all(|&b| b == 0), "mac_array tail not zero-padded"); + + let mut mac = M::new_allow_weak_key(key).unwrap(); + mac.do_update(input); + let arr: [u8; 64] = mac.do_final_array().unwrap(); + assert_eq!(&arr[..expected_output.len()], expected_output, "do_final_array digest mismatch"); + assert!( + arr[expected_output.len()..].iter().all(|&b| b == 0), + "do_final_array tail not zero-padded" + ); + // Test .init(), .do_update(), .do_mac_final_out() let mut mac = M::new_allow_weak_key(key).unwrap(); mac.do_update(input); @@ -71,7 +89,6 @@ impl TestFrameworkMAC { mac.do_update(input); mac.do_verify_final(expected_output); - // entropy of input key // MACs of all security strengths should throw an error on a no-security (and non-zero) key. @@ -81,43 +98,54 @@ impl TestFrameworkMAC { key_none.set_security_strength(SecurityStrength::None).unwrap(); match M::new(&key_none) { - Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ }, - _ => panic!("This should have thrown a KeyMaterialError::SecurityStrength error but it didn't"), + Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ } + _ => panic!( + "This should have thrown a KeyMaterialError::SecurityStrength error but it didn't" + ), } let mut low_security_key = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); - low_security_key.allow_hazardous_operations(); - match M::new_allow_weak_key(key).unwrap().max_security_strength() { - SecurityStrength::None => { - low_security_key.truncate(13).unwrap(); - low_security_key.set_security_strength(SecurityStrength::None).unwrap(); - } - SecurityStrength::_112bit => { - low_security_key.truncate(28).unwrap(); - low_security_key.set_security_strength(SecurityStrength::None).unwrap(); - } - SecurityStrength::_128bit => { - low_security_key.truncate(32).unwrap(); - low_security_key.set_security_strength(SecurityStrength::_112bit).unwrap(); - } - SecurityStrength::_192bit => { - low_security_key.truncate(48).unwrap(); - low_security_key.set_security_strength(SecurityStrength::_128bit).unwrap(); - } - SecurityStrength::_256bit => { - low_security_key.truncate(64).unwrap(); - low_security_key.set_security_strength(SecurityStrength::_192bit).unwrap(); - } - }; - low_security_key.drop_hazardous_operations(); + do_hazardous_operations(&mut low_security_key, |low_security_key| { + match M::new_allow_weak_key(key).unwrap().max_security_strength() { + SecurityStrength::None => { + low_security_key.set_key_len(13).unwrap(); // truncates should be infallible + low_security_key.set_security_strength(SecurityStrength::None).unwrap(); + } + SecurityStrength::_112bit => { + low_security_key.set_key_len(28).unwrap(); // truncate should be infallible + low_security_key.set_security_strength(SecurityStrength::None).unwrap(); + } + SecurityStrength::_128bit => { + low_security_key.set_key_len(32).unwrap(); // truncate should be infallible + low_security_key.set_security_strength(SecurityStrength::_112bit).unwrap(); + } + SecurityStrength::_192bit => { + low_security_key.set_key_len(48).unwrap(); // truncate should be infallible + low_security_key.set_security_strength(SecurityStrength::_128bit).unwrap(); + } + SecurityStrength::_256bit => { + low_security_key.set_key_len(64).unwrap(); // truncate should be infallible + low_security_key.set_security_strength(SecurityStrength::_192bit).unwrap(); + } + }; + Ok(()) + }) + .unwrap(); // init - assert!(low_security_key.security_strength() < M::new_allow_weak_key(key).unwrap().max_security_strength()); + assert!( + low_security_key.security_strength() + < M::new_allow_weak_key(key).unwrap().max_security_strength() + ); // complains at first match M::new(&low_security_key) { Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ } - _ => { panic!("This should have thrown a KeyMaterialError::SecurityStrength error but it didn't") } + _ => { + panic!( + "This should have thrown a KeyMaterialError::SecurityStrength error but it didn't" + ) + } } // but fine if you do it with .allow_weak_keys() let mut hmac = M::new_allow_weak_key(&low_security_key).unwrap(); diff --git a/crypto/core-test-framework/src/signature.rs b/crypto/core-test-framework/src/signature.rs index 1ee694e..914ae44 100644 --- a/crypto/core-test-framework/src/signature.rs +++ b/crypto/core-test-framework/src/signature.rs @@ -1,6 +1,9 @@ use crate::DUMMY_SEED_1024; use bouncycastle_core::errors::SignatureError; -use bouncycastle_core::traits::{PHSignature, Signature, SignaturePrivateKey, SignaturePublicKey}; +use bouncycastle_core::traits::{ + Hash, PHSignatureVerifier, PHSigner, SignaturePrivateKey, SignaturePublicKey, + SignatureVerifier, Signer, +}; pub struct TestFrameworkSignature { // Put any config options here @@ -16,54 +19,59 @@ impl TestFrameworkSignature { Self { alg_is_deterministic, alg_accepts_ctx } } - /// Test all the members of trait Hash against the given input-output pair. + /// Test all the members of traits [Signer] and [SignatureVerifier] against the given input-output pair. /// This gives good baseline test coverage, but is not exhaustive. + /// + /// Since key generation is not part of either signature trait, the caller supplies a + /// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct). pub fn test_signature< PK: SignaturePublicKey, SK: SignaturePrivateKey, - SigAlg: Signature, + SIGNER: Signer, + VERIFIER: SignatureVerifier, const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, >( &self, + keygen: fn() -> Result<(PK, SK), SignatureError>, run_full_bitflipping_tests: bool, ) { let msg = b"The quick brown fox jumped over the lazy dog"; // Basic test - let (pk, sk) = SigAlg::keygen().unwrap(); - let sig_val = SigAlg::sign(&sk, msg, None).unwrap(); - SigAlg::verify(&pk, msg, None, &sig_val).unwrap(); + let (pk, sk) = keygen().unwrap(); + let sig_val = SIGNER::sign(&sk, msg, None).unwrap(); + VERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); // Test non-determinism if !self.alg_is_deterministic { - let sig1 = SigAlg::sign(&sk, msg, None).unwrap(); - let sig2 = SigAlg::sign(&sk, msg, None).unwrap(); + let sig1 = SIGNER::sign(&sk, msg, None).unwrap(); + let sig2 = SIGNER::sign(&sk, msg, None).unwrap(); assert_ne!(sig1, sig2); } // uses ctx // success case - let sig = SigAlg::sign(&sk, msg, Some(b"test with ctx")).unwrap(); - SigAlg::verify(&pk, msg, Some(b"test with ctx"), &sig).unwrap(); + let sig = SIGNER::sign(&sk, msg, Some(b"test with ctx")).unwrap(); + VERIFIER::verify(&pk, msg, Some(b"test with ctx"), &sig).unwrap(); // but it had better produce something different if !self.alg_accepts_ctx { - let sig1 = SigAlg::sign(&sk, msg, None).unwrap(); - let sig2 = SigAlg::sign(&sk, msg, Some(&[0u8; 1])).unwrap(); + let sig1 = SIGNER::sign(&sk, msg, None).unwrap(); + let sig2 = SIGNER::sign(&sk, msg, Some(&[0u8; 1])).unwrap(); assert_ne!(sig1, sig2); } // Test that verification fails for broken signature value - let (pk, sk) = SigAlg::keygen().unwrap(); - let sig_val = SigAlg::sign(&sk, msg, None).unwrap(); + let (pk, sk) = keygen().unwrap(); + let sig_val = SIGNER::sign(&sk, msg, None).unwrap(); // spot-check let mut sig_val_copy = sig_val.clone(); sig_val_copy[8] ^= 0x0F; // should throw an Err - match SigAlg::verify(&pk, msg, None, &sig_val_copy) { + match VERIFIER::verify(&pk, msg, None, &sig_val_copy) { Err(SignatureError::SignatureVerificationFailed) => (), _ => panic!("This should have thrown an error but it didn't."), } @@ -76,7 +84,7 @@ impl TestFrameworkSignature { sig_val_copy[i] ^= 1 << j; // should throw an Err - match SigAlg::verify(&pk, msg, None, &sig_val_copy) { + match VERIFIER::verify(&pk, msg, None, &sig_val_copy) { Err(SignatureError::SignatureVerificationFailed) => (), _ => panic!( "This should have thrown an error but it didn't when byte {i} bit {j} of the signature was flipped" @@ -91,13 +99,13 @@ impl TestFrameworkSignature { // Success case let mut output = [0u8; SIG_LEN]; - let bytes_written = SigAlg::sign_out(&sk, msg, None, &mut output).unwrap(); + let bytes_written = SIGNER::sign_out(&sk, msg, None, &mut output).unwrap(); assert_eq!(bytes_written, SIG_LEN); - SigAlg::verify(&pk, msg, None, &sig_val).unwrap(); + VERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); // test with a large message - let sig = SigAlg::sign(&sk, DUMMY_SEED_1024, None).unwrap(); - SigAlg::verify(&pk, DUMMY_SEED_1024, None, &sig).unwrap(); + let sig = SIGNER::sign(&sk, DUMMY_SEED_1024, None).unwrap(); + VERIFIER::verify(&pk, DUMMY_SEED_1024, None, &sig).unwrap(); // Test the streaming signing API // fn sign_init(&mut self, sk: &SK) -> Result<(), SignatureError>; @@ -106,37 +114,37 @@ impl TestFrameworkSignature { // fn sign_final_out(&mut self, msg_chunk: &[u8], ctx: &[u8], output: &mut [u8]) -> Result<(), SignatureError>; // First, test the streaming API with one call to .sign_update - let mut s = SigAlg::sign_init(&sk, Some(b"streaming API")).unwrap(); + let mut s = SIGNER::sign_init(&sk, Some(b"streaming API")).unwrap(); s.sign_update(DUMMY_SEED_1024); let sig_val = s.sign_final().unwrap(); - SigAlg::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); + VERIFIER::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); // Then with the message broken into chunks - let mut s = SigAlg::sign_init(&sk, Some(b"streaming API chunked")).unwrap(); + let mut s = SIGNER::sign_init(&sk, Some(b"streaming API chunked")).unwrap(); for msg_chunk in DUMMY_SEED_1024.chunks(100) { s.sign_update(msg_chunk); } let sig_val = s.sign_final().unwrap(); - SigAlg::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API chunked"), &sig_val).unwrap(); + VERIFIER::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API chunked"), &sig_val).unwrap(); // Test the streaming verification API // one-shot - let sig = SigAlg::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); - let mut v = SigAlg::verify_init(&pk, Some(b"streaming API")).unwrap(); + let sig = SIGNER::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); + let mut v = VERIFIER::verify_init(&pk, Some(b"streaming API")).unwrap(); v.verify_update(DUMMY_SEED_1024); v.verify_final(&sig).unwrap(); // chunked - let sig = SigAlg::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); - let mut v = SigAlg::verify_init(&pk, Some(b"streaming API")).unwrap(); + let sig = SIGNER::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); + let mut v = VERIFIER::verify_init(&pk, Some(b"streaming API")).unwrap(); for msg_chunk in DUMMY_SEED_1024.chunks(100) { v.verify_update(msg_chunk); } v.verify_final(&sig).unwrap(); // failure case for streaming verify - let sig = SigAlg::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); - let mut v = SigAlg::verify_init(&pk, Some(b"streaming API")).unwrap(); + let sig = SIGNER::sign(&sk, DUMMY_SEED_1024, Some(b"streaming API")).unwrap(); + let mut v = VERIFIER::verify_init(&pk, Some(b"streaming API")).unwrap(); v.verify_update(b"this is the wrong message"); match v.verify_final(&sig) { Err(SignatureError::SignatureVerificationFailed) => (), @@ -144,70 +152,75 @@ impl TestFrameworkSignature { } // test sign_out version of streaming API - let mut s = SigAlg::sign_init(&sk, Some(b"streaming API")).unwrap(); + let mut s = SIGNER::sign_init(&sk, Some(b"streaming API")).unwrap(); s.sign_update(DUMMY_SEED_1024); let mut sig_val = [0u8; SIG_LEN]; let bytes_written = s.sign_final_out(&mut sig_val).unwrap(); assert_eq!(bytes_written, SIG_LEN); - SigAlg::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); - + VERIFIER::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); // the ::verify API should accept a sig value that's too long and just ignore the extra bytes let mut sig_val_too_long = vec![1u8; SIG_LEN + 2]; sig_val_too_long[..SIG_LEN].copy_from_slice(&sig_val); - SigAlg::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); + VERIFIER::verify(&pk, DUMMY_SEED_1024, Some(b"streaming API"), &sig_val).unwrap(); } - - /// Test all the members of trait Hash against the given input-output pair. + /// Test all the members of traits [PHSigner] and [PHSignatureVerifier] against the given input-output pair. /// This gives good baseline test coverage, but is not exhaustive. + /// + /// Since key generation is not part of either signature trait, the caller supplies a + /// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct). pub fn test_ph_signature< PK: SignaturePublicKey, SK: SignaturePrivateKey, - SigAlg: PHSignature, + // todo split this into two params: SIGNER: Signer and VERIFIER: SignatureVerifier + PHSIGNER: PHSigner, + PHVERIFIER: PHSignatureVerifier, + HASH: Hash + Default, const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, const PH_LEN: usize, >( &self, + keygen: fn() -> Result<(PK, SK), SignatureError>, run_full_bitflipping_tests: bool, ) { let msg = b"The quick brown fox jumped over the lazy dog"; // Basic test - let (pk, sk) = SigAlg::keygen().unwrap(); - let sig_val = SigAlg::sign(&sk, msg, None).unwrap(); - SigAlg::verify(&pk, msg, None, &sig_val).unwrap(); + let (pk, sk) = keygen().unwrap(); + let sig_val = PHSIGNER::sign(&sk, msg, None).unwrap(); + PHVERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); // Test non-determinism if !self.alg_is_deterministic { - let sig1 = SigAlg::sign(&sk, msg, None).unwrap(); - let sig2 = SigAlg::sign(&sk, msg, None).unwrap(); + let sig1 = PHSIGNER::sign(&sk, msg, None).unwrap(); + let sig2 = PHSIGNER::sign(&sk, msg, None).unwrap(); assert_ne!(sig1, sig2); } // uses ctx // success case - let sig = SigAlg::sign(&sk, msg, Some(b"test with ctx")).unwrap(); - SigAlg::verify(&pk, msg, Some(b"test with ctx"), &sig).unwrap(); + let sig = PHSIGNER::sign(&sk, msg, Some(b"test with ctx")).unwrap(); + PHVERIFIER::verify(&pk, msg, Some(b"test with ctx"), &sig).unwrap(); // but it had better produce something different if !self.alg_accepts_ctx { - let sig1 = SigAlg::sign(&sk, msg, None).unwrap(); - let sig2 = SigAlg::sign(&sk, msg, Some(&[0u8; 1])).unwrap(); + let sig1 = PHSIGNER::sign(&sk, msg, None).unwrap(); + let sig2 = PHSIGNER::sign(&sk, msg, Some(&[0u8; 1])).unwrap(); assert_ne!(sig1, sig2); } // Test that verification fails for broken signature value - let (pk, sk) = SigAlg::keygen().unwrap(); - let sig_val = SigAlg::sign(&sk, msg, None).unwrap(); + let (pk, sk) = keygen().unwrap(); + let sig_val = PHSIGNER::sign(&sk, msg, None).unwrap(); // spot-check let mut sig_val_copy = sig_val.clone(); sig_val_copy[8] ^= 0x0F; // should throw an Err - match SigAlg::verify(&pk, msg, None, &sig_val_copy) { + match PHVERIFIER::verify(&pk, msg, None, &sig_val_copy) { Err(SignatureError::SignatureVerificationFailed) => (), _ => panic!("This should have thrown an error but it didn't."), } @@ -220,7 +233,7 @@ impl TestFrameworkSignature { sig_val_copy[i] ^= 1 << j; // should throw an Err - match SigAlg::verify(&pk, msg, None, &sig_val_copy) { + match PHVERIFIER::verify(&pk, msg, None, &sig_val_copy) { Err(SignatureError::SignatureVerificationFailed) => (), _ => panic!( "This should have thrown an error but it didn't when byte {i} bit {j} of the signature was flipped" @@ -235,54 +248,72 @@ impl TestFrameworkSignature { // Success case let mut output = [0u8; SIG_LEN]; - let bytes_written = SigAlg::sign_out(&sk, msg, None, &mut output).unwrap(); + let bytes_written = PHSIGNER::sign_out(&sk, msg, None, &mut output).unwrap(); assert_eq!(bytes_written, SIG_LEN); - SigAlg::verify(&pk, msg, None, &sig_val).unwrap(); + PHVERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); // test with a large message - let sig = SigAlg::sign(&sk, DUMMY_SEED_1024, None).unwrap(); - SigAlg::verify(&pk, DUMMY_SEED_1024, None, &sig).unwrap(); - + let sig = PHSIGNER::sign(&sk, DUMMY_SEED_1024, None).unwrap(); + PHVERIFIER::verify(&pk, DUMMY_SEED_1024, None, &sig).unwrap(); // the ::verify API should not accept a sig value that's too let mut sig_val_too_long = vec![1u8; SIG_LEN + 2]; sig_val_too_long[..SIG_LEN].copy_from_slice(&sig); - match SigAlg::verify(&pk, DUMMY_SEED_1024, None, &sig_val_too_long) { + match PHVERIFIER::verify(&pk, DUMMY_SEED_1024, None, &sig_val_too_long) { Err(SignatureError::LengthError(_)) => (), _ => panic!("Unexpected error"), } + + // sign_ph + let (pk, sk) = keygen().unwrap(); + let ph: [u8; PH_LEN] = HASH::default().hash(msg)[..PH_LEN].try_into().unwrap(); + let sig_val = PHSIGNER::sign_ph(&sk, &ph, None).unwrap(); + PHVERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); + PHVERIFIER::verify_ph(&pk, &ph, None, &sig_val).unwrap(); + + // sign_ph_out + let (pk, sk) = keygen().unwrap(); + let ph: [u8; PH_LEN] = HASH::default().hash(msg)[..PH_LEN].try_into().unwrap(); + let mut sig_val = [0u8; SIG_LEN]; + let bytes_written = PHSIGNER::sign_ph_out(&sk, &ph, None, &mut sig_val).unwrap(); + assert_eq!(bytes_written, SIG_LEN); + PHVERIFIER::verify_ph(&pk, &ph, None, &sig_val).unwrap(); + PHVERIFIER::verify(&pk, msg, None, &sig_val).unwrap(); } } pub struct TestFrameworkSignatureKeys {} impl TestFrameworkSignatureKeys { - pub fn new() -> Self { - Self { } + Self {} } + /// Since key generation is not part of either signature trait, the caller supplies a + /// `keygen` function pointer (the inherent `keygen` associated function on the algorithm struct). pub fn test_keys< PK: SignaturePublicKey, SK: SignaturePrivateKey, - SigAlg: Signature, const PK_LEN: usize, const SK_LEN: usize, - const SIG_LEN: usize, - >(&self) { - self.test_boundary_conditions::(); + >( + &self, + keygen: fn() -> Result<(PK, SK), SignatureError>, + ) { + self.test_boundary_conditions::(keygen); } /// Tests the correct behaviour on buffers too large / too small. fn test_boundary_conditions< PK: SignaturePublicKey, SK: SignaturePrivateKey, - SigAlg: Signature, const PK_LEN: usize, const SK_LEN: usize, - const SIG_LEN: usize, - >(&self) { - let (pk, sk) = SigAlg::keygen().unwrap(); + >( + &self, + keygen: fn() -> Result<(PK, SK), SignatureError>, + ) { + let (pk, sk) = keygen().unwrap(); let pk_bytes = pk.encode(); assert_eq!(pk_bytes.len(), PK_LEN); @@ -300,7 +331,6 @@ impl TestFrameworkSignatureKeys { _ => panic!("Should have failed"), } - let sk_bytes = sk.encode(); assert_eq!(sk_bytes.len(), SK_LEN); // too short diff --git a/crypto/core-test-framework/src/suspendable_state.rs b/crypto/core-test-framework/src/suspendable_state.rs new file mode 100644 index 0000000..677ed4e --- /dev/null +++ b/crypto/core-test-framework/src/suspendable_state.rs @@ -0,0 +1,179 @@ +use bouncycastle_core::errors::SuspendableError; +use bouncycastle_core::suspendable_state::{LIB_VERSION, SemVer}; +use bouncycastle_core::traits::{Suspendable, SuspendableKeyed}; + +pub struct TestFrameworkSuspendableState {} + +impl TestFrameworkSuspendableState { + pub fn new() -> Self { + Self {} + } + + /// Test all the members of trait SerializableState. + /// + /// Expects ta be handed an instance of the object that has some in-progress state to be serialized. + pub fn test + Clone>( + &self, + instance: &S, + ) { + // There's not a lot we can test here in the abstract, but we can test a few things to + // ensure that the SerializableState trait has been impl'd correctly. + + // we need to work on a clone because .serialize_state() moves self, which you can't do on a + // borrowed instance. + let instance_clone = instance.clone(); + + // You can serialize and then deserialize the state. + let serialized_state = instance_clone.suspend(); + assert_eq!(serialized_state.len(), SERIALIZED_STATE_LEN); + + let _deserialized_state = S::from_suspended(serialized_state).unwrap(); + + // The serialized state MUST include a prefix indicating the current version of the library. + let state_sized: [u8; 3] = serialized_state[..3].try_into().unwrap(); + assert_eq!(SemVer::from(state_sized), LIB_VERSION); + + // All implementations MUST reject a serialized state from lib ver 0.0.0 + // This doesn't really serve any purpose except testing that all impl's have properly + // used the helper functions. + let mut ver0_serialized_state = serialized_state.clone(); + ver0_serialized_state[..3].copy_from_slice(&[0, 0, 0]); + match S::from_suspended(ver0_serialized_state) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + // All implementations MUST reject a serialized state from a future MAJOR or MINOR version. + let mut future_ver = LIB_VERSION; + future_ver.major += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + match S::from_suspended(futurever_serialized_state) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + let mut future_ver = LIB_VERSION; + future_ver.minor += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + match S::from_suspended(futurever_serialized_state) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + // but should accept anything on the same patch stream. + let mut future_ver = LIB_VERSION; + future_ver.patch += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + let _deserialized_state = S::from_suspended(futurever_serialized_state).unwrap(); + + // ... even up to patch 255 + let mut future_ver = LIB_VERSION; + future_ver.patch = 255; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + let _deserialized_state = S::from_suspended(futurever_serialized_state).unwrap(); + } +} + +pub struct TestFrameworkSuspendableKeyedState {} + +impl TestFrameworkSuspendableKeyedState { + pub fn new() -> Self { + Self {} + } + + /// Test all the members of trait SerializableState. + /// + /// Expects ta be handed an instance of the object that has some in-progress state to be serialized. + pub fn test< + const SERIALIZED_STATE_LEN: usize, + S: SuspendableKeyed + Clone, + >( + &self, + instance: &S, + key: &S::Key, + ) { + // There's not a lot we can test here in the abstract, but we can test a few things to + // ensure that the SerializableState trait has been impl'd correctly. + + // we need to work on a clone because .serialize_state() moves self, which you can't do on a + // borrowed instance. + let instance_clone = instance.clone(); + + // You can serialize and then deserialize the state. + let serialized_state = instance_clone.suspend(); + assert_eq!(serialized_state.len(), SERIALIZED_STATE_LEN); + + let _deserialized_state = S::from_suspended(serialized_state, key).unwrap(); + + // The serialized state MUST include a prefix indicating the current version of the library. + let state_sized: [u8; 3] = serialized_state[..3].try_into().unwrap(); + assert_eq!(SemVer::from(state_sized), LIB_VERSION); + + // All implementations MUST reject a serialized state from lib ver 0.0.0 + // This doesn't really serve any purpose except testing that all impl's have properly + // used the helper functions. + let mut ver0_serialized_state = serialized_state.clone(); + ver0_serialized_state[..3].copy_from_slice(&[0, 0, 0]); + match S::from_suspended(ver0_serialized_state, key) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + // All implementations MUST reject a serialized state from a future MAJOR or MINOR version. + let mut future_ver = LIB_VERSION; + future_ver.major += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + match S::from_suspended(futurever_serialized_state, key) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + let mut future_ver = LIB_VERSION; + future_ver.minor += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + match S::from_suspended(futurever_serialized_state, key) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => { + panic!("Expected IncompatibleVersion error") + } + } + + // but should accept anything on the same patch stream. + let mut future_ver = LIB_VERSION; + future_ver.patch += 1; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + let _deserialized_state = S::from_suspended(futurever_serialized_state, key).unwrap(); + + // ... even up to patch 255 + let mut future_ver = LIB_VERSION; + future_ver.patch = 255; + let mut futurever_serialized_state = serialized_state.clone(); + futurever_serialized_state[..3] + .copy_from_slice(&[future_ver.major, future_ver.minor, future_ver.patch]); + let _deserialized_state = S::from_suspended(futurever_serialized_state, key).unwrap(); + } +} diff --git a/crypto/core/Cargo.toml b/crypto/core/Cargo.toml index 2b9c969..2079bbb 100644 --- a/crypto/core/Cargo.toml +++ b/crypto/core/Cargo.toml @@ -3,6 +3,13 @@ name = "bouncycastle-core" version = "0.1.1" edition.workspace = true +[features] +# `alloc` pulls in the `Vec`/`Box`-returning convenience APIs. It is on by default so that +# regular (std) builds are unaffected. `no_std` users who cannot allocate should build with +# `default-features = false` and use the `*_out(&mut [u8])` / `*_array::()` APIs instead. +default = ["alloc"] +alloc = [] + [dependencies] bouncycastle-utils.workspace = true diff --git a/crypto/core/src/errors.rs b/crypto/core/src/errors.rs index de80d9a..a079aad 100644 --- a/crypto/core/src/errors.rs +++ b/crypto/core/src/errors.rs @@ -64,9 +64,22 @@ pub enum RNGError { /// Indicates that the RNG cannot produce any more output until it has been reseeded with fresh entropy. ReseedRequired, + /// Thrown my algorithms attempting to use an RNG instance, for example for key generation or + /// other randomness required by the algorithm, but the provided RNG is at a lower security strength + /// than the algorithm requires. + SecurityStrengthInsufficientForAlgorithm, + KeyMaterialError(KeyMaterialError), } +#[derive(Debug)] +pub enum SuspendableError { + /// The serialized state was produced by a library version incompatible with this one. + IncompatibleVersion, + /// The serialized state is malformed or corrupt. + InvalidData, +} + #[derive(Debug)] pub enum SignatureError { GenericError(&'static str), @@ -80,9 +93,6 @@ pub enum SignatureError { RNGError(RNGError), } - - - /*** Promotion functions ***/ impl From for HashError { fn from(e: KeyMaterialError) -> HashError { @@ -115,7 +125,9 @@ impl From for KEMError { } impl From for KEMError { - fn from(e: RNGError) -> KEMError { Self::RNGError(e) } + fn from(e: RNGError) -> KEMError { + Self::RNGError(e) + } } impl From for MACError { @@ -143,5 +155,7 @@ impl From for SignatureError { } impl From for SignatureError { - fn from(e: RNGError) -> SignatureError { Self::RNGError(e) } + fn from(e: RNGError) -> SignatureError { + Self::RNGError(e) + } } diff --git a/crypto/core/src/key_material.rs b/crypto/core/src/key_material.rs index 56aae3f..ece984c 100644 --- a/crypto/core/src/key_material.rs +++ b/crypto/core/src/key_material.rs @@ -2,10 +2,6 @@ //! The main purpose is to hold metadata about the contained key material such as the key type and //! entropy content to prevent accidental misuse security bugs, such as deriving cryptographic keys //! from uninitialized data. -//! -//! This object allows several types of manual-overrides, which typically require setting the [KeyMaterial::allow_hazardous_operations] flag. -//! For example, the raw bytes data can be extracted, or the key forced to a certain type, -//! but well-designed use of the bc-rust.test library should not need to ever set the [KeyMaterial::allow_hazardous_operations] flag. //! The core idea of this wrapper is to keep track of the usage of the key material, including //! the amount of entropy that it is presumed to contain in order to prevent users from accidentally //! using it inappropriately in a way that could lead to security weaknesses. @@ -21,25 +17,42 @@ //! * Keyed KDFs that are given a key of RawFullEntropy or KeyedHashKey a KeyMaterial data of type RawLowEntropy or RawUnknownEntropy will promote it into RawFullEntropy. //! * Symmetric ciphers or asymmetric ciphers such as X25519 or ML-KEM that accept private key seeds will expect KeyMaterial of type AsymmetricPrivateKeySeed. //! -//! However, there is a [KeyMaterial::convert_key_type] for cases where the user has more context knowledge than the library. +//! However, there is a [KeyMaterialTrait::set_key_type] for cases where the user has more context knowledge than the library. //! Some conversions, such as converting a key of type RawLowEntropy into a SymmetricCipherKey, will fail unless -//! the user has explicitly allowed them via calling allow_hazardous_operations() prior to the conversion. -//! -//! Examples of hazardous conversions that require allow_hazardous_operations() to be called first: +//! run inside of a [do_hazardous_operations] closure, see below. //! -//! * Converting a KeyMaterial of type RawLowEntropy or RawUnknownEntropy into RawFullEntropy or any other full-entropy key type. -//! * Converting any algorithm-specific key type into a different algorithm-specific key type, which is considered hazardous since key reuse between different cryptographic algorithms is generally discouraged and can sometimes lead to key leakage. +//! # Security //! //! Additional security features: //! * Zeroizes on destruction. //! * Implementing Display and Debug to print metadata but not key material to prevent accidental logging. //! +//! # Hazardous Operations +//! +//! This object allows several types of manual-overrides, many of which are considered +//! "hazardous operations" since by definition they are allowing you to bypass checks meant to detect +//! conditions that could lead to security vulnerabilities. +//! Consider, for example, that you are reading a symmetric key from somewhere outside the library, +//! maybe from disk or from another process, but maybe you handed in the wrong variable and instead +//! handed in an uninitialized (all-zero) buffer. +//! Since this is a common bug that has catestrophic security implications, the library will normally +//! check for all-zero KeyMoterial objects and throw an error. +//! But there will be cases in which you really do need to use an all-zero key, so you can create +//! one if you do it in hazardous operations mode. +//! +//! Examples of hazardous conversions that are required to be run inside of a do_hazardous_operations() closure: +//! +//! * Converting a KeyMaterial of type RawLowEntropy or RawUnknownEntropy into RawFullEntropy or any other full-entropy key type. +//! * Converting any algorithm-specific key type into a different algorithm-specific key type, which is considered hazardous since key reuse between different cryptographic algorithms is generally discouraged and can sometimes lead to key leakage. +//! //! As with all wrappers of this nature, the intent is to protect the user from making silly mistakes, not to prevent expert users from doing what they need to do. //! It as always possible, for example, to extract the bytes from a KeyMaterial object, manipulate them, and then re-wrap them in a new KeyMaterial object. +//! +//! See [do_hazardous_operations] for documentation and sample code. -use crate::errors::KeyMaterialError; -use crate::traits::{RNG, SecurityStrength, Secret}; -use bouncycastle_utils::{ct, max, min}; +use crate::errors::{KeyMaterialError, SuspendableError}; +use crate::traits::{RNG, Secret, SecurityStrength}; +use bouncycastle_utils::{ct, min}; use core::cmp::{Ordering, PartialOrd}; use core::fmt; @@ -51,20 +64,47 @@ pub type KeyMaterial128 = KeyMaterial<16>; pub type KeyMaterial256 = KeyMaterial<32>; pub type KeyMaterial512 = KeyMaterial<64>; - /// A helper class used across the bc-rust.test library to hold bytes-like key material. /// See [KeyMaterial] for for details, such as constructors. -pub trait KeyMaterialTrait { +#[allow(private_bounds)] +pub trait KeyMaterialTrait: KeyMaterialInternalTrait { /// Loads the provided data into a new KeyMaterial of the specified type. /// This is discouraged unless the caller knows the provenance of the data, such as loading it /// from a cryptographic private key file. - /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead. + /// + /// This behaves differently on all-zero input key depending on whether it is run within a [do_hazardous_operations] closure: + /// if not set, then it will succeed, setting the key type to [KeyType::Zeroized] and also return a [KeyMaterialError::ActingOnZeroizedKey] + /// to indicate that you may want to perform error-handling, which could be manually setting the key type + /// if you intend to allow zero keys, or do some other error-handling, like figure out why your RNG is broken. + /// Note that even if a [KeyMaterialError::ActingOnZeroizedKey] is returned, the object is still populated and usable. + /// For example, you could catch it like this: + /// ``` + /// use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait, do_hazardous_operations}; + /// use bouncycastle_core::key_material::KeyMaterial; + /// use bouncycastle_core::errors::KeyMaterialError; + /// + /// let key_bytes = [0u8; 16]; + /// let mut key = KeyMaterial256::new(); + /// let res = key.set_bytes_as_type(&key_bytes, KeyType::Unknown); + /// match res { + /// Err(KeyMaterialError::ActingOnZeroizedKey) => { + /// // Either figure out why your passed an all-zero key, + /// // or set the key type manually, if that's what you intended. + /// do_hazardous_operations(&mut key, |key| { + /// key.set_key_type(KeyType::Unknown) + /// }).unwrap(); // probably you should do something more elegant than .unwrap in your code ;) + /// }, + /// Err(_) => { /* figure out what else went wrong */ }, + /// Ok(_) => { /* good */ }, + /// } + /// ``` + /// On the other hand, if run inside a [do_hazardous_operations] closure then it will just do what you asked without complaining. + /// /// Since this zeroizes and resets the key material, this is considered a dangerous conversion. /// - /// The only hazardous operation here that requires setting [KeyMaterialTrait::allow_hazardous_operations] is giving it - /// an all-zero key, which is checked as a courtesy to catch mistakes of feeding an initialized buffer - /// instead of an actual key. See the note on [KeyMaterial::set_bytes_as_type] for suggestions - /// for handling this. + /// Will set the [SecurityStrength] automatically according to the following rules: + /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::Unknown] then it will be [SecurityStrength::None]. + /// * Otherwise it will set it based on the length of the provided source bytes. fn set_bytes_as_type( &mut self, source: &[u8], @@ -74,19 +114,20 @@ pub trait KeyMaterialTrait { /// Get a reference to the underlying key material bytes. /// /// By reading the key bytes out of the [KeyMaterialTrait] object, you lose the protections that it offers, - /// however, this does not require [KeyMaterialTrait::allow_hazardous_operations] in the name of API ergonomics: - /// setting [KeyMaterialTrait::allow_hazardous_operations] requires a mutable reference and reading the bytes + /// however, this does not require [do_hazardous_operations] in the name of API ergonomics: + /// setting [do_hazardous_operations] requires a mutable reference and reading the bytes /// is not an operation that should require mutability. - /// TODO -- consider whether this should consume the object fn ref_to_bytes(&self) -> &[u8]; /// Get a mutable reference to the underlying key material bytes so that you can read or write /// to the underlying bytes without needing to create a temporary buffer, especially useful in /// cases where the required size of that buffer may be tricky to figure out at compile-time. - /// This requires [KeyMaterialTrait::allow_hazardous_operations] to be set. - /// When writing directly to the buffer, you are responsible for setting the key_len and key_type afterwards, - /// and you should [KeyMaterialTrait::drop_hazardous_operations]. - fn mut_ref_to_bytes(&mut self) -> Result<&mut [u8], KeyMaterialError>; + /// + /// # 🚨 Hazardous Operation🚨 + /// This function needs to be run within a [do_hazardous_operations] closure. + /// + /// When writing directly to the buffer, you are responsible for setting the key_len and key_type afterward. + fn ref_to_bytes_mut(&mut self) -> Result<&mut [u8], KeyMaterialError>; /// The size of the internal buffer; ie the largest key that this instance can hold. /// Equivalent to the constant param this object was created with. @@ -95,12 +136,35 @@ pub trait KeyMaterialTrait { /// Length of the key material in bytes. fn key_len(&self) -> usize; - /// Requires [KeyMaterialTrait::allow_hazardous_operations]. + /// Sets the internal key length without changing the capacity of the KeyMaterial. + /// Primarily intended for truncation if you are provided with a key that is larger than you need, + /// or to extend the length of an undersized KeyMaterial. + /// + /// If truncating, it will automatically downgrade the SecurityStrength accordingly. + /// + /// # 🚨 Hazardous Operation🚨 + /// Using this function to extend the length of a key is always hazardous and needs to be run + /// within a [do_hazardous_operations] closure since this can result + /// in a key containing a large number of zeroes, or containing key material from a previous key + /// held in the same buffer. When extending the length, you take responsibility for the security + /// implications. + /// + /// Truncation (that is, reducing the length) is always safe and does not require a + /// [do_hazardous_operations] closure. fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError>; fn key_type(&self) -> KeyType; - /// Requires [KeyMaterialTrait::allow_hazardous_operations]. + /// Sets (or safely converts) the [KeyType] of this KeyMaterial object. + /// Does not perform any operations on the actual key material, other than changing the key_type field. + /// + /// # 🚨 Hazardous Operation🚨 + /// Inside a [do_hazardous_operations] closure this will set the key to any [KeyType]. + /// Outside such a closure, only "safe" conversions are permitted: a [KeyType::CryptographicRandom] + /// key may be converted to any type, and any type may be converted to itself (a no-op). + /// A hazardous conversion attempted outside a [do_hazardous_operations] closure returns + /// [KeyMaterialError::HazardousOperationNotPermitted], and converting a [KeyType::Zeroized] key + /// returns [KeyMaterialError::ActingOnZeroizedKey]. fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError>; /// Security Strength, as used here, aligns with NIST SP 800-90A guidance for random number generation, @@ -116,55 +180,28 @@ pub trait KeyMaterialTrait { /// tracked independantly from key length and entropy level / key type. fn security_strength(&self) -> SecurityStrength; - /// Requires [KeyMaterialTrait::allow_hazardous_operations] to raise the security strength, but not to lower it. - /// Throws [KeyMaterialError::HazardousOperationNotPermitted] on a request to raise the security level without - /// [KeyMaterialTrait::allow_hazardous_operations] set. - /// Throws [KeyMaterialError::InvalidLength] on a request to set the security level higher than the current key length. - fn set_security_strength(&mut self, strength: SecurityStrength) - -> Result<(), KeyMaterialError>; - - /// Sets this instance to be able to perform potentially hazardous conversions such as - /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey, - /// or manually setting the key bytes via [KeyMaterialTrait::mut_ref_to_bytes], which then requires you to be responsible - /// for setting the key_len and key_type afterwards. + /// Set the [SecurityStrength] of the KeyMaterial. /// - /// The purpose of the hazardous_conversions guard is not to prevent the user from accessing their data, - /// but rather to make the developer think carefully about the operation they are about to perform, - /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants - /// further inspection. - fn allow_hazardous_operations(&mut self); - - /// Resets this instance to not be able to perform potentially hazardous conversions. - fn drop_hazardous_operations(&mut self); - - /// Sets the key_type of this KeyMaterial object. - /// Does not perform any operations on the actual key material, other than changing the key_type field. - /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise - /// checking is performed to ensure that the conversion is "safe". - /// This drops the allow_hazardous_operations flag, so if you need to do multiple hazardous conversions - /// on the same instance, then you'll need to call .allow_hazardous_operations() each time. - fn convert_key_type(&mut self, new_key_type: KeyType) -> Result<(), KeyMaterialError>; + /// # 🚨 Hazardous Operation🚨 + /// This function needs to be run within a [do_hazardous_operations] closure to raise the security + /// strength, but not to lower it. + /// + /// Outside of a [do_hazardous_operations] closure it will throw a + /// [KeyMaterialError::HazardousOperationNotPermitted] on a request to raise the security level, and + /// throw a [KeyMaterialError::InvalidLength] on a request to set the security level higher than the current key length. Inside a [do_hazardous_operations] it will do what you asked without complaining. + fn set_security_strength(&mut self, strength: SecurityStrength) + -> Result<(), KeyMaterialError>; + /// Whether or not the KeyMaterial is one of the full entropy key types. fn is_full_entropy(&self) -> bool; + /// Securely resets the contents to all zeroes. + /// Note that KeyMaterial will automatically zeroize itself when dropped, so it is not necessary + /// to call this method simply because the object is going out of scope, but it provided + /// in case you want to zeroize it early, or before re-using the same instance of KeyMaterial to + /// hold a different key, potentially of a different length. fn zeroize(&mut self); - /// Is simply an alias to [KeyMaterialTrait::set_key_len], however, this does not require [KeyMaterialTrait::allow_hazardous_operations] - /// since truncation is a safe operation. - /// If truncating below the current security strength, the security strength will be lowered accordingly. - fn truncate(&mut self, new_len: usize) -> Result<(), KeyMaterialError>; - - /// Adds the other KeyMaterial into this one, assuming there is space. - /// Does not require [KeyMaterialTrait::allow_hazardous_operations]. - /// Throws [KeyMaterialError::InvalidLength] if this object does not have enough space to add the other one. - /// The resulting [KeyType] and security strength will be the lesser of the two keys. - /// In other words, concatenating two 128-bit full entropy keys generated at a 128-bit DRBG security level - /// will result in a 256-bit full entropy key still at the 128-bit DRBG security level. - /// Concatenating a full entropy key with a low entropy key will result in a low entropy key. - /// - /// Returns the new key_len. - fn concatenate(&mut self, other: &dyn KeyMaterialTrait) -> Result; - /// Perform a constant-time comparison between the two key material buffers, /// ignoring differences in capacity, [KeyType], [SecurityStrength], etc. fn equals(&self, other: &dyn KeyMaterialTrait) -> bool; @@ -184,25 +221,54 @@ pub struct KeyMaterial { impl Secret for KeyMaterial {} +// The explicit `#[repr(u8)]` discriminants are the stable on-the-wire encoding used by +// `SerializableState` implementations (see the `TryFrom` impl below). Pin each value to its +// variant name: reordering variants is fine, but never reuse or renumber an existing discriminant, +// or previously-serialized states will be misread. #[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[repr(u8)] pub enum KeyType { /// The KeyMaterial is zeroized and MUST NOT be used for any cryptographic operation in this state. - Zeroized, + Zeroized = 0, - /// The KeyMaterial contains data of low or unknown entropy. - BytesLowEntropy, + /// The KeyMaterial contains non-zero data of unknown key type. + /// A KeyMaterial of key type Unknown will always have a [SecurityStrength] of [SecurityStrength::None]. + /// + /// This is the default KeyType for data loaded via [KeyMaterial::from_bytes]. + /// Promotion from Unknown to any other key type is considered to be a hazardous operation + /// and must be done within a [do_hazardous_operations] closure. + /// If you want to import key material directly into a known key type, use [KeyMaterial::from_bytes_as_type], + /// which does not require a hazardous operations closure. + Unknown = 1, - /// The KeyMaterial contains data of full entropy and can be safely converted to any other full-entropy key type. - BytesFullEntropy, + /// The KeyMaterial contains data of full entropy and can be safely converted to any other key type. + CryptographicRandom = 2, /// A seed for asymmetric private keys, RNGs, and other seed-based cryptographic objects. - Seed, + Seed = 3, /// A MAC key. - MACKey, + MACKey = 4, /// A key for a symmetric block or stream cipher. - SymmetricCipherKey, + SymmetricCipherKey = 5, +} + +impl TryFrom for KeyType { + type Error = SuspendableError; + + /// Inverse of `self as u8`; rejects unrecognized discriminants with [SuspendableError::InvalidData]. + fn try_from(value: u8) -> Result { + Ok(match value { + 0 => Self::Zeroized, + 1 => Self::Unknown, + 2 => Self::CryptographicRandom, + 3 => Self::Seed, + 4 => Self::MACKey, + 5 => Self::SymmetricCipherKey, + _ => return Err(SuspendableError::InvalidData), + }) + } } impl Default for KeyMaterial { @@ -226,23 +292,24 @@ impl KeyMaterial { /// Create a new instance of KeyMaterial containing random bytes from the provided random number generator. pub fn from_rng(rng: &mut impl RNG) -> Result { let mut key = Self::new(); - key.allow_hazardous_operations(); - rng.next_bytes_out(&mut key.mut_ref_to_bytes().unwrap()) - .map_err(|_| KeyMaterialError::GenericError("RNG failed."))?; + do_hazardous_operations(&mut key, |key| { + rng.next_bytes_out(&mut key.ref_to_bytes_mut().unwrap()) + .map_err(|_| KeyMaterialError::GenericError("RNG failed."))?; + Ok(()) + })?; key.key_len = KEY_LEN; - key.key_type = KeyType::BytesFullEntropy; + key.key_type = KeyType::CryptographicRandom; key.security_strength = rng.security_strength(); - key.drop_hazardous_operations(); Ok(key) } /// Constructor. - /// Loads the provided data into a new KeyMaterial of type [KeyType::BytesLowEntropy]. + /// Loads the provided data into a new KeyMaterial of type [KeyType::Unknown]. /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead. pub fn from_bytes(source: &[u8]) -> Result { - Self::from_bytes_as_type(source, KeyType::BytesLowEntropy) + Self::from_bytes_as_type(source, KeyType::Unknown) } /// Constructor. @@ -252,7 +319,7 @@ impl KeyMaterial { /// It will detect if you give it all-zero source data and set the key type to [KeyType::Zeroized] instead. /// /// Will set the [SecurityStrength] automatically according to the following rules: - /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None]. + /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::Unknown] then it will be [SecurityStrength::None]. /// * Otherwise it will set it based on the length of the provided source bytes. pub fn from_bytes_as_type(source: &[u8], key_type: KeyType) -> Result { let mut key_material = Self::default(); @@ -287,50 +354,12 @@ impl KeyMaterial { } impl KeyMaterialTrait for KeyMaterial { - /// Loads the provided data into a new KeyMaterial of the specified type. - /// This is discouraged unless the caller knows the provenance of the data, such as loading it - /// from a cryptographic private key file. - /// - /// This behaves differently on all-zero input key depending on whether [KeyMaterialTrait::allow_hazardous_operations] is set: - /// if not set, then it will succeed, setting the key type to [KeyType::Zeroized] and also return a [KeyMaterialError::ActingOnZeroizedKey] - /// to indicate that you may want to perform error-handling, which could be manually setting the key type - /// if you intend to allow zero keys, or do some other error-handling, like figure out why your RNG is broken. - /// Note that even if a [KeyMaterialError::ActingOnZeroizedKey] is returned, the object is still populated and usable. - /// For example, you could catch it like this: - /// ``` - /// use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait}; - /// use bouncycastle_core::key_material::KeyMaterial; - /// use bouncycastle_core::errors::KeyMaterialError; - /// - /// let key_bytes = [0u8; 16]; - /// let mut key = KeyMaterial256::new(); - /// let res = key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy); - /// match res { - /// Err(KeyMaterialError::ActingOnZeroizedKey) => { - /// // Either figure out why your passed an all-zero key, - /// // or set the key type manually, if that's what you intended. - /// key.allow_hazardous_operations(); - /// key.set_key_type(KeyType::BytesLowEntropy).unwrap(); // probably you should do something more elegant than .unwrap in your code ;) - /// key.drop_hazardous_operations(); - /// }, - /// Err(_) => { /* figure out what else went wrong */ }, - /// Ok(_) => { /* good */ }, - /// } - /// ``` - /// On the other hand, if [KeyMaterialTrait::allow_hazardous_operations] is set then it will just do what you asked without complaining. - /// - /// Since this zeroizes and resets the key material, this is considered a dangerous conversion. - /// - /// Will set the [SecurityStrength] automatically according to the following rules: - /// * If [KeyType] is [KeyType::Zeroized] or [KeyType::BytesLowEntropy] then it will be [SecurityStrength::None]. - /// * Otherwise it will set it based on the length of the provided source bytes. fn set_bytes_as_type( &mut self, source: &[u8], key_type: KeyType, ) -> Result<(), KeyMaterialError> { let allowed_hazardous_operations = self.allow_hazardous_operations; - self.allow_hazardous_operations(); if source.len() > KEY_LEN { return Err(KeyMaterialError::InputDataLongerThanKeyCapacity); @@ -346,12 +375,14 @@ impl KeyMaterialTrait for KeyMaterial { self.key_len = source.len(); self.key_type = new_key_type; - if new_key_type <= KeyType::BytesLowEntropy { - self.set_security_strength(SecurityStrength::None)?; - } else { - self.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?; - } - self.drop_hazardous_operations(); + do_hazardous_operations(self, |s| { + if new_key_type <= KeyType::Unknown { + s.set_security_strength(SecurityStrength::None)?; + } else { + s.set_security_strength(SecurityStrength::from_bits(source.len() * 8))?; + } + Ok(()) + })?; // return if new_key_type == KeyType::Zeroized { @@ -365,7 +396,7 @@ impl KeyMaterialTrait for KeyMaterial { &self.buf[..self.key_len] } - fn mut_ref_to_bytes(&mut self) -> Result<&mut [u8], KeyMaterialError> { + fn ref_to_bytes_mut(&mut self) -> Result<&mut [u8], KeyMaterialError> { if !self.allow_hazardous_operations { return Err(KeyMaterialError::HazardousOperationNotPermitted); } @@ -381,23 +412,79 @@ impl KeyMaterialTrait for KeyMaterial { } fn set_key_len(&mut self, key_len: usize) -> Result<(), KeyMaterialError> { - if !self.allow_hazardous_operations { - return Err(KeyMaterialError::HazardousOperationNotPermitted); - } if key_len > KEY_LEN { return Err(KeyMaterialError::InvalidLength); } - self.key_len = key_len; - Ok(()) + + // are we extending the key length, or truncating? + if key_len <= self.key_len { + // truncation is always allowed (not hazardous) + + self.security_strength = + min(&self.security_strength, &SecurityStrength::from_bits(key_len * 8)).clone(); + + if key_len == 0 { + self.key_type = KeyType::Zeroized; + } + + self.key_len = key_len; + + Ok(()) + } else { + if !self.allow_hazardous_operations { + return Err(KeyMaterialError::HazardousOperationNotPermitted); + } + self.key_len = key_len; + Ok(()) + } } fn key_type(&self) -> KeyType { self.key_type.clone() } fn set_key_type(&mut self, key_type: KeyType) -> Result<(), KeyMaterialError> { - if !self.allow_hazardous_operations { - return Err(KeyMaterialError::HazardousOperationNotPermitted); + if self.allow_hazardous_operations { + // just do it + self.key_type = key_type; + return Ok(()); + } + + match self.key_type { + KeyType::Zeroized => { + return Err(KeyMaterialError::ActingOnZeroizedKey); + } + KeyType::CryptographicRandom => { + // raw full entropy can be safely converted to anything. + self.key_type = key_type; + } + KeyType::Unknown => match key_type { + KeyType::Unknown => { /* No change */ } + _ => { + return Err(KeyMaterialError::HazardousOperationNotPermitted); + } + }, + KeyType::MACKey => match key_type { + KeyType::MACKey => { /* No change */ } + // Else: Once a KeyMaterial is typed, it should stay that way. + _ => { + return Err(KeyMaterialError::HazardousOperationNotPermitted); + } + }, + KeyType::SymmetricCipherKey => match key_type { + KeyType::SymmetricCipherKey => { /* No change */ } + // Else: Once a KeyMaterial is typed, it should stay that way. + _ => { + return Err(KeyMaterialError::HazardousOperationNotPermitted); + } + }, + KeyType::Seed => match key_type { + KeyType::Seed => { /* No change */ } + // Else: Once a KeyMaterial is typed, it should stay that way. + _ => { + return Err(KeyMaterialError::HazardousOperationNotPermitted); + } + }, } - self.key_type = key_type.clone(); + Ok(()) } fn security_strength(&self) -> SecurityStrength { @@ -412,7 +499,7 @@ impl KeyMaterialTrait for KeyMaterial { return Err(KeyMaterialError::HazardousOperationNotPermitted); }; - if self.key_type <= KeyType::BytesLowEntropy && strength > SecurityStrength::None { + if self.key_type <= KeyType::Unknown && strength > SecurityStrength::None { return Err(KeyMaterialError::SecurityStrength( "BytesLowEntropy keys cannot have a security strength other than None.", )); @@ -451,92 +538,15 @@ impl KeyMaterialTrait for KeyMaterial { } self.security_strength = strength; - self.drop_hazardous_operations(); - Ok(()) - } - /// Sets this instance to be able to perform potentially hazardous operations such as - /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey. - /// - /// The purpose of the hazardous operations guard is not to prevent the user from accessing their data, - /// but rather to make the developer think carefully about the operation they are about to perform, - /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants - /// further inspection. - fn allow_hazardous_operations(&mut self) { - self.allow_hazardous_operations = true; - } - /// Resets this instance to not be able to perform potentially hazardous operations. - fn drop_hazardous_operations(&mut self) { - self.allow_hazardous_operations = false; - } - /// Sets the key_type of this KeyMaterial object. - /// Does not perform any operations on the actual key material, other than changing the key_type field. - /// If allow_hazardous_operations is true, this method will allow conversion to any KeyType, otherwise - /// checking is performed to ensure that the conversion is "safe". - /// This drops the allow_hazardous_operations flag, so if you need to do multiple hazardous operations - /// on the same instance, then you'll need to call .allow_hazardous_operations() each time. - fn convert_key_type(&mut self, new_key_type: KeyType) -> Result<(), KeyMaterialError> { - if self.allow_hazardous_operations { - // just do it - self.key_type = new_key_type; - return Ok(()); - } - - match self.key_type { - KeyType::Zeroized => { - return Err(KeyMaterialError::ActingOnZeroizedKey); - } - KeyType::BytesFullEntropy => { - // raw full entropy can be safely converted to anything. - self.key_type = new_key_type; - } - KeyType::BytesLowEntropy => { - match new_key_type { - KeyType::BytesLowEntropy => { /* No change */ } - _ => { - return Err(KeyMaterialError::HazardousOperationNotPermitted); - } - } - } - KeyType::MACKey => { - match new_key_type { - KeyType::MACKey => { /* No change */ } - // Else: Once a KeyMaterial is typed, it should stay that way. - _ => { - return Err(KeyMaterialError::HazardousOperationNotPermitted); - } - } - } - KeyType::SymmetricCipherKey => { - match new_key_type { - KeyType::SymmetricCipherKey => { /* No change */ } - // Else: Once a KeyMaterial is typed, it should stay that way. - _ => { - return Err(KeyMaterialError::HazardousOperationNotPermitted); - } - } - } - KeyType::Seed => { - match new_key_type { - KeyType::Seed => { /* No change */ } - // Else: Once a KeyMaterial is typed, it should stay that way. - _ => { - return Err(KeyMaterialError::HazardousOperationNotPermitted); - } - } - } - } - - // each call to allow_hazardous_operations() is only good for one conversion. - self.drop_hazardous_operations(); Ok(()) } fn is_full_entropy(&self) -> bool { match self.key_type { - KeyType::BytesFullEntropy + KeyType::CryptographicRandom | KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => true, - KeyType::Zeroized | KeyType::BytesLowEntropy => false, + KeyType::Zeroized | KeyType::Unknown => false, } } @@ -546,34 +556,6 @@ impl KeyMaterialTrait for KeyMaterial { self.key_type = KeyType::Zeroized; } - fn truncate(&mut self, new_len: usize) -> Result<(), KeyMaterialError> { - if new_len > self.key_len { - return Err(KeyMaterialError::InvalidLength); - } - - self.security_strength = - min(&self.security_strength, &SecurityStrength::from_bits(new_len * 8)).clone(); - - if new_len == 0 { - self.key_type = KeyType::Zeroized; - } - - self.key_len = new_len; - Ok(()) - } - - fn concatenate(&mut self, other: &dyn KeyMaterialTrait) -> Result { - let new_key_len = self.key_len() + other.key_len(); - if self.key_len() + other.key_len() > KEY_LEN { - return Err(KeyMaterialError::InputDataLongerThanKeyCapacity); - } - self.buf[self.key_len..new_key_len].copy_from_slice(other.ref_to_bytes()); - self.key_len += other.key_len(); - self.key_type = max(&self.key_type, &other.key_type()).clone(); - self.security_strength = max(&self.security_strength, &other.security_strength()).clone(); - Ok(self.key_len()) - } - fn equals(&self, other: &dyn KeyMaterialTrait) -> bool { if self.key_len() != other.key_len() { return false; @@ -584,7 +566,7 @@ impl KeyMaterialTrait for KeyMaterial { /// Checks for equality of the key data (using a constant-time comparison), but does not check that /// the two keys have the same type. -/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::BytesLowEntropy] and +/// Therefore, for example, two keys loaded from the same bytes, one with type [KeyType::Unknown] and /// the other with [KeyType::MACKey] will be considered equal. impl PartialEq for KeyMaterial { fn eq(&self, other: &Self) -> bool { @@ -605,18 +587,18 @@ impl PartialOrd for KeyType { KeyType::Zeroized => Some(Ordering::Equal), _ => Some(Ordering::Less), }, - KeyType::BytesLowEntropy => match other { + KeyType::Unknown => match other { KeyType::Zeroized => Some(Ordering::Greater), - KeyType::BytesLowEntropy => Some(Ordering::Equal), + KeyType::Unknown => Some(Ordering::Equal), _ => Some(Ordering::Less), }, - KeyType::BytesFullEntropy => match other { - KeyType::Zeroized | KeyType::BytesLowEntropy => Some(Ordering::Greater), - KeyType::BytesFullEntropy => Some(Ordering::Equal), + KeyType::CryptographicRandom => match other { + KeyType::Zeroized | KeyType::Unknown => Some(Ordering::Greater), + KeyType::CryptographicRandom => Some(Ordering::Equal), _ => Some(Ordering::Less), }, KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => match other { - KeyType::Zeroized | KeyType::BytesLowEntropy | KeyType::BytesFullEntropy => { + KeyType::Zeroized | KeyType::Unknown | KeyType::CryptographicRandom => { Some(Ordering::Greater) } KeyType::Seed | KeyType::MACKey | KeyType::SymmetricCipherKey => { @@ -655,3 +637,149 @@ impl Drop for KeyMaterial { self.zeroize() } } + +/* Hazardous Operations Runner */ + +/// Internal-use trait holding the low-level hazardous-operations guard toggle. +/// +/// These methods are deliberately split out of [KeyMaterialTrait] into a private trait so that +/// they are not accessible from outside this module. +/// +/// This is a supertrait of [KeyMaterialTrait], so anything that implements [KeyMaterialTrait] +/// also implements this. [KeyMaterialTrait] therefore stays dyn-compatible (both methods here are +/// object-safe), which matters because `Box` is used widely as a return type. +trait KeyMaterialInternalTrait { + /// Whether this instance is currently allowed to perform potentially hazardous operations. + fn allows_hazardous_operations(&self) -> bool; + /// Sets this instance to be able to perform potentially hazardous operations such as + /// casting a KeyMaterial of type RawUnknownEntropy or RawLowEntropy into RawFullEntropy or SymmetricCipherKey, + /// or manually setting the key bytes via [KeyMaterialTrait::mut_ref_to_bytes], which then requires you to be responsible + /// for setting the key_len and key_type afterwards. + /// + /// The purpose of the hazardous operations guard is not to prevent the user from accessing their data, + /// but rather to make the developer think carefully about the operation they are about to perform, + /// and to give static analysis tools an obvious marker that a given KeyMaterial variable warrants + /// further inspection. + /// + /// Prefer the scoped [KeyMaterial::do_hazardous_operations] wrapper, which calls this and + /// [KeyMaterialInternalTrait::drop_hazardous_operations] for you so the guard can't be left set. + fn allow_hazardous_operations(&mut self); + + /// Resets this instance to not be able to perform potentially hazardous operations. + fn drop_hazardous_operations(&mut self); +} + +impl KeyMaterialInternalTrait for KeyMaterial { + fn allows_hazardous_operations(&self) -> bool { + self.allow_hazardous_operations + } + fn allow_hazardous_operations(&mut self) { + self.allow_hazardous_operations = true; + } + fn drop_hazardous_operations(&mut self) { + self.allow_hazardous_operations = false; + } +} + +/// Runs the provided closure within which hazardous operations are allowed. +/// All hazardous operations will return a [KeyMaterialError::HazardousOperationNotPermitted] +/// if used outside of this closure. +/// +/// Example usage: +/// +/// ```rust +/// use bouncycastle_core::key_material::{KeyType, KeyMaterial256, KeyMaterialTrait, do_hazardous_operations}; +/// use bouncycastle_core::traits::SecurityStrength; +/// +/// // Let's create an all-zero key +/// let mut key = KeyMaterial256::default(); +/// +/// // Let's set a key of all zeroes, which the library would normally force to be +/// // [KeyType::Zeroized], but we want to force it to [KeyType::Seed], which is considered a +/// // hazardous operation. +/// do_hazardous_operations(&mut key, |key| { +/// key.set_bytes_as_type(&[8u8; 32], KeyType::Seed) +/// // note that the closure is required to return Result<(), KeyMaterialError>, +/// // so we can chain [KeyMaterial::set_bytes_as_type], otherwise we would need +/// // to end with Ok(()). +/// }).unwrap(); +/// +/// assert_eq!(key.key_len(), 32); +/// assert_eq!(key.key_type(), KeyType::Seed); +/// ``` +/// +/// ```rust +/// use bouncycastle_core::key_material::{KeyType, KeyMaterial256, KeyMaterialTrait, do_hazardous_operations}; +/// use bouncycastle_core::traits::SecurityStrength; +/// +/// // Let's create an all-zero key +/// let mut key = KeyMaterial256::default(); +/// assert_eq!(key.key_type(), KeyType::Zeroized); +/// assert_eq!(key.security_strength(), SecurityStrength::None); +/// +/// // Now we want to tell the library that this all-zero key +/// // is to be used as a 32-byte [KeyType::Seed] at the 256-bit security strength, +/// // which the library will not allow you to do outside of the hazerdous operations closure. +/// do_hazardous_operations(&mut key, |key| { +/// key.set_key_len(32)?; +/// key.set_key_type(KeyType::Seed)?; +/// key.set_security_strength(SecurityStrength::_256bit)?; +/// Ok(()) +/// }).unwrap(); +/// +/// assert_eq!(key.key_type(), KeyType::Seed); +/// assert_eq!(key.security_strength(), SecurityStrength::_256bit); +/// ``` +/// +/// Another common usage of hazardous operations is to get a direct mutable reference to the +/// underlying KeyMaterial byte buffer; for example if you want to copy in key bytes from somewhere else. +/// +/// ```rust +/// use bouncycastle_core::key_material::{KeyType, KeyMaterial512, KeyMaterialTrait, do_hazardous_operations}; +/// use bouncycastle_core::traits::SecurityStrength; +/// +/// // In this example, we initialize a KeyMateriol512 (64 bytes) with only 32 bytes of input. +/// let mut key = KeyMaterial512::from_bytes_as_type( +/// &[1u8; 32], +/// KeyType::CryptographicRandom +/// ).unwrap(); +/// assert_eq!(key.key_len(), 32); +/// +/// // Now we want to expand the length to 64 bytes and copy in an additional 32 bytes of key data, +/// // using [KeyMaterial::mut_ref_to_bytes]. +/// let additional_bytes = [2u8; 32]; +/// do_hazardous_operations(&mut key, |key| { +/// key.set_key_len(64)?; +/// key.ref_to_bytes_mut()?[32..].copy_from_slice(&additional_bytes); +/// Ok(()) +/// }).unwrap(); +/// +/// assert_eq!(key.key_len(), 64); +/// // Reading the key bytes via [KeyMateriol::ref_to_bytes] is not a hazardous operation. +/// assert_eq!(key.ref_to_bytes()[..32], [1u8; 32]); +/// assert_eq!(key.ref_to_bytes()[32..], [2u8; 32]); +/// ``` +/// +// Dev note: This is a free function rather than a method on [KeyMaterialTrait] because it is +// generic over the closure type, which would make the trait non-dyn-compatible; the trait is used +// as `&dyn KeyMaterialTrait` elsewhere (e.g. [KeyMaterialTrait::concatenate], [KeyMaterialTrait::equals]). +// The toggle itself lives on the module-private [KeyMaterialInternalTrait], so external crates cannot +// flip the guard by hand and must go through this scoped wrapper (hence `#[allow(private_bounds)]`). +#[allow(private_bounds)] +pub fn do_hazardous_operations(key: &mut KEY, f: F) -> Result<(), KeyMaterialError> +where + KEY: KeyMaterialTrait + ?Sized, + F: FnOnce(&mut KEY) -> Result<(), KeyMaterialError>, +{ + let allows = key.allows_hazardous_operations(); + + key.allow_hazardous_operations(); + let ret = f(key); + + // to allow nested closures, if this key instance allowed + // before entering, then leave it. + if !allows { + key.drop_hazardous_operations(); + } + ret +} diff --git a/crypto/core/src/lib.rs b/crypto/core/src/lib.rs index cb0d8d8..df19778 100644 --- a/crypto/core/src/lib.rs +++ b/crypto/core/src/lib.rs @@ -1,11 +1,15 @@ //! This crate defines the core traits and types used by the rest of the bc-rust.test library. -// todo -- this is the goal, but first need to remove all the Vec in favour of compile-time array sizing. -// #![no_std] - -#![feature(adt_const_params)] +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] +// The `Vec`/`Box`-returning convenience APIs live behind the (default-on) `alloc` feature. +// When it is enabled we pull in the `alloc` crate; `no_std` users who disable it get the +// allocation-free `*_out(&mut [u8])` and `*_array::()` APIs only. +#[cfg(feature = "alloc")] +extern crate alloc; + pub mod errors; pub mod key_material; +pub mod suspendable_state; pub mod traits; diff --git a/crypto/core/src/suspendable_state.rs b/crypto/core/src/suspendable_state.rs new file mode 100644 index 0000000..a668676 --- /dev/null +++ b/crypto/core/src/suspendable_state.rs @@ -0,0 +1,118 @@ +//! Helper functions for standardizing serialization and deserialization of stateful objects. + +use crate::errors::SuspendableError; + +/// A semantic library version, ordered by `major`, then `minor`, then `patch`. +/// +/// The field declaration order matters: the derived [`Ord`]/[`PartialOrd`] compare fields +/// lexicographically in declaration order, which is exactly semantic-version precedence. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct SemVer { + pub major: u8, + pub minor: u8, + pub patch: u8, + // A semantic version can often also take a suffix, e.g. "alpha", "beta", "rc1", etc. + // We're not going to model that here because it's not useful for versioning serialized states. +} + +impl From<[u8; 3]> for SemVer { + fn from(v: [u8; 3]) -> Self { + SemVer { major: v[0], minor: v[1], patch: v[2] } + } +} + +impl From for [u8; 3] { + fn from(v: SemVer) -> Self { + [v.major, v.minor, v.patch] + } +} + +/// Parse a decimal ASCII string (a Cargo version component) into a u8 at compile time. +const fn parse_version_component(s: &str) -> u8 { + let bytes = s.as_bytes(); + let mut result: u8 = 0; + let mut i = 0; + while i < bytes.len() { + let d = bytes[i]; + assert!(d >= b'0' && d <= b'9', "version component must be numeric"); + // A component > 255 overflows u8 and fails the build (SemVer fields are u8 by design). + result = result * 10 + (d - b'0'); + i += 1; + } + result +} + +/// The current library version -- ie the version of the *bouncycastle-core* crate -- at compile time (via Cargo's +/// `CARGO_PKG_VERSION_*` env vars). +pub const LIB_VERSION: SemVer = SemVer { + major: parse_version_component(env!("CARGO_PKG_VERSION_MAJOR")), + minor: parse_version_component(env!("CARGO_PKG_VERSION_MINOR")), + patch: parse_version_component(env!("CARGO_PKG_VERSION_PATCH")), +}; + +#[test] +fn test_cmp_lib_ver() { + use core::cmp::Ordering; + + assert!([0, 0, 0] < [0, 0, 1]); + + let cmp = |a: [u8; 3], b: [u8; 3]| SemVer::from(a).cmp(&SemVer::from(b)); + assert_eq!(cmp([0, 2, 1], [1, 1, 1]), Ordering::Less); + assert_eq!(cmp([2, 1, 1], [1, 1, 1]), Ordering::Greater); + assert_eq!(cmp([1, 0, 2], [1, 1, 1]), Ordering::Less); + assert_eq!(cmp([1, 2, 0], [1, 1, 1]), Ordering::Greater); + assert_eq!(cmp([1, 1, 0], [1, 1, 1]), Ordering::Less); + assert_eq!(cmp([1, 1, 2], [1, 1, 1]), Ordering::Greater); + assert_eq!(cmp([1, 1, 1], [1, 1, 1]), Ordering::Equal); +} + +/// Puts the library version into the first three bytes of the state array. +/// +/// Hands back a slice to the same array, starting after the version tag. +pub fn add_lib_ver(state: &mut [u8; SERIALIZED_LEN]) -> &mut [u8] { + state[..3].copy_from_slice(&<[u8; 3]>::from(LIB_VERSION)); + &mut state[3..] +} + +/// A helper for deserializing an object's state +/// +/// The state_out array must have length at least SERIALIZED_LEN - 3. +/// +/// Returns the number of bytes written to state_out, or a [SuspendableError::IncompatibleVersion] if +/// the version of the serialized state is earlier than the specified `not_before` version, or +/// is a future MAJOR or MINOR version (but future PATCH versions are ok). +/// +/// Note that for testability, this will always reject if the serialized state contains a version tag +/// of `[0,0,0]`. +/// +/// Hands back a slice to the same array, starting after the version tag. +pub fn check_lib_ver( + state: &[u8; SERIALIZED_LEN], + not_before: Option<[u8; 3]>, +) -> Result<&[u8], SuspendableError> { + // the .unwrap is infallible after the guard check + if state.len() < 3 { + return Err(SuspendableError::InvalidData); + } + let ver_bytes: [u8; 3] = state[..3].try_into().unwrap(); + let ver = SemVer::from(ver_bytes); + + let not_before = SemVer::from(not_before.unwrap_or([0, 0, 0])); + + if ver < not_before { + return Err(SuspendableError::IncompatibleVersion); + }; + // Nothing is ever compatible with [0,0,0] + if ver == SemVer::from([0, 0, 0]) { + return Err(SuspendableError::IncompatibleVersion); + }; + + // Check if state was produced by a later MAJOR or MINOR version; + // a future version on the same patch stream is ok (if not, then we've broken the rules of semantic versioning); + let patch_stream = SemVer::from([LIB_VERSION.major, LIB_VERSION.minor, 255]); + if ver > patch_stream { + return Err(SuspendableError::IncompatibleVersion); + } + + Ok(&state[3..]) +} diff --git a/crypto/core/src/traits.rs b/crypto/core/src/traits.rs index 27dd844..606df88 100644 --- a/crypto/core/src/traits.rs +++ b/crypto/core/src/traits.rs @@ -1,9 +1,17 @@ //! Provides simplified abstracted APIs over classes of cryptigraphic primitives, such as Hash, KDF, etc. -use core::marker::Sized; -use core::fmt::{Debug, Display}; -use crate::errors::{HashError, KDFError, KEMError, MACError, RNGError, SignatureError}; +use crate::errors::*; use crate::key_material::KeyMaterialTrait; +use core::fmt::{Debug, Display}; +use core::marker::Sized; + +// `Vec`/`Box` come from the `alloc` crate under `#![no_std]` and are gated behind the (default-on) +// `alloc` feature. When the feature is disabled, use the allocation-free alternatives: the +// `*_out(&mut [u8])` fill methods, or the `*_array::()` methods that return a fixed-size array. +#[cfg(feature = "alloc")] +use alloc::boxed::Box; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; // Imports needed for docs #[allow(unused_imports)] @@ -17,7 +25,7 @@ pub trait Algorithm { const MAX_SECURITY_STRENGTH: SecurityStrength; } -pub trait Hash : Default { +pub trait Hash: Algorithm + Default { /// The size of the internal block in bits -- needed by functions such as HMAC to compute security parameters. fn block_bitlen(&self) -> usize; @@ -26,45 +34,97 @@ pub trait Hash : Default { /// A static one-shot API that hashes the provided data. /// `data` can be of any length, including zero bytes. + #[cfg(feature = "alloc")] fn hash(self, data: &[u8]) -> Vec; + /// A static one-shot, `no_std`-friendly API that hashes the provided data and returns the digest + /// in a caller-sized array. This is the allocation-free counterpart to [Hash::hash]. + /// + /// `N` should equal [Hash::output_len]; the same truncation / zero-padding rules as + /// [Hash::hash_out] apply if `N` differs from the output length. + fn hash_array(self, data: &[u8]) -> [u8; N] + where + Self: Sized, + { + let mut output = [0u8; N]; + let _ = self.hash_out(data, &mut output); + output + } + /// A static one-shot API that hashes the provided data into the provided output slice. /// `data` can be of any length, including zero bytes. + /// The entire output buffer is zeroized before the hash output is written. /// The return value is the number of bytes written. fn hash_out(self, data: &[u8], output: &mut [u8]) -> usize; /// Provide a chunk of data to be absorbed into the hashes. /// `data` can be of any length, including zero bytes. /// do_update() is intended to be used as part of a streaming interface, and so may by called multiple times. - // fn do_update(&mut self, data: &[u8]) -> Result<(), HashError>; fn do_update(&mut self, data: &[u8]); /// Finish absorbing input and produce the hashes output. /// Consumes self, so this must be the final call to this object. - // fn do_final(self) -> Result, HashError>; + #[cfg(feature = "alloc")] fn do_final(self) -> Vec; + /// Finish absorbing input and produce the hashes output in a caller-sized array. + /// This is the allocation-free, `no_std`-friendly counterpart to [Hash::do_final]. + /// Consumes self, so this must be the final call to this object. + /// + /// `N` should equal [Hash::output_len]; the same truncation / zero-padding rules as + /// [Hash::do_final_out] apply if `N` differs from the output length. + fn do_final_array(self) -> [u8; N] + where + Self: Sized, + { + let mut output = [0u8; N]; + let _ = self.do_final_out(&mut output); + output + } + /// Finish absorbing input and produce the hashes output. /// Consumes self, so this must be the final call to this object. /// /// If the provided buffer is smaller than the hash's output length, the output will be truncated. /// If the provided buffor is larger than the hash's output length, the output will be placed in /// the first [Hash::output_len] bytes. + /// The entire output buffer is zeroized before the hash output is written, so any bytes past + /// [Hash::output_len] will be 0. /// /// The return value is the number of bytes written. fn do_final_out(self, output: &mut [u8]) -> usize; /// The same as [Hash::do_final], but allows for supplying a partial byte as the last input. /// Assumes that the input is in the least significant bits (big endian). + #[cfg(feature = "alloc")] fn do_final_partial_bits( self, partial_byte: u8, num_partial_bits: usize, ) -> Result, HashError>; + /// The same as [Hash::do_final_partial_bits], but returns the output in a caller-sized array. + /// This is the allocation-free, `no_std`-friendly counterpart. + /// + /// `N` should equal [Hash::output_len]; the same truncation / zero-padding rules as + /// [Hash::do_final_partial_bits_out] apply if `N` differs from the output length. + fn do_final_partial_bits_array( + self, + partial_byte: u8, + num_partial_bits: usize, + ) -> Result<[u8; N], HashError> + where + Self: Sized, + { + let mut output = [0u8; N]; + self.do_final_partial_bits_out(partial_byte, num_partial_bits, &mut output)?; + Ok(output) + } + /// The same as [Hash::do_final_out], but allows for supplying a partial byte as the last input. /// Assumes that the input is in the least significant bits (big endian). /// will be placed in the first [Hash::output_len] bytes. + /// The entire output buffer is zeroized before the hash output is written. /// The return value is the number of bytes written. fn do_final_partial_bits_out( self, @@ -84,7 +144,7 @@ pub trait HashAlgParams: Algorithm { /// A Key Derivation Function (KDF) is a function that takes in one or more input key and some unstructured /// additional input, and uses them to produces a derived key. -pub trait KDF : Default { +pub trait KDF: Default { /// Implementations of this function are capable of deriving an output key from an input key, /// assuming that they have been properly initialized. /// @@ -94,14 +154,14 @@ pub trait KDF : Default { /// /// ex.: /// - /// * [KeyType::BytesLowEntropy] -> [KeyType::BytesLowEntropy]) - /// * [KeyType::BytesFullEntropy] -> [KeyType::BytesFullEntropy]) + /// * [KeyType::Unknown] -> [KeyType::Unknown]) + /// * [KeyType::CryptographicRandom] -> [KeyType::CryptographicRandom]) /// * [KeyType::SymmetricCipherKey] -> [KeyType::SymmetricCipherKey]) /// - /// If provided with an input key, even if it is [KeyType::BytesFullEntropy], but that + /// If provided with an input key, even if it is [KeyType::CryptographicRandom], but that /// contains less key material than the internal block size of the KDF, then the KDF /// will not be considered properly seeded, and the output [KeyMaterial] will be set to - /// [KeyType::BytesLowEntropy] -- for example, seeding SHA3-256 with a [KeyMaterial] containing + /// [KeyType::Unknown] -- for example, seeding SHA3-256 with a [KeyMaterial] containing /// only 128 bits of key material. /// /// An implement can, and in most cases SHOULD, return a [HashError] if provided @@ -115,6 +175,9 @@ pub trait KDF : Default { /// /// Output length: this function will create a KeyMaterial populated with the default output length /// of the underlying hash primitive. + /// + /// `no_std` alternative: use [KDF::derive_key_out] to fill a caller-provided [KeyMaterial]. + #[cfg(feature = "alloc")] fn derive_key( self, key: &impl KeyMaterialTrait, @@ -125,8 +188,8 @@ pub trait KDF : Default { /// /// Output length: this function will behave differently depending on the underlying hash primitive; /// some, such as SHA2 or SHA3 will produce a fixed-length output, while others, such as SHAKE or HKDF, - /// will fill the provided KeyMaterial to capacity and require you to truncate it afterwards - /// using [KeyMaterialTrait::truncate]. + /// will fill the provided KeyMaterial to capacity and require you to truncate it afterward + /// using [KeyMaterialTrait::set_key_len]. fn derive_key_out( self, key: &impl KeyMaterialTrait, @@ -148,13 +211,16 @@ pub trait KDF : Default { /// /// Implementations can, and in most cases SHOULD, return a [KeyMaterial] of the same type as the /// strongest key, and SHOULD throw a [HashError] if all input keys are zeroized. - /// For example output a [KeyType::BytesFullEntropy] key whenever any one of - /// the input keys is a [KeyType::BytesFullEntropy] key. - /// As another example, combining a [KeyType::BytesLowEntropy] key with a [KeyType::MACKey] key + /// For example output a [KeyType::CryptographicRandom] key whenever any one of + /// the input keys is a [KeyType::CryptographicRandom] key. + /// As another example, combining a [KeyType::Unknown] key with a [KeyType::MACKey] key /// should return a [KeyType::MACKey]. /// /// Output length: this function will create a KeyMaterial populated with the default output length /// of the underlying hash primitive. + /// + /// `no_std` alternative: use [KDF::derive_key_from_multiple_out] to fill a caller-provided [KeyMaterial]. + #[cfg(feature = "alloc")] fn derive_key_from_multiple( self, keys: &[&impl KeyMaterialTrait], @@ -165,8 +231,8 @@ pub trait KDF : Default { /// /// Output length: this function will behave differently depending on the underlying hash primitive; /// some, such as SHA2 or SHA3 will produce a fixed-length output, while others, such as SHAKE or HKDF, - /// will fill the provided KeyMaterial to capacity and require you to truncate it afterwards - /// by using [KeyMaterialTrait::truncate]. + /// will fill the provided KeyMaterial to capacity and require you to truncate it afterward + /// by using [KeyMaterialTrait::set_key_len]. fn derive_key_from_multiple_out( self, keys: &[&impl KeyMaterialTrait], @@ -178,23 +244,65 @@ pub trait KDF : Default { fn max_security_strength(&self) -> SecurityStrength; } -/// A Key Encapsulation Mechanism -pub trait KEM< +/// A Key Encapsulation Mechanism (KEM) is defined as a set of three operations: +/// key generation, encapsulation, and decapsulation. +/// +/// This trait represents the encapsulation operation performed by the holder of the public key. +/// Decapsulation operations are performed by the corresponding [KEMDecapsulator] trait, and key +/// generation is provided as an inherent associated function directly on the algorithm struct. +/// There are several reasons for this split: first is architectural; some complex algorithms may +/// benefit from having the encapsulation and decapsulation implementations split into separate modules. +/// Second is for compliance: sometimes a policy soft-deprecates an algorithm so that new ciphertexts +/// can no longer be created, but existing ciphertexts can still be decapsulated. Splitting the traits +/// makes this policy easier to enforce. +/// +/// The arrays used to encode public keys, ciphertexts, and shared secrets are statically-sized +/// because this allows us to safely remove runtime checks for array lengths, which overall reduces +/// the fallibility of the library. This design choice could make this trait complicated to apply +/// to a KEM algorithm that does not have fixed sizes for the encodings of these objects. +pub trait KEMEncapsulator< PK: KEMPublicKey, - SK: KEMPrivateKey, const PK_LEN: usize, - const SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, ->: Sized { - /// Generate a keypair. - /// Error condition: Basically only on RNG failures - fn keygen() -> Result<(PK, SK), KEMError>; - +>: Sized +{ /// Performs an encapsulation against the given public key. + /// Sources randomness from the library's default OS-backed RNG. /// Returns the ciphertext and derived shared secret. fn encaps(pk: &PK) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError>; + /// Performs an encapsulation against the given public key. + /// Sources randomness from the provided RNG. + /// Returns the ciphertext and derived shared secret. + fn encaps_rng( + pk: &PK, + rng: &mut dyn RNG, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError>; +} +/// A Key Encapsulation Mechanism (KEM) is defined as a set of three operations: +/// key generation, encapsulation, and decapsulation. +/// +/// This trait represents the decapsulation operation performed by the holder of the private key. +/// Encapsulation operations are performed by the corresponding [KEMEncapsulator] trait, and key +/// generation is provided as an inherent associated function directly on the algorithm struct. +/// There are several reasons for this split: first is architectural; some complex algorithms may +/// benefit from having the encapsulation and decapsulation implementations split into separate modules. +/// Second is for compliance: sometimes a policy soft-deprecates an algorithm so that new ciphertexts +/// can no longer be created, but existing ciphertexts can still be decapsulated. Splitting the traits +/// makes this policy easier to enforce. +/// +/// The arrays used to encode private keys, ciphertexts, and shared secrets are statically-sized +/// because this allows us to safely remove runtime checks for array lengths, which overall reduces +/// the fallibility of the library. This design choice could make this trait complicated to apply +/// to a KEM algorithm that does not have fixed sizes for the encodings of these objects. +pub trait KEMDecapsulator< + SK: KEMPrivateKey, + const SK_LEN: usize, + const CT_LEN: usize, + const SS_LEN: usize, +>: Sized +{ /// Performs a decapsulation of the given ciphertext. /// Returns the derived shared secret. fn decaps(sk: &SK, ct: &[u8]) -> Result, KEMError>; @@ -204,26 +312,29 @@ pub trait KEM< // todo: that automatically call the encode and from_bytes() ? /// A public key for a KEM algorithm, often denoted "pk". -pub trait KEMPublicKey : PartialEq + Eq + Clone + Debug + Display + Sized { +pub trait KEMPublicKey: + PartialEq + Eq + Clone + Debug + Display + Sized +{ /// Write it out to bytes in its standard encoding. fn encode(&self) -> [u8; PK_LEN]; /// Write it out to bytes in its standard encoding. + /// The entire output buffer is zeroized before the encoding is written. fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize; /// Read it in from bytes in its standard encoding. fn from_bytes(bytes: &[u8]) -> Result; } /// A private key for a KEM algorithm, often denoted "sk" (for "secret key"). -pub trait KEMPrivateKey : PartialEq + Eq + Clone + Secret + Sized { +pub trait KEMPrivateKey: PartialEq + Eq + Clone + Secret + Sized { /// Write it out to bytes in its standard encoding. fn encode(&self) -> [u8; SK_LEN]; /// Write it out to bytes in its standard encoding. + /// The entire output buffer is zeroized before the encoding is written. fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize; /// Read it in from bytes in its standard encoding. fn from_bytes(bytes: &[u8]) -> Result; } - /// A Message Authentication Code algorithm is a keyed hash function that behaves somewhat like a symmetric signature function. /// A MAC algorithm takes in a key and some data, and produces a MAC (message authentication code) that /// can be used to verify the integrity of data. @@ -285,15 +396,32 @@ pub trait MAC: Sized { /// ```text /// MACError::KeyMaterialError(KeyMaterialError::SecurityStrength("HMAC::init(): provided key has a lower security strength than the instantiated HMAC") /// ``` + #[cfg(feature = "alloc")] fn mac(self, data: &[u8]) -> Vec; + /// One-shot, `no_std`-friendly API that computes a MAC for the provided data and returns it in a + /// caller-sized array. This is the allocation-free counterpart to [MAC::mac]. + /// + /// `N` should equal [MAC::output_len]; the same rules as [MAC::mac_out] apply (including the + /// possible [MACError::InvalidLength] for undersized buffers). + fn mac_array(self, data: &[u8]) -> Result<[u8; N], MACError> + where + Self: Sized, + { + let mut out = [0u8; N]; + self.mac_out(data, &mut out)?; + Ok(out) + } + /// One-shot API that computes a MAC for the provided data and writes it into the provided output slice. /// `data` can be of any length, including zero bytes. /// /// Depending on the underlying MAC implementation, NIST may require that the library enforce /// a minimum length on the mac output value. See documentation for the underlying implementation /// to see conditions under which it throws [MACError::InvalidLength]. - fn mac_out(self, data: &[u8],out: &mut [u8]) -> Result; + /// + /// The entire output buffer is zeroized before the MAC value is written. + fn mac_out(self, data: &[u8], out: &mut [u8]) -> Result; /// One-shot API that verifies a MAC for the provided data. /// `data` can be of any length, including zero bytes. @@ -313,11 +441,28 @@ pub trait MAC: Sized { /// do_update() is intended to be used as part of a streaming interface, and so may by called multiple times. fn do_update(&mut self, data: &[u8]); + #[cfg(feature = "alloc")] fn do_final(self) -> Vec; + /// The allocation-free, `no_std`-friendly counterpart to [MAC::do_final]: returns the MAC value + /// in a caller-sized array. Consumes self, so this must be the final call to this object. + /// + /// `N` should equal [MAC::output_len]; the same rules as [MAC::do_final_out] apply (including the + /// possible [MACError::InvalidLength] for undersized buffers). + fn do_final_array(self) -> Result<[u8; N], MACError> + where + Self: Sized, + { + let mut out = [0u8; N]; + self.do_final_out(&mut out)?; + Ok(out) + } + /// Depending on the underlying MAC implementation, NIST may require that the library enforce /// a minimum length on the mac output value. See documentation for the underlying implementation /// to see conditions under which it throws [MACError::InvalidLength]. + /// + /// The entire output buffer is zeroized before the MAC value is written. fn do_final_out(self, out: &mut [u8]) -> Result; /// Internally, this will re-compute the MAC value and then compare it to the provided mac value @@ -334,13 +479,32 @@ pub trait MAC: Sized { fn max_security_strength(&self) -> SecurityStrength; } -#[derive(Eq, PartialEq, PartialOrd, Clone, Debug)] +// The explicit `#[repr(u8)]` discriminants are the stable on-the-wire encoding used by +// `SerializableState` implementations (see the `TryFrom` impl below). +#[derive(Eq, PartialEq, PartialOrd, Clone, Copy, Debug)] +#[repr(u8)] pub enum SecurityStrength { - None, - _112bit, - _128bit, - _192bit, - _256bit, + None = 0, + _112bit = 1, + _128bit = 2, + _192bit = 3, + _256bit = 4, +} + +impl TryFrom for SecurityStrength { + type Error = SuspendableError; + + /// Inverse of `self as u8`; rejects unrecognized discriminants with [SuspendableError::InvalidData]. + fn try_from(value: u8) -> Result { + Ok(match value { + 0 => Self::None, + 1 => Self::_112bit, + 2 => Self::_128bit, + 3 => Self::_192bit, + 4 => Self::_256bit, + _ => return Err(SuspendableError::InvalidData), + }) + } } impl SecurityStrength { @@ -381,20 +545,44 @@ impl SecurityStrength { /// be used by applications that intend to submit to FIPS certification as it more closely aligns with the /// requirements of SP 800-90A. /// Note: this interface produces bytes. If you want a [KeyMaterialTrait], then use [KeyMaterial::from_rng]. -pub trait RNG : Default { +/// +/// Implementors are expected to also implement [Default] (default-construction should produce a +/// securely OS-seeded instance), but this is intentionally *not* a supertrait bound: requiring +/// `Default` would make `RNG` not dyn-compatible, and `&mut dyn RNG` is needed so RNG instances +/// can be handed around as trait objects. +pub trait RNG { // TODO: add back once we figure out streaming interaction with entropy sources. // fn add_seed_bytes(&mut self, additional_seed: &[u8]) -> Result<(), RNGError>; - fn add_seed_keymaterial(&mut self, additional_seed: impl KeyMaterialTrait) -> Result<(), RNGError>; + fn add_seed_keymaterial( + &mut self, + additional_seed: &dyn KeyMaterialTrait, + ) -> Result<(), RNGError>; fn next_int(&mut self) -> Result; /// Returns the number of requested bytes. + #[cfg(feature = "alloc")] fn next_bytes(&mut self, len: usize) -> Result, RNGError>; + /// The allocation-free, `no_std`-friendly counterpart to [RNG::next_bytes]: returns `N` random + /// bytes in a fixed-size array. + /// + /// Bounded `where Self: Sized` so that adding this generic method does not make [RNG] + /// dyn-incompatible: `&mut dyn RNG` is used widely (e.g. [KEMEncapsulator::encaps_rng]). + fn next_bytes_array(&mut self) -> Result<[u8; N], RNGError> + where + Self: Sized, + { + let mut out = [0u8; N]; + self.next_bytes_out(&mut out)?; + Ok(out) + } + /// Returns the number of bytes written. + /// The entire output buffer is zeroized before the random bytes are written. fn next_bytes_out(&mut self, out: &mut [u8]) -> Result; - fn fill_keymaterial_out(&mut self, out: &mut impl KeyMaterialTrait) -> Result; + fn fill_keymaterial_out(&mut self, out: &mut dyn KeyMaterialTrait) -> Result; /// Returns the Security Strength of this RNG. fn security_strength(&self) -> SecurityStrength; @@ -402,21 +590,86 @@ pub trait RNG : Default { /// A trait that forces an object to implement a zeroizing Drop() as well as Debug and Display that /// will not log the sensitive contents, even in error or crash-dump scenarios. -#[allow(drop_bounds)] // Since rust auto-implements Drop, there's a lint that explicitly bounding on Drop is useless. - // I disagree because I want to force things that are secrets to manually implement Drop that zeroizes the data. - // So I'm turning off this lint. -pub trait Secret : Drop + Debug + Display {} +// Since rust auto-implements Drop, there's a lint that explicitly bounding on Drop is useless. +// I disagree because I want to force things that are secrets to manually implement Drop that zeroizes the data. +// So I'm turning off this lint. +#[allow(drop_bounds)] +pub trait Secret: Drop + Debug + Display {} + +/// Allows a stateful object to suspend its operation by serializing its state into a byte array +///so that it can be resumed later, potentially from a different host. +/// +/// This is intended for situations where an object is being used through its streaming API +/// (do_update, do_final) and the operation wants to be paused to a cache, for example while waiting +/// for network IO. +/// +/// This is not intended as a mechanism to clone the state of an object since in most cases `.clone()` +/// will be more straightforward. +/// +/// The serialized state MAY contain short-term sensitive values such as nonces or IVs, +/// but it MUST NOT include a serialized private key. +/// Keyed algorithms MUST instead impl +/// [SuspendableKeyed] which requires the key to be supplied independently at the time of deserialization. +pub trait Suspendable: Sized { + /// Suspend operation by serializing out the state of the object. + /// + /// Note that this consumes `self` to prevent accidentally continuing to use the object after serialization. + /// If you want to do this intentionally, then you will need to clone the object before serializing it. + /// + /// The serialized state MUST include a prefix indicating the version of the library that serialized it. + fn suspend(self) -> [u8; SERIALIZED_STATE_LEN]; + + /// Resume operation from a serialized state. + /// + /// Deserializers SHOULD check the version and reject serialized states from incompatible versions + /// (including rejecting serializations from a future version of the library). + /// For example, if a given object made a breaking change to its serialization in version 1.2.3, then its + /// deserializer should reject serialized states from that version or older. + fn from_suspended(state: [u8; SERIALIZED_STATE_LEN]) -> Result; +} -/// Pre-Hashed Signature is an extension to [Signature] that adds functionality specific to signature +/// Similar to [Suspendable] in that it allows a stateful object to suspend its operation by +/// serializing its state into a byte array so that it can be resumed later, potentially from a different host. +/// +/// The difference is that this trait is for keyed algorithms -- MACs, symmetric ciphers, signatures, etc -- +/// which require a private key in order to resume successfully. +/// For security reasons, the private key is not included in the serialized state +/// and must be provided separately as part of the deserialization process. +pub trait SuspendableKeyed: Sized { + /// The type of key that must be re-supplied to resume this object. + type Key: ?Sized; + + /// Suspend operation by serializing out the state of the object. + /// + /// Note that this consumes `self` to prevent accidentally continuing to use the object after serialization. + /// If you want to do this intentionally, then you will need to clone the object before serializing it. + /// + /// The serialized state MUST include a prefix indicating the version of the library that serialized it. + fn suspend(self) -> [u8; SERIALIZED_STATE_LEN]; + + /// Resume operation from a serialized state and the key. + /// + /// Deserializers SHOULD check the version and reject serialized states from incompatible versions + /// (including rejecting serializations from a future version of the library). + /// For example, if a given object made a breaking change to its serialization in version 1.2.3, then its + /// deserializer should reject serialized states from that version or older. + fn from_suspended( + state: [u8; SERIALIZED_STATE_LEN], + key: &Self::Key, + ) -> Result; +} + +/// Pre-Hashed Signer is an extension to [Signer] that adds functionality specific to signature /// primatives that can operate on a pre-hashed message instead of the full message. -pub trait PHSignature< +pub trait PHSigner< PK: SignaturePublicKey, SK: SignaturePrivateKey, const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, - const PH_LEN: usize>: - Signature{ + const PH_LEN: usize, +>: Signer +{ /// Produce a signature for the provided pre-hashed message and context. /// /// `ctx` accepts a zero-length byte array. @@ -441,40 +694,93 @@ pub trait PHSignature< /// Not all signature primitives will support a context value, so you may need to consult the /// documentation for the underlying primitive for how it handles a ctx in that case, for example, it /// might throw an error, ignore the provided ctx value, or append the ctx to the msg in a non-standard way. - fn sign_ph(sk: &SK, ph: &[u8; PH_LEN], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError>; + fn sign_ph( + sk: &SK, + ph: &[u8; PH_LEN], + ctx: Option<&[u8]>, + ) -> Result<[u8; SIG_LEN], SignatureError>; /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer. - fn sign_ph_out(sk: &SK, ph: &[u8; PH_LEN], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN]) -> Result; + /// The entire output buffer is zeroized before the signature is written. + fn sign_ph_out( + sk: &SK, + ph: &[u8; PH_LEN], + ctx: Option<&[u8]>, + output: &mut [u8; SIG_LEN], + ) -> Result; +} + +/// Pre-Hashed Signature Verifier is an extension to [SignatureVerifier] that adds functionality specific to signature +/// primatives that can operate on a pre-hashed message instead of the full message. +pub trait PHSignatureVerifier< + PK: SignaturePublicKey, + const PK_LEN: usize, + const SIG_LEN: usize, + const PH_LEN: usize, +>: SignatureVerifier +{ /// On success, returns Ok(()) /// On failure, returns Err([SignatureError::SignatureVerificationFailed]); may also return other types of [SignatureError] as appropriate (such as for invalid-length inputs). - fn verify_ph(pk: &PK, ph: &[u8; PH_LEN], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError>; + fn verify_ph( + pk: &PK, + ph: &[u8; PH_LEN], + ctx: Option<&[u8]>, + sig: &[u8], + ) -> Result<(), SignatureError>; +} + +// todo: could the public and private key types impl Into> and From> +// todo: that automatically call the encode and from_bytes() ? + +/// A public key for a signature algorithm, often denoted "pk". +pub trait SignaturePublicKey: + PartialEq + Eq + Clone + Debug + Display + Sized +{ + /// Write it out to bytes in its standard encoding. + fn encode(&self) -> [u8; PK_LEN]; + /// Write it out to bytes in its standard encoding. + /// The entire output buffer is zeroized before the encoding is written. + fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize; + /// Read it in from bytes in its standard encoding. + fn from_bytes(bytes: &[u8]) -> Result; +} + +/// A private key for a signature algorithm, often denoted "sk" (for "secret key"). +pub trait SignaturePrivateKey: + PartialEq + Eq + Clone + Secret + Sized +{ + /// Write it out to bytes in its standard encoding. + fn encode(&self) -> [u8; SK_LEN]; + /// Write it out to bytes in its standard encoding. + /// The entire output buffer is zeroized before the encoding is written. + fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize; + /// Read it in from bytes in its standard encoding. + fn from_bytes(bytes: &[u8]) -> Result; } /// A digital signature algorithm is defined as a set of three operations: /// key generation, signing, and verification. /// -/// To avoid the use of dyn, this trait does not include key generation; you'll have to consult the -/// documentation for the underlying signature primitive for how to generate a key pair. +/// This trait represents the operations performed by the holder of the signing private key: +/// which include signing and key generation. Verification operations are performed by the corresponding +/// [SignatureVerifier] trait. +/// There are several reasons for this split: first is architectural; some complex algorithms may +/// benefit from having the signature generation and verification implementations split into separate modules. +/// Second is for compliance: sometimes a policy soft-deprecates an algorithm so that new signatures +/// can no longer be created, but existing signatures can still be verified. Splitting the traits +/// makes this policy easier to enforce. /// /// This high-level trait defines the operations over a generic signature algorithm that is assumed /// to source all its randomness from bouncycastle's default os-backed RNG. /// The underlying signature primitives will expose APIs that allow for specifying a specific RNG /// or deterministic seed values. /// -/// Here we statically-size the arrays used to encode public keys, private keys, and signature values +/// The arrays used to encode public keys, private keys, and signature values are statically-sized /// because this allows us to safely remove runtime checks for array lengths, which overall reduces /// the fallibility of the library. This design choice could make this trait complicated to apply /// to a signature algorithm that do not have fixed sizes for the encodings of these objects. -pub trait Signature< - PK: SignaturePublicKey, - SK: SignaturePrivateKey, - const PK_LEN: usize, - const SK_LEN: usize, - const SIG_LEN: usize ->: Sized { - /// Generate a keypair. - /// Error condition: Basically only on RNG failures - fn keygen() -> Result<(PK, SK), SignatureError>; - +pub trait Signer, const SK_LEN: usize, const SIG_LEN: usize>: + Sized +{ /// Produce a signature for the provided message and context. /// Both the `msg` and `ctx` accept zero-length byte arrays. /// @@ -501,7 +807,13 @@ pub trait Signature< fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError>; /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer. - fn sign_out(sk: &SK, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN]) -> Result; + /// The entire output buffer is zeroized before the signature is written. + fn sign_out( + sk: &SK, + msg: &[u8], + ctx: Option<&[u8]>, + output: &mut [u8; SIG_LEN], + ) -> Result; /* streaming signing API */ /// Initialize a signer for streaming mode with the provided private key. @@ -516,8 +828,31 @@ pub trait Signature< fn sign_final(self) -> Result<[u8; SIG_LEN], SignatureError>; /// Returns the number of bytes written to the output buffer. Can be called with an oversized buffer. + /// The entire output buffer is zeroized before the signature is written. fn sign_final_out(self, output: &mut [u8; SIG_LEN]) -> Result; +} +/// A digital signature algorithm is defined as a set of three operations: +/// key generation, signing, and verification. +/// +/// This trait represents the verification operations performed by the holder of the verification public key. +/// Keygen and signing operations are performed by the corresponding [Signer] trait. +/// There are several reasons for this split: first is architectural; some complex algorithms may +/// benefit from having the signature generation and verification implementations split into separate modules. +/// Second is for compliance: sometimes a policy soft-deprecates an algorithm so that new signatures +/// can no longer be created, but existing signatures can still be verified. Splitting the traits +/// makes this policy easier to enforce. +/// +/// Here we statically-size the arrays used to encode public keys, private keys, and signature values +/// because this allows us to safely remove runtime checks for array lengths, which overall reduces +/// the fallibility of the library. This design choice could make this trait complicated to apply +/// to a signature algorithm that do not have fixed sizes for the encodings of these objects. +pub trait SignatureVerifier< + PK: SignaturePublicKey, + const PK_LEN: usize, + const SIG_LEN: usize, +>: Sized +{ /// On success, returns Ok(()) /// On failure, returns Err([SignatureError::SignatureVerificationFailed]); may also return other types of [SignatureError] as appropriate (such as for invalid-length inputs). fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError>; @@ -535,30 +870,6 @@ pub trait Signature< fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError>; } -// todo: could the public and private key types impl Into> and From> -// todo: that automatically call the encode and from_bytes() ? - -/// A public key for a signature algorithm, often denoted "pk". -pub trait SignaturePublicKey : PartialEq + Eq + Clone + Debug + Display + Sized { - /// Write it out to bytes in its standard encoding. - fn encode(&self) -> [u8; PK_LEN]; - /// Write it out to bytes in its standard encoding. - fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize; - /// Read it in from bytes in its standard encoding. - fn from_bytes(bytes: &[u8]) -> Result; -} - -/// A private key for a signature algorithm, often denoted "sk" (for "secret key"). -pub trait SignaturePrivateKey : PartialEq + Eq + Clone + Secret + Sized { - /// Write it out to bytes in its standard encoding. - fn encode(&self) -> [u8; SK_LEN]; - /// Write it out to bytes in its standard encoding. - fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize; - /// Read it in from bytes in its standard encoding. - fn from_bytes(bytes: &[u8]) -> Result; -} - - /// Extensible Output Functions (XOFs) are similar to hash functions, except that they can produce output of arbitrary length. /// The naming used for the functions of this trait are borrowed from the SHA3-style sponge constructions that split XOF operation /// into two phases: an absorb phase in which an arbitrary amount of input is provided to the XOF, @@ -577,12 +888,25 @@ pub trait SignaturePrivateKey : PartialEq + Eq + Clone + Se /// to break anonymity-preserving technology. /// Applications that require the arbitrary-length output of an XOF, but also care about these /// distinguishing attacks should consider adding a cryptographic salt to diversify the inputs. -pub trait XOF : Default { +pub trait XOF: Default { /// A static one-shot API that digests the input data and produces `result_len` bytes of output. + #[cfg(feature = "alloc")] fn hash_xof(self, data: &[u8], result_len: usize) -> Vec; + /// The allocation-free, `no_std`-friendly counterpart to [XOF::hash_xof]: digests the input data + /// and produces exactly `N` bytes of output in a fixed-size array. + fn hash_xof_array(self, data: &[u8]) -> [u8; N] + where + Self: Sized, + { + let mut output = [0u8; N]; + let _ = self.hash_xof_out(data, &mut output); + output + } + /// A static one-shot API that digests the input data and produces `result_len` bytes of output. /// Fills the provided output slice. + /// The entire output buffer is zeroized before the output is written. fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize; fn absorb(&mut self, data: &[u8]); @@ -595,10 +919,23 @@ pub trait XOF : Default { ) -> Result<(), HashError>; /// Can be called multiple times. + #[cfg(feature = "alloc")] fn squeeze(&mut self, num_bytes: usize) -> Vec; + /// The allocation-free, `no_std`-friendly counterpart to [XOF::squeeze]: squeezes exactly `N` + /// bytes into a fixed-size array. Can be called multiple times. + fn squeeze_array(&mut self) -> [u8; N] + where + Self: Sized, + { + let mut output = [0u8; N]; + let _ = self.squeeze_out(&mut output); + output + } + /// Can be called multiple times. /// Fills the provided output slice. + /// The entire output buffer is zeroized before the output is written. fn squeeze_out(&mut self, output: &mut [u8]) -> usize; /// Squeezes a partial byte from the XOF. @@ -606,6 +943,8 @@ pub trait XOF : Default { /// This is a final call and consumes self. fn squeeze_partial_byte_final(self, num_bits: usize) -> Result; + /// The same as [XOF::squeeze_partial_byte_final], but writes into the provided output byte. + /// The output byte is zeroized before the result is written. fn squeeze_partial_byte_final_out( self, num_bits: usize, diff --git a/crypto/core/tests/key_material_tests.rs b/crypto/core/tests/key_material_tests.rs index 4bb3083..0dfcf96 100644 --- a/crypto/core/tests/key_material_tests.rs +++ b/crypto/core/tests/key_material_tests.rs @@ -2,9 +2,10 @@ mod test_key_material { use bouncycastle_core::errors::KeyMaterialError; use bouncycastle_core::key_material::{ - KeyMaterial0, KeyMaterial128, KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait, + KeyMaterial, KeyMaterial0, KeyMaterial128, KeyMaterial256, KeyMaterial512, + KeyMaterialTrait, KeyType, do_hazardous_operations, }; - use bouncycastle_core::traits::{SecurityStrength}; + use bouncycastle_core::traits::SecurityStrength; const DUMMY_KEY: &[u8; 64] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\ \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\ @@ -36,7 +37,7 @@ mod test_key_material { fn test_set_bytes_as_type() { let key_bytes = [0u8; 16]; let mut key = KeyMaterial256::new(); - let res = key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy); + let res = key.set_bytes_as_type(&key_bytes, KeyType::Unknown); match res { Ok(_) => { panic!("should have thrown a KeyMaterialError::ActingOnZeroizedKey error.") @@ -46,35 +47,41 @@ mod test_key_material { assert_eq!(key.key_type(), KeyType::Zeroized); assert_eq!(key.key_len(), 16); - // ... but we can force it. - key.allow_hazardous_operations(); - key.set_key_type(KeyType::BytesLowEntropy).unwrap(); - key.drop_hazardous_operations(); + // but it'll allow it within tho do_hazardous closure. + do_hazardous_operations(&mut key, |key| { + key.set_key_type(KeyType::Unknown)?; + Ok(()) + }) + .unwrap(); } Err(_) => { panic!("should have thrown a KeyMaterialError::ActingOnZeroizedKey error.") } } - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); assert_eq!(key.security_strength(), SecurityStrength::None); - // but it'll allow it if you allow hazardous operations first. + // but it'll allow it within tho do_hazardous closure. let key_bytes = [0u8; 16]; let mut key = KeyMaterial256::new(); - key.allow_hazardous_operations(); - key.set_bytes_as_type(&key_bytes, KeyType::BytesLowEntropy).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + do_hazardous_operations(&mut key, |key| { + key.set_bytes_as_type(&key_bytes, KeyType::Unknown)?; + Ok(()) + }) + .unwrap(); + assert_eq!(key.key_type(), KeyType::Unknown); // nothing else requires setting hazardous operations. } #[test] fn test_refs() { + // by hand — this test exercises guard state-change semantics, so review carefully. let mut key = KeyMaterial256::from_bytes(&[1u8; 16]).unwrap(); assert_eq!(key.capacity(), 32); assert_eq!(key.ref_to_bytes(), &[1u8; 16]); // note: this is also testing that even though the internal buffer is larger than 16 bytes, it slices it down to length. - match key.mut_ref_to_bytes() { + match key.ref_to_bytes_mut() { Ok(_) => { panic!("getting a mut ref should require setting hazardous operations.") } @@ -83,14 +90,23 @@ mod test_key_material { panic!("getting a mut ref should require setting hazardous operations.") } } - key.allow_hazardous_operations(); - assert_eq!(key.mut_ref_to_bytes().unwrap().len(), 32); - assert_eq!(key.mut_ref_to_bytes().unwrap()[..16], [1u8; 16]); - assert_eq!(key.mut_ref_to_bytes().unwrap()[16..], [0u8; 16]); + + // check that we can read from the mut_ref_to_bytes + // (which is an odd way to use it, but legal) + do_hazardous_operations(&mut key, |key| { + assert_eq!(key.ref_to_bytes_mut()?.len(), 32); + assert_eq!(key.ref_to_bytes_mut()?[..16], [1u8; 16]); + assert_eq!(key.ref_to_bytes_mut()?[16..], [0u8; 16]); + Ok(()) + }) + .unwrap(); // and I can set them - key.mut_ref_to_bytes().unwrap().copy_from_slice(&[2u8; 32]); - key.set_key_len(32).unwrap(); + do_hazardous_operations(&mut key, |key| { + key.ref_to_bytes_mut().unwrap().copy_from_slice(&[2u8; 32]); + key.set_key_len(32) + }) + .unwrap(); assert_eq!(key.ref_to_bytes(), &[2u8; 32]); assert_eq!(key.key_len(), 32); } @@ -143,16 +159,16 @@ mod test_key_material { fn from_bytes() { let key = KeyMaterial512::from_bytes(&DUMMY_KEY[..64]).unwrap(); assert_eq!(key.key_len(), 64); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); // Basic success case let key = - KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesFullEntropy).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::CryptographicRandom).unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_128bit); // Success case: KeyType::BytesLowEntropy gets tagged with SecurityStrength::None. - let key = KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy); + let key = KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::Unknown); assert_eq!(key.unwrap().security_strength(), SecurityStrength::None); } @@ -162,51 +178,62 @@ mod test_key_material { let key = KeyMaterial256::from_rng(&mut rng::DefaultRNG::default()).unwrap(); assert_eq!(key.key_len(), 32); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); let key = KeyMaterial512::from_rng(&mut rng::DefaultRNG::default()).unwrap(); assert_eq!(key.key_len(), 64); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); } #[test] fn zeroize() { let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); + let capacity = key.capacity(); + + // Sanity check: the backing buffer actually holds non-zero key material before it is wiped. + // Without this, the post-zeroize assertion below could pass vacuously. + do_hazardous_operations(&mut key, |key| { + assert!(key.ref_to_bytes_mut().unwrap().iter().any(|&b| b != 0)); + Ok(()) + }) + .unwrap(); + key.zeroize(); let key_len = key.key_len(); assert_eq!(key_len, 0); assert_eq!(key.key_type(), KeyType::Zeroized); - key.allow_hazardous_operations(); - let mut buf = vec![0u8; key_len]; - buf.copy_from_slice(key.ref_to_bytes()); - assert!(buf.iter().all(|&b| b == 0)); + // zeroize() must wipe the entire backing buffer. + // Full capacity must be inspected to confirm the previously-set bytes were + // actually overwritten with zeros. + // Note: key_len is now 0, so ref_to_bytes() returns an empty slice. + do_hazardous_operations(&mut key, |key| { + let full_buf = key.ref_to_bytes_mut().unwrap(); + assert_eq!(full_buf.len(), capacity); + assert!(full_buf.iter().all(|&b| b == 0)); + Ok(()) + }) + .unwrap(); } #[test] - fn test_truncate() { + fn test_truncation() { let mut key = KeyMaterial512::from_bytes(&DUMMY_KEY[..64]).unwrap(); assert_eq!(key.key_len(), 64); // This should be no change - match key.truncate(64) { + match key.set_key_len(64) { Ok(()) => { /* good */ } _ => panic!("Expected Ok(())"), } assert_eq!(key.key_len(), 64); - key.truncate(32).unwrap(); + key.set_key_len(32).unwrap(); assert_eq!(key.key_len(), 32); - match key.truncate(64) { - Err(KeyMaterialError::InvalidLength) => { /* good */ } - _ => panic!("Expected InvalidLength"), - } - - key.truncate(16).unwrap(); + key.set_key_len(16).unwrap(); assert_eq!(key.key_len(), 16); - key.allow_hazardous_operations(); let key_len = key.key_len(); let mut buf = vec![0u8; key_len]; buf.copy_from_slice(key.ref_to_bytes()); @@ -216,67 +243,68 @@ mod test_key_material { // Test truncating a key to zero length let mut key_zero = KeyMaterial512::from_bytes(&DUMMY_KEY[..64]).unwrap(); - key_zero.truncate(0).unwrap(); + key_zero.set_key_len(0).unwrap(); assert_eq!(key_zero.key_len(), 0); assert_eq!(key_zero.key_type(), KeyType::Zeroized); // test security strength interactions with truncation let mut key = - KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap(); + KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_256bit); - key.truncate(16).unwrap(); + key.set_key_len(16).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_128bit); - key.truncate(14).unwrap(); + key.set_key_len(14).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_112bit); - key.truncate(11).unwrap(); + key.set_key_len(11).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::None); // truncate should not raise the security level let mut key = - KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::BytesFullEntropy).unwrap(); + KeyMaterial512::from_bytes_as_type(&[1u8; 64], KeyType::CryptographicRandom).unwrap(); key.set_security_strength(SecurityStrength::_112bit).unwrap(); - key.drop_hazardous_operations(); - key.truncate(64).unwrap(); + key.set_key_len(64).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_112bit); } #[test] fn test_conversions() { let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); assert!(!key.is_full_entropy()); // Note: can't use the usual assert_eq!() here because that requires PartialEq, but we're in a no_std context here. match key.key_type() { - KeyType::BytesLowEntropy => { /* good */ } + KeyType::Unknown => { /* good */ } _ => panic!("Expected BytesLowEntropy"), } // This should fail. - match key.convert_key_type(KeyType::BytesFullEntropy) { + match key.set_key_type(KeyType::CryptographicRandom) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::BytesFullEntropy).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert!(key.is_full_entropy()); - key.drop_hazardous_operations(); - match key.convert_key_type(KeyType::SymmetricCipherKey) { + // Now we can convert BytesFullEntropy -> SymmetricCipherKey outside of a hazop block + match key.set_key_type(KeyType::SymmetricCipherKey) { Ok(()) => { /* good */ } _ => panic!("Expected Ok(())"), } - match key.convert_key_type(KeyType::BytesFullEntropy) { + match key.set_key_type(KeyType::CryptographicRandom) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::BytesFullEntropy).unwrap(); - match key.convert_key_type(KeyType::Seed) { + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); + + // Now we can convert BytesFullEntropy -> Seed outside of a hazop block + match key.set_key_type(KeyType::Seed) { Ok(()) => { /* good */ } _ => panic!("Expected Ok(())"), } @@ -284,34 +312,26 @@ mod test_key_material { // each KeyType can convert to itself let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.set_key_type(KeyType::BytesLowEntropy).unwrap(); - key.drop_hazardous_operations(); - key.convert_key_type(KeyType::BytesLowEntropy).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::Unknown)).unwrap(); + key.set_key_type(KeyType::Unknown).unwrap(); let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.set_key_type(KeyType::BytesFullEntropy).unwrap(); - key.drop_hazardous_operations(); - key.convert_key_type(KeyType::BytesFullEntropy).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); + key.set_key_type(KeyType::CryptographicRandom).unwrap(); let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::MACKey)).unwrap(); key.set_key_type(KeyType::MACKey).unwrap(); - key.drop_hazardous_operations(); - key.convert_key_type(KeyType::MACKey).unwrap(); let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::SymmetricCipherKey)) + .unwrap(); key.set_key_type(KeyType::SymmetricCipherKey).unwrap(); - key.drop_hazardous_operations(); - key.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::Seed)).unwrap(); key.set_key_type(KeyType::Seed).unwrap(); - key.drop_hazardous_operations(); - key.convert_key_type(KeyType::Seed).unwrap(); } #[test] @@ -320,23 +340,23 @@ mod test_key_material { assert_eq!(zeroized_key.key_type(), KeyType::Zeroized); /* All conversions should fail. */ - match zeroized_key.convert_key_type(KeyType::BytesLowEntropy) { + match zeroized_key.set_key_type(KeyType::Unknown) { Err(KeyMaterialError::ActingOnZeroizedKey) => { /* good */ } _ => panic!("Expected ActingOnZeroizedKey"), } - match zeroized_key.convert_key_type(KeyType::BytesFullEntropy) { + match zeroized_key.set_key_type(KeyType::CryptographicRandom) { Err(KeyMaterialError::ActingOnZeroizedKey) => { /* good */ } _ => panic!("Expected ActingOnZeroizedKey"), } - match zeroized_key.convert_key_type(KeyType::MACKey) { + match zeroized_key.set_key_type(KeyType::MACKey) { Err(KeyMaterialError::ActingOnZeroizedKey) => { /* good */ } _ => panic!("Expected ActingOnZeroizedKey"), } - match zeroized_key.convert_key_type(KeyType::Seed) { + match zeroized_key.set_key_type(KeyType::Seed) { Err(KeyMaterialError::ActingOnZeroizedKey) => { /* good */ } _ => panic!("Expected ActingOnZeroizedKey"), } - match zeroized_key.convert_key_type(KeyType::SymmetricCipherKey) { + match zeroized_key.set_key_type(KeyType::SymmetricCipherKey) { Err(KeyMaterialError::ActingOnZeroizedKey) => { /* good */ } _ => panic!("Expected ActingOnZeroizedKey"), } @@ -349,7 +369,7 @@ mod test_key_material { // But it's totally fine if you give it non-zero input data. let not_zero_key = KeyMaterial256::from_bytes(&[1u8; 19]).unwrap(); - assert_eq!(not_zero_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(not_zero_key.key_type(), KeyType::Unknown); // test .set_bytes_as_type() // it should detect if you give it all zero input data. @@ -366,11 +386,12 @@ mod test_key_material { // but it should still have set the key bytes; it's just giving you a friendly warning assert_eq!(zero_key.key_type(), KeyType::Zeroized); - // ... but will allow it if you set .allow_hazardous_operations() first. + // ... but will allow it inside a hazop closure let mut zero_key = KeyMaterial256::new(); - zero_key.allow_hazardous_operations(); - zero_key.set_bytes_as_type(&[0u8; 19], KeyType::MACKey).unwrap(); - zero_key.drop_hazardous_operations(); + do_hazardous_operations(&mut zero_key, |key| { + key.set_bytes_as_type(&[0u8; 19], KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_key.key_type(), KeyType::MACKey); } @@ -378,45 +399,43 @@ mod test_key_material { /// Tests the conversions that should only be allowed if hazardous_conversions() has been set. fn test_hazardous_conversions_from_bytes() { let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); /* All the non-hazardous conversions should work. */ // ... none /* All the hazardous conversions should fail. */ - match key.convert_key_type(KeyType::BytesFullEntropy) { + match key.set_key_type(KeyType::CryptographicRandom) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - match key.convert_key_type(KeyType::MACKey) { + match key.set_key_type(KeyType::MACKey) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - match key.convert_key_type(KeyType::SymmetricCipherKey) { + match key.set_key_type(KeyType::SymmetricCipherKey) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - match key.convert_key_type(KeyType::Seed) { + match key.set_key_type(KeyType::Seed) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } /* Should work if you allow hazardous conversions. */ key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::BytesFullEntropy).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::MACKey).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::MACKey)).unwrap(); key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::SymmetricCipherKey)) + .unwrap(); key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::Seed).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::Seed)).unwrap(); } #[test] @@ -443,7 +462,7 @@ mod test_key_material { let key1 = KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::MACKey).unwrap(); assert_eq!(key1.key_type(), KeyType::MACKey); assert_eq!(key1.security_strength(), SecurityStrength::_256bit); - + // success case: same size using default From impl; only works if the sizes are the same (ie the compiler knows that they are the same type. let key2 = KeyMaterial256::from(key1.clone()); assert_eq!(key1.key_len(), key2.key_len()); @@ -468,80 +487,86 @@ mod test_key_material { /// Not exhaustive, cargo mutants will probably not be satisfied. fn test_hazardous_conversions_cast_types() { let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::MACKey).unwrap(); - key.drop_hazardous_operations(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::MACKey)).unwrap(); // converting to self should work (idempotency) - key.convert_key_type(KeyType::MACKey).unwrap(); + key.set_key_type(KeyType::MACKey).unwrap(); /* All the hazardous conversions should fail. */ - match key.convert_key_type(KeyType::BytesFullEntropy) { + match key.set_key_type(KeyType::CryptographicRandom) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - match key.convert_key_type(KeyType::SymmetricCipherKey) { + match key.set_key_type(KeyType::SymmetricCipherKey) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } - match key.convert_key_type(KeyType::Seed) { + match key.set_key_type(KeyType::Seed) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected HazardousConversion"), } // should work if you allow hazardous conversions. - key.allow_hazardous_operations(); - key.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); + do_hazardous_operations(&mut key, |key| key.set_key_type(KeyType::SymmetricCipherKey)) + .unwrap(); } #[test] fn test_security_strength() { let key = KeyMaterial512::from_bytes(DUMMY_KEY).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); assert_eq!(key.security_strength(), SecurityStrength::None); - let key = KeyMaterial512::from_bytes_as_type(DUMMY_KEY, KeyType::BytesFullEntropy).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(DUMMY_KEY, KeyType::CryptographicRandom).unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_256bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_256bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..31], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..31], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_192bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..24], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..24], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_192bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..16], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..16], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_128bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..15], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..15], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_112bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..14], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..14], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_112bit); - let key = KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..13], KeyType::BytesFullEntropy) - .unwrap(); - assert_eq!(key.key_type(), KeyType::BytesFullEntropy); + let key = + KeyMaterial512::from_bytes_as_type(&DUMMY_KEY[..13], KeyType::CryptographicRandom) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::None); // even if it's long enough, BytesLowEntropy or Zeroized always get ::None - let key = KeyMaterial512::from_bytes_as_type(DUMMY_KEY, KeyType::BytesLowEntropy).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + let key = KeyMaterial512::from_bytes_as_type(DUMMY_KEY, KeyType::Unknown).unwrap(); + assert_eq!(key.key_type(), KeyType::Unknown); assert_eq!(key.key_len(), 64); assert_eq!(key.security_strength(), SecurityStrength::None); @@ -551,146 +576,136 @@ mod test_key_material { assert_eq!(key.security_strength(), SecurityStrength::None); // test set_security_strength() - // Can't increase the security level without setting .allow_hazardous_operations() first. + // Can't increase the security level outside of a hazop block first. let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(key.key_type(), KeyType::Unknown); match key.set_security_strength(SecurityStrength::_128bit) { Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } _ => panic!("Expected KeyMaterialError::HazardousOperationNotPermitted"), } - key.allow_hazardous_operations(); - match key.set_security_strength(SecurityStrength::_128bit) { - Err(KeyMaterialError::SecurityStrength(_)) => { /* good */ } - _ => panic!("Expected KeyMaterialError::SecurityStrength"), - } + do_hazardous_operations(&mut key, |key| { + match key.set_security_strength(SecurityStrength::_128bit) { + Err(KeyMaterialError::SecurityStrength(_)) => { + /* good */ + Ok(()) + } + _ => panic!("Expected KeyMaterialError::SecurityStrength"), + } + }) + .unwrap(); - // So let's set it to a full-entropy type - key.set_key_type(KeyType::BytesFullEntropy).unwrap(); - // now it should work - key.set_security_strength(SecurityStrength::_128bit).unwrap(); + // Even in a hazops block, you can't set a BytesLowEntropy to any Security Strength other than None + do_hazardous_operations(&mut key, |key| { + match key.set_security_strength(SecurityStrength::_128bit) { + Err(KeyMaterialError::SecurityStrength(_)) => { + /* good */ + Ok(()) + } + _ => panic!("Expected KeyMaterialError::SecurityStrength"), + } + }) + .unwrap(); + // But it'll work if you set it to a full entropy type + do_hazardous_operations(&mut key, |key| { + key.set_key_type(KeyType::CryptographicRandom).unwrap(); + key.set_security_strength(SecurityStrength::_128bit) + }) + .unwrap(); + assert_eq!(key.key_type(), KeyType::CryptographicRandom); assert_eq!(key.security_strength(), SecurityStrength::_128bit); // BytesLowEntropy keys cannot have a security strength other than None. // success let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); - assert_eq!(key.key_type(), KeyType::BytesLowEntropy); - // setting to ::None should work .. even without setting .allow_hazardous_operations() + assert_eq!(key.key_type(), KeyType::Unknown); + // setting to ::None should work .. even outside of a hazop block key.set_security_strength(SecurityStrength::None).unwrap(); // but to ::_128bit should fail - key.allow_hazardous_operations(); - match key.set_security_strength(SecurityStrength::_128bit) { - Err(KeyMaterialError::SecurityStrength(_)) => { /* good */ } - _ => panic!("Expected KeyMaterialError::SecurityStrength"), - } + do_hazardous_operations(&mut key, |key| { + match key.set_security_strength(SecurityStrength::_128bit) { + Err(KeyMaterialError::SecurityStrength(_)) => { + /* good */ + Ok(()) + } + _ => panic!("Expected KeyMaterialError::SecurityStrength"), + } + }) + .unwrap(); // Zeroized keys cannot have a security strength other than None. // success let mut key = KeyMaterial256::new(); - key.allow_hazardous_operations(); - key.set_key_len(32).unwrap(); // still zeroized + do_hazardous_operations(&mut key, |key| { + key.set_key_len(32) // still zeroized + }) + .unwrap(); assert_eq!(key.key_type(), KeyType::Zeroized); - // setting to ::None should work .. even without setting .allow_hazardous_operations() + // setting to ::None should work, even outside of a hazop block key.set_security_strength(SecurityStrength::None).unwrap(); - // but to ::_128bit should fail - key.allow_hazardous_operations(); - match key.set_security_strength(SecurityStrength::_128bit) { - Err(KeyMaterialError::SecurityStrength(_)) => { /* good */ } - _ => panic!("Expected KeyMaterialError::SecurityStrength"), - } - } - - #[test] - fn test_concatenate() { - // intentionally half-full - let mut key1 = KeyMaterial256::from_bytes(&[1u8; 16]).unwrap(); - let key2 = KeyMaterial256::from_bytes(&[2u8; 16]).unwrap(); - assert_eq!(key1.key_len(), 16); - assert_eq!(key2.key_len(), 16); - - key1.concatenate(&key2).unwrap(); - assert_eq!(key1.key_len(), 32); - assert_eq!(key1.ref_to_bytes()[..16], [1u8; 16]); - assert_eq!(key1.ref_to_bytes()[16..], [2u8; 16]); - - let mut zeroized_key = KeyMaterial256::default(); - zeroized_key.allow_hazardous_operations(); - zeroized_key.set_key_len(8).unwrap(); - zeroized_key.drop_hazardous_operations(); - assert_eq!(zeroized_key.key_type(), KeyType::Zeroized); - assert_eq!(zeroized_key.key_len(), 8); - zeroized_key.concatenate(&key2).unwrap(); - assert_eq!(zeroized_key.key_len(), 24); - // should take max(Zeroized, BytesLowEntropy) - assert_eq!(zeroized_key.key_type(), KeyType::BytesLowEntropy); - assert_eq!(zeroized_key.security_strength(), SecurityStrength::None); - - // This should be symmetric, so test it in the other direction too. - let mut zeroized_key = KeyMaterial256::default(); - zeroized_key.allow_hazardous_operations(); - zeroized_key.set_key_len(8).unwrap(); - zeroized_key.drop_hazardous_operations(); - assert_eq!(zeroized_key.key_type(), KeyType::Zeroized); - assert_eq!(zeroized_key.key_len(), 8); - let mut key2 = KeyMaterial256::from_bytes(&[1u8; 16]).unwrap(); - key2.concatenate(&zeroized_key).unwrap(); - assert_eq!(key2.key_len(), 24); - // should take max(Zeroized, BytesLowEntropy) - assert_eq!(key2.key_type(), KeyType::BytesLowEntropy); - assert_eq!(key2.security_strength(), SecurityStrength::None); - - // now try it with keys of different key types - let mut low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap(); - let full_entropy_key = - KeyMaterial256::from_bytes_as_type(&[2u8; 16], KeyType::BytesFullEntropy).unwrap(); - low_entropy_key.concatenate(&full_entropy_key).unwrap(); - // should take max(BytesLowEntropy, BytesFullEntropy) - assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy); - // should take max(None, _128Bit) - assert_eq!(low_entropy_key.security_strength(), SecurityStrength::_128bit); - - // and in the other direction too - let low_entropy_key = - KeyMaterial256::from_bytes_as_type(&[1u8; 16], KeyType::BytesLowEntropy).unwrap(); - let mut full_entropy_key = - KeyMaterial256::from_bytes_as_type(&[2u8; 16], KeyType::BytesFullEntropy).unwrap(); - full_entropy_key.concatenate(&low_entropy_key).unwrap(); - // should take max(BytesLowEntropy, BytesFullEntropy) - assert_eq!(full_entropy_key.key_type(), KeyType::BytesFullEntropy); - // should take max(None, _128Bit) - assert_eq!(full_entropy_key.security_strength(), SecurityStrength::_128bit); - - // now with full entropy keys at different security levels - let mut low_entropy_key = - KeyMaterial512::from_bytes_as_type(&[1u8; 16], KeyType::BytesFullEntropy).unwrap(); - // Now we're gonna explictly tag it at the 112bit security level -- does not require allow_hazardous_operations(). - low_entropy_key.set_security_strength(SecurityStrength::_112bit).unwrap(); - let full_entropy_key = - KeyMaterial256::from_bytes_as_type(&[2u8; 32], KeyType::BytesFullEntropy).unwrap(); - low_entropy_key.concatenate(&full_entropy_key).unwrap(); - assert_eq!(low_entropy_key.key_type(), KeyType::BytesFullEntropy); - // should take max(_112Bit, _256Bit) - assert_eq!(low_entropy_key.security_strength(), SecurityStrength::_256bit); + // but to ::_128bit should fail, even in a hazop block + do_hazardous_operations(&mut key, |key| { + match key.set_security_strength(SecurityStrength::_128bit) { + Err(KeyMaterialError::SecurityStrength(_)) => { + /* good */ + Ok(()) + } + _ => panic!("Expected KeyMaterialError::SecurityStrength"), + } + }) + .unwrap(); } #[test] fn eq() { - // On instances of the same exact type (size). - let key1 = KeyMaterial256::from_bytes( - b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", - ) - .unwrap(); - let key2 = KeyMaterial256::from_bytes( - b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", - ) - .unwrap(); + // For context: + // DUMMY_KEY: &[u8; 64] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\ + // \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F\ + // \x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2A\x2B\x2C\x2D\x2E\x2F\ + // \x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3A\x3B\x3C\x3D\x3E\x3F"; + + // Same bytes, full capacity. Should be equal. + let key1 = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); + let key2 = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); assert_eq!(key1, key2); - let key3 = KeyMaterial256::from_bytes( - b"\x0F\x0E\x0D\x0C\x0B\x0A\x10\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00", - ) - .unwrap(); + // Same length, different content. Should NOT be equal. + let key3 = KeyMaterial256::from_bytes(&[0xFFu8; 32]).unwrap(); assert_ne!(key1, key3); + + // Different length, overlapping prefix. Should NOT be equal. + let key_short = KeyMaterial256::from_bytes(&DUMMY_KEY[..16]).unwrap(); + assert_ne!(key1, key_short); + + // PartialEq ignores key_type: same bytes, different KeyType. Should be equal. + let key_low = + KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::Unknown).unwrap(); + let key_mac = + KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::MACKey).unwrap(); + assert_eq!(key_low, key_mac); + + // PartialEq ignores security_strength: same bytes, different strength. Should be equal. + let key_strong = + KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::CryptographicRandom) + .unwrap(); + let mut key_weak = + KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..32], KeyType::CryptographicRandom) + .unwrap(); + key_weak.set_security_strength(SecurityStrength::_128bit).unwrap(); + assert_ne!(key_strong.security_strength(), key_weak.security_strength()); // strengths differ + assert_eq!(key_strong, key_weak); // but keys are still equal + + // Partially-filled buffers with identical content. Should be equal. + let key_half1 = KeyMaterial256::from_bytes(&DUMMY_KEY[..16]).unwrap(); + let key_half2 = KeyMaterial256::from_bytes(&DUMMY_KEY[..16]).unwrap(); + assert_eq!(key_half1, key_half2); + + // Verify with a second size (KeyMaterial512) to cover the generic impl. + let key512_a = KeyMaterial512::from_bytes(&DUMMY_KEY[..64]).unwrap(); + let key512_b = KeyMaterial512::from_bytes(&DUMMY_KEY[..64]).unwrap(); + assert_eq!(key512_a, key512_b); + + let key512_c = KeyMaterial512::from_bytes(&[0xFFu8; 64]).unwrap(); + assert_ne!(key512_a, key512_c); } #[test] @@ -712,8 +727,11 @@ mod test_key_material { // success case -- zero keys -- different type and different KeyType let key1 = KeyMaterial0::new(); let mut key2 = KeyMaterial256::new(); - key2.allow_hazardous_operations(); - key2.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); + do_hazardous_operations(&mut key2, |key2| { + key2.set_key_type(KeyType::SymmetricCipherKey).unwrap(); + Ok(()) + }) + .unwrap(); assert!(key1.equals(&key2)); assert!(key2.equals(&key1)); @@ -726,8 +744,11 @@ mod test_key_material { // success case -- non-zero keys -- different type and different KeyType let key1 = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); let mut key2 = KeyMaterial512::from_bytes(&DUMMY_KEY[..32]).unwrap(); - key2.allow_hazardous_operations(); - key2.convert_key_type(KeyType::SymmetricCipherKey).unwrap(); + do_hazardous_operations(&mut key2, |key2| { + key2.set_key_type(KeyType::SymmetricCipherKey).unwrap(); + Ok(()) + }) + .unwrap(); assert!(key1.equals(&key2)); assert!(key2.equals(&key1)); @@ -752,14 +773,13 @@ mod test_key_material { fn rank(kt: KeyType) -> u8 { match kt { Zeroized => 0, - BytesLowEntropy => 1, - BytesFullEntropy => 2, + Unknown => 1, + CryptographicRandom => 2, Seed | MACKey | SymmetricCipherKey => 3, } } - let all_types = - [Zeroized, BytesLowEntropy, BytesFullEntropy, Seed, MACKey, SymmetricCipherKey]; + let all_types = [Zeroized, Unknown, CryptographicRandom, Seed, MACKey, SymmetricCipherKey]; for &a in &all_types { for &b in &all_types { @@ -781,4 +801,67 @@ mod test_key_material { } } } + + #[test] + /// Pins the error behaviour of [do_hazardous_operations]: + /// 1. an `Err` returned from the closure propagates out verbatim (not swallowed or remapped), + /// 2. a real `KeyMaterialError` raised by a guarded op inside the closure propagates via `?`, + /// 3. the "flattening" idiom the KDF/RNG crates rely on — collapsing a *foreign* error type + /// into a `KeyMaterialError` with `map_err` inside the closure — surfaces as that + /// `KeyMaterialError`, and + /// 4. the guard is still cleared on the error path (an erroring closure does not leave the + /// instance stuck in hazardous mode). + fn test_hazardous_ops_error_handling() { + let mut key = KeyMaterial256::from_bytes(&DUMMY_KEY[..32]).unwrap(); + + // 1. An explicit Err returned from the closure propagates out unchanged. + let result = + do_hazardous_operations(&mut key, |_key| Err(KeyMaterialError::GenericError("boom"))); + match result { + Err(KeyMaterialError::GenericError("boom")) => { /* good */ } + _ => panic!("the closure's error should propagate out verbatim"), + } + + // 2. A real KeyMaterialError raised by a guarded op inside the closure propagates via `?`. + // Raising to _256bit requires >= 32 bytes, but this key is only 16, so it fails. + let mut short = + KeyMaterial256::from_bytes_as_type(&DUMMY_KEY[..16], KeyType::CryptographicRandom) + .unwrap(); + let result = do_hazardous_operations(&mut short, |k| { + k.set_security_strength(SecurityStrength::_256bit)?; + Ok(()) + }); + match result { + Err(KeyMaterialError::SecurityStrength(_)) => { /* good */ } + _ => panic!("the SecurityStrength error should propagate from inside the closure"), + } + + // 3. The "flattening" idiom: a foreign error type is collapsed into a KeyMaterialError via + // map_err inside the closure (exactly how hkdf/rng flatten MACError/RNGError), so it + // surfaces as that KeyMaterialError. Outside the closure, the caller would then convert + // the KeyMaterialError back to its own error type via `?` / `From`. + #[derive(Debug)] + struct ForeignError; + fn foreign_op() -> Result<(), ForeignError> { + Err(ForeignError) + } + + let result = do_hazardous_operations(&mut key, |_k| { + foreign_op().map_err(|_| KeyMaterialError::GenericError("flattened"))?; + Ok(()) + }); + match result { + Err(KeyMaterialError::GenericError("flattened")) => { /* good */ } + _ => panic!("the foreign error should be flattened into a KeyMaterialError"), + } + + // 4. The guard is cleared even when the closure returns Err: a guarded op afterwards, outside + // any closure, must still be rejected. `key` is BytesLowEntropy and was never mutated by + // the erroring closures above, so converting it to Seed is a hazardous op that requires + // the guard -- which proves the guard was restored to "off". + match key.set_key_type(KeyType::Seed) { + Err(KeyMaterialError::HazardousOperationNotPermitted) => { /* good */ } + _ => panic!("the guard should have been cleared after the erroring closures"), + } + } } diff --git a/crypto/factory/Cargo.toml b/crypto/factory/Cargo.toml index b869561..85ae5e1 100644 --- a/crypto/factory/Cargo.toml +++ b/crypto/factory/Cargo.toml @@ -3,13 +3,18 @@ name = "bouncycastle-factory" version = "0.1.1" edition.workspace = true +# The factory is a convenience aggregation layer (and includes RNGFactory, which wraps the std-only +# `rng` crate), so it always requires `alloc`: it enables the `alloc` feature on every internal +# dependency unconditionally. Users needing `no_std` should depend on the individual primitive crates +# directly with `default-features = false`. Note the factory types still expose the allocation-free +# `*_array::()` / `*_out(&mut [u8])` APIs via the core trait default methods. [dependencies] -bouncycastle-core.workspace = true -bouncycastle-hkdf.workspace = true -bouncycastle-hmac.workspace = true -bouncycastle-sha2.workspace = true -bouncycastle-sha3.workspace = true -bouncycastle-rng.workspace = true +bouncycastle-core = { workspace = true, features = ["alloc"] } +bouncycastle-hkdf = { workspace = true, features = ["alloc"] } +bouncycastle-hmac = { workspace = true, features = ["alloc"] } +bouncycastle-sha2 = { workspace = true, features = ["alloc"] } +bouncycastle-sha3 = { workspace = true, features = ["alloc"] } +bouncycastle-rng = { workspace = true, features = ["alloc"] } [dev-dependencies] bouncycastle-core-test-framework.workspace = true diff --git a/crypto/factory/src/hash_factory.rs b/crypto/factory/src/hash_factory.rs index d65586f..34793de 100644 --- a/crypto/factory/src/hash_factory.rs +++ b/crypto/factory/src/hash_factory.rs @@ -29,17 +29,12 @@ use crate::{AlgorithmFactory, FactoryError}; use crate::{DEFAULT, DEFAULT_128_BIT, DEFAULT_256_BIT}; use bouncycastle_core::errors::HashError; -use bouncycastle_core::traits::{Hash, SecurityStrength}; +use bouncycastle_core::traits::{Algorithm, Hash, SecurityStrength}; use bouncycastle_sha2 as sha2; use bouncycastle_sha2::{SHA224_NAME, SHA256_NAME, SHA384_NAME, SHA512_NAME}; use bouncycastle_sha3 as sha3; use bouncycastle_sha3::{SHA3_224_NAME, SHA3_256_NAME, SHA3_384_NAME, SHA3_512_NAME}; -/*** Defaults ***/ -pub const DEFAULT_HASH_NAME: &str = SHA3_256_NAME; -pub const DEFAULT_128BIT_HASH_NAME: &str = SHA3_256_NAME; -pub const DEFAULT_256BIT_HASH_NAME: &str = SHA3_512_NAME; - /// All members must impl Hash. /// Note: no SHAKE because SHAKE is not NIST approved as a hash function. See FIPS 202 section A.2. pub enum HashFactory { @@ -55,16 +50,16 @@ pub enum HashFactory { impl Default for HashFactory { fn default() -> HashFactory { - Self::new(DEFAULT_HASH_NAME).unwrap() + Self::SHA3_256(sha3::SHA3_256::new()) } } impl AlgorithmFactory for HashFactory { fn default_128_bit() -> HashFactory { - Self::new(DEFAULT_128BIT_HASH_NAME).unwrap() + Self::SHA3_256(sha3::SHA3_256::new()) } fn default_256_bit() -> HashFactory { - Self::new(DEFAULT_256BIT_HASH_NAME).unwrap() + Self::SHA3_512(sha3::SHA3_512::new()) } fn new(alg_name: &str) -> Result { @@ -88,6 +83,12 @@ impl AlgorithmFactory for HashFactory { } } +// TODO -- this does't work. Perhaps Algorithm needs to be re-worked so that these are functions instead? +impl Algorithm for HashFactory { + const ALG_NAME: &'static str = "TODO"; + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::None; +} + impl Hash for HashFactory { fn block_bitlen(&self) -> usize { match self { @@ -129,6 +130,8 @@ impl Hash for HashFactory { } fn hash_out(self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + match self { Self::SHA224(h) => h.hash_out(data, output), Self::SHA256(h) => h.hash_out(data, output), @@ -168,6 +171,8 @@ impl Hash for HashFactory { } fn do_final_out(self, output: &mut [u8]) -> usize { + output.fill(0); + match self { Self::SHA224(h) => h.do_final_out(output), Self::SHA256(h) => h.do_final_out(output), diff --git a/crypto/factory/src/kdf_factory.rs b/crypto/factory/src/kdf_factory.rs index f3deffb..50aa274 100644 --- a/crypto/factory/src/kdf_factory.rs +++ b/crypto/factory/src/kdf_factory.rs @@ -58,11 +58,6 @@ use bouncycastle_sha3::{ SHA3_224_NAME, SHA3_256_NAME, SHA3_384_NAME, SHA3_512_NAME, SHAKE128_NAME, SHAKE256_NAME, }; -/*** Defaults ***/ -pub const DEFAULT_KDF_NAME: &str = HKDF_SHA512_NAME; -pub const DEFAULT_128BIT_KDF_NAME: &str = HKDF_SHA256_NAME; -pub const DEFAULT_256BIT_KDF_NAME: &str = HKDF_SHA512_NAME; - // All members must impl KDF. pub enum KDFFactory { #[allow(non_camel_case_types)] @@ -79,17 +74,17 @@ pub enum KDFFactory { impl Default for KDFFactory { fn default() -> Self { - KDFFactory::new(DEFAULT_KDF_NAME).unwrap() + Self::HKDF_SHA512(hkdf::HKDF_SHA512::new()) } } impl AlgorithmFactory for KDFFactory { fn default_128_bit() -> Self { - KDFFactory::new(DEFAULT_128BIT_KDF_NAME).unwrap() + Self::HKDF_SHA256(hkdf::HKDF_SHA256::new()) } fn default_256_bit() -> Self { - KDFFactory::new(DEFAULT_256BIT_KDF_NAME).unwrap() + Self::HKDF_SHA512(hkdf::HKDF_SHA512::new()) } fn new(alg_name: &str) -> Result { diff --git a/crypto/factory/src/lib.rs b/crypto/factory/src/lib.rs index 3f6d974..7c0a12b 100644 --- a/crypto/factory/src/lib.rs +++ b/crypto/factory/src/lib.rs @@ -25,7 +25,7 @@ //! It also exposes [AlgorithmFactory::new] which can be used to create an instance of the algorithm //! by string name according to the string constants associated with the respective factory type. -use bouncycastle_core::errors::{MACError}; +use bouncycastle_core::errors::MACError; pub mod hash_factory; pub mod kdf_factory; @@ -38,7 +38,6 @@ pub const DEFAULT: &str = "Default"; pub const DEFAULT_128_BIT: &str = "Default128Bit"; pub const DEFAULT_256_BIT: &str = "Default256Bit"; - #[derive(Debug)] pub enum FactoryError { MACError(MACError), @@ -52,7 +51,6 @@ impl From for FactoryError { } pub trait AlgorithmFactory: Sized + Default { - // Get the default configured algorithm. // Not implemented because all factories MUST impl Default. // fn default() -> Self; @@ -65,4 +63,4 @@ pub trait AlgorithmFactory: Sized + Default { /// Create an instance of the algorithm by name. fn new(alg_name: &str) -> Result; -} \ No newline at end of file +} diff --git a/crypto/factory/src/mac_factory.rs b/crypto/factory/src/mac_factory.rs index 64ae090..141c59f 100644 --- a/crypto/factory/src/mac_factory.rs +++ b/crypto/factory/src/mac_factory.rs @@ -175,6 +175,8 @@ impl MAC for MACFactory { } fn mac_out(self, data: &[u8], out: &mut [u8]) -> Result { + out.fill(0); + match self { Self::HMAC_SHA224(h) => h.mac_out(data, out), Self::HMAC_SHA256(h) => h.mac_out(data, out), @@ -227,6 +229,8 @@ impl MAC for MACFactory { } fn do_final_out(self, mut out: &mut [u8]) -> Result { + out.fill(0); + match self { Self::HMAC_SHA224(h) => h.do_final_out(&mut out), Self::HMAC_SHA256(h) => h.do_final_out(&mut out), diff --git a/crypto/factory/src/rng_factory.rs b/crypto/factory/src/rng_factory.rs index 89aa3e7..4e542e4 100644 --- a/crypto/factory/src/rng_factory.rs +++ b/crypto/factory/src/rng_factory.rs @@ -41,22 +41,15 @@ //! let output: Vec = h.hash(data); //! ``` -use bouncycastle_core::errors::RNGError; -use bouncycastle_core::traits::{SecurityStrength, RNG}; -use bouncycastle_core::key_material::KeyMaterialTrait; use crate::{AlgorithmFactory, FactoryError}; use crate::{DEFAULT, DEFAULT_128_BIT, DEFAULT_256_BIT}; +use bouncycastle_core::errors::RNGError; +use bouncycastle_core::key_material::KeyMaterialTrait; +use bouncycastle_core::traits::{RNG, SecurityStrength}; use bouncycastle_rng as rng; use bouncycastle_rng::{HASH_DRBG_SHA256_NAME, HASH_DRBG_SHA512_NAME}; -/*** Defaults ***/ -pub const DEFAULT_DRBG_NAME: &str = HASH_DRBG_SHA512_NAME; -pub const DEFAULT_128BIT_DRBG_NAME: &str = HASH_DRBG_SHA256_NAME; -pub const DEFAULT_256BIT_DRBG_NAME: &str = HASH_DRBG_SHA512_NAME; - - - /// All members must impl RNG. pub enum RNGFactory { #[allow(non_camel_case_types)] @@ -66,12 +59,18 @@ pub enum RNGFactory { } impl Default for RNGFactory { - fn default() -> Self { Self::new(DEFAULT_DRBG_NAME).unwrap() } + fn default() -> Self { + Self::HashDRBG_SHA512(rng::HashDRBG_SHA512::new_from_os()) + } } impl AlgorithmFactory for RNGFactory { - fn default_128_bit() -> Self { Self::new(DEFAULT_128BIT_DRBG_NAME).unwrap() } - fn default_256_bit() -> Self { Self::new(DEFAULT_256BIT_DRBG_NAME).unwrap() } + fn default_128_bit() -> Self { + Self::HashDRBG_SHA256(rng::HashDRBG_SHA256::new_from_os()) + } + fn default_256_bit() -> Self { + Self::HashDRBG_SHA512(rng::HashDRBG_SHA512::new_from_os()) + } fn new(alg_name: &str) -> Result { match alg_name { @@ -80,51 +79,59 @@ impl AlgorithmFactory for RNGFactory { DEFAULT_256_BIT => Ok(Self::default_256_bit()), HASH_DRBG_SHA256_NAME => Ok(Self::HashDRBG_SHA256(rng::HashDRBG_SHA256::new_from_os())), HASH_DRBG_SHA512_NAME => Ok(Self::HashDRBG_SHA512(rng::HashDRBG_SHA512::new_from_os())), - _ => Err(FactoryError::UnsupportedAlgorithm(format!("The algorithm: \"{}\" is not a known RNG", alg_name))), + _ => Err(FactoryError::UnsupportedAlgorithm(format!( + "The algorithm: \"{}\" is not a known RNG", + alg_name + ))), } } } impl RNG for RNGFactory { - fn add_seed_keymaterial(&mut self, additional_seed: impl KeyMaterialTrait) -> Result<(), RNGError> { + fn add_seed_keymaterial( + &mut self, + additional_seed: &dyn KeyMaterialTrait, + ) -> Result<(), RNGError> { match self { - Self::HashDRBG_SHA256(rng) => {rng.add_seed_keymaterial(additional_seed) }, - Self::HashDRBG_SHA512(rng) => { rng.add_seed_keymaterial(additional_seed) }, + Self::HashDRBG_SHA256(rng) => rng.add_seed_keymaterial(additional_seed), + Self::HashDRBG_SHA512(rng) => rng.add_seed_keymaterial(additional_seed), } } fn next_int(&mut self) -> Result { match self { - Self::HashDRBG_SHA256(rng) => {rng.next_int() }, - Self::HashDRBG_SHA512(rng) => { rng.next_int() }, + Self::HashDRBG_SHA256(rng) => rng.next_int(), + Self::HashDRBG_SHA512(rng) => rng.next_int(), } } fn next_bytes(&mut self, len: usize) -> Result, RNGError> { match self { - Self::HashDRBG_SHA256(rng) => {rng.next_bytes(len) }, - Self::HashDRBG_SHA512(rng) => { rng.next_bytes(len) }, + Self::HashDRBG_SHA256(rng) => rng.next_bytes(len), + Self::HashDRBG_SHA512(rng) => rng.next_bytes(len), } } fn next_bytes_out(&mut self, out: &mut [u8]) -> Result { + out.fill(0); + match self { - Self::HashDRBG_SHA256(rng) => {rng.next_bytes_out(out) }, - Self::HashDRBG_SHA512(rng) => { rng.next_bytes_out(out) }, + Self::HashDRBG_SHA256(rng) => rng.next_bytes_out(out), + Self::HashDRBG_SHA512(rng) => rng.next_bytes_out(out), } } - fn fill_keymaterial_out(&mut self, out: &mut impl KeyMaterialTrait) -> Result { + fn fill_keymaterial_out(&mut self, out: &mut dyn KeyMaterialTrait) -> Result { match self { - Self::HashDRBG_SHA256(rng) => {rng.fill_keymaterial_out(out) }, - Self::HashDRBG_SHA512(rng) => { rng.fill_keymaterial_out(out) }, + Self::HashDRBG_SHA256(rng) => rng.fill_keymaterial_out(out), + Self::HashDRBG_SHA512(rng) => rng.fill_keymaterial_out(out), } } fn security_strength(&self) -> SecurityStrength { match self { - Self::HashDRBG_SHA256(rng) => {rng.security_strength() }, - Self::HashDRBG_SHA512(rng) => { rng.security_strength() }, + Self::HashDRBG_SHA256(rng) => rng.security_strength(), + Self::HashDRBG_SHA512(rng) => rng.security_strength(), } } } diff --git a/crypto/factory/src/xof_factory.rs b/crypto/factory/src/xof_factory.rs index 2809a39..e35e86e 100644 --- a/crypto/factory/src/xof_factory.rs +++ b/crypto/factory/src/xof_factory.rs @@ -86,6 +86,8 @@ impl XOF for XOFFactory { } fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + match self { Self::SHAKE128(h) => h.hash_xof_out(data, output), Self::SHAKE256(h) => h.hash_xof_out(data, output), @@ -118,6 +120,8 @@ impl XOF for XOFFactory { } fn squeeze_out(&mut self, output: &mut [u8]) -> usize { + output.fill(0); + match self { Self::SHAKE128(h) => h.squeeze_out(output), Self::SHAKE256(h) => h.squeeze_out(output), @@ -136,6 +140,8 @@ impl XOF for XOFFactory { num_bits: usize, output: &mut u8, ) -> Result<(), HashError> { + *output = 0; + match self { Self::SHAKE128(h) => h.squeeze_partial_byte_final_out(num_bits, output), Self::SHAKE256(h) => h.squeeze_partial_byte_final_out(num_bits, output), diff --git a/crypto/factory/tests/hash_factory_tests.rs b/crypto/factory/tests/hash_factory_tests.rs index 55221ee..6be8756 100644 --- a/crypto/factory/tests/hash_factory_tests.rs +++ b/crypto/factory/tests/hash_factory_tests.rs @@ -1,17 +1,17 @@ #[cfg(test)] mod hash_factory_tests { - use bouncycastle_factory::AlgorithmFactory; - use bouncycastle_factory::hash_factory::{HashFactory}; - use bouncycastle_factory::xof_factory::{XOFFactory}; use bouncycastle_core::traits::{Hash, XOF}; use bouncycastle_core_test_framework::DUMMY_SEED_512; + use bouncycastle_factory::AlgorithmFactory; + use bouncycastle_factory::hash_factory::HashFactory; + use bouncycastle_factory::xof_factory::XOFFactory; mod sha3_tests { use super::*; - use bouncycastle_sha2::SHA224; + use bouncycastle_factory as factory; use bouncycastle_sha2 as sha2; + use bouncycastle_sha2::SHA224; use bouncycastle_sha3 as sha3; - use bouncycastle_factory as factory; #[test] fn sha2_hash_tests() { @@ -20,7 +20,6 @@ mod hash_factory_tests { let h = SHA224::new(); h.hash(&DUMMY_SEED_512[..24]); - let sha2 = HashFactory::new("SHA224").unwrap(); assert_eq!(sha2.output_len(), 28); assert_eq!(sha2.hash(DUMMY_SEED_512), b"\xb8\x06\x0c\xcc\x82\xd4\x0c\x57\x61\x56\xf7\xca\x03\x33\xe4\x38\x9e\x41\x0d\xf0\x27\xd2\xfb\x8f\x76\x4f\xa6\x03"); @@ -29,7 +28,6 @@ mod hash_factory_tests { assert_eq!(sha2.output_len(), 28); assert_eq!(sha2.hash(DUMMY_SEED_512), b"\xb8\x06\x0c\xcc\x82\xd4\x0c\x57\x61\x56\xf7\xca\x03\x33\xe4\x38\x9e\x41\x0d\xf0\x27\xd2\xfb\x8f\x76\x4f\xa6\x03"); - // SHA256 let sha2 = HashFactory::new("SHA256").unwrap(); assert_eq!(sha2.output_len(), 32); @@ -69,7 +67,6 @@ mod hash_factory_tests { assert_eq!(sha3.output_len(), 28); assert_eq!(sha3.hash(DUMMY_SEED_512), b"\xFE\x51\xC5\xD7\x62\x48\xE1\xE9\xD3\x01\x29\x6A\xE8\xAB\x94\x69\xD2\x86\x34\xB4\xAD\x3E\x9E\x78\xC8\xB0\x9D\x47"); - // SHA3-256 let sha3 = HashFactory::new("SHA3-256").unwrap(); assert_eq!(sha3.output_len(), 32); @@ -102,7 +99,6 @@ mod hash_factory_tests { fn sha3_xof_tests() { assert_eq!(XOFFactory::new("SHAKE128").unwrap().hash_xof(DUMMY_SEED_512, 32), b"\x88\x90\xed\x20\x4d\x22\x89\xe1\x72\xe9\xae\x68\x48\x18\x23\x77\x08\x20\x90\x80\x60\xa4\xdf\x33\x51\xa3\xf1\x84\xeb\xb6\xdd\x0f"); assert_eq!(XOFFactory::new("SHAKE256").unwrap().hash_xof(DUMMY_SEED_512, 32), b"\xa1\xd7\x18\x85\xb0\xa8\x41\xf0\x3d\x1d\xc7\xf2\x73\x8a\x15\xcc\x98\x40\x71\xa1\x7f\xfe\xd5\xec\xac\xb9\xf5\x87\x20\xa4\x73\xbe"); - } #[test] @@ -147,4 +143,4 @@ mod hash_factory_tests { assert_ne!(out, vec![0u8; out.len()]); } } -} \ No newline at end of file +} diff --git a/crypto/factory/tests/kdf_factory_tests.rs b/crypto/factory/tests/kdf_factory_tests.rs index 5530d6b..16e6949 100644 --- a/crypto/factory/tests/kdf_factory_tests.rs +++ b/crypto/factory/tests/kdf_factory_tests.rs @@ -1,19 +1,22 @@ #[cfg(test)] mod kdf_factory_tests { - use bouncycastle_factory::AlgorithmFactory; - use bouncycastle_factory::kdf_factory::{KDFFactory}; - use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyType, KeyMaterialTrait}; + use bouncycastle_core::key_material::{ + KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, + }; use bouncycastle_core::traits::KDF; use bouncycastle_core_test_framework::DUMMY_SEED_512; - use bouncycastle_utils::ct; use bouncycastle_factory as factory; + use bouncycastle_factory::AlgorithmFactory; + use bouncycastle_factory::kdf_factory::KDFFactory; + use bouncycastle_utils::ct; #[test] fn sha3_kdf_tests() { let key_material = KeyMaterial256::from_bytes(&DUMMY_SEED_512[..32]).unwrap(); // SHA3_224 - let derived_key = KDFFactory::new("SHA3-224").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHA3-224").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial256::from_bytes(b"\xbf\xc9\xc1\xe8\x93\x9a\xee\x95\x3c\xa0\xd4\x25\xa2\xf0\xcb\xdd\x2d\x18\x02\x5d\x5d\x6b\x79\x8f\x1c\x81\x50\xb9").unwrap(); // assert_eq!(&derived_key, &expected_key); // assert!(KeyMaterialInternal::equals(&expected_key, derived_key.deref())); @@ -21,52 +24,59 @@ mod kdf_factory_tests { assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); // SHA3_256 - let derived_key = KDFFactory::new("SHA3-256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHA3-256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial256::from_bytes(b"\x05\x0a\x48\x73\x3b\xd5\xc2\x75\x6b\xa9\x5c\x58\x28\xcc\x83\xee\x16\xfa\xbc\xd3\xc0\x86\x88\x5b\x77\x44\xf8\x4a\x0f\x9e\x0d\x94").unwrap(); // assert_eq!(&derived_key, &expected_key); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); // SHA3_384 - let derived_key = KDFFactory::new("SHA3-384").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHA3-384").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial512::from_bytes(b"\xe0\x86\xa2\xb6\xa6\x9b\xb6\xfa\xe3\x7c\xaa\x70\x73\x57\x23\xe7\xcc\x8a\xe2\x18\x37\x88\xfb\xb4\xa5\xf1\xcc\xac\xd8\x32\x26\x85\x2c\xa6\xfa\xff\x50\x3e\x12\xff\x95\x42\x3f\x94\xf8\x72\xdd\xa3").unwrap(); // assert_eq!(&derived_key, &expected_key); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); // SHA3_512 - let derived_key = KDFFactory::new("SHA3-512").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHA3-512").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial512::from_bytes(b"\xcb\xd3\xf6\xee\xba\x67\x6b\x21\xe0\xf2\xc4\x75\x22\x29\x24\x82\xfd\x83\x0f\x33\x0c\x1d\x84\xa7\x94\xbb\x94\x72\x8b\x2d\x93\xfe\xbe\x4c\x18\xea\xe5\xa7\xe0\x17\xe3\x5f\xa0\x90\xde\x24\x26\x2e\x70\x95\x1a\xd1\xd7\xdf\xb3\xa8\xc9\x6d\x11\x34\xfb\x18\x79\xf2").unwrap(); // assert_eq!(&derived_key, &expected_key); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); // SHAKE128 - let derived_key = KDFFactory::new("SHAKE128").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHAKE128").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial512::from_bytes(b"\x06\x6a\x36\x1d\xc6\x75\xf8\x56\xce\xcd\xc0\x2b\x25\x21\x8a\x10\xce\xc0\xce\xcf\x79\x85\x9e\xc0\xfe\xc3\xd4\x09\xe5\x84\x7a\x92").unwrap(); // assert_eq!(&derived_key, &expected_key); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); // SHAKE256 - let derived_key = KDFFactory::new("SHAKE256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("SHAKE256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial512::from_bytes(b"\x69\xf0\x7c\x88\x40\xce\x80\x02\x4d\xb3\x09\x39\x88\x2c\x3d\x5b\xbc\x9c\x98\xb3\xe3\x1e\x45\x13\xeb\xd2\xca\x9b\x45\x03\xcd\xd3\xc9\xc9\x07\x42\x45\x2c\x71\x73\xd4\xa7\x5a\xc4\x91\x63\xe1\x4e\xe0\xcc\x24\xef\x70\x35\xb2\x72\xd1\x9a\x7a\xf1\x09\x9b\x33\x3f").unwrap(); // assert_eq!(&derived_key, &expected_key); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); } - + #[test] fn hkdf_tests() { /* HKDF-SHA256 */ // Note: this value is not checked against any external reference implementation, // I just hard-coded the value to make sure it stays consistent. - let key_material = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); - let derived_key = KDFFactory::new("HKDF-SHA256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let key_material = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let derived_key = + KDFFactory::new("HKDF-SHA256").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial256::from_bytes(b"\x37\xad\x29\x10\x9f\x43\x26\x52\x87\x80\x4b\x67\x4e\x26\x53\xd0\xa5\x13\x71\x89\x07\xf9\x7f\xca\x97\xc9\x5b\xde\xd8\x10\x4b\xbf").unwrap(); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); - /* HKDF-SHA512 */ // Note: this value is not checked against any external reference implementation, // I just hard-coded the value to make sure it stays consistent. let key_material = KeyMaterial512::from_bytes(&DUMMY_SEED_512[..64]).unwrap(); - let derived_key = KDFFactory::new("HKDF-SHA512").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); + let derived_key = + KDFFactory::new("HKDF-SHA512").unwrap().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial512::from_bytes(b"\x8f\x5a\x29\x79\xfe\x16\x4d\x3a\x01\x72\x02\x32\x6c\x61\x97\xae\xa2\x58\x56\x3d\x90\x9b\x01\x20\x12\x1c\x37\x22\x6c\xb3\xd3\x68\xf4\x31\xf9\x79\x9d\x33\x8c\xe3\x0e\xfc\x5f\x41\xaf\xfc\x3d\x38\x54\x44\xa0\x65\xae\x80\x78\x60\x59\x45\x79\x50\xa1\xe6\x5e\x57").unwrap(); assert!(ct::ct_eq_bytes(derived_key.ref_to_bytes(), &expected_key.ref_to_bytes())); } @@ -87,14 +97,15 @@ mod kdf_factory_tests { let _ = KDFFactory::new("Default128Bit").unwrap().derive_key(&key, &[0u8; 0]).unwrap(); - let _ = KDFFactory::new(factory::DEFAULT_128_BIT).unwrap().derive_key(&key, &[0u8; 0]).unwrap(); - + let _ = + KDFFactory::new(factory::DEFAULT_128_BIT).unwrap().derive_key(&key, &[0u8; 0]).unwrap(); // All the ways to get "default_256_bit" let _ = KDFFactory::default_256_bit().derive_key(&key, &[0u8; 0]).unwrap(); let _ = KDFFactory::new("Default256Bit").unwrap().derive_key(&key, &[0u8; 0]).unwrap(); - let _ = KDFFactory::new(factory::DEFAULT_256_BIT).unwrap().derive_key(&key, &[0u8; 0]).unwrap(); + let _ = + KDFFactory::new(factory::DEFAULT_256_BIT).unwrap().derive_key(&key, &[0u8; 0]).unwrap(); } -} \ No newline at end of file +} diff --git a/crypto/factory/tests/mac_factory_tests.rs b/crypto/factory/tests/mac_factory_tests.rs index d4c04da..912a758 100644 --- a/crypto/factory/tests/mac_factory_tests.rs +++ b/crypto/factory/tests/mac_factory_tests.rs @@ -1,9 +1,9 @@ #[cfg(test)] mod hash_factory_tests { - use bouncycastle_hex as hex; - use bouncycastle_factory::mac_factory::{MACFactory}; - use bouncycastle_core::traits::{MAC}; use bouncycastle_core::key_material::{KeyMaterial, KeyType}; + use bouncycastle_core::traits::MAC; + use bouncycastle_factory::mac_factory::MACFactory; + use bouncycastle_hex as hex; mod sha3_tests { use super::*; @@ -14,7 +14,8 @@ mod hash_factory_tests { let key = KeyMaterial::<32>::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); let hmac = MACFactory::new("HMAC-SHA224", &key).unwrap(); assert!(hmac.verify( b"Hi There", @@ -24,4 +25,4 @@ mod hash_factory_tests { // TODO: at least one test for each type } } -} \ No newline at end of file +} diff --git a/crypto/factory/tests/rng_factory_tests.rs b/crypto/factory/tests/rng_factory_tests.rs index eb7361d..6e5d253 100644 --- a/crypto/factory/tests/rng_factory_tests.rs +++ b/crypto/factory/tests/rng_factory_tests.rs @@ -1,8 +1,8 @@ #[cfg(test)] mod tests { - use bouncycastle_core::traits::{SecurityStrength, RNG}; - use bouncycastle_factory::AlgorithmFactory; + use bouncycastle_core::traits::{RNG, SecurityStrength}; use bouncycastle_factory as factory; + use bouncycastle_factory::AlgorithmFactory; #[test] fn test_defaults() { @@ -19,7 +19,6 @@ mod tests { let out = rng.next_bytes(10).unwrap(); assert_ne!(out, &[0u8; 10],); - // All the ways to get "default_128_bit" let mut rng = factory::rng_factory::RNGFactory::default_128_bit(); assert_eq!(rng.security_strength(), SecurityStrength::_128bit); @@ -36,7 +35,6 @@ mod tests { let out = rng.next_bytes(10).unwrap(); assert_ne!(out, &[0u8; 10],); - // All the ways to get "default_256_bit" let mut rng = factory::rng_factory::RNGFactory::default_256_bit(); assert_eq!(rng.security_strength(), SecurityStrength::_256bit); @@ -53,4 +51,4 @@ mod tests { let out = rng.next_bytes(10).unwrap(); assert_ne!(out, &[0u8; 10],); } -} \ No newline at end of file +} diff --git a/crypto/factory/tests/xof_factory_tests.rs b/crypto/factory/tests/xof_factory_tests.rs index 6ca613d..7e414f9 100644 --- a/crypto/factory/tests/xof_factory_tests.rs +++ b/crypto/factory/tests/xof_factory_tests.rs @@ -1,4 +1,4 @@ #[cfg(test)] mod tests { // todo -} \ No newline at end of file +} diff --git a/crypto/hex/Cargo.toml b/crypto/hex/Cargo.toml index f2514e7..94b3df5 100644 --- a/crypto/hex/Cargo.toml +++ b/crypto/hex/Cargo.toml @@ -3,6 +3,12 @@ name = "bouncycastle-hex" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the `String`-returning `encode` and `Vec`-returning `decode`. The allocation-free +# `encode_out`/`decode_out` (fill a caller `&mut [u8]`) work in `no_std` without it. On by default. +default = ["alloc"] +alloc = [] + [dependencies] bouncycastle-utils.workspace = true diff --git a/crypto/hex/benches/hex_benches.rs b/crypto/hex/benches/hex_benches.rs index ae7b369..6cd85a2 100644 --- a/crypto/hex/benches/hex_benches.rs +++ b/crypto/hex/benches/hex_benches.rs @@ -1,5 +1,5 @@ -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use bouncycastle_hex as hex; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use std::hint::black_box; fn bench_hex_encode(c: &mut Criterion) { diff --git a/crypto/hex/src/lib.rs b/crypto/hex/src/lib.rs index 2e71988..fc22660 100644 --- a/crypto/hex/src/lib.rs +++ b/crypto/hex/src/lib.rs @@ -20,8 +20,16 @@ //! //! The decoder ignores whitespace and "\x". +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] +// `encode` (returns `String`) and `decode` (returns `Vec`) live behind the default-on `alloc` +// feature. `no_std` users should use `encode_out` / `decode_out`, which fill a caller `&mut [u8]`. +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "alloc")] +use alloc::{string::String, vec, vec::Vec}; + use bouncycastle_utils::ct::Condition; #[derive(Debug)] @@ -32,6 +40,9 @@ pub enum HexError { } /// One-shot encode from bytes to a hex-encoded string using a constant-time implementation. +/// +/// `no_std` alternative: [encode_out], which writes into a caller-provided `&mut [u8]`. +#[cfg(feature = "alloc")] pub fn encode>(input: T) -> String { let mut out = vec![0u8; input.as_ref().len() * 2]; encode_out(input.as_ref(), &mut out).unwrap(); @@ -47,6 +58,8 @@ pub fn encode_out>(input: T, out: &mut [u8]) -> Result> 4); out[2 * i + 1] = ct_word_to_hex(inref[i] & 0x0F); @@ -74,6 +87,9 @@ pub fn encode_out>(input: T, out: &mut [u8]) -> Result>(input: T) -> Result, HexError> { let inref = input.as_ref(); let mut out: Vec = vec![0u8; inref.len() / 2]; @@ -90,6 +106,8 @@ pub fn decode_out>(input: T, out: &mut [u8]) -> Result`-returning `derive_key`/`derive_key_from_multiple`. +# Forwarded to every internal dependency to keep the `alloc` gates consistent across the graph. +# On by default; disable with `default-features = false` for `no_std` use. +default = ["alloc"] +alloc = [ + "bouncycastle-core/alloc", + "bouncycastle-hmac/alloc", + "bouncycastle-sha2/alloc", +] + [dependencies] bouncycastle-core.workspace = true bouncycastle-hmac.workspace = true diff --git a/crypto/hkdf/benches/hkdf_benches.rs b/crypto/hkdf/benches/hkdf_benches.rs index 7c0134b..72e3d77 100644 --- a/crypto/hkdf/benches/hkdf_benches.rs +++ b/crypto/hkdf/benches/hkdf_benches.rs @@ -1,9 +1,11 @@ -use std::hint::black_box; -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; -use bouncycastle_rng as rng; -use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait}; +use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, +}; use bouncycastle_core::traits::RNG; use bouncycastle_hkdf::{HKDF_SHA256, HKDF_SHA512}; +use bouncycastle_rng as rng; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_hkdf_sha256(c: &mut Criterion) { let mut data_block = [0_u8; 1024]; @@ -57,11 +59,12 @@ fn bench_hkdf_sha256(c: &mut Criterion) { group.finish(); let mut group = c.benchmark_group("HKDF_SHA256::expand_out max output size (255*HashLen)"); - group.throughput(Throughput::Bytes(255*32u64)); - group.bench_function(format!("{} bytes of output key material", 255*32u64), |b| { + group.throughput(Throughput::Bytes(255 * 32u64)); + group.bench_function(format!("{} bytes of output key material", 255 * 32u64), |b| { let mut output_key = KeyMaterial::<8160>::new(); b.iter(|| { - HKDF_SHA256::extract_and_expand_out(&key, &key, &data_block, 255*32, &mut output_key).unwrap(); + HKDF_SHA256::extract_and_expand_out(&key, &key, &data_block, 255 * 32, &mut output_key) + .unwrap(); black_box(&output_key); }); }); @@ -120,18 +123,17 @@ fn bench_hkdf_sha512(c: &mut Criterion) { group.finish(); let mut group = c.benchmark_group("HKDF_SHA512::expand_out max output size (255*HashLen)"); - group.throughput(Throughput::Bytes(255*64u64)); - group.bench_function(format!("{} bytes of output key material", 255*64u64), |b| { + group.throughput(Throughput::Bytes(255 * 64u64)); + group.bench_function(format!("{} bytes of output key material", 255 * 64u64), |b| { let mut output_key = KeyMaterial::<16320>::new(); b.iter(|| { - HKDF_SHA512::extract_and_expand_out(&key, &key, &data_block, 255*64, &mut output_key).unwrap(); + HKDF_SHA512::extract_and_expand_out(&key, &key, &data_block, 255 * 64, &mut output_key) + .unwrap(); black_box(&output_key); }); }); group.finish(); } - - criterion_group!(benches, bench_hkdf_sha256, bench_hkdf_sha512); criterion_main!(benches); diff --git a/crypto/hkdf/src/lib.rs b/crypto/hkdf/src/lib.rs index e79139c..612f655 100644 --- a/crypto/hkdf/src/lib.rs +++ b/crypto/hkdf/src/lib.rs @@ -144,26 +144,85 @@ //! let mut okm = KeyMaterial::<200>::new(); //! let _bytes_written = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, info, 200, &mut okm).unwrap(); //! ``` +//! +//! # Suspending and resuming execution +//! +//! The *HKDF-Extract* phase supports a streaming API whereby any amount of additional input keying +//! material can be provided either via [HKDF::do_extract_update_key] -- which will +//! credit the entropy of the provided [KeyMaterial] -- or as raw uncredited bytes via +//! [HKDF::do_extract_update_bytes]. +//! +//! As such, The *HKDF-Extract* phase can be suspended to a cache and resumed later via the +//! [SuspendableKeyed] trait. +//! +//! The HKDF algorithm is keyed by a `salt`, which is required twice: once at initialization and again +//! during finalization. Suspension and resumption are supported via the [SuspendableKeyed] trait +//! which requires the caller to store the salt securely and provide it again during resumption. +//! Note that providing a different salt during resumption cannot be detected by the library and +//! would silently produce a different PRK. +//! +//! ```rust +//! use bouncycastle_hkdf::HKDF_SHA256; +//! use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; +//! use bouncycastle_core::traits::SuspendableKeyed; +//! +//! let salt = KeyMaterial256::from_bytes_as_type( +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! KeyType::MACKey).unwrap(); +//! let ikm_part1 = b"input keying material part 1"; +//! let ikm_part2 = b" ...and part 2"; +//! +//! let mut hkdf = HKDF_SHA256::new(); +//! hkdf.do_extract_init(&salt).unwrap(); +//! hkdf.do_extract_update_bytes(ikm_part1).unwrap(); +//! +//! // suspend the in-progress extract (the salt is NOT included in the serialized state) +//! let serialized_state = hkdf.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! // ... later, possibly on another host: resume from the serialized state by re-supplying +//! // the same salt (make sure you store it securely!). +//! let mut hkdf = HKDF_SHA256::from_suspended(serialized_state, &salt).unwrap(); +//! hkdf.do_extract_update_bytes(ikm_part2).unwrap(); +//! let _prk = hkdf.do_extract_final().unwrap(); +//! ``` - +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] -use std::marker::PhantomData; -use bouncycastle_core::errors::{KDFError, MACError}; -use bouncycastle_core::key_material::{KeyMaterial0, KeyMaterial512, KeyMaterial, KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Hash, KDF, MAC, HashAlgParams, SecurityStrength}; -use bouncycastle_hmac::HMAC; +// The `Box`-returning `derive_key` / `derive_key_from_multiple` live behind the +// default-on `alloc` feature. `no_std` users should use the `derive_key_out` / `derive_key_from_multiple_out` +// APIs that fill a caller-provided `KeyMaterial` instead. +#[cfg(feature = "alloc")] +extern crate alloc; + +use bouncycastle_core::errors::{KDFError, KeyMaterialError, MACError, SuspendableError}; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterial, KeyMaterial0, KeyMaterialTrait, KeyType}; +// Only used by the alloc-gated `derive_key` / `derive_key_from_multiple`. +#[cfg(feature = "alloc")] +use bouncycastle_core::key_material::KeyMaterial512; +use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; +use bouncycastle_core::traits::{ + Hash, HashAlgParams, KDF, MAC, SecurityStrength, SuspendableKeyed, +}; +use bouncycastle_hmac::{HMAC, SUSPENDED_HMAC_SHA256_STATE_LEN, SUSPENDED_HMAC_SHA512_STATE_LEN}; use bouncycastle_sha2::{SHA256, SHA512}; use bouncycastle_utils::{max, min}; +use core::marker::PhantomData; +#[cfg(feature = "alloc")] +use alloc::boxed::Box; // Imports needed only for docs #[allow(unused_imports)] use bouncycastle_core::traits::XOF; // end doc-only imports - /*** Constants ***/ -// Slightly hacky, but set this to accomodate the underlying hash primitive with the largest output size. +// Slightly hacky, but set this to accommodate the underlying hash primitive with the largest output size. // Would be better to somehow pull that at compile time from H, but I'm not sure how to do that. const HMAC_BLOCK_LEN: usize = 64; @@ -172,17 +231,18 @@ const HMAC_BLOCK_LEN: usize = 64; pub const HKDF_SHA256_NAME: &str = "HKDF-SHA256"; pub const HKDF_SHA512_NAME: &str = "HKDF-SHA512"; - /*** Types ***/ #[allow(non_camel_case_types)] pub type HKDF_SHA256 = HKDF; #[allow(non_camel_case_types)] pub type HKDF_SHA512 = HKDF; +#[derive(Clone)] pub struct HKDF { - hmac: Option>, // Optional because we can't construct an HMAC until they give us a key - // to initialize it with. - // None should correspond to a state of Uninitialized. + // Optional because we can't construct an HMAC until they give us a key + // to initialize it with. + // None should correspond to a state of Uninitialized. + hmac: Option>, entropy: HkdfEntropyTracker, state: HkdfStates, } @@ -190,18 +250,34 @@ pub struct HKDF { // Note: does not need to impl Drop because HKDF itself does not hold any sensitive state data. #[derive(Clone, Debug, PartialOrd, PartialEq)] +#[repr(u8)] enum HkdfStates { /// waiting for salt - Uninitialized, + Uninitialized = 0, /// Salt set, waiting for IKMs or do_final - Initialized, + Initialized = 1, /// [HKDF::do_extract_update_key] has been called, after which no more credited IKMs can be given. /// This is in conformance with NIST SP 800-133 which requires all keys to come before other inputs. - TakingAdditionalInfo, + TakingAdditionalInfo = 2, +} + +impl TryFrom for HkdfStates { + type Error = SuspendableError; + + /// Inverse of `self as u8`; rejects unrecognized discriminants with [SuspendableError::InvalidData]. + fn try_from(value: u8) -> Result { + Ok(match value { + 0 => Self::Uninitialized, + 1 => Self::Initialized, + 2 => Self::TakingAdditionalInfo, + _ => return Err(SuspendableError::InvalidData), + }) + } } +#[derive(Clone)] struct HkdfEntropyTracker { _phantomhash: PhantomData, entropy: usize, @@ -209,7 +285,9 @@ struct HkdfEntropyTracker { } impl HkdfEntropyTracker { - fn new() -> Self { Self { _phantomhash: PhantomData, entropy: 0, security_strength: SecurityStrength::None } } + fn new() -> Self { + Self { _phantomhash: PhantomData, entropy: 0, security_strength: SecurityStrength::None } + } /// Takes in a KeyMaterial that is being mixed and figures out how much entropy to credit. /// Returns the amount of entropy credited. @@ -217,7 +295,8 @@ impl HkdfEntropyTracker { let additional_entropy = if key.is_full_entropy() { key.key_len() } else { 0 }; self.entropy += additional_entropy; self.security_strength = max(&self.security_strength, &key.security_strength()).clone(); - self.security_strength = min(&self.security_strength, &SecurityStrength::from_bytes(H::OUTPUT_LEN/2)).clone(); + self.security_strength = + min(&self.security_strength, &SecurityStrength::from_bytes(H::OUTPUT_LEN / 2)).clone(); additional_entropy } @@ -233,11 +312,7 @@ impl HkdfEntropyTracker { /// Either [KeyMaterialTrait::BytesLowEntropy] or [KeyMaterialTrait::BytesFullEntropy] depending on /// whether enough input key material was provided for the internal hash function to have a full block. fn get_output_key_type(&self) -> KeyType { - if self.is_fully_seeded() { - KeyType::BytesFullEntropy - } else { - KeyType::BytesLowEntropy - } + if self.is_fully_seeded() { KeyType::CryptographicRandom } else { KeyType::Unknown } } } @@ -247,18 +322,22 @@ fn test_entropy_tracker() { let mut entropy = HkdfEntropyTracker::::new(); assert_eq!(entropy.get_entropy(), 0); - assert_eq!(entropy.get_output_key_type(), KeyType::BytesLowEntropy); + assert_eq!(entropy.get_output_key_type(), KeyType::Unknown); - let key = KeyMaterial512::from_bytes_as_type(b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", KeyType::BytesFullEntropy).unwrap(); + let key = KeyMaterial512::from_bytes_as_type( + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + KeyType::CryptographicRandom, + ) + .unwrap(); entropy.credit_entropy(&key); assert_eq!(entropy.get_entropy(), 16); assert_eq!(entropy.is_fully_seeded(), false); - assert_eq!(entropy.get_output_key_type(), KeyType::BytesLowEntropy); + assert_eq!(entropy.get_output_key_type(), KeyType::Unknown); entropy.credit_entropy(&key); assert_eq!(entropy.get_entropy(), 32); assert_eq!(entropy.is_fully_seeded(), true); - assert_eq!(entropy.get_output_key_type(), KeyType::BytesFullEntropy); + assert_eq!(entropy.get_output_key_type(), KeyType::CryptographicRandom); } impl Default for HKDF { @@ -394,34 +473,39 @@ impl HKDF { let N = L.div_ceil(hash_len) as u8; let mut bytes_written: usize = 0; - okm.allow_hazardous_operations(); - let out: &mut [u8] = okm.mut_ref_to_bytes()?; // Could potentially speed this up by unrolling T(0) and T(1) // We're gonna have to kludge the prk key type to MACKey to make HMAC happy, but we'll set it back to the original value afterwards. - let prk_as_mac_key = KeyMaterial::::from_bytes_as_type(prk.ref_to_bytes(), KeyType::MACKey)?; + let prk_as_mac_key = + KeyMaterial::::from_bytes_as_type(prk.ref_to_bytes(), KeyType::MACKey)?; #[allow(non_snake_case)] let mut T = [0u8; HMAC_BLOCK_LEN]; let mut t_len: usize = 0; let mut i = 1u8; - while i < N { - // todo: might need this to be new_allow_weak_key() - let mut hmac = HMAC::::new(&prk_as_mac_key)?; - hmac.do_update(&T[..t_len]); - hmac.do_update(info); - hmac.do_update(&[i]); - - t_len = hmac.do_final_out(&mut T)?; - debug_assert_eq!(t_len, hash_len); // this will be true for every iteration after T(0) / T(1) - out[bytes_written..bytes_written + t_len].copy_from_slice(&T[..t_len]); - bytes_written += t_len; - i += 1; - } + + key_material::do_hazardous_operations(okm, |okm| { + let out = okm.ref_to_bytes_mut()?; + while i < N { + let mut hmac = HMAC::::new(&prk_as_mac_key) + .map_err(|_| KeyMaterialError::GenericError("HMAC initialization failed"))?; + hmac.do_update(&T[..t_len]); + hmac.do_update(info); + hmac.do_update(&[i]); + + t_len = hmac + .do_final_out(&mut T) + .map_err(|_| KeyMaterialError::GenericError("HMAC finalization failed"))?; + debug_assert_eq!(t_len, hash_len); // this will be true for every iteration after T(0) / T(1) + out[bytes_written..bytes_written + t_len].copy_from_slice(&T[..t_len]); + bytes_written += t_len; + i += 1; + } + Ok(()) + })?; // On the last iteration, we don't take all of the output. let remaining = L - bytes_written; - // todo: might need this to be new_allow_weak_key() let mut hmac = HMAC::::new(&prk_as_mac_key)?; hmac.do_update(&T[..t_len]); hmac.do_update(info); @@ -429,23 +513,32 @@ impl HKDF { t_len = hmac.do_final_out(&mut T[..remaining])?; debug_assert_eq!(t_len, remaining); // this will be true for every iteration after T(0) / T(1) - out[bytes_written..bytes_written + t_len].copy_from_slice(&T[..t_len]); + + key_material::do_hazardous_operations(okm, |okm| { + let out = okm.ref_to_bytes_mut()?; + out[bytes_written..bytes_written + t_len].copy_from_slice(&T[..t_len]); + Ok(()) + })?; bytes_written += t_len; // set the KeyType of the output // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. - if prk.key_type() == KeyType::Zeroized { - okm.set_key_type(KeyType::BytesLowEntropy)?; - } else { - okm.set_key_type(prk.key_type().clone())?; - } - okm.set_key_len(bytes_written)?; - if okm.key_type() <= KeyType::BytesLowEntropy { - okm.set_security_strength(SecurityStrength::None)?; - } else { - okm.set_security_strength(min(&SecurityStrength::from_bytes(okm.key_len()), &entropy.security_strength).clone())?; - } - okm.drop_hazardous_operations(); + key_material::do_hazardous_operations(okm, |okm| { + if prk.key_type() == KeyType::Zeroized { + okm.set_key_type(KeyType::Unknown)?; + } else { + okm.set_key_type(prk.key_type().clone())?; + } + okm.set_key_len(bytes_written)?; + if okm.key_type() <= KeyType::Unknown { + okm.set_security_strength(SecurityStrength::None) + } else { + okm.set_security_strength( + min(&SecurityStrength::from_bytes(okm.key_len()), &entropy.security_strength) + .clone(), + ) + } + })?; Ok(bytes_written) } @@ -471,8 +564,7 @@ impl HKDF { /// The output KeyMaterial will be of fixed size, with a capacity large enough to cover any /// underlying hash function, but the actual key length will be appropriate to the underlying hash function. /// - /// Salt is optional, which is indicated by providing an uninitialized KeyMaterial object of length zero, - /// the capacity is irrelevant, so KeyMateriol256::new() or KeyMaterial_internal::<0>::new() would both count as an absent salt. + /// Salt is optional; to omit it, provide a KeyMaterial0, which will cause HKDF to use the default all-zero salt. /// /// Returns the number of bits of entropy credited to this input key material. pub fn do_extract_init(&mut self, salt: &impl KeyMaterialTrait) -> Result { @@ -497,7 +589,10 @@ impl HKDF { /// In particular, this function may be called multiple times to add more than one IKM. /// /// Returns the number of bits of entropy credited to this input key material. - pub fn do_extract_update_key(&mut self, ikm: &impl KeyMaterialTrait) -> Result { + pub fn do_extract_update_key( + &mut self, + ikm: &impl KeyMaterialTrait, + ) -> Result { if self.state == HkdfStates::Uninitialized { return Err(MACError::InvalidState( "Must call do_extract_init() before calling do_extract_update_key()", @@ -559,16 +654,27 @@ impl HKDF { let output_key_type = self.entropy.get_output_key_type(); // need to do this above self.hmac.do_final_out, which will consume self. - okm.allow_hazardous_operations(); // doing it here to get mut_ref_to_bytes - let bytes_written = self.hmac.unwrap().do_final_out(&mut okm.mut_ref_to_bytes().unwrap())?; - okm.set_key_len(bytes_written)?; - okm.set_key_type(output_key_type)?; - if output_key_type <= KeyType::BytesLowEntropy { - okm.set_security_strength(SecurityStrength::None)?; - } else { - okm.set_security_strength(min(&SecurityStrength::from_bytes(okm.key_len()), &self.entropy.security_strength).clone())?; - } - okm.drop_hazardous_operations(); + let mut bytes_written = 0; + key_material::do_hazardous_operations(okm, |okm| { + bytes_written = self + .hmac + .unwrap() + .do_final_out(&mut okm.ref_to_bytes_mut()?) + .map_err(|_| KeyMaterialError::GenericError("HMAC do_final_out failed"))?; + okm.set_key_len(bytes_written)?; + okm.set_key_type(output_key_type)?; + if output_key_type <= KeyType::Unknown { + okm.set_security_strength(SecurityStrength::None) + } else { + okm.set_security_strength( + min( + &SecurityStrength::from_bytes(okm.key_len()), + &self.entropy.security_strength, + ) + .clone(), + ) + } + })?; Ok(bytes_written) } } @@ -585,6 +691,7 @@ impl HKDF { impl KDF for HKDF { /// This invokes [HKDF::extract_and_expand_out] with a zero salt and using the provided key as ikm. /// This provides a fixed-length output, which may be truncated as needed. + #[cfg(feature = "alloc")] fn derive_key( self, key: &impl KeyMaterialTrait, @@ -592,7 +699,7 @@ impl KDF for HKDF { ) -> Result, KDFError> { let mut output_key = KeyMaterial512::new(); _ = self.derive_key_out(key, additional_input, &mut output_key)?; - output_key.truncate(H::OUTPUT_LEN)?; + output_key.set_key_len(H::OUTPUT_LEN)?; Ok(Box::new(output_key)) } @@ -604,7 +711,13 @@ impl KDF for HKDF { additional_input: &[u8], output_key: &mut impl KeyMaterialTrait, ) -> Result { - let bytes_written = HKDF::::extract_and_expand_out(&KeyMaterial::<0>::new(), key, additional_input, output_key.capacity(), output_key)?; + let bytes_written = HKDF::::extract_and_expand_out( + &KeyMaterial::<0>::new(), + key, + additional_input, + output_key.capacity(), + output_key, + )?; Ok(bytes_written) } @@ -617,6 +730,7 @@ impl KDF for HKDF { /// Therefore, derive_key_from_multiple(&[KeyMaterial0::new(), &key], &info) is equivalent to derive_key(&key, &info). /// /// This provides a fixed-length output, which may be truncated as needed. + #[cfg(feature = "alloc")] fn derive_key_from_multiple( self, keys: &[&impl KeyMaterialTrait], @@ -624,11 +738,10 @@ impl KDF for HKDF { ) -> Result, KDFError> { let mut output_key = KeyMaterial512::new(); _ = self.derive_key_from_multiple_out(keys, additional_input, &mut output_key)?; - output_key.truncate(*min(&output_key.key_len(), &H::OUTPUT_LEN))?; + output_key.set_key_len(*min(&output_key.key_len(), &H::OUTPUT_LEN))?; Ok(Box::new(output_key)) } - /// This behaves the same as [KDF::derive_key_from_multiple], except that it fills the provided /// [KeyMaterialTrait] object in place of exposing a Length parameter. fn derive_key_from_multiple_out( @@ -655,12 +768,19 @@ impl KDF for HKDF { } let mut prk = KeyMaterial::::new(); _ = hkdf.do_extract_final_out(&mut prk)?; - let bytes_written = HKDF::::expand_out(&prk, additional_input, output_key.capacity(), output_key)?; - - output_key.allow_hazardous_operations(); - output_key.set_key_type(entropy.get_output_key_type())?; - output_key.set_security_strength(min(&SecurityStrength::from_bytes(output_key.key_len()), &entropy.security_strength).clone())?; - output_key.drop_hazardous_operations(); + let bytes_written = + HKDF::::expand_out(&prk, additional_input, output_key.capacity(), output_key)?; + + key_material::do_hazardous_operations(output_key, |output_key| { + output_key.set_key_type(entropy.get_output_key_type())?; + output_key.set_security_strength( + min( + &SecurityStrength::from_bytes(output_key.key_len()), + &entropy.security_strength, + ) + .clone(), + ) + })?; Ok(bytes_written) } @@ -669,3 +789,124 @@ impl KDF for HKDF { H::default().max_security_strength() } } + +/// Length in bytes of the serialized state of [HKDF_SHA256]. +pub const SUSPENDED_HKDF_SHA256_STATE_LEN: usize = SUSPENDED_HMAC_SHA256_STATE_LEN + 14; +/// Length in bytes of the serialized state of [HKDF_SHA512]. +pub const SUSPENDED_HKDF_SHA512_STATE_LEN: usize = SUSPENDED_HMAC_SHA512_STATE_LEN + 14; + +/// HKDF is *keyed by its salt* -- the salt keys the extract-phase HMAC -- so it implements +/// [SuspendableKeyed] (not [SerializableState]). An in-progress +/// extract operation can be suspended and resumed, but the salt is NOT written into the serialized +/// state and must be re-supplied to [SuspendableKeyed::from_serialized_state]. +/// +/// Only the extract phase carries resumable state (expand is a one-shot static operation). As with +/// HMAC, resuming with the wrong salt cannot be detected and will silently produce a wrong PRK. +/// +/// Serialized layout: HKDF writes its own 3-byte library version header first and checks it before +/// parsing anything else. This matters because the inner HMAC blob (which carries its own header) is +/// absent before extract is initialized -- without HKDF's own header, a pre-init state would have no +/// version tag at all. Using `B` = the inner HMAC blob length: +/// [0 .. 3) HKDF library version header (checked on resume) +/// [3] inner-HMAC present flag (0 = extract not yet initialized) +/// [4 .. 4 + B) the inner HMAC's SuspendableKeyed blob (salt excluded); zeroed when absent +/// [4 + B] state-machine tag (see `HkdfStates`) +/// [5 + B .. 13 + B) entropy counter (usize serialized as u64, little-endian) +/// [13 + B] accumulated security strength (1-byte tag) +/// So the total per HKDF variant is the 3-byte version header + 11 bytes of HKDF bookkeeping +/// (present flag, state tag, entropy counter, security strength) + the inner HMAC's blob = `B + 14`. +macro_rules! impl_suspendable_keyed_state_for_hkdf { + // $hash: the concrete hash; $serialized_hmac_len: the inner HMAC's serialized-state length for that + // hash; $serialized_hkdf_len: the full HKDF serialized-state length (= 3 + 11 + $serialized_hmac_len). + ($hash:ty, $serialized_hmac_len:expr, $serialized_hkdf_len:expr) => { + impl SuspendableKeyed<{ $serialized_hkdf_len }> for HKDF<$hash> { + // HMAC accepts any key material, so the key type is the trait object `dyn KeyMaterialTrait` + // rather than a single concrete key type. The key is only used (by reference) to reload the key + // bytes at from_serialized_state, so dynamic dispatch here is negligible. + type Key = dyn KeyMaterialTrait; + + fn suspend(self) -> [u8; $serialized_hkdf_len] { + debug_assert_eq!($serialized_hkdf_len, $serialized_hmac_len + 14); + let mut state = [0u8; $serialized_hkdf_len]; + + // HKDF's own library version header comes first: the inner HMAC blob is absent before + // extract is initialized, so we can't rely on its header being present. + add_lib_ver(&mut state); + + // The present flag, then (when present) the inner salt-keyed HMAC blob right after it. + if let Some(hmac) = self.hmac { + state[3] = 1; // present flag + state[4..4 + $serialized_hmac_len].copy_from_slice(&hmac.suspend()); + } + // else None: + // the presence flag = 0 + // the content = [u8; 0] + // which is how it already is, so nothing to do. + + state[4 + $serialized_hmac_len] = self.state as u8; + state[5 + $serialized_hmac_len..13 + $serialized_hmac_len] + .copy_from_slice(&(self.entropy.entropy as u64).to_le_bytes()); + state[13 + $serialized_hmac_len] = self.entropy.security_strength as u8; + + state + } + + fn from_suspended( + state: [u8; $serialized_hkdf_len], + salt: &Self::Key, + ) -> Result { + // Check HKDF's own version header before parsing anything else. + check_lib_ver(&state, None)?; + + // Rebuild the salt-keyed HMAC (when present) by re-supplying the salt. + let hmac = match state[3] { + 0 => None, + 1 => Some(HMAC::<$hash>::from_suspended( + state[4..4 + $serialized_hmac_len].try_into().unwrap(), + salt, + )?), + _ => return Err(SuspendableError::InvalidData), + }; + + let hkdf_state = HkdfStates::try_from(state[4 + $serialized_hmac_len])?; + + // check that the hkdf_state aligns with the presence of an hmac + if + // an hmac object should not be present in the init state. + (hmac.is_some() && hkdf_state == HkdfStates::Uninitialized) || + // any other state must have an hmac object. + (hmac.is_none() && hkdf_state != HkdfStates::Uninitialized) + { + return Err(SuspendableError::InvalidData); + } + + let entropy = u64::from_le_bytes( + state[5 + $serialized_hmac_len..13 + $serialized_hmac_len].try_into().unwrap(), + ) as usize; + let security_strength = + SecurityStrength::try_from(state[13 + $serialized_hmac_len])?; + + Ok(HKDF { + hmac, + entropy: HkdfEntropyTracker { + _phantomhash: PhantomData, + entropy, + security_strength, + }, + state: hkdf_state, + }) + } + } + }; +} + +impl_suspendable_keyed_state_for_hkdf!( + SHA256, + SUSPENDED_HMAC_SHA256_STATE_LEN, + SUSPENDED_HKDF_SHA256_STATE_LEN +); +impl_suspendable_keyed_state_for_hkdf!( + SHA512, + SUSPENDED_HMAC_SHA512_STATE_LEN, + SUSPENDED_HKDF_SHA512_STATE_LEN +); diff --git a/crypto/hkdf/tests/hkdf_tests.rs b/crypto/hkdf/tests/hkdf_tests.rs index e30fccf..fee0444 100644 --- a/crypto/hkdf/tests/hkdf_tests.rs +++ b/crypto/hkdf/tests/hkdf_tests.rs @@ -1,19 +1,24 @@ #[cfg(test)] mod hkdf_tests { - use bouncycastle_core::errors::{KDFError, KeyMaterialError, MACError}; - use bouncycastle_core::key_material::{KeyMaterial0, KeyMaterial128, KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait}; - use bouncycastle_core::traits::{HashAlgParams, SecurityStrength, KDF}; + use bouncycastle_core::errors::{KDFError, KeyMaterialError, MACError, SuspendableError}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial0, KeyMaterial128, KeyMaterial256, KeyMaterial512, + KeyMaterialTrait, KeyType, + }; + use bouncycastle_core::traits::{HashAlgParams, KDF, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; use bouncycastle_core_test_framework::kdf::TestFrameworkKDF; use bouncycastle_hex as hex; use bouncycastle_hkdf::{HKDF, HKDF_SHA256, HKDF_SHA512}; - use bouncycastle_sha2::{SHA256}; + use bouncycastle_sha2::{SHA256, SHA512}; use bouncycastle_utils::ct; #[test] fn test_streaming_apis() { // setup variables - let salt = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); + let salt = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); let ikm = KeyMaterial256::from_bytes(&DUMMY_SEED_512[16..48]).unwrap(); let info = &DUMMY_SEED_512[48..64]; let mut okm = KeyMaterial512::new(); @@ -41,14 +46,23 @@ mod hkdf_tests { let info: &[u8] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\0xF"; let zero_key = KeyMaterial0::new(); - let key1 = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); - let key2 = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[32..64], KeyType::MACKey).unwrap(); - let key3 = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[64..96], KeyType::MACKey).unwrap(); - + let key1 = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let key2 = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[32..64], KeyType::MACKey).unwrap(); + let key3 = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[64..96], KeyType::MACKey).unwrap(); /* test case: 0 input keys (ie empty salt and no ikm's) */ let mut expected_okm = KeyMaterial512::new(); - HKDF_SHA256::extract_and_expand_out(&KeyMaterial256::new(), &KeyMaterial0::new(), info, 32, &mut expected_okm).unwrap(); + HKDF_SHA256::extract_and_expand_out( + &KeyMaterial256::new(), + &KeyMaterial0::new(), + info, + 32, + &mut expected_okm, + ) + .unwrap(); let okm1 = HKDF_SHA256::new().derive_key(&zero_key, info).unwrap(); assert_eq!(okm1.ref_to_bytes(), expected_okm.ref_to_bytes()); @@ -58,13 +72,18 @@ mod hkdf_tests { let okm2 = HKDF_SHA256::new().derive_key_from_multiple(&keys, info).unwrap(); assert!(ct::ct_eq_bytes(okm2.ref_to_bytes(), expected_okm.ref_to_bytes())); - - /* test case: 1 input ikm key (ie empty salt) */ // derive_key_from_multiple(&[&KeyMaterial0, &key) is equivalent to derive_key(&key) above // which is equivalent to extract_and_expand((&KeyMaterial0::new(), &key, info) let mut expected_okm = KeyMaterial512::new(); - HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &key1, info, 32, &mut expected_okm).unwrap(); + HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &key1, + info, + 32, + &mut expected_okm, + ) + .unwrap(); let okm1 = HKDF_SHA256::new().derive_key(&key1, info).unwrap(); assert!(ct::ct_eq_bytes(okm1.ref_to_bytes(), expected_okm.ref_to_bytes())); @@ -73,7 +92,6 @@ mod hkdf_tests { let okm2 = HKDF_SHA256::new().derive_key_from_multiple(&keys, info).unwrap(); assert!(ct::ct_eq_bytes(okm2.ref_to_bytes(), expected_okm.ref_to_bytes())); - /* test case: 1 input keys (salt and no ikm's) */ let mut expected_okm = KeyMaterial512::new(); HKDF_SHA256::extract_and_expand_out(&key1, &zero_key, info, 32, &mut expected_okm).unwrap(); @@ -84,7 +102,6 @@ mod hkdf_tests { let okm2 = HKDF_SHA256::new().derive_key_from_multiple(&keys, info).unwrap(); assert!(ct::ct_eq_bytes(okm2.ref_to_bytes(), expected_okm.ref_to_bytes())); - /* test case: 2 input keys (ie salt and one ikm) */ let mut expected_okm = KeyMaterial512::new(); HKDF_SHA256::extract_and_expand_out(&key1, &key2, info, 32, &mut expected_okm).unwrap(); @@ -95,9 +112,9 @@ mod hkdf_tests { let okm2 = HKDF_SHA256::new().derive_key_from_multiple(&keys, info).unwrap(); assert!(ct::ct_eq_bytes(okm2.ref_to_bytes(), expected_okm.ref_to_bytes())); - /* test case: 3 input keys (ie salt and two ikm's) */ - let key23 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[32..96], KeyType::MACKey).unwrap(); + let key23 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[32..96], KeyType::MACKey).unwrap(); let mut expected_okm = KeyMaterial512::new(); HKDF_SHA256::extract_and_expand_out(&key1, &key23, info, 32, &mut expected_okm).unwrap(); @@ -110,206 +127,284 @@ mod hkdf_tests { #[test] fn test_entropy_tracking() { - // test the thresholds of HMAC-SHA256 - let key255 = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..31], KeyType::MACKey).unwrap(); - let key256 = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); - let key512 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); + let key255 = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..31], KeyType::MACKey).unwrap(); + let key256 = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let key512 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); let zero_key = KeyMaterial0::new(); // not enough assert_eq!(key255.security_strength(), SecurityStrength::_192bit); let mut okm = HKDF_SHA256::extract(&key255, &zero_key).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); assert_eq!(okm.security_strength(), SecurityStrength::None); _ = HKDF_SHA256::extract_and_expand_out(&key255, &zero_key, &[], 32, &mut okm).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); assert_eq!(okm.security_strength(), SecurityStrength::None); // too much assert_eq!(key512.security_strength(), SecurityStrength::_256bit); let mut okm = HKDF_SHA256::extract(&key512, &zero_key).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); // should get downgraded to match hash alg assert_eq!(okm.security_strength(), SecurityStrength::_128bit); _ = HKDF_SHA256::extract_and_expand_out(&key512, &zero_key, &[], 32, &mut okm).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); assert_eq!(okm.security_strength(), SecurityStrength::_128bit); // just right let mut okm = HKDF_SHA256::extract(&key256, &zero_key).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); _ = HKDF_SHA256::extract_and_expand_out(&key256, &zero_key, &[], 32, &mut okm).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); - + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); // test the thresholds of HMAC-SHA512 - let key511 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..63], KeyType::MACKey).unwrap(); - let key512 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); + let key511 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..63], KeyType::MACKey).unwrap(); + let key512 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); let zero_key = KeyMaterial0::new(); // not enough let mut okm = HKDF_SHA512::extract(&key511, &zero_key).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); _ = HKDF_SHA512::extract_and_expand_out(&key511, &zero_key, &[], 32, &mut okm).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); // just right let mut okm = HKDF_SHA512::extract(&key512, &zero_key).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); _ = HKDF_SHA512::extract_and_expand_out(&key512, &zero_key, &[], 32, &mut okm).unwrap(); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); - - + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); // variable setup - let low_entropy_key = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesLowEntropy).unwrap(); + let low_entropy_key = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::Unknown).unwrap(); let mut okm = KeyMaterial256::new(); - // failure case: should complain if low entropy bytes are provided // only as salt - match HKDF_SHA256::extract_and_expand_out(&low_entropy_key, &KeyMaterial0::new(), &[], 32, &mut okm) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError"); } + match HKDF_SHA256::extract_and_expand_out( + &low_entropy_key, + &KeyMaterial0::new(), + &[], + 32, + &mut okm, + ) { + Ok(_) => { + panic!("Should have thrown a KeyMaterialError"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError"); + } }; let keys = [&low_entropy_key]; match HKDF_SHA256::new().derive_key_from_multiple(&keys, &[]) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError"); } + Ok(_) => { + panic!("Should have thrown a KeyMaterialError"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError"); + } }; - // as both salt and ikm - match HKDF_SHA256::extract_and_expand_out(&low_entropy_key, &low_entropy_key, &[], 32, &mut okm) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError"); } + match HKDF_SHA256::extract_and_expand_out( + &low_entropy_key, + &low_entropy_key, + &[], + 32, + &mut okm, + ) { + Ok(_) => { + panic!("Should have thrown a KeyMaterialError"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError"); + } }; let keys = [&low_entropy_key, &low_entropy_key]; match HKDF_SHA256::new().derive_key_from_multiple(&keys, &[]) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError"); } + Ok(_) => { + panic!("Should have thrown a KeyMaterialError"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError"); + } }; - - // zero-length salt is allowed -- zeroized ikm - _ = HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &KeyMaterial0::new(), &[], 32, &mut okm).unwrap(); + _ = HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &KeyMaterial0::new(), + &[], + 32, + &mut okm, + ) + .unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); HKDF_SHA256::new().derive_key_out(&KeyMaterial0::new(), &[], &mut okm).unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); let keys = [&KeyMaterial0::new(), &KeyMaterial0::new()]; HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm).unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); - + assert_eq!(okm.key_type(), KeyType::Unknown); // zero-length salt is allowed -- low entropy ikm - _ = HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &low_entropy_key, &[], 32, &mut okm).unwrap(); + _ = HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &low_entropy_key, + &[], + 32, + &mut okm, + ) + .unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); HKDF_SHA256::new().derive_key_out(&low_entropy_key, &[], &mut okm).unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); let keys = [&KeyMaterial256::new(), &low_entropy_key]; HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm).unwrap(); // okm should be tracked as LowEntropy - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); - - + assert_eq!(okm.key_type(), KeyType::Unknown); // salt and ikm are full-entropy, but not enough to seed the HKDF, according to FIPS // first, error case; not a MACKey - let salt = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::BytesFullEntropy).unwrap(); - let ikm = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[8..16], KeyType::BytesFullEntropy).unwrap(); + let salt = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::CryptographicRandom) + .unwrap(); + let ikm = KeyMaterial128::from_bytes_as_type( + &DUMMY_SEED_512[8..16], + KeyType::CryptographicRandom, + ) + .unwrap(); match HKDF_SHA256::extract_and_expand_out(&salt, &ikm, &[], 32, &mut okm) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); } + Ok(_) => { + panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); + } }; // derive_key has a different behaviour here, since it hard-codes a zero salt as the HMAC key, which is valid, // it will produce output of Keytype::BytesLowEntropy _ = HKDF_SHA256::new().derive_key_out(&ikm, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); let keys = [&salt, &ikm]; match HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); }, - Err(KDFError::MACError(MACError::KeyMaterialError(KeyMaterialError::InvalidKeyType(_)))) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); } + Ok(_) => { + panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); + } + Err(KDFError::MACError(MACError::KeyMaterialError( + KeyMaterialError::InvalidKeyType(_), + ))) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError that you didn't give it a MACKey"); + } }; - // success case -- insufficient entropy returns KeyType::BytesLowEntropy - let salt = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::MACKey).unwrap(); - let ikm = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[8..16], KeyType::BytesFullEntropy).unwrap(); + let salt = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::MACKey).unwrap(); + let ikm = KeyMaterial128::from_bytes_as_type( + &DUMMY_SEED_512[8..16], + KeyType::CryptographicRandom, + ) + .unwrap(); _ = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, &[], 32, &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); _ = HKDF_SHA256::new().derive_key_out(&salt, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); let keys = [&salt, &ikm]; _ = HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); - - + assert_eq!(okm.key_type(), KeyType::Unknown); // success case -- sufficient entropy returns the highest input key type -- KeyType::BytesFullEntropy // Note that FIPS requires it to be seeded to a full internal block (which is, for example 512 bits for SHA256) // Note: will still return BytesFullEntropy because that one was first in the inputs. - let salt = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); - let ikm = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[32..64], KeyType::BytesFullEntropy).unwrap(); + let salt = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let ikm = KeyMaterial256::from_bytes_as_type( + &DUMMY_SEED_512[32..64], + KeyType::CryptographicRandom, + ) + .unwrap(); _ = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, &[], 32, &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); - let salt1 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); + let salt1 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); _ = HKDF_SHA256::new().derive_key_out(&salt1, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); let keys = [&salt, &ikm]; _ = HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesFullEntropy); - + assert_eq!(okm.key_type(), KeyType::CryptographicRandom); // success case -- insufficient entropy due to key types -- KeyType::BytesLowEntropy // Note: will still return MACKey because that one was first in the inputs. - let salt = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); - let ikm = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[16..32], KeyType::BytesLowEntropy).unwrap(); + let salt = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); + let ikm = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[16..32], KeyType::Unknown).unwrap(); _ = HKDF_SHA256::extract_and_expand_out(&salt, &ikm, &[], 32, &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); + assert_eq!(okm.key_type(), KeyType::Unknown); // no way to test this on derive_out let keys = [&salt, &ikm]; _ = HKDF_SHA256::new().derive_key_from_multiple_out(&keys, &[], &mut okm); - assert_eq!(okm.key_type(), KeyType::BytesLowEntropy); - + assert_eq!(okm.key_type(), KeyType::Unknown); /* get_entropy */ // This requires using the stateful streaming API and check the amount of entropy it tracks after each addition. - let salt16 = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); - let salt64 = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); - let low_entropy_key16 = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesLowEntropy).unwrap(); - let full_entropy_key16 = KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[16..32], KeyType::BytesFullEntropy).unwrap(); + let salt16 = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); + let salt64 = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); + let low_entropy_key16 = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::Unknown).unwrap(); + let full_entropy_key16 = KeyMaterial128::from_bytes_as_type( + &DUMMY_SEED_512[16..32], + KeyType::CryptographicRandom, + ) + .unwrap(); // can't test with a low entropy salt because the salt has to be full entropy or zero. // but can test with a zeroized key @@ -359,21 +454,44 @@ mod hkdf_tests { // success case: large but not over-limit let mut mega_huge_key = KeyMaterial::<8100>::new(); assert!(mega_huge_key.capacity() < 255 * hash_len); - _ = HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &KeyMaterial0::new(), &[], 8100, &mut mega_huge_key).unwrap(); + _ = HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &KeyMaterial0::new(), + &[], + 8100, + &mut mega_huge_key, + ) + .unwrap(); // test exactly the capacity let mut mega_huge_key = KeyMaterial::<8160>::new(); assert_eq!(mega_huge_key.capacity(), 255 * hash_len); - _ = HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &KeyMaterial0::new(), &[], 8160, &mut mega_huge_key).unwrap(); - + _ = HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &KeyMaterial0::new(), + &[], + 8160, + &mut mega_huge_key, + ) + .unwrap(); // failure case let mut mega_huge_key = KeyMaterial::<8192>::new(); assert!(mega_huge_key.capacity() > 255 * hash_len); - match HKDF_SHA256::extract_and_expand_out(&KeyMaterial0::new(), &KeyMaterial0::new(), &[], 8192, &mut mega_huge_key) { - Ok(_) => { panic!("Should have thrown a KeyMaterialError"); }, - Err(KDFError::InvalidLength(_)) => { /* good */ }, - Err(_) => { panic!("Should have thrown a KeyMaterialError"); } + match HKDF_SHA256::extract_and_expand_out( + &KeyMaterial0::new(), + &KeyMaterial0::new(), + &[], + 8192, + &mut mega_huge_key, + ) { + Ok(_) => { + panic!("Should have thrown a KeyMaterialError"); + } + Err(KDFError::InvalidLength(_)) => { /* good */ } + Err(_) => { + panic!("Should have thrown a KeyMaterialError"); + } }; } @@ -416,20 +534,18 @@ mod hkdf_tests { /*** First with the one-shot APIs ::extract() and ::expand_out(). ***/ let mut ikm_key = KeyMaterial::<100>::new(); - ikm_key.allow_hazardous_operations(); - let r = ikm_key.set_bytes_as_type(&hex::decode(ikm).unwrap(), KeyType::BytesFullEntropy); // just for testing, ignore the error about zeroized keys - if r.is_err() { - ikm_key.allow_hazardous_operations(); - ikm_key.set_key_type(KeyType::MACKey).unwrap(); - } + key_material::do_hazardous_operations(&mut ikm_key, |ikm_key| { + // just for testing, ignore the error about zeroized keys + ikm_key.set_bytes_as_type(&hex::decode(ikm).unwrap(), KeyType::CryptographicRandom) + }) + .unwrap(); let mut salt_key = KeyMaterial::<100>::new(); - salt_key.allow_hazardous_operations(); - let r = salt_key.set_bytes_as_type(&hex::decode(salt).unwrap(), KeyType::MACKey); // just for testing, ignore the error about zeroized keys - if r.is_err() { - salt_key.allow_hazardous_operations(); - ikm_key.set_key_type(KeyType::MACKey).unwrap(); - } + key_material::do_hazardous_operations(&mut salt_key, |salt_key| { + // just for testing, ignore the error about zeroized keys + salt_key.set_bytes_as_type(&hex::decode(salt).unwrap(), KeyType::MACKey) + }) + .unwrap(); let info = hex::decode(info).unwrap(); let mut prk_key = HKDF_SHA256::extract(&salt_key, &ikm_key).unwrap(); @@ -438,12 +554,14 @@ mod hkdf_tests { // Some of the RFC5896 test vectors have input keys that are too short to meet the entropy seeding rules. // So, just for testing, we'll bump this up to full entropy, regardless of what entropy HKDF::extract() // thinks it should be based on the inputs. - prk_key.allow_hazardous_operations(); - prk_key.set_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut prk_key, |prk_key| { + prk_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); let mut okm_key = KeyMaterial::<100>::new(); _ = HKDF_SHA256::expand_out(&prk_key, &info, L, &mut okm_key).unwrap(); - okm_key.truncate(L).unwrap(); + okm_key.set_key_len(L).unwrap(); assert_eq!(okm_key.ref_to_bytes().len(), L); assert_eq!(okm_key.ref_to_bytes(), hex::decode(okm).unwrap()); @@ -452,9 +570,11 @@ mod hkdf_tests { Ok(_) => { assert_eq!(okm_key.ref_to_bytes().len(), L); assert_eq!(okm_key.ref_to_bytes(), hex::decode(okm).unwrap()); - }, - Err(KDFError::KeyMaterialError(_)) => { /* some of the rfc5896 test vectors are in fact low entropy, so just skip */ }, - Err(_) => panic!("Should have returned a MACError::KeyMaterialError.") + } + Err(KDFError::KeyMaterialError(_)) => { + /* some of the rfc5896 test vectors are in fact low entropy, so just skip */ + } + Err(_) => panic!("Should have returned a MACError::KeyMaterialError."), } /*** with the streaming APIs ... do_extract_final() ***/ @@ -487,7 +607,8 @@ mod hkdf_tests { #[test] fn hkdf_state_tests() { // setup - let key = KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let key = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); // error case: try to initialize twice let mut hkdf = HKDF_SHA256::new(); @@ -565,12 +686,13 @@ mod hkdf_tests { * "dkm":"1f6f7381c72f1d46d83e819bedeb482944adb0e3352cf2718e4bd334d95699e7256d01a48f35016f807bc1b739d41f5d53e442c67f6b455776d50d8667d62b3f3b632c5a88c371f7229a4c88beea1f28752a95fb2c533af28e6bfb19e69d750bbadc6609b2715dac19de9d9a6cfe3472a5e5312eabfd9bdbfa222cc7046ce3f7" * } * - **/ + **/ // SP800-56Cr2 tcId 1 let mut salt = KeyMaterial::<128>::new(); // have to do it this way for it to accept a zeroized key - salt.allow_hazardous_operations(); - salt.set_bytes_as_type(&hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut salt, |salt| { + salt.set_bytes_as_type(&hex::decode("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000").unwrap(), KeyType::MACKey) + }).unwrap(); // ikm = z||t // let ikm = KeyMaterial_internal::<128>::from_bytes_as_type(&hex::decode("589410408990A227518017C37997BE2F770AF54063E7393B2AA5463196136D18F365733139EB74CDD6B7268F41D33DB6").unwrap(), KeyType::MACKey).unwrap(); @@ -578,16 +700,25 @@ mod hkdf_tests { // info = uPartyInfo||vPartyInfo||i32(l) let mut additional_input: Vec = Vec::::new(); - /*fixedInfoPartyU*/additional_input.append(&mut hex::decode("338D1F2A8B222598565C798886E7B35B6D4BD5DD3018C899548C52A40A5A5EE82A93FF7F50E8BE7ADCF0DAA15B90CD292F5D14F905A3CA036D47A62F37C140954AE90CCDA9E4E08145DB80FA1B36E1FABC89664AF0A8D13761A963316A04565DEAF5D73C8B89B295A804A2D7DA2BADD178").unwrap()); - /*fixedInfoPartyV*/additional_input.append(&mut hex::decode("03EEF6BD71BCE550BBA98D7C0080B582").unwrap()); - /*L*/additional_input.append(&mut hex::decode("00000400").unwrap()); + /*fixedInfoPartyU*/ + additional_input.append(&mut hex::decode("338D1F2A8B222598565C798886E7B35B6D4BD5DD3018C899548C52A40A5A5EE82A93FF7F50E8BE7ADCF0DAA15B90CD292F5D14F905A3CA036D47A62F37C140954AE90CCDA9E4E08145DB80FA1B36E1FABC89664AF0A8D13761A963316A04565DEAF5D73C8B89B295A804A2D7DA2BADD178").unwrap()); + /*fixedInfoPartyV*/ + additional_input.append(&mut hex::decode("03EEF6BD71BCE550BBA98D7C0080B582").unwrap()); + /*L*/ + additional_input.append(&mut hex::decode("00000400").unwrap()); let mut expected_key = KeyMaterial::<128>::from_bytes_as_type(&hex::decode("1f6f7381c72f1d46d83e819bedeb482944adb0e3352cf2718e4bd334d95699e7256d01a48f35016f807bc1b739d41f5d53e442c67f6b455776d50d8667d62b3f3b632c5a88c371f7229a4c88beea1f28752a95fb2c533af28e6bfb19e69d750bbadc6609b2715dac19de9d9a6cfe3472a5e5312eabfd9bdbfa222cc7046ce3f7").unwrap(), KeyType::MACKey).unwrap(); - // do it manually just to check that we have the test vector right. let mut output_key = KeyMaterial::<128>::new(); - let bytes_written = HKDF::::extract_and_expand_out(&salt, &ikm, additional_input.as_slice(), 128, &mut output_key).unwrap(); + let bytes_written = HKDF::::extract_and_expand_out( + &salt, + &ikm, + additional_input.as_slice(), + 128, + &mut output_key, + ) + .unwrap(); assert_eq!(bytes_written, 128); assert_eq!(output_key.ref_to_bytes(), expected_key.ref_to_bytes()); @@ -596,12 +727,106 @@ mod hkdf_tests { let output_key = hkdf.derive_key(&ikm, &additional_input).unwrap(); // kdf.derive_key is a one-step that doesn't expand, so need to truncate the expected key to match. let mut expected_key_truncated = expected_key.clone(); - expected_key_truncated.truncate(output_key.key_len()).unwrap(); + expected_key_truncated.set_key_len(output_key.key_len()).unwrap(); assert_eq!(output_key.ref_to_bytes(), expected_key_truncated.ref_to_bytes()); - testframework.test_kdf_single_key::>(&ikm, &additional_input, &expected_key_truncated); + testframework + .test_kdf_single_key::>(&ikm, &additional_input, &expected_key_truncated); let keys = [&salt, &ikm]; - testframework.test_kdf_multiple_key::>(&keys, additional_input.as_slice(), &mut expected_key); + testframework.test_kdf_multiple_key::>( + &keys, + additional_input.as_slice(), + &mut expected_key, + ); + } + #[test] + fn serializable_keyed_state() { + use bouncycastle_core::traits::{Hash, SuspendableKeyed}; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableKeyedState; + use bouncycastle_hkdf::{SUSPENDED_HKDF_SHA256_STATE_LEN, SUSPENDED_HKDF_SHA512_STATE_LEN}; + + // HKDF is keyed by its salt: the salt is NOT serialized and is re-supplied on resume. + let salt = + KeyMaterial128::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::MACKey).unwrap(); + let ikm = &DUMMY_SEED_512[16..64]; + let (part1, part2) = ikm.split_at(20); + + // A helper that exercises the full round-trip for one HKDF variant. A concrete `&KeyMaterial128` + // works for `do_extract_init` (which wants a `Sized` `&impl KeyMaterialTrait`) and coerces to + // `&dyn KeyMaterialTrait` for the serialization APIs. + fn round_trip(salt: &KeyMaterial128, part1: &[u8], part2: &[u8]) + where + H: Hash + HashAlgParams + Default, + HKDF: Clone + SuspendableKeyed, + { + let hkdf = HKDF::::new(); + + // it can be serialized pre-init, which is kinda a no-op, but at least it works. + let serialized_state = hkdf.suspend(); + assert_eq!(serialized_state.len(), LEN); + let mut hkdf = HKDF::::from_suspended(serialized_state, salt).unwrap(); + + hkdf.do_extract_init(salt).unwrap(); + hkdf.do_extract_update_bytes(part1).unwrap(); + + // generic trait-conformance tests (version header present, [0,0,0]/future rejected) + TestFrameworkSuspendableKeyedState::new().test(&hkdf, salt); + + // serialize the in-progress extract state (on a clone), then finish the original + let serialized_state = hkdf.clone().suspend(); + assert_eq!(serialized_state.len(), LEN); + + hkdf.do_extract_update_bytes(part2).unwrap(); + let prk = hkdf.do_extract_final().unwrap(); + + // resume (re-supplying the salt), feed the identical remaining IKM, and compare PRKs + let mut resumed = HKDF::::from_suspended(serialized_state, salt).unwrap(); + resumed.do_extract_update_bytes(part2).unwrap(); + let prk_resumed = resumed.do_extract_final().unwrap(); + + assert_eq!(prk.ref_to_bytes(), prk_resumed.ref_to_bytes()); + } + + round_trip::(&salt, part1, part2); + round_trip::(&salt, part1, part2); + + // Test the guard for invalid states + // testing just on HKDF_SHA256 + + const UNINITIALIZED: u8 = 0; // HkdfStates::Uninitialized + const INITIALIZED: u8 = 1; // HkdfStates::Initialized + // Layout: [version(3) | present flag | hmac blob | state | entropy(8) | strength]. + let present_idx = 3; + let state_idx = SUSPENDED_HKDF_SHA256_STATE_LEN - 10; + + // Case 1: no HMAC present but state claims Initialized -> reject. + // construct a valid, pre-init state: no HMAC (flag = 0), state = Uninitialized. + let valid_uninitialized = HKDF_SHA256::new().suspend(); + assert_eq!(valid_uninitialized[present_idx], 0); + assert_eq!(valid_uninitialized[state_idx], UNINITIALIZED); + + let mut corrupt = valid_uninitialized; + corrupt[state_idx] = INITIALIZED; + assert!(matches!( + HKDF_SHA256::from_suspended(corrupt, &salt), + Err(SuspendableError::InvalidData) + )); + + // Case 2: HMAC present but state claims Uninitialized -> reject. + // construct a valid, mid-extract state: HMAC present (flag = 1), state = Initialized. + let mut hkdf = HKDF_SHA256::new(); + hkdf.do_extract_init(&salt).unwrap(); + hkdf.do_extract_update_bytes(ikm).unwrap(); + let valid_initialized = hkdf.suspend(); + assert_eq!(valid_initialized[present_idx], 1); + assert_ne!(valid_initialized[state_idx], UNINITIALIZED); + + let mut corrupt = valid_initialized; + corrupt[state_idx] = UNINITIALIZED; + assert!(matches!( + HKDF_SHA256::from_suspended(corrupt, &salt), + Err(SuspendableError::InvalidData) + )); } } diff --git a/crypto/hmac/Cargo.toml b/crypto/hmac/Cargo.toml index 6f8155f..6e83d4a 100644 --- a/crypto/hmac/Cargo.toml +++ b/crypto/hmac/Cargo.toml @@ -3,6 +3,20 @@ name = "bouncycastle-hmac" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the `Vec`-returning convenience APIs (`mac`, `do_final`) and turns on the same +# feature in bouncycastle-core. The allocation-free streaming / `_out` / `_array::()` APIs work +# without it. On by default; disable with `default-features = false` for `no_std` use. +default = ["alloc"] +# Forward `alloc` to every internal dependency so that the `alloc` gates stay consistent across the +# dependency graph (otherwise a dep's default features could turn on `core/alloc` while hmac's own +# `alloc` is off, leaving the gated trait method unimplemented). +alloc = [ + "bouncycastle-core/alloc", + "bouncycastle-sha2/alloc", + "bouncycastle-sha3/alloc", +] + [dependencies] bouncycastle-core.workspace = true bouncycastle-sha2.workspace = true diff --git a/crypto/hmac/benches/hmac_benches.rs b/crypto/hmac/benches/hmac_benches.rs index f395c37..0e9dd03 100644 --- a/crypto/hmac/benches/hmac_benches.rs +++ b/crypto/hmac/benches/hmac_benches.rs @@ -1,9 +1,9 @@ -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; -use std::hint::black_box; -use bouncycastle_rng as rng; use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyType}; use bouncycastle_core::traits::{MAC, RNG}; use bouncycastle_hmac::{HMAC_SHA256, HMAC_SHA512}; +use bouncycastle_rng as rng; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_hmac_sha256(c: &mut Criterion) { let mut data_block = [0_u8; 1024]; @@ -52,4 +52,4 @@ fn bench_hmac_sha512(c: &mut Criterion) { } criterion_group!(benches, bench_hmac_sha256, bench_hmac_sha512); -criterion_main!(benches); \ No newline at end of file +criterion_main!(benches); diff --git a/crypto/hmac/src/lib.rs b/crypto/hmac/src/lib.rs index 9776adb..e45b439 100644 --- a/crypto/hmac/src/lib.rs +++ b/crypto/hmac/src/lib.rs @@ -137,111 +137,194 @@ //! } //! ``` //! -//! # Request for feedback on fallability of this API -//! We have made an effort to reduce the number of fallibly APIs in the [MAC] trait; in particular -//! the APIs for the update and most of the finalize phases are infallible -- they just work. -//! However, we were not able to design it to completely avoid runtime error conditions, such as -//! providing [MAC::do_final_out] with a buffer that is too small to hold what NIST considered the smallest -//! valid MAC value for the given underlying hash function. +//! # Suspending and resuming execution //! -//! Also, the key strength and type checking in the initialization phase means that both [MAC::new] and -//! [MAC::new_allow_weak_key] have several runtime error conditions that they check for. +//! When MAC'ing a large message, it can be advantageous to be able to suspend the operation +//! to a cache and resume it later; for example if waiting for the message to stream over a slow network +//! connection. For this reason, all HMAC algorithms impl [SuspendableKeyed]. //! -//! All of this leads to application code that requires a lot more .unwrap(), .expect(), or match than we would -//! really like. +//! Note that since HMAC is a keyed +//! algorithm and we do not want to serialize the private key into the state, the trait structure forces you to +//! re-provide the same key when you resume the operation. Securely storing this key in the interim +//! is the responsibility of the caller. Note also that if you resume the HMAC with the wrong key, +//! `from_serialized_state` has no way to detect this, so the end result will be a broken MAC value +//! computed with different keys in the inner and outer pad. So make sure you resume with the same key! //! -//! We would love feedback on an alternate design for this API than carries less runtime error -//! conditions, without sacrificing the key strength checking that the metadata in the [KeyMaterialTrait] object allows. +//!```rust +//! use bouncycastle_hmac::HMAC_SHA256; +//! use bouncycastle_core::key_material::KeyMaterial256; +//! use bouncycastle_core::traits::{MAC, SuspendableKeyed}; +//! use bouncycastle_core::key_material::KeyType; +//! +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let key = KeyMaterial256::from_bytes_as_type( +//! b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", +//! KeyType::MACKey).unwrap(); +//! +//! let mut hmac = HMAC_SHA256::new(&key).unwrap(); +//! hmac.do_update(msg_part1); +//! +//! // suspend the in-progress mac (the key is NOT included in the serialized state) +//! let serialized_state = hmac.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! // ... later, possibly on another host: resume from the serialized state by re-supplying +//! // the same salt (make sure you store it securely!). +//! let mut hmac_resumed = HMAC_SHA256::from_suspended(serialized_state, &key).unwrap(); +//! hmac_resumed.do_update(msg_part2); +//! let h: Vec = hmac_resumed.do_final(); +//! ``` +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] #![allow(incomplete_features)] // because at time of writing, generic_const_exprs is not a stable feature #![feature(generic_const_exprs)] -use bouncycastle_core::errors::{KeyMaterialError, MACError}; +// The `Vec`-returning convenience APIs (`mac`, `do_final`) live behind the default-on `alloc` +// feature. The streaming API and the `mac_out`/`do_final_out`/`mac_array::()` APIs are all +// allocation-free and work in `no_std` without it. +#[cfg(feature = "alloc")] +extern crate alloc; + +use bouncycastle_core::errors::{KeyMaterialError, MACError, SuspendableError}; use bouncycastle_core::key_material::{KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Algorithm, Hash, MAC, SecurityStrength}; -use bouncycastle_sha2::{SHA224, SHA256, SHA384, SHA512}; -use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512}; +use bouncycastle_core::traits::{ + Algorithm, Hash, MAC, Secret, SecurityStrength, Suspendable, SuspendableKeyed, +}; +use bouncycastle_sha2::{ + SHA224, SHA256, SHA384, SHA512, SUSPENDED_SHA256_STATE_LEN, SUSPENDED_SHA512_STATE_LEN, +}; +use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512, SUSPENDED_SHA3_STATE_LEN}; use bouncycastle_utils::ct; +use core::fmt::{Debug, Display, Formatter}; + +#[cfg(feature = "alloc")] +use alloc::{vec, vec::Vec}; /*** String constants ***/ +/// pub const HMAC_SHA224_NAME: &str = "HMAC-SHA224"; +/// pub const HMAC_SHA256_NAME: &str = "HMAC-SHA256"; +/// pub const HMAC_SHA384_NAME: &str = "HMAC-SHA384"; +/// pub const HMAC_SHA512_NAME: &str = "HMAC-SHA512"; +/// pub const HMAC_SHA3_224_NAME: &str = "HMAC-SHA3-224"; +/// pub const HMAC_SHA3_256_NAME: &str = "HMAC-SHA3-256"; +/// pub const HMAC_SHA3_384_NAME: &str = "HMAC-SHA3-384"; +/// pub const HMAC_SHA3_512_NAME: &str = "HMAC-SHA3-512"; /*** Type aliases ***/ #[allow(non_camel_case_types)] -pub type HMAC_SHA224 = HMAC; +pub type HMAC_SHA224 = HMAC; impl Algorithm for HMAC_SHA224 { const ALG_NAME: &'static str = HMAC_SHA224_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA256 = HMAC; +pub type HMAC_SHA256 = HMAC; impl Algorithm for HMAC_SHA256 { const ALG_NAME: &'static str = HMAC_SHA256_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA384 = HMAC; +pub type HMAC_SHA384 = HMAC; impl Algorithm for HMAC_SHA384 { const ALG_NAME: &'static str = HMAC_SHA384_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA512 = HMAC; +pub type HMAC_SHA512 = HMAC; impl Algorithm for HMAC_SHA512 { const ALG_NAME: &'static str = HMAC_SHA512_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA3_224 = HMAC; +pub type HMAC_SHA3_224 = HMAC; impl Algorithm for HMAC_SHA3_224 { const ALG_NAME: &'static str = HMAC_SHA3_224_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA3_256 = HMAC; +pub type HMAC_SHA3_256 = HMAC; impl Algorithm for HMAC_SHA3_256 { const ALG_NAME: &'static str = HMAC_SHA3_256_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA3_384 = HMAC; +pub type HMAC_SHA3_384 = HMAC; impl Algorithm for HMAC_SHA3_384 { const ALG_NAME: &'static str = HMAC_SHA3_384_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; } #[allow(non_camel_case_types)] -pub type HMAC_SHA3_512 = HMAC; +pub type HMAC_SHA3_512 = HMAC; impl Algorithm for HMAC_SHA3_512 { const ALG_NAME: &'static str = HMAC_SHA3_512_NAME; const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; } -// TODO: is there a rustacious way to extract this from HASH? +// The internal key buffer must be able to hold a key up to the *block length* of the underlying hash: +// per RFC 2104, a key no longer than the block is used verbatim (only longer keys are pre-hashed down +// to the output length). So the buffer size is a const parameter of the struct, set per hash to its +// block length by the type aliases below. Block lengths (bytes): SHA-224/256 = 64, SHA-384/512 = 128, +// SHA3-224 = 144, SHA3-256 = 136, SHA3-384 = 104, SHA3-512 = 72. +// +// The default is used only when `HMAC` is written without an explicit buffer size; it is the +// largest block length across all supported hashes, so it is always large enough. +const LARGEST_HASHER_BLOCK_LEN: usize = 144; + +// The largest output length across all supported hashes (SHA-512 / SHA3-512 = 64 bytes). Used to +// size allocation-free stack scratch buffers so that the streaming / `_out` APIs work in `no_std`. const LARGEST_HASHER_OUTPUT_LEN: usize = 64; // HMAC implements RFC 2104. #[derive(Clone)] -pub struct HMAC { +pub struct HMAC { hasher: HASH, - key: [u8; LARGEST_HASHER_OUTPUT_LEN], + key: [u8; KEY_BUF_LEN], key_len: usize, // Doing it this way to avoid needing a vec, so that this can be made no_std friendly. } +// Because the HMAC struct contains a copy of the long-term key +impl Secret for HMAC {} + +impl Drop for HMAC { + fn drop(&mut self) { + self.key.fill(0); + self.key_len = 0; + } +} + +impl Debug for HMAC { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "HMAC-{} instance", HASH::ALG_NAME,) + } +} + +impl Display for HMAC { + fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { + write!(f, "HMAC-{} instance", HASH::ALG_NAME,) + } +} + // See definitions in RFC 2104 Section 2. const IPAD_BYTE: u8 = 0x36; const OPAD_BYTE: u8 = 0x5C; @@ -257,30 +340,47 @@ const OPAD_BYTE: u8 = 0x5C; // be too strict about it, pub const MIN_FIPS_DIGEST_LEN: usize = 4; // 32 / 8; -impl HMAC { +impl HMAC { fn pad_key_into_hasher(&mut self, padding: u8) { - // TODO: it would be nice to be able to statically extract the length of HASH and not need a Vec or over-sized array here. - // TODO: make this no_std-friendly - let mut padded = vec![0u8; self.hasher.block_bitlen() / 8]; + // Allocation-free scratch: the padded key block is exactly `block_len` bytes (`block_len` is + // never larger than [LARGEST_HASHER_BLOCK_LEN]), so use an oversized stack buffer sliced down + // to `block_len`. This keeps the streaming API `no_std`-friendly (no `Vec`). + let block_len = self.hasher.block_bitlen() / 8; + let mut padded_buf = [0u8; LARGEST_HASHER_BLOCK_LEN]; + let padded = &mut padded_buf[..block_len]; - // Per RFC 2104 Section 2, if the application key exceeds the block - // length of the underlying hashes algorithm, we apply a hash invocation - // over the key first. - // if self.key_len > self.hasher.block_bitlen() / 8 { - // HASH::default().hash_out(&self.key[..self.key_len], &mut padded[..self.hasher.output_len()])?; - // } else { - // TODO: does this need a guard for a key_len longer than the block length? padded[..self.key_len].copy_from_slice(&self.key[..self.key_len]); - // } - // XXX: easier way to xor over Vec? - for entry in &mut padded { + // Per RFC 2104 Section 2, the key is zero-padded to the block length and then XORed with the + // pad byte over the entire block. + for entry in padded.iter_mut() { *entry ^= padding; } // Per RFC 2104 Section 2, write the padded key into the stream prior // to any other data. - self.hasher.do_update(&padded) + self.hasher.do_update(padded) + } + + /// Per RFC 2104 Section 2, if the application key exceeds the block + /// length of the underlying hashes algorithm, we apply a hash invocation + /// over the key first. + /// This does NOT absorb the key into the hasher; that is done separately via [HMAC::pad_key_into_hasher]. + fn load_key_material(&mut self, key_bytes: &[u8]) { + if key_bytes.len() > self.hasher.block_bitlen() / 8 { + // then we have to pre-hash it -- use a new instance of the hasher rather than the internal one + HASH::default().hash_out(key_bytes, &mut self.key[..self.hasher.output_len()]); + self.key_len = self.hasher.output_len(); + } else { + self.key[..key_bytes.len()].copy_from_slice(key_bytes); + self.key_len = key_bytes.len(); + } + + // Just as a sanity-check. + assert!( + self.key_len <= KEY_BUF_LEN, + "Fatal error: Key length exceeds HMAC internal buffer length" + ); } /// Private init so that users are forced to go through one of the public new methods and thus we @@ -300,14 +400,7 @@ impl HMAC { // length of the underlying hashes algorithm, we apply a hash invocation // over the key first. - if key.key_len() > self.hasher.block_bitlen() / 8 { - // then we have to pre-hash it -- use a new instance of the hasher rather than the internal one - HASH::default().hash_out(key.ref_to_bytes(), &mut self.key[..self.hasher.output_len()]); - self.key_len = self.hasher.output_len(); - } else { - self.key[..key.key_len()].copy_from_slice(key.ref_to_bytes()); - self.key_len = key.key_len(); - } + self.load_key_material(key.ref_to_bytes()); self.pad_key_into_hasher(IPAD_BYTE); @@ -330,20 +423,27 @@ impl HMAC { )); } + out.fill(0); + // Per RFC 2104 Section 2, save our inner digest to calculate our // outer digest. Note that we can't (necessarily) reuse out as a // scratch pad here: if we're truncating the output but not // truncating the underlying hashes, we'd lose bytes and compute an // invalid outer hashes. - // TODO: rework this to be no_std friendly (ie no vec!) - let mut ihash = vec![0u8; self.hasher.output_len()]; - self.hasher.do_final_out(&mut ihash); + // Allocation-free scratch: the inner digest is exactly `output_len` bytes (never larger than + // [LARGEST_HASHER_OUTPUT_LEN]), so use an oversized stack buffer sliced down to `output_len`. + let out_len = self.hasher.output_len(); + let mut ihash_buf = [0u8; LARGEST_HASHER_OUTPUT_LEN]; + let ihash = &mut ihash_buf[..out_len]; + // `HMAC` implements `Drop` (required by `Secret`), so we cannot move `self.hasher` out + // directly. Swap in a fresh default and consume the taken-out hasher instead. + core::mem::take(&mut self.hasher).do_final_out(ihash); // ohash self.hasher = HASH::default(); self.pad_key_into_hasher(OPAD_BYTE); - self.hasher.do_update(&ihash); - Ok(self.hasher.do_final_out(out)) + self.hasher.do_update(ihash); + Ok(core::mem::take(&mut self.hasher).do_final_out(out)) } } @@ -352,17 +452,15 @@ impl HMAC { // TODO: This is essentially a "batch mode" where you want to perform many MACs or Verifications with the same key // TODO: against different data. -impl MAC for HMAC { +impl MAC for HMAC { fn new(key: &impl KeyMaterialTrait) -> Result { - let mut hmac = - Self { hasher: HASH::default(), key: [0u8; LARGEST_HASHER_OUTPUT_LEN], key_len: 0 }; + let mut hmac = Self { hasher: HASH::default(), key: [0u8; KEY_BUF_LEN], key_len: 0 }; hmac.init(key, false)?; Ok(hmac) } fn new_allow_weak_key(key: &impl KeyMaterialTrait) -> Result { - let mut hmac = - Self { hasher: HASH::default(), key: [0u8; LARGEST_HASHER_OUTPUT_LEN], key_len: 0 }; + let mut hmac = Self { hasher: HASH::default(), key: [0u8; KEY_BUF_LEN], key_len: 0 }; hmac.init(key, true)?; Ok(hmac) } @@ -371,6 +469,7 @@ impl MAC for HMAC { self.hasher.output_len() } + #[cfg(feature = "alloc")] fn mac(self, data: &[u8]) -> Vec { let mut out = vec![0_u8; self.hasher.output_len()]; let bytes_written = self.mac_out(data, &mut out).expect("HMAC::mac(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules."); @@ -378,6 +477,8 @@ impl MAC for HMAC { } fn mac_out(mut self, data: &[u8], mut out: &mut [u8]) -> Result { + out.fill(0); + self.do_update(data); self.do_final_out(&mut out) } @@ -391,6 +492,7 @@ impl MAC for HMAC { self.hasher.do_update(data) } + #[cfg(feature = "alloc")] fn do_final(self) -> Vec { let mut out = vec![0_u8; self.hasher.output_len()]; self.do_final_internal_out(&mut out).expect("HMAC::do_final(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules."); @@ -398,12 +500,16 @@ impl MAC for HMAC { } fn do_final_out(self, mut out: &mut [u8]) -> Result { + out.fill(0); + self.do_final_internal_out(&mut out) } fn do_verify_final(self, mac: &[u8]) -> bool { - let mut out = vec![0_u8; HASH::default().output_len()]; - let output_len = self.do_final_internal_out(&mut out).expect("HMAC::do_final(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules."); + // Allocation-free scratch buffer (see [LARGEST_HASHER_OUTPUT_LEN]). + let mut out_buf = [0_u8; LARGEST_HASHER_OUTPUT_LEN]; + let out = &mut out_buf[..HASH::default().output_len()]; + let output_len = self.do_final_internal_out(out).expect("HMAC::do_final(): should not have failed because we gave it a sufficiently large output buffer to meet FIPS rules."); if mac.len() != output_len { return false; } @@ -414,3 +520,72 @@ impl MAC for HMAC { HASH::default().max_security_strength() } } + +/* SerializedState */ + +/*** Serialized-state length constants ***/ +/// Length in bytes of the serialized state of [HMAC_SHA224]. +pub const SUSPENDED_HMAC_SHA224_STATE_LEN: usize = SUSPENDED_SHA256_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA256]. +pub const SUSPENDED_HMAC_SHA256_STATE_LEN: usize = SUSPENDED_SHA256_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA384]. +pub const SUSPENDED_HMAC_SHA384_STATE_LEN: usize = SUSPENDED_SHA512_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA512]. +pub const SUSPENDED_HMAC_SHA512_STATE_LEN: usize = SUSPENDED_SHA512_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA3_224]. +pub const SUSPENDED_HMAC_SHA3_224_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA3_256]. +pub const SUSPENDED_HMAC_SHA3_256_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA3_384]. +pub const SUSPENDED_HMAC_SHA3_384_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; +/// Length in bytes of the serialized state of [HMAC_SHA3_512]. +pub const SUSPENDED_HMAC_SHA3_512_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; + +/// HMAC is a keyed algorithm, so it implements [SuspendableKeyed] (rather than +/// [Suspendable]) for suspending and resuming in-progress operations. +/// The key is deliberately NOT written into the serialized +/// bytes and must be re-supplied at deserialization. +/// +/// The serialized state is exactly the inner hasher's state (which has already absorbed `K ⊕ ipad` +/// and any message chunks provided so far) — so this is a straight passthrough to the underlying hash's +/// [Suspendable] impl. The re-supplied key is needed to reconstruct the material for the outer +/// (`K ⊕ opad`) step at finalization. +/// +/// There is no way to detect a mismatched key on +/// resume: the caller MUST supply the same key the HMAC was created with, otherwise the resumed +/// operation will silently produce an incorrect MAC. +impl< + const HASH_STATE_LEN: usize, + const KEY_BUF_LEN: usize, + HASH: Hash + Default + Suspendable, +> SuspendableKeyed for HMAC +{ + // HMAC accepts any key material, so the key type is the trait object `dyn KeyMaterialTrait` + // rather than a single concrete key type. The key is only used (by reference) to reload the key + // bytes at from_serialized_state, so dynamic dispatch here is negligible. + type Key = dyn KeyMaterialTrait; + + fn suspend(mut self) -> [u8; HASH_STATE_LEN] { + // The key is intentionally excluded; the resumable state is just the inner hasher, which + // already carries the library version header from the hash's own SerializableState impl. + // `HMAC` implements `Drop` (required by `Secret`), so move the hasher out via `mem::take` + // rather than a direct partial move. + core::mem::take(&mut self.hasher).suspend() + } + + fn from_suspended( + state: [u8; HASH_STATE_LEN], + key: &Self::Key, + ) -> Result { + // Rebuild the inner hasher (version-compatibility is validated by the hash's impl). + let hasher = HASH::from_suspended(state)?; + + // Re-load the key material exactly as `new()` did (pre-hashing an over-length key), but do + // NOT re-absorb `K ⊕ ipad` — the deserialized hasher already contains it. The key is only + // needed for the outer `K ⊕ opad` step at finalization. + let mut hmac = HMAC { hasher, key: [0u8; KEY_BUF_LEN], key_len: 0 }; + hmac.load_key_material(key.ref_to_bytes()); + + Ok(hmac) + } +} diff --git a/crypto/hmac/tests/hmac_tests.rs b/crypto/hmac/tests/hmac_tests.rs index 87dd118..817a89d 100644 --- a/crypto/hmac/tests/hmac_tests.rs +++ b/crypto/hmac/tests/hmac_tests.rs @@ -1,11 +1,14 @@ #[cfg(test)] mod hmac_tests { - use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait}; - use bouncycastle_core::traits::{Algorithm, Hash, SecurityStrength, MAC}; - use bouncycastle_core_test_framework::mac::TestFrameworkMAC; - use bouncycastle_hex as hex; use bouncycastle_core::errors::{KeyMaterialError, MACError}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, + }; + use bouncycastle_core::traits::{Algorithm, Hash, MAC, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; + use bouncycastle_core_test_framework::mac::TestFrameworkMAC; + use bouncycastle_hex as hex; use bouncycastle_hmac::*; use bouncycastle_sha2::*; use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512}; @@ -14,8 +17,10 @@ mod hmac_tests { fn simple_tests() { // Simple test with zero-length key let mut zero_length_key = KeyMaterial256::default(); - zero_length_key.allow_hazardous_operations(); - zero_length_key.convert_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut zero_length_key, |zero_length_key| { + zero_length_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); @@ -28,7 +33,8 @@ mod hmac_tests { let key = KeyMaterial256::from_bytes_as_type( b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); let mut mac = HMAC::::new(&key).unwrap(); mac.do_update(b"Hi There"); let output = mac.do_final(); @@ -38,18 +44,23 @@ mod hmac_tests { let key = KeyMaterial256::from_bytes_as_type( b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); let mac = HMAC::::new(&key).unwrap(); // mac.do_update(b"").unwrap(); let output = mac.do_final(); - assert_eq!(&output, &hex::decode("999a901219f032cd497cadb5e6051e97b6a29ab297bd6ae722bd6062a2f59542").unwrap()); + assert_eq!( + &output, + &hex::decode("999a901219f032cd497cadb5e6051e97b6a29ab297bd6ae722bd6062a2f59542") + .unwrap() + ); } #[test] fn test_type_aliases() { let key = KeyMaterial512::from_bytes_as_type( - b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", - KeyType::MACKey).unwrap(); + b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", + KeyType::MACKey).unwrap(); _ = HMAC::::new(&key).unwrap(); _ = HMAC_SHA224::new(&key).unwrap(); @@ -63,7 +74,6 @@ mod hmac_tests { _ = HMAC::::new(&key).unwrap(); _ = HMAC_SHA512::new(&key).unwrap(); - _ = HMAC::::new(&key).unwrap(); _ = HMAC_SHA3_224::new(&key).unwrap(); @@ -82,12 +92,15 @@ mod hmac_tests { let short_key = KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); assert_eq!(short_key.security_strength(), SecurityStrength::_112bit); // key is too short, so we expect it to fail match HMAC::::new(&short_key) { - Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* good */ }, - _ => panic!("This should have thrown a KeyMaterialError::SecurityStrength error but it didn't"), + Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* good */ } + _ => panic!( + "This should have thrown a KeyMaterialError::SecurityStrength error but it didn't" + ), } // but this'll work fine @@ -97,19 +110,59 @@ mod hmac_tests { let key = KeyMaterial256::from_bytes_as_type( &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); HMAC::::new(&key).unwrap(); } #[test] fn test_block_bitlen() { // give it a key that is exactly the block size - let key = KeyMaterial::<1000>::from_bytes_as_type(&vec![0x3cu8; SHA256::new().block_bitlen()/8], KeyType::MACKey).unwrap(); - assert!(HMAC_SHA256::new(&key).unwrap().verify(b"Hi There", &hex::decode("4da0a0bb56f010db147b8f6e5f2dbecf7bb35ff00c8b9da31c9b94cc81815873").unwrap())); + let key = KeyMaterial::<1000>::from_bytes_as_type( + &vec![0x3Cu8; SHA256::new().block_bitlen() / 8], + KeyType::MACKey, + ) + .unwrap(); + assert!( + HMAC_SHA256::new(&key).unwrap().verify( + b"Hi There", + &hex::decode("4da0a0bb56f010db147b8f6e5f2dbecf7bb35ff00c8b9da31c9b94cc81815873") + .unwrap() + ) + ); // Now give it a key that is larger than the block size and needs to be hashed down - let key = KeyMaterial::<1000>::from_bytes_as_type(&vec![0x3cu8; SHA256::new().block_bitlen()/8 + 1], KeyType::MACKey).unwrap(); - HMAC_SHA256::new(&key).unwrap().verify(b"Hi There", &hex::decode("5cb9475aba606afe8c82c2d1b3e1cfb1c814e8a72ce5a7b4fe43b0a0aac45144").unwrap()); + let key = KeyMaterial::<1000>::from_bytes_as_type( + &vec![0x3Cu8; SHA256::new().block_bitlen() / 8 + 1], + KeyType::MACKey, + ) + .unwrap(); + HMAC_SHA256::new(&key).unwrap().verify( + b"Hi There", + &hex::decode("5cb9475aba606afe8c82c2d1b3e1cfb1c814e8a72ce5a7b4fe43b0a0aac45144") + .unwrap(), + ); + } + + #[test] + fn long_key() { + // Regression test: a key just under the maximum length before HMAC will hash it down. + // (RFC 2104 only pre-hashes keys *longer* than the block). + // This test is designed to detect an overflow-write and panic on HMAC's internal key buffer. + + // SHA-512 has a 128-byte block, so use a 127-byte key + let key = KeyMaterial::<200>::from_bytes_as_type(&[0x0B; 127], KeyType::MACKey).unwrap(); + let mut mac = HMAC_SHA512::new(&key).unwrap(); + mac.do_update(b"Hi There"); + let tag = mac.do_final(); + assert!(HMAC_SHA512::new(&key).unwrap().verify(b"Hi There", &tag)); + + // SHA3-224 has the largest block (144 bytes); a 143-byte key exercises the top of the range. + let key = KeyMaterial::<200>::from_bytes_as_type(&[0x0B; 143], KeyType::MACKey).unwrap(); + let mut mac = HMAC_SHA3_224::new(&key).unwrap(); + mac.do_update(b"Hi There"); + let tag = mac.do_final(); + assert!(HMAC_SHA3_224::new(&key).unwrap().verify(b"Hi There", &tag)); } #[test] @@ -123,21 +176,25 @@ mod hmac_tests { HMAC_SHA256::new_allow_weak_key(&zero_key).unwrap(); // non-zero len key of all-zero bytes - zero_key.allow_hazardous_operations(); - zero_key.set_key_len(32).unwrap(); + key_material::do_hazardous_operations(&mut zero_key, |zero_key| zero_key.set_key_len(32)) + .unwrap(); HMAC_SHA256::new_allow_weak_key(&zero_key).unwrap(); // but we don't allow zero-len keys that are not Zeroized or MACKey - // init - let mut key = KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); + let mut key = + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::MACKey).unwrap(); assert_eq!(key.security_strength(), SecurityStrength::_256bit); key.set_security_strength(SecurityStrength::_128bit).unwrap(); // complains at first match HMAC::::new(&key) { - Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ }, - _ => { panic!("This should have thrown a KeyMaterialError::SecurityStrength error but it didn't") }, + Err(MACError::KeyMaterialError(KeyMaterialError::SecurityStrength(_))) => { /* fine */ } + _ => { + panic!( + "This should have thrown a KeyMaterialError::SecurityStrength error but it didn't" + ) + } } // but fine if you set .allow_weak_keys() let mut hmac = HMAC::::new_allow_weak_key(&key).unwrap(); @@ -158,7 +215,8 @@ mod hmac_tests { let key = KeyMaterial256::from_bytes_as_type( b"\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b", KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); // get the known-good output let out = HMAC::::new(&key).unwrap().mac(b"Hi There"); @@ -166,24 +224,28 @@ mod hmac_tests { // test output that's the wrong length, should simply return False let mut mac = HMAC::::new(&key).unwrap(); mac.do_update(b"Hi There"); - assert!( ! mac.do_verify_final(&out[..out.len() - 1])); + assert!(!mac.do_verify_final(&out[..out.len() - 1])); // test output that's the right length but wrong value -- do_verify let mut mac = HMAC::::new(&key).unwrap(); mac.do_update(b"Hi There"); - assert!( ! mac.do_verify_final(&[0x01_u8; 28])); + assert!(!mac.do_verify_final(&[0x01_u8; 28])); // test output that's the right length but wrong value -- static verify - assert!( ! HMAC_SHA224::new(&key).unwrap().verify( b"Hi There", &[0x01_u8; 28])); + assert!(!HMAC_SHA224::new(&key).unwrap().verify(b"Hi There", &[0x01_u8; 28])); // error case: test that it'll refuse to truncate below MIN_FIPS_DIGEST_LEN let mut mac = HMAC::::new(&key).unwrap(); mac.do_update(b"Hi There"); let mut out = vec![0u8; MIN_FIPS_DIGEST_LEN - 1]; match mac.do_final_out(&mut out) { - Ok(_) => { panic!("This should have throw an InvalidLength error")} - Err(MACError::InvalidLength(_)) => { /*** good **/ }, - Err(_) => { panic!("This should have throw an InvalidLength error but it threw something else")}, + Ok(_) => { + panic!("This should have throw an InvalidLength error") + } + Err(MACError::InvalidLength(_)) => { /*** good **/ } + Err(_) => { + panic!("This should have throw an InvalidLength error but it threw something else") + } } // success case: ... but it will truncate to exactly MIN_FIPS_DIGEST_LEN @@ -197,17 +259,17 @@ mod hmac_tests { // fail case: mac value is correct but truncated let mac = HMAC_SHA3_224::new(&key).unwrap(); let mut mac_val = mac.mac(b"Polly want a cracker?"); - let verifier = HMAC_SHA3_224::new(&key).unwrap(); + let verifier = HMAC_SHA3_224::new(&key).unwrap(); assert!(verifier.verify(b"Polly want a cracker?", &mac_val)); // truncation of the mac value is considered a fail - let verifier = HMAC_SHA3_224::new(&key).unwrap(); - assert!(! verifier.verify(b"Polly want a cracker?", &mac_val[..mac_val.len() - 1]) ); + let verifier = HMAC_SHA3_224::new(&key).unwrap(); + assert!(!verifier.verify(b"Polly want a cracker?", &mac_val[..mac_val.len() - 1])); // .. as is some extra bytes at the end - let verifier = HMAC_SHA3_224::new(&key).unwrap(); + let verifier = HMAC_SHA3_224::new(&key).unwrap(); mac_val.extend_from_slice(&[0u8; 4]); - assert!(! verifier.verify(b"Polly want a cracker?", &mac_val) ); + assert!(!verifier.verify(b"Polly want a cracker?", &mac_val)); } #[test] @@ -225,15 +287,17 @@ mod hmac_tests { #[cfg(test)] mod core_test_framework_rfc4231 { - use bouncycastle_core::key_material::KeyMaterial; use super::*; + use bouncycastle_core::key_material::KeyMaterial; #[test] fn hmac_sha224() { let test_framework = TestFrameworkMAC::new(); let mut zero_length_key = KeyMaterial256::default(); - zero_length_key.allow_hazardous_operations(); - zero_length_key.convert_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut zero_length_key, |zero_length_key| { + zero_length_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); @@ -266,14 +330,14 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), - &hex::decode("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea").unwrap() + &hex::decode("7fb3cb3588c6c1f6ffa9694d7d6ad2649365b0c1f65d69d1ec8333ea").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), - &hex::decode("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a").unwrap() + &hex::decode("6c11506874013cac6a2abc1bb382627cec6a90d86efc012de7afec5a").unwrap(), ); // RFC4231 Test Case 5 -- Test with a truncation of output to 128 bits. @@ -289,16 +353,17 @@ mod hmac_tests { // RFC4231 Test Case 6 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). let key = KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(); - test_framework.test_mac::>(&key, - b"Test Using Larger Than Block-Size Key - Hash Key First", - &hex::decode("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e").unwrap() + test_framework.test_mac::>( + &key, + b"Test Using Larger Than Block-Size Key - Hash Key First", + &hex::decode("95e9a0db962095adaebe9b2d6f0dbce2d499f112f2d2b7273fa6870e").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", - &hex::decode("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1").unwrap() + &hex::decode("3a854166ac5d9f023f54d517d0b39dbd946770db9c2b95c9f6f565d1").unwrap(), ); } @@ -307,15 +372,18 @@ mod hmac_tests { // test with zero-length key let test_framework = TestFrameworkMAC::new(); let mut zero_length_key = KeyMaterial256::default(); - zero_length_key.allow_hazardous_operations(); - zero_length_key.convert_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut zero_length_key, |zero_length_key| { + zero_length_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); test_framework.test_mac::>( &zero_length_key, b"Hello, world", - &hex::decode("c0fa4c55880318c31c1020e7a2cf830c2c695716387795c7a0eb918ba84e4bf0").unwrap(), + &hex::decode("c0fa4c55880318c31c1020e7a2cf830c2c695716387795c7a0eb918ba84e4bf0") + .unwrap(), ); // RFC4231 Test Case 1 @@ -325,30 +393,32 @@ mod hmac_tests { &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), KeyType::MACKey, ) - .unwrap(), + .unwrap(), b"Hi There", - &hex::decode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7").unwrap(), + &hex::decode("b0344c61d8db38535ca8afceaf0bf12b881dc200c9833da726e9376c2e32cff7") + .unwrap(), ); // RFC4231 Test Case 2 -- Test with a key shorter than the length of the HMAC output. test_framework.test_mac::>( &KeyMaterial256::from_bytes_as_type(b"Jefe", KeyType::MACKey).unwrap(), b"what do ya want for nothing?", - &hex::decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843").unwrap(), + &hex::decode("5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843") + .unwrap(), ); // RFC4231 Test Case 3 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), - &hex::decode("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe").unwrap() + &hex::decode("773ea91e36800e46854db8ebd09181a72959098b3ef8c122d9635514ced565fe").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), - &hex::decode("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b").unwrap() + &hex::decode("82558a389a443c0ea4cc819899f2083a85f0faa3e578f8077a2e3ff46729665b").unwrap(), ); // RFC4231 Test Case 5 -- Test with a truncation of output to 128 bits. @@ -356,7 +426,7 @@ mod hmac_tests { &hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(), KeyType::MACKey, ) - .unwrap(); + .unwrap(); let mut out = [0u8; 128 / 8]; HMAC::::new(&key).unwrap().mac_out(b"Test With Truncation", &mut out).unwrap(); assert_eq!(&Vec::from(out), &hex::decode("a3b6167473100ee06e0c796c2955552b").unwrap()); @@ -365,14 +435,14 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", - &hex::decode("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54").unwrap() + &hex::decode("60e431591ee0b67f0d8a26aacbf5b77f8e0bc6213728c5140546040f0ee37f54").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", - &hex::decode("9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2").unwrap() + &hex::decode("9b09ffa71b942fcb27635fbcd5b0e944bfdc63644f0713938a7f51535c3a35e2").unwrap(), ); } @@ -381,8 +451,10 @@ mod hmac_tests { // test with zero-length key let test_framework = TestFrameworkMAC::new(); let mut zero_length_key = KeyMaterial256::default(); - zero_length_key.allow_hazardous_operations(); - zero_length_key.convert_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut zero_length_key, |zero_length_key| { + zero_length_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); @@ -414,14 +486,14 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), - &hex::decode("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27").unwrap() + &hex::decode("88062608d3e6ad8a0aa2ace014c8a86f0aa635d947ac9febe83ef4e55966144b2a5ab39dc13814b94e3ab6e101a34f27").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), - &hex::decode("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb").unwrap() + &hex::decode("3e8a69b7783c25851933ab6290af6ca77a9981480850009cc5577c6e1f573b4e6801dd23c4a7d679ccf8a386c674cffb").unwrap(), ); // RFC4231 Test Case 5 -- Test with a truncation of output to 128 bits. @@ -429,7 +501,7 @@ mod hmac_tests { &hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(), KeyType::MACKey, ) - .unwrap(); + .unwrap(); let mut out = [0u8; 128 / 8]; // Key is shorter than HMAC security strength, so need to use new_allow_weak_keys() let hmac = HMAC::::new_allow_weak_key(&key).unwrap(); @@ -440,14 +512,14 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", - &hex::decode("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952").unwrap() + &hex::decode("4ece084485813e9088d2c63a041bc5b44f9ef1012a2b588f3cd11f05033ac4c60c2ef6ab4030fe8296248df163f44952").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", - &hex::decode("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e").unwrap() + &hex::decode("6617178e941f020d351e2f254e8fd32c602420feb0b8fb9adccebb82461e99c5a678cc31e799176d3860e6110c46523e").unwrap(), ); } @@ -456,8 +528,10 @@ mod hmac_tests { // test with zero-length key let test_framework = TestFrameworkMAC::new(); let mut zero_length_key = KeyMaterial256::default(); - zero_length_key.allow_hazardous_operations(); - zero_length_key.convert_key_type(KeyType::MACKey).unwrap(); + key_material::do_hazardous_operations(&mut zero_length_key, |zero_length_key| { + zero_length_key.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(zero_length_key.key_len(), 0); assert_eq!(zero_length_key.key_type(), KeyType::MACKey); @@ -490,14 +564,14 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd").unwrap(), - &hex::decode("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb").unwrap() + &hex::decode("fa73b0089d56a284efb0f0756c890be9b1b5dbdd8ee81a3655f83e33b2279d39bf3e848279a722c806b485a47e67c807b946a337bee8942674278859e13292fb").unwrap(), ); // RFC4231 Test Case 4 -- Test with a combined length of key and data that is larger than 64 // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial256::from_bytes_as_type(&hex::decode("0102030405060708090a0b0c0d0e0f10111213141516171819").unwrap(), KeyType::MACKey).unwrap(), &hex::decode("cdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcdcd").unwrap(), - &hex::decode("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd").unwrap() + &hex::decode("b0ba465637458c6990e5a8c5f61d4af7e576d97ff94b872de76f8050361ee3dba91ca5c11aa25eb4d679275cc5788063a5f19741120c4f2de2adebeb10a298dd").unwrap(), ); // RFC4231 Test Case 5 -- Test with a truncation of output to 128 bits. @@ -505,7 +579,7 @@ mod hmac_tests { &hex::decode("0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c").unwrap(), KeyType::MACKey, ) - .unwrap(); + .unwrap(); let mut out = [0u8; 128 / 8]; // Key is shorter than HMAC security strength, so need to use new_allow_weak_keys() let hmac = HMAC::::new_allow_weak_key(&key).unwrap(); @@ -516,15 +590,98 @@ mod hmac_tests { // bytes (= block-size of SHA-224 and SHA-256). test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"Test Using Larger Than Block-Size Key - Hash Key First", - &hex::decode("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598").unwrap() + &hex::decode("80b24263c7c1a3ebb71493c1dd7be8b49b46d1f41b4aeec1121b013783f8f3526b56d037e05f2598bd0fd2215d6a1e5295e64f73f63f0aec8b915a985d786598").unwrap(), ); // RFC4231 Test Case 7 -- Test with a key and data that is larger than 128 bytes (= block-size // of SHA-384 and SHA-512) test_framework.test_mac::>(&KeyMaterial::<131>::from_bytes_as_type(&hex::decode("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa").unwrap(), KeyType::MACKey).unwrap(), b"This is a test using a larger than block-size key and a larger than block-size data. The key needs to be hashed before being used by the HMAC algorithm.", - &hex::decode("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58").unwrap() + &hex::decode("e37b6a775dc87dbaa4dfa9f96e5e3ffddebd71f8867289865df5a32d20cdc944b6022cac3c4982b10d5eeb55c3e4de15134676fb6de0446065c97440fa8c6a58").unwrap(), ); } } + + #[test] + fn suspendable_keyed_state() { + use bouncycastle_core::errors::SuspendableError; + use bouncycastle_core::suspendable_state::LIB_VERSION; + use bouncycastle_core::traits::SuspendableKeyed; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableKeyedState; + + let key = + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::MACKey).unwrap(); + let msg = b"Colorless green ideas sleep furiously"; + + // A helper that exercises the full round-trip for one HMAC variant. HMAC is keyed, so the + // key is NOT in the serialized state -- it is re-supplied (by reference) to + // from_serialized_state. + // The `+ 'static` on the trait object matches the associated type `type Key = dyn + // KeyMaterialTrait` (a bare `dyn` in an associated type defaults to `'static`). The concrete + // key types are owned, so they satisfy it. + fn round_trip( + mut hmac: H, + key: &(dyn KeyMaterialTrait + 'static), + input: &[u8], + ) where + H: MAC + Clone + SuspendableKeyed, + { + hmac.do_update(&input[..10]); + + // do the default trait-conformance tests + TestFrameworkSuspendableKeyedState::new().test(&hmac, key); + + // serialize the in-progress state (on a clone), then finish the original + let serialized_state = hmac.clone().suspend(); + + // the serialized state carries the library version header (from the inner hash) + let header: [u8; 3] = serialized_state[..3].try_into().unwrap(); + assert_eq!(header, <[u8; 3]>::from(LIB_VERSION)); + + hmac.do_update(&input[10..]); + let expected = hmac.do_final(); + + // rebuild from the serialized state (re-supplying the key), feed the identical remaining + // input, and confirm the MAC matches + let mut from_state = H::from_suspended(serialized_state, key).unwrap(); + from_state.do_update(&input[10..]); + assert_eq!(expected, from_state.do_final()); + + // a state whose version header is zeroed must be rejected (delegated to the hash's impl) + let mut busted = serialized_state; + busted[..3].copy_from_slice(&[0, 0, 0]); + match H::from_suspended(busted, key) { + Err(SuspendableError::IncompatibleVersion) => { /* good */ } + _ => panic!("Expected IncompatibleVersion for a zeroed version header"), + } + } + + round_trip(HMAC_SHA256::new(&key).unwrap(), &key, msg); + round_trip(HMAC_SHA512::new(&key).unwrap(), &key, msg); + round_trip(HMAC_SHA3_256::new(&key).unwrap(), &key, msg); + + // test suspend / resume with a key larger than block size + let long_key = + KeyMaterial::<200>::from_bytes_as_type(&DUMMY_SEED_512[..200], KeyType::MACKey) + .unwrap(); + round_trip(HMAC_SHA256::new(&long_key).unwrap(), &long_key, msg); + } + + /// Tests that no private data is displayed + #[test] + fn test_display() { + let key = KeyMaterial256::from_bytes_as_type( + &hex::decode("0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b").unwrap(), + KeyType::MACKey, + ) + .unwrap(); + let hmac = HMAC_SHA256::new(&key).unwrap(); + + // test fmt + let fmt_str = format!("{}", &hmac); + assert_eq!(fmt_str, "HMAC-SHA256 instance"); + + // test debug + assert_eq!(format!("{:?}", &hmac), "HMAC-SHA256 instance"); + } } diff --git a/crypto/mldsa_lowmemory/Cargo.toml b/crypto/mldsa-lowmemory/Cargo.toml similarity index 96% rename from crypto/mldsa_lowmemory/Cargo.toml rename to crypto/mldsa-lowmemory/Cargo.toml index f9a0a81..83ccce7 100644 --- a/crypto/mldsa_lowmemory/Cargo.toml +++ b/crypto/mldsa-lowmemory/Cargo.toml @@ -15,6 +15,7 @@ bouncycastle-core-test-framework.workspace = true bouncycastle-hex.workspace = true bouncycastle-rng.workspace = true criterion.workspace = true +serde_json = "1.0" [[bench]] name = "mldsa_benches" diff --git a/crypto/mldsa_lowmemory/benches/mldsa_benches.rs b/crypto/mldsa-lowmemory/benches/mldsa_benches.rs similarity index 88% rename from crypto/mldsa_lowmemory/benches/mldsa_benches.rs rename to crypto/mldsa-lowmemory/benches/mldsa_benches.rs index 79ffd00..6927d0d 100644 --- a/crypto/mldsa_lowmemory/benches/mldsa_benches.rs +++ b/crypto/mldsa-lowmemory/benches/mldsa_benches.rs @@ -1,9 +1,11 @@ -use criterion::{Criterion, criterion_group, criterion_main}; use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; -use std::hint::black_box; -use bouncycastle_core::traits::Signature; -use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA65, MLDSA65_SIG_LEN, MLDSA87, MLDSA87_SIG_LEN}; +use bouncycastle_core::traits::{SignatureVerifier, Signer}; use bouncycastle_hex as hex; +use bouncycastle_mldsa_lowmemory::{ + MLDSA44, MLDSA44_SIG_LEN, MLDSA65, MLDSA65_SIG_LEN, MLDSA87, MLDSA87_SIG_LEN, MLDSATrait, +}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_mldsa_keygen(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen"); @@ -18,11 +20,11 @@ fn bench_mldsa_keygen(c: &mut Criterion) { group.throughput(criterion::Throughput::Elements(seeds.len() as u64)); group.bench_function("ML-DSA-44_lowmemory", |b| { - b.iter(|| { - for seed in seeds.iter() { - black_box(MLDSA44::keygen_from_seed(seed)).unwrap(); - } - }) + b.iter(|| { + for seed in seeds.iter() { + black_box(MLDSA44::keygen_from_seed(seed)).unwrap(); + } + }) }); group.bench_function("ML-DSA-65_lowmemory", |b| { @@ -52,16 +54,16 @@ fn bench_mldsa_sign(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; /*** ML-DSA-44 ***/ let (_mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -74,7 +76,6 @@ fn bench_mldsa_sign(c: &mut Criterion) { }) }); - /*** ML-DSA-65 ***/ let (_mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); @@ -87,7 +88,6 @@ fn bench_mldsa_sign(c: &mut Criterion) { }) }); - /*** ML-DSA-87 ***/ let (_mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); @@ -112,21 +112,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; - /*** ML-DSA-44 ***/ let (mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA44_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA44_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -136,22 +136,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA44::verify(&mldsa44_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA44::verify(&mldsa44_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); - /*** ML-DSA-65 ***/ let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA65_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA65_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -161,22 +160,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA65::verify(&mldsa65_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA65::verify(&mldsa65_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); - /*** ML-DSA-87 ***/ let (mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA87_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA87_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -186,7 +184,7 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA87::verify(&mldsa87_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA87::verify(&mldsa87_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); @@ -194,7 +192,6 @@ fn bench_mldsa_verify(c: &mut Criterion) { group.finish(); } - criterion_group!(benches, bench_mldsa_keygen, bench_mldsa_sign, bench_mldsa_verify); criterion_main!(benches); diff --git a/crypto/mldsa_lowmemory/benches/note_on_mem_usage_benches.md b/crypto/mldsa-lowmemory/benches/note_on_mem_usage_benches.md similarity index 100% rename from crypto/mldsa_lowmemory/benches/note_on_mem_usage_benches.md rename to crypto/mldsa-lowmemory/benches/note_on_mem_usage_benches.md diff --git a/crypto/mldsa_lowmemory/src/aux_functions.rs b/crypto/mldsa-lowmemory/src/aux_functions.rs similarity index 94% rename from crypto/mldsa_lowmemory/src/aux_functions.rs rename to crypto/mldsa-lowmemory/src/aux_functions.rs index a8ac1a2..f525609 100644 --- a/crypto/mldsa_lowmemory/src/aux_functions.rs +++ b/crypto/mldsa-lowmemory/src/aux_functions.rs @@ -3,8 +3,7 @@ // use crate::matrix::{Matrix, Vector}; use crate::mldsa::{G, H, POLY_T0PACKED_LEN}; use crate::mldsa::{ - MLDSA44_GAMMA1, MLDSA44_GAMMA2, MLDSA65_GAMMA1, MLDSA65_GAMMA2, N, - POLY_T1PACKED_LEN, d, q, + MLDSA44_GAMMA1, MLDSA44_GAMMA2, MLDSA65_GAMMA1, MLDSA65_GAMMA2, N, POLY_T1PACKED_LEN, d, q, }; use crate::polynomial::Polynomial; use bouncycastle_core::traits::XOF; @@ -24,6 +23,7 @@ pub(crate) fn coeff_from_three_bytes(b: &[u8; 3]) -> Result { let z: i32 = ((b2_prime as i32) << 16) | ((b[1] as i32) << 8) | (b[0] as i32); + // mutants note: yeah, this could be z <= q and nothing changes because z == q is the same as z == 0 if z < q { Ok(z) } else { Err(()) } } @@ -59,12 +59,11 @@ pub(crate) fn simple_bit_pack_t1(w: &Polynomial) -> [u8; POLY_T1PACKED_LEN] { output[5 * i + 1] = ((w[4 * i] >> 8) | (w[4 * i + 1] << 2)) as u8; output[5 * i + 2] = ((w[4 * i + 1] >> 6) | (w[4 * i + 2] << 4)) as u8; output[5 * i + 3] = ((w[4 * i + 2] >> 4) | (w[4 * i + 3] << 6)) as u8; - output[5 * i + 4] = (w[4 * i + 3] >> 2) as u8; + output[5 * i + 4] = (w[4 * i + 3] >> 2) as u8; } output } - /// As defined in Algorithm 17, this gives the length of a packed bitstring representing a polynomial /// whose coefficients have been rounded to \[-eta, eta], which is 32*bitlen(2*eta). pub const fn bitlen_eta(eta: usize) -> usize { @@ -166,7 +165,10 @@ pub(crate) fn bit_pack_t0(t0: &Polynomial) -> [u8; POLY_T0PACKED_LEN] { /// A variant of Algorithm 17 specific to packing z in the signature value in \[−𝛾1 + 1, 𝛾1]. pub(crate) fn bitpack_gamma1( z: &Polynomial, - out: &mut [u8; POLY_Z_PACKED_LEN]) { + out: &mut [u8; POLY_Z_PACKED_LEN], +) { + out.fill(0); + let mut t: [u32; 4] = [0; 4]; match GAMMA1 { MLDSA44_GAMMA1 => { @@ -217,7 +219,7 @@ pub(crate) fn simple_bit_unpack_t1(v: &[u8; POLY_T1PACKED_LEN]) -> Polynomial { let mut w = Polynomial::new(); - for i in 0..N/4 { + for i in 0..N / 4 { w[4 * i] = ((v[5 * i] as i32) | ((v[5 * i + 1] as i32) << 8)) & 0x3FF; w[4 * i + 1] = (((v[5 * i + 1] as i32) >> 2) | ((v[5 * i + 2] as i32) << 6)) & 0x3FF; w[4 * i + 2] = (((v[5 * i + 2] as i32) >> 4) | ((v[5 * i + 3] as i32) << 4)) & 0x3FF; @@ -227,7 +229,6 @@ pub(crate) fn simple_bit_unpack_t1(v: &[u8; POLY_T1PACKED_LEN]) -> Polynomial { w } - /// A variant of Algorithm 19 BitUnpack specific to a=eta, b=eta /// Input: 𝑎, 𝑏 ∈ ℕ and a byte string 𝑣 of length 32 ⋅ bitlen (𝑎 + 𝑏). /// Output: A polynomial 𝑤 ∈ 𝑅 with coefficients in [𝑏 − 2𝑐 + 1, 𝑏], where 𝑐 = bitlen (𝑎 + 𝑏). @@ -344,15 +345,13 @@ pub(crate) fn bit_unpack_gamma1(v: &[u8]) -> Polynomial { } /// Part of unpacking the sig value -pub(crate) fn unpack_c_tilde< - const LAMBDA_over_4: usize, ->(sig: &[u8]) -> &[u8; LAMBDA_over_4] { +pub(crate) fn unpack_c_tilde(sig: &[u8]) -> &[u8; LAMBDA_over_4] { sig[..LAMBDA_over_4].try_into().unwrap() } /// Part of unpacking the sig value pub(crate) fn unpack_z_row< const GAMMA1: i32, - const BETA: i32, + const GAMMA1_MINUS_BETA: i32, const LAMBDA_over_4: usize, const POLY_Z_PACKED_LEN: usize, const SIG_LEN: usize, @@ -360,16 +359,17 @@ pub(crate) fn unpack_z_row< idx: usize, sig: &[u8; SIG_LEN], ) -> Result { - // assert: idx < l + // assert: idx < l, but we don't have easy access to l // skip to the start of the z's let pos = LAMBDA_over_4; let z = bit_unpack_gamma1::( - &sig[pos + idx * POLY_Z_PACKED_LEN..pos + (idx + 1) * POLY_Z_PACKED_LEN]); + &sig[pos + idx * POLY_Z_PACKED_LEN..pos + (idx + 1) * POLY_Z_PACKED_LEN], + ); // Perform the norm check from // Alg 8; Line 13 (first half) return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] - if z.check_norm(GAMMA1 - BETA) { Err(()) } else { Ok(z) } + if z.check_norm::() { Err(()) } else { Ok(z) } } /// Part of unpacking the sig value pub(crate) fn unpack_h_row< @@ -391,7 +391,6 @@ pub(crate) fn unpack_h_row< // skip over the other stuff in the encoded sig value let pos = LAMBDA_over_4 + l * POLY_Z_PACKED_LEN; - // This inlines Algorithm 21 HintBitUnpack(𝑦) // 2: Index ← 0 @@ -399,12 +398,13 @@ pub(crate) fn unpack_h_row< // let mut idx = 0usize; // This row calc is a bit weird because technically it's supposed to be done at the end // of the previous loop - let idx = if row == 0 { 0 } else { sig[pos + OMEGA as usize + row-1] as usize }; + let idx = if row == 0 { 0 } else { sig[pos + OMEGA as usize + row - 1] as usize }; // 3: for 𝑖 from 0 to 𝑘 − 1 do // ▷ reconstruct 𝐡[𝑖] // for i in 0..k { // 4: if 𝑦[𝜔 + 𝑖] < Index or 𝑦[𝜔 + 𝑖] > 𝜔 then return ⊥ + // mutants note: don't have test vectors that exercise this condition if sig[pos + (OMEGA as usize) + row] < (idx as u8) || sig[pos + (OMEGA as usize) + row] > OMEGA as u8 { @@ -418,6 +418,7 @@ pub(crate) fn unpack_h_row< // 8: if Index > First then // 9: if 𝑦[Index − 1] ≥ 𝑦[Index] then return ⊥ // ▷ malformed input + // mutants note: don't have test vectors that exercise this condition if j > idx && sig[pos + j - 1] >= sig[pos + j] { return None; } @@ -429,6 +430,7 @@ pub(crate) fn unpack_h_row< } // ▷ read any leftover bytes in the first 𝜔 bytes of 𝑦 for malformed (nonzero) bytes + // mutants note: if row == k - 1 { let idx = sig[pos + OMEGA as usize + row] as usize; for j in idx..OMEGA as usize { @@ -693,6 +695,10 @@ pub(crate) fn decompose(r: i32) -> (i32, i32) { } r1 = r - r0 * 2 * GAMMA2; + + // mutants note: the choice of (q - 1) is a bit arbitrary in that after doing the bit-shifting, + // this seems to work out mathematically equivalent if you do q/2, or (q+3)/2, but we'll leave it as (q-1)/2 + // since that's algorithmically correct, and just ignore the mutants results. r1 -= (((q - 1) / 2 - r1) >> 31) & q; (r0, r1) @@ -735,13 +741,8 @@ pub(crate) fn make_hint(z: i32, r: i32) -> i32 { // if r1 != v1 { 1 } else { 0 } // By the powers of someone much more clever than me, this is equivalent. - - if z <= GAMMA2 || z > q - GAMMA2 || (z == q - GAMMA2 && r == 0) - { - 0 - } else { - 1 - } + // mutants note: we do not have KATs that exercise all branches of this if + if z <= GAMMA2 || z > q - GAMMA2 || (z == q - GAMMA2 && r == 0) { 0 } else { 1 } } /// Algorithm 40 UseHint(ℎ, 𝑟) @@ -759,6 +760,8 @@ pub(super) fn use_hint(a: i32, hint: i32) -> i32 { match GAMMA2 { MLDSA44_GAMMA2 => { + // mutants note: this passes unit tests if it's a1 >= 0 + // we'll leave it like this because it matches the spec if a1 > 0 { if a0 == 43 { 0 } else { a0 + 1 } } else { @@ -767,11 +770,9 @@ pub(super) fn use_hint(a: i32, hint: i32) -> i32 { } // ML-DSA65 and 87 have the same GAMMA2 MLDSA65_GAMMA2 => { - if a1 > 0 { - (a0 + 1) & 15 - } else { - (a0 - 1) & 15 - } + // mutants note: this passes unit tests if it's a0 >= 0 + // we'll leave it like this because it matches the spec + if a1 > 0 { (a0 + 1) & 15 } else { (a0 - 1) & 15 } } _ => { panic!("Invalid GAMMA2 value") diff --git a/crypto/mldsa_lowmemory/src/hash_mldsa.rs b/crypto/mldsa-lowmemory/src/hash_mldsa.rs similarity index 80% rename from crypto/mldsa_lowmemory/src/hash_mldsa.rs rename to crypto/mldsa-lowmemory/src/hash_mldsa.rs index 81a0b1f..179301c 100644 --- a/crypto/mldsa_lowmemory/src/hash_mldsa.rs +++ b/crypto/mldsa-lowmemory/src/hash_mldsa.rs @@ -3,11 +3,11 @@ //! mode of [MLDSA]; possibly because you have to digest the message before you know which public key //! will sign it. //! -//! HashML-DSA is a full signature algorithm implementing the [Signature] trait: +//! HashML-DSA is a full signature algorithm implementing the [Signer] and [SignatureVerifier] traits: //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSATrait, HashMLDSA65_with_SHA512, HashMLDSA44_with_SHA512}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -24,11 +24,13 @@ //! } //! ``` //! -//! But you also have access to the pre-hashed function available from [PHSignature]: +//! But you also have access to the pre-hashed function available from [PHSigner] and [PHSignatureVerifier]: //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::{Signature, PHSignature, Hash}; +//! use bouncycastle_core::traits::{ +//! Hash, PHSignatureVerifier, PHSigner, SignatureVerifier, Signer, +//! }; //! use bouncycastle_sha2::SHA512; //! use bouncycastle_mldsa_lowmemory::{MLDSATrait, HashMLDSA65_with_SHA512, HashMLDSA44_with_SHA512}; //! @@ -44,14 +46,14 @@ //! let sig = HashMLDSA65_with_SHA512::sign_ph(&sk, &ph, None).unwrap(); //! // This is the signature value that you can save to a file or whatever you need. //! -//! // This verifies either through the usual one-shot API of the [Signature] trait +//! // This verifies either through the usual one-shot API of the [SignatureVerifier] trait //! match HashMLDSA65_with_SHA512::verify(&pk, msg, None, &sig) { //! Ok(()) => println!("Signature is valid!"), //! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), //! Err(e) => panic!("Something else went wrong: {:?}", e), //! } //! -//! // Or though the verify_ph of the [PHSignature] trait +//! // Or though the verify_ph of the [PHSignatureVerifier] trait //! match HashMLDSA65_with_SHA512::verify_ph(&pk, &ph, None, &sig) { //! Ok(()) => println!("Signature is valid!"), //! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), @@ -61,30 +63,30 @@ //! //! Note that the [HashMLDSA] object is just a light wrapper around [MLDSA], and, for example, they share key types, //! so if you need the fancy keygen functions, just use them from [MLDSA]. -//! But a simple [HashMLDSA::keygen] is provided in order to have conformance to the [Signature] trait. +//! But a simple [HashMLDSA::keygen] is provided. use crate::mldsa::{H, MLDSA_MU_LEN, MLDSA_RND_LEN, MLDSATrait}; use crate::mldsa::{ MLDSA44_BETA, MLDSA44_C_TILDE, MLDSA44_ETA, MLDSA44_FULL_SK_LEN, MLDSA44_GAMMA1, - MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA2, MLDSA44_LAMBDA, MLDSA44_LAMBDA_over_4, MLDSA44_OMEGA, - MLDSA44_PK_LEN, MLDSA44_POLY_ETA_PACKED_LEN, MLDSA44_POLY_W1_PACKED_LEN, - MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_SIG_LEN, - MLDSA44_SK_LEN, MLDSA44_TAU, MLDSA44_k, MLDSA44_l, + MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA1_MINUS_BETA, MLDSA44_GAMMA2, MLDSA44_GAMMA2_MINUS_BETA, + MLDSA44_LAMBDA, MLDSA44_LAMBDA_over_4, MLDSA44_OMEGA, MLDSA44_PK_LEN, + MLDSA44_POLY_W1_PACKED_LEN, MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_S1_PACKED_LEN, + MLDSA44_S2_PACKED_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN, MLDSA44_TAU, MLDSA44_k, MLDSA44_l, }; use crate::mldsa::{MLDSA44_T1_PACKED_LEN, MLDSA65_T1_PACKED_LEN, MLDSA87_T1_PACKED_LEN}; use crate::mldsa::{ MLDSA65_BETA, MLDSA65_C_TILDE, MLDSA65_ETA, MLDSA65_FULL_SK_LEN, MLDSA65_GAMMA1, - MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA2, MLDSA65_LAMBDA, MLDSA65_LAMBDA_over_4, MLDSA65_OMEGA, - MLDSA65_PK_LEN, MLDSA65_POLY_ETA_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, - MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_SIG_LEN, - MLDSA65_SK_LEN, MLDSA65_TAU, MLDSA65_k, MLDSA65_l, + MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA1_MINUS_BETA, MLDSA65_GAMMA2, MLDSA65_GAMMA2_MINUS_BETA, + MLDSA65_LAMBDA, MLDSA65_LAMBDA_over_4, MLDSA65_OMEGA, MLDSA65_PK_LEN, + MLDSA65_POLY_W1_PACKED_LEN, MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_S1_PACKED_LEN, + MLDSA65_S2_PACKED_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN, MLDSA65_TAU, MLDSA65_k, MLDSA65_l, }; use crate::mldsa::{ MLDSA87_BETA, MLDSA87_C_TILDE, MLDSA87_ETA, MLDSA87_FULL_SK_LEN, MLDSA87_GAMMA1, - MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA2, MLDSA87_LAMBDA, MLDSA87_LAMBDA_over_4, MLDSA87_OMEGA, - MLDSA87_PK_LEN, MLDSA87_POLY_ETA_PACKED_LEN, MLDSA87_POLY_W1_PACKED_LEN, - MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_SIG_LEN, - MLDSA87_SK_LEN, MLDSA87_TAU, MLDSA87_k, MLDSA87_l, + MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA1_MINUS_BETA, MLDSA87_GAMMA2, MLDSA87_GAMMA2_MINUS_BETA, + MLDSA87_LAMBDA, MLDSA87_LAMBDA_over_4, MLDSA87_OMEGA, MLDSA87_PK_LEN, + MLDSA87_POLY_W1_PACKED_LEN, MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_S1_PACKED_LEN, + MLDSA87_S2_PACKED_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN, MLDSA87_TAU, MLDSA87_k, MLDSA87_l, }; use crate::mldsa_keys::{MLDSAPrivateKeyInternalTrait, MLDSAPublicKeyInternalTrait}; use crate::{ @@ -94,7 +96,8 @@ use crate::{ use bouncycastle_core::errors::SignatureError; use bouncycastle_core::key_material::KeyMaterial; use bouncycastle_core::traits::{ - Algorithm, Hash, PHSignature, RNG, SecurityStrength, Signature, XOF, + Algorithm, Hash, PHSignatureVerifier, PHSigner, RNG, SecurityStrength, SignatureVerifier, + Signer, XOF, }; use bouncycastle_rng::HashDRBG_SHA512; use bouncycastle_sha2::{SHA256, SHA512}; @@ -151,8 +154,9 @@ pub type HashMLDSA44_with_SHA256 = HashMLDSA< MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_T1_PACKED_LEN, - MLDSA44_POLY_ETA_PACKED_LEN, MLDSA44_LAMBDA_over_4, + MLDSA44_GAMMA1_MINUS_BETA, + MLDSA44_GAMMA2_MINUS_BETA, MLDSA44_GAMMA1_MASK_LEN, >; @@ -188,8 +192,9 @@ pub type HashMLDSA65_with_SHA256 = HashMLDSA< MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_T1_PACKED_LEN, - MLDSA65_POLY_ETA_PACKED_LEN, MLDSA65_LAMBDA_over_4, + MLDSA65_GAMMA1_MINUS_BETA, + MLDSA65_GAMMA2_MINUS_BETA, MLDSA65_GAMMA1_MASK_LEN, >; @@ -225,8 +230,9 @@ pub type HashMLDSA87_with_SHA256 = HashMLDSA< MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_T1_PACKED_LEN, - MLDSA87_POLY_ETA_PACKED_LEN, MLDSA87_LAMBDA_over_4, + MLDSA87_GAMMA1_MINUS_BETA, + MLDSA87_GAMMA2_MINUS_BETA, MLDSA87_GAMMA1_MASK_LEN, >; @@ -262,8 +268,9 @@ pub type HashMLDSA44_with_SHA512 = HashMLDSA< MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_T1_PACKED_LEN, - MLDSA44_POLY_ETA_PACKED_LEN, MLDSA44_LAMBDA_over_4, + MLDSA44_GAMMA1_MINUS_BETA, + MLDSA44_GAMMA2_MINUS_BETA, MLDSA44_GAMMA1_MASK_LEN, >; @@ -299,8 +306,9 @@ pub type HashMLDSA65_with_SHA512 = HashMLDSA< MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_T1_PACKED_LEN, - MLDSA65_POLY_ETA_PACKED_LEN, MLDSA65_LAMBDA_over_4, + MLDSA65_GAMMA1_MINUS_BETA, + MLDSA65_GAMMA2_MINUS_BETA, MLDSA65_GAMMA1_MASK_LEN, >; @@ -336,8 +344,9 @@ pub type HashMLDSA87_with_SHA512 = HashMLDSA< MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_T1_PACKED_LEN, - MLDSA87_POLY_ETA_PACKED_LEN, MLDSA87_LAMBDA_over_4, + MLDSA87_GAMMA1_MINUS_BETA, + MLDSA87_GAMMA2_MINUS_BETA, MLDSA87_GAMMA1_MASK_LEN, >; @@ -397,8 +406,9 @@ pub struct HashMLDSA< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, > { _phantom: PhantomData<(PK, SK)>, @@ -468,8 +478,9 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, > HashMLDSA< @@ -497,11 +508,48 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, > { + /// Generate a keypair, sourcing randomness from bouncycastle's default os-backed RNG. + /// + /// Key generation is intentionally not part of the [Signer] / [SignatureVerifier] traits; + /// it is provided as an inherent associated function directly on the algorithm struct. + /// Keys are interchangeable between MLDSA and HashMLDSA. + /// Error condition: basically only on RNG failures. + pub fn keygen() -> Result<(PK, SK), SignatureError> { + MLDSA::< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + >::keygen() + } + /// Imports a secret key from a seed. pub fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> { MLDSA::< @@ -526,8 +574,9 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, >::keygen_internal(seed) } @@ -583,6 +632,8 @@ impl< return Err(SignatureError::LengthError("ctx value is longer than 255 bytes")); } + output.fill(0); + // Algorithm 7 // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀', 64) let mut h = H::new(); @@ -623,17 +674,18 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, >::sign_mu_deterministic_out(sk, &mu, rnd, output)?; Ok(bytes_written) } - /// To be used for deterministic signing in conjunction with the [Signature::sign_init], - /// [Signature::sign_update], and [Signature::sign_final] flow. - /// Can be set anywhere after [Signature::sign_init] and before [Signature::sign_final] + /// To be used for deterministic signing in conjunction with the [Signer::sign_init], + /// [Signer::sign_update], and [Signer::sign_final] flow. + /// Can be set anywhere after [Signer::sign_init] and before [Signer::sign_final] pub fn set_signer_rnd(&mut self, rnd: [u8; 32]) { self.signer_rnd = Some(rnd); } @@ -719,10 +771,11 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, -> Signature +> Signer for HashMLDSA< HASH, PH_LEN, @@ -748,41 +801,12 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, > { - /// Keygen, and keys in general, are interchangeable between MLDSA and HashMLDSA. - fn keygen() -> Result<(PK, SK), SignatureError> { - MLDSA::< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - S1_PACKED_LEN, - S2_PACKED_LEN, - T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, - >::keygen() - } - /// Algorithm 4 HashML-DSA.Sign(𝑠𝑘, 𝑀 , 𝑐𝑡𝑥, PH) /// Generate a “pre-hash” ML-DSA signature. fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> { @@ -798,6 +822,8 @@ impl< ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut ph_m = [0u8; PH_LEN]; _ = HASH::default().hash_out(msg, &mut ph_m); Self::sign_ph_out(sk, &ph_m, ctx, output) @@ -836,28 +862,18 @@ impl< )); } - if output.len() < SIG_LEN { - return Err(SignatureError::LengthError( - "Output buffer insufficient size to hold signature", - )); - } - let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap(); + output.fill(0); if self.sk.is_some() { if self.signer_rnd.is_none() { - Self::sign_ph_out( - &self.sk.unwrap(), - &ph, - Some(&self.ctx[..self.ctx_len]), - output_sized, - ) + Self::sign_ph_out(&self.sk.unwrap(), &ph, Some(&self.ctx[..self.ctx_len]), output) } else { Self::sign_ph_deterministic_out( &self.sk.unwrap(), Some(&self.ctx[..self.ctx_len]), &ph, self.signer_rnd.unwrap(), - output_sized, + output, ) } } else if self.seed.is_some() { @@ -871,18 +887,94 @@ impl< // since at this point we need to fully reconstruct SK in order to compute tr for mu anyway // there is no savings to using the fancy MLDSA::sign_from_seed let (_pk, sk) = Self::keygen_from_seed(&self.seed.unwrap())?; - Self::sign_ph_deterministic_out( - &sk, - Some(&self.ctx[..self.ctx_len]), - &ph, - rnd, - output_sized, - ) + Self::sign_ph_deterministic_out(&sk, Some(&self.ctx[..self.ctx_len]), &ph, rnd, output) } else { unreachable!() } } +} +impl< + HASH: Hash + Default, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, + const PH_LEN: usize, + const oid: &'static [u8], + const PK_LEN: usize, + const SK_LEN: usize, + const FULL_SK_LEN: usize, + const SIG_LEN: usize, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const S1_PACKED_LEN: usize, + const S2_PACKED_LEN: usize, + const T1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, +> SignatureVerifier + for HashMLDSA< + HASH, + PH_LEN, + oid, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> { let mut ph_m = [0u8; PH_LEN]; _ = HASH::default().hash_out(msg, &mut ph_m); @@ -963,10 +1055,11 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, -> PHSignature +> PHSigner for HashMLDSA< HASH, PH_LEN, @@ -992,8 +1085,9 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, > { @@ -1003,7 +1097,7 @@ impl< ctx: Option<&[u8]>, ) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; - Self::sign_out(sk, ph, ctx, &mut out)?; + Self::sign_ph_out(sk, ph, ctx, &mut out)?; Ok(out) } @@ -1018,17 +1112,95 @@ impl< ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { - if output.len() < SIG_LEN { - return Err(SignatureError::LengthError( - "Output buffer insufficient size to hold signature", - )); - } + output.fill(0); let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN]; HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; Self::sign_ph_deterministic_out(sk, ctx, ph, rnd, output) } +} +impl< + HASH: Hash + Default, + const PH_LEN: usize, + const oid: &'static [u8], + const PK_LEN: usize, + const SK_LEN: usize, + const FULL_SK_LEN: usize, + const SIG_LEN: usize, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const S1_PACKED_LEN: usize, + const S2_PACKED_LEN: usize, + const T1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, +> PHSignatureVerifier + for HashMLDSA< + HASH, + PH_LEN, + oid, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify_ph( pk: &PK, ph: &[u8; PH_LEN], @@ -1064,7 +1236,7 @@ impl< let mut mu = [0u8; MLDSA_MU_LEN]; _ = h.squeeze_out(&mut mu); - if MLDSA::< + MLDSA::< PK_LEN, SK_LEN, FULL_SK_LEN, @@ -1086,14 +1258,10 @@ impl< S1_PACKED_LEN, S2_PACKED_LEN, T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, GAMMA1_MASK_LEN, - >::verify_mu_internal(pk, &mu, sig_sized) - { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + >::verify_mu(pk, &mu, sig_sized) } } diff --git a/crypto/mldsa_lowmemory/src/lib.rs b/crypto/mldsa-lowmemory/src/lib.rs similarity index 97% rename from crypto/mldsa_lowmemory/src/lib.rs rename to crypto/mldsa-lowmemory/src/lib.rs index ccc5a33..5d2ed88 100644 --- a/crypto/mldsa_lowmemory/src/lib.rs +++ b/crypto/mldsa-lowmemory/src/lib.rs @@ -45,7 +45,7 @@ //! //! We also get a surprising amount of memory-savings by good coding hygiene: //! Using un-named scopes to tell the compiler when an intermediate variable is no longer needed and -//! con be popped off the stack. This sometimes requires re-ordering the steps of the algorithms given in +//! can be popped off the stack. This sometimes requires re-ordering the steps of the algorithms given in //! FIPS 204 so that variables can be created, used, and released in a self-contained block. //! Sometimes this is not possible and we have to make a choice between keeping the variable around //! or releasing it and re-deriving it later. @@ -128,8 +128,7 @@ //! ## Generating Keys //! //! ```rust -//! use bouncycastle_core::traits::Signature; -//! use bouncycastle_mldsa_lowmemory::MLDSA65; +//! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); //! ``` @@ -153,13 +152,13 @@ //! //! See [MLDSATrait] and [MLDSATrait::sign_mu_deterministic_from_seed] for an API flow that uses a merged //! keygen-and-sign function to provide improved speed and memory performance compared with making -//! separate calls to [MLDSATrait::keygen_from_seed] followed by [Signature::sign]. +//! separate calls to [MLDSATrait::keygen_from_seed] followed by [Signer::sign]. //! //! ## Generating and Verifying Signatures //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait}; //! //! let msg = b"The quick brown fox"; @@ -221,11 +220,7 @@ #[allow(unused_imports)] use bouncycastle_core::key_material::KeyMaterialTrait; #[allow(unused_imports)] -use bouncycastle_core::traits::Signature; - -// todo -- re-run mutants - -// todo -- crucible tests +use bouncycastle_core::traits::{SignatureVerifier, Signer}; mod aux_functions; pub mod hash_mldsa; @@ -263,5 +258,7 @@ pub use mldsa::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; pub use mldsa::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; pub use mldsa::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; +pub use mldsa::MU_BUILDER_SERIALIZED_STATE_LEN; + // re-export just so it's visible to unit tests pub use polynomial::Polynomial; diff --git a/crypto/mldsa_lowmemory/src/low_memory_helpers.rs b/crypto/mldsa-lowmemory/src/low_memory_helpers.rs similarity index 79% rename from crypto/mldsa_lowmemory/src/low_memory_helpers.rs rename to crypto/mldsa-lowmemory/src/low_memory_helpers.rs index d947bbe..d9e52f1 100644 --- a/crypto/mldsa_lowmemory/src/low_memory_helpers.rs +++ b/crypto/mldsa-lowmemory/src/low_memory_helpers.rs @@ -2,10 +2,12 @@ //! and other intermediate values by never holding the whole thing in memory at once, but re-constructing //! what it needs in pieces, which generally means handling the matrices and vectors row-wise or entry-wise. -use bouncycastle_core::errors::SignatureError; -use crate::mldsa::{d}; -use crate::aux_functions::{bit_unpack_eta, bitlen_eta, expand_mask_poly, rej_ntt_poly, unpack_z_row}; +use crate::aux_functions::{ + bit_unpack_eta, bitlen_eta, expand_mask_poly, rej_ntt_poly, unpack_z_row, +}; +use crate::mldsa::d; use crate::polynomial::Polynomial; +use bouncycastle_core::errors::SignatureError; #[inline(always)] pub(crate) fn expandA_elem(rho: &[u8; 32], i: usize, j: usize) -> Polynomial { @@ -41,17 +43,18 @@ pub(crate) fn compute_w_row( rho: &[u8; 32], sig: &[u8; SIG_LEN], t1: &Polynomial, c: &Polynomial, - idx: usize) -> Result { + idx: usize, +) -> Result { // Algorithm 8: line 9: 𝐰′_approx ← NTT−1(𝐀_hat ∘ NTT(𝐳) − NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑)) // broken out for clarity: // NTT−1( @@ -60,13 +63,18 @@ pub(crate) fn compute_wp_approx_row< // ) // ▷ 𝐰'_approx = 𝐀𝐳 − 𝑐𝐭1 ⋅ 2^𝑑 - let mut z_i = unpack_z_row::(0, sig)?; + let mut z_i = + unpack_z_row::( + 0, sig, + )?; z_i.ntt(); let mut Az_acc = rej_ntt_poly(rho, &[0u8, idx as u8]); Az_acc.multiply_ntt(&z_i); for col in 1..l { - z_i = unpack_z_row::(col, sig)?; + z_i = unpack_z_row::( + col, sig, + )?; z_i.ntt(); // [Optimization Note]: @@ -77,15 +85,15 @@ pub(crate) fn compute_wp_approx_row< Az_acc.add_ntt(&tmp); } - let ct1 = { - let mut t1_i = t1.clone(); + let ct1 = compute_ct1(t1.clone(), c.clone()); + fn compute_ct1(mut t1_i: Polynomial, mut c: Polynomial) -> Polynomial { t1_i.shift_left::(); t1_i.ntt(); - let mut c = c.clone(); c.ntt(); t1_i.multiply_ntt(&c); + t1_i - }; + } Az_acc.sub(&ct1); Az_acc.inv_ntt(); @@ -95,7 +103,9 @@ pub(crate) fn compute_wp_approx_row< } pub(crate) fn compute_z_component< - const GAMMA1: i32, const GAMMA1_MASK_LEN: usize, const BETA: i32, + const GAMMA1: i32, + const GAMMA1_MASK_LEN: usize, + const GAMMA1_MINUS_BETA: i32, >( s1: &Polynomial, rho_p_p: &[u8; 64], @@ -112,13 +122,10 @@ pub(crate) fn compute_z_component< let mut z = cs1; z.add_ntt(&y); - if z.check_norm(GAMMA1 - BETA) { Ok(None) } else { Ok(Some(z)) } + if z.check_norm::() { Ok(None) } else { Ok(Some(z)) } } -pub(crate) fn compute_w0cs2_component< - const GAMMA2: i32, - const BETA: i32, ->( +pub(crate) fn compute_w0cs2_component( s2: &Polynomial, w: &Polynomial, c_hat: &Polynomial, @@ -129,7 +136,6 @@ pub(crate) fn compute_w0cs2_component< let mut cs2 = s2_hat; // rename cs2.inv_ntt(); - // Note: this could be further optimized by using the optimization described in // https://pq-crystals.org/dilithium/data/dilithium-specification-round3-20210208.pdf section 5.1: // "instead of computing (r1, r0) = Decomposeq (w − cs2, α) @@ -139,12 +145,10 @@ pub(crate) fn compute_w0cs2_component< let mut w0cs2 = w.clone(); w0cs2.low_bits::(); w0cs2.sub(&cs2); - if w0cs2.check_norm(GAMMA2 - BETA) { None } else { Some(w0cs2) } + if w0cs2.check_norm::() { None } else { Some(w0cs2) } } -pub(crate) fn compute_ct0_component< - const GAMMA2: i32, ->( +pub(crate) fn compute_ct0_component( t0_row: &Polynomial, c_hat: &Polynomial, ) -> Option { @@ -154,7 +158,7 @@ pub(crate) fn compute_ct0_component< let mut ct0 = t0_hat; // rename ct0.inv_ntt(); - if ct0.check_norm(GAMMA2) { None } else { Some(ct0) } + if ct0.check_norm::() { None } else { Some(ct0) } } pub(crate) fn s_unpack(s_packed: &[u8], idx: usize) -> Polynomial { diff --git a/crypto/mldsa_lowmemory/src/mldsa.rs b/crypto/mldsa-lowmemory/src/mldsa.rs similarity index 71% rename from crypto/mldsa_lowmemory/src/mldsa.rs rename to crypto/mldsa-lowmemory/src/mldsa.rs index 28f4386..843a858 100644 --- a/crypto/mldsa_lowmemory/src/mldsa.rs +++ b/crypto/mldsa-lowmemory/src/mldsa.rs @@ -9,7 +9,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); @@ -51,7 +51,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); @@ -97,7 +97,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, _) = MLDSA65::keygen().unwrap(); @@ -116,7 +116,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, _) = MLDSA65::keygen().unwrap(); @@ -136,7 +136,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -181,7 +181,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait}; //! //! let msg = b"The quick brown fox"; @@ -220,7 +220,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa_lowmemory::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -265,7 +265,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait}; //! use bouncycastle_hex as hex; //! use bouncycastle_mldsa_lowmemory::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; @@ -308,27 +308,111 @@ //! private key encoding (which is often called the "semi-expanded format" since the in-memory representation //! is still larger). //! Contact us if you need such a thing implemented. +//! +//! # Suspending and resuming execution via SerializableState +//! +//! When signing or verifying a large message, it can be advantageous to be able to suspend the operation +//! to a cache and resume it later; for example if waiting for the message to stream over a slow network +//! connection. +//! +//! This can bo accomplished for both the ML-DSA signer and verifier through the [MuBuilder] object. +//! +//! Suspending an in-progress sign operation: +//! +//! ```rust +//! use bouncycastle_mldsa_lowmemory::{MLDSA65, MuBuilder, MLDSATrait, MLDSAPublicKeyTrait}; +//! use bouncycastle_core::traits::{Signer, Suspendable}; +//! +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let (pk, sk) = MLDSA65::keygen().unwrap(); +//! +//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); +//! mb.do_update(msg_part1); +//! +//! // here, we'll suspend while "waiting" for the second part of the message +//! let serialized_state = mb.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); +//! mb_resumed.do_update(msg_part2); +//! let mu: [u8; 64] = mb_resumed.do_final(); +//! +//! // Now we'll do the actual sign_mu operation +//! let sig = MLDSA65::sign_mu(&sk, &mu).unwrap(); +//! ``` +//! +//! Suspending an in-progress verify operation behaves exactly the same way: +//! +//! ```rust +//! use bouncycastle_mldsa_lowmemory::{MLDSA65, MuBuilder, MLDSATrait, MLDSAPublicKeyTrait}; +//! use bouncycastle_core::traits::{Signer, Suspendable}; +//! use bouncycastle_core::errors::SignatureError; +//! +//! let (pk, sk) = MLDSA65::keygen().unwrap(); +//! +//! // first, let's generate a signature to verify +//! let sig = MLDSA65::sign(&sk, b"The quick brown fox jumped over the lazy dog", None).unwrap(); +//! +//! // Now we'll verify it with a suspension in the middle +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); +//! mb.do_update(msg_part1); +//! +//! // here, we'll suspend while "waiting" for the second part of the message +//! let serialized_state = mb.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); +//! mb_resumed.do_update(msg_part2); +//! let mu: [u8; 64] = mb_resumed.do_final(); +//! +//! // Now we'll do the actual verify_mu operation +//! match MLDSA65::verify_mu(&pk, &mu, &sig) { +//! Ok(()) => println!("Signature is valid!"), +//! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), +//! Err(e) => panic!("Something else went wrong: {:?}", e), +//! } +//! ``` +use crate::aux_functions::{ + bitlen_eta, bitpack_gamma1, sample_in_ball, unpack_c_tilde, unpack_h_row, +}; +use crate::low_memory_helpers::{ + compute_ct0_component, compute_w_row, compute_w0cs2_component, compute_wp_approx_row, + compute_z_component, s_unpack, +}; +use crate::mldsa_keys::{MLDSAPrivateKeyInternalTrait, MLDSAPrivateKeyTrait}; +use crate::mldsa_keys::{MLDSAPublicKeyInternalTrait, MLDSAPublicKeyTrait}; +use crate::{ + MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, + MLDSA87PublicKey, +}; +use bouncycastle_core::errors::{RNGError, SignatureError, SuspendableError}; +use bouncycastle_core::key_material::KeyMaterial; +use bouncycastle_core::traits::{ + Algorithm, RNG, SecurityStrength, SignatureVerifier, Signer, Suspendable, XOF, +}; +use bouncycastle_rng::HashDRBG_SHA512; +use bouncycastle_sha3::{SHAKE128, SHAKE256, SUSPENDED_SHA3_STATE_LEN}; use core::marker::PhantomData; -use crate::aux_functions::{sample_in_ball, bitpack_gamma1, bitlen_eta, unpack_h_row, unpack_c_tilde}; -use crate::low_memory_helpers::{compute_ct0_component, compute_w0cs2_component, compute_w_row, compute_wp_approx_row, compute_z_component, s_unpack}; -use crate::mldsa_keys::{MLDSAPublicKeyTrait, MLDSAPublicKeyInternalTrait}; -use crate::mldsa_keys::{MLDSAPrivateKeyTrait, MLDSAPrivateKeyInternalTrait}; -use crate::{MLDSA44PublicKey, MLDSA44PrivateKey, MLDSA65PublicKey, MLDSA65PrivateKey, MLDSA87PublicKey, MLDSA87PrivateKey}; -use bouncycastle_core::errors::SignatureError; -use bouncycastle_core::key_material::{KeyMaterial}; -use bouncycastle_core::traits::{RNG, SecurityStrength, XOF, Signature, Algorithm}; -use bouncycastle_rng::{HashDRBG_SHA512}; -use bouncycastle_sha3::{SHAKE128, SHAKE256}; - // imports needed just for docs #[allow(unused_imports)] -use bouncycastle_core::traits::PHSignature; +use crate::hash_mldsa; #[allow(unused_imports)] use bouncycastle_core::key_material::KeyMaterial256; #[allow(unused_imports)] -use crate::hash_mldsa; +use bouncycastle_core::traits::{PHSignatureVerifier, PHSigner}; /*** Constants ***/ /// @@ -351,16 +435,17 @@ pub const MLDSA_RND_LEN: usize = 32; pub const MLDSA_TR_LEN: usize = 64; /// Length of the \[u8] holding a ML-DSA mu value. pub const MLDSA_MU_LEN: usize = 64; +/// Length of the \[u8] holding an private key seed. +pub const MLDSA_SEED_LEN: usize = 32; pub(crate) const POLY_T0PACKED_LEN: usize = 416; pub(crate) const POLY_T1PACKED_LEN: usize = 320; - /* ML-DSA-44 params */ /// Length of the \[u8] holding a ML-DSA-44 public key. pub const MLDSA44_PK_LEN: usize = 1312; /// Length of the \[u8] holding a ML-DSA-44 private key, which in this implementation is just a 32-byte seed. -pub const MLDSA44_SK_LEN: usize = 32; +pub const MLDSA44_SK_LEN: usize = MLDSA_SEED_LEN; /// The length of the FIPS representation of the private key, which can be produced by [MLDSAPrivateKeyTrait::encode_full_sk] pub const MLDSA44_FULL_SK_LEN: usize = 2560; /// Length of the \[u8] holding a ML-DSA-44 signature value. @@ -368,7 +453,7 @@ pub const MLDSA44_SIG_LEN: usize = 2420; pub(crate) const MLDSA44_TAU: i32 = 39; pub(crate) const MLDSA44_LAMBDA: i32 = 128; pub(crate) const MLDSA44_GAMMA1: i32 = 1 << 17; -pub(crate) const MLDSA44_GAMMA2: i32 = (q - 1) / 88; +pub(crate) const MLDSA44_GAMMA2: i32 = (q - 1) / 88; // mutants note: because of the bitshifting, the "- 1" ends up not mattering pub(crate) const MLDSA44_k: usize = 4; pub(crate) const MLDSA44_l: usize = 4; pub(crate) const MLDSA44_ETA: usize = 2; @@ -379,23 +464,23 @@ pub(crate) const MLDSA44_OMEGA: i32 = 80; pub(crate) const MLDSA44_C_TILDE: usize = 32; pub(crate) const MLDSA44_POLY_Z_PACKED_LEN: usize = 576; pub(crate) const MLDSA44_POLY_W1_PACKED_LEN: usize = 192; -pub(crate) const MLDSA44_S1_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA) * MLDSA44_l; // 384 bytes -pub(crate) const MLDSA44_S2_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA) * MLDSA44_k; // 384 bytes -pub(crate) const MLDSA44_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA44_k; // 768 bytes -pub(crate) const MLDSA44_POLY_ETA_PACKED_LEN: usize = 32*3; -pub(crate) const MLDSA44_LAMBDA_over_4: usize = 128/4; +pub(crate) const MLDSA44_S1_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA) * MLDSA44_l; // 384 bytes +pub(crate) const MLDSA44_S2_PACKED_LEN: usize = bitlen_eta(MLDSA44_ETA) * MLDSA44_k; // 384 bytes +pub(crate) const MLDSA44_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA44_k; // 768 bytes +pub(crate) const MLDSA44_LAMBDA_over_4: usize = 128 / 4; +pub(crate) const MLDSA44_GAMMA1_MINUS_BETA: i32 = MLDSA44_GAMMA1 - MLDSA44_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. +pub(crate) const MLDSA44_GAMMA2_MINUS_BETA: i32 = MLDSA44_GAMMA2 - MLDSA44_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. // Alg 32 // 1: 𝑐 ← 1 + bitlen (𝛾1 − 1) -pub(crate) const MLDSA44_GAMMA1_MASK_LEN: usize = 576; // 32*(1 + bitlen (𝛾1 − 1) ) - +pub(crate) const MLDSA44_GAMMA1_MASK_LEN: usize = 576; // 32*(1 + bitlen (𝛾1 − 1) ) /* ML-DSA-65 params */ /// Length of the \[u8] holding a ML-DSA-65 public key. pub const MLDSA65_PK_LEN: usize = 1952; /// Length of the \[u8] holding a ML-DSA-65 private key, which in this implementation is just a 32-byte seed. -pub const MLDSA65_SK_LEN: usize = 32; +pub const MLDSA65_SK_LEN: usize = MLDSA_SEED_LEN; /// The length of the FIPS representation of the private key, which can be produced by [MLDSAPrivateKeyTrait::encode_full_sk] pub const MLDSA65_FULL_SK_LEN: usize = 4032; /// Length of the \[u8] holding a ML-DSA-65 signature value. @@ -403,7 +488,7 @@ pub const MLDSA65_SIG_LEN: usize = 3309; pub(crate) const MLDSA65_TAU: i32 = 49; pub(crate) const MLDSA65_LAMBDA: i32 = 192; pub(crate) const MLDSA65_GAMMA1: i32 = 1 << 19; -pub(crate) const MLDSA65_GAMMA2: i32 = (q - 1) / 32; +pub(crate) const MLDSA65_GAMMA2: i32 = (q - 1) / 32; // mutants note: because of the bitshifting, the "- 1" ends up not mattering pub(crate) const MLDSA65_k: usize = 6; pub(crate) const MLDSA65_l: usize = 5; pub(crate) const MLDSA65_ETA: usize = 4; @@ -414,24 +499,23 @@ pub(crate) const MLDSA65_OMEGA: i32 = 55; pub(crate) const MLDSA65_C_TILDE: usize = 48; pub(crate) const MLDSA65_POLY_Z_PACKED_LEN: usize = 640; pub(crate) const MLDSA65_POLY_W1_PACKED_LEN: usize = 128; -pub(crate) const MLDSA65_S1_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA) * MLDSA65_l; // 640 bytes -pub(crate) const MLDSA65_S2_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA) * MLDSA65_k; // 768 bytes -pub(crate) const MLDSA65_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA65_k; // 1152 bytes -pub(crate) const MLDSA65_POLY_ETA_PACKED_LEN: usize = 32*4; -pub(crate) const MLDSA65_LAMBDA_over_4: usize = 192/4; +pub(crate) const MLDSA65_S1_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA) * MLDSA65_l; // 640 bytes +pub(crate) const MLDSA65_S2_PACKED_LEN: usize = bitlen_eta(MLDSA65_ETA) * MLDSA65_k; // 768 bytes +pub(crate) const MLDSA65_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA65_k; // 1152 bytes +pub(crate) const MLDSA65_LAMBDA_over_4: usize = 192 / 4; +pub(crate) const MLDSA65_GAMMA1_MINUS_BETA: i32 = MLDSA65_GAMMA1 - MLDSA65_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. +pub(crate) const MLDSA65_GAMMA2_MINUS_BETA: i32 = MLDSA65_GAMMA2 - MLDSA65_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. // Alg 32 // 1: 𝑐 ← 1 + bitlen (𝛾1 − 1) pub(crate) const MLDSA65_GAMMA1_MASK_LEN: usize = 640; - - /* ML-DSA-87 params */ /// Length of the \[u8] holding a ML-DSA-87 public key. pub const MLDSA87_PK_LEN: usize = 2592; /// Length of the \[u8] holding a ML-DSA-87 private key, which in this implementation is just a 32-byte seed. -pub const MLDSA87_SK_LEN: usize = 32; +pub const MLDSA87_SK_LEN: usize = MLDSA_SEED_LEN; /// The length of the FIPS representation of the private key, which can be produced by [MLDSAPrivateKeyTrait::encode_full_sk] pub const MLDSA87_FULL_SK_LEN: usize = 4896; /// Length of the \[u8] holding a ML-DSA-87 signature value. @@ -439,7 +523,7 @@ pub const MLDSA87_SIG_LEN: usize = 4627; pub(crate) const MLDSA87_TAU: i32 = 60; pub(crate) const MLDSA87_LAMBDA: i32 = 256; pub(crate) const MLDSA87_GAMMA1: i32 = 1 << 19; -pub(crate) const MLDSA87_GAMMA2: i32 = (q - 1) / 32; +pub(crate) const MLDSA87_GAMMA2: i32 = (q - 1) / 32; // mutants note: because of the bitshifting, the "- 1" ends up not mattering pub(crate) const MLDSA87_k: usize = 8; pub(crate) const MLDSA87_l: usize = 7; pub(crate) const MLDSA87_ETA: usize = 2; @@ -450,23 +534,21 @@ pub(crate) const MLDSA87_OMEGA: i32 = 75; pub(crate) const MLDSA87_C_TILDE: usize = 64; pub(crate) const MLDSA87_POLY_Z_PACKED_LEN: usize = 640; pub(crate) const MLDSA87_POLY_W1_PACKED_LEN: usize = 128; -pub(crate) const MLDSA87_S1_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA) * MLDSA87_l; // 672 bytes -pub(crate) const MLDSA87_S2_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA) * MLDSA87_k; // 768 bytes -pub(crate) const MLDSA87_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA87_k; // 1024 bytes -pub(crate) const MLDSA87_POLY_ETA_PACKED_LEN: usize = 32*3; -pub(crate) const MLDSA87_LAMBDA_over_4: usize = 256/4; +pub(crate) const MLDSA87_S1_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA) * MLDSA87_l; // 672 bytes +pub(crate) const MLDSA87_S2_PACKED_LEN: usize = bitlen_eta(MLDSA87_ETA) * MLDSA87_k; // 768 bytes +pub(crate) const MLDSA87_T1_PACKED_LEN: usize = POLY_T1PACKED_LEN * MLDSA87_k; // 1024 bytes +pub(crate) const MLDSA87_LAMBDA_over_4: usize = 256 / 4; +pub(crate) const MLDSA87_GAMMA1_MINUS_BETA: i32 = MLDSA87_GAMMA1 - MLDSA87_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. +pub(crate) const MLDSA87_GAMMA2_MINUS_BETA: i32 = MLDSA87_GAMMA2 - MLDSA87_BETA; // mutants note: there is a test vector for this in the regular implementation, but we don't know the sk seed for it, so can't test it here. // Alg 32 // 1: 𝑐 ← 1 + bitlen (𝛾1 − 1) pub(crate) const MLDSA87_GAMMA1_MASK_LEN: usize = 640; - - // Typedefs just to make the algorithms look more like the FIPS 204 sample code. pub(crate) type H = SHAKE256; pub(crate) type G = SHAKE128; - /*** Pub Types ***/ /// The ML-DSA-44 algorithm. @@ -492,8 +574,9 @@ pub type MLDSA44 = MLDSA< MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_T1_PACKED_LEN, - MLDSA44_POLY_ETA_PACKED_LEN, MLDSA44_LAMBDA_over_4, + MLDSA44_GAMMA1_MINUS_BETA, + MLDSA44_GAMMA2_MINUS_BETA, MLDSA44_GAMMA1_MASK_LEN, >; @@ -525,8 +608,9 @@ pub type MLDSA65 = MLDSA< MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_T1_PACKED_LEN, - MLDSA65_POLY_ETA_PACKED_LEN, MLDSA65_LAMBDA_over_4, + MLDSA65_GAMMA1_MINUS_BETA, + MLDSA65_GAMMA2_MINUS_BETA, MLDSA65_GAMMA1_MASK_LEN, >; @@ -558,8 +642,9 @@ pub type MLDSA87 = MLDSA< MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_T1_PACKED_LEN, - MLDSA87_POLY_ETA_PACKED_LEN, MLDSA87_LAMBDA_over_4, + MLDSA87_GAMMA1_MINUS_BETA, + MLDSA87_GAMMA2_MINUS_BETA, MLDSA87_GAMMA1_MASK_LEN, >; @@ -576,9 +661,28 @@ pub struct MLDSA< const SK_LEN: usize, const FULL_SK_LEN: usize, const SIG_LEN: usize, - PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, - SK: MLDSAPrivateKeyTrait - + MLDSAPrivateKeyInternalTrait, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, const TAU: i32, const LAMBDA: i32, const GAMMA1: i32, @@ -594,8 +698,9 @@ pub struct MLDSA< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, > { _phantom: PhantomData<(PK, SK)>, @@ -620,9 +725,28 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const SIG_LEN: usize, - PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, - SK: MLDSAPrivateKeyTrait - + MLDSAPrivateKeyInternalTrait, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, const TAU: i32, const LAMBDA: i32, const GAMMA1: i32, @@ -638,45 +762,39 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, -> MLDSA< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - S1_PACKED_LEN, - S2_PACKED_LEN, - T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, > + MLDSA< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > { - /// Should still be ok in FIPS mode - pub fn keygen_from_os_rng() -> Result< - (PK, SK), - SignatureError, - > { - let mut seed = KeyMaterial::<32>::new(); - HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?; - Self::keygen_internal(&seed) - } /// Performs the first step of key generation to transform the single provided seed into a set of internal intermediate seeds. /// /// Unlike other interfaces across the library that take an &impl KeyMaterial, this one @@ -684,12 +802,7 @@ impl< /// the appropriate [SecurityStrength] for the requested ML-DSA parameter set. /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using /// [KeyMaterial::from_key]. - pub(crate) fn keygen_internal( - seed: &KeyMaterial<32>, - ) -> Result< - (PK, SK), - SignatureError, - > { + pub(crate) fn keygen_internal(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> { let sk = SK::from_keymaterial(seed)?; let pk = sk.derive_pk(); let pk = PK::new(pk.rho, pk.t1_packed); // stupid conversion, but it gets around these overly-generified rust types @@ -702,9 +815,28 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const SIG_LEN: usize, - PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, - SK: MLDSAPrivateKeyTrait - + MLDSAPrivateKeyInternalTrait, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, const TAU: i32, const LAMBDA: i32, const GAMMA1: i32, @@ -720,35 +852,55 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, -> MLDSATrait for MLDSA< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - eta, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - S1_PACKED_LEN, - S2_PACKED_LEN, - T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, -> { +> + MLDSATrait< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + LAMBDA, + GAMMA2, + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + eta, + > + for MLDSA< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + eta, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ /*** Key Generation and PK / SK consistency checks ***/ /// Imports a secret key from a seed. @@ -764,10 +916,7 @@ impl< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<32>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - SignatureError, - > { + ) -> Result<(PK, SK), SignatureError> { let (pk, sk) = Self::keygen_internal(seed)?; let sk_from_bytes = SK::sk_decode(encoded_sk); @@ -787,10 +936,7 @@ impl< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [SignatureError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), SignatureError> { + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), SignatureError> { // This is maybe a computationally heavy way to compare them, but it works let derived_pk = sk.derive_pk(); if derived_pk.compute_tr() == pk.compute_tr() { @@ -858,10 +1004,7 @@ impl< /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both /// FIPS 204 itself, as well as subsequent FAQ documents. /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG. - fn sign_mu( - sk: &SK, - mu: &[u8; 64], - ) -> Result<[u8; SIG_LEN], SignatureError> { + fn sign_mu(sk: &SK, mu: &[u8; 64]) -> Result<[u8; SIG_LEN], SignatureError> { let mut out: [u8; SIG_LEN] = [0u8; SIG_LEN]; Self::sign_mu_out(sk, mu, &mut out)?; Ok(out) @@ -877,13 +1020,19 @@ impl< mu: &[u8; 64], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN]; HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; Self::sign_mu_deterministic_out(sk, mu, rnd, output) } - fn sign_mu_deterministic(sk: &SK, mu: &[u8; 64], rnd: [u8; 32]) -> Result<[u8; SIG_LEN], SignatureError> { + fn sign_mu_deterministic( + sk: &SK, + mu: &[u8; 64], + rnd: [u8; 32], + ) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; let bytes_written = Self::sign_mu_deterministic_out(sk, mu, rnd, &mut out)?; debug_assert_eq!(bytes_written, SIG_LEN); @@ -898,6 +1047,11 @@ impl< rnd: [u8; 32], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + + // This function is a mash-up of keyGen (Algorithm 6) and sign (Algorithm 7), + // with a special emphasis on deriving values only as we need them, which in particular + // means that we'll process matrices and vectors row or component-wise. // I have tried to keep this as clean as possible for correspondence with the FIPS, // but I have moved things around so that I can use unnamed scopes to limit how many @@ -929,6 +1083,7 @@ impl< h.absorb(mu); let mut rho_p_p = [0u8; 64]; h.squeeze_out(&mut rho_p_p); + rho_p_p }; @@ -942,6 +1097,7 @@ impl< loop { // FIPS 204 s. 6.2 allows: // "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value." + // mutants note: there is no test for this because we don't know of a KAT that will exceed this limit. if kappa > 1000 * k as u16 { return Err(SignatureError::GenericError( "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.", @@ -949,11 +1105,17 @@ impl< } // 11-15: derive c_tilde without materializing y_hat or w as full vectors. - let sig_val_c_tilde = { // scope for hash + let sig_val_c_tilde = { + // scope for hash let mut hash = H::new(); hash.absorb(mu); for row in 0..k { - let mut w = compute_w_row::(&sk.rho(), &rho_p_p, kappa, row); + let mut w = compute_w_row::( + &sk.rho(), + &rho_p_p, + kappa, + row, + ); w.high_bits::(); hash.absorb(&w.w1_encode::()); } @@ -978,14 +1140,17 @@ impl< // 18-23 (z path): compute and encode each z polynomial directly into the caller buffer. let mut rejected = false; for col in 0..l { - let z = match compute_z_component::( + let z = match compute_z_component::( // [Optimization Note]: // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form. // weirdly, in perf testing, this actually caused memory usage to go by a small amount; // maybe because re-computing the intermediates adds more to the widest point of the alg? // &sk.compute_s1_row(col), &s_unpack::(&s1_packed, col), - &rho_p_p, &c_hat, kappa, col, + &rho_p_p, + &c_hat, + kappa, + col, )? { Some(z) => z, None => { @@ -998,6 +1163,7 @@ impl< } if rejected { + // mutants note: we don't have a test vector that exercises this kappa += l as u16; continue; } @@ -1005,26 +1171,30 @@ impl< // 19-28 (hint path): recompute rows as needed and write the packed hint directly. let mut hint_count = 0usize; for row in 0..k { - let mut w = compute_w_row::(&sk.rho(), &rho_p_p, kappa, row); - let mut tmp = - match compute_w0cs2_component::( - // [Optimization Note]: - // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form. - // &sk.compute_s2_row(row), - &s_unpack::(&s2_packed, row), - &w, &c_hat) { - Some(tmp) => tmp, - None => { - rejected = true; - break; - } - }; + let mut w = + compute_w_row::(&sk.rho(), &rho_p_p, kappa, row); + let mut tmp = match compute_w0cs2_component::( + // [Optimization Note]: + // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form. + // &sk.compute_s2_row(row), + &s_unpack::(&s2_packed, row), + &w, + &c_hat, + ) { + Some(tmp) => tmp, + None => { + rejected = true; + break; + } + }; let ct0 = match compute_ct0_component::( // [Optimization Note]: // This is one of the places that a row of s1 can be re-computed instead of unpacked from the compressed form. // &sk.compute_t0_row(row), &c_hat) { - &sk.compute_t0_row(row, &s1_packed, &s2_packed), &c_hat) { + &sk.compute_t0_row(row, &s1_packed, &s2_packed), + &c_hat, + ) { Some(ct0) => ct0, None => { rejected = true; @@ -1038,6 +1208,9 @@ impl< w.high_bits::(); let (hint_row, weight) = tmp.make_hint_row::(&w); let next_hint_count = hint_count + weight as usize; + + // mutants note: don't have a test vector that exercises this condition, + // not even in bc-test-data if next_hint_count > OMEGA as usize { rejected = true; break; @@ -1064,19 +1237,29 @@ impl< Ok(SIG_LEN) } - fn sign_mu_deterministic_from_seed(seed: &KeyMaterial<32>, mu: &[u8; 64], rnd: [u8; 32]) -> Result<[u8; SIG_LEN], SignatureError> { + fn sign_mu_deterministic_from_seed( + seed: &KeyMaterial<32>, + mu: &[u8; 64], + rnd: [u8; 32], + ) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; SK::from_keymaterial(&seed)?; Self::sign_mu_deterministic_out(&SK::from_keymaterial(&seed)?, mu, rnd, &mut out)?; Ok(out) } - fn sign_mu_deterministic_from_seed_out(seed: &KeyMaterial<32>, mu: &[u8; 64], rnd: [u8; 32], output: &mut [u8; SIG_LEN]) -> Result { + fn sign_mu_deterministic_from_seed_out( + seed: &KeyMaterial<32>, + mu: &[u8; 64], + rnd: [u8; 32], + output: &mut [u8; SIG_LEN], + ) -> Result { + output.fill(0); + SK::from_keymaterial(&seed)?; Self::sign_mu_deterministic_out(&SK::from_keymaterial(&seed)?, mu, rnd, output) } - /// To be used for deterministic signing in conjunction with the [MLDSA44::sign_init], [MLDSA44::sign_update], and [MLDSA44::sign_final] flow. /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final] fn set_signer_rnd(&mut self, rnd: [u8; 32]) { @@ -1085,28 +1268,26 @@ impl< /// Alternative initialization of the streaming signer where you have your private key /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons. - fn sign_init_from_seed(seed: &KeyMaterial<32>, ctx: Option<&[u8]>) -> Result { + fn sign_init_from_seed( + seed: &KeyMaterial<32>, + ctx: Option<&[u8]>, + ) -> Result { let (_pk, sk) = Self::keygen_from_seed(seed)?; - Ok( - Self { - _phantom: PhantomData, - mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?, - signer_rnd: None, - sk: None, - seed: Some(seed.clone()), - pk: None } - ) + Ok(Self { + _phantom: PhantomData, + mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?, + signer_rnd: None, + sk: None, + seed: Some(seed.clone()), + pk: None, + }) } /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ . /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑) and message 𝑀′ ∈ {0, 1}∗ . /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. - fn verify_mu_internal( - pk: &PK, - mu: &[u8; 64], - sig: &[u8; SIG_LEN], - ) -> bool { + fn verify_mu(pk: &PK, mu: &[u8; 64], sig: &[u8; SIG_LEN]) -> Result<(), SignatureError> { // 1: (𝜌, 𝐭1) ← pkDecode(𝑝𝑘) // Already done -- the pk struct is already decoded @@ -1132,24 +1313,33 @@ impl< for row in 0..k { let mut wp_approx = match { // 9: 𝐰′_approx ← NTT−1(𝐀_hat ∘ NTT(𝐳) − NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑)) - compute_wp_approx_row::( - pk.rho(), - sig, - &pk.unpack_t1_row(row), - &c, - row) + compute_wp_approx_row::< + GAMMA1, + GAMMA1_MINUS_BETA, + l, + POLY_Z_PACKED_LEN, + LAMBDA_over_4, + SIG_LEN, + >(pk.rho(), sig, &pk.unpack_t1_row(row), &c, row) } { Ok(wp_approx) => wp_approx, // means the norm check on z failed - Err(_) => return false, + Err(_) => return Err(SignatureError::SignatureVerificationFailed), }; - let h_i - = match unpack_h_row:: - (row, &sig) { - Some(h_i) => h_i, - // means there were more than OMEGA bits set in the hint - None => return false, + let h_i = match unpack_h_row::< + GAMMA1, + k, + l, + OMEGA, + LAMBDA_over_4, + POLY_Z_PACKED_LEN, + SIG_LEN, + >(row, &sig) + { + Some(h_i) => h_i, + // means there were more than OMEGA bits set in the hint + None => return Err(SignatureError::SignatureVerificationFailed), }; // 10: 𝐰1′ ← UseHint(𝐡, 𝐰'_approx) @@ -1165,7 +1355,11 @@ impl< // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] and [[𝑐 ̃ = 𝑐′ ]] // note: the first half of this check (the norm check) is buried in unpack_z_row(), // which is called from compute_wp_approx_row() - bouncycastle_utils::ct::ct_eq_bytes(unpack_c_tilde::(sig), &c_tilde_p) + if bouncycastle_utils::ct::ct_eq_bytes(unpack_c_tilde::(sig), &c_tilde_p) { + Ok(()) + } else { + Err(SignatureError::SignatureVerificationFailed) + } } } @@ -1175,9 +1369,28 @@ pub trait MLDSATrait< const SK_LEN: usize, const FULL_SK_LEN: usize, const SIG_LEN: usize, - PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, - SK: MLDSAPrivateKeyTrait - + MLDSAPrivateKeyInternalTrait, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, const LAMBDA: i32, const GAMMA2: i32, const k: usize, @@ -1185,8 +1398,28 @@ pub trait MLDSATrait< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const ETA: usize -> : Sized { + const ETA: usize, +>: Sized +{ + /// Runs a key generation using the library's default RNG, seeded from the OS. + /// In environments where the default OS based RNG is not available, use instead [MLDSA::keygen_from_rng] + /// and explicitly provide a [RNG] implementation, or use [MLDSATrait::keygen_from_seed] and provide the + /// private key seed directly. + fn keygen() -> Result<(PK, SK), SignatureError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::keygen_from_rng(&mut os_rng) + } + /// Run a keygen using the provided RNG implementation. + // Should still be ok in FIPS mode, provided that you're using the FIPS-approved RNG. + fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), SignatureError> { + // Source the seed from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } + let mut seed = KeyMaterial::<32>::new(); + rng.fill_keymaterial_out(&mut seed)?; + Self::keygen_from_seed(&seed) + } /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError>; /// Imports a secret key from both a seed and an encoded_sk. @@ -1198,10 +1431,7 @@ pub trait MLDSATrait< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<32>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - SignatureError, - >; + ) -> Result<(PK, SK), SignatureError>; /// Given a public key and a secret key, check that the public key matches the secret key. /// This is a sanity check that the public key was generated correctly from the secret key. /// @@ -1210,10 +1440,7 @@ pub trait MLDSATrait< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [SignatureError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), SignatureError>; + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), SignatureError>; /// This provides the first half of the "External Mu" interface to ML-DSA which is described /// in, and allowed under, NIST's FAQ that accompanies FIPS 204. /// @@ -1267,10 +1494,7 @@ pub trait MLDSATrait< /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both /// FIPS 204 itself, as well as subsequent FAQ documents. /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG. - fn sign_mu( - sk: &SK, - mu: &[u8; 64], - ) -> Result<[u8; SIG_LEN], SignatureError>; + fn sign_mu(sk: &SK, mu: &[u8; 64]) -> Result<[u8; SIG_LEN], SignatureError>; /// Performs an ML-DSA signature using the provided external message representative `mu`. /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both /// FIPS 204 itself, as well as subsequent FAQ documents. @@ -1357,16 +1581,14 @@ pub trait MLDSATrait< /// Can be set anywhere after [MLDSA44::sign_init] and before [MLDSA44::sign_final] fn set_signer_rnd(&mut self, rnd: [u8; 32]); /// An alternate way to start the streaming signing mode by providing a private key seed instead of an expanded private key - fn sign_init_from_seed(seed: &KeyMaterial<32>, ctx: Option<&[u8]>) -> Result; - /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) - /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ . - /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑) and message 𝑀′ ∈ {0, 1}∗ . - /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. - fn verify_mu_internal( - pk: &PK, - mu: &[u8; 64], - sig: &[u8; SIG_LEN], - ) -> bool; + fn sign_init_from_seed( + seed: &KeyMaterial<32>, + ctx: Option<&[u8]>, + ) -> Result; + /// Performs an ML-DSA signature verification using the provided external message representative `mu`. + /// This implements FIPS 204 Algorithm 8 with line 7 removed; a modification that is allowed by both + /// FIPS 204 itself, as well as subsequent FAQ documents. + fn verify_mu(pk: &PK, mu: &[u8; 64], sig: &[u8; SIG_LEN]) -> Result<(), SignatureError>; } impl< @@ -1374,9 +1596,28 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const SIG_LEN: usize, - PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, - SK: MLDSAPrivateKeyTrait - + MLDSAPrivateKeyInternalTrait, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, const TAU: i32, const LAMBDA: i32, const GAMMA1: i32, @@ -1392,40 +1633,39 @@ impl< const S1_PACKED_LEN: usize, const S2_PACKED_LEN: usize, const T1_PACKED_LEN: usize, - const POLY_ETA_PACKED_LEN: usize, const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, const GAMMA1_MASK_LEN: usize, -> Signature for MLDSA< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - S1_PACKED_LEN, - S2_PACKED_LEN, - T1_PACKED_LEN, - POLY_ETA_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, -> { - - fn keygen() -> Result<(PK, SK), SignatureError> { - Self::keygen_from_os_rng() - } - +> Signer + for MLDSA< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; Self::sign_out(sk, msg, ctx, &mut out)?; @@ -1433,7 +1673,14 @@ impl< Ok(out) } - fn sign_out(sk: &SK, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN]) -> Result { + fn sign_out( + sk: &SK, + msg: &[u8], + ctx: Option<&[u8]>, + output: &mut [u8; SIG_LEN], + ) -> Result { + output.fill(0); + let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?; let bytes_written = Self::sign_mu_out(sk, &mu, output)?; @@ -1441,15 +1688,14 @@ impl< } fn sign_init(sk: &SK, ctx: Option<&[u8]>) -> Result { - Ok( - Self { - _phantom: PhantomData, - mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?, - signer_rnd: None, - sk: Some(sk.clone()), - seed: None, - pk: None } - ) + Ok(Self { + _phantom: PhantomData, + mu_builder: MuBuilder::do_init(&sk.tr(), ctx)?, + signer_rnd: None, + sk: Some(sk.clone()), + seed: None, + pk: None, + }) } fn sign_update(&mut self, msg_chunk: &[u8]) { @@ -1466,17 +1712,23 @@ impl< let mu = self.mu_builder.do_final(); if self.sk.is_none() && self.seed.is_none() { - return Err(SignatureError::GenericError("Somehow you managed to construct a streaming signer without a private key, impressive!")) + return Err(SignatureError::GenericError( + "Somehow you managed to construct a streaming signer without a private key, impressive!", + )); } - if output.len() < SIG_LEN { return Err(SignatureError::LengthError("Output buffer insufficient size to hold signature")) } - let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap(); + output.fill(0); if self.sk.is_some() { if self.signer_rnd.is_none() { - Self::sign_mu_out(&self.sk.unwrap(), &mu, output_sized) + Self::sign_mu_out(&self.sk.unwrap(), &mu, output) } else { - Self::sign_mu_deterministic_out(&self.sk.unwrap(), &mu, self.signer_rnd.unwrap(), output_sized) + Self::sign_mu_deterministic_out( + &self.sk.unwrap(), + &mu, + self.signer_rnd.unwrap(), + output, + ) } } else if self.seed.is_some() { let rnd = if self.signer_rnd.is_some() { @@ -1486,31 +1738,106 @@ impl< HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; rnd }; - Self::sign_mu_deterministic_from_seed_out(&self.seed.unwrap(), &mu, rnd, output_sized) - } else { unreachable!() } + Self::sign_mu_deterministic_from_seed_out(&self.seed.unwrap(), &mu, rnd, output) + } else { + unreachable!() + } } +} +impl< + const PK_LEN: usize, + const SK_LEN: usize, + const FULL_SK_LEN: usize, + const SIG_LEN: usize, + PK: MLDSAPublicKeyTrait + + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + ETA, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + >, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const S1_PACKED_LEN: usize, + const S2_PACKED_LEN: usize, + const T1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, +> SignatureVerifier + for MLDSA< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?; - if sig.len() < SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) } - if Self::verify_mu_internal(pk, &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) + if sig.len() != SIG_LEN { + return Err(SignatureError::LengthError("Signature value is not the correct length.")); } + Self::verify_mu(pk, &mu, &sig.try_into().unwrap()) } fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result { - Ok( - Self { - _phantom: Default::default(), - mu_builder: MuBuilder::do_init(&pk.compute_tr(), ctx)?, - signer_rnd: None, - sk: None, - seed: None, - pk: Some(pk.clone()) } - ) + Ok(Self { + _phantom: Default::default(), + mu_builder: MuBuilder::do_init(&pk.compute_tr(), ctx)?, + signer_rnd: None, + sk: None, + seed: None, + pk: Some(pk.clone()), + }) } fn verify_update(&mut self, msg_chunk: &[u8]) { @@ -1520,18 +1847,19 @@ impl< fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> { let mu = self.mu_builder.do_final(); - assert!(self.pk.is_some(), "Somehow you managed to construct a streaming verifier without a public key, impressive!"); + assert!( + self.pk.is_some(), + "Somehow you managed to construct a streaming verifier without a public key, impressive!" + ); - if sig.len() < SIG_LEN { return Err(SignatureError::LengthError("Signature value is not the correct length.")) } - if Self::verify_mu_internal(&self.pk.unwrap(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) + if sig.len() != SIG_LEN { + return Err(SignatureError::LengthError("Signature value is not the correct length.")); } + + Self::verify_mu(&self.pk.unwrap(), &mu, &sig.try_into().unwrap()) } } - /// Implements parts of Algorithm 2 and Line 6 of Algorithm 7 of FIPS 204. /// Provides a stateful version of [MLDSATrait::compute_mu_from_pk] and [MLDSATrait::compute_mu_from_tr] /// that supports streaming @@ -1540,7 +1868,8 @@ impl< /// Note: this struct is only exposed for "pure" ML-DSA and not for HashML-DSA because HashML-DSA /// does not benefit from allowing external construction of the message representative mu. /// You can get the same behaviour by computing the pre-hash `ph` with the appropriate hash function -/// and providing that to HashMLDSA via [PHSignature::sign_ph]. +/// and providing that to HashMLDSA via [PHSigner::sign_ph]. +#[derive(Clone)] pub struct MuBuilder { h: H, } @@ -1548,7 +1877,11 @@ pub struct MuBuilder { impl MuBuilder { /// Algorithm 7 /// 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀′, 64) - pub fn compute_mu(tr: &[u8; 64],msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; 64], SignatureError> { + pub fn compute_mu( + tr: &[u8; 64], + msg: &[u8], + ctx: Option<&[u8]>, + ) -> Result<[u8; 64], SignatureError> { let mut mu_builder = MuBuilder::do_init(&tr, ctx)?; mu_builder.do_update(msg); let mu = mu_builder.do_final(); @@ -1559,7 +1892,10 @@ impl MuBuilder { /// This function requires the public key hash `tr`, which can be computed from the public key /// using [MLDSAPublicKeyTrait::compute_tr]. pub fn do_init(tr: &[u8; 64], ctx: Option<&[u8]>) -> Result { - let ctx = match ctx { Some(ctx) => ctx, None => &[] }; + let ctx = match ctx { + Some(ctx) => ctx, + None => &[], + }; // Algorithm 2 // 1: if |𝑐𝑡𝑥| > 255 then @@ -1599,3 +1935,25 @@ impl MuBuilder { mu } } + +/// The length, in bytes, of a serialized state of a [MuBuilder] object. +pub const MU_BUILDER_SERIALIZED_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; + +/// If you are processing a large input message into ML-DSA and want to pause the operation +/// -- maybe while waiting for slow network IO), you'll need to use [Suspendable]. +/// Serialization of the state of an in-progress ML-DSA instance is really just serialization +/// of the construction of the message representative mu, since no other part of the ML-DSA algorithm +/// has a pausable state. +// A [MuBuilder]'s (and by virtue, an ML-DSA instance's) entire mutable state is its inner SHAKE256 sponge, +// so serialization delegates directly to [SHAKE256]'s [SerializableState] impl. +impl Suspendable for MuBuilder { + fn suspend(self) -> [u8; SUSPENDED_SHA3_STATE_LEN] { + self.h.suspend() + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA3_STATE_LEN], + ) -> Result { + Ok(MuBuilder { h: H::from_suspended(serialized_state)? }) + } +} diff --git a/crypto/mldsa_lowmemory/src/mldsa_keys.rs b/crypto/mldsa-lowmemory/src/mldsa_keys.rs similarity index 72% rename from crypto/mldsa_lowmemory/src/mldsa_keys.rs rename to crypto/mldsa-lowmemory/src/mldsa_keys.rs index 66b70aa..7a733fe 100644 --- a/crypto/mldsa_lowmemory/src/mldsa_keys.rs +++ b/crypto/mldsa-lowmemory/src/mldsa_keys.rs @@ -1,14 +1,31 @@ -use crate::aux_functions::{bit_pack_eta, bit_pack_t0, bitlen_eta, power_2_round, rej_bounded_poly, simple_bit_pack_t1, simple_bit_unpack_t1}; +use crate::aux_functions::{ + bit_pack_eta, bit_pack_t0, bitlen_eta, power_2_round, rej_bounded_poly, simple_bit_pack_t1, + simple_bit_unpack_t1, +}; +use crate::low_memory_helpers::{expandA_elem, s_unpack}; use crate::mldsa::{H, N, POLY_T0PACKED_LEN}; +use crate::mldsa::{ + MLDSA44_ETA, MLDSA44_FULL_SK_LEN, MLDSA44_GAMMA2, MLDSA44_LAMBDA, MLDSA44_PK_LEN, + MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN, MLDSA44_SK_LEN, MLDSA44_k, MLDSA44_l, +}; +use crate::mldsa::{ + MLDSA44_T1_PACKED_LEN, MLDSA65_T1_PACKED_LEN, MLDSA87_T1_PACKED_LEN, POLY_T1PACKED_LEN, +}; +use crate::mldsa::{ + MLDSA65_ETA, MLDSA65_FULL_SK_LEN, MLDSA65_GAMMA2, MLDSA65_LAMBDA, MLDSA65_PK_LEN, + MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN, MLDSA65_SK_LEN, MLDSA65_k, MLDSA65_l, +}; +use crate::mldsa::{ + MLDSA87_ETA, MLDSA87_FULL_SK_LEN, MLDSA87_GAMMA2, MLDSA87_LAMBDA, MLDSA87_PK_LEN, + MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN, MLDSA87_SK_LEN, MLDSA87_k, MLDSA87_l, +}; use crate::{ML_DSA_44_NAME, ML_DSA_65_NAME, ML_DSA_87_NAME}; -use crate::mldsa::{MLDSA44_LAMBDA, MLDSA44_GAMMA2, MLDSA44_ETA, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_FULL_SK_LEN, MLDSA44_k, MLDSA44_l, MLDSA44_S1_PACKED_LEN, MLDSA44_S2_PACKED_LEN}; -use crate::mldsa::{MLDSA65_LAMBDA, MLDSA65_GAMMA2, MLDSA65_ETA, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_FULL_SK_LEN, MLDSA65_k, MLDSA65_l, MLDSA65_S1_PACKED_LEN, MLDSA65_S2_PACKED_LEN}; -use crate::mldsa::{MLDSA87_LAMBDA, MLDSA87_GAMMA2, MLDSA87_ETA, MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_FULL_SK_LEN, MLDSA87_k, MLDSA87_l, MLDSA87_S1_PACKED_LEN, MLDSA87_S2_PACKED_LEN}; -use crate::mldsa::{POLY_T1PACKED_LEN, MLDSA44_T1_PACKED_LEN, MLDSA65_T1_PACKED_LEN, MLDSA87_T1_PACKED_LEN}; -use crate::low_memory_helpers::{expandA_elem, s_unpack}; use bouncycastle_core::errors::SignatureError; +use bouncycastle_core::key_material; use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Secret, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, XOF}; +use bouncycastle_core::traits::{ + Secret, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, XOF, +}; use core::fmt; use core::fmt::{Debug, Display, Formatter}; @@ -17,21 +34,56 @@ use core::fmt::{Debug, Display, Formatter}; use crate::mldsa::MLDSATrait; use crate::polynomial::Polynomial; - /* Pub Types */ /// ML-DSA-44 Public Key pub type MLDSA44PublicKey = MLDSAPublicKey; /// ML-DSA-44 Private Key -pub type MLDSA44PrivateKey = MLDSASeedPrivateKey; +pub type MLDSA44PrivateKey = MLDSASeedPrivateKey< + MLDSA44_LAMBDA, + MLDSA44_GAMMA2, + MLDSA44_k, + MLDSA44_l, + MLDSA44_ETA, + MLDSA44_S1_PACKED_LEN, + MLDSA44_S2_PACKED_LEN, + MLDSA44_T1_PACKED_LEN, + MLDSA44_PK_LEN, + MLDSA44_SK_LEN, + MLDSA44_FULL_SK_LEN, +>; /// ML-DSA-65 Public Key pub type MLDSA65PublicKey = MLDSAPublicKey; /// ML-DSA-65 Private Key -pub type MLDSA65PrivateKey = MLDSASeedPrivateKey; +pub type MLDSA65PrivateKey = MLDSASeedPrivateKey< + MLDSA65_LAMBDA, + MLDSA65_GAMMA2, + MLDSA65_k, + MLDSA65_l, + MLDSA65_ETA, + MLDSA65_S1_PACKED_LEN, + MLDSA65_S2_PACKED_LEN, + MLDSA65_T1_PACKED_LEN, + MLDSA65_PK_LEN, + MLDSA65_SK_LEN, + MLDSA65_FULL_SK_LEN, +>; /// ML-DSA-87 Public Key pub type MLDSA87PublicKey = MLDSAPublicKey; /// ML-DSA-87 Private Key -pub type MLDSA87PrivateKey = MLDSASeedPrivateKey; +pub type MLDSA87PrivateKey = MLDSASeedPrivateKey< + MLDSA87_LAMBDA, + MLDSA87_GAMMA2, + MLDSA87_k, + MLDSA87_l, + MLDSA87_ETA, + MLDSA87_S1_PACKED_LEN, + MLDSA87_S2_PACKED_LEN, + MLDSA87_T1_PACKED_LEN, + MLDSA87_PK_LEN, + MLDSA87_SK_LEN, + MLDSA87_FULL_SK_LEN, +>; /// An ML-DSA public key. #[derive(Clone)] @@ -41,7 +93,9 @@ pub struct MLDSAPublicKey : SignaturePublicKey { +pub trait MLDSAPublicKeyTrait: + SignaturePublicKey +{ /// Algorithm 23 pkDecode(𝑝𝑘) /// Reverses the procedure pkEncode. /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑). @@ -57,7 +111,12 @@ pub trait MLDSAPublicKeyTrait [u8; 64]; } -pub(crate) trait MLDSAPublicKeyInternalTrait { +pub(crate) trait MLDSAPublicKeyInternalTrait< + const k: usize, + const T1_PACKED_LEN: usize, + const PK_LEN: usize, +> +{ /// Not exposing a constructor publicly because you should have to get an instance either by /// running a keygen, or by decoding an existing key. fn new(rho: [u8; 32], t1_packed: [u8; T1_PACKED_LEN]) -> Self; @@ -69,12 +128,11 @@ pub(crate) trait MLDSAPublicKeyInternalTrait Polynomial; } -impl MLDSAPublicKeyTrait for MLDSAPublicKey { +impl + MLDSAPublicKeyTrait for MLDSAPublicKey +{ fn pk_decode(pk: &[u8; PK_LEN]) -> Self { - Self { - rho: pk[..32].try_into().unwrap(), - t1_packed: pk[32..].try_into().unwrap() - } + Self { rho: pk[..32].try_into().unwrap(), t1_packed: pk[32..].try_into().unwrap() } } fn compute_tr(&self) -> [u8; 64] { @@ -85,19 +143,30 @@ impl MLDSAPubli } } -impl MLDSAPublicKeyInternalTrait for MLDSAPublicKey { +impl + MLDSAPublicKeyInternalTrait + for MLDSAPublicKey +{ fn new(rho: [u8; 32], t1_packed: [u8; T1_PACKED_LEN]) -> Self { Self { rho, t1_packed } } - fn rho(&self) -> &[u8; 32] { &self.rho } + fn rho(&self) -> &[u8; 32] { + &self.rho + } fn unpack_t1_row(&self, row: usize) -> Polynomial { - simple_bit_unpack_t1(&self.t1_packed[row * POLY_T1PACKED_LEN .. (row + 1) * POLY_T1PACKED_LEN].try_into().unwrap()) + simple_bit_unpack_t1( + &self.t1_packed[row * POLY_T1PACKED_LEN..(row + 1) * POLY_T1PACKED_LEN] + .try_into() + .unwrap(), + ) } } -impl SignaturePublicKey for MLDSAPublicKey { +impl SignaturePublicKey + for MLDSAPublicKey +{ /// Algorithm 22 pkEncode(𝜌, 𝐭1) /// Encodes a public key for ML-DSA into a byte string. /// Input:𝜌 ∈ 𝔹32, 𝐭1 ∈ 𝑅𝑘 with coefficients in [0, 2bitlen (𝑞−1)−𝑑 − 1]. @@ -115,6 +184,8 @@ impl Signature fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize { debug_assert_eq!(out.len(), PK_LEN); + out.fill(0); + out[..32].copy_from_slice(&self.rho); out[32..].copy_from_slice(&self.t1_packed); @@ -122,15 +193,24 @@ impl Signature } fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PK_LEN { return Err(SignatureError::DecodingError("Provided key bytes are the incorrect length")) } + if bytes.len() != PK_LEN { + return Err(SignatureError::DecodingError( + "Provided key bytes are the incorrect length", + )); + } let sized_bytes: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); Ok(Self::pk_decode(&sized_bytes)) } } -impl Eq for MLDSAPublicKey { } +impl Eq + for MLDSAPublicKey +{ +} -impl PartialEq for MLDSAPublicKey { +impl PartialEq + for MLDSAPublicKey +{ fn eq(&self, other: &Self) -> bool { let self_encoded = self.encode(); let other_encoded = other.encode(); @@ -138,7 +218,9 @@ impl PartialEq } } -impl fmt::Debug for MLDSAPublicKey { +impl fmt::Debug + for MLDSAPublicKey +{ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let alg = match k { 4 => ML_DSA_44_NAME, @@ -150,7 +232,9 @@ impl fmt::Debug } } -impl Display for MLDSAPublicKey { +impl Display + for MLDSAPublicKey +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 4 => ML_DSA_44_NAME, @@ -162,8 +246,6 @@ impl Display fo } } - - /// General trait for all ML-DSA private keys types. pub trait MLDSAPrivateKeyTrait< const k: usize, @@ -174,44 +256,33 @@ pub trait MLDSAPrivateKeyTrait< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> : SignaturePrivateKey { +>: SignaturePrivateKey +{ /// New from KeyMaterial. Can throw a SignatureError if the KeyMaterial does not contain sufficient entropy. fn from_keymaterial(seed: &KeyMaterial<32>) -> Result; /// Get a ref to the seed, if there is one stored with this private key - fn seed(&self) -> &KeyMaterial<32>; + fn seed(&self) -> Option<&KeyMaterial<32>>; /// Get a copy of the key hash `tr`. /// This is computationally intensive as it requires fully re-computing the public key (and then discarding it). /// It is highly recommended that if you already have a copy of the public key, get `tr` from that, /// or else compute tr once and store it. fn tr(&self) -> [u8; 64]; - /// Returns the full public key, and has the side-effect of setting the public key hash tr in this MLDSASeedSK object. fn derive_pk(&self) -> MLDSAPublicKey; - /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) - /// Encodes a secret key for ML-DSA into a byte string. - /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with - /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. - /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). - fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize; /// This produces the full private key in the encoding specified in FIPS 204 Algorithm 24 skEncode() /// so that it is compatible with other implementations. /// /// Note that since this encoding does not include the seed, this is a one-way operation; /// after exporting in this encoding, it will be impossible to re-import it into a [MLDSASeedPrivateKey]. - fn encode_full_sk(&self) -> [u8; FULL_SK_LEN] { - let mut out = [0; FULL_SK_LEN]; - self.encode_full_sk_out(&mut out); - - out - } + fn encode_full_sk(&self) -> [u8; FULL_SK_LEN]; /// This produces the full private key in the encoding specified in FIPS 204 Algorithm 24 skEncode() /// so that it is compatible with other implementations. /// /// Note that since this encoding does not include the seed, this is a one-way operation; /// after exporting in this encoding, it will be impossible to re-import it into a [MLDSASeedPrivateKey]. - fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]); + fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize; /// Algorithm 25 skDecode(𝑠𝑘) /// Reverses the procedure skEncode. /// Input: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((ℓ+𝑘)⋅bitlen (2𝜂)+𝑑𝑘). @@ -244,7 +315,6 @@ pub struct MLDSASeedPrivateKey< K: [u8; 32], } - impl< const LAMBDA: i32, const GAMMA2: i32, @@ -257,7 +327,21 @@ impl< const SK_LEN: usize, const PK_LEN: usize, const FULL_SK_LEN: usize, -> Drop for MLDSASeedPrivateKey { +> Drop + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ fn drop(&mut self) { // seed is a KeyMaterialSized which will zeroize itself self.rho.fill(0u8); @@ -278,7 +362,22 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> Secret for MLDSASeedPrivateKey {} +> Secret + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ +} impl< const LAMBDA: i32, @@ -292,7 +391,21 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> Debug for MLDSASeedPrivateKey { +> Debug + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 4 => ML_DSA_44_NAME, @@ -300,12 +413,7 @@ impl< 8 => ML_DSA_87_NAME, _ => panic!("Unsupported key length"), }; - write!( - f, - "MLDSASeedPrivateKey {{ alg: {}, pub_key_hash (tr): {:x?} }}", - alg, - self.tr(), - ) + write!(f, "MLDSASeedPrivateKey {{ alg: {}, pub_key_hash (tr): {:x?} }}", alg, self.tr(),) } } @@ -321,7 +429,21 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> Display for MLDSASeedPrivateKey { +> Display + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 4 => ML_DSA_44_NAME, @@ -329,12 +451,7 @@ impl< 8 => ML_DSA_87_NAME, _ => panic!("Unsupported key length"), }; - write!( - f, - "MLDSASeedPrivateKey {{ alg: {}, pub_key_hash (tr): {:x?} }}", - alg, - self.tr(), - ) + write!(f, "MLDSASeedPrivateKey {{ alg: {}, pub_key_hash (tr): {:x?} }}", alg, self.tr(),) } } @@ -350,13 +467,27 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> MLDSASeedPrivateKey { +> + MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ /// Create a new MLDSASeedPrivateKey from a 32-byte KeyMaterial. /// Seed SecurityStrength must match algorithm security strength: 128-bit (ML-DSA-44), 192-bit (ML-DSA-65), or 256-bit (ML-DSA-87), /// otherwise it throws a SignatureError::KeyGenError("SecurityStrength". pub fn new(seed: &KeyMaterial<32>) -> Result { - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) - || seed.key_len() != 32 + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::CryptographicRandom) + || seed.key_len() != 32 { return Err(SignatureError::KeyGenError( "Seed must be 32 bytes and KeyType::Seed or KeyType::BytesFullEntropy.", @@ -368,7 +499,7 @@ impl< } let (rho, rho_prime, K) = Self::compute_rhos_and_K(&seed); - Ok(Self { seed: seed.clone(), rho, rho_prime, K}) + Ok(Self { seed: seed.clone(), rho, rho_prime, K }) } fn compute_rhos_and_K(seed: &KeyMaterial<32>) -> ([u8; 32], [u8; 64], [u8; 32]) { @@ -393,12 +524,7 @@ impl< (rho, rho_prime, K) } - fn compute_t_row( - &self, - idx: usize, - s1_packed: &[u8], - s2_packed: &[u8], - ) -> Polynomial { + fn compute_t_row(&self, idx: usize, s1_packed: &[u8], s2_packed: &[u8]) -> Polynomial { debug_assert!(idx < k); // [Optimization Note]: @@ -417,13 +543,6 @@ impl< // s1 = self.compute_s1_row(col); let mut s1_hat = s_unpack::(s1_packed, col); s1_hat.ntt(); - // let tmp = polynomial::multiply_ntt( - // // [Optimization Note]: - // // this is reconstructing a row of the public matrix A_hat, - // // which nobody is proposing to keep in memory. - // &rej_ntt_poly(&self.rho, &[col as u8, idx as u8]), - // &s1_hat, - // ); let mut A_elem = expandA_elem(&self.rho, idx, col); A_elem.multiply_ntt(&s1_hat); t_hat_i.add_ntt(&A_elem); @@ -456,7 +575,21 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> SignaturePrivateKey for MLDSASeedPrivateKey { +> SignaturePrivateKey + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ /// Encodes the private key seed. fn encode(&self) -> [u8; SK_LEN] { debug_assert_eq!(SK_LEN, /* seed */ 32); @@ -465,6 +598,8 @@ impl< } fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + out.copy_from_slice(self.seed.ref_to_bytes()); debug_assert_eq!(self.seed.ref_to_bytes().len(), SK_LEN); @@ -476,10 +611,10 @@ impl< return Err(SignatureError::DecodingError("Invalid seed length")); } let mut keymat = KeyMaterial::<32>::from_bytes(bytes)?; - keymat.allow_hazardous_operations(); - keymat.set_key_type(KeyType::Seed)?; - keymat.set_security_strength(SecurityStrength::_256bit)?; - keymat.drop_hazardous_operations(); + key_material::do_hazardous_operations(&mut keymat, |keymat| { + keymat.set_key_type(KeyType::Seed)?; + keymat.set_security_strength(SecurityStrength::_256bit) + })?; Self::new(&keymat) } @@ -497,13 +632,38 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> MLDSAPrivateKeyTrait -for MLDSASeedPrivateKey { +> + MLDSAPrivateKeyTrait< + k, + l, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ fn from_keymaterial(seed: &KeyMaterial<32>) -> Result { Self::new(seed) } - fn seed(&self) -> &KeyMaterial<32> { &self.seed } + fn seed(&self) -> Option<&KeyMaterial<32>> { + Some(&self.seed) + } fn tr(&self) -> [u8; 64] { let pk: MLDSAPublicKey = self.derive_pk(); @@ -520,26 +680,20 @@ for MLDSASeedPrivateKey::new(self.rho.clone(), t1_packed) } - fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { - out.copy_from_slice(self.seed.ref_to_bytes()); - - SK_LEN - } fn encode_full_sk(&self) -> [u8; FULL_SK_LEN] { let mut out = [0; FULL_SK_LEN]; - self.encode_full_sk_out(&mut out); + _ = self.encode_full_sk_out(&mut out); out } - fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]) { + fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize { out.fill(0); // Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) @@ -556,29 +710,30 @@ for MLDSASeedPrivateKey Self { Self::from_bytes(sk).unwrap() } @@ -594,7 +749,7 @@ pub(crate) trait MLDSAPrivateKeyInternalTrait< const S2_PACKED_LEN: usize, const PK_LEN: usize, const SK_LEN: usize, -> : Sized +>: Sized { fn rho(&self) -> &[u8; 32]; fn K(&self) -> &[u8; 32]; @@ -624,8 +779,32 @@ impl< const PK_LEN: usize, const SK_LEN: usize, const FULL_SK_LEN: usize, -> MLDSAPrivateKeyInternalTrait -for MLDSASeedPrivateKey { +> + MLDSAPrivateKeyInternalTrait< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + PK_LEN, + SK_LEN, + > + for MLDSASeedPrivateKey< + LAMBDA, + GAMMA2, + k, + l, + eta, + S1_PACKED_LEN, + S2_PACKED_LEN, + T1_PACKED_LEN, + PK_LEN, + SK_LEN, + FULL_SK_LEN, + > +{ fn rho(&self) -> &[u8; 32] { &self.rho } @@ -634,10 +813,7 @@ for MLDSASeedPrivateKey Polynomial { + fn compute_s1_row(&self, idx: usize) -> Polynomial { debug_assert!(idx < l); rej_bounded_poly::(&self.rho_prime, &(idx as u16).to_le_bytes()) } @@ -646,15 +822,15 @@ for MLDSASeedPrivateKey(&s1_i, &mut s1_packed[idx * bitlen_eta(eta)..(idx + 1) * bitlen_eta(eta)]); + bit_pack_eta::( + &s1_i, + &mut s1_packed[idx * bitlen_eta(eta)..(idx + 1) * bitlen_eta(eta)], + ); } s1_packed } - fn compute_s2_row( - &self, - idx: usize, - ) -> Polynomial { + fn compute_s2_row(&self, idx: usize) -> Polynomial { debug_assert!(idx < k); rej_bounded_poly::(&self.rho_prime, &((idx + l) as u16).to_le_bytes()) } @@ -663,17 +839,15 @@ for MLDSASeedPrivateKey(&s2_i, &mut s2_packed[idx * bitlen_eta(eta)..(idx + 1) * bitlen_eta(eta)]); + bit_pack_eta::( + &s2_i, + &mut s2_packed[idx * bitlen_eta(eta)..(idx + 1) * bitlen_eta(eta)], + ); } s2_packed } - fn compute_t0_row( - &self, - idx: usize, - s1_packed: &[u8], - s2_packed: &[u8], - ) -> Polynomial { + fn compute_t0_row(&self, idx: usize, s1_packed: &[u8], s2_packed: &[u8]) -> Polynomial { let mut t0 = self.compute_t_row(idx, s1_packed, s2_packed); for j in 0..N { (_, t0[j]) = power_2_round(t0[j]); @@ -682,12 +856,7 @@ for MLDSASeedPrivateKey Polynomial { + fn compute_t1_row(&self, idx: usize, s1_packed: &[u8], s2_packed: &[u8]) -> Polynomial { let mut t1 = self.compute_t_row(idx, s1_packed, s2_packed); for j in 0..N { (t1[j], _) = power_2_round(t1[j]); @@ -696,4 +865,3 @@ for MLDSASeedPrivateKey for Polynomial { @@ -32,7 +34,7 @@ impl IndexMut for Polynomial { impl Polynomial { /// Create a new polynomial with all coefficients set to zero. pub const fn new() -> Self { - Self{ coeffs: [0i32; N] } + Self { coeffs: [0i32; N] } } pub(crate) fn conditional_add_q(&mut self) { @@ -80,20 +82,24 @@ impl Polynomial { } } - pub(crate) fn check_norm(&self, bound: i32) -> bool { + pub(crate) fn check_norm(&self) -> bool { // Fine that this is not constant-time (returns true early) because it is used in a rejection loop. // IE the early quit here leads to rejection and continuing to the top of the rejection loop, or failing the signature validation. // So the i32 that we just checked in a non-constant-time manner is about to get thrown away. - if bound > (q - 1) / 8 { - return true; - } + + // Note: this formulation of the check_norm function usually requires this bounds check + // if bound > (q - 1) / 8 { + // return true; + // } + // but since BOUND is a constant here, we'll just do a debug_assert to make sure the value is what we expect. + debug_assert!(BOUND <= (q - 1) / 8); let mut t: i32; for x in self.coeffs.iter() { t = *x >> 31; t = *x - (t & (2 * *x)); - if t >= bound { + if t >= BOUND { return true; } } @@ -128,29 +134,28 @@ impl Polynomial { // then it's free to optimize all of the computation into CPU registers and skip, in this case, // several hundred physical memory writes. // So while it looks odd to use a scope variable in a low-memory implementation, it's way faster - // and I'm not convinced that it uses any more physical memory. + // and I'm not convinced that it uses any more physical memory. let mut r = [0u8; POLY_W1_PACKED_LEN]; match POLY_W1_PACKED_LEN { MLDSA44_POLY_W1_PACKED_LEN => { - for i in 0..N/4 { - r[3 * i] = - ((self[4 * i]) as u8) | ((self[4 * i + 1] << 6) as u8); - r[3 * i + 1] = - ((self[4 * i + 1] >> 2) as u8) | ((self[4 * i + 2] << 4) as u8); - r[3 * i + 2] = - ((self[4 * i + 2] >> 4) as u8) | ((self[4 * i + 3] << 2) as u8); + for i in 0..N / 4 { + r[3 * i] = ((self[4 * i]) as u8) | ((self[4 * i + 1] << 6) as u8); + r[3 * i + 1] = ((self[4 * i + 1] >> 2) as u8) | ((self[4 * i + 2] << 4) as u8); + r[3 * i + 2] = ((self[4 * i + 2] >> 4) as u8) | ((self[4 * i + 3] << 2) as u8); } - }, + } // ML-DSA65 and 87 share a POLY_W1_PACKED_LEN value MLDSA65_POLY_W1_PACKED_LEN => { - for i in 0..N/2 { + for i in 0..N / 2 { r[i] = ((self[2 * i]) | (self[2 * i + 1] << 4)) as u8; } - }, - _ => { unreachable!() } + } + _ => { + unreachable!() + } } - + r } @@ -234,11 +239,7 @@ impl Polynomial { } } - - pub(crate) fn use_hint( - &mut self, - h: &Polynomial, - ) { + pub(crate) fn use_hint(&mut self, h: &Polynomial) { for i in 0..N { self[i] = use_hint::(self[i], h[i]); } @@ -270,7 +271,7 @@ impl Display for Polynomial { /// of expressions of the form c = a * b (mod q). /// The output is not necessarily less than q in absolute value, but it is less than 2q in absolute value pub(crate) fn montgomery_reduce(a: i64) -> i32 { - debug_assert!(a > - ((q as i64) <<31) && a < ((q as i64) <<31)); + debug_assert!(a > -((q as i64) << 31) && a < ((q as i64) << 31)); // 2: 𝑡 ← ((𝑎 mod 2^32) ⋅ QINV) mod 2^32 let t: i32 = (a as i32).wrapping_mul(q_inv); @@ -279,7 +280,6 @@ pub(crate) fn montgomery_reduce(a: i64) -> i32 { ((a - ((t as i64) * (q as i64))) >> 32) as i32 } - pub(crate) fn conditional_add_q(a: i32) -> i32 { a + ((a >> 31) & q) } @@ -287,16 +287,16 @@ pub(crate) fn conditional_add_q(a: i32) -> i32 { #[test] /// These are the results it's giving; I'm not sure if these are "correct" or not. fn test_conditional_add_q() { - assert_eq!(conditional_add_q(-q -1), -1); + assert_eq!(conditional_add_q(-q - 1), -1); assert_eq!(conditional_add_q(-q), 0); - assert_eq!(conditional_add_q(-q -2), -2); - assert_eq!(conditional_add_q(-q +1), 1); - assert_eq!(conditional_add_q(-1), q-1); + assert_eq!(conditional_add_q(-q - 2), -2); + assert_eq!(conditional_add_q(-q + 1), 1); + assert_eq!(conditional_add_q(-1), q - 1); assert_eq!(conditional_add_q(0), 0); assert_eq!(conditional_add_q(1), 1); - assert_eq!(conditional_add_q(q -1), q-1); + assert_eq!(conditional_add_q(q - 1), q - 1); assert_eq!(conditional_add_q(q), q); - assert_eq!(conditional_add_q(q +1), q+1); + assert_eq!(conditional_add_q(q + 1), q + 1); } /// Constants for NTT @@ -327,4 +327,4 @@ const ZETAS: [i32; 256] = [ 472078, -426683, 1723600, -1803090, 1910376, -1667432, -1104333, -260646, -3833893, -2939036, -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687, -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782, -]; \ No newline at end of file +]; diff --git a/crypto/mldsa-lowmemory/tests/bc_test_data.rs b/crypto/mldsa-lowmemory/tests/bc_test_data.rs new file mode 100644 index 0000000..94c4b67 --- /dev/null +++ b/crypto/mldsa-lowmemory/tests/bc_test_data.rs @@ -0,0 +1,977 @@ +// Test against the bc-test-data repo +// Requires that the bc-test-data repository is cloned and available for testing at "../bc-test-data" +// relative to the root of this git project. + +use bouncycastle_core::errors::SignatureError; +use bouncycastle_core::traits::XOF; +use bouncycastle_sha3::SHAKE256; + +#[allow(unused_imports)] +#[allow(dead_code)] +#[cfg(test)] +mod bc_test_data { + #![allow(unused)] + + #[allow(unused)] + #[allow(dead_code)] + use crate::BustedMuBuilder; + use bouncycastle_core::errors::SignatureError; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{ + Hash, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, + }; + use bouncycastle_hex as hex; + use bouncycastle_mldsa_lowmemory::{ + HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA44, + MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, + MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87, + MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, + MLDSATrait, + }; + use bouncycastle_sha2::SHA512; + use std::fs; + use std::path::Path; + use std::process::exit; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../bc-test-data/pqc/crypto/mldsa"; + const TEST_DATA_PATH: &str = "../../../bc-test-data/pqc/crypto/mldsa"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + #[allow(non_snake_case)] + fn ML_DSA_keyGen() { + let contents = match get_test_data("ML-DSA-keyGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = KeyGenTestCase::parse(contents); + + for test_case in test_cases { + test_case.run(); + } + } + + #[derive(Clone)] + struct KeyGenTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + tc_id: u32, + seed: String, + pk: String, + sk: String, + } + + impl KeyGenTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + seed: String::new(), + pk: String::new(), + sk: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = KeyGenTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "seed" => test_case.seed = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "sk" => test_case.sk = value.to_string(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "keyGen"); + + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + seed.set_security_strength(SecurityStrength::_256bit) + }); + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA44_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA44_SK_LEN] = + hex::decode(&self.seed).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + "ML-DSA-65" => { + let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA65_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA65_SK_LEN] = + hex::decode(&self.seed).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + "ML-DSA-87" => { + let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA87_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA87_SK_LEN] = + hex::decode(&self.seed).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // this seems buggy and I'm not sure why. Possibly because the bc-test-data was written against Round 3 Dilithium and not ML-DSA. + // todo -- debug + // #[test] + #[allow(non_snake_case)] + fn ML_DSA_sigGen() { + let contents = match get_test_data("ML-DSA-sigGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = SigGenTestCase::parse(contents); + + let num_tests = test_cases.len(); + for test_case in test_cases { + test_case.run(); + } + + println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); + } + + #[derive(Clone)] + struct SigGenTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + deterministic: bool, + tc_id: u32, + sk: String, + message: String, + rnd: String, + signature: String, + } + + impl SigGenTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + deterministic: false, + tc_id: 0, + sk: String::new(), + message: String::new(), + rnd: String::new(), + signature: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = SigGenTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "deterministic" => test_case.deterministic = value.parse().unwrap(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "sk" => test_case.sk = value.to_string(), + "message" => test_case.message = value.to_string(), + "rnd" => test_case.rnd = value.to_string(), + "signature" => test_case.signature = value.to_string(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "sigGen"); + + let rnd = if self.deterministic { + [0u8; 32] + } else { + hex::decode(&self.rnd).unwrap().as_slice().try_into().unwrap() + }; + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let sk = + MLDSA44PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA44::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); + assert_eq!( + &sig, + &*hex::decode(&self.signature).unwrap(), + "ML-DSA-sigGen params: {}, vsId: {}, tgId: {}, tcId: {}", + self.parameter_set, + self.vs_id, + self.tg_id, + self.tc_id + ); + } + "ML-DSA-65" => { + let sk = + MLDSA65PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA65::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); + assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); + } + "ML-DSA-87" => { + let sk = + MLDSA87PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA87::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); + assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // this seems buggy and I'm not sure why. Possibly because the bc-test-data was written against Round 3 Dilithium and not ML-DSA. + // todo -- debug + // #[test] + #[allow(non_snake_case)] + fn ML_DSA_sigVer() { + let contents = match get_test_data("ML-DSA-sigVer.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = SigVerTestCase::parse(contents); + + for test_case in test_cases { + test_case.run(); + } + } + + #[derive(Clone)] + struct SigVerTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + pk: String, + tc_id: u32, + message: String, + signature: String, + test_passed: bool, + } + + impl SigVerTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + pk: String::new(), + message: String::new(), + signature: String::new(), + test_passed: false, + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = SigVerTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "message" => test_case.message = value.to_string(), + "signature" => test_case.signature = value.to_string(), + "testPassed" => test_case.test_passed = value.parse().unwrap(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "sigVer"); + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let pk = MLDSA44PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + match MLDSA44::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if !self.test_passed { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if self.test_passed { + panic!( + "Verification failed when it shouldn't have! vsId: {}, tgId: {}, tcId: {}", + self.vs_id, self.tg_id, self.tc_id + ) + } + } + _ => panic!("An unexpected error occurred"), + } + } + "ML-DSA-65" => { + let pk = MLDSA65PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + match MLDSA65::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if self.test_passed { /* good */ + } else { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if !self.test_passed { + } else { + panic!("Verification failed when it should have!") + } + } + _ => panic!("An unexpected error occurred"), + } + } + "ML-DSA-87" => { + let pk = MLDSA87PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + match MLDSA87::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if self.test_passed { /* good */ + } else { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if !self.test_passed { + } else { + panic!("Verification failed when it should have!") + } + } + _ => panic!("An unexpected error occurred"), + } + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // not working, not totally sure why + // #[test] + #[allow(non_snake_case)] + fn ML_DSA_rsp() { + // MLDsa44 + let contents = match get_test_data("mldsa44.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa44"); + } + + // MLDsa65 + let contents = match get_test_data("mldsa65.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa65"); + } + + // MLDsa87 + let contents = match get_test_data("mldsa87.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa87"); + } + + // MLDsa44 + let contents = match get_test_data("mldsa44sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa44"); + } + + // MLDsa65 + let contents = match get_test_data("mldsa65sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MlDsa65"); + } + + // MLDsa87 + let contents = match get_test_data("mldsa87sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MlDsa87"); + } + } + + #[derive(Clone)] + struct MldsaRspTestCase { + count: u32, + seed: String, + mlen: u32, + msg: String, + pk: String, + sk: String, + smlen: u32, + sm: String, + message_hash: String, + message_prime: String, + context: String, + } + + impl MldsaRspTestCase { + fn new() -> Self { + Self { + count: 0, + seed: String::new(), + mlen: 0, + msg: String::new(), + pk: String::new(), + sk: String::new(), + smlen: 0, + sm: String::new(), + message_hash: String::new(), + message_prime: String::new(), + context: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.seed.is_empty() + } + + fn parse(data: String) -> Vec> { + let mut test_cases = Vec::new(); + let mut test_case = MldsaRspTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "count" => test_case.count = value.parse().unwrap(), + "seed" => test_case.seed = value.to_string(), + "mlen" => test_case.mlen = value.parse().unwrap(), + "msg" => test_case.msg = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "sk" => test_case.sk = value.to_string(), + "smlen" => test_case.smlen = value.parse().unwrap(), + "sm" => test_case.sm = value.to_string(), + "message_hash" => test_case.message_hash = value.to_string(), + "message_prime" => test_case.message_prime = value.to_string(), + "context" => { + test_case.context = value.to_string(); + if test_case.context == "zero_length" || test_case.context == "none" { + test_case.context = String::new(); + } + } + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self, parameter_set: &str) { + match parameter_set { + "MLDsa44" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + seed.set_security_strength(SecurityStrength::_256bit) + }); + + let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA44_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA44_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA44_with_SHA512::verify( + &pk, + &*hex::decode(&self.msg).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .expect(&format!( + "paramSet: {}, is_hash: {}, count: {}", + parameter_set, IS_HASH_MLDSA, self.count + )); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + &sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!( + sig, + &*hex::decode(&self.sm).unwrap(), + "paramSet: {}, count: {}", + parameter_set, + self.count + ); + + MLDSA44::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + "MlDsa65" | "MLDsa65" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + seed.set_security_strength(SecurityStrength::_256bit) + }); + + let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA65_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA65_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA65_with_SHA512::sign_ph_deterministic( + &sk, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA65_with_SHA512::verify( + &pk, + &*hex::decode(&self.message_hash).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .expect(&format!( + "paramSet: {}, isHash: {}, count: {}", + parameter_set, IS_HASH_MLDSA, self.count + )); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + &sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + MLDSA65::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + "MLDsa87" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + seed.set_security_strength(SecurityStrength::_256bit) + }); + + let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA87_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA87_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic( + &sk, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA87_with_SHA512::verify( + &pk, + &*hex::decode(&self.message_hash).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + &sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + MLDSA87::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + val => panic!("Invalid parameter set: {}", val), + } + } + } +} + +/// This builds a "busted" mu where the ctx is absent (not 0-length, but actually not there) +/// just for the sake of compatibility with the bc-test-data tests +pub struct BustedMuBuilder { + h: SHAKE256, +} + +impl BustedMuBuilder { + /// Algorithm 7 + /// 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀′, 64) + pub fn compute_mu(msg: &[u8], tr: &[u8; 64]) -> Result<[u8; 64], SignatureError> { + let mut mu_builder = Self::do_init(&tr)?; + mu_builder.do_update(msg); + let mu = mu_builder.do_final(); + + Ok(mu) + } + + /// This function requires the public key hash `tr`, which can be computed from the public key using [MLDSAPublicKey::compute_tr]. + pub fn do_init(tr: &[u8; 64] /*ctx: Option<&[u8]>*/) -> Result { + // let ctx = match ctx { + // Some(ctx) => ctx, + // None => &[] + // }; + + // Algorithm 2 + // 1: if |𝑐𝑡𝑥| > 255 then + // if ctx.len() > 255 { + // return Err(SignatureError::LengthError("ctx value is longer than 255 bytes")); + // } + + // Algorithm 7 + // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀', 64) + let mut mb = Self { h: SHAKE256::new() }; + mb.h.absorb(tr); + + // Algorithm 2 + // 10: 𝑀′ ← BytesToBits(IntegerToBytes(0, 1) ∥ IntegerToBytes(|𝑐𝑡𝑥|, 1) ∥ 𝑐𝑡𝑥) ∥ 𝑀 + // all done together + // mb.h.absorb(&[0u8]); // these are the busted lines -- bc-java just doesn't do these in the test code + // mb.h.absorb(&[ctx.len() as u8]); + // mb.h.absorb(ctx); + + // now ready to absorb M + Ok(mb) + } + + /// Stream a chunk of the message. + pub fn do_update(&mut self, msg_chunk: &[u8]) { + self.h.absorb(msg_chunk); + } + + /// Finalize and return the mu value. + pub fn do_final(mut self) -> [u8; 64] { + // Completion of + // Algorithm 7 + // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀 ′, 64) + let mut mu = [0u8; 64]; + self.h.squeeze_out(&mut mu); + + mu + } +} diff --git a/crypto/mldsa_lowmemory/tests/hash_mldsa_tests.rs b/crypto/mldsa-lowmemory/tests/hash_mldsa_tests.rs similarity index 76% rename from crypto/mldsa_lowmemory/tests/hash_mldsa_tests.rs rename to crypto/mldsa-lowmemory/tests/hash_mldsa_tests.rs index d208e9c..d5b1bdf 100644 --- a/crypto/mldsa_lowmemory/tests/hash_mldsa_tests.rs +++ b/crypto/mldsa-lowmemory/tests/hash_mldsa_tests.rs @@ -1,40 +1,42 @@ -use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; -use bouncycastle_core::traits::{Hash, Signature}; -use bouncycastle_mldsa_lowmemory::{HashMLDSA44_with_SHA512, MLDSA44_SIG_LEN}; -use bouncycastle_sha2::SHA512; +use bouncycastle_core::traits::{SignatureVerifier, Signer}; use bouncycastle_hex as hex; #[cfg(test)] mod hash_mldsa_tests { use super::*; + use bouncycastle_core::errors::SignatureError; use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; - use bouncycastle_core::traits::{Hash}; + use bouncycastle_core::traits::{Hash, PHSignatureVerifier}; use bouncycastle_core_test_framework::signature::TestFrameworkSignature; - use bouncycastle_mldsa_lowmemory::{HashMLDSA44_with_SHA256, HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA256, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA256, HashMLDSA87_with_SHA512, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSATrait, MLDSA44, MLDSA65, MLDSA87}; - use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_SIG_LEN}; - use bouncycastle_sha2::SHA512; - + use bouncycastle_mldsa_lowmemory::{ + HashMLDSA44_with_SHA256, HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA256, + HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA256, HashMLDSA87_with_SHA512, MLDSA44, + MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87, + MLDSA87PrivateKey, MLDSA87PublicKey, MLDSATrait, + }; + use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; + use bouncycastle_sha2::{SHA256, SHA512}; #[test] fn core_framework_signature() { let tf = TestFrameworkSignature::new(false, true); // Test HashML-DSA-SHA512 as a regular signature alg - tf.test_signature::(false); - tf.test_signature::(false); - tf.test_signature::(false); + tf.test_signature::(HashMLDSA44_with_SHA512::keygen, false); + tf.test_signature::(HashMLDSA65_with_SHA512::keygen, false); + tf.test_signature::(HashMLDSA87_with_SHA512::keygen, false); // Test HashML-DSA-SHA256 as a ph signature alg - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); + tf.test_ph_signature::(HashMLDSA44_with_SHA256::keygen, false); + tf.test_ph_signature::(HashMLDSA65_with_SHA256::keygen, false); + tf.test_ph_signature::(HashMLDSA87_with_SHA256::keygen, false); // Test HashML-DSA-SHA512 as a ph signature alg - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); + tf.test_ph_signature::(HashMLDSA44_with_SHA512::keygen, false); + tf.test_ph_signature::(HashMLDSA65_with_SHA512::keygen, false); + tf.test_ph_signature::(HashMLDSA87_with_SHA512::keygen, false); } #[test] @@ -43,13 +45,18 @@ mod hash_mldsa_tests { // bc-java only supports HashML-DSA with SHA512, not with SHA256, so can't cross-test that. let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f") + .unwrap(), KeyType::Seed, - ).unwrap(); - let rnd: [u8; 32] = hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f").unwrap()[..32].try_into().unwrap(); + ) + .unwrap(); + let rnd: [u8; 32] = + hex::decode("000102030405060708090a0b0c0d0e0f000102030405060708090a0b0c0d0e0f") + .unwrap()[..32] + .try_into() + .unwrap(); let msg = b"The quick brown fox"; - // HashML-DSA-44_with_SHA512 let expected_sig = hex::decode("8fbf0813a2bbe17e6a8bae1bbabc8704c59fe8910b8125426b6983eb50bb26c8b6249722fdea7c26d731d7ca34ff100be306d6e7d11367e521e783eaf799cd8c235e45c663abf632aad1543c5faf13220af0eb06c7a0e7f0d1a6385dbc7fd10e58ed905850c9f9692ee8ca6642dcaa2bb1c6fea12bcbdc59d5a19c78ad1ec952dd4f22e651b2a42035b63cf5b510ab95cf0c9a9fd77389d3fae9b42b123199c84a881ff30d7955c9841f5319d93a2c531d4d26bc6341f07c42acda0f5ec4cf70932dee570292699128d23f13ebc7d79bea2ff7ca352369e8b765e4e2fbcb2476f67b8cc8c84690be164e08c34be160806435993be3dce5455338f14eb9f3918fd70b3753d374cdd84c350654d626881a0757a20244b86e7b5eba61a517e75f60e8658795133079e72b8bd4ce9fce5c6af2a94988bb3141b38e8498d9f01a5cea3f2e24f5f4b6f64e2105010d9efe12693241149f115ca2a4086c456a9c852ade47f07f0a78eaad4ed4a67a18ffb12f9f9eaa151b5973010f021c7f11a79df404b637fa4a777b3ef7dc724f191baac9dcf1a5e376978c146c944c1f8f510412c05c872551e625b50426dc0433f89b89e67e6a6bcac4c1ab86c2da13cc0c52911319889cbecfde58c5af586ff0b802aebc18b13014f5d189af1fe335a8fc3b37d90cbfaa89d7f6db2d9960787a49c7c632e339c75d3e618d55971885d4b45f58db4c9a0fd50ababadde1ad2423178e0aa26e6f3d16f6b6f03f5dcb2e2eb54ca4aac44fabc92f6b4eea194174e15f5c26801cbb8519e04fc8bfbc8ddb63a3cfbe4ba2b92c7a38f3c64a1702ee785ccb745d3a6f5853521796526c1dfc2b0bfb774a2b1812524e6ab5f15137e22dcf70136274cb0181cb277303478d9a5153f56e9624ea9d2f838a9bc054e080973a86e174c72fa4bb78c01598ed3f5115939fa172537d8799ada93af028b437048b0ae1b412fda490b3a5a292552927cf3ac540b1c67a97c2a7a94a6217a7a3fb7526c00a0d2a13e64aed1449c4029c4f9ef7b7c783929c37713c7cec1d55d1371dbe6ed00782e143e2ffb74cef8bec56c18e37e707e1a7e1fb04cc0243f0002de7644e8780f215910754985ce1cf6b4e16c0656e2b9fe55fd4fa4340a4de5b01624afbc819902b90a17f0b8d55841f2d3b41e43bc2727b3584ab49db5548169c5e207ace157469cc2d712e885e67735afbee9d5874b9bbac6a2d88cf8f957537c137e44b105202942ecd3cfdd792b2657f025d48c4ea172052c7f33ef8f44e808b8888ca755414eb191a1c4cfed2ec6ab9dcf8aa1451b1640b09f0022349091d19665fa3ca2d5f6ab9d883c0f03fabfe9565c7fc2a536ea73758fde6490f4de2e138f39a628175f2860e8694bdb9c2045d218c78b29243ec2b40e5bebbe2688985e337b528df5549f4adc5a36dd04f7045bcc436676cc6c8b58b0e0205b7e1bea512749102883e4a65600dbc0744b03f2445950eeb536cdd8a88cb90d069c4205e4a0df830170c73779245729d896d14730dccce05a2f1cab706e9929cc1ace014727d793b1f1f8b572bc7a760b15b325c5fa4b1511f253567caebaafe7acc0cc400e470cce9ed5121caba5371038906d8ee1643f336146ac6c743c2cc36912195da57aa1e557ee4040997583dfa77e0bbad48ff901ccf4f28b32b350f2383812a5bb59211f8a90aefda3eef487de26746303676d5727a4ee39dd5a2d8e0072fcd4dab6e0af099aee6b379283272c3e56b5a55b5b399832482ce311a3a629ea2e01cf4c236ca4bc807898fbce977521fb75ff02699f81a26bb69c7a69d46edbe4575ca2f11c361b269b918f7826c61496b815390efa51b92bc70b83c3fb1f311be5b23d7cf6fcf2d4877c3e7d439c4bac5aef81348688f97fd34b32c3cb798feb38197c6754527a75cdb38e28647de8fec0d77cf3786cb5d339f6569ca879d941d88c8cc1194443c40c0ea86d5d4cad5f7db683effde3339bfd63ad5cfb1caba26521e3c9c6d93d9c58e38431e40eab5f7cb2158c8f48e771f551e940a8607af3fd44aad01bdb9a04418aa03aefeceeef5bffed53cb37919d280f8f8d73965b02ca4515d26d33ae3afc97c779b72656ef34399e6508bdc9017bd17d17ed675db7294fee98bf8fed1d84154949dfad1dba8168ee1f6d8828f80ad5a8c11aceffde97886fe2440f26b74436a8534f5ac3de9fb61f3bd6c7ec5c761aaf0036be004a9d5d952b8719afd5cc6da5081632e1a10398fc7d7edabe522e75ea774819b1f2f558c46c276eb6419504a4f9d1226544ebc4dccfa76cf26ad90661e9f78d563472e78cbed3833655983e9458aa71dcdb44fbe13295606bfe7a02715044589652c641585e3950086e40e30885934b92e302ced1a94e95fffa9402afe1f359569a394019d5265862dce4b828b657e43591d199b3500394f871155debc78922305c366350868bd81b06608a44ae383aacb8c0761bbf8bc7a1ee1b9bc7f5a9173544f9987c9b15706a50a193c84dea3317b71e04369a52c32cc3d0eafa918eededa4dd321b1ba99a668c436f16f7f2f1a1ffe847f86a6a1c39b857c118b848593265042eb4a1ba8a50303ad7034d2ab4960bdde975dbc3fa632777b8ff5c541af07e63ee05defa4aed3fda7a69a67191617f92dac21e511db12fa95a5fe1ca37f184e02f58b835faa8ceadd8bfbd938626a7565007a5e022b97debe1732835560e74bfd58c0eb0624fb36703d5aa05a71256cc432bc3850f7b982048c3329f717317e9a755440d1e6d3934dab952e23a993d15fad17534bc848060b51a15e670766c6bd3649957bf89e8fa34950fb1870089a5a9e82af440cd2571f2edaf68d4c1ff4a82c30d7e0b1ee60483fbfc3eeff73c97c7ec9d07444d05624cebbe408f2d2fe6cb43c17d64f135b113538035d0ab73e9822b804b607e88ae999a035ee22d7fda883c817ee5a027208bc22046585f24451f76dfc6e9da9e62085de03a323de7b7ba09cfe6bf1e3b1643dda9d1b798edc54741084595af65b36b9a323a90edefbd37e9038b68991846cb5ecc442785aa7fe6993cf3cda097c3417d234aeac8540e12f810a07fd78548708a72092ff1c4b59f9f8c4023e89a344ded87915b65cfb5547a57cca97c33c861b04125550648434e960c144dc7cefb12459b314da4d6cfdab29e2f4354dbe9ca93970964816c366924c84fd1e7f592cdd8fb37264d359d508bff7b2fd342d80375f87fd76bdc5932517aebe6aed1a7e27632e980b63ec70af947130ab190de8bb309ad1528a51a5142215181b252b2f345f6a72aaabcaea1114152f344c5764656a6c89a2a7b7b9badce5050a2661738f99b5babdbec4cccfdd35677b84b9c8d9deedf4f9fc00000000000000000000000000000000000000000e21303c").unwrap(); @@ -61,7 +68,6 @@ mod hash_mldsa_tests { let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic(&sk, None, &ph, rnd).unwrap(); assert_eq!(&sig, expected_sig.as_slice()); - // HashML-DSA-65_with_SHA512 let expected_sig = hex::decode("cb99a9fd2063ddda7114cf99b577b7d9a6e7540ca225d84e5b04c28e30a4a09c6ec470a596a1efa809a42250487d908676b8a50df1a032c1c4c5124e989ce795a44faf0bd5e46627b1272d99b065d38c60148892bdc93159c828cc7e60996fd1825993fb001a901d7cbfb5a24b05446ce0f4ba5629434224646d10dcf4a55c851a22c690ebc443fea0fe2a7858d5e175b1e0a9115b3431ebeb78ab670f6f79c0f94d60fc2658b24a9ef06f51434aa4dc1b0797cf905dea13d5a1519e6483dd60dd33eee62a2898d3179d1b459cf316cc7ab2a0be94ec676f1b7e35b5ee123bf17fa39aa188210d2906991beeaa5e63516debd90504dadf4673de1ebc67a2b7e399b30485a4ebe37c9e7dce4c885076d3741c7841b319fb6b5fc228392935b0d20fe1ec8a4441ea53e47940816f8ead499d73702be1ed6d4f99c5f48d82acf60d1b08c4fc7d66c0cbfa93c4880977e0fde301dcc038b0cd354f6ba7b14191ed925f25cc1168ae1b48849b6f24e2911e8a4046bfa1b3e2be62369a730096c79d9f460e79adbafe4e9c723123df5d872cad3fe1553df2f6f49ee7f5278b75c445a3aa4fb16836faa8f6e766d457f803ffb11d32ca9de876ad0b8733a16cbd91d319489b738c9af693266c115af3be2afc29e2c6f669a133aa7b22aa6a84a7adbfe0dac48871b3f41ad20f78f003761b729eeb8a6c05cba15df2491b882e9699025cf2483151a6fc6e0839fc13c4529b3a5a67c0fbaedbbed2aaa764d5b936a94e3e281bb465b2b2f837b396c96c75bd3e58b8b344c13094eeb3105eddb41b7d1e8fd0f05a127e4d306c6d011a8d7da26438ba50bcaa11cd7136a738c1562c4a5681b3ae5870c21838f0fd1b79d20528726adf64fe3c85bd92632e720cc6c9fbdf37c6d293ffaf7ad2e01284d66ef48b6c25b9503f61ba967031bdb462528eb6b02566385a7f51bd0f92404c43c2eeb325c76190e121ab57d308f6749ac612c138664f198a477a1eb59d2cdd0ab217a2f3e79120d9c09936a8671ffe35ab87130909f84a9beaf8733b86498736be36052a6852ef2320226369120c20c1bc4cb676a9a31dbe346601d5166db023829fe000e2be7d7f9ee29ea4de25ad3fb66dc2cce9669d2c7aeb2bedbf24c117fd40a563172246fee9ac79bde567d09032d25b54ad017c367dff2f69c55115f142724bcee8da2095efc81611e5f357aa5b5a46513e86ed1e28266a9d110433fc4f69b7e965abb6d781d69936b1eefac7a5efec7478d3b1b3e3181bb3f415311510d6284349f63586bfbeb5e0ffa6d89d518a2a12997d0622387fcb267844a9c7281f04a34379038d1b8db1050058c8b57072b4c6bcc582a734730529e5e301dff85bfb90d7e6a6214c3414caaff452b11adacf7dad839516ad2d6dc2b7e8c9273ad0229b004a303c2771e744cb227f7dd999c08094cc70849152557ec3e90797710cf88996db05970f457c7c32a219033ffa71d8f11a422d2bd6a71becf07b9694149bbbcc1bc773c94f170d7702086099c53d0a24b8780d586cee313df06d5cd4ba04925a6f85390bf8a9e93f1251a84e34802cea764e7b8c3e50ee500cc77ca4e265cd2e7514db01a502e054cd407c5369008390d239c62e42dec5f628da94e71a97dfc86e3f916fe66d13a8c72102ffd03171293a6be503768eb418c4e6e66b7836244354bca0f7076683364130219c19ad843397c307f0c05c05fa521226bda6ea6768a3f473e5304964feaaa78c2b6a50bc666a4ed0ed655702239ce85fb86b7a2d8520b0257260faf830b681b86b11cc0cf43d63d77f6d2c8c84f4fb55a6ba70a32e389d167b4ecd07bf909707d3e72da5411e0136320e0460b70e0a39469260ea51370b4cec27f82ad86940725584b7a2a370cc9af96ad607bb1855f9631bd796b34cfc1971c23174d54375535422f5965170e84b78e89d4bd9094e9996e018cd8872ade216ff5c4679174cc24d9ad112e5e2be28f9792a1a5716969dacadc1ede4911a8cb7f6fd9fa35d68974d0481c736a265f1c74397cec2eb1235d725b0241875d6e413632496ab62ced9e922c955a7d09e9dbaba4ca6642a56ed1617088298d497257c5be1f7ea55d72560c919785d24395873efbfab048506fd88489aff60c1846419a43e77cd3689d2913b9845c2d1249440aa143e0043036efb56a8877e1218ff693d4e95f8c86dc0b38af1d41c651dc681680bfd3742127bebfe32acadd8cda346b40374fd7aefa84072e52b51832b79ee5aba35562c161f53234093472b65785ab638fd56c0219deb1c19c97feac2e833f5105538fc81acdffe51dee05ac4fbc583be22c62d45aad7263736981a9fc5472f30465bb4ae1f677bfe53342a573befe34baf9b8b5e3b64e3cdc994f0b7cba4ec6342ffb0ebfeadf01eecd63bd6496b534747f0379505626f1068e997fd61effcb3574e372b030dbba4a14e47e28b38e4f8ff910bc589756255863400f1bc638ec227eb05d49f9227f59c463c2162ee107ba3f6151cb09220287b90d27f9843b74db9d22848d197d4124aa20cb2ccc673344646ed87b1fd9e16d674b6d7ddb8f7921f5de5c045b9c13362442c0eeab3ea35573a720c68fd640ccc7a7b3e630a9b7c799686e051666976581e69fe79c4275e308fc7a60f69021e2c501811269acc05fbaf82f12a0535da36134aad9def3a8523c6f6edccfa3523c02f63bf590deea39fd30b3bdbd2deb9a4a076b19262781a5f549943e09f7995b594980f838b1ffd801c0a3b69f5a007975f72cc01d64291e85b3159623f210819f44f773375a2434b3b479b9004fdb8d6100481f64a2386de41677da5b1e7aeef840fda0e0f9a01dfdbf52cb18f60d459029bd536c22da9af2777dfa5093c032c4a62fff604fb66e9d0d3a249e8a6596d47b7d658492b3dd7fa14d507a5dd08927bd118e889e5ccc940e484233d245aa2cf67394e4fd15e5e129d3955d4ac6e230c974414cad06ad301da92fc6453abbef3eb14647e088b9c6ac74c3517f9d6ffd24eeb62d3f591aa34f828c1ba902efdbe322160326e4d46eef78726cae9c6e68643e2dfa259ef0e761f5142e06bdd02c5f516d101fc4171391ec338c427ab89c757a3847a928f1cfffe40a9fa6276c7d08b27f9910cc02f14f60d0fcca7cf518e7f0a6f546892b38592744fd4c670e6e68912b0ff2de7e5a907bf67f33a75f16ace28927eb375e5c694a712c122ccefd72b3d755bef017fb7f1ce17e366a8993a1b8d0aa14c3eaffeb78e778a192d3d80e37dcc4344bf4de73c6ef7523f53fb04232ac97aa64ab76b115405adfeeeb5fbf97f448dae8ce4dd26e68f8aaafb9cdce8d38522976fd18c80f7e3f12dc459f6c8c43dfa3b1ce0cc4d7b67236e63a20ec156117e0893b8aaed7d9b5c9d3751589e38591533c944049bcac38a7585d4dd3016ce70ac1f0d5a4d656e611d87f5ba65839aedabfb1820ce60e2e02c33ba16e02dc21ec349c1da32a54a460baea2a73f43cef1d8877d9320098dd543f786bef664ba8216d263dca871760926a405a25caf226b85a95382386fa5a2144723c3aa2ceee4a69d4f3f8473facea0fdcb38c5f8a83ac087301c4e95de8023351a258870f6fe4be5f9e713fd84ec28fdfb1737016affe39b29daeeb8ddedb5a1025885a7c77e54c4c556944bc6fa0dcfe1147d8be50c262970e645254e38ffe1b4a2c8583cb4769b313e37b86ba2c572cbb374d61ff294c62c9320f28af2e2ac62ea6d3a564f0ba53e79d4f870306cc8c524ecc2f13cf9eab683a483f65aa99249ab8437ed759b664cf71123ab2774892680c8aa0948e0f4a320a45583d672a4d70b7ac3e2fd42302a6f572fd5d5873f80f68b6da6a47d0e3361cbe30071345406e992439911a20df8c82875992ca4a45f8934d3e45ad202341a58520c12ad83e22da9161588688aef5483c9f15c9d8a0a5c7c4dfbc9fc0369d6727fc4ac6e532dcbb8bec20a552c4acfaae9db33fff348041ea2887c0e8d5c9b798032ada7a2c0bfe814e2535fabf01bceac486106f16ea2df69d2eaacb7d4b02e50c3352c821eddb503075278ee4eaf779d6c3cfd2b77c388b97c66abe59b507a111de824211cb57c1ce7a8fa25309061cd3847ee67508bb6354686057b3518a3a33de389919398a6f5609779a5cf709355fcd6be7ed78c5c83951489ea7cf4fbb7c3ec210abca5263736dc462b600132f206bcb3009c6c261769745b7a9d5b691ffe858bae747c710217de7ba66431526bd6e63fb9d6f36e828d88b32fe9212ede700e41d94040a9312961a4071907c2161cf9e5b15b426aa25074ee0363563e1e215a3c2b44173e27704ec8e788ea6b1a9ec4465ac6d74b78d6564cb67ba378f419a0d65214a488ad2120be6b72c58054580cafc3ab2b6224b23af3c248920bf601285f2b9cce1793904b84e8070315c4479eebc003b4f3cbd37c1f0895614d22950695c864ddd8ea4ca20d472f44860a991969d797f5d502bac7d8797639313afa2244f43874c3758cc4f7231500d8bf1052f7e0d370650f946beaba47dc78ed963019f7ed67e93b69993d0947b159d4dd0eb88526242a3542749dbaeef1fa1361db6e7a9193b0c2d0e3e5e6f6fb232580b2b8c111acc3fc233b8d90e500000000000000000000000000000000090c181e2227").unwrap(); @@ -73,7 +79,6 @@ mod hash_mldsa_tests { let sig = HashMLDSA65_with_SHA512::sign_ph_deterministic(&sk, None, &ph, rnd).unwrap(); assert_eq!(&sig, expected_sig.as_slice()); - // HashML-DSA-87_with_SHA512 let expected_sig = hex::decode("54b340f2f8318713194c4a7fa5381a4bc09af874ce020573c2da381b156b84643209a5506c2d31049cc71db7552acfd32185fa5755b4bcfbc8570c148a15e487a8be1ebe7f7cf735689cd49ace98831144dae634d50ffeab8fca9963077ff25f534f23af50d86b9131f4293fe638452389d1df255bb9a9e011cb6987f45f4a51d85d907b839378d2fa1d4ac23f6736eb2941d9faca5c174025cfb88ca43c24ca0d774391fd02784a7e0b1064f9e9f5b736ad0c9b9b3410c15a2c24dd178a7ff04d52b4985a7fb2c6375002546667cbc79c759b61f54087c3cf49d1c5014f66d4d6b1bbe7b30a5dc26bd4d667bf5f970452806bd43c8e195aa7219f073776c4a05fd886197158fc694a5be8a057d57ce499175672000d677c3c20f4b814cf3f340335127dc5c7d9359eebacca82386ec1ea0c4b8735151437ef7f33b9f276b5d95344305d336798936a1edab987ff50569d9410aa245192d1d71732b2e8b90ec275ef7c293bb982bb73cf1b46dbb585daa4c89feca3b197c8cd29eec186b1a724f16deebf498e6a22deed4b47c980bdace1a05fe7f9c42fdaea8caf11a9d4a84949a4a3be6c7fe6e8c0cd721fe238558bb158c5a40203024cfef40f27afb216ba61dbd3af379b86bf675f6cecc6cf5acde4e76b63b62b6760802ba7497689bf24580659b17c15195bb9b8f55547dfdc04171eae5356817800880bd47de17396171af06408b73f87c8f9ead1124c13899618518eeba5eba9ac0fc39b615611d25ccb1a11e83226d42bc3bbb0eff64cf6e666227c2a3d551edf1e04565ea33e5b8c8c9885e153dcdcb3608e90ff2251700bcd2111bf5e292c29f69dc80a4dbd47b9e0368ecdf0925dba0d27c9b6f82aa6f7ff7e8385d3d0017b5ca65e039c2a8913bece73baf10a413be5de60729b29614d1c35928b91831ff2c1e6d24be82b56e446584c2a2a18ced134676eb887a9bf0026934147e4f70b9d30ea95b432301e2280981a6a8628c9d52ca5f91f5e2e20b58ee09abc035e079b508b565b7de0d221c80c022f83e5835747e2b5de4d5070af101657e253f3e26caa1238462a151149e66ad8d435fde6fe0d08bf5cf9ef9ecbe280ff5715f03401994cf37d25aef7e100db66c86ea84bf738145e2e9a13317bf7649371d6c21f79ea599e86e2f25879e512b5b7aa9f6e84e85eb20aeedfafd605d69a674c662e921a54c28110fd4b457f8058aa1c781379a6720e420a7ecd949c2027ce629808c9ab26efc951e3a1fd3ff3241303376191d3d9e1409ac56f6871e13a012f65d80d4f566133919ce3372c47e5be464cc70dabe9fa201fcdc59f8e8c311f8f43d40bbee23d563da37f82b1dd7feafd112020d5ffdd3301e440722d5bda145048a52d10b43418cdef434901e3fcbb2e931352c2a675bea9bbc5d6eddf96a0ebc7d71648f211bcbe02ef26a22f489428dfb38473be6ffc7cb2703c3e4aaa9c27f6e0b933638ff432cb91146180e9368aa2cce8416768a3fe8984ae3910f14888c64cf3f1e1222904831aec32b6d3b2d2244218452c44441e20d27f342b9cd60d603f0fbb7879752cc4553e6dc9176515b90bc0d933d6d4da2c405e5d4475798acb57e419c25f2d7af10557ed48e1f493ee55303bbf5824ba7447f4f41e2767da3d288d2beb067181cdf1bb98664608776134c351561e761d222c8906df68a04de76b99358d76b538db244cba491000c6dd61373f3ce6d62fc4f4b158ccf9f6260ea02e19b9697d946d21264846e4cf7d78f5fc0e9de819ef654c3e1d820ccdf45cd0a600806813d5ffdbd6f0c64fd1928df7d92f21b20a6593a71d418a0a480a7beca0abfc3cb732cb93e3a0ed51a06b5cfa782601ee6174a35ff188a7af57426fe0129a1039d76c9b6d523873272cc7cd90f042948bdaa4f2b690be247564938f185c51ae63a6e54e618f6787164fc09275d4b7b201d06de3bd4b57c4909c0c901f39742a6f5301d4c5a2d8e314b5009082bc292a9b39f61c45c88160f93ef2988ba9d7a80d5f52360e331cc828100e17af7ed4aa3491298355bae804e3af7418299bab44f09b171562ae098b0d507b0bfede06f75fe6daab51f6dcb9ccbd38da9b123dc6cc5b775399696bea6c5768e875d341eb173aa6d63d85f150c5039d843234c67c3cdf1c101b3b019711adb42acd8ed03ae6c7aa44c35a845263877023a3b7b4679c4797f43e4980f27e18e30e0cb088b8e9586aea971fdaf6bfb9ddef3ae7598932cd4b15bf9be8c0c3778f7aa609c86d583f7facdaaeb41404a07d3bbe225792e76c2ef86dd68ba3b945d7b849cdf87c2ccef534597464901fefa1441723d601ade276380b97d2426fcd19fc90c09aac5852599b955f1084c79ade067bbf764b4d2ae597e8b87a600e79f84796a12f61047568612ea279baf9e3feffa3d16de35ac00aa010c551ebf7c27f8484b1cfcc47959ad20ae180c92cc121f0d298bbac47ae482d4fab23087df6fac3b8e026c39faf921ba1e53d6a776832a4b96da9875a682d626399ded7f577e1e88011b9e14a2ed73d3d7db2ac67844e3017613a237991fabd91b7df66948e2f8bf1324dcf45ac3fc2983ab749c62ea5bd1db3caf8e7958686a1c7b7cbeaa88d4bacbbba0ea21b74c9d47fdc61df30fe021adc5129d3b82a7c81778deb908b3f2bed5519c124ec1395a222ba7e54dac2df1656c318f66546485d1037c34ae70ee64f678ca6a1f1930e235c5a14658e98dba421e0c3ab342d6f2045c35d70bb712ed9a7890f9471c43c376ce8effe0f4f263ab307973557c38c2b6dcc137c9f7b42048f78acad85f0d5ee9efc570139475628bc6a02c4ff431bfd4c9d9e7307ddf6bf348ca778618fde825f1f8bf286e70a2bb6d784602a83400cbae753b9fa35f59bf9770a26dd8c04cf46a7e5e9435ff26ab3719f8ca685ad321de859d3a56819d47ceec489e9bddaa85ac0fd75b831b96b374b9effe1425c4f6ca49a66858b5563033bb91a383f91a85220c088209614975bf509a91a58d36fd027425d01f2516cf87a50f7928981b1367f29cbf8ca313e4a7ce130bbcfe95775ff3b410c0c4ccb0b2e6ff154235ecb28694c16ea728ffc0a7bec0b84cf01b0605f1d32be08c70a794e73dad1c5a1498e8ba9bdb020ad7beb959a0c70b93e3c02d8d068611b2465e096f9b08acc49b90bf697b197f1dd00b0452a06ffecc35627e031143e9beee4b292a130f0e526e82eba33be227e813c3ad47acfba9399418d2910ece7e14db125b5f1e7c4431599024ffa3e82d020702d3f65338b9df5fb03c0795b892a8e0fb494bca5f0127bafa303532524baad8031259511d863ebd8d58afe810b37a612b99c4514bb5d0d22615fa0ca833804c7bf06605720f3f18ff3d257678a4ed048f6c5c21897a9c810192d80a24696a0a99a88675352b934d6d4ef884ef5702927d499525744ce004c56e5d231f5bd1520e679cbad48741cab40707bbd651cb65940f635b83c94a76c462418e6059919fdad24fd55c32680848f97ba89ca383506ce8b5b1d0165af06e0d7b51694aa5607f1dd78cdcaaf87f700019fba62ba17f6609b993a44c839210fb266ea437bf88c69cf7dc3159b93dd30e995249094b7460670892f2da18d47e34a01b212277f5cfd8aaa07f090c3390ab7a56425c322cb130fb7c5bd7dfd602ad7c586a21ab0bc03a89ec49ce08f1cf920cf25d07561996673ea5f261e122616753d0a49fb7416c4dd8070f5a95b9c0fe6d11abe1f8aaec1c620ac3033ed6d1e5c29f773184f9f10790bae64d1f6a21bff248c6773acb075827e7193fe9d40584b6533afab5cb1f4c8a7d6c4adce7a2e634ef907020f06a13be3c8586fd7e65a745c345b3efede788b8c04623872461652f0b04239c19b5968c23e1a3e41529b7c58690bb9d749e6d462ccef13baaceeaf6013f9538e82f47553b01011b3e599b8ebabd150c1f5776193d18dac011d47e7da75c2c2e28e96105d20ae7d2c77f0eb19cfae34a8d18580bec72823d2f50122f06de21c6f21859fa90abb2a4f6bd07b377bd40c0e93a84b16f4d0b469d5d0168b3b632c3006ff6432331cb4da52b2f69e5304905b1903a6d1cc2305296107148eed143d9ae5069757cd91541b83658e82370ea6ec36b9811e6c35081e11c1380e59315f9ad94cc25087c071429c0e92fac8a2c1a53dd52ee18ebd281deef9048225572c765de65e50d6a3c9f4be09bc8617c9b7666c0322b73d81c333e50f8ba470ea4c7ab0b2c2aa972eb67a68a0e7fde93e75b09d81558fb61adea9d7f74a3532baf1b099d78e35402e95968f325331118c69c9176a9e90b11977b2ee7b4036c702e92894aae27da2c2074191e791db8fae4f85245fff7375519ba86627cbb38489d2afce34b0a2f76e52c2dc828b139b79ff9e5dff83e14b294a839fc8f688a60d2893eee9c9425d2fd49912476d05222800b936e3458b971c870f4c6cfec89ca70e91e5000a8fcc84f763cadbb56103af4bdea1276bccfbb228818f76667f85f63bc10b22105b2067c52ab52fa513bad6fbb0b1462f11b8cff7231942765f2e4eb225a73af6ed4873166badee6e8a2cb6ceba09691a0b8c0da29946b82fd07e4f484e66fa89b3912283a9c877d4b40e6c9e9ca256447c9d2d5c6b40790694706d6ad88f0da66ffab7cade6322989e8197c3d3bdac3ccdf16a4f59fd02f0ad3ffe3bdc77f8d83d21491808de9880111a91bee85225ec89f5d0f585c82d0c5dbe448a7fb1ab79d4a993aedae36a02abf5970970971eaf674761d299be56177f17c3889db7a4ad539df3d4f0a4f5a76212809ad3e18124e24799a55e6c28ceda011120bee72b695a17268913320a40201fef32e554e8c90d78eedeb90b44769fd54ffd1acf58135bbd5d927f82a99a747cf1503e9702a0fa72ddabf25e1f67948456947bd98aaf04136aa815901d94103c0377f4d4dfa1e9c3075d2e63f0f0f9167068c1345786f3c159ecdf694c1509da518e26bb22dccf6c14b4bc13b75ab00f71aca9b09b25247497c4dd29af1484290bca60864f7bebcc69627f4a3467e29751a01ff0a4e7c1fbc363b20ecec477061a9cf326823aed9adc396f4f12d15cc806f6e14b5ceca6b0dd3729c60e91ca7b34fc9368aaab98111fb8b33817e843090b5c8418cd9617f2e61f238f0258ddffb13413092cb67dfbb04188e8770ad09753deffa9e83f2ba3d72efcd5b8c8cb1ff8910eb53fbdd96ef6342f358f5c3d08f5c863123f6de0454e8c358abba2bcff97c563ecc59175f8314ffbc082ca893d5383dceacbda304450c7c9553f5aa0f1e0a41b8a50b1f03d689904f446cf56f2ca1202bd24f153c06efd63558407318d12eb933a0d68dbf9164e1a487161ed78c58eab5649ad84fb3b42494e797fdabd19857b786177deeb3b84896f0f31f4a71041bbdb39c2723ea6a82fb61882c4770cba23f3d009220f2c15fdd6ea0fa2d473f41c7d5527d498e131435b11844d3f285da458c86d80eda63609a6fd83b0ed004f7f553e3a2787922c881139e2f40819f4a1f29dffb7b42bccb38238b17fc63c18d2931c2c6bc45a418eedd10c3799ad5a993a54647b8537752fbc456e00330fca1dcf249c4614cd16715521a98503f80e4e97b36015fef9bdf38424a9ce2ffd9441c8cad34757da186f2ba4ebb39c664e33a6fc9df721da3b94e2bc9ff9cab1dbfe5aa07a7c7c7a715b8bd97997578428e9c89a0c8569049f942f54a0a63a000b412849a5897d2fa8b79c479bdf17e9ee8869c8e62c952c91b9c52acfc20a78993d9f93ab523975beecc1d52c01dd18b88821719012979c21818143adf4e89b60a4f21631e0023c6c25c9236d9ccaea6c44eae4507bdcffdb83c719005293e5f0d4232f3d3497b63b6f5464d78378a65b5cf677a4dd2a3f9c072c101f0207ecb99ddad33c333ec425d3f4065b3c02e9b441dfe7b15c2647f5ef6074b0c67ddde60c6d82e9f7d5a60fc429fb77b433ec71305e9939ae60ff395f2a05a413ae2cc5d6bd78c6ff2cb38334e0dacdfa36b247cd21bfbae48c8e9ac6d3012067c9721bcb946acb2bfc8004f08b5c69f7ef86bb2fb982e2a0ffc1d408c85819500eac5d02d13832b5edaa3bcc9477d4749eb7929cc66e8a47d5378b268ec26832906be7ceadddefa1c449fadda6b371950dc66343e27c74b62fa265cf7a2e9c81b19cce51a1d0b72c03dac45b825c2569841885460ccccbde6e9fe88bdb2acacbed6f18e7e77c1680f8ab792f2d1b08d67e36c194947e22f6862bd24003d1e139f0fcc2a7784ae420e3e03943e3de2d6d6627ee3d6ccfa4c14155ff8428a2ef31b420df2988f8c77072cf53ae91b3ca0f7f42c14b8a88b2e2ba94a55834dd2da785d6b2095664e4cbf1b94796aac2c0e3a1ba3c070804336848a0b5e558a50812b0b1fc0207769ca4db0a8296a9adbabbbddaf417193236698390b0f9fe0d192d404953ade7ebf2415998dadde9ea153b3e757ef52989ced5fb00000000000000000000000000000000050b151f2930363b").unwrap(); @@ -85,57 +90,147 @@ mod hash_mldsa_tests { let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic(&sk, None, &ph, rnd).unwrap(); assert_eq!(&sig, expected_sig.as_slice()); } -} - -#[test] -fn test_streaming_api() { - // I don't have a KAT, so I'll test against the regular implementation - - let msg = b"The quick brown fox jumped over the lazy dog."; - - // ML-DSA-44 - - let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), - KeyType::Seed, - ).unwrap(); - - let rnd = [1u8; 32]; - - let ctx: Option<&[u8]> = Some(b"testing streaming API"); - - // BEGIN expected values - let (expected_pk, expected_sk) = HashMLDSA44_with_SHA512::keygen_from_seed(&seed).unwrap(); - let expected_ph: [u8; 64] = SHA512::new().hash(msg).try_into().unwrap(); - let mut expected_sig = [0u8; MLDSA44_SIG_LEN]; - let bytes_written = HashMLDSA44_with_SHA512::sign_ph_deterministic_out( - &expected_sk, ctx, &expected_ph, rnd, &mut expected_sig).unwrap(); - assert_eq!(bytes_written, MLDSA44_SIG_LEN); - HashMLDSA44_with_SHA512::verify(&expected_pk, msg, ctx, &expected_sig).unwrap(); - // END expected values + #[test] + fn test_streaming_api() { + // I don't have a KAT, so I'll test against the regular implementation - // test the streaming API from sk - - let mut s = HashMLDSA44_with_SHA512::sign_init(&expected_sk, ctx).unwrap(); - s.set_signer_rnd(rnd); - s.sign_update(msg); - let sig = s.sign_final().unwrap(); - assert_eq!(&sig, &expected_sig); - - - // test the streaming API from seed - - let mut s = HashMLDSA44_with_SHA512::sign_init_from_seed(&seed, ctx).unwrap(); - s.set_signer_rnd(rnd); - s.sign_update(msg); - let sig = s.sign_final().unwrap(); - assert_eq!(&sig, &expected_sig); + let msg = b"The quick brown fox jumped over the lazy dog."; + // ML-DSA-44 - // test the streaming verifier + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + + let rnd = [1u8; 32]; + + let ctx: Option<&[u8]> = Some(b"testing streaming API"); + + // BEGIN expected values + let (expected_pk, expected_sk) = HashMLDSA44_with_SHA512::keygen_from_seed(&seed).unwrap(); + let expected_ph: [u8; 64] = SHA512::new().hash(msg).try_into().unwrap(); + let mut expected_sig = [0u8; MLDSA44_SIG_LEN]; + let bytes_written = HashMLDSA44_with_SHA512::sign_ph_deterministic_out( + &expected_sk, ctx, &expected_ph, rnd, &mut expected_sig, + ) + .unwrap(); + assert_eq!(bytes_written, MLDSA44_SIG_LEN); + HashMLDSA44_with_SHA512::verify(&expected_pk, msg, ctx, &expected_sig).unwrap(); + // END expected values + + // test the streaming API from sk + + let mut s = HashMLDSA44_with_SHA512::sign_init(&expected_sk, ctx).unwrap(); + s.set_signer_rnd(rnd); + s.sign_update(msg); + let sig = s.sign_final().unwrap(); + assert_eq!(&sig, &expected_sig); + + // test the streaming API from seed + + let mut s = HashMLDSA44_with_SHA512::sign_init_from_seed(&seed, ctx).unwrap(); + s.set_signer_rnd(rnd); + s.sign_update(msg); + let sig = s.sign_final().unwrap(); + assert_eq!(&sig, &expected_sig); + + // test the streaming verifier + + let mut v = HashMLDSA44_with_SHA512::verify_init(&expected_pk, ctx).unwrap(); + v.verify_update(msg); + v.verify_final(&expected_sig).unwrap(); + } - let mut v = HashMLDSA44_with_SHA512::verify_init(&expected_pk, ctx).unwrap(); - v.verify_update(msg); - v.verify_final(&expected_sig).unwrap(); -} \ No newline at end of file + #[test] + fn test_boundary_conditions() { + let msg = b"The quick brown fox jumped over the lazy dog"; + + // ctx too long + // this is common to all parameter sets, so I'll just test MLDSA44 + let (_pk, sk) = HashMLDSA44_with_SHA256::keygen().unwrap(); + + // ctx with len 255 works + HashMLDSA44_with_SHA256::sign_init(&sk, Some(&[1u8; 255])).unwrap(); + + // ctx with len 256 is too long + let too_long_ctx = [1u8; 256]; + match HashMLDSA44_with_SHA256::sign_init(&sk, Some(&too_long_ctx)) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for ctx too long"), + } + + // test various things that are shorter / longer than required + + // sig too long / too short + + // MLDSA44 + let (pk, sk) = HashMLDSA44_with_SHA256::keygen().unwrap(); + let sig = HashMLDSA44_with_SHA256::sign(&sk, msg, None).unwrap(); + // too short + match HashMLDSA44_with_SHA256::verify(&pk, msg, None, &sig[..MLDSA44_SIG_LEN - 1]) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for sig too short"), + } + + // sig too long + let mut sig_too_long = [0u8; MLDSA44_SIG_LEN + 2]; + sig_too_long[..MLDSA44_SIG_LEN].copy_from_slice(&sig); + sig_too_long[MLDSA44_SIG_LEN..].copy_from_slice(&[1u8, 0u8]); + match HashMLDSA44_with_SHA256::verify(&pk, msg, None, &sig_too_long) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for sig too short"), + } + + // sign_ph_deterministic ctx just right at 255 + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + /*ctx*/ Some(&[1u8; 255]), + /*ph*/ &[2u8; 64], + [3u8; 32], + ) + .unwrap(); + HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[1u8; 255]), &sig).unwrap(); + + // sign_ph_deterministic ctx too long + match HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + /*ctx*/ Some(&[1u8; 256]), + /*ph*/ &[2u8; 64], + [3u8; 32], + ) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + + // sign_ph_deterministic ctx just right at 255 + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + Some(&[1u8; 255]), + &[2u8; 64], + [3u8; 32], + ) + .unwrap(); + HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[1u8; 255]), &sig).unwrap(); + + // sign_ph_deterministic ctx too long + match HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + Some(&[2u8; 256]), + &[2u8; 64], + [3u8; 32], + ) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + + // verify_ph ctx too long + match HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[2u8; 256]), &sig) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + } +} diff --git a/crypto/mldsa-lowmemory/tests/mldsa_key_tests.rs b/crypto/mldsa-lowmemory/tests/mldsa_key_tests.rs new file mode 100644 index 0000000..f80978c --- /dev/null +++ b/crypto/mldsa-lowmemory/tests/mldsa_key_tests.rs @@ -0,0 +1,277 @@ +#[cfg(test)] +mod mldsa_key_tests { + #![allow(dead_code)] + #![allow(unused_imports)] + + use bouncycastle_core::errors::SignatureError; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{SecurityStrength, SignaturePrivateKey, SignaturePublicKey}; + use bouncycastle_core_test_framework::signature::TestFrameworkSignatureKeys; + use bouncycastle_hex as hex; + use bouncycastle_mldsa_lowmemory::mldsa::{MLDSA_SEED_LEN, MLDSA44_FULL_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{ + MLDSA44, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, MLDSA65PrivateKey, MLDSA65PublicKey, + MLDSA87, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait, + MLDSATrait, + }; + use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; + + #[test] + fn core_framework_tests() { + let tf = TestFrameworkSignatureKeys::new(); + + tf.test_keys::( + MLDSA44::keygen, + ); + tf.test_keys::( + MLDSA65::keygen, + ); + tf.test_keys::( + MLDSA87::keygen, + ); + } + + #[test] + fn encode_decode() { + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + + let expected_sk_bytes: [u8; MLDSA44_FULL_SK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668").unwrap() + .try_into().unwrap(); + let expected_pk_bytes: [u8; MLDSA44_PK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap() + .try_into().unwrap(); + + let (pk1, sk1) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk1_bytes = pk1.encode(); + let sk1_bytes = sk1.encode(); + + let (pk2, sk2) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk2_bytes = pk2.encode(); + assert_eq!(pk1_bytes, pk2_bytes); + + let sk2_bytes = sk2.encode(); + assert_eq!(sk1_bytes, sk2_bytes); + + // encode_out + let mut pk_bytes = [0u8; MLDSA44_PK_LEN]; + let bytes_written = pk1.encode_out(&mut pk_bytes); + assert_eq!(bytes_written, MLDSA44_PK_LEN); + assert_eq!(pk_bytes, expected_pk_bytes); + + let mut sk_bytes = [0u8; MLDSA44_SK_LEN]; + let bytes_written = sk1.encode_out(&mut sk_bytes); + assert_eq!(bytes_written, MLDSA_SEED_LEN); + assert_eq!(sk_bytes.as_slice(), seed.ref_to_bytes()); + + // encode_full_sk + assert_eq!(sk1.encode_full_sk(), expected_sk_bytes); + + // encode_full_sk_out + let mut sk_bytes = [0u8; MLDSA44_FULL_SK_LEN]; + let bytes_written = sk1.encode_full_sk_out(&mut sk_bytes); + assert_eq!(bytes_written, MLDSA44_FULL_SK_LEN); + assert_eq!(sk_bytes, expected_sk_bytes); + } + + #[test] + fn seed() { + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + + // todo change mlkem to also hold the whole keymaterial? + + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + assert_eq!(sk.seed(), Some(&seed)); + + // it'll reject a keyen with a seed too weak, and preserve the seed otherwise + let mut seed128 = seed.clone(); + key_material::do_hazardous_operations(&mut seed128, |seed| { + seed.set_security_strength(SecurityStrength::_128bit) + }) + .unwrap(); + + let mut seed192 = seed.clone(); + key_material::do_hazardous_operations(&mut seed192, |seed| { + seed.set_security_strength(SecurityStrength::_192bit) + }) + .unwrap(); + + let mut seed256 = seed.clone(); + key_material::do_hazardous_operations(&mut seed256, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + // MLDSA44 + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed128).unwrap(); + assert_eq!(sk.seed(), Some(&seed128)); + + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed192).unwrap(); + assert_eq!(sk.seed(), Some(&seed192)); + + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); + + // MLDSA65 + match MLDSA65::keygen_from_seed(&seed128) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + + let (_pk, sk) = MLDSA65::keygen_from_seed(&seed192).unwrap(); + assert_eq!(sk.seed(), Some(&seed192)); + + let (_pk, sk) = MLDSA65::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); + + // MLDSA87 + match MLDSA87::keygen_from_seed(&seed128) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + match MLDSA87::keygen_from_seed(&seed192) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + let (_pk, sk) = MLDSA87::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); + } + + #[test] + fn test_eq() { + // MLDSA-44 + + let (pk, sk) = MLDSA44::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA44PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA44PrivateKey::from_bytes(&sk.encode()).unwrap()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA44PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA44PrivateKey::from_bytes(&bytes).unwrap()); + + // MLDSA-65 + + let (pk, sk) = MLDSA65::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA65PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA65PrivateKey::from_bytes(&sk.encode()).unwrap()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA65PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA65PrivateKey::from_bytes(&bytes).unwrap()); + + // MLDSA-87 + + let (pk, sk) = MLDSA87::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA87PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA87PrivateKey::from_bytes(&sk.encode()).unwrap()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA87PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA87PrivateKey::from_bytes(&bytes).unwrap()); + } + + /// Tests that no private data is displayed + #[test] + fn test_display() { + let (pk44, sk44) = MLDSA44::keygen().unwrap(); + let (pk65, sk65) = MLDSA65::keygen().unwrap(); + let (pk87, sk87) = MLDSA87::keygen().unwrap(); + + /*** MLDSAPublicKey ***/ + // fmt + + let pk_str = format!("{}", pk44); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-44, pub_key_hash (tr):")); + + let pk_str = format!("{}", pk65); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-65, pub_key_hash (tr):")); + + let pk_str = format!("{}", pk87); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-87, pub_key_hash (tr):")); + + // debug + let pk_str = format!("{:?}", pk44); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-44, pub_key_hash (tr):")); + + let pk_str = format!("{:?}", pk65); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-65, pub_key_hash (tr):")); + + let pk_str = format!("{:?}", pk87); + assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-87, pub_key_hash (tr):")); + + /*** MLDSAPrivateKey ***/ + // fmt + let sk_str = format!("{}", sk44); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-44, pub_key_hash (tr):")); + + let sk_str = format!("{}", sk65); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-65, pub_key_hash (tr):")); + + let sk_str = format!("{}", sk87); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-87, pub_key_hash (tr):")); + + // debug + let sk_str = format!("{:?}", sk44); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-44, pub_key_hash (tr):")); + + let sk_str = format!("{:?}", sk65); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-65, pub_key_hash (tr):")); + + let sk_str = format!("{:?}", sk87); + assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-87, pub_key_hash (tr):")); + } +} diff --git a/crypto/mldsa_lowmemory/tests/mldsa_tests.rs b/crypto/mldsa-lowmemory/tests/mldsa_tests.rs similarity index 65% rename from crypto/mldsa_lowmemory/tests/mldsa_tests.rs rename to crypto/mldsa-lowmemory/tests/mldsa_tests.rs index 78d3b43..c195e63 100644 --- a/crypto/mldsa_lowmemory/tests/mldsa_tests.rs +++ b/crypto/mldsa-lowmemory/tests/mldsa_tests.rs @@ -1,26 +1,119 @@ /// This performs tests using the public interfaces of the crate. #[cfg(test)] mod mldsa_tests { - use bouncycastle_core::errors::SignatureError; - use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait}; - use bouncycastle_core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey, RNG}; + use crate::{MLDSA44_KAT1, MLDSA65_KAT1, MLDSA87_KAT1}; + use bouncycastle_core::errors::{RNGError, SignatureError, SuspendableError}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{ + RNG, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, Signer, + Suspendable, + }; use bouncycastle_core_test_framework::DUMMY_SEED_1024; + use bouncycastle_core_test_framework::FixedSeedRNG; use bouncycastle_core_test_framework::signature::*; use bouncycastle_hex as hex; - use bouncycastle_mldsa_lowmemory::{MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MuBuilder, Polynomial, MLDSA44, MLDSA65, MLDSA87, MLDSA_TR_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSAPublicKeyTrait, MLDSAPrivateKeyTrait}; - use crate::{MLDSA44_KAT1, MLDSA65_KAT1, MLDSA87_KAT1}; + use bouncycastle_mldsa_lowmemory::{ + MLDSA_TR_LEN, MLDSA44, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, MLDSA65PrivateKey, + MLDSA65PublicKey, MLDSA87, MLDSA87PrivateKey, MLDSA87PublicKey, MuBuilder, + }; + use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; + use bouncycastle_mldsa_lowmemory::{MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait, MLDSATrait}; + + #[test] + fn keygen_from_rng_tests() { + /* keygen from seed must match keygen from rng, for a fixed-seed rng */ + // Same arbitrary fixed seed as rfc9881_keygen. + let seed_bytes: [u8; 32] = + hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap() + .try_into() + .unwrap(); + + // The seed as fed directly to keygen_from_seed. + let seed = KeyMaterial256::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); + + // ML-DSA-44 + let (pk_seed, sk_seed) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA44::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-44 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-44 sk from RNG must match sk from seed"); + + // ML-DSA-65 + let (pk_seed, sk_seed) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA65::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-65 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-65 sk from RNG must match sk from seed"); + + // ML-DSA-87 + let (pk_seed, sk_seed) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA87::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-87 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-87 sk from RNG must match sk from seed"); + + /* Test that keygen rejects a seed from a weak RNG */ + + // MLDSA44 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_112bit); + match MLDSA44::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA44 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_128bit); + _ = MLDSA44::keygen_from_rng(&mut rng).unwrap(); + + // MLDSA65 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_128bit); + match MLDSA65::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA65 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_192bit); + _ = MLDSA65::keygen_from_rng(&mut rng).unwrap(); + + // MLDSA87 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_192bit); + match MLDSA87::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA87 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_256bit); + _ = MLDSA87::keygen_from_rng(&mut rng).unwrap(); + } #[test] fn test_framework_signature() { let tf = TestFrameworkSignature::new(false, true); - tf.test_signature::(false); - tf.test_signature::(false); - tf.test_signature::(false); + tf.test_signature::(MLDSA44::keygen, false); + tf.test_signature::(MLDSA65::keygen, false); + tf.test_signature::(MLDSA87::keygen, false); } /// This runs the full bitflipping tests and takes several minutes. @@ -42,10 +135,11 @@ mod mldsa_tests { fn rfc9881_keygen() { // note: same seed for MLDSA44, MLDSA65, MLDSA87 let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), KeyType::Seed, - ).unwrap(); - + ) + .unwrap(); /* MLDSA44 */ let expected_pk_bytes: [u8; MLDSA44_PK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap() @@ -60,7 +154,11 @@ mod mldsa_tests { // run keygen from seed let (derived_pk, derived_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); let sk_bytes = derived_sk.encode(); - assert_eq!(&sk_bytes, &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()); + assert_eq!( + &sk_bytes, + &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap() + ); assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // also test the `impl Eq` @@ -74,15 +172,13 @@ mod mldsa_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLDSA44::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(SignatureError::KeyGenError(_)) => {/* good */ }, + Err(SignatureError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } // check that it outputs to the full encoding correctly assert_eq!(derived_sk.encode_full_sk(), &*hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668").unwrap()); - - /* MLDSA65 */ let expected_pk_bytes: [u8; MLDSA65_PK_LEN] = hex::decode("48683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d05b38dbb2edf01e664efd81be1ea893688ce68aa2d51c5958f8bbc6eb4e89ee67d2c0320954d57212cac7229ff1d6eaf03928bd51511f8d88d847736c7de2730d5978e5410713160978867711bf5539a0bfc4c350c2be572baf0ee2e2fb16ccfea08028d99ac49aebb75937ddce111cdab62fff3cea8ba2233d1e56fbc5c5a1e726de63fadd2af016b119177fa3d971a2d9277173fce55b67745af0b7c21d597dbeb93e6a32f341c49a5a8be9e825088d1f2aa45155d6c8ae15367e4eb003b8fdf7851071949739f9fff09023eaf45104d2a84a45906eed4671a44dc28d27987bb55df69e9e8561f61a80a72699503865fed9b7ee72a8e17a19c408144f4b29afef7031c3a6d8571610b42c9f421245a88f197e16812b031159b65b9687e5b3e934c5225ae98a79ba73d2b399d73510effad19e53b8450f0ba8fce1012fd98d260a74aaaa13fae249a006b1c34f5ba0b882f26378222fb36f2283c243f0ffeb5f1bb414a0a70d55e3d40a56b6cbc88ae1f03b7b2882d98deea28e145c9dedfd8eaf1cef2ed94a8b050f8964f46d1ea0d0c2a43e0dda6182adbf4f6ed175b6742257859bf22f3a417ecf1f9d89317b5e539d587af16b9e1313e04514ffa64ba8b3ff2b8321f8811cb3fb022c8f644e70a4b80a2fbfee604abb7379091ea8e6c5c74dfc0283666b40c0793870028204a136bf5da9568eb798d349038bdb0c11e03445e7847cb5069c75cf28ac601c7799d958210ddbcb226e51afef9f1de47b073873d6d3f97456bede085082e74a298b2cd48f4b3093155f366c8fa601c6af858dfa32c08491b2a29887f90335949a5d6edaa679882a3a95d6bf6d970a221f4b9d3d8cbf384af81aac95e2b3294e04789ac83727a5dc04559f96af41d8a053516feeeebc52746eb6ab2819e09108710d835f011fa63065872ad334d5cdffb2b2310507e92fc993ae317da97f4f309cdaf0f67ed99d90215576083849f953b246d7fedb3fdb67679850a5ad404e64147fb7cf4f6aeddd05afb4b834968d1fe88014960dce5d942236526e12a478d69e5fbe6970310b308c06845018cfc7b2ab430a13a6b1ac7bb02cccbb3d911ac2f11068613fbe029bfdce02cf5cd38950ed72c83944edfbc75615af87f864c051f3c55456c5412863a40c06d1dab562bdff0571b8d3c3917bbd300880bba5e998239b95fa91b7d6416d4f398b3adbcd30983ed3592b4d9ef7d4236fd00f50d98aa53a235ac4172720f77d96172672980cfe8ff7a5a702783edc2ba31b2259015a112fc7f468a9c2f9464039002d30ef678b4cb798bc116216bf7a9a7c18ba03b7b58fd07515d3115049d3614be7a07e744300750df1d2c58753389059eafc3d785ccdd31c07648bedc03a5c3b8ad46d064d59c13d57374729fc4e295362e2a5191204530428bc1522afa28ff5fe1655e304ca5bc8c27ad0e0c6a39dd4df28956c14b38cc93682cefe402bbd5e82d29c464e44eb5d37b48fc568dfe0cc6e8e16baea05e5135590f19294e73e8367b0216dbb815030b9de55913f08039c42351c59e5515dd5af8e089a15e625e8f6dee639386c46497d7a263288774de581a7de9629b41b4424141f978fb8331208efdec3c6e0de39bc57063f3dcd6c470373c08891ea29cbc7cc6d6483b8889083ace86aa7b51b1c2cfe6e2ad18d97ce36fbc56ea42fae97e6a7ac114864478c366df1ebb1e7b11a9098504fd5975bdf1f49dc70002b63c1739a9d263fbad4073f6a9f6c2b8af4b4c332a103a0cffa5deeb2d062ca3c215fd360026be7c5164f4a4424ef74948804d66f46487732c8202c795478647b4ea71d627c086024cca354a41f0877b38f19b3774ad2095c8da53b069e21c76ae2d2007e16719ed40080d334f7da52e9f5a5990439caf083a95b833f02ad10a08c1a6d0f260c007285bd4a2f47703a5aef465287d253b18ac22514316210ff566814b10f87a293d6f199d3c3959990d0c1268b4f50d5f9fcefbbf237bd0c28b80182d6659741f14f10bfbb21bba12ab620aa2396f56c0686b4ea9017990224216b2fe8ad76c4a9148eef9a86a3635a6aa77bc1dcfb6fba59a77dfda9b7530dc0ca8648c8d973738e01bab8f08b4905e84aa4641bd602410cd97520265f2f231f2b35e15eb2fa04d2bd94d5a77abaf1e0e161010a990087f5b46ea988b2bc0512fda0fa923dadd6c45c5301d09483673265b5ab2e10f4ba520f6bbad564a5c3d5e27bdb080f7d20e13296a3181954c39c649c943ebe17df5c1f7aae0a8fe126c477585a5d4d648a0d008b6af5e8cd31be69a9296d4f3fd25ed86f221e4b93f65f5929967533624b9235750c30707550b58536d109a7131c5a5bbe4a5715567c12534aec7660761eebb9fae2891c774589b80e566ad557ddef7367196b7227ea9870ef09ddfec79d6b9319a6879b5205d76bf7aba5acf33afb59d17fc54e68383d6be5a08e9b66da53dcde008bb294b8582bd132cdcc49959fdbc21e52721880c8ad0352c79f03a43bbd84c4cdfdc6c529005e1e7cd9a349a7168a35569ba5dea818968d5a91466bd6e64e20bf62417198afc4e81c28dd77ed4028232398b52fbde86bc84f475b9016710ce2aabc11a06b4dbac901ec16cf365ca3f2d53813948a693a0f93e79c46ca5d5a6dca3d28ca50ad18bd13fca55059dd9b185f79f9c47196a4e81b2104bc460a051e02f2e8444f").unwrap() .try_into().unwrap(); @@ -93,12 +189,14 @@ mod mldsa_tests { assert_eq!(pk_bytes.len(), expected_pk_bytes.len()); assert_eq!(pk_bytes, expected_pk_bytes.as_slice()); - // run keygen from seed let (derived_pk, derived_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); let sk_bytes = derived_sk.encode(); - assert_eq!(&sk_bytes, &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()); - + assert_eq!( + &sk_bytes, + &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap() + ); assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // also test the `impl Eq` @@ -112,15 +210,13 @@ mod mldsa_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLDSA65::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(SignatureError::KeyGenError(_)) => {/* good */ }, + Err(SignatureError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } // check that it outputs to the full encoding correctly assert_eq!(derived_sk.encode_full_sk(), &*hex::decode("48683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d853fa69b8199023e8cd678dd9fabf9047646ffd0cb3cc7f795805a71e70d2371b0563e3cd3346149c8c9ebcf23b0a4e5a900eea9c6562790a7c63e38663daa2dddb6e480dc405a1e701948b74841ef5cc1c3f2bf327972e9510510cd5375ecc0855717711872221862381000424778061475007501717035504515125471838046175722244108868608646012747567180870666864332444122043638667502823634244322057364106455547722755681433614625508206437685468754353751068718333805475052580752818843811087260202008588301836113828212061711578768788878643754601657155084718866072732880664741856762180318276641578245025646643113504364780126673143011660655864718368863503847861101202356116137860785321240075478823043666116604255418285605367785638434430632610770731784272141116530385276867460150823735320766107504681248066603032652312445408800318088767217307182472151278011654474866172233380866064468352158420368011802118183317735453488100448653674370577258833460384232856810060426042584560235682051838638432421224245645858677145728504788717180618836086864156508116502646700608266227383172407257300727288620667588682607064020330343663155464245345667187345658370225084685628807036708462371710065717584778708655537822351446772856730322870014332061715845526632502651334777380355164313473510662751757402468881706743468186017652453330872104343401032287635155265081307745444168154183636411204026873043677712808846355453006245810458365124842780345166635843785601465115742321436685224777313450178362420550006484471234408800604735405783336308210615225207248851348637067622588571265673476816464684258708122705500838320023208066345336003346857247063554003577122752307142536874374570056643224482852072183330205337334077278055253063525040673346131807280717248377634573185851602333443625164338160858773462428830070365853755007552315037021324630437086806361503030043586357080211066473463522620330438021085287578321078867480856347436734284058466841437005510873426447721127384736526472577144704178644260247118740812216605847178137067680817058185585471363421075580163583585184403847110338742628247741365544270734635777500662562684202124683864616646031225388845400845734464754472560546166846630880638271563287183840652247681160662130330186802801384630505657238758365723230688046122606651675570532413227673517080153001628460134887701118815571315464311704732882856368234555041862765631111687505104254414427852211171788153685157447166255365583630250285576875327137103723705714761713651841242366444664143520521085157033363860258426628148110546268173038756433216588568663632813406254012040886547886171657623726234867030115115632050753502122108426531435567111525720106853630150557586058784314313278788087384788637881813873426178388524667733506021151464238232680135440783475385535752832335187601152134325773333655188615816168241842212230841448151201103024777242544366067717707603014525403500183873237735265086357113734481605277456553730085837785035121115480628850180268138652053468013207241803213005723864076427114101838525510632607104865176833828572762354518735083132886376661426311675033112553764176031433177212234418a82e4f5c9ea0faf99eb04d78a7332711117c33f18eca21f8743376ada5219804a7ed9a5557fcd67a3550b3a4b8c588629c021475fa3d56d5d6cfbb1a09bda8d14de622ddff16d8bc99b14278a8af1d76bed157672dd9c32316f97e8daadef8d9da69586725567fb96b59990d4bf0bc9c195b90b74295f5675b24257c2710c175b0153f2911328c2eb7abb9ad46e70a8b53c39ea642cee4b3cb42620e863ce8b650ce8adcd923721a1687023c673a8cbb6b03d51cd197e8c346ebadce93950f88cee201db9e320843e29f300d9a19500d70a4caf272c69e4eef69fbb8a55efd7ca2bed990d2d3b582848f9c45c2abc54cfc47d34f06c0ffa56fcd762ab9cba9146d7725218963b240d72b6d22c93171fbd47788b76e72042def0878d23df631a1a1e5a6027686de5b4a10e91069c8f2ba0259b04d6409da96567ca52da497026e583a0ecefc1f01e6b988e21f9767a2b7e1672deb9a1e2a3fcc863aa91517c334620601b4fe79730e934935f4b6fbc4e32695145c2b5f6a127fecc0a277451ebc3fd523444f9ee7c9c34534f356db544fc31c1bfde5f65c77ea2f7c2eae4c55ebaf104271c566fd4ebac71c7a62c74952817ae675504d9599b1b762b6aca168a83248c9d9adb0ceb1556e5759490bbc0c7900795ad72123038b662f64f106a9993681a25d59af7bc97a235be9284c5bc45a6c90cb1c2999c663d96b478e2307f85548957d65740e2673e9ebd1352829038f462b8fd3b5681da55c0252523853525ea0ad647e71ac2c5a8893e603ac97e56c04ceb2f26f5c5b4b6d94ab811380fd00f2208fe86535086aebfd35c29120624c04fbb6113929d9c556350253766c209fdba83c95fccd342a28099355d00bc863f4eef596eb0b42ebcc7c79491cceae205ea0b8059fbb8a5726c5949d2b15e7e29c51fc9b02ee1a4fc357b5f1bef9c4add46a2a920c2fbf08a37eb1514bfa15110a4392a74c6f13c50c5cffd97531098d7cd23b60eb35c4a428b46c55386e1010c4ba7f70e4c7ecb7575f3063a71e84dfdcf09a58b2cdb0f99f27ed378610d25cbad7bfa6ba0d59189cfe88eab9b46d7e6db0307eabe4198e99bd71f779ab66581e0912fc7b1d2585245e9a12687a975cd5e8e1dcc045d5f891c4c685db07cf81e77389b363eb6bdfe39b27ff84c97eefee162e3b451fe6914719cb6436d855960ff915d7cea6adeafdfc1c05786c49f923a474ffdfc3153a06e6ed0b0ad220d72524434d5273c0aab6dde4e91476d581a2695a60de6d9f44d77aa08266e938eeb4a9597c9b64986059e49262a4eab2454e14015ad0536c42733a5d77d7995c2a20446009ebfe5632c80c08ed2b97af35066489f597eb1b1f11f04f60e0c9040159c44ab3e60e0a15229d191228bed17bbc3ac939b3c67cee135f352c27216c9c31f72a3e87040c5f619306eb0b6cca2a9ce7b22a1694d00ca9c05e315126457f26ce84f9617241860782f864b473d84017491902b1bdc8cdc5800dd46127fb80a71c095b473a562529b3b1e7e437e158a5f6666e9974d005b062c2309e6dce98f9b658c6e3f9a216d58c8c9142bd1c8c85a9da872ebbfad3fea9d9aba2b68c0e8f19c6ff5f00584d45daf9d6c9d69ed04b8da8d687258b77807927612c530446fea7697ae3f926698929bc6a5a8cf3e2024c0f0c5ee57b5869bf981881caf9e3665fc7f7efc678929f87a56eaa42ea4d1ff6691822dd79a47096b776d1d8f01456e5873b0738406c382c573ae9cde2d9e7f231b6cc5c676e7cf43963373013a58075381ff0949be084546d72e4f8a3e5fe4aa5091add234e2afe0030b1b663ae9d2d32410986b9402aaaf2465b74a5e2d0bc38e3a92bbddd8a1fed7b948c23cce6f8c08fe356835ba65b0f984068616ef48138efd89bf357a54d2ebbf376cbdcc69c5f1f61c64d2794bc06ccb9abdf66e25085d8c830e2ae3b0fe0f07a7af8b9320bf342970997d67d7c12593a8fbfade635aac53083a7022c47d5f77a52b57b598da9392ae6d86afc46fc06455181b9c75a646dc21f81e4bf213753de737fd2a140027920add35a223f9f5f4465ceb60c03ed0455a333a5cc83adbf43f1f42c2ccb8328c21c7ab7faed2b21cfade2da55223aaab2af9b41c7332341746341b39aa2f43815650f5480511424cfa6901779c4d18b638cc0287aaaf31680338d20b17c7449fdc6a278a8d96a82ee4c4eca40125e2d65290071c7aef1be6a991598fb9d59512523bcd4b38c566b8e80a73ae333e134414327ef1d83c47c49dfe7936df1338a5e247787868fc84fdcb95ac89c185c4bb5fd57b2338ac42b41c10a823df39624f36b15a2f067584e06ca2e08ccaff1618fe01dd06df3512e0b724dec8506da24215acacc2c51b82ad8d302002fb41068b1da4f8bb147987b3516bad5dbddf01318fd3fa9bc43702ac498c719d95f2e841b622a5e4848a3c5c262959992ea7a7d72ca8a368028f497dfad93355cbb1bb9786d14ff2cf590317848f95856427110dda36f5192a816ce9c8816cc7bbfc804efc40085a3850b89f1e7fe5656dba410f906a97c32336c1ae7e81737a83e087354e428da8538d948dbf5dfacb59dd2b5fd3bc803f4ba432c9a739df2cfa9ed9484320f97edff1a48c6b86b3002cfb772dd5e562bc4c3d683ed964b6199fa0514b0790d958095b7b85c6be875fbb559e1930146ccea63a388a194fe09c3dea03be52de27e901017afe809af630a7382bf5c4cd4d1b8f41579fb4348ede4ca05f4cd3f139a31b2544e516dbe4086b9bb4b2bed47e2d230982dd5192429d377b7c0745cc068e2f5a4aa04c7ff87209ed1259976a0fc9b25e9e851d4e3502c02c85d6dff029e211d01ebf0e9e7188d568f8437d813b0f122f2fb17603b693ed9c38f17cfd50b815e6d9dfc0ed2ccf19f6399274a1420f235a59d8bf724345e14e45d9e4be8934dfc3fa92678db61d7118bf53cb8a2225b335f7eae50e3f941237628db76d8ea38f77a72af3a26c81fe43523b335535a5d1db7c38f341082bb5734d089e8ae309cfda3a0bcb5cd5b097113c8edf9616aa4f6e6631b9125276fb3f680a34341c3db668dc6cad45fc93b2708ca2af75ccce734fd191c50089dad53982fddae02531ff93e1f21ff395fc0a12874edf06b6f9647e95a7324586c71dfd91d901d621858190fecd00ccd110bbac59f96cb884c3c93994748a56f41283bfc41fb89052153a894588c3cb9017f3d66326c985637e575acb812346342654025d602de3ba940c19ac1a633dffda977b529b8013e19c1d6d0680f4dae62c924450ae66aab82f21473061dab3d62b247f907e3551939ad3f5465e9d08a82bfea17eea1b6b2b923757477f993000b2f43b70f28aaab1fe9a26ad1fd3361616c0b0e242fe76604b7033a1f30e97e28f526ca3c880fe2b8d9d1b0c9ff188b31cb9d97425acab9b216d98a6ae355e583da71e8864ee3d16b0759796190ef545c1e62bfef92af6ca147b13244d6c892fc8ef223ab3f43f924c2f466097ee8").unwrap()); - - /* MLDSA87 */ let expected_pk_bytes: [u8; MLDSA87_PK_LEN] = hex::decode("9792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124a73b323b9ba21ab64d767c433f5a521effe18f86e46a188952c4467e048b729e7fc4d115e7e48da1896d5fe119b10dcddef62cb307954074b42336e52836de61da941f8d37ea68ac8106fabe19070679af6008537120f70793b8ea9cc0e6e7b7b4c9a5c7421c60f24451ba1e933db1a2ee16c79559f21b3d1b8305850aa42afbb13f1f4d5b9f4835f9d87dfceb162d0ef4a7fdc4cba1743cd1c87bb4967da16cc8764b6569df8ee5bdcbffe9a4e05748e6fdf225af9e4eeb7773b62e8f85f9b56b548945551844fbd89806a4ac369bed2d256100f688a6ad5e0a709826dc4449e91e23c5506e642361ef5a313712f79bc4b3186861ca85a4bab17e7f943d1b8a333aa3ae7ce16b440d6018f9e04daf5725c7f1a93fad1a5a27b67895bd249aa91685de20af32c8b7e268c7f96877d0c85001135a4f0a8f1b8264fa6ebe5a349d8aecad1a16299ccf2fd9c7b85bace2ced3aa1276ba61ee78ed7e5ca5b67cdd458a9354030e6abbbabf56a0a2316fec9dba83b51d42fd3167f1e0f90855d5c66509b210265dc1e54ec44b43ba7cf9aef118b44d80912ce75166a6651e116cebe49229a7062c09931f71abd2293f76f7efc3215ba97800037e58e470bdbbb43c1b0439eaf79c54d93b44aac9efe9fbe151874cfb2a64cbee28cc4c0fe7775e5d870f1c02e5b2e3c5004c995f24c9b779cb753a277d0e71fd425eb6bc2ca56ce129db51f70740f31e63976b50c7312e9797d78c5b1ac24a5fa347cc916e0a83f5c3b675cd30b81e3fa10b93444e07397571cce98b28da51db9056bc728c5b0b1181e2fbd387b4c79ab1a5fefece37167af772ddad14eb4c3982da5a59d0e9eb173ec6315091170027a3ab5ef6aa129cb8585727b9358a28501d713a72f3f1db31714286f9b6408013af06045d75592fc0b7dd47c73ed9c75b11e9d7c69f7cadfc3280a9062c5273c43be1c34f87448864cea7b5c97d6d32f59bd5f25384653bb5c4faa45bea8b89402843e645b6b9269e2bd988ddacb033328ffb060450f7df080053e6969b251e875ecec32cfc592840d69ab69a75e06b379c535d95266b082f4f09c93162b33b0d9f7307a4eaaa52104437fed66f8ee3eabbd45d67b25a8133f496468b52baffdbfad93eef1a9818b5e42ec722788a3d8d3529fc777d2ba570801dfae01ec88302837c1fb9e0355727645ee1046c3f915f6ae82dad4fb6b0356a46518ffc834155c3b4fe6dafa6cc8a5ccf53c73a0849d8d44f7dcf72754e70e1b7dfb447bb4ef49d1a718f6171bbce200950e0ce926106b151a3e871d5ce49731bd6650a9b0ca972da1c5f136d44820ea6383c08f3b384cf2338e789c513f618cc5694a6f0cee104511e1ed7c5f23a1ebfd8a0db8424553240156dbf622831b0c643d1c551b6f3f7a98d29b85c2de05a65fa615eee16495bd90737672115b53e91c5d90028cf3f1a93953a153de53b44084e9ccff6b736693926daefebb2d77aa5ad689b92f31686669df16d1715cc58f7a2cfb72dd1a51e92f825993a74022be7e9eb6054654457094d14928f20215e7b222ac56b51adbec8d8bdb6983979a7e3a21b44b5d1518ca97d0b5195f51ed6a24350c89747e1edea51b448e3e9147054ce927873c90db394d86888e07dff177593d6f79e152302204aeb03be2386af3e24078bd028b1689f5e147c9f452c8ceb02ec59cc9db63a03576ceeafe98239023897da0236630a53c0de7f435a19869792fab36e7b9e635760f09069e6432e700035ac2a02879fff0a1e1bec522047193d94eb5df1efd53eea1144ca78940852f5ec9727904b366ede4f5e2d331fad5fc282ea2c47e923142771c3dd75a87357487def99e5f18e9d9ed623c175d02888c51f82c07a80d54716b3c3c2bdbe2e9f0a9bbaaebeb4d52936876406f5c00e8e4bbd0a5ec05797e6207c5ab6c88f1a688421bd05a114f4d7de2ac241fa0e8bedff47f762ddcbeaa91004f8d31e85095c81054994ad3826e344ba96040810fc0b2ad1de48cfade002c62e5a49a0731ab38344bc1636df16bf607d56855e56d684003c718e4bad9e5a099979fcddeeb1c4a7776cd37a3417cb0e184e29ef9bc0e87475ba663be09e00ab562eb7c0f7165f969a9b42414198ccf1bff2a2c8d689a414ece7662927665689e94db961ebaec5615cbc1a7895c6851ac961432ff1118d4607d32ef9dc732d51333be4b4d0e30ddea784eca8be47e741be9c19631dc470a52ef4dc13a4f3633fd434d787c170977b417df598e1d0dde506bb71d6f0bc17ec70e3b03cdc1965cb36993f633b0472e50d0923ac6c66fdf1d3e6459cc121f0f5f94d09e9dbcf5d690e23233838a0bacb7c638d1b2650a4308cd171b6855126d1da672a6ed85a8d78c286fb56f4ab3d21497528045c63262c8a42af2f9802c53b7bb8be28e78fe0b5ce45fbb7a1af1a3b28a8d94b7890e3c882e39bc98e9f0ad76025bf0dd2f00298e7141a226b3d7cee414f604d1e0ba54d11d5fe58bccea6ad77ad2e8c1caacf32459014b7b91001b1efa8ad172a523fb8e365b577121bf9fd88a2c60c21e821d7b6acb47a5a995e40caced5c223b8fe6de5e18e9d2e5893aefebb7aae7ff1a146260e2f110e939528213a0025a38ec79aabc861b25ebc509a4674c132aaacb7e0146f14efd11cfcaf4caa4f775a716ce325e0a435a4d349d720bcf137450afc45046fc1a1f83a9d329777a7084e4aadae7122ce97005930528eb3c7f7f1129b372887a371155a3ba201a25cbf1dcb64e7cdee092c3141fb5550fe3d0dd82e870e578b2b46500818113b8f6569773c677385b69a42b77dcba7acffd95fd4452e23aaa1d37e1da2151ea658d40a3596b27ac9f8129dc6cf0643772624b59f4f461230df471ca26087c3942d5c6687df6082835935a3f87cb762b0c3b1d0dda4a6533965bef1b7b8292e254c014d090fed857c44c1839c694c0a64e3fad90a11f534722b6ee1574f2e149d55d744de4887024e08511431c062750e16c74ab9f3242f2db3ffb12a8d6107faa229d6f6373b07f36d3932b3bdb04c19dd64eadd7f93c3c564c358a1c81dcf1c9c31e5b06568f97544c17dc15698c5cb38983a9afc42783faa773a52c9d8260690be9e3156aa5bc1509dea3f69587695cd6ff172ba83e6a6d8a7d6bbebbbcda3672731983f89bc5831dc37c3f3c5c56facc697f3cb20bd5dbadbd702e54844ac2f626901fe159db93dfd4773d8fe73562b846c1fc856d1802762840ebc72d7988bde75cbca70d319d32ce0cc0253bb2ad455723ee0c7f4736ce6e6665c5aca32a481c53839bc259167b013d0423395eeb9aaaee3206149a7d550d67fc5fdfe4a8a5c35d2510b664379ab8f72855a2af47abce2a632048eaf89e5cb4a88debc53a595103acce4f1cff18acff07afe1eb5716aa1e40b63134c3a3ae9579fa87f515be093c2d29db6d6b65c93661e00636b592704d093cc6716c2342eb1853d48c85c63ac8a2854462c7b77e7e3bd1eac5bca28ffaa00b5d349f8a547ad875b96a8c2b2910c9301309a3f9138a5693111f55b3c009ca947c39dfc82d98eb1caa4a9cbe885f786fa86e55be062222f8ba90a974073326b31212aece0a34a60").unwrap() .try_into().unwrap(); @@ -131,12 +227,14 @@ mod mldsa_tests { assert_eq!(pk_bytes.len(), expected_pk_bytes.len()); assert_eq!(pk_bytes, expected_pk_bytes.as_slice()); - // run keygen from seed let (derived_pk, derived_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); let sk_bytes = derived_sk.encode(); - assert_eq!(&sk_bytes, &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap()); - + assert_eq!( + &sk_bytes, + &*hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap() + ); assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // also test the `impl Eq` @@ -150,27 +248,28 @@ mod mldsa_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLDSA87::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(SignatureError::KeyGenError(_)) => {/* good */ }, + Err(SignatureError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } // check that it outputs to the full encoding correctly assert_eq!(derived_sk.encode_full_sk(), &*hex::decode("9792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124d8e9ee4e90a16c602f5ec9bc38517dc30e329d5ab27673bd85f4c9b0300f776389886750b57c24db3fc012e61ede59753337374fa7124991549af243496d0637cb3be05a5948235bf79875f896d8fe0cab30c84948db4d6315aaaf160ac6243664220148161109112c94028922452c62b84500452a08967090126e149370d446108444515896910ca92982b241c90871c428680496894840859b226d1c28645912419cb891840489449005cb3462a086904026922099291305695c3468a4328e19269259461009a44923424d1236615810650128901a334c998631d3a249098225431428c0388103154d5b2886088748233152942225c3c04da49821984020d14286cb40705bb0719c962cc112065346090c450214466e91b42154b08ce446429a208c0121251341055a402213c90ca0184052c230cb342c4bc8681ba4604984846330294aa0695b8004d2380a14264ce2b2448ba211244649c414520b427103b210922880012488e308110a052819c481002022dc446842122244002ac9266a0c8731e0c04499148418360d11374222188c63b2910c9808a1a0010892440413245c987182847184325251b0319c422e1aa82802089101c0890bc7058a246522c2644c88915c826813a56450208621040291944448223022394a02988409a28819192944488c22950ca104720487701221104206841b498589064ad33608dbc04058b6510ca7098c24619990648cc2905b249010a34903256143328a119844c8b22004384110472c19c6441c252c048830d946699b20001b46825aa4805ba0491890250026800bc2315a407254c620c1b03124b14d10952814000aa0c84d54a28823988160900402162c13214091868d08c291911426d0b40c09c66051442e04112600291193c20863a431220028c114080c402c4114069c20725422068b084da148691030411c284944188ecc9648d942501b06490c458823040d20302e23852c14073040b6854cc02044862019006c540248cc886c59063249148404c750134928e40609d3c610c8284c23394452a464cca8494438320a898400342c22858d1031090932651c898c40402921850009a16d84c064e2022d48044012098ee0422e93440812106a01840592308acb348ea2262e5c86110b3508181000023426242389d1840024466013b249242846180271a03890891444d3962da31840232721c0185043c80441428d5c264144a26d48120e4032250b14820a482ecb828803a3601b25268cb82024b08598042108a72c833864543289010401234984029569d1a44d13a40c91460d6194809038455cc65001172053c6289b1810411268901221c01084421692538229812649d8a4059a2624240329e04026d20248112468119989981485c9200d50128c1c0810021000009528c1289059b485d314650a406e11296518c24c213465d830691030521931668c188ac8c0084c9830a3a62041162218052e22252a64b8250ab30163208409800919280e021101a39411e3986c58202109411060362208067112c2855aa085c0c6845c3806cbb669148484532282a1a640cc8600c42622a0a808983472d4204143c4904816681b16521a370250204248488a201141e2006cc0c20c14064911314d19060a8946091b816544c800820670001672cc24508a42899c969064287092b268982662619440c11689d842641a214e62906421c8248b286d5c4292a0c64d0c8580cc884dd4428d42348a0b0451c32686242581123506a04404c894815bb4311c08065c240803276a20c225e1809019b46da3460c4b186050c62c1b922d111504a2000421482ed81606d2108a83a22508310d093851d948490b164c2332251919024a4409d1b2210b832c23258593168544a0441b83500222724b04809b146521936018130ad9460d224561c8b440a1422d02b8090014449bb6110b978c40104a82146ada90051c028e0c1972a3b48d24305011870964c628e4189298b46c61165140460e1c3248da205188368a23b1218290281a1532e2186192048e13b690131368c984684c406d0b330081464dd2380c049681a4885002908522b004d3a471d28010ca964051a641a48428e008520b308cd2380a0c2951c38209ca2091d83692a3a628924222a216011a348637d9a659169881ec21cf4811869d1d7f139f0537e96f1184585405fd17808af1e06239d3b34e5aca8bf1369677b447ac718ac47d850c4d77b0be31dc9f508e3978f24274ab0185f727abdff59f4490371bf04610e364e64ec875ef9d20dc94077e1e166327a879b8ab516160b2a3f77437b9b3cc7d17aeaddc84db62746a35ac096f782f62a7f01aa6d6693deec90b23c66985a02307e0a1cae598a67324dba0f52f22432275e93257065c3b7e5e1cfe1dfd4d0df086df21243414a2d27e20230a829be4eb4c82c16d35f78b0e5e198332e00074bb64612fab17d4c8971cb68e5edab0369f1157b3469abd8384e2d9553f1b78e786e1ee9d0b98d39f83ccecf37d1ebd3a9d63aec766164a10171a4fd8c63daf182c421258c5f529aa55cb7ebae2e1652315e1f71e8a74131410d03247ede11d34db91f6f08aa2478fd789679c04949f71bc0171e07e3a8bb5753dbbdaa411a6350ab46eefbf86fc551c29efe4cdd7661d5cf6c3db22d0cedde599854459d97f20df7455bdf356a198d0f7eb6d34111fc940b25c0543b788edda9d26810eac3d6cc9c51327c2cf83e887d4089e19695e11add837f6f440cc360f93f32fee8a9663712c6bbd38c84ab7b54823ec363eb7e42eb59fc1fce60fbd55307b3ec85fd9daf3206d7b4b3917f1c8b7a92e3c67d89880fdf2e47f5a0c994595db170af41babf5a25b4dc1c42dd6a9db271e764de2fb015a49a850c7919be47006a336e2e325fde53ac599554d0a7de4ef45ec40c39d6baff311beee75d89e02ad31f4be4bd20ae9194f5edddaa6650776116e9f270f77714ad7a8e89acef74b7ff7d8dbec27f8020a985247e2cdacef4894a4d68ba37ca912d6be73501c995181e5b77723350b3631da3700e13fd366e131bf06b36eb6b0345093209f0a7beffae1fdd875b00687c1163c353d7d2ac90937b34e978e92f821adc9662202ece89a17e7bb65ae17d83b90dbbe6a501a4e1345bee4e5a5b53af2e5ba3d1ef3f4e05adf0b3a4cf2e530360fee64929902b571f6fd2e305652a4cb010f79f815e18f2bbb8cc89fa6fc76f77c89e293cf175a0b195800fe72d2ccdd7d75e5bd90bc6ac435d6a440ef852e9a1c8c53de03bf193365d735aaf29c5162a617e364e7f944168d0fb48fef40558f454297cc3dd508662cf23fb88e1954aa45d1c5e115bcc36f05b3e098d555220f40be2629b34507b8464c54c27b5dec78da8f22650514797af86a2512bcb7e2923379ef6d73c137006c1b38f51e37f93585e29041a3e4e3af46007ce13b8b5f7b17d5d65d7d5668e427bcbe7ec1d7c408c054a48c1ae797bf99acbc8d2607522935fd665ea7822d930f23eabff783bb23697569e204b943141e00c08810956be0525365dbab54ed48cb76964ccdf5cbd3aee7282d4a0000d2784d7b8fab16b2f7f0d5225732b1efbc4eb1cfedeb43fde79b69ecc0fbeaa1e6b40728673bd4b2e98a0d4a8f02f853950730f28d35eb12fcc79768b8e18e4bda0e58a331a2f71d7ccc2d451b32b1c65c312acf47ee513b21954c41c00c873872ee94cf14f46037425361f4bdb54821f711460cebae8c07508a9219f88fa6bedaa678eed501944a16ae6f7b5bb7a2e1e357e70d7b98461a2c71cb0fa762d6ad9824081d37f292fd4be8b84c36110dc744360201beebe0bd6c9d05e869256d2ff3f99517b7efd2a33774056cb5671675a8b492e9f5f2620eb8ef9381d3d1df19938b7b5ffaac59bc8110fa87ba8d7a3d0165f8e41dd0f804f11b9ded0f352a597835d06307a8e0c6ef4d21904339e1cf458923a3e89e025d945347366c02f3dd6368d4e47e85d3d2a9705bd57961852e5a579f93b1c514c539f49ea1163a2a493b0efcb47f4748f6a99e10bf7078282e4ace18136e2a8b3ee0a380dcd3b3ef3e65e1b8157289d62467ad488ba0392b2e90a1ededcbdc931dc17298ccef76645c7d330a05c2ce40f89b85468f357a217751e154631304ec4e04bb45b3678909c74af51ce370364d8f4f7eb1e61e00287429c9961de8322ca9a2629b1309d800e92bc1dc5055dcc797f33866eb0cfd8d490250d48ffca8022f49290e2d5376162fbaa982d16453c825b35f6515635ea92bea72367baa54de3f9eaea69542a81a4127f71cbaa257f324fefef14f08fbd65a049cd2fb362594a8e23ff1a2617db5b158f6f01cf50ab0ed95c6e709841164108b06e1b40ab0ab11c408301d3d9d8ea69e968a9600b3d17f38011ce28074e2c2e10bf6197c602d8d0ce7d3a3ef2d89623bc9f12ea338791e9266bb8ce02b124c6c7929baea693244098454a080eb7523e13bb1b7c5b6775fabababbe9075fe5687aa451397bb9cfccd051243e9bf5aef24062d335de5fce24e9ddbde1191052d80c36df9f8434872f277ed4f5a1ce8ebd3b960824a4e4f1001b04cb685f9bee4d0ddb0c571598ac2021a6606fd23345c6fbb84f0ce05fe52734521b7b07c6388d3a3b99318bf0131504aa9dfbaf548f9d32a9cd4c6893524b11330a2d3aad3ed2a58966ebb0134465d543fd7797af549f568eaebe957f64fec854674902b97558756986946ea3ab7a251cbbea11a687bd43f5d0bd89cd2caba61d5218374990ee8b92219ed25dca011c68a9757c013bd837b2dd734e3751f64fcb4b23dcd6bc57ea567f5716e17367244751e2303b22a953e772756956cdcc013ffd2c32490754422a572529d4c92f1ebb19f1dad4d036f2fdf31ca9101bdf81aea948aedcf217aa8fccd7a0771aa2753e1a823bf41c95377a2ffa61b2265138153ce86d2c87dd07a4b32d27f5f2872641431ce9a18a502aaefd9afc5b0d13cd46c357e38e69e1ee945add1992932a5b1e5c5629c9f48f7661853da00787c9d78fb925553bf07a50dd5b9d935853420e4d1a71ae62ff90ca193cdd6c2f4bed263415aaf9a35094bc2a22e2a663c7645001cd190b7bc17c75feadf8e87ce5c24b763b6584ed32e71b0268142ea3ed6898157bf923bebf0192d1bf5ee30a7d351634a60b504dde38a2e114f7ae9bf176d4a18ba2895a7bb4b47444a9ba8dbb4c124cd41bbb32f4bcb1de48c4abb510607a001b5a000bba43618b6c19e43517b45b42405928b67c713881858bad3a42511c2716ff9cd332034b672b52ff16610805cdbe7544a8a84b66e1c745a73c1b6bcda5b77b951f36c0f7a5372de9e5d1f9bbcde8843c6909002dda4875e67571af0bec581856c32c09c240e664e761e57cd0d8dc8a71cb918a5762d111285cd8b5613ddbd0ca08ac0342b2bdee38f96fa754bb2b087179c113c93986a810356eb94540b93cb9dec4aa9290ff12ec1aa2e656c9be3d590753c366c601406c061bc22033a1fd1f4e1111d039b8813b983cb506c3ea7ff3057983e8bf01682fbb00f43005313c82c1392918a6165a13338ffe11a992c1fb3d1032aa679a418c8ba4f8a0bc199e10cf6bd77a14fdd6a06093514348e3a8974434ae8a3676369c6be2cf90e672b343fce04ac6b22e0cf47568bc45d70a68e68c649a4830ae218590c1a437e7a23a54efe44f67086eb697b9fa57835f0b8f70f0a929226efb336c0e21833a028218cd63732c80aa477e62d141dba81854f70da68daff4a84cb6de779254e8a97e73565374af4092af05cbd6654afc3fd72f0ae232695cb6668eafecc4069bd90bb528b83efa2fbcdbd93b289929621ed74d808738fc103eeb105510851fc9319f171ea0ced0b97b5b9fb5ef985186bc52098f9eb476f67b7cc7665d47587975cb45a50fc64100719bf76345f0fdf1e09efe9fb800dc114e46be0879a195cc06870e23d2631dae71c3994481c8761c40d07c5bfca95e718b7b22585af03ed34175a46d57af3518e32a7fc1aa4482732a81a87f724f8d2e780b3a39d451a380f75c2d680cc7213eab1d4a59d394ae3810a1c90818d52f93fb203e2d8b1b5fa8f60b2d585d9135d648846f138b86953242d2bb1f2ecdf389b4de7651817b8e4e64b333f1aac523a93f2748a9c38ffbc29ced457b6f9781b08a67a1975d031ccd71545c0037434056c2434d13e6c4beebf46fc12222c0b2eccd6159d5aea8e554d7a09652b06bf7ca699a7199e716d05dd553041a8f2b303d236a9babaafb9fa528f28a2ca2aa780b940383c099aa65a0074b83fd1f0bc5b7b5e46c25e54838b3cbcfc95f87f1d471b3ba894434fa58952fdcb77f161372693306dba4e8f216d1c8e5caff0fe8360a51c60763644169fdc6a8267f2e3f909a61b2a678bce6ae90403a836b1a7b7e8cd8b54c37087a9e14446d95e6908d2eedbfcc653e02fdf771f701a79b9e5a26ed0a947842070f3b5701742211219e761762c37f0d0a1d1b9750fee577e1208115c66ac07ec091e6a3fc4aa6a253bcba868edd3154dcaf5162f615e85490a6ca342f34c43ac61a3ea6bfeefd850e190eb1d8da4d28b5eceeb1678c02433ecd5d48b2536404257e8ca7bef5855f2b813ed2f4c409445a3317c9be1a35ae2fb4d2b87921b904bf2c14db514cee045251cfc276374db15c99dea15acde197c6eb524988e39b63287beb8676865aaa3bad1b43b8cab15cbf27a498759e3203abf369e97242f0b0154149f14ac233cdb73a22b7fb8f09325bf2ace83bb6b5db8a121a2b682149a69131ccce52229840b113fc7b0bcc58405bfe87f1f95ffc2e96fc5596567e94364dfaa6d9d5a6eb99ae4ddf424").unwrap()); - } #[test] fn keygen_error_cases() { /* - Testing this condition: - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) - || seed.key_len() != 32 - */ + Testing this condition: + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + || seed.key_len() != 32 + */ // success case KeyType: seed let mut seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* MLDSA44 */ let expected_pk_bytes: [u8; MLDSA44_PK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap() .try_into().unwrap(); @@ -178,19 +277,22 @@ mod mldsa_tests { assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // success case KeyType: BytesFullEntropy - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::BytesFullEntropy).unwrap(); + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::CryptographicRandom) + }) + .unwrap(); _ = MLDSA44::keygen_from_seed(&seed).unwrap(); - // Failure case: key type != Seed || BytesFullEntropy let mac_seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); match MLDSA44::keygen_from_seed(&mac_seed) { - Err(SignatureError::KeyGenError(_)) => { /* good */ }, + Err(SignatureError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } @@ -198,11 +300,12 @@ mod mldsa_tests { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); assert_eq!(seed.key_len(), 25); match MLDSA44::keygen_from_seed(&seed) { - Err(SignatureError::KeyGenError(_)) => { /* good */ }, + Err(SignatureError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } } @@ -216,22 +319,36 @@ mod mldsa_tests { let sk = MLDSA44PrivateKey::from_bytes(&hex::decode(MLDSA44_KAT1.sk).unwrap()).unwrap(); let rnd = if !MLDSA44_KAT1.deterministic { - let mut rnd = [0u8; 32]; - bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); - rnd - } else { [0u8; 32] }; + let mut rnd = [0u8; 32]; + bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); + rnd + } else { + [0u8; 32] + }; - let mu = MLDSA44::compute_mu_from_sk(&sk,&hex::decode(MLDSA44_KAT1.message).unwrap(), Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); + let mu = MLDSA44::compute_mu_from_sk( + &sk, + &hex::decode(MLDSA44_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), + ) + .unwrap(); let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); assert_eq!(&sig, &*hex::decode(MLDSA44_KAT1.signature).unwrap()); - MLDSA44::verify(&sk.derive_pk(), &hex::decode(MLDSA44_KAT1.message).unwrap(), Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), &sig).unwrap(); + MLDSA44::verify( + &sk.derive_pk(), + &hex::decode(MLDSA44_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), + &sig, + ) + .unwrap(); // test the streaming API on the same value let mut s = MLDSA44::sign_init(&sk, Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); s.set_signer_rnd(rnd); s.sign_update(&hex::decode(MLDSA44_KAT1.message).unwrap()); let sig = s.sign_final().unwrap(); - let decoded_sig: &[u8; MLDSA44_SIG_LEN] = &hex::decode(MLDSA44_KAT1.signature).unwrap().try_into().unwrap(); + let decoded_sig: &[u8; MLDSA44_SIG_LEN] = + &hex::decode(MLDSA44_KAT1.signature).unwrap().try_into().unwrap(); assert_eq!(&sig, decoded_sig); // Then with the message broken into chunks @@ -241,9 +358,8 @@ mod mldsa_tests { s.sign_update(msg_chunk); } let sig_val = s.sign_final().unwrap(); - MLDSA44::verify(&sk.derive_pk(), DUMMY_SEED_1024, Some(b"streaming API chunked"), &sig_val).unwrap(); - - + MLDSA44::verify(&sk.derive_pk(), DUMMY_SEED_1024, Some(b"streaming API chunked"), &sig_val) + .unwrap(); // ML-DSA-65 @@ -252,25 +368,36 @@ mod mldsa_tests { let mut rnd = [0u8; 32]; bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); rnd - } else { [0u8; 32] }; + } else { + [0u8; 32] + }; - let mu = MLDSA65::compute_mu_from_sk(&sk, &hex::decode(MLDSA65_KAT1.message).unwrap(), Some(&hex::decode(MLDSA65_KAT1.ctx).unwrap())).unwrap(); + let mu = MLDSA65::compute_mu_from_sk( + &sk, + &hex::decode(MLDSA65_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA65_KAT1.ctx).unwrap()), + ) + .unwrap(); let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); assert_eq!(&sig, &*hex::decode(MLDSA65_KAT1.signature).unwrap()); MLDSA65::verify( - &sk.derive_pk(), &*hex::decode(MLDSA65_KAT1.message).unwrap(), Some(&hex::decode(MLDSA65_KAT1.ctx).unwrap()), &sig).unwrap(); + &sk.derive_pk(), + &*hex::decode(MLDSA65_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA65_KAT1.ctx).unwrap()), + &sig, + ) + .unwrap(); // test the streaming API on the same value let mut s = MLDSA65::sign_init(&sk, Some(&hex::decode(MLDSA65_KAT1.ctx).unwrap())).unwrap(); s.set_signer_rnd(rnd); s.sign_update(&hex::decode(MLDSA65_KAT1.message).unwrap()); let sig = s.sign_final().unwrap(); - let decoded_sig: &[u8; MLDSA65_SIG_LEN] = &hex::decode(MLDSA65_KAT1.signature).unwrap().try_into().unwrap(); + let decoded_sig: &[u8; MLDSA65_SIG_LEN] = + &hex::decode(MLDSA65_KAT1.signature).unwrap().try_into().unwrap(); assert_eq!(&sig, decoded_sig); - - // ML-DSA-87 let sk = MLDSA87PrivateKey::from_bytes(&hex::decode(MLDSA87_KAT1.sk).unwrap()).unwrap(); @@ -278,39 +405,57 @@ mod mldsa_tests { let mut rnd = [0u8; 32]; bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); rnd - } else { [0u8; 32] }; + } else { + [0u8; 32] + }; - let mu = MLDSA87::compute_mu_from_sk(&sk, &hex::decode(MLDSA87_KAT1.message).unwrap(), Some(&hex::decode(MLDSA87_KAT1.ctx).unwrap())).unwrap(); + let mu = MLDSA87::compute_mu_from_sk( + &sk, + &hex::decode(MLDSA87_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA87_KAT1.ctx).unwrap()), + ) + .unwrap(); let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); assert_eq!(&sig, &*hex::decode(MLDSA87_KAT1.signature).unwrap()); - MLDSA87::verify(&sk.derive_pk(), &*hex::decode(MLDSA87_KAT1.message).unwrap(), Some(&hex::decode(MLDSA87_KAT1.ctx).unwrap()), &sig).unwrap(); + MLDSA87::verify( + &sk.derive_pk(), + &*hex::decode(MLDSA87_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA87_KAT1.ctx).unwrap()), + &sig, + ) + .unwrap(); // test the streaming API on the same value let mut s = MLDSA87::sign_init(&sk, Some(&hex::decode(MLDSA87_KAT1.ctx).unwrap())).unwrap(); s.set_signer_rnd(rnd); s.sign_update(&hex::decode(MLDSA87_KAT1.message).unwrap()); let sig = s.sign_final().unwrap(); - let decoded_sig: &[u8; MLDSA87_SIG_LEN] = &hex::decode(MLDSA87_KAT1.signature).unwrap().try_into().unwrap(); + let decoded_sig: &[u8; MLDSA87_SIG_LEN] = + &hex::decode(MLDSA87_KAT1.signature).unwrap().try_into().unwrap(); assert_eq!(&sig, decoded_sig); } #[test] - fn test_sign_mu_deterministic_from_seed_out() { + fn test_sign_mu_deterministic_from_seed() { // I don't have a KAT, so I'll test against the regular implementation // ML-DSA-44 let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let rnd = if !MLDSA44_KAT1.deterministic { let mut rnd = [0u8; 32]; bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); rnd - } else { [0u8; 32] }; + } else { + [0u8; 32] + }; let tr: [u8; MLDSA_TR_LEN]; { @@ -320,38 +465,116 @@ mod mldsa_tests { // BEGIN expected values let (_, expected_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - let expected_mu = MLDSA44::compute_mu_from_sk(&expected_sk, - &hex::decode(MLDSA44_KAT1.message).unwrap(), - Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); + let expected_mu = MLDSA44::compute_mu_from_sk( + &expected_sk, + &hex::decode(MLDSA44_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), + ) + .unwrap(); let mut expected_sig = [0u8; MLDSA44_SIG_LEN]; - let bytes_written = MLDSA44::sign_mu_deterministic_out(&expected_sk, &expected_mu, rnd, &mut expected_sig).unwrap(); + let bytes_written = + MLDSA44::sign_mu_deterministic_out(&expected_sk, &expected_mu, rnd, &mut expected_sig) + .unwrap(); assert_eq!(bytes_written, MLDSA44_SIG_LEN); // END expected values - let mu = MLDSA44::compute_mu_from_tr(&tr, &hex::decode(MLDSA44_KAT1.message).unwrap(), Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); + let mu = MLDSA44::compute_mu_from_tr( + &tr, + &hex::decode(MLDSA44_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), + ) + .unwrap(); assert_eq!(&expected_mu, &mu); + + let sig = MLDSA44::sign_mu_deterministic_from_seed(&seed, &mu, rnd).unwrap(); + assert_eq!(&sig, &expected_sig); + let mut sig = [0u8; MLDSA44_SIG_LEN]; - let bytes_written = MLDSA44::sign_mu_deterministic_from_seed_out(&seed, &mu, rnd, &mut sig).unwrap(); + let bytes_written = + MLDSA44::sign_mu_deterministic_from_seed_out(&seed, &mu, rnd, &mut sig).unwrap(); assert_eq!(bytes_written, MLDSA44_SIG_LEN); assert_eq!(&sig, &expected_sig); let (pk, _) = MLDSA44::keygen_from_seed(&seed).unwrap(); - MLDSA44::verify(&pk, &hex::decode(MLDSA44_KAT1.message).unwrap(), Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), &sig).unwrap(); + MLDSA44::verify( + &pk, + &hex::decode(MLDSA44_KAT1.message).unwrap(), + Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap()), + &sig, + ) + .unwrap(); + + // test invalid seed types + let wrong_len_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d").unwrap(), + KeyType::Seed, + ) + .unwrap(); + assert_eq!(wrong_len_seed.key_len(), 30); + + match MLDSA44::sign_mu_deterministic_from_seed_out(&wrong_len_seed, &mu, rnd, &mut sig) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => panic!("Expected KeyGenError"), + }; + let wrong_type_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::SymmetricCipherKey, + ) + .unwrap(); + assert_eq!(wrong_type_seed.key_type(), KeyType::SymmetricCipherKey); + + match MLDSA44::sign_mu_deterministic_from_seed_out(&wrong_type_seed, &mu, rnd, &mut sig) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => panic!("Expected KeyGenError"), + }; + + // success case: seed SecurityStrength is exactly right + let mut low_security_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + key_material::do_hazardous_operations(&mut low_security_seed, |seed| { + seed.set_security_strength(SecurityStrength::_192bit) + }) + .unwrap(); + // a 128bit secure seed should be rejected by MLDSA87 + MLDSA65::sign_mu_deterministic_from_seed(&low_security_seed, &mu, rnd).unwrap(); + + // seed SecurityStrength is too low + let mut low_security_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + key_material::do_hazardous_operations(&mut low_security_seed, |seed| { + seed.set_security_strength(SecurityStrength::_128bit) + }) + .unwrap(); + // a 128bit secure seed should be rejected by MLDSA87 + match MLDSA87::sign_mu_deterministic_from_seed(&low_security_seed, &mu, rnd) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => panic!("Expected KeyGenError"), + }; // test the streaming API on the same value - let mut s = MLDSA44::sign_init_from_seed(&seed, Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); + let mut s = + MLDSA44::sign_init_from_seed(&seed, Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())) + .unwrap(); s.set_signer_rnd(rnd); s.sign_update(&hex::decode(MLDSA44_KAT1.message).unwrap()); let sig = s.sign_final().unwrap(); assert_eq!(&sig, &expected_sig); - - // while we're at it, test the streaming verifier cause I'm not sure where else this is being tested. - let mut v = MLDSA44::verify_init(&pk, Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); + let mut v = + MLDSA44::verify_init(&pk, Some(&hex::decode(MLDSA44_KAT1.ctx).unwrap())).unwrap(); v.verify_update(&hex::decode(MLDSA44_KAT1.message).unwrap()); v.verify_final(&expected_sig).unwrap(); } @@ -363,59 +586,116 @@ mod mldsa_tests { // ctx too long // this is common to all parameter sets, so I'll just test MLDSA44 let (_pk, sk) = MLDSA44::keygen().unwrap(); + + // ctx with len 255 works + MLDSA44::sign_init(&sk, Some(&[1u8; 255])).unwrap(); + + // ctx with len 256 is too long let too_long_ctx = [1u8; 256]; match MLDSA44::sign_init(&sk, Some(&too_long_ctx)) { - Err(SignatureError::LengthError(_)) => { /* good */ }, + Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for ctx too long"), } // test various things that are shorter / longer than required - // sig too long / too short // MLDSA44 let (pk, sk) = MLDSA44::keygen().unwrap(); let sig = MLDSA44::sign(&sk, msg, None).unwrap(); // too short - match MLDSA44::verify(&pk, msg, None, &sig[..MLDSA44_SIG_LEN-1]) { - Err(SignatureError::LengthError(_)) => { /* good */ }, + match MLDSA44::verify(&pk, msg, None, &sig[..MLDSA44_SIG_LEN - 1]) { + Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long - let mut sig_too_long= vec![1u8; MLDSA44_SIG_LEN + 2]; + let mut sig_too_long = [0u8; MLDSA44_SIG_LEN + 2]; sig_too_long[..MLDSA44_SIG_LEN].copy_from_slice(&sig); - MLDSA44::verify(&pk, msg, None, &sig_too_long).unwrap(); + sig_too_long[MLDSA44_SIG_LEN..].copy_from_slice(&[1u8, 0u8]); + match MLDSA44::verify(&pk, msg, None, &sig_too_long) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for sig too long"), + } // MLDSA65 let (pk, sk) = MLDSA65::keygen().unwrap(); let sig = MLDSA65::sign(&sk, msg, None).unwrap(); // too short - match MLDSA65::verify(&pk, msg, None, &sig[..MLDSA65_SIG_LEN-1]) { - Err(SignatureError::LengthError(_)) => { /* good */ }, + match MLDSA65::verify(&pk, msg, None, &sig[..MLDSA65_SIG_LEN - 1]) { + Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long - let mut sig_too_long= vec![1u8; MLDSA65_SIG_LEN + 2]; + let mut sig_too_long = [0u8; MLDSA65_SIG_LEN + 2]; sig_too_long[..MLDSA65_SIG_LEN].copy_from_slice(&sig); - MLDSA65::verify(&pk, msg, None, &sig_too_long).unwrap(); + sig_too_long[MLDSA65_SIG_LEN..].copy_from_slice(&[1u8, 0u8]); + match MLDSA65::verify(&pk, msg, None, &sig_too_long) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for sig too short"), + } // MLDSA87 let (pk, sk) = MLDSA87::keygen().unwrap(); let sig = MLDSA87::sign(&sk, msg, None).unwrap(); // too short - match MLDSA87::verify(&pk, msg, None, &sig[..MLDSA44_SIG_LEN-1]) { - Err(SignatureError::LengthError(_)) => { /* good */ }, + match MLDSA87::verify(&pk, msg, None, &sig[..MLDSA44_SIG_LEN - 1]) { + Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long - let mut sig_too_long= vec![1u8; MLDSA87_SIG_LEN + 2]; + let mut sig_too_long = [0u8; MLDSA87_SIG_LEN + 2]; sig_too_long[..MLDSA87_SIG_LEN].copy_from_slice(&sig); - MLDSA87::verify(&pk, msg, None, &sig_too_long).unwrap(); + sig_too_long[MLDSA87_SIG_LEN..].copy_from_slice(&[1u8, 0u8]); + match MLDSA87::verify(&pk, msg, None, &sig_too_long) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error for sig too long"), + } + + /* GAMMA boundary conditions */ + + // test to pin MLDSA65_GAMMA1_MINUS_BETA (produces the wrong sig val if MLDSA65_GAMMA1_MINUS_BETA is wrong) + // this test doesn't work in the lowmemory version because I don't have the seed for this KAT's sk + // let sk = MLDSA65PrivateKey::from_bytes(&hex::decode("FAC3F0A1C095D7F14FE867233429E7FBECC3E56C1C47C5776F60FCC952271C16D701337CC4DB701037240B2CC96700E76D912695D01ECFD144A1A4480C9A7AFC6AEBFF85B31A51CEDCF03C089DB78D57D5736998094CED527BF62306D163DD076746390725A575F658812850340A6A22F21D1A4488F81CC9C51E34C7195E24E62743418082738460173216564147714141447322747378303064332088852786387656485685067602315841385530303073748288551114488671126472324211001632768375217888105730287103413366631348063456705643028124418424362865147136784028750605280522354664402012156867463840124312702746274317185820204146418146260428165381878680661671606625835681411564078617545272781118153404548772767065605505661113346550212253001708208871437608544687461742554212252531618480788375428626465682802665176276452816778253827888713776811675325120157277407836344676441366411518042352072878407260148473360521722835364503831153320866470060388717774746280482741018426182467855676081768764502460378420511274573338543580057135466786576641657141853703461273673864078756846882878275266371317370221447843022470612874036187668341702635480511522823511802187750128755208466178350262551186287764073638143070751153170178714610652685824431071335605871487821771381233638082075233653821363404721684841385534088177538652001225820118825132746376784420308558673853184746535462708561415062307838047410305386755301311878544252655175427735642725125852462520300512237334101417850348822685720422134232331416163464745560268223741265100778127023844048536253761851061180163560244303674215200615686636064370316373872671703546376007034507065834571177568604078030182100382824024244355146222000521887855141800051638076135301876078282783854476883578855568168226642036114282711266780675104628153263815046834464683021517741886573830273710441308840745348777606127672844722386630210200025302263135713023513144334831232011681183146135820156511536562848603567353564216431366041187872531665824337234157375170402400366431125740874338582777401587182726007834342527487856427375026483665335500311348218318404831768517434724867376286526385537375022315084547851162348038628580233345112714217535531172243054028425076334167574124642505645643703357747258152772101731762154367415443402180013310507803185561248014235117864462560741807827475862248680132084577068144204516541344387456176453454137741684473663420305220270013380651567340128517050186661163071768085126283043061688144133578020758648635074223303518423535803274805136544782015667213011858810228662670086040427500742786645441425845543080676585818187373073177826648377838253361800836061614077137087700227656343025125400007421502577613446081061602370815457845047316447020371536762643681172462742533741043757221578526022521413633247403254078850774373623685012472806652753134016260818410320635707047651107738122602141422051241873175027656625672307441218576154467471554748806141478270237022035541450228312172183426588573284554185388468111581188733140148403753651886533044825265188582527046117408104630522280766840883618814145722050535737005141474643706767817456415644868337004352380220517858504287578807218203860374826463502187671017880147716606130536501355309DEDE7F9572F3915A1C1B014B2FB3D7591DB0BED20443D420E8D4D3458381AF3506F93C6CB3561449E50A9E3349DC9B8CAD8AB1ECBF946CF6ECBBBC11A8B32B295D1E3CB23DF662E8A8625EE91ACCC74EE9AB059FB12A46AAEC343FF1B21BED35E135F46D05EE11877126A9961C897BFEA0D552E03BCB313AD1BEF5B21FFF09005417B0F531120D037D1106C4FFF3692C607108563B272D054D98299D1FEC0646536152FD6F915FBBD8ED1E864904149F919DA295CB79F4EE6A9123E82FC5DF0BC379743B0A650D8D26799B02C0A6F278B323BB7F58811E75CDDD6BA27200DD7BD5B94F6AF7AC76B791F12A1C464E2E112A591A12E3EE569C7B0BBF726C634737A7388D676BF36DA496E3A6DD4744D83D5379642261B61D6A08A164BE30360BEC7E05473A66CCDE29DE5F68006932739DD876E29954419750B6C2E282432E0E900F30C6DEFA5042216C398189F1072F3738DEA06AB44F7611BD995430B2204FCA4474A2A8DFAFC44607F9AABB80D5FF235BA93A74BCE8297D45A35322116117A5BD9C960A18758040EC670D8F0B5F75515DFAD91A60FE8F26A5048F9A955210B6CFFC7F6C963531E9B6EB0DAC6D744E63A22AC9E2935FCBD8FFDC6D1D5621266A338FFD3E28257C5F04B209DD1013C2EA6FBE2D197BBBC9B8F1A6DB84E361F1E1D6235566A289861E193CF51604CE29DB66C546C20CD3D769E128BE0C10C7E8EA640F23778F426DE99A675726CB837EBF6F2EDFD886E7522FEA3E9AE793E34B702493C6575828021B8A4E1DAB64DE98DB52126120B8B192AA8977143E11E7819A2C116598CA301670C51569432DC1C86270F72E474CC35B4A7621F0EF883BD9B187DF4EBF2BECB7487BE331A45E9CB43E5DB9225B21724D60CFFBE5A3817765B5DB2E111AE6445BFEDD6631A1FE8048E2008E5DF450E0FE622319217567AB202BE5F6E9D8859CADEB2210784D9FB1778BA039A98A2C47435EBA76EF170B9ACB2D3884D64831E910D2CE1E8346881490FEABFC5882C7CF9D6101916E4021B9CB723189345049B5048400307E0236F177329EE6CF9A67848A4D7C98349A94A68400EA141BDA866F5071F3F4A7B8B96372C5442B43A003962DA6A5F07B9A572E7EB8A057BE06C7AF19D4EA728B7BF7F0AA493C413190CEE7D9AA90033378A2FEAFB9F1F360A904BFFA62FE1161240FCCAD33473A93FDAD26DAADBBA4CD05BD1F5897C40FD60BBEA8ED0404347131B2251D4CE06A65BB5EC31BFF5D03CFEAFFD7D06D09CFE8D88706A0AED09DFBCD273C65E1797D65136325A78E56CC4836B39EBD5A964229D2E9A163FE0E12BF0355D6265D01B7F94874E579119AA7E8C7EA69C09A2FA46069FA64CF0CD2ADDE12ADCE6B422676C0F05951B5C85404E25455505730986415B693604F557E09A6C6F48F5C193BF7DDC0ADFFC91CCBE0D3F7577F9AF85BC039CF2787ACE3010A413E3A0613C323B00C5A42776789382EF804BA918646A27ADFF1C3BDB8D86D4D1237ADF4190FDA2D87510ED68CF9234A766EFAFF779EF9FC4F8337E147FB5C83227E97F67D497F675DA66D55A88E9611459D98473DBDDC0730B2F8C6FA606A22E26679E68BCA03A50DF3731992A97063A4A6AFFF8C268290422A9D094B85BD6851177BE2BC04FFC64CD96E866731B552DD3C779D49C7781BEB8DA3F85B942A9780E47675B11E5DD77ED1F343775E876A737CFA3CBDF5CBBD7614A3EABE4DB919BC3E13ADE651F45133A04DC5DE0DDBC0BE01DDD615BFE6666769CC663F4941131A80DE835C851EBC67DFA48024C07FFB50F5C7E0A5686D1AA9EA22E45F6DE2A07DFEF28E90B00C24074897A76FFBAAEDE6BBB9A0DC76D35E2163169C426B1F5221D0A33F06C25072A2B164521736C441D4547EBE8AC4C408D30EA34ABF595BCC4CDF6157E86D8F8C5950E9EF27290EA077D812941A1DBBB4C6D06D52583DF55547F657346373CA37BFE9CDD6010CB5B0033F643EE0D6C9052699FC8E2739B69E1896BA14C7BDAAA7305F5F424350979F60868CB7E9884C818444C0FC0481216D06AF22076B726DB17A496CD45E9BA344C9F49AAE12E2771DDC9AD40908A00F8A19B85F120DCB1BE238E77E15ABB6E24E4E8BC55A68DFA7D473ACF7519EB85F714C0F49908BF2A6F70AAFF77A3DF2500992E304E98F6641505B1A5DF02C66118492354EA720A7078827A1B769B308440864FDE478F312DD497F7C507AEEB9AE85933607F20E4FD1906FB6FD074B8935D0038A2793489136E8305131FB95296A42CCB66F4F90F7214642929DC6E6843CE5B83E58E3F22CA8A5586A1D93527D75691B1E485953FE1D3E56E1C3071965B8E8EBBFE0F3FAB5F24FC245D2AFC423AD67DBD22E69ED2619E42CA0AF8F30C06D969CD7C69AD0783E17D018DEF90E75198EFADE2AE031FAC1905D5911D198635A1972D9CD8C1E99F049FBD96E6F88A25773B9F6048B6C136B4110DA4F587EA5C27700BBB6800854C9A4D1375F9777E09B1E53AFAF73EECC5E7711F7B19E7026408DAA10E93B8D916ACB0A4AD57848C2B9F767C48709382A2CC57B9DF56264B8EA91A529B4ACC7BE8DD3F3DF30276CD459CAF32F65118B6914E22507C2D5E032038BC272442FF16A80224EE35A4CA90B2564EA78B3856858D985644B683F62FF5AFC04F4F6FAED8434B6CF4EAF116FE231FB7931968D7BBA18BA347628304EB13EC8BF83FAE853E208FD276EA60B2DA751B058D82EDEEB4099BE90A581B52415B3E043B39D570C6E3996F42733A1459A5E3667009FF1124C106D2ACA45107D242ED4FF486F1991A01B5E471D6D360C19D3D064B578147A82DEA65FE0F8B7897B7347038522A2EBEB7ED217E43C33D5B7ADDD9942B43F9D5B8D87416C425F3E6CA3BCD3D995051C02EA4EE84BCB3B877F27FB16993522A91539D3FF43925671FB256573017302DDCA5A562FFCEC07A1204560252C78C2D797DD4EBF300922D2891AF7D220D32C3AF68F20CC096F6D5F9038E5AE7BAD090F99F9AE7FF3A615EC4A7F1F759A52428F94DBD496A81B0B71278EE4127AA72599C97618BF1D053C471E85D888C2971D816E5147BB07AB2192FA473641495CE64596D543D37DF340181EC609E8EA8F72676C2DCDA357C19051A8642D99FA00F962D28B4830E81C8D0CD2DF26ABE514803C8192608E29B9ACB3B150A664C474C6E064F1BCAAD0A08344E911FBF8425A0893D71325012113B8F2FD74FC0E2966AAFD72F280D18D8D02F779B0B2C61DD5E4385B5B82242AC327C3DDB6AB8F288E8DDA24F1E5812A111860058DEA190208928F1AD208974AA2EDD53BAFF400888AA9D74C0071C65C30AFE4A02B99876621A0542BBCE337ECFC06F0A236C3E9C672B48E61EA70CDF82421C14B7F0D25474ED2B04DA1C570598B14CFF438BBC44AB3C931F47D65DD0E767DCD7DDA522CC4CB07EB78962A50D281BEC69B54A77D26C902D3759560DAB544404BE0AA3F80D3B84EC4731DFFD1579533004D2D1EBA09148AA84C93B6A2049AD019A0BDAB9F99389FA4").unwrap()).unwrap(); + // // because this is from the bc-test-data with busted mu values, need to fix the (busted) mu + // let mu = hex::decode("dd6438e69fdbe810789fb22940fb451c28ac570c8e195f4aebc22db848cabe970a76c92011702d672478fc09f2bdc10b0b7c2b1752a9d99ea7ac3a6381684540").unwrap(); + // let expected_sig = hex::decode( + // "C3263802CBF2818D55BAAE9C2BE8C1EE0123802BDAB7C87CF9E6CF06E2A8823E750FFC8FB917F9052275A2CC68C2ADF4CFC78D0144FD3E96694C116CE1672BBCA6A3E1F6BBE8A0C34E5C1874DE1798CFC7766FE1BA906A1C6EF40EAB1A914705C3B0F14ED8D212B81D400281F68644B2EF72330DA2BD16C792D8160896177E920EB8E198AB5A38F5FFC5088663D0D0E0EB30C342F41B886FF209C8939CA424C2C24380E4F5AD805242CD25F4AC445AB883CD5826FA4135F4D549B12662C687B0FAFF068F8C18077B463E910E7A17B3BA9A76418C79FC53CADC75E48E977EAEA1FE748EDF0A1C0CDF9D037812B1BE505BD39E4EDA489CD56369A1B3F793554D7DD815AB1A7DE536018F0E0D73B95C8EBA07626D92B0365F6F71FAEA07E1E80FEEA666EB57865D6D13E46B801FECE841A2179BA8DA1FC1C1AA53743F187F426DFA34510E11AF1D9570FAC5DF77C9BF7ACE64BB130DB2192959091DDFEED580C722ACE89F84729540EEE8CFF6D8E4AF91C1B0EBB42A65F47B80F4A300431D7DEC90BE739E31C9906D7403B82C196266D4FCC6659C5AA549D0ADC3AAAD1F9DB7C80E17839957DF682C10B1938DD9A292C65E0146566B1EE24529C925691BAC109CB308C4303C3513FF5B62275E84C8C94DD6CFBF2E0F9F0B735FEFC44095FA2E01009C37C33BBDF082B305AD479C743076EC2EFAC0176F15A0EFBFA504894A3F8C6B7ACB096182B71131F46B1EC7343C9DE78F85CC0352F97C188667898ACB4EB4173D715FEA3B69DCB1B47904C8AB51EB4915357995B5F69F979776C6CBF114BCB0A154D82D17A9251ED545FDBE7E04AB06B99662D18673F08D933CA0B84FBEBE17F783D4039078465C378E9AEF18423C7B86C411DF9B4A77A2BC8804C4E5A5F9BC5777166C221F60107D2107AB55BB53E354267F8329B71A77BFF920EA94D9240B436E8CBA65940C0734D6928F57257A16154BFA5D8C9459979DEF567EE0965239C0DBDF122723636F2C5C5CAC6C607BDB9FDE8B8557A687E3A68970BFFAD695BADA74CE3C681B81AC8CE3747B844B7530A606F6320ABE91F16E79EFFBCB8446319D888D957252836BE6D88C018EED2F5C46369FE88CAEE624905E71C41252B70F115F781E0D4D851D2820718DA03FA1E9F9490AA06D74E3D15A44CFC98E57AB49A63285451C2A43D5ACA6733952814C05391431052DD9FE5D4AFAFE7027E5E5C8E9247FD5B574B438C4E541D6F408964BFEACC54FF873C6976F4383AE2517D4860E3D775280DE63D2EB01709282170E33A2F5FBD80937B0D23188C978BA370B5BB154FE0F2FA0361A2F1E46FBAFE6494FEAFF26097EBD2E307301C8649FAF82B57788B62E6C69DE6F6B3ECCDF17C10CBA41C3D1A8025F5BE98D65F2B37AC2527BA60E99B89FDA9028845F6EE6E20C2C8E2E8D8627C4D5859CD141920133D4D4B3AC8B91C1B14B6F6B8DE35E9674F312B938DADB90C335931081DB90640883512125BD793467C78AB84208146EAE0D62004E51031377BBDE89AA0AF0609E9AE315EA8A043C368167073422018900F9C653535A96FF73DA466377CF462314F361E08ABA6CACAB9AA666633FC6602D645CEEF305829B90AFC8BBDA9F8E40EFFD62FC31A1492914E22DE711DC3102D64AF46D13ED766CF36E51629409F69EC4DF98CA97EC93DA4BA215C567C73104FDF6863229E2A2C9A3CA11DD3C8D505CF88944428F9D500029B770FDEB16388F63C50FCAD1953E2AC759730E7CAA930362274B6A3E0ABE1211613F3C36FEFD09454401B6110D95201BD022A3B6C68DD054AE0B0C547AFF5C7421EBF0879098112286C962A6164C799A7A20CCF3D4189B2AA240F895C54CB2F9E6C2C1E2BA9BEB185CCBD82CEC60A49D8CBBD3C26AC0BC5C43E2D5672E21F19ACFF857217A243D4C90600D3737EE78638AD6239C3AF4E79FCE030DDB0527EE428ADA0C1C9EB867593092CD1A5B77347FEF4541B71AB33932F8F1BE31AA475B9F4BBC07F218377E436E348F47607D9140E6114A05D2A072409C390EBC79231CB6DC2E3A698776F9C32B44C10F156AF450A8DAD6DEC0D294392DF6E17DDD312BB22997CF1D8A6EC55DFE401EBA486CF9C8C0E6E713D48C0CA8CD26EAF88DC0B34F86F35B6617C45173131779ED98E880BBA3520835F29604B6F32AB1DA8034F6F559E81050BBCD7833B0D71E90101339386AE3BC653CA50F37BBBEB64377987BD892CA51280499B6EB051345EF9BF1F6EE766142491F96E27787ED8991338F60D2A4BAABF43ACE7A6072C6BEB6D0FFF79A3B956DD5CF6348F6E02104E1F9F33B2AEDFFA6B833A00CB21BAD6AD04047DD845180131506080272925960DA888E393B43EA5770530347EAF9E9812E87A317D1BA66C61CACD55A8FFD6355BB39E596D33F4E8355A6B51C4E70697D4C5BE9FFE64FB11EA714D81B20677DBA268E8A9A5F61E8E268DDE6F6FC93F3FAD688312885681E0F67474689680532DB88E178AAF2FDD19E37B091020FF01008BF0FA8017075C2F02B5E7DCACFD1F337A24815C6D9A9E1678EE2EE1721A61ED774A7DE98051F799D4F3A5407C1F725B618AD3B0C17FD13D762005C143A9AD78B13DF4B376D924BD9AE37502446D2263046BC7CE97CB6371CBA72FE0224FE71D501884E9AA820F92E1CCC3F7F4D57214F0842C96B85BFEFC3767B81AD76F778B00EB2BAE3C99D75EAF295901B9DB273F4288FA4EA74650B90D114B081B1D061D97EF211A84726756396F36BCACC45A8EF3F57E2331EE4FBCEED55C48605B4A3D842067933F8AD6D72C1793B17515977EEB8404CB55388EA3CB43F504A5325870473C14287DE22D55015391D973A9556B93B0943C64F5CE8DCD365C1F1DB7B56B2FC91BF5552E5233C28755E366BCF303B33CE2FE608A5BF6348EAB272E4FC6B77F13367E12085E008C8CE17095727D83266CC321ACCC9C8B58F2D149C205A4DC6D3A599CBA0E3FC4EE042C3461445A536FB9AC09D2FEA87C7E0E8BE39B2D0894EA1E203CAECC5F79F13ACAA917401E75E8F36CBA4D34EB639725FE41E64912F48A70D1BA47309D23E40DBCFF986568B8536B5F464C5B169D5E0F90ADE7F46004098E5880340DB738A7B9C849A71D01B8F8F9FEA8B3B331210553C469A5EE257B4EB02EC8932787C54086C5DE6A82096F0AB32B716BBCBCAF26BAAA792931D946FA1D59EE8A9924484E38B02F89D12971903C675B9FE420DACB1C0172351A3A44376E6AF6F25B261173E98B04DD2E49BD795A17E9C3E1D7DD1EECAEC4B8A5AE4B266E4DC0618E41AB8F82A4F652D2A07814DDEBA856B6000EDAA8A8A0542466A83243AD8128B1C468215EB58CC436BBE6B7A8D8CB12FA78131E1887B3253BD623055D72B13E175AB7DB00E8D7680360990C1D9EAD06F04F51F739734DEDCB8AA5ED3BCFEFB49D5C89ED291C4AF1EC09BDFD24029B7A219067DAF493C53809BAAAF830C8D4A3D643AA1B0CFABE58D0B1A5E9C0357AC421459FF0B0766CF82E08F95BF8B6C65D81BFC342A07BA51C914D7F1FFFAE1E069BEA449EC9BCF898BC29CAF4FDDDE6846361A4393F6C6987C766037526A9FE42008FB59DEE597547B8F61C62085BCA0686B6237C384107ADF094C91A230D783F55D39F1E57128A92F0FF0CD92D6B454DF4A3AFA2E1E4DF2C6C9E3CA3B304E27A8D7F44C75EEF09F475ECE74B07A80DBE1B4F2F309756BCFEC0C2D78DEA4CDF3BC864FADBEAA1B9475BC7EA4ED80D26B14B61EDE93AA2A777C8403DAA7BEC36C5503A2114A97CCA8B04565F17A4F0DFAA4985A5FCBB4398323233ABCC728CB2E0D1E2CCD36645E5915985D39ADAE871AA5B1CF3B001FDC7D9F0BD82FCE569A5C6BBD216E09C11CC6AAE067686E95BB28C441F189AFA83A9E117D944927C4A76FED756A35E5EE0B0DF4159187CFFE5F6EC19F8DAB04791E04FA0A3DF55EC95B96D7AEE099867987986D3FF1422DFDA40100C226F8A24468393F21E11D3B583DD8A5FEED01832ACD1E24E2D5A8CE16C24629B9964F8B4E0B2B0F30601FA688711065190665ACEA6A971DF8FB708C056D68540A78FA750B43E0D54086502D4B19171F61B51AC075F7F09260B95F59BF343E1210BCD39C04B13267122B6CD78D2578EA3534EDF243D0FD520A212EBA25407E2B8119068B2CD1D1BA672E5CC73BE9676C9E2D906D44DEBA9573E7FC909E69BD9CCAEBE6600E30D3588CB8EE2E5CE95C65F3FD24E4D527CE764FEF539639155086FE564B607A1082A74CDE886527F8D821E42AE88281A1CFF38EB5159955C1326C2D425D1C09205128E0386CA5C71B911896543799C3B1ADE8391E4370519DB17122A1A5EB6338208984E7347FC174D6DC4D59AF64767115A344A11E2049CDA0AE0BE6D7AE0592FC3468DB58E29CD3010E3EB17F736EE0E6062BF1F345A804F1733C73FE30C70B6FB625D00E529A400C069074235969485142D37F3D963D8492EA4026867ADE6108D982E20B188504A6724E6448992A3F7962AC2F01222BBE0715069FDE7D9DD93A6AC8AD321163FDBF9429EB2DD38F44BCA086AEA91642D4C85DFCF322F0B822B6EF73C570B564DD721FC3E73ED50A7FC88644C4340951C20B1E2C4A65B0BEC3C5E10E4C5F769CABD7E571748485A3BFD0D2204748DAE3E5187A7D97A7000000000000000000000000000000030D151D2328", + // ).unwrap(); + // + // let sig = MLDSA65::sign_mu_deterministic(&sk, mu.as_slice().try_into().unwrap(), [0u8; 32]) + // .unwrap(); + // assert_eq!(&sig, expected_sig.as_slice()); + + // test to pin MLDSA44_GAMMA2_MINUS_BETA (produces the wrong sig val if MLDSA44_GAMMA2_MINUS_BETA is wrong) + // this can't be tested here because I don't have the seed for this KAT's sk + // let sk = MLDSA44PrivateKey::from_bytes(&hex::decode("1B8664E94372A180CAD8C3D93F57FC16F452EF524D2DACEF0A50C0DDED23FBC052EA7330489B17CA2FE4234FB0F9CCA63B6185963852374EE10116AB38AAE2A922693A91188E5F5057AB18485D98B87EB21CCFDF5E9F4B5C5787F5043988EF6790A47D70515D93C48C28A855111D73F245898C877BA4E6A646057579A111F59C1BC629D82652D388901B2961C2C270A3B68054329299362691889122320418918820132D03C28D8C144200B825E0A88198A210031328D4884D1126690AC38D1B1510089051C8142D02B390C8440564040800290CA4480EC4424D1A960409A58010472D42346911A4401481884830411B122589808981222E63164D11C165DB064C0A430412374AD19271400846409271001748C9124894064952C06D1C316AC0484C19B69103A70950100208460458084C49224EE4484559048D514265D99220E2A41162C231D01620C31800D4A45008390A22A67018B36882049103440642364E09B3484A8448DA382E5CA810E01810182466D446681B489102384D8B18510397259A400A00436992C40C129285C9C05060486450988884386220808D919268233401A0B8405B0662E4C42119B74099B265E2400860462AA0B40D1A3248CC06318304489BC6114A484503A4010CB68D11860111984CA21410CC2666E0A02C94B01183182C904805A4320860B29122B66142886DC9222D24C171A42211C9A08814854023034582A25183B6711BC88DD0480292387220B041238790C8081203C0099436918BA2244A422959042D48260194322860928C00A844D21264A0A6240C9870C4A6042134209CA4099B128A1B3249249651A022281B44640AB36C029969C4C2710B396D0B964509B6209CC87150142A80B620CB28121A93684B382E01350A88160520983181322104426E83A6481014495C304DCB1826000390A4386103314E02B3008A14440AC78111C5704018021426650BC68C53C4708098481B9044C242680C124E2049284A3671C9B08911A77049982403C0300A83654B426D99345110B82C021092999429A0124E98184D50242061908981282661B28108A6651B22450A342C9C984C4B382DC4086292188D41C46CE2C491D902419C1231C2120CA040440CB12D22062DA3901102286C580066801228C9083101872554244902C62CCCB28502168CD8422E24416D1C20924A0660892826D2329092922C03451258B2099B2481183804243390C2B44C5AB485C814610A188503852988409203A580C9C0E30E0791B8E694262282FDD2FA69AD5CA94D015922BB63BC690FD546BC1ACFA0E79B02E7D1C1E92F419DA68C30CB12C736E216FC72C394883FBF40CFF538A43D08CE8A390DC7B78542199A54EEFE8FCD49225596FC97B27CF8BDDB57209E48FB628909CF889D38855CFE04AB04486378D17DC5780E81168B5E1DF462FC2D43860031B59D80AF95AFF0278210B9D96023E8A88FF0E4403D710895C830049AF4B8BCB57C5972D067A579C8C0021E5C870E1EE10629518191E283E796B58700061BA656B9385F54AA6917EEF52E60669B7B4F4FD36063DFB7822CDB4F19D9DA4E2F41E4BAE8C5274E7A9EBDB80BC9555687827A10224D9044DB2E45A89F582A716B4D3B131AF7DBCEA95DB88D84C73281D4CA7961F752FFA520BA3FDCC245331849768CE4D4B1FC330DCE474F31558764395F97FFC847BE9147DC22A3021F666A4327FF7FBEF007683A84C9FD99F5088703784AFFB3D86F216405BB9218DF05BB4CFA100145C9358EDE1A0F4C69AD23F7BA56FE8755F711067D7D43CF9FE0A93D642383DB3A0086CF7FCCA3FC59379D416DACBCEB7B49DFF554748EE36DFBCC07D15270A0388DA88360E6CA476DCFF56DE0B7B359609E58A728EE4B3ABCC296E42CB76034C7E13BD0B5208EE8CDF451F6CCCE87E9A21C3DCB28B8B9805CA7115065757B3C6D2A39B28B8610A3F30092C2E9F43A744372FFDF6B5F69DA6589860C520882FBEC130B28E1788572252A5995EB00B4B008931220B27488FEF5D04BC28C1FFA9DB5D0F1ED49CC4D775EB3DEC26ACD92BBB8A33A195A0C2F5AB7386BEA3AC9A9EE90C762E005DEF59348B0B1A6A373CCCD0BCD4125E0602EAA2BE057603020C2DC849513333C019A22210B55BCDAD54279BFEC5F998C1D14F7590B31B403564ED2E54AD007EDA4F2DE06AB0315E4C3384BE2C5392BB9FF1D41C770A767E4947FF7B609CFEAFF54DC672E866BDB861B81CAD3CC646DC325A5E1CD37C45FD0E29D6C2117CDF4C8B3082249C4F8D3E736A1CC0628D5FCB322711D78B881E7AABF4FE8967C5D584296DE520B818966A9490C82063D0C3C743208B25EB69BAEF6CC65E3CFF0C0C02997F328D2F1D307D421A87EE8A2737C8D5E7F63141DE2E03F5BB1EA20C829C4A00407F2E500BA2877DA65D64B29E42F097164F09F69C9587E4F1A1A88F619B6D19F210C01D35443F21C7C987658DCFF4163C8646F7671D04ACCE322E32513737B35944D2E70D62924F922A35AE86347843CA971FDFE616FF0DF4C5F77C699B14A555E0D41FF39BA7BD156751E73EC64A25E80E93F59217A5AC8B2162CBA9416E54F04447A99F8073D881BDA47B7D8CAEC135C0D09A028DD2A3EA74BAEFDCECA0D08B8FA701B9ED25B59F39BAB7ECA16A9ACD0446827829EBD9393E8DEC95C2395704F8CAED2155CBE604B047F94A650A981BAA93F070C3142011BF4F1561DFFC4D2F2B7144AC4CCC8DB17FBC3811C421961B156DF1FDE2893FE87902385B0FB5538E996FC40CC9B5995297DB905C262E25145A3C35A6B6F2014CC9DDAF4EE05DED72B3D436E4A6711BBA6B802D0CBC4ED44AB923644CF9E4BA4F24EFE1427E44D0A3FA98C1AA92F37E56440BA282AE0A77831650AE866C86BB2AA49EDDA7AF27B13A612F6A185265D93097679A7ED5A3A38B4B40F16CF5B11B99E2F5B529B940A4583D38B46D388167B34458CAF13682B6070D4FC37700E0222B04B9C1CCFE8FA0BB2B193E743E93C38DFB5029561287C782D9A0BAEE67705C1DF3AB2D8A456EB7C3F5E5D117108C3C15C3F4D2D382A6F69A5716621D004E5F510C4226E4B6BF76C36E649DA5D6EAE2690B1ADA69D8570DFADDE392CE5D9AF4AC94085903C52B0CAA61856150DACA070342754BC3C71AF32F8EB6D5CDF10F0B20471971569E7F1490A32E6F4DAF129A6CAE4DBC977C6B0D18102B9F18226ADF28F168B24D307753BA24574808444E488D812E9E97827261801AB2DD2F62EA4F4C552E54735A018D7ACCB666F6FBA8BF9EA8280876591B8E73D6303B2F1E585BB86F4DA1769503E144D3BD503BA460802D9287CB7B2238C2FBF3490DCDE4D35F02A63463A4F488632070345CA7C19604FED48A7102562965BFD87F2776357B3D1B17AE98CEEC0159FBBE604AEE9D92570E526CF6A5806D1BFD3092007D3DE29B9860DA1DE62184B4F576A8E515BAB346E2A2A4DB1338F4490400CA5A3EBE9AC3A8173C1991839F3A62DF7B903F777B0E5608C7396719639C8BBE3CF8CB371A43F7C436796259728ED11C3AA32BA97FB0B9C708785FD00345543EB8CF195A442A42F6AFBBBCF597CA8230B019C283777830E70C19DCF018C7C0561A7F2FE").unwrap()).unwrap(); + // // because this is from the bc-test-data with busted mu values, need to fix the (busted) mu + // let mu = hex::decode("35fd0d24022864a6f16159a654290be0c26688439c30c979d14b9553299429cd7c01f9f9835121149c254f2615805f64a32feb0607d87e9417f2900e0d86e351").unwrap(); + // let expected_sig = hex::decode( + // "20E8368CE256DCE0BD48E92EB0C35821EBE95B1388A861DAF45ACD482BBD95AA3FFBCB71B01E95FEA987EF538F300EDB7B8211698B5190AC5214C681EA39C78CA5EAA17AE994CDAEA07C186342E50A85A5FF292E19922286A96C42B4A998420C9CF0743F36ABF758E695166F04E0FADF9922C48188BB0F5AF0FCF2072C409AB2128ACA60E2780EB6DC1F12A979D85AE7484DA9F13534FD77C57554DC1BE883F1D1551E1FD750B03708D3FBAC87677100EABFDEAFD6DCAD15D1B7B7DC57928916F2EB68C8DC169274401E67A1CDD8E6A0F7122D6733A4FA86AC099446522C9E0ED60E244530A5DEE67E2A07E802145B783C59B9D873BB21A40477A307A84E2BF35EB1A66C42E18B8227B95623B1BB21590AEFF8D36C4B0EF6168D667037DC52B88C6F2FC5C1B1C232E2516B213B5F54A757880AF90ED73FE076A1C0613FF93415EC517632B7D10E2C1B7AB67B4DD50EF4E04C8910E088B8800DD50D04E276171C45C2B755BDE1BED2E740FA6D28D52130C6A6EB8BCFC3B3FFB815B8F82C1DCBF1320AE1B8627B2A893407159B8A1C684051E37A47E61776B9A80A53D3F41473A719B0D60CF8C25546C1EA4878362DA2C29F94617FEDDEC531A1D8E59BE133C033C0C85E3A476F7D0A27216E269A410A9EB7ED154A974C434C06024472D896D6EE24472D915C0C6544DFF73A0AE32C1365042906C54545EFC28EE2B272AE7A3D9F04EE7F975B1B1C04587CB42130421E026E58E45F27FA3ED02D8121F48C82587A968D0DC14C3C84E10F853D486DF6B3D997D5958BBB2A97F57CCC7702BB29EA359BD62E2ACF5A77D16E3A9B0B82BB94B3F6E75871D44D873E0A5BCF7BBF4131A0A9551FC1F504D25CAD2998FC213CD999A8F031667856E67E3FBC74D400C14F7C826FB676FB9735DDFCEDEC534E920386FD7CA810028DEDFE6902C65AC329753AE2AB7D8F63C154FDFF0FEEEB4E6C9026C4508C5A7973B1DC6746FB75E819E976DED75E6473D4DB7E6F018F587EAEA112080CEB7CD91CFC001763CACFCAD115D80246935D97DCD5820D3BD28FCB3582AB2AC40500C7636CC6A69A5542ED845103B21E0FBD05914C734F7048974298776CD8C5E8F2B018469668660ADD8211E3878F5A4F3E40A9BB27B846276F505718973E6AC5B15C0E1AC01D316F93CEF58D5E360909BC374276B4345011BED44C5F2159D5CDE2E0CFB4CDFECB51D676A46055D6C6A7F733E05D5D1B13B5F6F49FD435BADFA5965AB32275F8434C05E0DFF1DF18E5BD575F42F35B24A6E7C5A8A3BD4132AE96D67F2938738904D6C7C6DF612DFA2EB64FE9505EE47CD708D69222D24B5E109C36D4B7044C2BBC9E3554693915119737BAE15D7BA6B65C43E73D4552442D893ED78E1C08BF91231E5F1780079CE01A9C86FA8EC38A12EDEA62243F188AD182A988E7364BF1DEBD958121179346E0C4D164739C35D4D51D5E84587A5636E8FBBE116AC3F39AD92A5A9458C98CC56396D37519C7D229CCF732C254C8AB46101FB79038F06DD00EF23DE921FEF86CC17A7F6A17EDACA42B4910FC8119D1B5FC2DEE2D365BCDB7DCBB1281EAD990230915F8CAFB11E3C9D00E723DB001723B1DD7CF94568417EDEC07398A67FF7B27FF28F778DFF71C349871097BAD91D16688300834E5113573B982AC37FA2C75EF807FC7E616A92C386E6E49759B428556CACFD9E73A91F8EBB1DEE8863DA836EFB161714C68B3CE44034E0823A86C7B5AEF4B7DB7708E04DE158A94D1835573F7E973FF19DCFA95764B0263DFAC46DF55F39C3C98602BFCA12953A300F4702587EC845C65F40C46298008B1AFFC5CC4436516B74B9C740E05601CCF67F3E913F4E53DFFF4776E33EAD531B0DEE7C43692B99D6B4EFE9354675712BAA24235B62B64AC5D3EA1D1E7CBD1B14F67B45BC07F78E4F12E322C4060CA3A6D8F5323FE4B6239F5B01D4381D31F0610209EE83E3E726121E543525F10468006C0FBFF6F6E93E62ECE69E2A98F4E4CEABAC0DE867CE60EDA11C25DF160F367BAF6E54987F678F43847465D5AD1D9947CFA888CBA92B45D1A367890ABAC3FBD50F3E7ED4AF97C1F29C927C339C973A1009ECBB9F6954D4EB8FB30601C5052E7608F6EE48CE00B246E9F47A3E5991F250BF7506BD577C41F14E41570DF2C50E8087BB912E84C5F4E8C51A9870D027029809133FDB22F311E9A71F61CB71F6981D71F45AA80BC922777E655B63E6C14C8706BAA424B9C8C7CFFCC38DEAD82FD95D916E71AA3825B95088698A5C03B18B766299E4CCF71C4A959724308BC2AEDC7AF7C630B92A8B5CACD810A504A261DA7A527EBE24D23985809A5BE891C48926458E0B2890E5FA9695D5F48AF59CF21C37D150433BA777724AA7BDEDB170156BB3A6D9E56FBADA473B279996DEC61E80408636BF546F967FB0AF8ED9E5081F0A4A8B532E40D0C4BC55AC7A4F37F603CB6426B5D36B0B34D231C9AA83666B9C63DF5F497A67B0FB040D106C01BE00D0B1A3E4291F2156D1E637A3A9A766794D683AE106B639B5213337F8A1FBD924F7B4D9566A7C0A009B8E1925DC2FBC1FB3385A42FBB868388BBE6EF55942F9D5ED2C30992FFE514EEFA7F145E150F94C001E8D08904FC19401E871379B79AC79F39A7EF9E738BECA86C9078A6FCB62CD1D84E23708AD98FD2E759E7C841C40A2241DDF864D75B400CB5C6B03502D016C491615A05316F9586414694A67DE2C4D509025B1671FAEFF5B5CD262FBAB6234673E7D7D062016CE7D4B54C813A1AA10E6C1859C8EEEFDB81A138B2378EFB821DA5BAD135FE4E65363AF136F8E6C6D1640D4F8FF491B0FABF3E4D5668E931E39B5928E6ED7792719222E479FBCC018D755068E3C5C66E305E9C52BAAD0019B35B21F1A3B2F1DCEE98B9D0FB8E28E95CCD36838D65282B87F03D0CD5C096164B2C24E149D9128976688518790B30C087F9786C7F8F2E797082D839467C3AE8F076EC973D333515D12CA3CF9C50772AA3434829AB45B1F63EEE12769812B95F7C8ED33E5CBB49851142737562E9EED8ABD525D4C9423CD48BAF00B58343AD12F3EDD0D55A8895974FD99201CB1A0AE42EFC728A3D0D691B78FB85D3FB9210AC0D75B0BEC391AF0731921DF2E1DA5CA56FC6A68A0FE99525B85635665E0F290B98018201558C29F197100ECD37B9112924F60707E4B30BCBC5B7187A09DA0628FAFCCD930F545570FA835D911C9E35B1B73ADF15F7432ABAB1682201A93EDC842400381C645BFC729C8DE1C06AA2171EAACD88E919F9CA772EBEAFEF4D616FA5C973963181807844A3C84A57767F9AACB6BBCAD3FA24333D585C70737C7F8486888D919FA1A9B2C1D2DEDFF7FD1422536D8B8D9CABB0DCEC000B141C31393D4249566587B1D3DAE7FEFF000000000000000000000000000000000B232E40", + // ).unwrap(); + // + // let sig = MLDSA44::sign_mu_deterministic(&sk, mu.as_slice().try_into().unwrap(), [0u8; 32]) + // .unwrap(); + // assert_eq!(&sig, expected_sig.as_slice()); + + // test to pin MLDSA87_GAMMA2_MINUS_BETA (produces the wrong sig val if MLDSA87_GAMMA2_MINUS_BETA is wrong) + // this can't be tested here because I don't have the seed for this KAT's sk + // let sk = MLDSA87PrivateKey::from_bytes(&hex::decode("381286DCCAFF85CC31536E8CFA4D6FFF74AA12D0004D0BC826F54D3EF0F632090C06B8D3D562D3EA6C5D9E486AEE773AB7CB8A08382A055C6339BC650523ABA7176BB4169C6294FC52FEC2E43BA13210D3CAF23124DF2D0B4F7BE6327B48DE145071503F0E9EAEF1136A0005F6F5B03FCAD1AF83C10EA38D288934417E86F3D69CB69044064090063248C6895AB0019B080D99189012892CE008495A0609031005C42668C0C64540C80D80164D58388A18A40C0804720AB55020110293B09061348D4AC40182006E089750DBB02823938CA28688421000511066932200D8A2805BB05003255023064D9B184E83448282148103454A540020DA048ECCC24011904D6348801C004244300922A6314B860C0A4985A1184C18888112442D531690E280249486488C300909997119030DC09820E20664618089E4B01041208484349023058601A83123A63103050464226954006C1AA930884052A082402221690449411A83050B278AC4003200B521CC345083A20D60C84019330E00162941A82DCB446592844C024631611608E2842988023121B4844B288921187188C0011103201238280BA6112303490107629C9625C416445236708338929022084AC60C1B00648202324024315B867100104DD1228E58404C1082119AC245DBB204D0084E5BA20D20245159342821B004C8166DD3260419B10100486010A90CD122489914404C288624080E1B436C49A00C59942051108CA2466822A1714A982140366E24A1251C3421E3060A91C40098185223865114C38DD2002D00B03189A8409B940860A0701898488AA4449000621295280C10055292705A260A1C80211913281339421C0146E31030D9848C13202D99462E0C10881384302431712447418132860B948110112C5838898314825110711A982904260C599230102072C23666CAB669E406861C8469843012C49421241820DA12520BC3214B900DDBA004118808DCB465123085C1448E1C11859226128910840B1864E2286998880C90A6011B08495C180261C88DA1184621C109199389244925503625198490922030E28651C49685CA460410456A1B3862632809A202641BB341CB369054262C1C054E1042718348649A4605A2086041A2900A110151366CC49208C0C444A1C080D9408404C0708032851C43608CC4309CA6800A404E63481019B830CAB82944160461348E84184A24181013019188362C93246612C06463961000C3909B9600438685E1188E18C6059CA064844429108445C9000281266AE11804E01004591426E3328023C405E198710C16328896640C33825B2245C9082619236E44224C12126AE4B811C98845100732DA40825BB2316392895C1826218265639820D30022802220DB9624E29440E2042D2432424832295828461A9145D2988D21999010112D5A866D93A844CC42481282288C988422A791249001113270104045E12610A11411A3820D0BB64DE138019A046124C76412496E1BA004A3B08C49026E5C0069214144C8082913C83018940560228A0AC9258286444A102509361018C32111302A1BB5092047614C10511A422699A40041426ED40681DCB48122020A00336D53C284138751D9C851C238325C809101174013366D64A86019962022A38858B02C08078002170ACBA670213805D0222624A640A1A08D181350E0B42CE0225001A6886444011B29890B000092026C48B62508B60060926019953110B311D022215B006C80B008D18205133924DBC049544820C318069C102AC888645A248018C04D0A038010A891A4086461880C0B002A02B98D24220EDB246D21C76DA4126A58026190B2311935858B462102C144C9864998B2641210486110899896850816050B1980E2C6080886610B040D1A1880A4B80104C52564449021109291202863242120066A4CA208190222529491120886933206C49211CA000461162A00000E43022E98928881228E0C456511882102849149C21010920D8B126E10B42014234101A521A2C220813291D2344580324A1C29824906824B2060001060D0322A43208050342D1013010A094ED0C06448262050886013A07000037114948420A30D21055282A8911149125A342CDC880849C8288306650B16501489048C3626A408709C908DA0100AC90060D046718BA208190760CB8085A1008E4A222A01858192844C98B22164C8000199887457577111119A9C0D695E5008224F1F8D3B293D5067D12EA5D9B1ACD120060BE64483AA869B34C317DC13C55FA59FB1939381A21B8E043795F95E4ECB1592217A42B07F3C9731ED0ABFB2CD487CDCF6F956D5DECAA394378DCCB079CE795E96A9BDE857502A1EE1DBB3D6400EFE25F81EA9AA71A2124C05A0E97A271BF8B166385A3AB1E01E235AF62546933E80906B47627F0DD508B22CD0FB34DC2459B21C1437DDBEF93F603CEF19929437CDC85AEA07303F84662E64973F65BA31B0142FA866EBD1CD858AC538E9A598EB1A2A80D463E84125461926FD53DA52C9D53EC1C8EE2E5FC63A7D3259FC69C7A973421A9B4B3BCF4E938450C17ED123D7428C6D17CBC6EDCC4673750B726A36E187A42221BBE039A9EAE4A01528B5CA3074A7448193E8F29C65AB065A551FA7827ADD240C06AAD74692AEE6C44BDD6EAB9F0DC88D3564C3EA09AEA275507B8B085DC4B3BBA5EE7E9A35A3F25FBF2855A76383DC498935BEFC14D32A0EF90EB2F604C0DADFA4B5C7D8FD79BAA318F68B2A529944F87CC4FA80F60C4BC351A95E00EFD0A0D8ECD2F998F30030CF6D9BEEF2F7D63077DBAFC7AD5E7667F8FA671E5B69BC0B0435C80B28ED28D8D884FD60766E030A570282D8110419820D437FBA52C82CC99C94631B5C8AA9B681DEC98957A2E19C6123721B0DD4722D62F7722B3678E5752DD91C4D0EF6A932960EC1312724C56F58F21D0BFCA70340DD4C48ACC5606387663313472E8194B65FCC048A858D6480DD9C2B83841E85205E6C5E9A95776BA29835BBBF8E567200C84E5499CDFBA85355A2067A170BB5897889A73C204CA5A93118E51A4595281A80B952CB35D5589673270142CDE60402FCA4C8D9E437134DA79E7944522FB72D35F3FDFF2C15D4585AF10C3B64A7A40F2418A336F4EE66CCB98AB0D1B082C62802C87A9778B86D9F304847A76420507978FAC7987E8BF667A8E40F15FA1D9B0DD6299458BA196D24E9C6906C26D04708B08F99FDD9D110094D5B36F48213B213C66F5F55F97D4BFB500157E03A074AF090B7609204A71CD1642D11BF34C76400C124D55CF7A80DA98FFCF52AB2B229720357ED071616BD8E03D7276561ADA1A746C4EC37C0B21F964DCE7B1AA303A9CD7CB8108D7E18E32DB22BCB89FB7E6408324ABF03E296572648FD2E335CD4463A64EC5CBDC8AD7953618D8B2E309F04A74302562FDE3CFFE805C4BB4C661EF131D37674AD6F9C5BBF2290727AA67EA3848C48E3C3CA5A35009FB91C0CC383005604C096422407F4337C17D63D885592A5F15F9851F5BF27EB5AB5095B95AC69C2F36A1664B0AE71E4BB131C3A576B41155A2F997BCBCA494584788ACF2F5CCBAE28D09E69F3A39C2CC394B3465358E1F2A23D2E8A3083E3E26E90121553309A1B8E24932CB64FE659EBCF38AF0D1A8819FDA08CF4F2B03D79208061E113F9B89A8B9563E5163C6CBCC9BC2048C15E2AE39CC2B64ABD6DD6CCE5D01ACCD8CC08A4F266491446C1CB9337DAEEFC6777CBEB3291F60CBCC3194706492B7F953009DD5E70245180651DA918647F05A2902F4719F6D97CF3AF5D855FD1A1C76C29910D518771C604AD7B802E264F39BAF16AD772B0EC9DA32FCA34A85FE2E2447CAF2EE925EFF5A442EE9DA327C38EAA22B8E67DE4A7AFC312CBFDEB44856F09015237E59D26244631A8B14AFED134805DEF32FF49058C72C2A7A4B77518EE2B162DAF6B90F1C96AF5225453C5E4D8CD401C6FE1C626D4CA037F380BF288DC428B9680B595E10BDDC9D8A2D2BC85DC316BD9239D557535F66B3802FFEA1095DD4DC2F1A9A1E6190619BC5A2E063EF8C95066F5AF92B3D99A418E26C68F8AB464374FF3A789F07B2C5A99084C7275DDC481BC1BBBE8BD6171DABEC4A5B01F115EBFADAB26C9AC17A4D4EBE37BC294F0ADE593A0DD4AAC258D106457748FDF0059752442E4B96F1294D6D92F6D9657E131D0288EB3AED6C89390E05DC84FA5EC0EAE33F4DA4DF2A7614FF1B4ADBBBC9ACE849366AB82D5902BF7C5C66C095BD92DABB145BCDF8A744FAD2FCF44AD868C349A6DF2F509EE92687123E39DDA96A735AAF57C09F5EEEDFE3CFBC0B9C05B5D51E2F66F8C0E77AB0302AE603DEC8CFCC79485948C012BFDF7D0B9E8FF0D6545EBC781054183785D95173510E65E0E52CEDBB241D8E23C997DA523B974C53ED07A8DA63958768C9520DA7EE6B7093721EDD8633B6192E12D19065F63EB79504B92C7BC31F176DBF4FF21F5DDE5E414FE6555C8D76A2A77B3B3CE1EF545CFF4E776EF9B440506392ECBDED301490678328FFA2D83DE6099B750C3C99A9797A14FA372A57BB2CB72BDBFC640144D1C100899E2E12F2A23147C0B30E4F162184E1E69C1FA770B46D213F52CB4B36864289FCC3DAD1D54216EF9D9FC81B709D6045DCE41E91E5A0C8B5518EEAAE07D2574DB1D53ACA5B2A4A2FCD6237388A86B4C58B4200511103CCCB2420E62789C13F87C82A90751E121788274C028D2106D5FBB0D9D3128618FCD7DC5B4801C01A2410ADA95A7CA902A8F03E2D538D783AFEE2B7DC674C1A71DCAFCDFFCCB00273B942D79A5435DA1CFB2C44C3FB3A4E221AA1B2868EC9F4FA43A522562AAC34BBDA07D337A12748A1B5120D176CD117110D4A7061BA6AEF3EFBAD21E998B78192214380FD84A5D08F9B11282A15BD9226AA54F019213AB6C0BFE8291ADF09B680A16770EAFB2B7042274EEB896DC7B6903113862B1BD5C523787693E65B6C2A85845768A3BC54DD7A02C4ECE6CA3DBAD2D83DC65CA1DBF7C3744EE4E4748507DCE7F913F4332CF5214D8EFC1B4E67C86AEDF09D545C354FFE6B8A4B8F15E765A8F8CB13B24062A17084C52E82964CF6657C808326ABAAFF60545BADEA8A44DAF0A038EDFA6F4A66C57C721A6ED2E8736EF92F4BB8D230DA58DAD187ABF58DFD17FD11A9764D3608D288AA2EF3F1DE94DD7DF07E829D5D07CAB1FFADDDB4DD460D9FF3DAAE1D918AF2AE195B0F95CFD4E1543B4920B20CC808C6473C58933B370C45E47416E973F42FE6FC9C4FB5FA95DDE7D8325A8837648625F722EF95B0D08803FF0299E6A8108122A1C7172FA96ABB263240FC560CF3E1BFFCB2ACA913A9788D5C02EA0EFAD07BEF958A144B15A902AFD13A0AF976044065068C77100066F68982C0B98BB77F814AB624EED9C84531969DB1257C7AEC11DC682434809C7594A02116D6840DD99630629C150CB0CEFDA1E851F35C50CEEE6689B8450E6E9C4F8DAC4F487AE4DFCC60B8641A780BBD724036C21B38BEB1C8ED30EE9AA03DF899678FFFECE5200DD2270A9332B9557E577BB7E5B3378E4512F0BF439CA3BC16E8575DC46B2E497988C7C6F4EF529A730D7D418FF548AA7710F6F3230503D92A8B6228FD19CDC53F1A40165BA1F3EF1FC68BD5717AAF0810AA56AB90604BC0DF466D92403C1B9CCE0B2662D415EA69A1BE094AA5583DFC472480EE0430A93B21BE5B1BD4E27026DF5ACBE51D891B0E49F70DDE93FF43CE43A4F9B82AB951DE14A8A9BF4650FC38D769F4850688103D0D3E4A9BA645DAEC785A571D573ED6ABB22D987C2B52C5102CAB5B14610196B8D71DE69103E59B5C2A682390290B508A693CF87AD0214DC8DF2541FFAC89B38E19D703AC52D1624216FAB0227BC1045A34FD658111F92DB7B97FDF1CDC56FF7CABDA8BC26EF8347A92A2D8A3C56C150C5E3094F3D34D4CEC3C371602907419D8259DA0C74E32F63A9225852B5C9208785E56D0F4DB3349F5DBD06A10397965453D4E70D2E1E7FE56AD98143720BF25FD8204497D0A5EE1F37BD8AD4364D9BAAF2BE2BF519316D1314E0D4801CB90FE52F66A82FCB4708EFDE4FD45F70C38E70B59206C221B42E840B72FE1FD0DE5C7B0B8C0BD1B6561899FC5771764733108B69F81888FDA9E8CF60044F3BDD3289637F6ACBAA5B9B1145DFF8CE247B438320535C514BCBD445146BC00EF8E1DF3CDE218775DBB135FDBE92B7F303B1C344FDDC2E381420544DC9694DBB142704A0F092BA281537176F17B0C1561F0BE320D79677887DEE7C27E01232BAD43D49D9D1FC1A6D8C887443FDD756EE7561C24FFE0FAEEE66DE9A6F05CF687EF4CF456C7DD624DF017E11F66231793FE81C958AF0DD89C0D088B17DF94304659B79E9F9C5196BC1A9F22CB5698C751B829427CFB437B16FE1E3960A70B223E7C46CFD9F1C6195386A170AA07074D7BF9F634CD834939F8520B73D2648D1994CE80FC17E549DFC87CC6164A9CFD77E6E1968D9C9B582FB70736B6163D1E01C4632D594E3A3532E61E1CE87FE5AB5A2E11F98224E95D7136B34D814F138CC5C557690B57DF3EFCE187CE9A01D1CDDE645DD10814E3B3FDD69947A0746C68D9F77B9AFC61474C55CFF7EAB24AA5643E6B451A4C6D84C2E6E3D7015C6F00FB264D55D8B2CFF5F87DB3050E7DA34F7612EE116A088180BD0E1F3AA1BD63B2CC029B3E96C9EFF4C2D1A2C53F25084503639819E5A6E73BC07EFA5212CD9075A6547710A1237AC4991445ECC76D0C11BA557E0BDBD7651A4A6E66E3F9DBCD532C2301BDBDF63307AB425E6AB902164925BC424C5E7B2892DBBED51E0AB5BF33976243D5AB4DB7272EC292D1DBFC494372B51526C9DC1380C3BA563D2CCFA3A20EE7F800ED8C2A7D199D2EA6F1FF7F4B6B4AC51041C974D740FE7E1EA1D7311B81E5ADBB600CDF62BE0").unwrap()).unwrap(); + // // because this is from the bc-test-data with busted mu values, need to fix the (busted) mu + // let mu = hex::decode("d6961b5a9cc085939e493d4cfabfabb4442604d4058c4bfea3fb5c25cd7a3e4551b34bd9735be6e05925132e0aac4aa374534867289a6af4503baf449e6f6b73").unwrap(); + // let expected_sig = hex::decode( + // "1A9FF550C3F682A043D145B8A8ED2A2C2F92914E1D3E4F9C52EFD2A478EB028ED77C2E9C9450909F7A4032EA4059B51EAD6D9F61A8EA867AFCB794CA32AAD3180675DA90252E11952E936F2ECC62E1386BDCCD12DC22D23A2E21FCB009486376C85E2CA99C51D8F68AE93506120BEF1CCBC38E1A5DDF940E37DD0EB650503B969F9760D0F19448164FDBF0983077DA415648D70289BF6163C32DCC6AA8CC3B7ACF261237EC70D216FD157C1ACA6BA135470CCB4FFDC1710644AC9DD5A67E037FDBFDCE2A4627555D48AD1C8E537928B6A954FC006C5A34ED3483E2D241B618F59FD8481F69B6199E91A6545CFD485ED8665A82F75E36B27662562BC0D729D19426CD4AC19DAD71AE298FCFC278BB3B89B28E7713BAB58C4DC7BEEFA8444B6AC5CBF252F71CC7146D6EB43E0E9981C971BA9EA12BDD4BED8C20CB4BF91E805F93307AA6DEDFD083307CCF9FD540E674A40D68DB0538C8598CEE8A7186B65C4A71A4B053CACCA88887F96101547029FC74765DDE92ED69956B30C09BECEDE6DD947A7C25FF087C30AE4DACDB9496C51EEAC61C5C6B9D3AA48E255999A1F76811455B567188F26778D8C62758E1A9FA884CBCF72E04C7A6B4BDB13B208158C67D5B6A2CF60F8D27FDFE5D2E37947203D0C204D7937A10420DED707F73DE8771F7866B9537E1265101EDDC3D7C192A3B209B0E5A0D72495F1AB0C776FBBE892DA53A0638F98392E816B7C0DF714DDE62B473DEDE5876555112A554BA9EB37A363D78290C0B8CA56DDB600F0773CDF68898876BD34714F5F6C1FD38D50FDDFE778621133F14A1270E6C20E089753269D5B590318AD9AAEBD4BADA6EE5C48044CB11CFD14D153A8EE69335EA6D0C7435C33B94CE2C6E97F1D3CB80A70F66FD06F0A480FEE71B7B6A6E2DC7741269D85F05EAFDBD22D5DF71AFD9AB2D24F24729DF492926525F03336B515CCA54BB08989C16D42C1DEF1996E99659BB2F9F96004C258EF140E5306FD8CA549C99267B237584C733921192E89C8EBFB9934C3335B722F7E3EB519924A3F02A2B7E08D8C23F49C1F1262968D77887C1DB0D9941B203882C1703A8B345503E0E3CA472D7D7E11330D8B53704B8C59D12CF280857065031BA223B0D59C87C85AD214339DB60D6A2ECB4B908C07F0E2B285D0DEAA8CBF7B2BBCED1A3AA443C9580D1C7A6C5D618F6E1C5CB60E81F8E4397373850F5C18FECB489261932CCABA6CF42840DEB52A62CF0D829E33C2D4685ACB4F5CDD3A1B98D2777C09AF4DD926297E6B22037A37026062A6A42187976758B7406032B50A42D9028BFF12737F2E2CD1BAE8F78D10D87AAD2FCB4524B083D5542151A520A5B3C850F2879760875B0C034D714B7890B9BB7E86FFD946C9C4E583D0B7B1AE0A29553C50C3C4C30AEA7DE67BB9DC08E7845BA2ED6B6D6BBB1F65DAB6D269CE4B625B05A4BA5BB7272E4E7976AE6ACBE1AD43CD73DBBD215E9F603363919802497A7DC9B52C084BCD5E441C0B11FBB53142E56D1F182686614C97233A176741FE46E2F5BB749BE5A34790FF242331BA007EA53291487C96F998A2927C25863030DB6AD1860252DC2834DC3612E84F2499F39856DA4EADD223C5D2CC610026E54657ED6B9DD718DE6299979F76C0BED0933CE1F820D65F32614CE3DDFEF863A1A894534A9B93B5A0D54EBE30094BA323C5874BADBAAC2485D83363793D2CB96E129CB6C46C8A504797276C313034068539FCA7FD5929151B1D38F93243B93A772FCE64381534F9ECAF57899397E9DF6DF6CB9619B7CEC5EC498B020E708889C762776EA9436BD86B23550FD3F5D61CBDA5D1B40C629662D8D987B208B6A2015ACED52288A555E149688737323B5B5B0E6EDBE1D70A1D127113A39DAAABD281FC18109FDCD90C49FB5647E69A8EA116AAAAB1E0E5D73286DE32437B41ABAB779B20BDEDCD92F6D6D683587D33228D4A83DCCAFAFD6334680F4F9F370A5EDF4A70683BBEDBC064021DFEC2E47034275B8416D085B4086BB374FFF58E2F33E0D0920F638C9752098207C5E28341D8AD70DCA78CA47A2268F28B3350DD69B2EE15A7AF278C01EBE1DFAA8AD4739F7E5E408E2A39FCE7B8FCA51EDD592B8D2766FED9919425EC7CD80C4836795C924222D73AC0B380E00BFD20ABB704959CCA10B5EFFD9A0F72ADC823C74E90E6E95B2A3DE9D8B9F1C1A161D64E7393B259990D2BB4636B02A216ED12D2AA0B5E26C0BFB7D34B3230C6090DF5422F2A6E80DC8DE9DA3B5A1CDA4971E96C7DBB7F6992A60AD02B14C5E7CD6C9977DA0D3DA84CB07810325581F9D7BED0BEEC12E6C30338D903BF0F655EE8AF3779C87265911032433690CA981D0CCBC4F98869F316B6918B3135D405B34A3C7069112FF17E44554ECBEA8DC61269EFB5E58EF2AB1FEAA15B426879A344CE7DCE621082370F0F4F9B0E2602E942A1BB156B6DCEA3D9DD2B4FDE908E91B1913CC8BB6FB46961B9669EDEB713843C2ADB164EABB9D9615C4AB97AD17143891EEB6529C1C0E1542C0A36F70E93DA415D9ECACD6DAA5465840084C99F8F68CD236A8EE23B8A14EEB186909F6A9A4B1D1D2360DD548E57CBBF41C8A86FBCA6679E55341BAEA6F2F01898A7DE07E4BD91F6674DB7B7FD0667154DF75E543DABDDDBB4283BA0F4D8E38108BBD4BD65B82D9C84F0D8CD2E559D01E399C621056323B8CC79C53B8B8C9DEABAD2C0BE1DFC067EFCE38C591287D61545C36F36B81E7ACA5F00A68926C61090ABAA6B48F19DF830F4D94E65673CA03F50ECE3FA3BF6C235498D402E4B0521135255D85000260421C2D28D0D560C535CE6CBAA62E0491C5B305352CF1BA8E8A33F76F22D6CD72A15D07D6930DCB507EB88AFEDA15E810FC728602312B3053CFD2D8314DDE6A63083248480680A2882E173B8D89C14646FBD844A5C790DBA9B842A41F48B6BED1FB4AFE6826F95082A33C9568A40AA4B5B40CF1E723F90AF1A1DC5FE08C91B5D24B8BC1FFABA36B308A6C2184DBFC3B3D36916B2B744239D548F4F5A05254EC548F3437520609C5E562C82BEB9D3ECF1C1F9AF5E84DF9AEA3998B23D51741CDD936C24E23F1DEC3DC58BAE1FEF9A2F1B71A2604B699E212AFD349944DB53A39554CFA94E0EB747722674707A6F99F0204EE68AA20094DE58AC97F733FA76C928DBD668F80584F6FEDB7218BA74E126A0316B5FCF38F970484117B88BC8545ED9FD67466E7777E66B0F41F593E46135665096DA1CC75C90437A01379925F3327983207CE482F6AE3780B1265D6054CD2AE6B0418D2857D59FA7E94FC32AB5E94CD2C83452F420C8861F55B0A5A6BDC8C5C4157B33D6AE11C3C9C2E8919A3D09B9ED8FC09594D365A7E85F3D9647E324116518F6CDA1ADA250F194A87AFCE0D9D60B768AFFF8FCC7057AC73FB520DC707F9BC7408EFBCC3F85D5F618941441CED77C09B3E697141DE2AD6C3A4551884EE1E88D196E1AEA08285D47D81C219BE827D07891F8273BA4B3A26C3813EEA482674EA8EB4FCFBF04EC0C9428A7686816B53EE0749FDE60F50460B672D58C06F103304C28908FCD079A12574B170ABFC7E1F39FED5131D0672ECB3983810C316DD38E2FEF6DC99FB7FB5315F2D6496CB45E1385EABB69537628D9DFB0FAC95B5E5D4B05488B18E0068B0407CC737B0E711A0179EEDA1C7FAED33AFC5AF4B8C318B98244C97255563BAD20784AADE6A618388FFAE1C286A4C5EB891E236FE57FF414D619E8BAFFC84C225C59D02534721490A50BD39C163A1EEB6D811646E54D426AE9B3449C3E428ECEAC6E23F6CBD8EA06D9682385554DEC853C5C0ECAF26F5CF5BC6EABC4A957215FC497084E9A8CAFC00ACD228B4F8245F9230FAF8306635DB89663C5D9A31C648948500FBE0771E2BF15C52395468CF50C6C8953DBB6DE252E6649C6D2D060EE34F53960B5B7EA0981B7BEF3D0A1D64853D9EE06EC4D73F4A75DAC5E5A2B59682E6B8293E74ADA3322C1204FA66C6E8B5EAFD796305AA884249D00E3DAB6267A87844D595B3BEC430BB416BD4A4F38505F046425ECA84495DC0928DC4D9FA18BFB800C1B0578B2EECE2B97153A1C296E36036F9366E3443B236E4E124F82EC405CE2ACA48DF43E543C514727CE182B34D1131E366E394D07F9C72022FAD6E4AEB12CD675E7EB837A7EB13BF4D9DE553DD19DFBD29A6E2157B58D893F3E4B574AF01F103989D8FE2FAEE269BAED7094BE606678719E6E82DA97D3DB43B3FD72863180E1B4D7BE55A721301B1D81963BD37C3259D55FA6D43FBC217A77B38B83CEAA4853A921EC7F138F666BFBBFA49B4672DDBBF1CEC212B530771A39EA244B547F28375936A3E8F692216358A944542AF2D559D6FA216A5CC60DBC892D2BF38CAF1745F5CF6336754A21378F25DE5F472FDD8A1B33189B5134647F7965FE0E573C508B61980DF059B5D9B3AFFCE632230FE873F6462E533D4528DB8B03DD464D3020B5DC22525AE33AE1FC345DC2486D5647863ECE3DD49EAE7A5AE128AD8E39065F77EEE3456653E3FDA5135A45A365B8043F2F019825BA9CEBDBA96C8EEE7A8544DBBC60218776455843E5EC61D9DFD7DF3ACD6DF5E130A5D2F958F5E26F3B1A80029EBFD13F7EB24301750EB8608E7FC73882797E2342A37DF45486ACA0603D411592A13934799D203ECCC197C0F103180D5A0C1CCC3BFA3CF1DB199ED934B4A5B52CA9BA66FB9C97D85B06977C83533F16F6B7F3D6E5945B802362E7ED9C42E873017EA4E0C9E7AC96087AA7FD269D363A8BE8FD39A0234CA842ED209865164D39C4EF592E7223F3C3684EAE0A2AC757D3CBAFB2E873C52B09BAA4788190EDA773D946A5CC67D59C6EDF9EF20394D2F40DF35D20E5F67E1891C6E61798B1A8343E22B7FDF274A996F679A4EAA1505DA4EFE1C4D94A69EAECE3DA05D6CFB63294D7433D9885EE18EC94FF1F23DD6FD9F99BBA2D5BEF46E0A477CC434F86B2FD67FD55B06AA2B698A70FAA1894E925BCBB2E9F229A1858E789920A1AFB5FCDFBD4D3F904F87FD863372CB7C103BE365AD8848141AEE2534D42BCBF5A954F8BF62DF7C29F9ECAEAE7543B56E7188D440A0BB4760FD0ADC019796611E4A31152849818CCF0B146C9CCBCF4FBED7969BE81C49D92FB26A63249DB99EE6CBE935677AB40F50A267815FFBA5BFD7A72C5EBEE8DF1DDF713516F2E4550C9E74FB1C7E2CBA3ABCB91E315A1ABC8E9D48CED3971A0B73DFAE6955CF3CFDD925D86B4F3FD182327599379F9E13D7EA5E405C335B1D95B6DDE4DE69C492F98F51974BCB66C317439653A9014166C922E61150FF9902F9C22C29B7F15A27121BEB39835D835CB7135C3992E82C765A9131485893DC7C853937E1F0C9E0EB30E0502224195E318AB98C56E4211C926240BDB95A1C9345BF9AC10E2482950974895268C12DD92D0EC7E941A162A57106C0E2A3D36376488EB22694D4F68C0FD03FC8ABE4F5EDDF3E47A9E9F7612C2ADD9F847309DDBAD4DC71C3EB2838F29CAC79E8AAF3E88865C8C4FC5FDDDD5EE0CEAC09985CCCD5A5AA6D009BCE39AF3F8C8AF865F584AAF03FB8431F6BEBDA7EE1F1F0B3D672BC6840F3417F497CDE3CD3DC2A214C2C895F61A20A7CB184DC6DEC73002923A6B50B671329F2A16E611E0099B5F0438AFBDD2285D1AA097DD74C4E5D7CC6F75E4313C67E91C71AA49B5048780C0331BDB8917247BF059D9CCDF6FDC885C2950C6FB84BA8ABC463C2DD67629F8C28600081C10C75EE88DB112C432A516983A933A584A957646E2B5B4F9255055A0558574D46EE3E074CFF5FAEBFEAB61FFC994E5B9F46E16552590257B6C4FBF80597B11A705BA730FA495D31FCB0B3E568FA264A6C4512E9B598162AC242437DD47A319FE31A5D9622343B71286BE7E2CCC6709978044170B6B7C8F0F1D109B9D79F86C9A2B4E2758AA97569F47F242BC0233CC44E0E51A02EA805D15A30E193776174FA43787B621293CAA785B609AC5FB851A90647C6A35547AB985E5CCFF969EA452A047C5D7E6CC86404DE41FF4FDDD2B20474BEA70C843CE3A3B383E62F2BC234B4D4962275A3C73E530ED2CC8BF45CB2086FFAA5A299B5236A46066294D80B938A18D71B718B083E685173D10725DAEACCEAA6821A7C9362E5DA01DE8DA4DC54C4C7882E61056FCE7EDEE3A84E3414AA7055DAB55349D3DA50EE216C797C4E5B7D7A225D35E29B150F7499086442509ECC8240CE53A08032C97F6A07ACA7C599DDAE5E3B38E9D78FAF9FE804EA5AD23DE442794991D6633F3CE3D83B08F8FE5D862ECE02B8E710EDD552CE1CE465353A6A3FC014E60F0A8ACE5FD7E0A6E524FA7453E795B96C2DE5E2081460C3754DF5A782984D8CD5C7ED1200B45C45E58387EBA329B93F63143BE6D4CC911D305EAEB6685A07B1E4F982E17904B775F6255E8279FE7B5B445AA0523023313343478D9CA9B40E2048B2D1D637468C91D2EFFF0611164254646782C6D77197A1A7B2C9D5E0F0FD4E576E7782ED0C50ACBCD2192A5275808BEE000000000000000000000000000000090F16202A30353C", + // ).unwrap(); + // + // let sig = MLDSA87::sign_mu_deterministic(&sk, mu.as_slice().try_into().unwrap(), [0u8; 32]) + // .unwrap(); + // assert_eq!(&sig, expected_sig.as_slice()); } #[test] - fn sig_val_z_too_big() { + fn malformed_sig_val() { // This signature value was manually generated in the debugger to have a z containing several // coefficients of (1<<17) -1, which are just below GAMMA1, and in aggregate should cause check_norm() to fail. // ... a condition that generally does not show up with well-behaved inputs. @@ -425,9 +705,11 @@ mod mldsa_tests { let msg = b"The quick brown fox jumped over the lazy dog"; let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); @@ -443,11 +725,18 @@ mod mldsa_tests { Err(SignatureError::SignatureVerificationFailed) => (), _ => panic!("Expected verification to fail due to busted signature"), } + + // a signature of all 1's exercises the "leftover bytes" check in the sig_decode() + match MLDSA44::verify(&pk, msg, None, &[1u8; MLDSA44_SIG_LEN]) { + Err(SignatureError::SignatureVerificationFailed) => (), + _ => panic!("Expected verification to fail due to busted signature"), + } } /// Tests that no private data is displayed #[test] fn test_display() { + use bouncycastle_mldsa_lowmemory::Polynomial; // Polynomials (could) contain private data, // and therefore should be protected against accidental crash dumps: @@ -460,8 +749,6 @@ mod mldsa_tests { assert_eq!(format!("{:?}", p), "Polynomial (data masked)"); } - - #[test] fn keypair_consistency_check() { // this is common to all parameter sets, so I'll just test MLDSA44 @@ -473,11 +760,11 @@ mod mldsa_tests { // failure case: different but valid key let (pk2, sk2) = MLDSA44::keygen().unwrap(); match MLDSA44::keypair_consistency_check(&pk, &sk2) { - Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ }, + Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ } _ => panic!("Expected error for different key"), }; match MLDSA44::keypair_consistency_check(&pk2, &sk) { - Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ }, + Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ } _ => panic!("Expected error for different key"), }; @@ -486,7 +773,7 @@ mod mldsa_tests { pk_bytes[17] ^= 0x01; let pk2 = MLDSA44PublicKey::from_bytes(&pk_bytes).unwrap(); match MLDSA44::keypair_consistency_check(&pk2, &sk) { - Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ }, + Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ } _ => panic!("Expected error for different key"), }; @@ -494,7 +781,7 @@ mod mldsa_tests { sk_bytes[17] ^= 0x01; let sk2 = MLDSA44PrivateKey::from_bytes(&sk_bytes).unwrap(); match MLDSA44::keypair_consistency_check(&pk, &sk2) { - Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ }, + Err(SignatureError::ConsistencyCheckFailed()) => { /* good */ } _ => panic!("Expected error for different key"), }; } @@ -524,6 +811,92 @@ mod mldsa_tests { mb.do_update(b"jumped over the lazy dog"); let mu6 = mb.do_final(); assert_eq!(mu1, mu6); + + // test SerializedState for MuBuilder + + // serializing and resuming after init + let mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); + let serialized_state = mb.suspend(); + + let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); + mb_resumed.do_update(msg); + let mu_resumed = mb_resumed.do_final(); + assert_eq!(mu_resumed, mu1); + + // serializing and resuming partway through message consumption + let msg1 = b"The quick brown fox"; + let msg2 = b" jumped over the lazy dog"; + + let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); + mb.do_update(msg1); + + let serialized_state = mb.suspend(); + let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); + mb_resumed.do_update(msg2); + let mu_resumed2 = mb_resumed.do_final(); + assert_eq!(mu_resumed2, mu1); + } + + #[test] + fn mubuilder_suspendable_state() { + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableState; + + // `tr` is the 64-byte public-key hash that seeds mu computation; its exact value doesn't + // matter for exercising serialization, only that it is fixed across the comparison. + let tr = [0x42u8; 64]; + let msg = b"Colorless green ideas sleep furiously"; + + // Put the builder into a mid-stream state (some, but not all, of the message absorbed). + let mut mb = MuBuilder::do_init(&tr, None).unwrap(); + mb.do_update(&msg[..10]); + + // Generic trait-conformance tests (version header present, [0,0,0] and future versions + // rejected). + TestFrameworkSuspendableState::new().test(&mb); + + // Serialize the in-progress state, then finish the original. + let serialized_state = mb.clone().suspend(); + mb.do_update(&msg[10..]); + let expected_mu = mb.do_final(); + + // Rebuild from the serialized state, feed it the identical remaining input, and confirm it + // produces the same mu. + let mut from_state = MuBuilder::from_suspended(serialized_state).unwrap(); + from_state.do_update(&msg[10..]); + assert_eq!(expected_mu, from_state.do_final()); + + // Anchor correctness to the existing one-shot path: streaming must match compute_mu. + let one_shot = MuBuilder::compute_mu(&tr, msg, None).unwrap(); + assert_eq!(expected_mu, one_shot); + + // A corrupt SHAKE256 `squeezing` byte must be rejected. Layout of the delegated SHAKE256 + // state: 3 version bytes + variant tag(1) + [u64;25](200) + data_queue(192) + // + bits_in_queue(8) + squeezing(1) -> the squeezing byte is at offset 3 + 1 + 400. + let mut busted = serialized_state; + busted[3 + 1 + 400] = 42; + match MuBuilder::from_suspended(busted) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error for a corrupt squeezing byte"), + } + } + + #[test] + fn serializable_state_mubuilder_rejects_wrong_variant() { + use bouncycastle_core::traits::XOF; + use bouncycastle_sha3::SHAKE128; + + // A MuBuilder is always backed by SHAKE256. A serialized SHAKE128 state has the same length + // (both are SHA3-family states), so this would deserialize into the wrong sponge if the + // variant tag weren't checked -- SHAKE128 (tag 5) must be rejected by MuBuilder (SHAKE256, + // tag 6). + let mut shake128 = SHAKE128::new(); + shake128.absorb(b"Colorless green ideas sleep furiously"); + let serialized_128 = shake128.suspend(); + + match MuBuilder::from_suspended(serialized_128) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHAKE128 state into a MuBuilder"), + } } #[test] @@ -534,7 +907,6 @@ mod mldsa_tests { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, None).unwrap(); - let sig = MLDSA44::sign_mu(&sk, &mu).unwrap(); MLDSA44::verify(&pk, msg, None, &sig).unwrap(); diff --git a/crypto/mldsa-lowmemory/tests/wycheproof.rs b/crypto/mldsa-lowmemory/tests/wycheproof.rs new file mode 100644 index 0000000..9a95f45 --- /dev/null +++ b/crypto/mldsa-lowmemory/tests/wycheproof.rs @@ -0,0 +1,712 @@ +//! Test against the project wycheproof repo available at: +//! https://github.com/C2SP/wycheproof +//! Requires that the wycheproof repository is cloned and available for testing at "../wycheproof" +//! relative to the root of this git project. +//! +//! This test file exercises the following test sets: +//! +//! * mldsa_44_sign_seed_test +//! * mldsa_44_verify_test +//! * mldsa_65_sign_seed_test +//! * mldsa_65_verify_test +//! * mldsa_87_sign_seed_test +//! * mldsa_87_verify_test +//! +//! Note: the following test sets are not tested because this low memory implementation requires +//! seed-based private keys and has no functionality for operating on non-seed private keys. +//! +//! * mldsa_44_sign_noseed_test +//! * mldsa_65_sign_noseed_test +//! * mldsa_87_sign_noseed_test + +#![allow(dead_code)] + +use bouncycastle_core::errors::SignatureError; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterialTrait, KeyType}; +use bouncycastle_core::traits::{SecurityStrength, SignaturePublicKey, SignatureVerifier}; +use bouncycastle_hex as hex; +use bouncycastle_mldsa_lowmemory::{ + MLDSA44, MLDSA44PublicKey, MLDSA65, MLDSA65PublicKey, MLDSA87, MLDSA87PublicKey, + MLDSAPublicKeyTrait, MLDSATrait, MuBuilder, +}; + +#[cfg(test)] +mod wycheproof { + use crate::{MLDSASignSeedTestCase, MLDSAVerifyTestCase, ParameterSet}; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../wycheproof/testvectors_v1"; + const TEST_DATA_PATH: &str = "../wycheproof/testvectors_v1"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + fn mldsa_44_sign_seed_test() { + let contents = match get_test_data("mldsa_44_sign_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa44); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa44(); + } + + println!("mldsa_44_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_44_verify_test() { + let contents = match get_test_data("mldsa_44_verify_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa44); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa44(); + } + + println!("mldsa_44_verify_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_65_sign_seed_test() { + let contents = match get_test_data("mldsa_65_sign_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa65); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa65(); + } + + println!("mldsa_65_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_65_verify_test() { + let contents = match get_test_data("mldsa_65_verify_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa65); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa65(); + } + + println!("mldsa_65_verify_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_87_sign_seed_test() { + let contents = match get_test_data("mldsa_87_sign_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa87); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa87(); + } + + println!("mldsa_87_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_87_verify_test() { + let contents = match get_test_data("mldsa_87_verify_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa87); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa87(); + } + + println!("mldsa_87_verify_test: all {} test cases passed.", num_test_cases); + } +} + +/* Structs for holding test data */ + +#[derive(Clone, Debug, PartialEq)] +enum ParameterSet { + Mldsa44, + Mldsa65, + Mldsa87, +} + +#[derive(Clone)] +struct MLDSASignSeedTestCase { + parameter_set: ParameterSet, + // testGroup-level fields, copied onto every test case in the group + private_seed: String, + public_key: String, + // test-level fields + tc_id: u32, + comment: String, + msg: Option, + mu: String, + ctx: Option, + sig: String, + result: String, +} + +impl MLDSASignSeedTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + private_seed: String::new(), + public_key: String::new(), + tc_id: 0, + comment: String::new(), + msg: None, + mu: String::new(), + ctx: None, + sig: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + // The private/public key are defined once per group and shared by + // every test in that group. + let private_seed = group["privateSeed"].as_str().unwrap_or("").to_string(); + let public_key = group["publicKey"].as_str().unwrap_or("").to_string(); + + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + private_seed: private_seed.clone(), + public_key: public_key.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + msg: match test["msg"].as_str() { + Some(msg) => Some(msg.to_string()), + None => None, + }, + mu: test["mu"].as_str().unwrap_or("").to_string(), + ctx: test["ctx"].as_str().map(|s| s.to_string()), + sig: test["sig"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mldsa44(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa44); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (pk, sk) = match MLDSA44::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + let loaded_pk = + MLDSA44PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = + MLDSA44::verify_mu(&pk, &mu, &hex::decode(&self.sig).unwrap().try_into().unwrap()); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa65(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa65); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (pk, sk) = match MLDSA65::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + if self.result == "invalid" { + /* good, test passed */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + let loaded_pk = + MLDSA65PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = + MLDSA65::verify_mu(&pk, &mu, &hex::decode(&self.sig).unwrap().try_into().unwrap()); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa87(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa87); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (pk, sk) = match MLDSA87::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + if self.result == "invalid" { + /* good, test passed */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + let loaded_pk = + MLDSA87PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = + MLDSA87::verify_mu(&pk, &mu, &hex::decode(&self.sig).unwrap().try_into().unwrap()); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } +} + +#[derive(Clone)] +struct MLDSAVerifyTestCase { + parameter_set: ParameterSet, + // testGroup-level fields, copied onto every test case in the group + public_key: String, + // test-level fields + tc_id: u32, + comment: String, + msg: String, + ctx: Option, + sig: String, + result: String, +} + +impl MLDSAVerifyTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + public_key: String::new(), + tc_id: 0, + comment: String::new(), + msg: String::new(), + ctx: None, + sig: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + // The private/public key are defined once per group and shared by + // every test in that group. + let public_key = group["publicKey"].as_str().unwrap_or("").to_string(); + + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + public_key: public_key.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + msg: test["msg"].as_str().unwrap_or("").to_string(), + ctx: test["ctx"].as_str().map(|s| s.to_string()), + sig: test["sig"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mldsa44(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa44); + + /* Load the key */ + + let pk = match MLDSA44PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA44::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed:"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } + + fn run_mldsa65(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa65); + + /* Load the key */ + + let pk = match MLDSA65PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA65::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed:"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } + + fn run_mldsa87(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa87); + + /* Load the key */ + + let pk = match MLDSA87PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA87::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } +} diff --git a/crypto/mldsa/Cargo.toml b/crypto/mldsa/Cargo.toml index 8828d9d..cddc8c6 100644 --- a/crypto/mldsa/Cargo.toml +++ b/crypto/mldsa/Cargo.toml @@ -15,6 +15,7 @@ bouncycastle-core-test-framework.workspace = true bouncycastle-hex.workspace = true bouncycastle-rng.workspace = true criterion.workspace = true +serde_json = "1.0" [[bench]] name = "mldsa_benches" diff --git a/crypto/mldsa/benches/mldsa_benches.rs b/crypto/mldsa/benches/mldsa_benches.rs index 9d019a4..87be0fe 100644 --- a/crypto/mldsa/benches/mldsa_benches.rs +++ b/crypto/mldsa/benches/mldsa_benches.rs @@ -1,9 +1,14 @@ -use criterion::{Criterion, criterion_group, criterion_main}; use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; -use std::hint::black_box; -use bouncycastle_core::traits::Signature; -use bouncycastle_mldsa::{MLDSA44PrivateKeyExpanded, MLDSA44PublicKeyExpanded, MLDSA65PrivateKeyExpanded, MLDSA65PublicKeyExpanded, MLDSA87PrivateKeyExpanded, MLDSA87PublicKeyExpanded, MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait, MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA65, MLDSA65_SIG_LEN, MLDSA87, MLDSA87_SIG_LEN}; +use bouncycastle_core::traits::{SignatureVerifier, Signer}; use bouncycastle_hex as hex; +use bouncycastle_mldsa::{ + MLDSA44, MLDSA44_SIG_LEN, MLDSA44PrivateKeyExpanded, MLDSA44PublicKeyExpanded, MLDSA65, + MLDSA65_SIG_LEN, MLDSA65PrivateKeyExpanded, MLDSA65PublicKeyExpanded, MLDSA87, MLDSA87_SIG_LEN, + MLDSA87PrivateKeyExpanded, MLDSA87PublicKeyExpanded, MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait, + MLDSATrait, +}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_mldsa_keygen(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen"); @@ -18,11 +23,11 @@ fn bench_mldsa_keygen(c: &mut Criterion) { group.throughput(criterion::Throughput::Elements(seeds.len() as u64)); group.bench_function("ML-DSA-44", |b| { - b.iter(|| { - for seed in seeds.iter() { - black_box(MLDSA44::keygen_from_seed(seed)).unwrap(); - } - }) + b.iter(|| { + for seed in seeds.iter() { + black_box(MLDSA44::keygen_from_seed(seed)).unwrap(); + } + }) }); group.bench_function("ML-DSA-65", |b| { @@ -94,16 +99,16 @@ fn bench_mldsa_sign(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; /*** ML-DSA-44 ***/ let (_mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -116,7 +121,6 @@ fn bench_mldsa_sign(c: &mut Criterion) { }) }); - /*** ML-DSA-65 ***/ let (_mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); @@ -129,7 +133,6 @@ fn bench_mldsa_sign(c: &mut Criterion) { }) }); - /*** ML-DSA-87 ***/ let (_mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); @@ -154,7 +157,8 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -164,9 +168,8 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { sk.tr().clone() }; - // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -179,7 +182,6 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { }) }); - /*** ML-DSA-65 ***/ let tr = { let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); @@ -187,7 +189,7 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { }; // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -200,7 +202,6 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { }) }); - /*** ML-DSA-87 ***/ let tr = { @@ -209,7 +210,7 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { }; // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -225,7 +226,6 @@ fn bench_mldsa_sign_from_seed(c: &mut Criterion) { group.finish(); } - fn bench_mldsa_sign_with_expanded_key(c: &mut Criterion) { let mut group = c.benchmark_group("Sign_with_expanded"); @@ -234,7 +234,8 @@ fn bench_mldsa_sign_with_expanded_key(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -242,9 +243,8 @@ fn bench_mldsa_sign_with_expanded_key(c: &mut Criterion) { let (mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); let a_hat = mldsa44_pk.A_hat(); - // signing nonce; we'll increment each time - let mut rnd= [0u8; 32]; + let mut rnd = [0u8; 32]; group.throughput(criterion::Throughput::Elements(1_u64)); @@ -257,7 +257,6 @@ fn bench_mldsa_sign_with_expanded_key(c: &mut Criterion) { }) }); - /*** ML-DSA-65 ***/ let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); let a_hat = mldsa65_pk.A_hat(); @@ -271,7 +270,6 @@ fn bench_mldsa_sign_with_expanded_key(c: &mut Criterion) { }) }); - /*** ML-DSA-87 ***/ let (mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); @@ -297,21 +295,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; - /*** ML-DSA-44 ***/ let (mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA44_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA44_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -321,22 +319,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA44::verify(&mldsa44_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA44::verify(&mldsa44_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); - /*** ML-DSA-65 ***/ let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA65_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA65_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -346,22 +343,21 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA65::verify(&mldsa65_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA65::verify(&mldsa65_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); - /*** ML-DSA-87 ***/ let (mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA87_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA87_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -371,7 +367,7 @@ fn bench_mldsa_verify(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA87::verify(&mldsa87_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box(MLDSA87::verify(&mldsa87_pk, msg, Some(&ctx.to_le_bytes()), sig).unwrap()) } }) }); @@ -387,22 +383,22 @@ fn bench_mldsa_verify_with_expanded_key(c: &mut Criterion) { let seed = KeyMaterial256::from_bytes_as_type( &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; - /*** ML-DSA-44 ***/ let (mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); let pk_expanded = MLDSA44PublicKeyExpanded::from(&mldsa44_pk); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA44_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA44_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA44::sign(&mldsa44_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -412,23 +408,30 @@ fn bench_mldsa_verify_with_expanded_key(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA44::verify_with_expanded_key(&pk_expanded, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box( + MLDSA44::verify_with_expanded_key( + &pk_expanded, + msg, + Some(&ctx.to_le_bytes()), + sig, + ) + .unwrap(), + ) } }) }); - /*** ML-DSA-65 ***/ let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); let pk_expanded = MLDSA65PublicKeyExpanded::from(&mldsa65_pk); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA65_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA65_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA65::sign(&mldsa65_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -438,23 +441,30 @@ fn bench_mldsa_verify_with_expanded_key(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA65::verify_with_expanded_key(&pk_expanded, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box( + MLDSA65::verify_with_expanded_key( + &pk_expanded, + msg, + Some(&ctx.to_le_bytes()), + sig, + ) + .unwrap(), + ) } }) }); - /*** ML-DSA-87 ***/ let (mldsa87_pk, mldsa87_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); let pk_expanded = MLDSA87PublicKeyExpanded::from(&mldsa87_pk); // Create a vec of 1000 different signatures to verify // use ctx to make them different (in addition to the signing nonce being different) - let mut sigs = Vec::< ([u8; MLDSA87_SIG_LEN], u128) >::with_capacity(1000); + let mut sigs = Vec::<([u8; MLDSA87_SIG_LEN], u128)>::with_capacity(1000); let mut ctx = 0u128; for _ in 0..1000 { - sigs.push( (MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx) ); + sigs.push((MLDSA87::sign(&mldsa87_sk, msg, Some(&ctx.to_le_bytes())).unwrap(), ctx)); ctx += 1 } @@ -464,7 +474,15 @@ fn bench_mldsa_verify_with_expanded_key(c: &mut Criterion) { b.iter(|| { for i in 0..sigs.len() { let (sig, ctx) = &sigs[i]; - black_box( MLDSA87::verify_with_expanded_key(&pk_expanded, msg, Some(&ctx.to_le_bytes()), sig).unwrap() ) + black_box( + MLDSA87::verify_with_expanded_key( + &pk_expanded, + msg, + Some(&ctx.to_le_bytes()), + sig, + ) + .unwrap(), + ) } }) }); @@ -472,11 +490,16 @@ fn bench_mldsa_verify_with_expanded_key(c: &mut Criterion) { group.finish(); } - -criterion_group!(benches, - bench_mldsa_keygen, bench_mldsa_keygen_and_expand, - bench_mldsa_sign, bench_mldsa_sign_with_expanded_key, bench_mldsa_sign_from_seed, - bench_mldsa_verify, bench_mldsa_verify_with_expanded_key); +criterion_group!( + benches, + bench_mldsa_keygen, + bench_mldsa_keygen_and_expand, + bench_mldsa_sign, + bench_mldsa_sign_with_expanded_key, + bench_mldsa_sign_from_seed, + bench_mldsa_verify, + bench_mldsa_verify_with_expanded_key +); criterion_main!(benches); const DUMMY_SEED_1024: &[u8; 1024] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"; diff --git a/crypto/mldsa/src/aux_functions.rs b/crypto/mldsa/src/aux_functions.rs index 720a480..3164838 100644 --- a/crypto/mldsa/src/aux_functions.rs +++ b/crypto/mldsa/src/aux_functions.rs @@ -1,7 +1,7 @@ //! Implements auxiliary functions for ML-DSA as defined in Section 7 of FIPS 204. use crate::matrix::{Matrix, Vector}; -use crate::mldsa::{q_inv, G, H}; +use crate::mldsa::{G, H, q_inv}; use crate::mldsa::{ MLDSA44_GAMMA1, MLDSA44_GAMMA2, MLDSA65_GAMMA1, MLDSA65_GAMMA2, N, POLY_T0PACKED_LEN, POLY_T1PACKED_LEN, d, q, @@ -24,6 +24,7 @@ pub(crate) fn coeff_from_three_bytes(b: &[u8; 3]) -> Result { let z: i32 = ((b2_prime as i32) << 16) | ((b[1] as i32) << 8) | (b[0] as i32); + // mutants note: yeah, this could be z <= q and nothing changes because z == q is the same as z == 0 if z < q { Ok(z) } else { Err(()) } } @@ -308,8 +309,7 @@ pub(crate) fn bit_unpack_t0(a: &[u8; POLY_T0PACKED_LEN]) -> Polynomial { t0[8 * i + 6] = ((((a[13 * i + 9] as i32) >> 6) | (a[13 * i + 10] as i32) << 2) | ((a[13 * i + 11] as i32) << 10)) & 0x1FFF; - t0[8 * i + 7] = - (((a[13 * i + 11] as i32) >> 3) | ((a[13 * i + 12] as i32) << 5)) & 0x1FFF; + t0[8 * i + 7] = (((a[13 * i + 11] as i32) >> 3) | ((a[13 * i + 12] as i32) << 5)) & 0x1FFF; t0[8 * i] = (1 << (d - 1)) - t0[8 * i]; t0[8 * i + 1] = (1 << (d - 1)) - t0[8 * i + 1]; @@ -397,6 +397,8 @@ pub(crate) fn sig_encode< h: &Vector, output: &mut [u8; SIG_LEN], ) -> usize { + output.fill(0); + let mut pos = 0; output[..LAMBDA_over_4].copy_from_slice(c_tilde); @@ -464,6 +466,8 @@ pub(crate) fn sig_decode< // ▷ reconstruct 𝐡[𝑖] for i in 0..k { // 4: if 𝑦[𝜔 + 𝑖] < Index or 𝑦[𝜔 + 𝑖] > 𝜔 then return ⊥ + // todo: this needs a specific test for malformed signature values. Maybe crucible coveres this case? + // ... could hide an assert here and see if it triggers. if sig[pos + (OMEGA as usize) + i] < (idx as u8) || sig[pos + (OMEGA as usize) + i] > OMEGA as u8 { @@ -816,8 +820,8 @@ pub(crate) fn decompose(r: i32) -> (i32, i32) { r1 = r - r0 * 2 * GAMMA2; // mutants note: the choice of (q - 1) is a bit arbitrary in that after doing the bit-shifting, - // this seems to work out mathematically equivalent if you do q/2, or (q+3)/2, but we'll leave it as (q-1)/2 - // since that's algorithmically correct, and just ignore the mutants results. + // this seems to work out mathematically equivalent if you do q/2, or (q+3)/2, but we'll leave it as (q-1)/2 + // since that's algorithmically correct, and just ignore the mutants results. r1 -= (((q - 1) / 2 - r1) >> 31) & q; (r0, r1) @@ -860,13 +864,8 @@ pub(crate) fn make_hint(z: i32, r: i32) -> i32 { // if r1 != v1 { 1 } else { 0 } // By the powers of someone much more clever than me, this is equivalent. - - if z <= GAMMA2 || z > q - GAMMA2 || (z == q - GAMMA2 && r == 0) - { - 0 - } else { - 1 - } + // mutants note: we do not have KATs that exercise all branches of this if + if z <= GAMMA2 || z > q - GAMMA2 || (z == q - GAMMA2 && r == 0) { 0 } else { 1 } } /// Creates the hint vector from two Vector's, and also returns its hamming weight (ie the number of 1's). @@ -880,6 +879,8 @@ pub(crate) fn make_hint_vecs( for i in 0..k { let (w, c) = r.vec[i].make_hint::(&s.vec[i]); out.vec[i] = w; + + // mutants note: this chains up to hint_hamming_weight > OMEGA and there is no test KAT that triggers this branch count += c; } @@ -913,11 +914,7 @@ pub(super) fn use_hint(a: i32, hint: i32) -> i32 { MLDSA65_GAMMA2 => { // mutants note: this passes unit tests if it's a1 >= 0 // we'll leave it like this because it matches the spec - if a1 > 0 { - (a0 + 1) & 15 - } else { - (a0 - 1) & 15 - } + if a1 > 0 { (a0 + 1) & 15 } else { (a0 - 1) & 15 } } _ => { panic!("Invalid GAMMA2 value") @@ -967,7 +964,7 @@ pub(crate) fn multiply_ntt(a: &Polynomial, b: &Polynomial) -> Polynomial { /// of expressions of the form c = a * b (mod q). /// The output is not necessarily less than q in absolute value, but it is less than 2q in absolute value pub(crate) fn montgomery_reduce(a: i64) -> i32 { - debug_assert!(a > - ((q as i64) <<31) && a < ((q as i64) <<31)); + debug_assert!(a > -((q as i64) << 31) && a < ((q as i64) << 31)); // 2: 𝑡 ← ((𝑎 mod 2^32) ⋅ QINV) mod 2^32 let t: i32 = (a as i32).wrapping_mul(q_inv); @@ -980,23 +977,21 @@ pub(crate) fn conditional_add_q(a: i32) -> i32 { a + ((a >> 31) & q) } - #[test] /// These are the results it's giving; I'm not sure if these are "correct" or not. fn test_conditional_add_q() { - assert_eq!(conditional_add_q(-q -1), -1); + assert_eq!(conditional_add_q(-q - 1), -1); assert_eq!(conditional_add_q(-q), 0); - assert_eq!(conditional_add_q(-q -2), -2); - assert_eq!(conditional_add_q(-q +1), 1); - assert_eq!(conditional_add_q(-1), q-1); + assert_eq!(conditional_add_q(-q - 2), -2); + assert_eq!(conditional_add_q(-q + 1), 1); + assert_eq!(conditional_add_q(-1), q - 1); assert_eq!(conditional_add_q(0), 0); assert_eq!(conditional_add_q(1), 1); - assert_eq!(conditional_add_q(q -1), q-1); + assert_eq!(conditional_add_q(q - 1), q - 1); assert_eq!(conditional_add_q(q), q); - assert_eq!(conditional_add_q(q +1), q+1); + assert_eq!(conditional_add_q(q + 1), q + 1); } - /// Constants for NTT pub(crate) const ZETAS: [i32; 256] = [ 0, 25847, -2608894, -518909, 237124, -777960, -876248, 466468, 1826347, 2353451, -359251, @@ -1026,4 +1021,3 @@ pub(crate) const ZETAS: [i32; 256] = [ -2235985, -420899, -2286327, 183443, -976891, 1612842, -3545687, -554416, 3919660, -48306, -1362209, 3937738, 1400424, -846154, 1976782, ]; - diff --git a/crypto/mldsa/src/hash_mldsa.rs b/crypto/mldsa/src/hash_mldsa.rs index c4fa85d..0cfd620 100644 --- a/crypto/mldsa/src/hash_mldsa.rs +++ b/crypto/mldsa/src/hash_mldsa.rs @@ -3,12 +3,12 @@ //! mode of [MLDSA]; possibly because you have to digest the message before you know which public key //! will sign it. //! -//! HashML-DSA is a full signature algorithm implementing the [Signature] trait: +//! HashML-DSA is a full signature algorithm implementing the [Signer] and [SignatureVerifier] traits: //! //! ```rust //! use bouncycastle_core::errors::SignatureError; //! use bouncycastle_mldsa::{HashMLDSA65_with_SHA512, MLDSATrait, HashMLDSA44_with_SHA512}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; //! @@ -24,12 +24,14 @@ //! } //! ``` //! -//! But you also have access to the pre-hashed function available from [PHSignature]: +//! But you also have access to the pre-hashed functions available from [PHSigner] and [PHSignatureVerifier]: //! //! ```rust //! use bouncycastle_core::errors::SignatureError; //! use bouncycastle_mldsa::{HashMLDSA65_with_SHA512, MLDSATrait, HashMLDSA44_with_SHA512}; -//! use bouncycastle_core::traits::{Signature, PHSignature, Hash}; +//! use bouncycastle_core::traits::{ +//! Hash, PHSignatureVerifier, PHSigner, SignatureVerifier, Signer, +//! }; //! use bouncycastle_sha2::SHA512; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -44,14 +46,14 @@ //! let sig = HashMLDSA65_with_SHA512::sign_ph(&sk, &ph, None).unwrap(); //! // This is the signature value that you can save to a file or whatever you need. //! -//! // This verifies either through the usual one-shot API of the [Signature] trait +//! // This verifies either through the usual one-shot API of the [SignatureVerifier] trait //! match HashMLDSA65_with_SHA512::verify(&pk, msg, None, &sig) { //! Ok(()) => println!("Signature is valid!"), //! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), //! Err(e) => panic!("Something else went wrong: {:?}", e), //! } //! -//! // Or though the verify_ph of the [PHSignature] trait +//! // Or though the verify_ph of the [PHSignatureVerifier] trait //! match HashMLDSA65_with_SHA512::verify_ph(&pk, &ph, None, &sig) { //! Ok(()) => println!("Signature is valid!"), //! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), @@ -61,7 +63,7 @@ //! //! Note that the [HashMLDSA] object is just a light wrapper around [MLDSA], and, for example, they share key types, //! so if you need the fancy keygen functions, just use them from [MLDSA]. -//! But a simple [HashMLDSA::keygen] is provided in order to have conformance to the [Signature] trait. +//! But a simple [HashMLDSA::keygen] is provided. use crate::mldsa::{H, MLDSA_MU_LEN, MLDSA_RND_LEN, MLDSATrait}; use crate::mldsa::{ @@ -91,10 +93,11 @@ use crate::{ use bouncycastle_core::errors::SignatureError; use bouncycastle_core::key_material::KeyMaterial; use bouncycastle_core::traits::{ - Algorithm, Hash, PHSignature, RNG, SecurityStrength, Signature, XOF, + Algorithm, Hash, PHSignatureVerifier, PHSigner, RNG, SecurityStrength, SignatureVerifier, + Signer, XOF, }; use bouncycastle_rng::HashDRBG_SHA512; -use bouncycastle_sha2::{SHA256, SHA512}; +use bouncycastle_sha2::{SHA256, SHA256_NAME, SHA512, SHA512_NAME}; use core::marker::PhantomData; // Imports needed only for docs @@ -126,7 +129,6 @@ pub const HASH_ML_DSA_87_WITH_SHA512_NAME: &str = "HashML-DSA-87_with_SHA512"; pub type HashMLDSA44_with_SHA256 = HashMLDSA< SHA256, 32, - SHA256_OID, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_SIG_LEN, @@ -145,9 +147,9 @@ pub type HashMLDSA44_with_SHA256 = HashMLDSA< MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_POLY_W1_PACKED_LEN, MLDSA44_LAMBDA_over_4, - MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA1_MINUS_BETA, MLDSA44_GAMMA2_MINUS_BETA, + MLDSA44_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA44_with_SHA256 { @@ -160,7 +162,6 @@ impl Algorithm for HashMLDSA44_with_SHA256 { pub type HashMLDSA65_with_SHA256 = HashMLDSA< SHA256, 32, - SHA256_OID, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_SIG_LEN, @@ -179,9 +180,9 @@ pub type HashMLDSA65_with_SHA256 = HashMLDSA< MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, MLDSA65_LAMBDA_over_4, - MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA1_MINUS_BETA, MLDSA65_GAMMA2_MINUS_BETA, + MLDSA65_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA65_with_SHA256 { @@ -194,7 +195,6 @@ impl Algorithm for HashMLDSA65_with_SHA256 { pub type HashMLDSA87_with_SHA256 = HashMLDSA< SHA256, 32, - SHA256_OID, MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_SIG_LEN, @@ -213,9 +213,9 @@ pub type HashMLDSA87_with_SHA256 = HashMLDSA< MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_POLY_W1_PACKED_LEN, MLDSA87_LAMBDA_over_4, - MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA1_MINUS_BETA, MLDSA87_GAMMA2_MINUS_BETA, + MLDSA87_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA87_with_SHA256 { @@ -228,7 +228,6 @@ impl Algorithm for HashMLDSA87_with_SHA256 { pub type HashMLDSA44_with_SHA512 = HashMLDSA< SHA512, 64, - SHA512_OID, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_SIG_LEN, @@ -247,9 +246,9 @@ pub type HashMLDSA44_with_SHA512 = HashMLDSA< MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_POLY_W1_PACKED_LEN, MLDSA44_LAMBDA_over_4, - MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA1_MINUS_BETA, MLDSA44_GAMMA2_MINUS_BETA, + MLDSA44_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA44_with_SHA512 { @@ -262,7 +261,6 @@ impl Algorithm for HashMLDSA44_with_SHA512 { pub type HashMLDSA65_with_SHA512 = HashMLDSA< SHA512, 64, - SHA512_OID, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_SIG_LEN, @@ -281,9 +279,9 @@ pub type HashMLDSA65_with_SHA512 = HashMLDSA< MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, MLDSA65_LAMBDA_over_4, - MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA1_MINUS_BETA, MLDSA65_GAMMA2_MINUS_BETA, + MLDSA65_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA65_with_SHA512 { @@ -296,7 +294,6 @@ impl Algorithm for HashMLDSA65_with_SHA512 { pub type HashMLDSA87_with_SHA512 = HashMLDSA< SHA512, 64, - SHA512_OID, MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_SIG_LEN, @@ -315,9 +312,9 @@ pub type HashMLDSA87_with_SHA512 = HashMLDSA< MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_POLY_W1_PACKED_LEN, MLDSA87_LAMBDA_over_4, - MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA1_MINUS_BETA, MLDSA87_GAMMA2_MINUS_BETA, + MLDSA87_GAMMA1_MASK_LEN, >; impl Algorithm for HashMLDSA87_with_SHA512 { @@ -332,9 +329,8 @@ impl Algorithm for HashMLDSA87_with_SHA512 { /// by specifying the hash function to use (in the verifier), and specifying the bytes of the OID to /// to use as its domain separator in constructing the message representative M'. pub struct HashMLDSA< - HASH: Hash + Default, + HASH: Hash + Algorithm + Default, const HASH_LEN: usize, - const oid: &'static [u8], const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, @@ -354,9 +350,9 @@ pub struct HashMLDSA< const POLY_Z_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, > { _phantom: PhantomData<(PK, SK)>, @@ -381,9 +377,8 @@ pub struct HashMLDSA< } impl< - HASH: Hash + Default, + HASH: Hash + Algorithm + Default, const PH_LEN: usize, - const oid: &'static [u8], const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, @@ -410,7 +405,6 @@ impl< HashMLDSA< HASH, PH_LEN, - oid, PK_LEN, SK_LEN, SIG_LEN, @@ -429,11 +423,43 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { + /// Generate a keypair, sourcing randomness from bouncycastle's default os-backed RNG. + /// + /// Key generation is intentionally not part of the [Signer] / [SignatureVerifier] traits; + /// it is provided as an inherent associated function directly on the algorithm struct. + /// Keygen, and keys in general, are interchangeable between MLDSA and HashMLDSA. + /// Error condition: basically only on RNG failures. + pub fn keygen() -> Result<(PK, SK), SignatureError> { + MLDSA::< + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + >::keygen() + } + /// Imports a secret key from a seed. pub fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> { MLDSA::< @@ -455,12 +481,12 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, >::keygen_internal(seed) } - /// Same as [Signature::sign], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [Signer::sign], but signs from an [MLDSAPrivateKeyExpanded]. pub fn sign_with_expanded_key( sk: &MLDSAPrivateKeyExpanded, msg: &[u8], @@ -471,18 +497,20 @@ impl< Ok(out) } - /// Same as [Signature::sign_out], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [Signer::sign_out], but signs from an [MLDSAPrivateKeyExpanded]. pub fn sign_with_expanded_key_out( sk: &MLDSAPrivateKeyExpanded, msg: &[u8], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut ph_m = [0u8; PH_LEN]; _ = HASH::default().hash_out(msg, &mut ph_m); Self::sign_ph_with_expanded_key_out(sk, &ph_m, ctx, output) } - /// Same as [PHSignature::sign_ph], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [PHSigner::sign_ph], but signs from an [MLDSAPrivateKeyExpanded]. pub fn sign_ph_with_expanded_key( sk: &MLDSAPrivateKeyExpanded, ph: &[u8; PH_LEN], @@ -493,13 +521,15 @@ impl< Ok(out) } - /// Same as [PHSignature::sign_ph_out], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [PHSigner::sign_ph_out], but signs from an [MLDSAPrivateKeyExpanded]. pub fn sign_ph_with_expanded_key_out( sk: &MLDSAPrivateKeyExpanded, ph: &[u8; PH_LEN], ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN]; HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; Self::sign_ph_deterministic_out(&sk.sk, Some(&sk.A_hat), ctx, ph, rnd, output) @@ -556,6 +586,8 @@ impl< return Err(SignatureError::LengthError("ctx value is longer than 255 bytes")); } + output.fill(0); + // Algorithm 7 // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀', 64) let mu = { @@ -568,7 +600,20 @@ impl< h.absorb(&[1u8]); h.absorb(&[ctx.len() as u8]); h.absorb(ctx); - h.absorb(oid); + + // this is all statics, so the branch should compile out. + // Really, this should be a generic param of HashMLDSA, but unsized_const_params is currently + // a nightly-only feature. + match HASH::ALG_NAME { + SHA256_NAME => h.absorb(SHA256_OID), + SHA512_NAME => h.absorb(SHA512_OID), + _ => { + return Err(SignatureError::GenericError( + "Unsupported hash algorithm; you need to add it to the switch", + )); + } + }; + h.absorb(ph); let mut mu = [0u8; MLDSA_MU_LEN]; let bytes_written = h.squeeze_out(&mut mu); @@ -597,17 +642,17 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, >::sign_mu_deterministic_out(sk, A_hat, &mu, rnd, output)?; Ok(bytes_written) } - /// To be used for deterministic signing in conjunction with the [Signature::sign_init], - /// [Signature::sign_update], and [Signature::sign_final] flow. - /// Can be set anywhere after [Signature::sign_init] and before [Signature::sign_final] + /// To be used for deterministic signing in conjunction with the [Signer::sign_init], + /// [Signer::sign_update], and [Signer::sign_final] flow. + /// Can be set anywhere after [Signer::sign_init] and before [Signer::sign_final] pub fn set_signer_rnd(&mut self, rnd: [u8; 32]) { self.signer_rnd = Some(rnd); } @@ -646,7 +691,7 @@ impl< ctx_len, }) } - /// Same as [Signature::verify], but verifies from an [MLDSAPublicKeyExpanded]. + /// Same as [SignatureVerifier::verify], but verifies from an [MLDSAPublicKeyExpanded]. pub fn verify_with_expanded_key( pk: &MLDSAPublicKeyExpanded, msg: &[u8], @@ -691,7 +736,18 @@ impl< h.absorb(&[1u8]); h.absorb(&[ctx.len() as u8]); h.absorb(ctx); - h.absorb(oid); + // this is all statics, so the branch should compile out. + // Really, this should be a generic param of HashMLDSA, but unsized_const_params is currently + // a nightly-only feature. + match HASH::ALG_NAME { + SHA256_NAME => h.absorb(SHA256_OID), + SHA512_NAME => h.absorb(SHA512_OID), + _ => { + return Err(SignatureError::GenericError( + "Unsupported hash algorithm; you need to add it to the switch", + )); + } + }; h.absorb(ph); let mut mu = [0u8; MLDSA_MU_LEN]; _ = h.squeeze_out(&mut mu); @@ -700,77 +756,62 @@ impl< }; match A_hat { - Some(A_hat) => { - if MLDSA::< - PK_LEN, - SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, - GAMMA1_MINUS_BETA, - GAMMA2_MINUS_BETA, - >::verify_mu_internal(pk, A_hat, &mu, sig_sized) - { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } - } - None => { - if MLDSA::< - PK_LEN, - SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, - GAMMA1_MINUS_BETA, - GAMMA2_MINUS_BETA, - >::verify_mu_internal(pk, &pk.A_hat(), &mu, sig_sized) - { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } - } + Some(A_hat) => MLDSA::< + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + >::verify_mu(pk, Some(A_hat), &mu, sig_sized), + None => MLDSA::< + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + >::verify_mu(pk, Some(&pk.A_hat()), &mu, sig_sized), } } } impl< - HASH: Hash + Default, + HASH: Hash + Algorithm + Default, PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, SK: MLDSAPrivateKeyTrait + MLDSAPrivateKeyInternalTrait, const PH_LEN: usize, - const oid: &'static [u8], const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, @@ -787,14 +828,13 @@ impl< const POLY_Z_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, -> Signature + const GAMMA1_MASK_LEN: usize, +> Signer for HashMLDSA< HASH, PH_LEN, - oid, PK_LEN, SK_LEN, SIG_LEN, @@ -813,38 +853,11 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { - /// Keygen, and keys in general, are interchangeable between MLDSA and HashMLDSA. - fn keygen() -> Result<(PK, SK), SignatureError> { - MLDSA::< - PK_LEN, - SK_LEN, - SIG_LEN, - PK, - SK, - TAU, - LAMBDA, - GAMMA1, - GAMMA2, - k, - l, - ETA, - BETA, - OMEGA, - C_TILDE, - POLY_Z_PACKED_LEN, - POLY_W1_PACKED_LEN, - LAMBDA_over_4, - GAMMA1_MASK_LEN, - GAMMA1_MINUS_BETA, - GAMMA2_MINUS_BETA, - >::keygen() - } - /// Algorithm 4 HashML-DSA.Sign(𝑠𝑘, 𝑀 , 𝑐𝑡𝑥, PH) /// Generate a “pre-hash” ML-DSA signature. fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> { @@ -860,6 +873,8 @@ impl< ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut ph_m = [0u8; PH_LEN]; _ = HASH::default().hash_out(msg, &mut ph_m); Self::sign_ph_out(sk, &ph_m, ctx, output) @@ -898,21 +913,11 @@ impl< )); } - if output.len() < SIG_LEN { - return Err(SignatureError::LengthError( - "Output buffer insufficient size to hold signature", - )); - } - let output_sized: &mut [u8; SIG_LEN] = output[..SIG_LEN].as_mut().try_into().unwrap(); + output.fill(0); if self.sk.is_some() { if self.signer_rnd.is_none() { - Self::sign_ph_out( - &self.sk.unwrap(), - &ph, - Some(&self.ctx[..self.ctx_len]), - output_sized, - ) + Self::sign_ph_out(&self.sk.unwrap(), &ph, Some(&self.ctx[..self.ctx_len]), output) } else { Self::sign_ph_deterministic_out( &self.sk.unwrap(), @@ -920,7 +925,7 @@ impl< Some(&self.ctx[..self.ctx_len]), &ph, self.signer_rnd.unwrap(), - output_sized, + output, ) } } else if self.seed.is_some() { @@ -940,13 +945,66 @@ impl< Some(&self.ctx[..self.ctx_len]), &ph, rnd, - output_sized, + output, ) } else { unreachable!() } } +} +impl< + HASH: Hash + Algorithm + Default, + PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait + + MLDSAPrivateKeyInternalTrait, + const PH_LEN: usize, + const PK_LEN: usize, + const SK_LEN: usize, + const SIG_LEN: usize, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, +> SignatureVerifier + for HashMLDSA< + HASH, + PH_LEN, + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> { let mut ph_m = [0u8; PH_LEN]; _ = HASH::default().hash_out(msg, &mut ph_m); @@ -983,9 +1041,8 @@ impl< } impl< - HASH: Hash + Default, + HASH: Hash + Algorithm + Default, const PH_LEN: usize, - const oid: &'static [u8], const PK_LEN: usize, const SK_LEN: usize, const SIG_LEN: usize, @@ -1008,11 +1065,10 @@ impl< const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, -> PHSignature +> PHSigner for HashMLDSA< HASH, PH_LEN, - oid, PK_LEN, SK_LEN, SIG_LEN, @@ -1031,9 +1087,9 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { fn sign_ph( @@ -1042,7 +1098,7 @@ impl< ctx: Option<&[u8]>, ) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; - Self::sign_out(sk, ph, ctx, &mut out)?; + Self::sign_ph_out(sk, ph, ctx, &mut out)?; Ok(out) } @@ -1057,11 +1113,66 @@ impl< ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN]; HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; Self::sign_ph_deterministic_out(sk, None, ctx, ph, rnd, output) } +} +impl< + HASH: Hash + Algorithm + Default, + const PH_LEN: usize, + const PK_LEN: usize, + const SK_LEN: usize, + const SIG_LEN: usize, + PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait + + MLDSAPrivateKeyInternalTrait, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MASK_LEN: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, +> PHSignatureVerifier + for HashMLDSA< + HASH, + PH_LEN, + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify_ph( pk: &PK, ph: &[u8; PH_LEN], diff --git a/crypto/mldsa/src/lib.rs b/crypto/mldsa/src/lib.rs index 06e37f9..e0f2b4f 100644 --- a/crypto/mldsa/src/lib.rs +++ b/crypto/mldsa/src/lib.rs @@ -14,8 +14,7 @@ //! ## Generating Keys //! //! ```rust -//! use bouncycastle_mldsa::MLDSA65; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_mldsa::{MLDSA65, MLDSATrait}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); //! ``` @@ -39,13 +38,13 @@ //! //! See [MLDSATrait] and [MLDSATrait::sign_mu_deterministic_from_seed] for an API flow that uses a merged //! keygen-and-sign function to provide improved speed and memory performance compared with making -//! separate calls to [MLDSATrait::keygen_from_seed] followed by [Signature::sign]. +//! separate calls to [MLDSATrait::keygen_from_seed] followed by [Signer::sign]. //! //! ## Generating and Verifying Signatures //! //! ```rust //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_core::errors::SignatureError; //! //! let msg = b"The quick brown fox"; @@ -115,9 +114,6 @@ #![no_std] #![forbid(missing_docs)] #![forbid(unsafe_code)] -#![allow(incomplete_features)] // needed because currently generic_const_exprs is experimental -#![feature(generic_const_exprs)] -#![feature(adt_const_params)] // These are because I'm matching variable names exactly against FIPS 204, for example both 'K' and 'k', // or 'A' and 'a' are used and have specific meanings. // But need to tell the rust linter to not care. @@ -127,18 +123,16 @@ // MLDSA implementation, but I don't want accessed from outside, such as FIPS-internal functions. #![allow(private_bounds)] #![allow(private_interfaces)] -// Used in HashMLDSA -#![feature(unsized_const_params)] +// Used in HashMLDSA for oid: &'static [u8] params. +// #![allow(incomplete_features)] // needed because currently unsized_const_params is experimental +// #![feature(adt_const_params)] +// #![feature(unsized_const_params)] // imports needed just for docs #[allow(unused_imports)] use bouncycastle_core::key_material::KeyMaterialTrait; #[allow(unused_imports)] -use bouncycastle_core::traits::Signature; - -// todo -- re-run mutants - -// todo -- crucible tests +use bouncycastle_core::traits::{SignatureVerifier, Signer}; mod aux_functions; pub mod hash_mldsa; @@ -182,6 +176,8 @@ pub use mldsa::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; pub use mldsa::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; pub use mldsa::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; +pub use mldsa::MU_BUILDER_SERIALIZED_STATE_LEN; + pub use matrix::Matrix; // re-export just so it's visible to unit tests diff --git a/crypto/mldsa/src/mldsa.rs b/crypto/mldsa/src/mldsa.rs index d634272..0869c44 100644 --- a/crypto/mldsa/src/mldsa.rs +++ b/crypto/mldsa/src/mldsa.rs @@ -9,7 +9,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); @@ -51,7 +51,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, sk) = MLDSA65::keygen().unwrap(); @@ -98,7 +98,7 @@ //! ```rust //! use bouncycastle_core::errors::SignatureError; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! //! let (pk, _) = MLDSA65::keygen().unwrap(); //! @@ -116,7 +116,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let (pk, _) = MLDSA65::keygen().unwrap(); @@ -136,7 +136,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -182,7 +182,7 @@ //! ```rust //! use bouncycastle_core::errors::SignatureError; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! //! let msg = b"The quick brown fox"; //! let ctx = b"FooTextDocumentFormat"; @@ -220,7 +220,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -258,7 +258,7 @@ //! ```rust //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait}; //! use bouncycastle_mldsa::{MLDSA65PublicKeyExpanded, MLDSA65PrivateKeyExpanded}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_core::errors::SignatureError; //! //! let msg = b"The quick brown fox"; @@ -303,7 +303,7 @@ //! //! ```rust //! use bouncycastle_core::errors::SignatureError; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_core::key_material::{KeyMaterial256, KeyType, KeyMaterialTrait}; //! use bouncycastle_hex as hex; //! use bouncycastle_mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait, MLDSAPublicKeyTrait, MuBuilder}; @@ -375,7 +375,7 @@ //! ```rust //! use bouncycastle_mldsa::{MLDSA65, MLDSATrait}; //! use bouncycastle_mldsa::{MLDSA65PublicKeyExpanded, MLDSA65PrivateKeyExpanded}; -//! use bouncycastle_core::traits::Signature; +//! use bouncycastle_core::traits::{Signer, SignatureVerifier}; //! use bouncycastle_core::errors::SignatureError; //! //! let msg = b"The quick brown fox jumped over the lazy dog"; @@ -398,6 +398,81 @@ //! Err(e) => panic!("Something else went wrong: {:?}", e), //! } //! ``` +//! +//! # Suspending and resuming execution via SerializableState +//! +//! When signing or verifying a large message, it can be advantageous to be able to suspend the operation +//! to a cache and resume it later; for example if waiting for the message to stream over a slow network +//! connection. +//! +//! This can bo accomplished for both the ML-DSA signer and verifier through the [MuBuilder] object. +//! +//! Suspending an in-progress sign operation: +//! +//! ```rust +//! use bouncycastle_mldsa::{MLDSA65, MuBuilder, MLDSATrait, MLDSAPublicKeyTrait}; +//! use bouncycastle_core::traits::{Signer, Suspendable}; +//! +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let (pk, sk) = MLDSA65::keygen().unwrap(); +//! +//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); +//! mb.do_update(msg_part1); +//! +//! // here, we'll suspend while "waiting" for the second part of the message +//! let serialized_state = mb.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); +//! mb_resumed.do_update(msg_part2); +//! let mu: [u8; 64] = mb_resumed.do_final(); +//! +//! // Now we'll do the actual sign_mu operation +//! let sig = MLDSA65::sign_mu(&sk, None, &mu).unwrap(); +//! ``` +//! +//! Suspending an in-progress verify operation behaves exactly the same way: +//! +//! ```rust +//! use bouncycastle_mldsa::{MLDSA65, MuBuilder, MLDSATrait, MLDSAPublicKeyTrait}; +//! use bouncycastle_core::traits::{Signer, Suspendable}; +//! use bouncycastle_core::errors::SignatureError; +//! +//! let (pk, sk) = MLDSA65::keygen().unwrap(); +//! +//! // first, let's generate a signature to verify +//! let sig = MLDSA65::sign(&sk, b"The quick brown fox jumped over the lazy dog", None).unwrap(); +//! +//! // Now we'll verify it with a suspension in the middle +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let mut mb = MuBuilder::do_init(&pk.compute_tr(), None).unwrap(); +//! mb.do_update(msg_part1); +//! +//! // here, we'll suspend while "waiting" for the second part of the message +//! let serialized_state = mb.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); +//! mb_resumed.do_update(msg_part2); +//! let mu: [u8; 64] = mb_resumed.do_final(); +//! +//! // Now we'll do the actual verify_mu operation +//! match MLDSA65::verify_mu(&pk, Some(&pk.A_hat()), &mu, &sig) { +//! Ok(()) => println!("Signature is valid!"), +//! Err(SignatureError::SignatureVerificationFailed) => println!("Signature is invalid!"), +//! Err(e) => panic!("Something else went wrong: {:?}", e), +//! } +//! ``` use crate::aux_functions::{ expand_mask, expandA, expandS, make_hint_vecs, power_2_round_vec, sample_in_ball, sig_decode, @@ -410,18 +485,20 @@ use crate::{ MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyExpanded, MLDSAPublicKeyExpanded, }; -use bouncycastle_core::errors::SignatureError; +use bouncycastle_core::errors::{RNGError, SignatureError, SuspendableError}; use bouncycastle_core::key_material::{KeyMaterial, KeyMaterial256, KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Algorithm, RNG, SecurityStrength, Signature, XOF}; +use bouncycastle_core::traits::{ + Algorithm, RNG, SecurityStrength, SignatureVerifier, Signer, Suspendable, XOF, +}; use bouncycastle_rng::HashDRBG_SHA512; -use bouncycastle_sha3::{SHAKE128, SHAKE256}; +use bouncycastle_sha3::{SHAKE128, SHAKE256, SUSPENDED_SHA3_STATE_LEN}; use core::marker::PhantomData; // imports needed just for docs #[allow(unused_imports)] use crate::hash_mldsa; #[allow(unused_imports)] -use bouncycastle_core::traits::PHSignature; +use bouncycastle_core::traits::{PHSignatureVerifier, PHSigner}; /*** Constants ***/ @@ -566,9 +643,9 @@ pub type MLDSA44 = MLDSA< MLDSA44_POLY_Z_PACKED_LEN, MLDSA44_POLY_W1_PACKED_LEN, MLDSA44_LAMBDA_over_4, - MLDSA44_GAMMA1_MASK_LEN, MLDSA44_GAMMA1_MINUS_BETA, MLDSA44_GAMMA2_MINUS_BETA, + MLDSA44_GAMMA1_MASK_LEN, >; impl Algorithm for MLDSA44 { @@ -596,9 +673,9 @@ pub type MLDSA65 = MLDSA< MLDSA65_POLY_Z_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, MLDSA65_LAMBDA_over_4, - MLDSA65_GAMMA1_MASK_LEN, MLDSA65_GAMMA1_MINUS_BETA, MLDSA65_GAMMA2_MINUS_BETA, + MLDSA65_GAMMA1_MASK_LEN, >; impl Algorithm for MLDSA65 { @@ -626,9 +703,9 @@ pub type MLDSA87 = MLDSA< MLDSA87_POLY_Z_PACKED_LEN, MLDSA87_POLY_W1_PACKED_LEN, MLDSA87_LAMBDA_over_4, - MLDSA87_GAMMA1_MASK_LEN, MLDSA87_GAMMA1_MINUS_BETA, MLDSA87_GAMMA2_MINUS_BETA, + MLDSA87_GAMMA1_MASK_LEN, >; impl Algorithm for MLDSA87 { @@ -659,9 +736,9 @@ pub struct MLDSA< const POLY_VEC_H_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, > { _phantom: PhantomData<(PK, SK)>, @@ -700,9 +777,9 @@ impl< const POLY_Z_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, > MLDSA< PK_LEN, @@ -723,17 +800,11 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { - /// Should still be ok in FIPS mode - pub fn keygen_from_os_rng() -> Result<(PK, SK), SignatureError> { - let mut seed = KeyMaterial256::new(); - HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?; - Self::keygen_internal(&seed) - } /// Implements Algorithm 6 of FIPS 204 /// Note: NIST has made a special exception in the FIPS 204 FAQ that this _internal function /// may in fact be exposed outside the crypto module. @@ -744,7 +815,7 @@ impl< /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using /// [KeyMaterialTrait::from_key] pub(crate) fn keygen_internal(seed: &KeyMaterial256) -> Result<(PK, SK), SignatureError> { - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::CryptographicRandom) || seed.key_len() != 32 { return Err(SignatureError::KeyGenError( @@ -845,6 +916,8 @@ impl< rnd: [u8; 32], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + // 1: (𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) ← skDecode(𝑠𝑘) // 2: 𝐬1̂_hat ← NTT(𝐬1) // 3: 𝐬2̂_hat ← NTT(𝐬2) @@ -888,6 +961,7 @@ impl< loop { // FIPS 204 s. 6.2 allows: // "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value." + // mutants note: there is no test for this because we don't know of a KAT that will exceed this limit. if kappa > 1000 * k as u16 { return Err(SignatureError::GenericError( "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.", @@ -971,13 +1045,14 @@ impl< continue; }; - // 25: ⟨⟨𝑐𝐭0⟩⟩ ← NTT−1(𝑐_hat * 𝐭0̂_hat ) + // 25: ⟨⟨𝑐𝐭0⟩⟩ ← NTT−1(𝑐_hat * 𝐭0̂_hat) let mut ct0 = sk.t0_hat().scalar_vector_ntt(&c_hat); ct0.inv_ntt(); // 28 (first half): if ||⟨⟨𝑐𝐭0⟩⟩||∞ ≥ 𝛾2 or the number of 1’s in 𝐡 is greater than 𝜔, then (z, h) ← ⊥ // out-of-order on purpose for performance reasons: // might as well do the rejection sampling check before any extra heavy computation + // mutants note: there is currently no unit test that triggers this branch if ct0.check_norm::() { kappa += l as u16; continue; @@ -996,6 +1071,7 @@ impl< }; // 28 (second half): if ||⟨⟨𝑐𝐭0⟩⟩||∞ ≥ 𝛾2 or the number of 1’s in 𝐡 is greater than 𝜔, then (z, h) ← ⊥ + // mutants note: there is no test KAT that triggers this branch if hint_hamming_weight > OMEGA { kappa += l as u16; continue; @@ -1018,6 +1094,99 @@ impl< Ok(bytes_written) } + + /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) + /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ . + /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑) and message 𝑀′ ∈ {0, 1}∗ . + /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. + fn verify_internal( + pk: &PK, + A_hat: &Matrix, + mu: &[u8; 64], + sig: &[u8; SIG_LEN], + ) -> Result<(), SignatureError> { + // 1: (𝜌, 𝐭1) ← pkDecode(𝑝𝑘) + // Already done -- the pk struct is already decoded + + // 2: (𝑐_tilde, 𝐳, 𝐡) ← sigDecode(𝜎) + // ▷ signer’s commitment hash c_tilde, response 𝐳, and hint 𝐡 + // 3: if 𝐡 = ⊥ then return false + let (c_tilde, z, h) = + sig_decode::(&sig) + .map_err(|_| SignatureError::SignatureVerificationFailed)?; + + // 13 (first half) return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] + if z.check_norm::() { + return Err(SignatureError::SignatureVerificationFailed); + } + + // 5: 𝐀 ← ExpandA(𝜌) + // ▷ 𝐀 is generated and stored in NTT representation as 𝐀 + // We're doing an optimization where the user can pre-expand A_hat within the + // public key object for faster repeated encapsulations against this public key. + + // 6: 𝑡𝑟 ← H(𝑝𝑘, 64) + // 7: 𝜇 ← (H(BytesToBits(𝑡𝑟)||𝑀 ′, 64)) + // ▷ message representative that may optionally be + // computed in a different cryptographic module + // skip because this function is being handed mu + + // 8: 𝑐 ∈ 𝑅𝑞 ← SampleInBall(c_tilde) + let c_hat = { + let mut c = sample_in_ball::(&c_tilde); + c.ntt(); + + c + }; + + // 9: 𝐰′_approx ← NTT−1(𝐀_hat ∘ NTT(𝐳) − NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑)) + // broken out for clarity: + // NTT−1( + // 𝐀_hat ∘ NTT(𝐳) − + // NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑) + // ) + // ▷ 𝐰'_approx = 𝐀𝐳 − 𝑐𝐭1 ⋅ 2^𝑑 + // weird nested scoping is to reduce peak stack memory usage + let w1p = { + let Az = { + let mut z_hat = z.clone(); + z_hat.ntt(); + A_hat.matrix_vector_ntt(&z_hat) + }; + let ct1 = { + // potential optimization -- pre-compute this on key load? + let mut t1_shift_hat = pk.t1().shift_left::(); + t1_shift_hat.ntt(); + t1_shift_hat.scalar_vector_ntt(&c_hat) + }; + let mut wp_approx = Az.sub_vector(&ct1); + wp_approx.inv_ntt(); + wp_approx.conditional_add_q(); + + // 10: 𝐰1′ ← UseHint(𝐡, 𝐰'_approx) + // ▷ reconstruction of signer’s commitment + use_hint_vecs::(&h, &wp_approx) + }; + // 12: 𝑐_tilde_p ← H(𝜇||w1Encode(𝐰1'), 𝜆/4) + // ▷ hash it; this should match 𝑐_tilde + let c_tilde_p = { + let mut c_tilde_p = [0u8; LAMBDA_over_4]; + let mut hash = H::new(); + hash.absorb(mu); + w1p.w1_encode_and_hash::(&mut hash); + hash.squeeze_out(&mut c_tilde_p); + + c_tilde_p + }; + + // verification probably doesn't technically need to be constant-time, but why not? + // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] and [[𝑐 ̃ = 𝑐′ ]] + if bouncycastle_utils::ct::ct_eq_bytes(&c_tilde, &c_tilde_p) { + Ok(()) + } else { + Err(SignatureError::SignatureVerificationFailed) + } + } } impl< @@ -1040,10 +1209,10 @@ impl< const POLY_Z_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, -> MLDSATrait + const GAMMA1_MASK_LEN: usize, +> MLDSATrait for MLDSA< PK_LEN, SK_LEN, @@ -1063,9 +1232,9 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError> { @@ -1131,6 +1300,8 @@ impl< ctx: Option<&[u8]>, out: &mut [u8; SIG_LEN], ) -> Result { + out.fill(0); + let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?; Self::sign_mu_out(&sk.sk, Some(&sk.A_hat), &mu, out) } @@ -1151,6 +1322,8 @@ impl< mu: &[u8; 64], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mut rnd: [u8; MLDSA_RND_LEN] = [0u8; MLDSA_RND_LEN]; HashDRBG_SHA512::new_from_os().next_bytes_out(&mut rnd)?; @@ -1172,6 +1345,8 @@ impl< mu: &[u8; 64], out: &mut [u8; SIG_LEN], ) -> Result { + out.fill(0); + Self::sign_mu_out(&sk.sk, A_hat, mu, out) } @@ -1193,6 +1368,8 @@ impl< rnd: [u8; 32], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + match A_hat { Some(A_hat) => Self::sign_internal(sk, A_hat, mu, rnd, output), None => Self::sign_internal(sk, &sk.A_hat(), mu, rnd, output), @@ -1217,6 +1394,8 @@ impl< rnd: [u8; 32], output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + // This has been kept as clean as possible for correspondence with the FIPS, // but things have been moved around so that unnamed scopes can be used to limit how many // stack variables are alive at the same time. @@ -1225,7 +1404,7 @@ impl< // to avoid having all of it in memory at the same time, // we're gonna derive what we need as we need it. - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::CryptographicRandom) || seed.key_len() != 32 { return Err(SignatureError::KeyGenError( @@ -1310,6 +1489,7 @@ impl< loop { // FIPS 204 s. 6.2 allows: // "Implementations may limit the number of iterations in this loop to not exceed a finite maximum value." + // mutants note: there is no test for this because we don't know of a KAT that will exceed this limit. if kappa > 1000 * k as u16 { return Err(SignatureError::GenericError( "Rejection sampling loop exceeded max iterations, try again with a different signing nonce.", @@ -1444,6 +1624,7 @@ impl< // Alg 7; 28 (first half): if ||⟨⟨𝑐𝐭0⟩⟩||∞ ≥ 𝛾2 or the number of 1’s in 𝐡 is greater than 𝜔, then (z, h) ← ⊥ // out-of-order on purpose for performance reasons: // might as well do the rejection sampling check before any extra heavy computation + // mutants note: there is currently no unit test that triggers this branch if ct0.check_norm::() { kappa += l as u16; continue; @@ -1462,6 +1643,7 @@ impl< }; // Alg 7; 28 (second half): if ||⟨⟨𝑐𝐭0⟩⟩||∞ ≥ 𝛾2 or the number of 1’s in 𝐡 is greater than 𝜔, then (z, h) ← ⊥ + // mutants note: there is currently no unit test that triggers this branch if hint_hamming_weight > OMEGA { kappa += l as u16; continue; @@ -1509,114 +1691,22 @@ impl< sig: &[u8], ) -> Result<(), SignatureError> { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?; - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(&pk.pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu(&pk.pk, Some(&pk.A_hat()), &mu, sig) } - /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) - /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ . - /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑) and message 𝑀′ ∈ {0, 1}∗ . - /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. - fn verify_mu_internal( + fn verify_mu( pk: &PK, - A_hat: &Matrix, + A_hat: Option<&Matrix>, mu: &[u8; 64], sig: &[u8; SIG_LEN], - ) -> bool { - // 1: (𝜌, 𝐭1) ← pkDecode(𝑝𝑘) - // Already done -- the pk struct is already decoded - - // 2: (𝑐_tilde, 𝐳, 𝐡) ← sigDecode(𝜎) - // ▷ signer’s commitment hash c_tilde, response 𝐳, and hint 𝐡 - // 3: if 𝐡 = ⊥ then return false - let (c_tilde, z, h) = match sig_decode::< - GAMMA1, - k, - l, - LAMBDA_over_4, - OMEGA, - POLY_Z_PACKED_LEN, - SIG_LEN, - >(&sig) - { - Ok((c_tilde, z, h)) => (c_tilde, z, h), - Err(_) => return false, - }; - - // 13 (first half) return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] - if z.check_norm::() { - return false; + ) -> Result<(), SignatureError> { + match A_hat { + Some(A_hat) => Self::verify_internal(pk, A_hat, mu, sig), + None => Self::verify_internal(pk, &pk.A_hat(), mu, sig), } - - // 5: 𝐀 ← ExpandA(𝜌) - // ▷ 𝐀 is generated and stored in NTT representation as 𝐀 - // We're doing an optimization where the user can pre-expand A_hat within the - // public key object for faster repeated encapsulations against this public key. - - // 6: 𝑡𝑟 ← H(𝑝𝑘, 64) - // 7: 𝜇 ← (H(BytesToBits(𝑡𝑟)||𝑀 ′, 64)) - // ▷ message representative that may optionally be - // computed in a different cryptographic module - // skip because this function is being handed mu - - // 8: 𝑐 ∈ 𝑅𝑞 ← SampleInBall(c_tilde) - let c_hat = { - let mut c = sample_in_ball::(&c_tilde); - c.ntt(); - - c - }; - - // 9: 𝐰′_approx ← NTT−1(𝐀_hat ∘ NTT(𝐳) − NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑)) - // broken out for clarity: - // NTT−1( - // 𝐀_hat ∘ NTT(𝐳) − - // NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑) - // ) - // ▷ 𝐰'_approx = 𝐀𝐳 − 𝑐𝐭1 ⋅ 2^𝑑 - // weird nested scoping is to reduce peak stack memory usage - let w1p = { - let Az = { - let mut z_hat = z.clone(); - z_hat.ntt(); - A_hat.matrix_vector_ntt(&z_hat) - }; - let ct1 = { - // potential optimization -- pre-compute this on key load? - let mut t1_shift_hat = pk.t1().shift_left::(); - t1_shift_hat.ntt(); - t1_shift_hat.scalar_vector_ntt(&c_hat) - }; - let mut wp_approx = Az.sub_vector(&ct1); - wp_approx.inv_ntt(); - wp_approx.conditional_add_q(); - - // 10: 𝐰1′ ← UseHint(𝐡, 𝐰'_approx) - // ▷ reconstruction of signer’s commitment - use_hint_vecs::(&h, &wp_approx) - }; - // 12: 𝑐_tilde_p ← H(𝜇||w1Encode(𝐰1'), 𝜆/4) - // ▷ hash it; this should match 𝑐_tilde - let c_tilde_p = { - let mut c_tilde_p = [0u8; LAMBDA_over_4]; - let mut hash = H::new(); - hash.absorb(mu); - w1p.w1_encode_and_hash::(&mut hash); - hash.squeeze_out(&mut c_tilde_p); - - c_tilde_p - }; - - // verification probably doesn't technically need to be constant-time, but why not? - // 13 (second half): return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] and [[𝑐 ̃ = 𝑐′ ]] - bouncycastle_utils::ct::ct_eq_bytes(&c_tilde, &c_tilde_p) } } @@ -1628,11 +1718,31 @@ pub trait MLDSATrait< PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, SK: MLDSAPrivateKeyTrait + MLDSAPrivateKeyInternalTrait, + const LAMBDA: i32, const k: usize, const l: usize, const ETA: usize, >: Sized { + /// Runs a key generation using the library's default RNG, seeded from the OS. + /// In environments where the default OS based RNG is not available, use instead [MLDSA::keygen_from_rng] + /// and explicitly provide a [RNG] implementation, or use [MLDSATrait::keygen_from_seed] and provide the + /// private key seed directly. + fn keygen() -> Result<(PK, SK), SignatureError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::keygen_from_rng(&mut os_rng) + } + /// Run a keygen using the provided RNG implementation. + // Should still be ok in FIPS mode, provided that you're using the FIPS-approved RNG. + fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), SignatureError> { + // Source the seed from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } + let mut seed = KeyMaterial256::new(); + rng.fill_keymaterial_out(&mut seed)?; + Self::keygen_from_seed(&seed) + } /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<32>) -> Result<(PK, SK), SignatureError>; /// Imports a secret key from both a seed and an encoded_sk. @@ -1704,7 +1814,7 @@ pub trait MLDSATrait< msg: &[u8], ctx: Option<&[u8]>, ) -> Result<[u8; 64], SignatureError>; - /// Same as [Signature::sign], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [Signer::sign], but signs from an [MLDSAPrivateKeyExpanded]. fn sign_with_expanded_key( sk: &MLDSAPrivateKeyExpanded, msg: &[u8], @@ -1721,6 +1831,8 @@ pub trait MLDSATrait< /// This implements FIPS 204 Algorithm 7 with line 6 removed; a modification that is allowed by both /// FIPS 204 itself, as well as subsequent FAQ documents. /// This mode uses randomized signing (called "hedged mode" in FIPS 204) using an internal RNG. + /// + /// Optionally, takes a pre-expanded public matrix `A_hat`. fn sign_mu( sk: &SK, A_hat: Option<&Matrix>, @@ -1742,13 +1854,13 @@ pub trait MLDSATrait< mu: &[u8; 64], output: &mut [u8; SIG_LEN], ) -> Result; - /// Same as [Signature::sign_mu], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [MLDSATrait::sign_mu], but signs from an [MLDSAPrivateKeyExpanded]. fn sign_mu_with_expanded_key( sk: &MLDSAPrivateKeyExpanded, A_hat: Option<&Matrix>, mu: &[u8; 64], ) -> Result<[u8; SIG_LEN], SignatureError>; - /// Same as [Signature::sign_mu_out], but signs from an [MLDSAPrivateKeyExpanded]. + /// Same as [MLDSATrait::sign_mu_out], but signs from an [MLDSAPrivateKeyExpanded]. fn sign_mu_with_expanded_key_out( sk: &MLDSAPrivateKeyExpanded, A_hat: Option<&Matrix>, @@ -1838,23 +1950,23 @@ pub trait MLDSATrait< seed: &KeyMaterial<32>, ctx: Option<&[u8]>, ) -> Result; - /// Same as [Signature::verify], but signs from an expanded key object. + /// Same as [SignatureVerifier::verify], but signs from an expanded key object. fn verify_with_expanded_key( pk: &MLDSAPublicKeyExpanded, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8], ) -> Result<(), SignatureError>; - /// Algorithm 8 ML-DSA.Verify_internal(𝑝𝑘, 𝑀′, 𝜎) - /// Internal function to verify a signature 𝜎 for a formatted message 𝑀′ . - /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑) and message 𝑀′ ∈ {0, 1}∗ . - /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. - fn verify_mu_internal( + /// Performs an ML-DSA signature verification using the provided external message representative `mu`. + /// This implements FIPS 204 Algorithm 8 with line 7 removed; a modification that is allowed by both + /// FIPS 204 itself, as well as subsequent FAQ documents. + /// Optionally, takes a pre-expanded public matrix `A_hat`. + fn verify_mu( pk: &PK, - A_hat: &Matrix, + A_hat: Option<&Matrix>, mu: &[u8; 64], sig: &[u8; SIG_LEN], - ) -> bool; + ) -> Result<(), SignatureError>; } impl< @@ -1877,10 +1989,10 @@ impl< const POLY_Z_PACKED_LEN: usize, const POLY_W1_PACKED_LEN: usize, const LAMBDA_over_4: usize, - const GAMMA1_MASK_LEN: usize, const GAMMA1_MINUS_BETA: i32, const GAMMA2_MINUS_BETA: i32, -> Signature + const GAMMA1_MASK_LEN: usize, +> Signer for MLDSA< PK_LEN, SK_LEN, @@ -1900,15 +2012,11 @@ impl< POLY_Z_PACKED_LEN, POLY_W1_PACKED_LEN, LAMBDA_over_4, - GAMMA1_MASK_LEN, GAMMA1_MINUS_BETA, GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, > { - fn keygen() -> Result<(PK, SK), SignatureError> { - Self::keygen_from_os_rng() - } - fn sign(sk: &SK, msg: &[u8], ctx: Option<&[u8]>) -> Result<[u8; SIG_LEN], SignatureError> { let mut out = [0u8; SIG_LEN]; Self::sign_out(sk, msg, ctx, &mut out)?; @@ -1922,6 +2030,8 @@ impl< ctx: Option<&[u8]>, output: &mut [u8; SIG_LEN], ) -> Result { + output.fill(0); + let mu = MuBuilder::compute_mu(&sk.tr(), msg, ctx)?; let bytes_written = Self::sign_mu_out(sk, None, &mu, output)?; @@ -1958,6 +2068,8 @@ impl< )); } + output.fill(0); + if self.sk.is_some() { if self.signer_rnd.is_none() { Self::sign_mu_out(&self.sk.unwrap(), None, &mu, output) @@ -1983,18 +2095,62 @@ impl< unreachable!() } } +} +impl< + const PK_LEN: usize, + const SK_LEN: usize, + const SIG_LEN: usize, + PK: MLDSAPublicKeyTrait + MLDSAPublicKeyInternalTrait, + SK: MLDSAPrivateKeyTrait + + MLDSAPrivateKeyInternalTrait, + const TAU: i32, + const LAMBDA: i32, + const GAMMA1: i32, + const GAMMA2: i32, + const k: usize, + const l: usize, + const ETA: usize, + const BETA: i32, + const OMEGA: i32, + const C_TILDE: usize, + const POLY_Z_PACKED_LEN: usize, + const POLY_W1_PACKED_LEN: usize, + const LAMBDA_over_4: usize, + const GAMMA1_MINUS_BETA: i32, + const GAMMA2_MINUS_BETA: i32, + const GAMMA1_MASK_LEN: usize, +> SignatureVerifier + for MLDSA< + PK_LEN, + SK_LEN, + SIG_LEN, + PK, + SK, + TAU, + LAMBDA, + GAMMA1, + GAMMA2, + k, + l, + ETA, + BETA, + OMEGA, + C_TILDE, + POLY_Z_PACKED_LEN, + POLY_W1_PACKED_LEN, + LAMBDA_over_4, + GAMMA1_MINUS_BETA, + GAMMA2_MINUS_BETA, + GAMMA1_MASK_LEN, + > +{ fn verify(pk: &PK, msg: &[u8], ctx: Option<&[u8]>, sig: &[u8]) -> Result<(), SignatureError> { let mu = MuBuilder::compute_mu(&pk.compute_tr(), msg, ctx)?; - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu(pk, Some(&pk.A_hat()), &mu, sig) } fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result { @@ -2015,20 +2171,14 @@ impl< fn verify_final(self, sig: &[u8]) -> Result<(), SignatureError> { let mu = self.mu_builder.do_final(); - assert!( - self.pk.is_some(), - "Somehow you managed to construct a streaming verifier without a public key, impressive!" - ); - let pk: &PK = &self.pk.unwrap(); - - if sig.len() != SIG_LEN { - return Err(SignatureError::LengthError("Signature value is not the correct length.")); - } - if Self::verify_mu_internal(pk, &pk.A_hat(), &mu, &sig[..SIG_LEN].try_into().unwrap()) { - Ok(()) - } else { - Err(SignatureError::SignatureVerificationFailed) - } + let pk: &PK = self + .pk + .as_ref() + .ok_or(SignatureError::GenericError("No public key set on streaming verifier."))?; + let sig: &[u8; SIG_LEN] = sig.try_into().map_err(|_| { + SignatureError::LengthError("Signature value is not the correct length.") + })?; + Self::verify_mu(pk, Some(&pk.A_hat()), &mu, sig) } } @@ -2040,7 +2190,8 @@ impl< /// Note: this struct is only exposed for "pure" ML-DSA and not for HashML-DSA because HashML-DSA /// does not benefit from allowing external construction of the message representative mu. /// You can get the same behaviour by computing the pre-hash `ph` with the appropriate hash function -/// and providing that to HashMLDSA via [PHSignature::sign_ph]. +/// and providing that to HashMLDSA via [PHSigner::sign_ph]. +#[derive(Clone)] pub struct MuBuilder { h: H, } @@ -2106,3 +2257,25 @@ impl MuBuilder { mu } } + +/// The length, in bytes, of a serialized state of a [MuBuilder] object. +pub const MU_BUILDER_SERIALIZED_STATE_LEN: usize = SUSPENDED_SHA3_STATE_LEN; + +/// If you are processing a large input message into ML-DSA and want to pause the operation +/// -- maybe while waiting for slow network IO), you'll need to use [Suspendable]. +/// Serialization of the state of an in-progress ML-DSA instance is really just serialization +/// of the construction of the message representative mu, since no other part of the ML-DSA algorithm +/// has a pausable state. +// A [MuBuilder]'s (and by virtue, an ML-DSA instance's) entire mutable state is its inner SHAKE256 sponge, +// so serialization delegates directly to [SHAKE256]'s [SerializableState] impl. +impl Suspendable for MuBuilder { + fn suspend(self) -> [u8; SUSPENDED_SHA3_STATE_LEN] { + self.h.suspend() + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA3_STATE_LEN], + ) -> Result { + Ok(MuBuilder { h: H::from_suspended(serialized_state)? }) + } +} diff --git a/crypto/mldsa/src/mldsa_keys.rs b/crypto/mldsa/src/mldsa_keys.rs index 708ca49..c1643ba 100644 --- a/crypto/mldsa/src/mldsa_keys.rs +++ b/crypto/mldsa/src/mldsa_keys.rs @@ -92,6 +92,8 @@ impl MLDSAPublicKey usize { + out.fill(0); + out[0..32].copy_from_slice(&self.rho); let (pk_chunks, last_chunk) = out[32..].as_chunks_mut::(); @@ -201,6 +203,8 @@ impl SignaturePublicKey usize { + out.fill(0); + self.pk_encode_out(out) } @@ -277,6 +281,8 @@ impl< } fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize { + out.fill(0); + self.pk.encode_out(out) } @@ -420,6 +426,75 @@ pub struct MLDSAPrivateKey< seed: Option>, } +impl + MLDSAPrivateKey +{ + /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) + /// Encodes a secret key for ML-DSA into a byte string. + /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with + /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. + /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). + fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + + // counter of progress along the output buffer + let mut off: usize = 0; + + out[0..32].copy_from_slice(&self.rho); + out[32..64].copy_from_slice(&self.K); + out[64..128].copy_from_slice(&self.tr); + off += 128; + + let mut buf = [0u8; 32 * 4]; // largest possible buffer + let eta_pack_len = bitlen_eta(eta); + + let sk_chunks = out[off..off + l * bitlen_eta(eta)].chunks_mut(bitlen_eta(eta)); + debug_assert_eq!(sk_chunks.len(), l); + for (sk_chunk, s1_hat_i) in sk_chunks.into_iter().zip(&self.s1_hat.vec) { + // Deviation from the FIPS: + // We are holding these in ntt form, so need to convert back to standard form + let mut s1_hat_i = s1_hat_i.clone(); + s1_hat_i.reduce(); + s1_hat_i.inv_ntt(); + let s1_i = s1_hat_i; + + bit_pack_eta::(&s1_i, &mut buf); + sk_chunk.copy_from_slice(&buf[..eta_pack_len]); + } + off += l * bitlen_eta(eta); + + let sk_chunks = out[off..off + k * bitlen_eta(eta)].chunks_mut(bitlen_eta(eta)); + debug_assert_eq!(sk_chunks.len(), k); + for (sk_chunk, s2_hat_i) in sk_chunks.into_iter().zip(&self.s2_hat.vec) { + // Deviation from the FIPS: + // We are holding these in ntt form, so need to convert back to standard form + let mut s2_hat_i = s2_hat_i.clone(); + s2_hat_i.reduce(); + s2_hat_i.inv_ntt(); + let s2_i = s2_hat_i; + + bit_pack_eta::(&s2_i, &mut buf); + sk_chunk.copy_from_slice(&buf[..eta_pack_len]); + } + off += k * bitlen_eta(eta); + + let sk_chunks = out[off..off + k * POLY_T0PACKED_LEN].chunks_mut(POLY_T0PACKED_LEN); + debug_assert_eq!(sk_chunks.len(), k); + for (sk_chunk, t0_hat_i) in sk_chunks.into_iter().zip(&self.t0_hat.vec) { + // Deviation from the FIPS: + // We are holding these in ntt form, so need to convert back to standard form + let mut t0_hat_i = t0_hat_i.clone(); + t0_hat_i.reduce(); + t0_hat_i.inv_ntt(); + let t0_i = t0_hat_i; + + sk_chunk.copy_from_slice(&bit_pack_t0(&t0_i)); + } + + SK_LEN + } +} + /// General trait for all ML-DSA private keys types. pub trait MLDSAPrivateKeyTrait< const k: usize, @@ -430,7 +505,7 @@ pub trait MLDSAPrivateKeyTrait< >: SignaturePrivateKey { /// Get a ref to the seed, if there is one stored with this private key - fn seed(&self) -> &Option>; + fn seed(&self) -> Option<&KeyMaterial<32>>; /// Get a ref to the key hash `tr`. fn tr(&self) -> &[u8; 64]; @@ -440,18 +515,6 @@ pub trait MLDSAPrivateKeyTrait< /// This is a partial implementation of keygen_internal(), and probably not allowed in FIPS mode. fn derive_pk(&self) -> MLDSAPublicKey; - /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) - /// Encodes a secret key for ML-DSA into a byte string. - /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with - /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. - /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). - fn sk_encode(&self) -> [u8; SK_LEN]; - /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) - /// Encodes a secret key for ML-DSA into a byte string. - /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with - /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. - /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). - fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize; /// Algorithm 25 skDecode(𝑠𝑘) /// Reverses the procedure skEncode. /// Input: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((ℓ+𝑘)⋅bitlen (2𝜂)+𝑑𝑘). @@ -495,8 +558,11 @@ pub(crate) trait MLDSAPrivateKeyInternalTrait< impl MLDSAPrivateKeyTrait for MLDSAPrivateKey { - fn seed(&self) -> &Option> { - &self.seed + fn seed(&self) -> Option<&KeyMaterial<32>> { + match self.seed { + Some(_) => self.seed.as_ref(), + None => None, + } } fn tr(&self) -> &[u8; 64] { @@ -538,79 +604,6 @@ impl::new(self.rho.clone(), t1) } - /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) - /// Encodes a secret key for ML-DSA into a byte string. - /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with - /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. - /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). - fn sk_encode(&self) -> [u8; SK_LEN] { - let mut out = [0u8; SK_LEN]; - let bytes_written = self.sk_encode_out(&mut out); - debug_assert_eq!(bytes_written, SK_LEN); - out - } - /// Algorithm 24 skEncode(𝜌, 𝐾, 𝑡𝑟, 𝐬1, 𝐬2, 𝐭0) - /// Encodes a secret key for ML-DSA into a byte string. - /// Input: 𝜌 ∈ 𝔹32, 𝐾 ∈ 𝔹32, 𝑡𝑟 ∈ 𝔹64 , 𝐬1 ∈ 𝑅ℓ with coefficients in [−𝜂, 𝜂], 𝐬2 ∈ 𝑅𝑘 with - /// coefficients in [−𝜂, 𝜂], 𝐭0 ∈ 𝑅𝑘 with coefficients in [−2𝑑−1 + 1, 2𝑑−1]. - /// Output: Private key 𝑠𝑘 ∈ 𝔹32+32+64+32⋅((𝑘+ℓ)⋅bitlen (2𝜂)+𝑑𝑘). - fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { - // counter of progress along the output buffer - let mut off: usize = 0; - - out[0..32].copy_from_slice(&self.rho); - out[32..64].copy_from_slice(&self.K); - out[64..128].copy_from_slice(&self.tr); - off += 128; - - let mut buf = [0u8; 32 * 4]; // largest possible buffer - let eta_pack_len = bitlen_eta(eta); - - let sk_chunks = out[off..off + l * bitlen_eta(eta)].chunks_mut(bitlen_eta(eta)); - debug_assert_eq!(sk_chunks.len(), l); - for (sk_chunk, s1_hat_i) in sk_chunks.into_iter().zip(&self.s1_hat.vec) { - // Deviation from the FIPS: - // We are holding these in ntt form, so need to convert back to standard form - let mut s1_hat_i = s1_hat_i.clone(); - s1_hat_i.reduce(); - s1_hat_i.inv_ntt(); - let s1_i = s1_hat_i; - - bit_pack_eta::(&s1_i, &mut buf); - sk_chunk.copy_from_slice(&buf[..eta_pack_len]); - } - off += l * bitlen_eta(eta); - - let sk_chunks = out[off..off + k * bitlen_eta(eta)].chunks_mut(bitlen_eta(eta)); - debug_assert_eq!(sk_chunks.len(), k); - for (sk_chunk, s2_hat_i) in sk_chunks.into_iter().zip(&self.s2_hat.vec) { - // Deviation from the FIPS: - // We are holding these in ntt form, so need to convert back to standard form - let mut s2_hat_i = s2_hat_i.clone(); - s2_hat_i.reduce(); - s2_hat_i.inv_ntt(); - let s2_i = s2_hat_i; - - bit_pack_eta::(&s2_i, &mut buf); - sk_chunk.copy_from_slice(&buf[..eta_pack_len]); - } - off += k * bitlen_eta(eta); - - let sk_chunks = out[off..off + k * POLY_T0PACKED_LEN].chunks_mut(POLY_T0PACKED_LEN); - debug_assert_eq!(sk_chunks.len(), k); - for (sk_chunk, t0_hat_i) in sk_chunks.into_iter().zip(&self.t0_hat.vec) { - // Deviation from the FIPS: - // We are holding these in ntt form, so need to convert back to standard form - let mut t0_hat_i = t0_hat_i.clone(); - t0_hat_i.reduce(); - t0_hat_i.inv_ntt(); - let t0_i = t0_hat_i; - - sk_chunk.copy_from_slice(&bit_pack_t0(&t0_i)); - } - - SK_LEN - } fn sk_decode(sk: &[u8; SK_LEN]) -> Result { let rho = sk[0..32].try_into().unwrap(); let K = sk[32..64].try_into().unwrap(); @@ -725,10 +718,16 @@ impl for MLDSAPrivateKey { fn encode(&self) -> [u8; SK_LEN] { - self.sk_encode() + let mut out = [0u8; SK_LEN]; + let bytes_written = self.sk_encode_out(&mut out); + debug_assert_eq!(bytes_written, SK_LEN); + + out } fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + self.sk_encode_out(out) } @@ -753,8 +752,8 @@ impl { fn eq(&self, other: &Self) -> bool { - let self_encoded = self.sk_encode(); - let other_encoded = other.sk_encode(); + let self_encoded = self.encode(); + let other_encoded = other.encode(); bouncycastle_utils::ct::ct_eq_bytes(self_encoded.as_ref(), other_encoded.as_ref()) } } @@ -985,6 +984,8 @@ impl< } fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + self.sk.encode_out(out) } @@ -1006,7 +1007,7 @@ impl< > MLDSAPrivateKeyTrait for MLDSAPrivateKeyExpanded { - fn seed(&self) -> &Option> { + fn seed(&self) -> Option<&KeyMaterial<32>> { self.sk.seed() } @@ -1022,14 +1023,6 @@ impl< self.sk.derive_pk() } - fn sk_encode(&self) -> [u8; SK_LEN] { - self.sk.sk_encode() - } - - fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { - self.sk.sk_encode_out(out) - } - fn sk_decode(sk: &[u8; SK_LEN]) -> Result { let sk1 = SK::sk_decode(sk)?; let A_hat = sk1.derive_pk().A_hat(); diff --git a/crypto/mldsa/src/polynomial.rs b/crypto/mldsa/src/polynomial.rs index b419b61..b5f0999 100644 --- a/crypto/mldsa/src/polynomial.rs +++ b/crypto/mldsa/src/polynomial.rs @@ -1,18 +1,22 @@ //! Represents a polynomial over the ML-DSA ring. +use crate::aux_functions::{ + ZETAS, conditional_add_q, high_bits, low_bits, make_hint, montgomery_reduce, +}; +use crate::mldsa::{MLDSA44_POLY_W1_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, N, q}; +use bouncycastle_core::traits::Secret; use core::fmt; use core::fmt::{Debug, Display, Formatter}; use core::ops::{Index, IndexMut}; -use bouncycastle_core::traits::Secret; -use crate::mldsa::{N, MLDSA44_POLY_W1_PACKED_LEN, MLDSA65_POLY_W1_PACKED_LEN, q}; -use crate::aux_functions::{conditional_add_q, high_bits, low_bits, make_hint, montgomery_reduce, ZETAS}; /// A polynomial over the ML-DSA ring. /// Dev note: this doesn't strictly need to be pub ... ie there's no good reason for a caller to use this class directly, /// but in order to test the Debug and Display traits, you need STD, so those can't be tested from inline tests in this file /// and the real unit tests are in a different crate, so here we are. #[derive(Clone)] -pub struct Polynomial{ pub(crate) coeffs: [i32; N] } +pub struct Polynomial { + pub(crate) coeffs: [i32; N], +} /// Convenience function to avoid ".0" all over the place. impl Index for Polynomial { @@ -32,7 +36,7 @@ impl IndexMut for Polynomial { impl Polynomial { /// Create a new polynomial with all coefficients set to zero. pub const fn new() -> Self { - Self{ coeffs: [0i32; N] } + Self { coeffs: [0i32; N] } } pub(crate) fn conditional_add_q(&mut self) { @@ -40,7 +44,7 @@ impl Polynomial { *x = conditional_add_q(*x); } } - + pub(crate) fn reduce(&mut self) { for i in 0..N { self[i] = montgomery_reduce(self[i] as i64); @@ -84,15 +88,14 @@ impl Polynomial { // Fine that this is not constant-time (returns true early) because it is used in a rejection loop. // IE the early quit here leads to rejection and continuing to the top of the rejection loop, or failing the signature validation. // So the i32 that we just checked in a non-constant-time manner is about to get thrown away. - + // Note: this formulation of the check_norm function usually requires this bounds check // if bound > (q - 1) / 8 { // return true; // } // but since BOUND is a constant here, we'll just do a debug_assert to make sure the value is what we expect. debug_assert!(BOUND <= (q - 1) / 8); - - + let mut t: i32; for x in self.coeffs.iter() { t = *x >> 31; @@ -118,6 +121,8 @@ impl Polynomial { for i in 0..N { let x = make_hint::(self[i], r[i]); out[i] = x; + + // mutants note: this chains up to hint_hamming_weight > OMEGA and there is no test KAT that triggers this branch count += x; } @@ -129,22 +134,21 @@ impl Polynomial { match POLY_W1_PACKED_LEN { MLDSA44_POLY_W1_PACKED_LEN => { - for i in 0..N/4 { - r[3 * i] = - ((self[4 * i]) as u8) | ((self[4 * i + 1] << 6) as u8); - r[3 * i + 1] = - ((self[4 * i + 1] >> 2) as u8) | ((self[4 * i + 2] << 4) as u8); - r[3 * i + 2] = - ((self[4 * i + 2] >> 4) as u8) | ((self[4 * i + 3] << 2) as u8); + for i in 0..N / 4 { + r[3 * i] = ((self[4 * i]) as u8) | ((self[4 * i + 1] << 6) as u8); + r[3 * i + 1] = ((self[4 * i + 1] >> 2) as u8) | ((self[4 * i + 2] << 4) as u8); + r[3 * i + 2] = ((self[4 * i + 2] >> 4) as u8) | ((self[4 * i + 3] << 2) as u8); } - }, + } // ML-DSA65 and 87 share a POLY_W1_PACKED_LEN value MLDSA65_POLY_W1_PACKED_LEN => { - for i in 0..N/2 { + for i in 0..N / 2 { r[i] = ((self[2 * i]) | (self[2 * i + 1] << 4)) as u8; } - }, - _ => { unreachable!() } + } + _ => { + unreachable!() + } } r diff --git a/crypto/mldsa/tests/bc_test_data.rs b/crypto/mldsa/tests/bc_test_data.rs index a504188..3891229 100644 --- a/crypto/mldsa/tests/bc_test_data.rs +++ b/crypto/mldsa/tests/bc_test_data.rs @@ -8,650 +8,927 @@ use bouncycastle_core::errors::SignatureError; use bouncycastle_core::traits::XOF; use bouncycastle_sha3::SHAKE256; -// commenting out because mutants copies to a /tmp dir, and so the relative links to ../bc-test-data break #[cfg(test)] -// mod bc_test_data { -// use std::{fs}; -// use std::path::Path; -// use bouncycastle_core::errors::SignatureError; -// use bouncycastle_hex as hex; -// use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial256, KeyType}; -// use bouncycastle_core::traits::{Hash, SecurityStrength, Signature, SignaturePrivateKey, SignaturePublicKey}; -// use bouncycastle_mldsa::{HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSATrait, MLDSA44, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA65, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA87, MLDSA87_PK_LEN, MLDSA87_SK_LEN}; -// use bouncycastle_sha2::SHA512; -// use crate::BustedMuBuilder; -// -// const TEST_DATA_PATH_RELATIVE: &str = "../../../bc-test-data/pqc/crypto/mldsa"; -// const TEST_DATA_PATH: &str = "../bc-test-data/pqc/crypto/mldsa"; -// -// // commenting out because mutants copies to a /tmp dir, and so the relative links to ../bc-test-data break -// #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_keyGen() { -// let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { -// fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/ML-DSA-keyGen.txt").unwrap() -// } else if Path::new(TEST_DATA_PATH).exists() { -// fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-keyGen.txt").unwrap() -// } else { -// println!("Current working directory: {:?}", std::env::current_dir().unwrap()); -// panic!("Test data directory not found") -// }; -// -// let test_cases = KeyGenTestCase::parse(contents); -// -// for test_case in test_cases { -// test_case.run(); -// } -// } -// -// #[derive(Clone)] -// struct KeyGenTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// tc_id: u32, -// seed: String, -// pk: String, -// sk: String, -// } -// -// impl KeyGenTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, seed: String::new(), pk: String::new(), sk: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = KeyGenTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "seed" => test_case.seed = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "sk" => test_case.sk = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "keyGen"); -// -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA44_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA44_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// "ML-DSA-65" => { -// let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA65_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA65_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// "ML-DSA-87" => { -// let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA87_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA87_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // commenting out because mutants copies to a /tmp dir, and so the relative links to ../bc-test-data break -// #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_sigGen() { -// let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { -// fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/ML-DSA-sigGen.txt").unwrap() -// } else { -// fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-sigGen.txt").unwrap() -// }; -// -// let test_cases = SigGenTestCase::parse(contents); -// -// let num_tests = test_cases.len(); -// for test_case in test_cases { -// test_case.run(); -// } -// -// println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); -// } -// -// #[derive(Clone)] -// struct SigGenTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// deterministic: bool, -// tc_id: u32, -// sk: String, -// message: String, -// rnd: String, -// signature: String, -// } -// -// impl SigGenTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), deterministic: false, tc_id: 0, sk: String::new(), message: String::new(), rnd: String::new(), signature: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = SigGenTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full(){ test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "deterministic" => test_case.deterministic = value.parse().unwrap(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "sk" => test_case.sk = value.to_string(), -// "message" => test_case.message = value.to_string(), -// "rnd" => test_case.rnd = value.to_string(), -// "signature" => test_case.signature = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "sigGen"); -// -// let rnd = if self.deterministic{ [0u8; 32] } else { hex::decode(&self.rnd).unwrap().as_slice().try_into().unwrap() }; -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let sk = MLDSA44PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA44::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap(), "ML-DSA-sigGen params: {}, vsId: {}, tgId: {}, tcId: {}", self.parameter_set, self.vs_id, self.tg_id, self.tc_id); -// }, -// "ML-DSA-65" => { -// let sk = MLDSA65PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA65::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); -// }, -// "ML-DSA-87" => { -// let sk = MLDSA87PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA87::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // This is also against the non-compliant mu that doesn't have a ctx, which I don't have an easy way to test -// // #[test] -// #[allow(unused)] -// #[allow(non_snake_case)] -// fn ML_DSA_sigVer() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-sigVer.txt").unwrap(); -// let test_cases = SigVerTestCase::parse(contents); -// -// for test_case in test_cases { -// test_case.run(); -// } -// } -// -// #[derive(Clone)] -// struct SigVerTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// pk: String, -// tc_id: u32, -// message: String, -// signature: String, -// test_passed: bool, -// } -// -// impl SigVerTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, pk: String::new(), message: String::new(), signature: String::new(), test_passed: false} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = SigVerTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "message" => test_case.message = value.to_string(), -// "signature" => test_case.signature = value.to_string(), -// "testPassed" => test_case.test_passed = value.parse().unwrap(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "sigVer"); -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let pk = MLDSA44PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA44::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if !self.test_passed { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if self.test_passed { panic!("Verification failed when it shouldn't have! vsId: {}, tgId: {}, tcId: {}", self.vs_id, self.tg_id, self.tc_id) } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// "ML-DSA-65" => { -// let pk = MLDSA65PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA65::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if self.test_passed { /* good */ } else { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if !self.test_passed {} else { panic!("Verification failed when it should have!") } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// "ML-DSA-87" => { -// let pk = MLDSA87PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA87::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if self.test_passed { /* good */ } else { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if !self.test_passed {} else { panic!("Verification failed when it should have!") } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // not working, not totally sure why -// // #[test] -// #[allow(unused)] -// #[allow(non_snake_case)] -// fn ML_DSA_rsp() { -// // MLDsa44 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa44.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa44"); -// } -// -// // MLDsa65 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa65.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa65"); -// } -// -// -// // MLDsa87 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa87.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa87"); -// } -// -// -// // MLDsa44 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa44sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa44"); -// } -// -// // MLDsa65 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa65sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MlDsa65"); -// } -// -// -// // MLDsa87 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa87sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MlDsa87"); -// } -// } -// -// #[derive(Clone)] -// struct MldsaRspTestCase { -// count: u32, -// seed: String, -// mlen: u32, -// msg: String, -// pk: String, -// sk: String, -// smlen: u32, -// sm: String, -// message_hash: String, -// message_prime: String, -// context: String, -// } -// -// impl MldsaRspTestCase { -// fn new() -> Self { -// Self{ count: 0, seed: String::new(), mlen: 0, msg: String::new(), pk: String::new(), sk: String::new(), smlen: 0, sm: String::new(), message_hash: String::new(), message_prime: String::new(), context: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.seed.is_empty() -// } -// -// fn parse(data: String) -> Vec> { -// let mut test_cases = Vec::<>::new(); -// let mut test_case = MldsaRspTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "count" => test_case.count = value.parse().unwrap(), -// "seed" => test_case.seed = value.to_string(), -// "mlen" => test_case.mlen = value.parse().unwrap(), -// "msg" => test_case.msg = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "sk" => test_case.sk = value.to_string(), -// "smlen" => test_case.smlen = value.parse().unwrap(), -// "sm" => test_case.sm = value.to_string(), -// "message_hash" => test_case.message_hash = value.to_string(), -// "message_prime" => test_case.message_prime = value.to_string(), -// "context" => { -// test_case.context = value.to_string(); -// if test_case.context == "zero_length" || test_case.context == "none" { -// test_case.context = String::new(); -// } -// }, -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self, parameter_set: &str) { -// match parameter_set { -// "MLDsa44" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// -// let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA44_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA44_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( -// &sk, None, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA44_with_SHA512::verify(&pk, -// &*hex::decode(&self.msg).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).expect(&format!("paramSet: {}, is_hash: {}, count: {}", parameter_set, IS_HASH_MLDSA, self.count)); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap(), "paramSet: {}, count: {}", parameter_set, self.count); -// -// MLDSA44::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// "MlDsa65" | "MLDsa65" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA65_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA65_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA65_with_SHA512::sign_ph_deterministic( -// &sk, None, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA65_with_SHA512::verify(&pk, -// &*hex::decode(&self.message_hash).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).expect(&format!("paramSet: {}, isHash: {}, count: {}", parameter_set, IS_HASH_MLDSA, self.count)); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// MLDSA65::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// "MLDsa87" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA87_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA87_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic( -// &sk, None, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA87_with_SHA512::verify(&pk, -// &*hex::decode(&self.message_hash).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).unwrap(); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// MLDSA87::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// val => panic!("Invalid parameter set: {}", val), -// } -// } -// } -// } +mod bc_test_data { + use crate::BustedMuBuilder; + use bouncycastle_core::errors::SignatureError; + use bouncycastle_core::key_material::{ + KeyMaterial256, KeyMaterialTrait, KeyType, do_hazardous_operations, + }; + use bouncycastle_core::traits::{ + Hash, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, + }; + use bouncycastle_hex as hex; + use bouncycastle_mldsa::{ + HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA44, + MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, + MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87, + MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, + MLDSATrait, + }; + use bouncycastle_sha2::SHA512; + use std::fs; + use std::path::Path; + use std::sync::Once; + const TEST_DATA_PATH_RELATIVE: &str = "../../../bc-test-data/pqc/crypto/mldsa"; + const TEST_DATA_PATH: &str = "../bc-test-data/pqc/crypto/mldsa"; + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + #[allow(non_snake_case)] + fn ML_DSA_keyGen() { + let contents = match get_test_data("ML-DSA-keyGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = KeyGenTestCase::parse(contents); + + for test_case in test_cases { + test_case.run(); + } + } + + #[derive(Clone)] + struct KeyGenTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + tc_id: u32, + seed: String, + pk: String, + sk: String, + } + + impl KeyGenTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + seed: String::new(), + pk: String::new(), + sk: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = KeyGenTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "seed" => test_case.seed = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "sk" => test_case.sk = value.to_string(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "keyGen"); + + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA44_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA44_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + "ML-DSA-65" => { + let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA65_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA65_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + "ML-DSA-87" => { + let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA87_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA87_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + #[test] + #[allow(non_snake_case)] + fn ML_DSA_sigGen() { + let contents = match get_test_data("ML-DSA-sigGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = SigGenTestCase::parse(contents); + + let num_tests = test_cases.len(); + for test_case in test_cases { + test_case.run(); + } + + println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); + } + + #[derive(Clone)] + struct SigGenTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + deterministic: bool, + tc_id: u32, + sk: String, + message: String, + rnd: String, + signature: String, + } + + impl SigGenTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + deterministic: false, + tc_id: 0, + sk: String::new(), + message: String::new(), + rnd: String::new(), + signature: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = SigGenTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "deterministic" => test_case.deterministic = value.parse().unwrap(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "sk" => test_case.sk = value.to_string(), + "message" => test_case.message = value.to_string(), + "rnd" => test_case.rnd = value.to_string(), + "signature" => test_case.signature = value.to_string(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "sigGen"); + + let rnd = if self.deterministic { + [0u8; 32] + } else { + hex::decode(&self.rnd).unwrap().as_slice().try_into().unwrap() + }; + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let sk = + MLDSA44PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA44::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); + assert_eq!( + &sig, + &*hex::decode(&self.signature).unwrap(), + "ML-DSA-sigGen params: {}, vsId: {}, tgId: {}, tcId: {}", + self.parameter_set, + self.vs_id, + self.tg_id, + self.tc_id + ); + } + "ML-DSA-65" => { + let sk = + MLDSA65PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA65::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); + assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); + } + "ML-DSA-87" => { + let sk = + MLDSA87PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); + + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + // let mu = MLDSA87::compute_mu_from_tr( + // &hex::decode(&self.message).unwrap(), + // None, + // sk.tr(), + // ).unwrap(); + let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); + mb.do_update(&hex::decode(&self.message).unwrap()); + let mu = mb.do_final(); + + let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, rnd).unwrap(); + assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // this seems buggy and I'm not sure why. Possibly because the bc-test-data was written against Round 3 Dilithium and not ML-DSA. + // todo -- debug + // #[test] + #[allow(unused)] + #[allow(non_snake_case)] + fn ML_DSA_sigVer() { + let contents = match get_test_data("ML-DSA-sigVer.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = SigVerTestCase::parse(contents); + + for test_case in test_cases { + test_case.run(); + } + } + + #[derive(Clone)] + struct SigVerTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + pk: String, + tc_id: u32, + message: String, + signature: String, + test_passed: bool, + } + + impl SigVerTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + pk: String::new(), + message: String::new(), + signature: String::new(), + test_passed: false, + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = SigVerTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "message" => test_case.message = value.to_string(), + "signature" => test_case.signature = value.to_string(), + "testPassed" => test_case.test_passed = value.parse().unwrap(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "sigVer"); + + match self.parameter_set.as_str() { + "ML-DSA-44" => { + let pk = MLDSA44PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + // No ctx because the bc-test-data tests were written against an earlier version of the spec + // that didn't have it. + match MLDSA44::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if !self.test_passed { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if self.test_passed { + panic!( + "Verification failed when it shouldn't have! vsId: {}, tgId: {}, tcId: {}", + self.vs_id, self.tg_id, self.tc_id + ) + } + } + _ => panic!("An unexpected error occurred"), + } + } + "ML-DSA-65" => { + let pk = MLDSA65PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + match MLDSA65::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if self.test_passed { /* good */ + } else { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if !self.test_passed { + } else { + panic!("Verification failed when it should have!") + } + } + _ => panic!("An unexpected error occurred"), + } + } + "ML-DSA-87" => { + let pk = MLDSA87PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); + + match MLDSA87::verify( + &pk, + &hex::decode(&self.message).unwrap(), + None, + &hex::decode(&self.signature).unwrap(), + ) { + Ok(()) => { + if self.test_passed { /* good */ + } else { + panic!("Verification succeeded when it shouldn't have!") + } + } + Err(SignatureError::SignatureVerificationFailed) => { + if !self.test_passed { + } else { + panic!("Verification failed when it should have!") + } + } + _ => panic!("An unexpected error occurred"), + } + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // this seems buggy and I'm not sure why. Possibly because the bc-test-data was written against Round 3 Dilithium and not ML-DSA. + // todo -- debug + // #[test] + #[allow(unused)] + #[allow(non_snake_case)] + fn ML_DSA_rsp() { + // MLDsa44 + let contents = match get_test_data("mldsa44.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa44"); + } + + // MLDsa65 + let contents = match get_test_data("mldsa65.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa65"); + } + + // MLDsa87 + let contents = match get_test_data("mldsa87.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa87"); + } + + // MLDsa44sha512 + let contents = match get_test_data("mldsa44sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MLDsa44"); + } + + // MLDsa65sha512 + let contents = match get_test_data("mldsa65sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MlDsa65"); + } + + // MLDsa87sha512 + let contents = match get_test_data("mldsa87sha512.rsp") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MldsaRspTestCase::::parse(contents); + for test_case in test_cases { + test_case.run("MlDsa87"); + } + } + + #[derive(Clone)] + struct MldsaRspTestCase { + count: u32, + seed: String, + mlen: u32, + msg: String, + pk: String, + sk: String, + smlen: u32, + sm: String, + message_hash: String, + message_prime: String, + context: String, + } + + impl MldsaRspTestCase { + fn new() -> Self { + Self { + count: 0, + seed: String::new(), + mlen: 0, + msg: String::new(), + pk: String::new(), + sk: String::new(), + smlen: 0, + sm: String::new(), + message_hash: String::new(), + message_prime: String::new(), + context: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.seed.is_empty() + } + + fn parse(data: String) -> Vec> { + let mut test_cases = Vec::new(); + let mut test_case = MldsaRspTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "count" => test_case.count = value.parse().unwrap(), + "seed" => test_case.seed = value.to_string(), + "mlen" => test_case.mlen = value.parse().unwrap(), + "msg" => test_case.msg = value.to_string(), + "pk" => test_case.pk = value.to_string(), + "sk" => test_case.sk = value.to_string(), + "smlen" => test_case.smlen = value.parse().unwrap(), + "sm" => test_case.sm = value.to_string(), + "message_hash" => test_case.message_hash = value.to_string(), + "message_prime" => test_case.message_prime = value.to_string(), + "context" => { + test_case.context = value.to_string(); + if test_case.context == "zero_length" || test_case.context == "none" { + test_case.context = String::new(); + } + } + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self, parameter_set: &str) { + match parameter_set { + "MLDsa44" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA44_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA44_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + None, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA44_with_SHA512::verify( + &pk, + &*hex::decode(&self.msg).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .expect(&format!( + "paramSet: {}, is_hash: {}, count: {}", + parameter_set, IS_HASH_MLDSA, self.count + )); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = + MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!( + sig, + &*hex::decode(&self.sm).unwrap(), + "paramSet: {}, count: {}", + parameter_set, + self.count + ); + + MLDSA44::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + "MlDsa65" | "MLDsa65" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA65_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA65_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA65_with_SHA512::sign_ph_deterministic( + &sk, + None, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA65_with_SHA512::verify( + &pk, + &*hex::decode(&self.message_hash).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .expect(&format!( + "paramSet: {}, isHash: {}, count: {}", + parameter_set, IS_HASH_MLDSA, self.count + )); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = + MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + MLDSA65::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + "MLDsa87" => { + let mut seed = KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) + .unwrap(); + // for the purposes of the test cases, accept an all-zero seed + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLDSA87_PK_LEN] = + hex::decode(&self.pk).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLDSA87_SK_LEN] = + hex::decode(&self.sk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode(), sk_sized); + + if IS_HASH_MLDSA { + // we're only testing SHA512 + let ph: [u8; 64] = SHA512::new() + .hash(&hex::decode(&self.msg).unwrap()) + .as_slice() + .try_into() + .unwrap(); + assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); + + let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic( + &sk, + None, + Some(&*hex::decode(&self.context).unwrap()), + &ph, + [0u8; 32], + ) + .unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + HashMLDSA87_with_SHA512::verify( + &pk, + &*hex::decode(&self.message_hash).unwrap(), + Some(&*hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } else { + // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() + // so need to manually compute mu + let mu = MLDSA65::compute_mu_from_tr( + sk.tr(), + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + ) + .unwrap(); + + let sig = + MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); + + MLDSA87::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + Some(&hex::decode(&self.context).unwrap()), + &sig, + ) + .unwrap(); + } + } + val => panic!("Invalid parameter set: {}", val), + } + } + } +} /// This builds a "busted" mu where the ctx is absent (not 0-length, but actually not there) /// just for the sake of compatibility with the bc-test-data tests @@ -671,7 +948,7 @@ impl BustedMuBuilder { } /// This function requires the public key hash `tr`, which can be computed from the public key using [MLDSAPublicKey::compute_tr]. - pub fn do_init(tr: &[u8; 64], /*ctx: Option<&[u8]>*/) -> Result { + pub fn do_init(tr: &[u8; 64] /*ctx: Option<&[u8]>*/) -> Result { // let ctx = match ctx { // Some(ctx) => ctx, // None => &[] @@ -714,4 +991,4 @@ impl BustedMuBuilder { mu } -} \ No newline at end of file +} diff --git a/crypto/mldsa/tests/hash_mldsa_tests.rs b/crypto/mldsa/tests/hash_mldsa_tests.rs index 10d293e..ecdfb7c 100644 --- a/crypto/mldsa/tests/hash_mldsa_tests.rs +++ b/crypto/mldsa/tests/hash_mldsa_tests.rs @@ -2,7 +2,9 @@ mod hash_mldsa_tests { use bouncycastle_core::errors::SignatureError; use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; - use bouncycastle_core::traits::{Hash, Signature}; + use bouncycastle_core::traits::{ + Hash, PHSignatureVerifier, PHSigner, SignatureVerifier, Signer, + }; use bouncycastle_core_test_framework::signature::TestFrameworkSignature; use bouncycastle_hex as hex; use bouncycastle_mldsa::{ @@ -14,26 +16,26 @@ mod hash_mldsa_tests { use bouncycastle_mldsa::{MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN}; use bouncycastle_mldsa::{MLDSA65_PK_LEN, MLDSA65_SIG_LEN, MLDSA65_SK_LEN}; use bouncycastle_mldsa::{MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN}; - use bouncycastle_sha2::SHA512; + use bouncycastle_sha2::{SHA256, SHA512}; #[test] fn core_framework_signature() { let tf = TestFrameworkSignature::new(false, true); // Test HashML-DSA-SHA512 as a regular signature alg - tf.test_signature::(false); - tf.test_signature::(false); - tf.test_signature::(false); + tf.test_signature::(HashMLDSA44_with_SHA512::keygen, false); + tf.test_signature::(HashMLDSA65_with_SHA512::keygen, false); + tf.test_signature::(HashMLDSA87_with_SHA512::keygen, false); // Test HashML-DSA-SHA256 as a ph signature alg - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); + tf.test_ph_signature::(HashMLDSA44_with_SHA256::keygen, false); + tf.test_ph_signature::(HashMLDSA65_with_SHA256::keygen, false); + tf.test_ph_signature::(HashMLDSA87_with_SHA256::keygen, false); // Test HashML-DSA-SHA512 as a ph signature alg - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); - tf.test_ph_signature::(false); + tf.test_ph_signature::(HashMLDSA44_with_SHA512::keygen, false); + tf.test_ph_signature::(HashMLDSA65_with_SHA512::keygen, false); + tf.test_ph_signature::(HashMLDSA87_with_SHA512::keygen, false); } #[test] @@ -67,7 +69,7 @@ mod hash_mldsa_tests { _ => panic!("Expected error for sig too short"), } - // too long + // sig too long let mut sig_too_long = [0u8; MLDSA44_SIG_LEN + 2]; sig_too_long[..MLDSA44_SIG_LEN].copy_from_slice(&sig); sig_too_long[MLDSA44_SIG_LEN..].copy_from_slice(&[1u8, 0u8]); @@ -75,6 +77,79 @@ mod hash_mldsa_tests { Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } + + // sign_ph_deterministic ctx just right at 255 + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + None, + /*ctx*/ Some(&[1u8; 255]), + /*ph*/ &[2u8; 64], + [3u8; 32], + ) + .unwrap(); + HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[1u8; 255]), &sig).unwrap(); + + // sign_ph_deterministic ctx too long + match HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + None, + /*ctx*/ Some(&[1u8; 256]), + /*ph*/ &[2u8; 64], + [3u8; 32], + ) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + + // sign_ph_deterministic ctx just right at 255 + let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + None, + Some(&[1u8; 255]), + &[2u8; 64], + [3u8; 32], + ) + .unwrap(); + HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[1u8; 255]), &sig).unwrap(); + + // sign_ph_deterministic ctx too long + match HashMLDSA44_with_SHA512::sign_ph_deterministic( + &sk, + None, + Some(&[2u8; 256]), + &[2u8; 64], + [3u8; 32], + ) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + + // verify_ph ctx too long + match HashMLDSA44_with_SHA512::verify_ph(&pk, &[2u8; 64], Some(&[2u8; 256]), &sig) { + Err(SignatureError::LengthError(_)) => { /* good */ } + _ => panic!("Expected error"), + } + } + + #[test] + fn test_ph() { + let msg = b"The quick brown fox jumped over the lazy dog"; + + let (pk, sk) = HashMLDSA44_with_SHA256::keygen().unwrap(); + let ph: [u8; 32] = SHA256::default().hash(msg)[..32].try_into().unwrap(); + let sig_val = HashMLDSA44_with_SHA256::sign_ph(&sk, &ph, None).unwrap(); + HashMLDSA44_with_SHA256::verify_ph(&pk, &ph, None, &sig_val).unwrap(); + HashMLDSA44_with_SHA256::verify(&pk, msg, None, &sig_val).unwrap(); + + // sign_ph_out + let (pk, sk) = HashMLDSA44_with_SHA256::keygen().unwrap(); + let ph: [u8; 32] = SHA256::default().hash(msg)[..32].try_into().unwrap(); + let mut sig_val = [0u8; MLDSA44_SIG_LEN]; + let bytes_written = + HashMLDSA44_with_SHA256::sign_ph_out(&sk, &ph, None, &mut sig_val).unwrap(); + assert_eq!(bytes_written, MLDSA44_SIG_LEN); + HashMLDSA44_with_SHA256::verify_ph(&pk, &ph, None, &sig_val).unwrap(); + HashMLDSA44_with_SHA256::verify(&pk, msg, None, &sig_val).unwrap(); } #[test] @@ -129,6 +204,21 @@ mod hash_mldsa_tests { let ph: [u8; 64] = SHA512::new().hash(msg)[..64].try_into().unwrap(); let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic(&sk, None, None, &ph, rnd).unwrap(); + + assert_eq!(&sig, expected_sig.as_slice()); + + // with ctx + let expected_sig = hex::decode("213c4f1ee1e26373499a705a1bfaab92ad7f5cdb9b29c8838816abc078e3aff3bd67ca6541bab93c0ec533c8acd215ab9e1b35a5a3ca4aee64f2562f1160c6102ce6dd274016cee192b2745dac6772bc20e1e050aa1cb5d64bdf1e6e4ea812fd44b5d44d98b472f66a2b3521aa1ad917a94186258ca0e42143892eff70be17b68d0066652bb96b8867e53cc2889db8b4210e296195cbb44c6611bf572aa40732e92a7fdb79e15fbc8321fc028d9f3385af0720b8a84eefa5675d0d9980ed78f64d5d368c7f8f0cb0fe9189514ebb6b3cb7d8f25ba9180525c82ea3d3953de9029dc53fd8a66f8d4f812b8c536cddd576e3f021d2baa01caae5872dce9da75aa9991110e5081825adab38c53d8482abfa04897ecd0905691b376ad06071dec0b18ca8f3c77d222006c57a753417bd03dcdd8518b16d56edce148b70ded34444f9a3816bc938b14f5f1220076b3569668ccaf99a7567b90b1e7c29b7a6a26b6e12d9a2ad0a5ee2bf87249c87f70d732e68427646ae295579d284181b0603c75507bb1d0e1ed1b00d06b48569c2882cde3078594a2bfa7d3bbfe17528507911e4af5cb443e85e04cb5f78503cfd43a49856cc45624108f72225bf91ffd2f3fde47411870c585062955254dea26d774df2e14a93d66e9948d00ce23bf365fb7e708c85db93097321734ba8d4cc752f4c17e908ab99284f017e631afa3a34be474b20ea2d530778d3f070848db0d88bfbce1dab12bd2dcb47d7ed33f0ed48d55ff4d16fe60275a8d885c63807fb2433864a016eddb3f0ba3f4abdbeb9a76e124fc81a7b4e2e2fb2cb9f6580175eb2db4640eca230f51c4400f147fb7b5cdca3e29c91bcfeefb400ff3c77526b13a3939b0658cefa34754bdb0914d0a7fdedc95d6f40c8a2fc74c0b36c8e4a1337351df0e7402d4494b3770e80aa41fea78536473236a8229b1d2d1fa936b0a9d023d4f8c6b8b553702ac47c74493d753cca3cd9fc3c9be7a97920216e0d13a1a2d6443bc0ce85a6c4160ded6e75e428e52b481ac15972c89ef9072f383ebc94b822dfc463ca540eda14212018ad87322558e09a25be72e6c3eb11409fc4c750f92e14756f1551233bb6dbdf5b26b4b101d7c44e5c6241373b0f83f2e25d666d61b154a5544c3607cacbf569e41011cadc870584416ae532c1c7a47eee17b7d5253fce7588fc28f09c48edf825add943b982b996b7e73112f48d8984fd331ea4fcdfa3d70bb591614fda037ee515ef1038bf7ef8acf17d1f310b0e3d8e98b03f4a9e73e4cddc512a236d2d47f08518d4d069b6e370732a5f6bfddd7f1dcadf64b90cf381f41cdc600e67727c7264b969811a234e8b66351fd6c5dd36185dc8cb883cabadf6d74a7bc6ff813a4d64d4de4205fc9eeffc1312a40275e329f84b75f64302ee80ebb7fdb3d74ccc16fa8b2ddd55e2bb6712f9f4e015a2580b311afa8ec30b8f1f258430e7d7c45cb05de4ef68072bae302a1da26906c234ffa43874041b2226f5142b4fb1b2a79d122f3cb0909ec5c530563fcf4464406ec54f489ab9964949669078f6b2261f1ccd2efc8a2440f97e956da919b03f027aaf0553bffa2f9b6c997fe013a581f53043595b1a86a51a1e98cad9550a79ac8faa2813b1a7d1d238c8c0ef8ae8399c74050c96d05fb1740fb09d2fce49cc2fed955d455f42f24535236499b36d9c7815916c084922df523e8b6a83c31c062b89b332adfd5af45acacb51fc3d75097518a8033aca238ecb59f7defc07a647c31712d0095c77fefe8b0ed248f98cfddd1be5a4e480161942271755293b4d0ef462797b05c36c316af817f7120c03d45883462f33a5f2ba3de277c34b2c51a193a87611291acc02c1adf7ed076078a5ec43e1b040bb5dc851413c093730dfa6ef8b275f24c3bc69d5807c27aa172006dbbbf88592630f1b1d4c35c6a670a16b5a4b3e600d0378c181eb3238e443188a0d629a0bf3e632acf0a9e755d64086e24747f1141b7bb1220cd0236aaf317c0fabab43a9ae001e9897830aefb7ffeba844cf02e929746f068e7370827b7929ca22f1d9f5d532608c1e0847843e051c10e2cab84df928ee5315d2e7317c41cd6d3aaf437fd89eb030f7c5f051d60a6576f42f855ec49007f50dd8d300832591ccf983bca9c9c152232c4d672d5d31d7b2f2730af0247cd0b2ef5421751b0c65e5fcab3483706eda5f67461524f80f04049b6e725e061b37c79b15a28a177a8f3b44029820d83acf747ae0a00bb096ead483cfec37f5ee249eb41fea4d607946f35165b21fa4ac1ff831b7ec177cf413dba3cd7cc16755b07f1917e0b61581f6974eaa78a4cd58941ec11658eea54e7f2c92f16f0db47f785b6b831241dbdc8acf428727ffaf9e229c85e4cada02cf42f16582279485bb72e6070d75d5b80b12737f9fb23866ba09c4c9c222153c555b72daceb19633715c875c7ccf9e11c2d3f2d62fa0ed53d545e8763bce260386107d95419311795ec0ac1f4a910c2bc4b4aefec2356ec96e847766898d1d52ec160fcf64d21d93315cfc7371f08594e8cdcf16fc5630eedd93062cafb1658494dd58d6cc1d4bfd9a9d1e22f1642842fba0574eda075c8831eb122664f06aa1cd345922faf87d0188db80d94689ca157cff03d4f52285c0e5a735dce00c37bf7836af992412614336b1bea7650853f158c5f1e73cff825c9e43a30a6496fa80a77e5147fb89399139447e8df9771c11282c9a2a1b12829dfa397c14a621c5ff564b86201f524c1beb1af5731489f23c56abdbd595e85e8e41932f1d3dd4663a9961bd8eeeac8b0228ba41324e7da88ad57a75654a331baf3c867be9c12b8cdce95358f75a0de570a03990c205dfe281f2af61f2458b0da3d9712002667f8d75957bfd03124300280d7b5d82fbe914003a401fd781f22461795590f93150ca0b9d6c865e5dabfeffa238a57e9dfbca35a0206116cf1f353e1292d02719116381e15f372114edd4dcddee6749604035ace9af15a912c88d36475d66bd5b716bd957aecf6ca76c9c42db81df8ca64e86ebc198daad2b6074635d979edbeec999fe27d251b732a1d3b4d418cef442be5c2e04026a4387520dc23c49f16142b157548174ead74f73e188b6b8c8a1df79b905966da8c31ac3cb28e56abb693b786d1183486d30fcdb2858ea46ca72488cd07597451c974e2f6c6862ffdd2f8d26125ca88608dc3ecca8331c799aac4aeb6c24a6cd88c892b8d1ab2edb276d9a841b9ab830a0ebaaeaa4d8555f812bcc2fec4673e4c9c90cd1342525a6f489531c3605111f4551696a6b91a7bac6cfd0dfe6f0f50203081743465b627bb3b5b6d2d5d901414e639fb1e3f2f9fd16335a656d7c8a999dadb7bbc7cad5f0fafc0000000000000000000000000000000000000012212b3d").unwrap(); + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + let ctx = b"testing streaming API"; + + // since I'm not exposing a sign_deterministic() that does the ph computation internally and takes an rnd, + // we have to compute the ph manually here + let ph: [u8; 64] = SHA512::new().hash(msg)[..64].try_into().unwrap(); + let sig = + HashMLDSA44_with_SHA512::sign_ph_deterministic(&sk, None, Some(ctx), &ph, rnd).unwrap(); + assert_eq!(&sig, expected_sig.as_slice()); } @@ -201,6 +291,14 @@ mod hash_mldsa_tests { let pk_expanded = MLDSA44PublicKeyExpanded::from(&pk); HashMLDSA44_with_SHA256::verify_with_expanded_key(&pk_expanded, msg, None, &sig).unwrap(); + // negative case + match HashMLDSA44_with_SHA256::verify_with_expanded_key( + &pk_expanded, msg, None, &[1u8; MLDSA44_SIG_LEN], + ) { + Err(SignatureError::SignatureVerificationFailed) => { /* good */ } + _ => panic!("Expected error"), + } + // test also sign_ph_with_expanded_key // since I'm not exposing a sign_deterministic() that does the ph computation internally and takes an rnd, diff --git a/crypto/mldsa/tests/mldsa_key_tests.rs b/crypto/mldsa/tests/mldsa_key_tests.rs index 0fa7c61..0fc5fe1 100644 --- a/crypto/mldsa/tests/mldsa_key_tests.rs +++ b/crypto/mldsa/tests/mldsa_key_tests.rs @@ -1,8 +1,10 @@ #[cfg(test)] mod mldsa_key_tests { use bouncycastle_core::errors::SignatureError; - use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; - use bouncycastle_core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey}; + use bouncycastle_core::key_material::{ + KeyMaterial256, KeyMaterialTrait, KeyType, do_hazardous_operations, + }; + use bouncycastle_core::traits::{SecurityStrength, SignaturePrivateKey, SignaturePublicKey}; use bouncycastle_core_test_framework::signature::TestFrameworkSignatureKeys; use bouncycastle_hex as hex; use bouncycastle_mldsa::{ @@ -13,17 +15,23 @@ mod mldsa_key_tests { MLDSAPrivateKeyTrait, MLDSATrait, }; use bouncycastle_mldsa::{ - MLDSA44_PK_LEN, MLDSA44_SIG_LEN, MLDSA44_SK_LEN, MLDSA65_PK_LEN, MLDSA65_SIG_LEN, - MLDSA65_SK_LEN, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87_SK_LEN, + MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA87_PK_LEN, + MLDSA87_SK_LEN, }; #[test] fn core_framework_tests() { let tf = TestFrameworkSignatureKeys::new(); - tf.test_keys::(); - tf.test_keys::(); - tf.test_keys::(); + tf.test_keys::( + MLDSA44::keygen, + ); + tf.test_keys::( + MLDSA65::keygen, + ); + tf.test_keys::( + MLDSA87::keygen, + ); } #[test] @@ -111,6 +119,10 @@ mod mldsa_key_tests { assert_eq!(pk1_bytes, pk1_bytes_out); assert_eq!(sk1_bytes, sk1_bytes_out); + + let bytes_written = sk1_expanded.encode_out(&mut sk1_bytes_out); + assert_eq!(bytes_written, MLDSA65_SK_LEN); + assert_eq!(sk1_expanded.encode(), sk2_expanded.encode()); } #[test] @@ -122,15 +134,88 @@ mod mldsa_key_tests { ) .unwrap(); + // todo change mlkem to also hold the whole keymaterial? + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - assert!(sk.seed().is_some()); - assert_eq!(sk.seed().as_ref().unwrap(), &seed); + assert_eq!(sk.seed(), Some(&seed)); + + // it'll reject a keyen with a seed too weak, and preserve the seed otherwise + let mut seed128 = seed.clone(); + do_hazardous_operations(&mut seed128, |seed128| { + seed128.set_security_strength(SecurityStrength::_128bit) + }) + .unwrap(); + + let mut seed192 = seed.clone(); + do_hazardous_operations(&mut seed192, |seed192| { + seed192.set_security_strength(SecurityStrength::_192bit) + }) + .unwrap(); + + let mut seed256 = seed.clone(); + do_hazardous_operations(&mut seed256, |seed256| { + seed256.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + // MLDSA44 + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed128).unwrap(); + assert_eq!(sk.seed(), Some(&seed128)); + + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed192).unwrap(); + assert_eq!(sk.seed(), Some(&seed192)); + + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); + + // MLDSA65 + match MLDSA65::keygen_from_seed(&seed128) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + + let (_pk, sk) = MLDSA65::keygen_from_seed(&seed192).unwrap(); + assert_eq!(sk.seed(), Some(&seed192)); + + let (_pk, sk) = MLDSA65::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); + + // MLDSA87 + match MLDSA87::keygen_from_seed(&seed128) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + match MLDSA87::keygen_from_seed(&seed192) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => { + panic!("unexpected error") + } + } + let (_pk, sk) = MLDSA87::keygen_from_seed(&seed256).unwrap(); + assert_eq!(sk.seed(), Some(&seed256)); // now load a key from bytes so that it doesn't have a seed + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); let sk_bytes = sk.encode(); - let sk2 = MLDSA44PrivateKey::from_bytes(&sk_bytes).unwrap(); - assert!(sk2.seed().is_none()); + let sk_no_seed = MLDSA44PrivateKey::from_bytes(&sk_bytes).unwrap(); + assert!(sk_no_seed.seed().is_none()); + + /* Expanded key */ + let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let sk_expanded = MLDSA44PrivateKeyExpanded::from(&sk); + match sk_expanded.seed() { + Some(s) => assert_eq!(s, &seed), + None => panic!("Expected expanded key to have seed"), + } + + // now try an expanded key that doesn't have a seed + let sk_expanded_no_seed = MLDSA44PrivateKeyExpanded::from(&sk_no_seed); + assert!(sk_expanded_no_seed.seed().is_none()); } #[test] @@ -213,96 +298,118 @@ mod mldsa_key_tests { #[test] fn test_eq() { // MLDSA-44 - - let (pk, sk) = MLDSA44::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA44PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA44PrivateKey::from_bytes(&sk.sk_encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA44PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA44PrivateKey::from_bytes(&bytes).unwrap()); + { + let (pk, sk) = MLDSA44::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA44PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA44PrivateKey::from_bytes(&sk.encode()).unwrap()); + + let mut sk_bytes = [0u8; MLDSA44_SK_LEN]; + sk.encode_out(&mut sk_bytes); + assert_eq!(sk_bytes, sk.encode()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA44PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA44PrivateKey::from_bytes(&bytes).unwrap()); + } // MLDSA-65 - - let (pk, sk) = MLDSA65::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA65PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA65PrivateKey::from_bytes(&sk.sk_encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA65PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA65PrivateKey::from_bytes(&bytes).unwrap()); + mldsa65(); + fn mldsa65() { + let (pk, sk) = MLDSA65::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA65PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA65PrivateKey::from_bytes(&sk.encode()).unwrap()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA65PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA65PrivateKey::from_bytes(&bytes).unwrap()); + } // MLDSA-87 - - let (pk, sk) = MLDSA87::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA87PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA87PrivateKey::from_bytes(&sk.sk_encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA87PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA87PrivateKey::from_bytes(&bytes).unwrap()); + mldsa87(); + fn mldsa87() { + let (pk, sk) = MLDSA87::keygen().unwrap(); + + // basic equality checks + assert_eq!(pk, pk); + assert_eq!(pk, pk.clone()); + assert_eq!(pk, MLDSA87PublicKey::from_bytes(&pk.encode()).unwrap()); + + assert_eq!(sk, sk); + assert_eq!(sk, sk.clone()); + assert_eq!(sk, MLDSA87PrivateKey::from_bytes(&sk.encode()).unwrap()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk, MLDSA87PublicKey::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk, MLDSA87PrivateKey::from_bytes(&bytes).unwrap()); + } /* Expanded keys */ - - let (pk, sk) = MLDSA65::keygen().unwrap(); - let pk_expanded = MLDSA65PublicKeyExpanded::from_bytes(&pk.encode()).unwrap(); - let sk_expanded = MLDSA65PrivateKeyExpanded::from_bytes(&sk.sk_encode()).unwrap(); - - // basic equality checks - assert_eq!(pk_expanded, pk_expanded); - assert_eq!(pk_expanded, pk_expanded.clone()); - assert_eq!(pk_expanded, MLDSA65PublicKeyExpanded::from_bytes(&pk.encode()).unwrap()); - assert_eq!(pk_expanded.encode(), pk.encode()); - - assert_eq!(sk_expanded, sk_expanded); - assert_eq!(sk_expanded, sk_expanded.clone()); - assert_eq!(sk_expanded, MLDSA65PrivateKeyExpanded::from_bytes(&sk.sk_encode()).unwrap()); - assert_eq!(sk_expanded.encode(), sk.encode()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk_expanded, MLDSA65PublicKeyExpanded::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk_expanded, MLDSA65PrivateKeyExpanded::from_bytes(&bytes).unwrap()); + expanded_keys(); + fn expanded_keys() { + let (pk, sk) = MLDSA65::keygen().unwrap(); + let pk_expanded = MLDSA65PublicKeyExpanded::from_bytes(&pk.encode()).unwrap(); + let sk_expanded = MLDSA65PrivateKeyExpanded::from_bytes(&sk.encode()).unwrap(); + + // basic equality checks + assert_eq!(pk_expanded, pk_expanded); + assert_eq!(pk_expanded, pk_expanded.clone()); + assert_eq!(pk_expanded, MLDSA65PublicKeyExpanded::from_bytes(&pk.encode()).unwrap()); + assert_eq!(pk_expanded.encode(), pk.encode()); + + assert_eq!(sk_expanded, sk_expanded); + assert_eq!(sk_expanded, sk_expanded.clone()); + assert_eq!(sk_expanded, MLDSA65PrivateKeyExpanded::from_bytes(&sk.encode()).unwrap()); + assert_eq!(sk_expanded.encode(), sk.encode()); + assert_eq!(sk_expanded.encode(), sk.encode()); + + let mut sk_bytes = [0u8; MLDSA65_SK_LEN]; + let bytes_writen = sk_expanded.encode_out(&mut sk_bytes); + assert_eq!(bytes_writen, MLDSA65_SK_LEN); + assert_eq!(sk_bytes, sk.encode()); + + assert_eq!(sk_expanded, sk_expanded); + assert_eq!(sk_expanded, sk_expanded.clone()); + assert_eq!(sk_expanded, MLDSA65PrivateKeyExpanded::from_bytes(&sk.encode()).unwrap()); + assert_eq!(sk_expanded.encode(), sk.encode()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk_expanded, MLDSA65PublicKeyExpanded::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk_expanded, MLDSA65PrivateKeyExpanded::from_bytes(&bytes).unwrap()); + } } /// Tests that no private data is displayed diff --git a/crypto/mldsa/tests/mldsa_tests.rs b/crypto/mldsa/tests/mldsa_tests.rs index e6f4191..d46bf98 100644 --- a/crypto/mldsa/tests/mldsa_tests.rs +++ b/crypto/mldsa/tests/mldsa_tests.rs @@ -2,10 +2,16 @@ #[cfg(test)] mod mldsa_tests { use crate::{MLDSA44_KAT1, MLDSA65_KAT1, MLDSA87_KAT1}; - use bouncycastle_core::errors::SignatureError; - use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterialTrait, KeyType}; - use bouncycastle_core::traits::{RNG, Signature, SignaturePrivateKey, SignaturePublicKey}; + use bouncycastle_core::errors::{RNGError, SignatureError, SuspendableError}; + use bouncycastle_core::key_material::{ + KeyMaterial256, KeyMaterialTrait, KeyType, do_hazardous_operations, + }; + use bouncycastle_core::traits::{ + RNG, SecurityStrength, SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, Signer, + Suspendable, + }; use bouncycastle_core_test_framework::DUMMY_SEED_1024; + use bouncycastle_core_test_framework::FixedSeedRNG; use bouncycastle_core_test_framework::signature::*; use bouncycastle_hex as hex; use bouncycastle_mldsa::{ @@ -42,9 +48,9 @@ mod mldsa_tests { fn test_framework_signature() { let tf = TestFrameworkSignature::new(false, true); - tf.test_signature::(false); - tf.test_signature::(false); - tf.test_signature::(false); + tf.test_signature::(MLDSA44::keygen, false); + tf.test_signature::(MLDSA65::keygen, false); + tf.test_signature::(MLDSA87::keygen, false); } /// This runs the full bitflipping tests and takes several minutes. @@ -80,7 +86,7 @@ mod mldsa_tests { // Decode and re-encode the sk, make sure you get the same thing let expected_sk = MLDSA44PrivateKey::from_bytes(&expected_sk_bytes).unwrap(); - let sk_bytes = expected_sk.sk_encode(); + let sk_bytes = expected_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -92,7 +98,7 @@ mod mldsa_tests { // run keygen from seed let (derived_pk, derived_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - let sk_bytes = derived_sk.sk_encode(); + let sk_bytes = derived_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -121,7 +127,7 @@ mod mldsa_tests { // Decode and re-encode the sk, make sure you get the same thing let decoded_sk = MLDSA65PrivateKey::from_bytes(&expected_sk_bytes).unwrap(); - let sk_bytes = decoded_sk.sk_encode(); + let sk_bytes = decoded_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -133,7 +139,7 @@ mod mldsa_tests { // run keygen from seed let (derived_pk, derived_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); - let sk_bytes = derived_sk.sk_encode(); + let sk_bytes = derived_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -162,7 +168,7 @@ mod mldsa_tests { // Decode and re-encode the sk, make sure you get the same thing let decoded_sk = MLDSA87PrivateKey::from_bytes(&expected_sk_bytes).unwrap(); - let sk_bytes = decoded_sk.sk_encode(); + let sk_bytes = decoded_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -174,7 +180,7 @@ mod mldsa_tests { // run keygen from seed let (derived_pk, derived_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); - let sk_bytes = derived_sk.sk_encode(); + let sk_bytes = derived_sk.encode(); assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); @@ -196,6 +202,91 @@ mod mldsa_tests { } } + #[test] + fn keygen_from_rng_tests() { + /* keygen from seed must match keygen from rng, for a fixed-seed rng */ + // Same arbitrary fixed seed as rfc9881_keygen. + let seed_bytes: [u8; 32] = + hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap() + .try_into() + .unwrap(); + + // The seed as fed directly to keygen_from_seed. + let seed = KeyMaterial256::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); + + // ML-DSA-44 + let (pk_seed, sk_seed) = MLDSA44::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA44::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-44 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-44 sk from RNG must match sk from seed"); + + // ML-DSA-65 + let (pk_seed, sk_seed) = MLDSA65::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA65::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-65 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-65 sk from RNG must match sk from seed"); + + // ML-DSA-87 + let (pk_seed, sk_seed) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLDSA87::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-DSA-87 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-DSA-87 sk from RNG must match sk from seed"); + + /* Test that keygen rejects a seed from a weak RNG */ + + // MLDSA44 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_112bit); + match MLDSA44::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA44 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_128bit); + _ = MLDSA44::keygen_from_rng(&mut rng).unwrap(); + + // MLDSA65 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_128bit); + match MLDSA65::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA65 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_192bit); + _ = MLDSA65::keygen_from_rng(&mut rng).unwrap(); + + // MLDSA87 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_192bit); + match MLDSA87::keygen_from_rng(&mut rng) { + Err(SignatureError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // MLDSA87 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 32]); + rng.set_security_strength(SecurityStrength::_256bit); + _ = MLDSA87::keygen_from_rng(&mut rng).unwrap(); + } + #[test] fn keygen_error_cases() { /* @@ -210,18 +301,19 @@ mod mldsa_tests { KeyType::Seed, ) .unwrap(); + /* MLDSA44 */ let expected_sk_bytes: [u8; MLDSA44_SK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec939ce0f7f77f8db5644dcda366bfe4734bd95f435ff9a613aa54aa41c2c694c04329a07b1fabb48f52a309f11a1898f848e2322ffe623ec810db3bee33685854a88269da320d5120bfcfe89a18e30f7114d83aa404a646b6c997389860d12522ee0006e2384819186619b260d118664d4a62822184482402898146148a6614c4248a19208c2382951244808a125c2083108c47120140914836c18a78084106ec9c07022b56408b0610c070498124451886959004622932041062e42b64c01164914284c41a85180460a5116515a0820022244dc9849d13251e13065d3c08592a85112a1640039220946621cc70cd9086dd0062652408580443091062c50c80924c5841a966d4a982c99066da4443220a7645a326e11b57020926124138e04852c0a4872c8a051d3082a99208058242024074e59148810a46460c06de0b28d1b1909203422c024410943710a212061a2015222521b80809a340013934dd3322922170a9892691a14512027219cc02062a2814818691a854d8344695b2041031242cb184601a90d0c023183b0215a224ac89205d9906904306a4b064ad2b2011c404081423252327254a6405a18100c321292c2805212625c82280bb46c03428d53100c14010ee1365288842491020a63462620062911c228d0204802b36ca236095a8648cbb4618b4662c440821a890910024d24b24520122524c90588288cc9c04d5948220a276ec134644c90605b445082864943880443b28c603080a2882d84a46d8ca629d0c68442064689885100a98d01498de4380da4068dd3947142b26c1a84611ba32842b42808a0711ac531e0a04c013765242862142890091061d940221b3360090292d02481200408491844a3222d5c8844149808a446610195640b390a0c9450ca406ad2b220c0380182308e13b908918084148829c0189112350da02422e20406d9c2850428121cc989180272d24029c20812d8062a9994719bb8682384291a2289144511dc82445096450c4484c0b2049aa60543862c44326e88442120a84c9a3070e3b82d63268803254903438c48a809ca147253344e1243081ba704593022d99480e234228142129c302a9434266104452426281346094a326d11280918b82562281113410d41b21190844c8b1212a2c688c9c030220606d2188e848630904452128831d9207113c52843060e033060cca6845826524c88011ef72562c85ffa43acfa49217f2b172d7bbc14620e6d980a71aabbdf0c45e9a206ecb1423fee15decc17601300149d9223cd6e6c6e1fa8e41fc7c64938ab68905fd3dcda50d87082e7d0d71d1bc9b2b84c85523ca8fe6cad294adf83be15b108ff721d0cc87bc3dd3a7590184b0e845663a91fc9e1c3c53a61d867420b04f092355753bc65a06368fd41295fd09924132c6f91f67964c142674a725c343914c4cecf58c074bcaf4558c97bf7911e07aa6d0938f2ee2bb3c1a8c595d635e84342fdea01dc24b211ad2fc281cf77e59110c7abc54bf0c86d480b9be276471dc9d603cee98cfdab3e9fcfb703793560549ea4450fa7b33fb9169c44b4d25fb9c457f49791cd3da03eac96095813c105132ccda4e63e49228cd23d8a1f37856f142d93b90db09f82af89258c63aab8047a80c036c9357ea2046f8dc6354f0c5295f342bb417d3cfeb0b1fd33622c29e14cbbd92e1363c65ebd4504b7512329b9670e32e1b2c67a54e7f1a55f8b9f9ea04e8ca3a705e62a3c5e637374afb7aeb6ddea612cde28f01a202d7aa4e34722d27dd3f9b89894d019fd5d4d7119efe3723bba104cb8bb0981e074de3afe200daaaead826cc45f244dbf431afab34efbdf782474d2fd57118f646214934ed99cba3b003e8d67a3836f6f19fc41910ce5163ee3ae99eb84d514eb761e63684ea56f9791d2dd4aac6e6168b948c817f75a222acb0e8cdc03cc4afe8f67157e1a363b7faeff9f172b98913677c5a1dd085e9ee4c22052c1af58193116673dcd3bfc5f34b855dcc6c77885649e9e71f43d4aea0f4b72ca7eda0578ba13d31a658d2d060a9a66ff69ed1be7997a2fb1d2723d38f9bfabe18f8e7b3cda906e4e9b5e942c8eaeb296070ebfd364947a940cc978bed66b37749e6d5dcd7be8c494440e2b84cecfefb98c0bedfb3c41e3359d2cd7197fbe720c48aa6c6b6465c1ee63e3569c2adc744491370b7f7826fe0b77a1d19d64101d032b918106b42d2ef73747e5601fe4ba50f23ede521f031a817d15294a43722e8378784b6db0cf1ba9e8ae911d9201b9ce9cc3019c6f5c27cb98da26144b64225a7c932b30f761e78a2d59a1d8b83ec6344a2f6dd47e765706d00bf4a79a6a926c3ba91d812c8f2c797ab1796709e5d16856778293529f0286d015c3b5399619642a333e9e593d6e3f5353994208e9e6a332851d7f652522a928b917e27e2d6d42137dfe2ebfa6fb1c67b26c0254528685f7ebdbe315a68eaa2da769e8a9f42d3e60007c71330926b2c0012d83ead4e4fd1ed872ccd1972201d2b027f3545ac2d30cd78bc1d740feccbc6fc2a0446c6e30eac51f5a69098aa2d447f2085b4e4e4b92ccc26921d2de478518cd090ce267aea2d27ada57fd88b4976d89fb843cdccf49a76ca2679e6801bfa7fb031896fb50629704b9923936bb5dd385311121cadfb11995e59b73034cf67ed03ab813867648d025828087e949a9afd16b95d72d99b1edca257aac132ffb7a0709aed5a9c0ff05fb0f2bbf28409eed7b5f5801be964ced019e1cb7851d3851f10290674e19ffb008b301c4acf641a2bb14216e1d69cabf52b5ef227496b0f30799a855d117fad3744a6fa33503ea798b52ddd7ee5426609dbfcd3f0c13b164d6c051f7ed4a119719a712e388d328402081ff1354b554d2c237afed3b151c4ba8e9f4bdeb8499a3066e26bbc69e8af089dec71731d1dc529eab17ef7374734c0fe475494c83836bdd34a03b9bc89914716061bfb98ec6e61c3ed4438edcaf25243c647086b9ea7018b0d9a8a0b00cecb00abde2498d69c2336101a772cbe4f571523f51bd05882cdf358b849cc140aa1faf22423a12851ce0e33fd48975a4959fa5c5fe418c93908191ab6e741b77bfe02cbd698ee795c466d615619e6441382c6eac01834ee9ab73cea80bbe235c78da91bd79b6f82f899785d68700d393e675c2224d6b7a1ad21320495679adaed70167b50866713a53109db7b6f7d81304ecdfd83b319b1ef248306b45ad29e7ddcc863dac56048b5d69ea175011f7614c00a86a863cde1872a8932878b9ac7e1ac5bda4997b72064f0cd75f4c814e034de11acb9013cf7ea926b4e7eaace070c7ba2188efad2e431e1223d45dd05c4d8403c2e45cee6413ecbe7527e873e455c4e610a61839aacc0bd56d2483e78f298b66a478eb2f558cbafca86be847baeb02c5b216c8cd88fea4df249b09e670a20703abac24b0a91abc4a5646601442ba10becfd30993880051d07f56a05a9379e7a8e6befee3f22faa106398f7706006e42e9be1ef89d25c272f11a95095c587d713732284de9dbd3c7217b0689e21d8eb0ff69668").unwrap() .try_into().unwrap(); let expected_pk_bytes: [u8; MLDSA44_PK_LEN] = hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap() .try_into().unwrap(); let (derived_pk, derived_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - assert_eq!(derived_sk.sk_encode(), expected_sk_bytes.as_slice()); + assert_eq!(derived_sk.encode(), expected_sk_bytes.as_slice()); assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // success case KeyType: BytesFullEntropy - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::BytesFullEntropy).unwrap(); + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); _ = MLDSA44::keygen_from_seed(&seed).unwrap(); // Failure case: key type != Seed || BytesFullEntropy @@ -262,6 +354,7 @@ mod mldsa_tests { let rnd = if !MLDSA44_KAT1.deterministic { let mut rnd = [0u8; 32]; bouncycastle_rng::DefaultRNG::default().next_bytes_out(&mut rnd).unwrap(); + rnd } else { [0u8; 32] @@ -481,8 +574,8 @@ mod mldsa_tests { } #[test] - fn test_sign_mu_deterministic_from_seed_out() { - // I don't have a KAT, so I'll test against the regular implementation + fn test_sign_mu_deterministic_from_seed() { + // I don't have a KAT, so I'll test dynamically against the regular implementation // ML-DSA-44 @@ -530,6 +623,10 @@ mod mldsa_tests { ) .unwrap(); assert_eq!(&expected_mu, &mu); + + let sig = MLDSA44::sign_mu_deterministic_from_seed(&seed, &mu, rnd).unwrap(); + assert_eq!(&sig, &expected_sig); + let mut sig = [0u8; MLDSA44_SIG_LEN]; let bytes_written = MLDSA44::sign_mu_deterministic_from_seed_out(&seed, &mu, rnd, &mut sig).unwrap(); @@ -571,6 +668,37 @@ mod mldsa_tests { _ => panic!("Expected KeyGenError"), }; + // success case: seed SecurityStrength is exactly right + let mut low_security_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + do_hazardous_operations(&mut low_security_seed, |low_security_seed| { + low_security_seed.set_security_strength(SecurityStrength::_192bit) + }) + .unwrap(); + // a 128bit secure seed should be rejected by MLDSA87 + MLDSA65::sign_mu_deterministic_from_seed(&low_security_seed, &mu, rnd).unwrap(); + + // seed SecurityStrength is too low + let mut low_security_seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f") + .unwrap(), + KeyType::Seed, + ) + .unwrap(); + do_hazardous_operations(&mut low_security_seed, |low_security_seed| { + low_security_seed.set_security_strength(SecurityStrength::_128bit) + }) + .unwrap(); + // a 128bit secure seed should be rejected by MLDSA87 + match MLDSA87::sign_mu_deterministic_from_seed(&low_security_seed, &mu, rnd) { + Err(SignatureError::KeyGenError(_)) => { /* good */ } + _ => panic!("Expected KeyGenError"), + }; + // test the streaming API on the same value let mut s = @@ -661,6 +789,47 @@ mod mldsa_tests { Err(SignatureError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too long"), } + + /* GAMMA boundary conditions */ + + // test to pin MLDSA65_GAMMA1_MINUS_BETA (produces the wrong sig val if MLDSA65_GAMMA1_MINUS_BETA is wrong) + let sk = MLDSA65PrivateKey::from_bytes(&hex::decode("FAC3F0A1C095D7F14FE867233429E7FBECC3E56C1C47C5776F60FCC952271C16D701337CC4DB701037240B2CC96700E76D912695D01ECFD144A1A4480C9A7AFC6AEBFF85B31A51CEDCF03C089DB78D57D5736998094CED527BF62306D163DD076746390725A575F658812850340A6A22F21D1A4488F81CC9C51E34C7195E24E62743418082738460173216564147714141447322747378303064332088852786387656485685067602315841385530303073748288551114488671126472324211001632768375217888105730287103413366631348063456705643028124418424362865147136784028750605280522354664402012156867463840124312702746274317185820204146418146260428165381878680661671606625835681411564078617545272781118153404548772767065605505661113346550212253001708208871437608544687461742554212252531618480788375428626465682802665176276452816778253827888713776811675325120157277407836344676441366411518042352072878407260148473360521722835364503831153320866470060388717774746280482741018426182467855676081768764502460378420511274573338543580057135466786576641657141853703461273673864078756846882878275266371317370221447843022470612874036187668341702635480511522823511802187750128755208466178350262551186287764073638143070751153170178714610652685824431071335605871487821771381233638082075233653821363404721684841385534088177538652001225820118825132746376784420308558673853184746535462708561415062307838047410305386755301311878544252655175427735642725125852462520300512237334101417850348822685720422134232331416163464745560268223741265100778127023844048536253761851061180163560244303674215200615686636064370316373872671703546376007034507065834571177568604078030182100382824024244355146222000521887855141800051638076135301876078282783854476883578855568168226642036114282711266780675104628153263815046834464683021517741886573830273710441308840745348777606127672844722386630210200025302263135713023513144334831232011681183146135820156511536562848603567353564216431366041187872531665824337234157375170402400366431125740874338582777401587182726007834342527487856427375026483665335500311348218318404831768517434724867376286526385537375022315084547851162348038628580233345112714217535531172243054028425076334167574124642505645643703357747258152772101731762154367415443402180013310507803185561248014235117864462560741807827475862248680132084577068144204516541344387456176453454137741684473663420305220270013380651567340128517050186661163071768085126283043061688144133578020758648635074223303518423535803274805136544782015667213011858810228662670086040427500742786645441425845543080676585818187373073177826648377838253361800836061614077137087700227656343025125400007421502577613446081061602370815457845047316447020371536762643681172462742533741043757221578526022521413633247403254078850774373623685012472806652753134016260818410320635707047651107738122602141422051241873175027656625672307441218576154467471554748806141478270237022035541450228312172183426588573284554185388468111581188733140148403753651886533044825265188582527046117408104630522280766840883618814145722050535737005141474643706767817456415644868337004352380220517858504287578807218203860374826463502187671017880147716606130536501355309DEDE7F9572F3915A1C1B014B2FB3D7591DB0BED20443D420E8D4D3458381AF3506F93C6CB3561449E50A9E3349DC9B8CAD8AB1ECBF946CF6ECBBBC11A8B32B295D1E3CB23DF662E8A8625EE91ACCC74EE9AB059FB12A46AAEC343FF1B21BED35E135F46D05EE11877126A9961C897BFEA0D552E03BCB313AD1BEF5B21FFF09005417B0F531120D037D1106C4FFF3692C607108563B272D054D98299D1FEC0646536152FD6F915FBBD8ED1E864904149F919DA295CB79F4EE6A9123E82FC5DF0BC379743B0A650D8D26799B02C0A6F278B323BB7F58811E75CDDD6BA27200DD7BD5B94F6AF7AC76B791F12A1C464E2E112A591A12E3EE569C7B0BBF726C634737A7388D676BF36DA496E3A6DD4744D83D5379642261B61D6A08A164BE30360BEC7E05473A66CCDE29DE5F68006932739DD876E29954419750B6C2E282432E0E900F30C6DEFA5042216C398189F1072F3738DEA06AB44F7611BD995430B2204FCA4474A2A8DFAFC44607F9AABB80D5FF235BA93A74BCE8297D45A35322116117A5BD9C960A18758040EC670D8F0B5F75515DFAD91A60FE8F26A5048F9A955210B6CFFC7F6C963531E9B6EB0DAC6D744E63A22AC9E2935FCBD8FFDC6D1D5621266A338FFD3E28257C5F04B209DD1013C2EA6FBE2D197BBBC9B8F1A6DB84E361F1E1D6235566A289861E193CF51604CE29DB66C546C20CD3D769E128BE0C10C7E8EA640F23778F426DE99A675726CB837EBF6F2EDFD886E7522FEA3E9AE793E34B702493C6575828021B8A4E1DAB64DE98DB52126120B8B192AA8977143E11E7819A2C116598CA301670C51569432DC1C86270F72E474CC35B4A7621F0EF883BD9B187DF4EBF2BECB7487BE331A45E9CB43E5DB9225B21724D60CFFBE5A3817765B5DB2E111AE6445BFEDD6631A1FE8048E2008E5DF450E0FE622319217567AB202BE5F6E9D8859CADEB2210784D9FB1778BA039A98A2C47435EBA76EF170B9ACB2D3884D64831E910D2CE1E8346881490FEABFC5882C7CF9D6101916E4021B9CB723189345049B5048400307E0236F177329EE6CF9A67848A4D7C98349A94A68400EA141BDA866F5071F3F4A7B8B96372C5442B43A003962DA6A5F07B9A572E7EB8A057BE06C7AF19D4EA728B7BF7F0AA493C413190CEE7D9AA90033378A2FEAFB9F1F360A904BFFA62FE1161240FCCAD33473A93FDAD26DAADBBA4CD05BD1F5897C40FD60BBEA8ED0404347131B2251D4CE06A65BB5EC31BFF5D03CFEAFFD7D06D09CFE8D88706A0AED09DFBCD273C65E1797D65136325A78E56CC4836B39EBD5A964229D2E9A163FE0E12BF0355D6265D01B7F94874E579119AA7E8C7EA69C09A2FA46069FA64CF0CD2ADDE12ADCE6B422676C0F05951B5C85404E25455505730986415B693604F557E09A6C6F48F5C193BF7DDC0ADFFC91CCBE0D3F7577F9AF85BC039CF2787ACE3010A413E3A0613C323B00C5A42776789382EF804BA918646A27ADFF1C3BDB8D86D4D1237ADF4190FDA2D87510ED68CF9234A766EFAFF779EF9FC4F8337E147FB5C83227E97F67D497F675DA66D55A88E9611459D98473DBDDC0730B2F8C6FA606A22E26679E68BCA03A50DF3731992A97063A4A6AFFF8C268290422A9D094B85BD6851177BE2BC04FFC64CD96E866731B552DD3C779D49C7781BEB8DA3F85B942A9780E47675B11E5DD77ED1F343775E876A737CFA3CBDF5CBBD7614A3EABE4DB919BC3E13ADE651F45133A04DC5DE0DDBC0BE01DDD615BFE6666769CC663F4941131A80DE835C851EBC67DFA48024C07FFB50F5C7E0A5686D1AA9EA22E45F6DE2A07DFEF28E90B00C24074897A76FFBAAEDE6BBB9A0DC76D35E2163169C426B1F5221D0A33F06C25072A2B164521736C441D4547EBE8AC4C408D30EA34ABF595BCC4CDF6157E86D8F8C5950E9EF27290EA077D812941A1DBBB4C6D06D52583DF55547F657346373CA37BFE9CDD6010CB5B0033F643EE0D6C9052699FC8E2739B69E1896BA14C7BDAAA7305F5F424350979F60868CB7E9884C818444C0FC0481216D06AF22076B726DB17A496CD45E9BA344C9F49AAE12E2771DDC9AD40908A00F8A19B85F120DCB1BE238E77E15ABB6E24E4E8BC55A68DFA7D473ACF7519EB85F714C0F49908BF2A6F70AAFF77A3DF2500992E304E98F6641505B1A5DF02C66118492354EA720A7078827A1B769B308440864FDE478F312DD497F7C507AEEB9AE85933607F20E4FD1906FB6FD074B8935D0038A2793489136E8305131FB95296A42CCB66F4F90F7214642929DC6E6843CE5B83E58E3F22CA8A5586A1D93527D75691B1E485953FE1D3E56E1C3071965B8E8EBBFE0F3FAB5F24FC245D2AFC423AD67DBD22E69ED2619E42CA0AF8F30C06D969CD7C69AD0783E17D018DEF90E75198EFADE2AE031FAC1905D5911D198635A1972D9CD8C1E99F049FBD96E6F88A25773B9F6048B6C136B4110DA4F587EA5C27700BBB6800854C9A4D1375F9777E09B1E53AFAF73EECC5E7711F7B19E7026408DAA10E93B8D916ACB0A4AD57848C2B9F767C48709382A2CC57B9DF56264B8EA91A529B4ACC7BE8DD3F3DF30276CD459CAF32F65118B6914E22507C2D5E032038BC272442FF16A80224EE35A4CA90B2564EA78B3856858D985644B683F62FF5AFC04F4F6FAED8434B6CF4EAF116FE231FB7931968D7BBA18BA347628304EB13EC8BF83FAE853E208FD276EA60B2DA751B058D82EDEEB4099BE90A581B52415B3E043B39D570C6E3996F42733A1459A5E3667009FF1124C106D2ACA45107D242ED4FF486F1991A01B5E471D6D360C19D3D064B578147A82DEA65FE0F8B7897B7347038522A2EBEB7ED217E43C33D5B7ADDD9942B43F9D5B8D87416C425F3E6CA3BCD3D995051C02EA4EE84BCB3B877F27FB16993522A91539D3FF43925671FB256573017302DDCA5A562FFCEC07A1204560252C78C2D797DD4EBF300922D2891AF7D220D32C3AF68F20CC096F6D5F9038E5AE7BAD090F99F9AE7FF3A615EC4A7F1F759A52428F94DBD496A81B0B71278EE4127AA72599C97618BF1D053C471E85D888C2971D816E5147BB07AB2192FA473641495CE64596D543D37DF340181EC609E8EA8F72676C2DCDA357C19051A8642D99FA00F962D28B4830E81C8D0CD2DF26ABE514803C8192608E29B9ACB3B150A664C474C6E064F1BCAAD0A08344E911FBF8425A0893D71325012113B8F2FD74FC0E2966AAFD72F280D18D8D02F779B0B2C61DD5E4385B5B82242AC327C3DDB6AB8F288E8DDA24F1E5812A111860058DEA190208928F1AD208974AA2EDD53BAFF400888AA9D74C0071C65C30AFE4A02B99876621A0542BBCE337ECFC06F0A236C3E9C672B48E61EA70CDF82421C14B7F0D25474ED2B04DA1C570598B14CFF438BBC44AB3C931F47D65DD0E767DCD7DDA522CC4CB07EB78962A50D281BEC69B54A77D26C902D3759560DAB544404BE0AA3F80D3B84EC4731DFFD1579533004D2D1EBA09148AA84C93B6A2049AD019A0BDAB9F99389FA4").unwrap()).unwrap(); + // because this is from the bc-test-data with busted mu values, need to pin the (busted) mu + let mu = hex::decode("dd6438e69fdbe810789fb22940fb451c28ac570c8e195f4aebc22db848cabe970a76c92011702d672478fc09f2bdc10b0b7c2b1752a9d99ea7ac3a6381684540").unwrap(); + let expected_sig = hex::decode( + "C3263802CBF2818D55BAAE9C2BE8C1EE0123802BDAB7C87CF9E6CF06E2A8823E750FFC8FB917F9052275A2CC68C2ADF4CFC78D0144FD3E96694C116CE1672BBCA6A3E1F6BBE8A0C34E5C1874DE1798CFC7766FE1BA906A1C6EF40EAB1A914705C3B0F14ED8D212B81D400281F68644B2EF72330DA2BD16C792D8160896177E920EB8E198AB5A38F5FFC5088663D0D0E0EB30C342F41B886FF209C8939CA424C2C24380E4F5AD805242CD25F4AC445AB883CD5826FA4135F4D549B12662C687B0FAFF068F8C18077B463E910E7A17B3BA9A76418C79FC53CADC75E48E977EAEA1FE748EDF0A1C0CDF9D037812B1BE505BD39E4EDA489CD56369A1B3F793554D7DD815AB1A7DE536018F0E0D73B95C8EBA07626D92B0365F6F71FAEA07E1E80FEEA666EB57865D6D13E46B801FECE841A2179BA8DA1FC1C1AA53743F187F426DFA34510E11AF1D9570FAC5DF77C9BF7ACE64BB130DB2192959091DDFEED580C722ACE89F84729540EEE8CFF6D8E4AF91C1B0EBB42A65F47B80F4A300431D7DEC90BE739E31C9906D7403B82C196266D4FCC6659C5AA549D0ADC3AAAD1F9DB7C80E17839957DF682C10B1938DD9A292C65E0146566B1EE24529C925691BAC109CB308C4303C3513FF5B62275E84C8C94DD6CFBF2E0F9F0B735FEFC44095FA2E01009C37C33BBDF082B305AD479C743076EC2EFAC0176F15A0EFBFA504894A3F8C6B7ACB096182B71131F46B1EC7343C9DE78F85CC0352F97C188667898ACB4EB4173D715FEA3B69DCB1B47904C8AB51EB4915357995B5F69F979776C6CBF114BCB0A154D82D17A9251ED545FDBE7E04AB06B99662D18673F08D933CA0B84FBEBE17F783D4039078465C378E9AEF18423C7B86C411DF9B4A77A2BC8804C4E5A5F9BC5777166C221F60107D2107AB55BB53E354267F8329B71A77BFF920EA94D9240B436E8CBA65940C0734D6928F57257A16154BFA5D8C9459979DEF567EE0965239C0DBDF122723636F2C5C5CAC6C607BDB9FDE8B8557A687E3A68970BFFAD695BADA74CE3C681B81AC8CE3747B844B7530A606F6320ABE91F16E79EFFBCB8446319D888D957252836BE6D88C018EED2F5C46369FE88CAEE624905E71C41252B70F115F781E0D4D851D2820718DA03FA1E9F9490AA06D74E3D15A44CFC98E57AB49A63285451C2A43D5ACA6733952814C05391431052DD9FE5D4AFAFE7027E5E5C8E9247FD5B574B438C4E541D6F408964BFEACC54FF873C6976F4383AE2517D4860E3D775280DE63D2EB01709282170E33A2F5FBD80937B0D23188C978BA370B5BB154FE0F2FA0361A2F1E46FBAFE6494FEAFF26097EBD2E307301C8649FAF82B57788B62E6C69DE6F6B3ECCDF17C10CBA41C3D1A8025F5BE98D65F2B37AC2527BA60E99B89FDA9028845F6EE6E20C2C8E2E8D8627C4D5859CD141920133D4D4B3AC8B91C1B14B6F6B8DE35E9674F312B938DADB90C335931081DB90640883512125BD793467C78AB84208146EAE0D62004E51031377BBDE89AA0AF0609E9AE315EA8A043C368167073422018900F9C653535A96FF73DA466377CF462314F361E08ABA6CACAB9AA666633FC6602D645CEEF305829B90AFC8BBDA9F8E40EFFD62FC31A1492914E22DE711DC3102D64AF46D13ED766CF36E51629409F69EC4DF98CA97EC93DA4BA215C567C73104FDF6863229E2A2C9A3CA11DD3C8D505CF88944428F9D500029B770FDEB16388F63C50FCAD1953E2AC759730E7CAA930362274B6A3E0ABE1211613F3C36FEFD09454401B6110D95201BD022A3B6C68DD054AE0B0C547AFF5C7421EBF0879098112286C962A6164C799A7A20CCF3D4189B2AA240F895C54CB2F9E6C2C1E2BA9BEB185CCBD82CEC60A49D8CBBD3C26AC0BC5C43E2D5672E21F19ACFF857217A243D4C90600D3737EE78638AD6239C3AF4E79FCE030DDB0527EE428ADA0C1C9EB867593092CD1A5B77347FEF4541B71AB33932F8F1BE31AA475B9F4BBC07F218377E436E348F47607D9140E6114A05D2A072409C390EBC79231CB6DC2E3A698776F9C32B44C10F156AF450A8DAD6DEC0D294392DF6E17DDD312BB22997CF1D8A6EC55DFE401EBA486CF9C8C0E6E713D48C0CA8CD26EAF88DC0B34F86F35B6617C45173131779ED98E880BBA3520835F29604B6F32AB1DA8034F6F559E81050BBCD7833B0D71E90101339386AE3BC653CA50F37BBBEB64377987BD892CA51280499B6EB051345EF9BF1F6EE766142491F96E27787ED8991338F60D2A4BAABF43ACE7A6072C6BEB6D0FFF79A3B956DD5CF6348F6E02104E1F9F33B2AEDFFA6B833A00CB21BAD6AD04047DD845180131506080272925960DA888E393B43EA5770530347EAF9E9812E87A317D1BA66C61CACD55A8FFD6355BB39E596D33F4E8355A6B51C4E70697D4C5BE9FFE64FB11EA714D81B20677DBA268E8A9A5F61E8E268DDE6F6FC93F3FAD688312885681E0F67474689680532DB88E178AAF2FDD19E37B091020FF01008BF0FA8017075C2F02B5E7DCACFD1F337A24815C6D9A9E1678EE2EE1721A61ED774A7DE98051F799D4F3A5407C1F725B618AD3B0C17FD13D762005C143A9AD78B13DF4B376D924BD9AE37502446D2263046BC7CE97CB6371CBA72FE0224FE71D501884E9AA820F92E1CCC3F7F4D57214F0842C96B85BFEFC3767B81AD76F778B00EB2BAE3C99D75EAF295901B9DB273F4288FA4EA74650B90D114B081B1D061D97EF211A84726756396F36BCACC45A8EF3F57E2331EE4FBCEED55C48605B4A3D842067933F8AD6D72C1793B17515977EEB8404CB55388EA3CB43F504A5325870473C14287DE22D55015391D973A9556B93B0943C64F5CE8DCD365C1F1DB7B56B2FC91BF5552E5233C28755E366BCF303B33CE2FE608A5BF6348EAB272E4FC6B77F13367E12085E008C8CE17095727D83266CC321ACCC9C8B58F2D149C205A4DC6D3A599CBA0E3FC4EE042C3461445A536FB9AC09D2FEA87C7E0E8BE39B2D0894EA1E203CAECC5F79F13ACAA917401E75E8F36CBA4D34EB639725FE41E64912F48A70D1BA47309D23E40DBCFF986568B8536B5F464C5B169D5E0F90ADE7F46004098E5880340DB738A7B9C849A71D01B8F8F9FEA8B3B331210553C469A5EE257B4EB02EC8932787C54086C5DE6A82096F0AB32B716BBCBCAF26BAAA792931D946FA1D59EE8A9924484E38B02F89D12971903C675B9FE420DACB1C0172351A3A44376E6AF6F25B261173E98B04DD2E49BD795A17E9C3E1D7DD1EECAEC4B8A5AE4B266E4DC0618E41AB8F82A4F652D2A07814DDEBA856B6000EDAA8A8A0542466A83243AD8128B1C468215EB58CC436BBE6B7A8D8CB12FA78131E1887B3253BD623055D72B13E175AB7DB00E8D7680360990C1D9EAD06F04F51F739734DEDCB8AA5ED3BCFEFB49D5C89ED291C4AF1EC09BDFD24029B7A219067DAF493C53809BAAAF830C8D4A3D643AA1B0CFABE58D0B1A5E9C0357AC421459FF0B0766CF82E08F95BF8B6C65D81BFC342A07BA51C914D7F1FFFAE1E069BEA449EC9BCF898BC29CAF4FDDDE6846361A4393F6C6987C766037526A9FE42008FB59DEE597547B8F61C62085BCA0686B6237C384107ADF094C91A230D783F55D39F1E57128A92F0FF0CD92D6B454DF4A3AFA2E1E4DF2C6C9E3CA3B304E27A8D7F44C75EEF09F475ECE74B07A80DBE1B4F2F309756BCFEC0C2D78DEA4CDF3BC864FADBEAA1B9475BC7EA4ED80D26B14B61EDE93AA2A777C8403DAA7BEC36C5503A2114A97CCA8B04565F17A4F0DFAA4985A5FCBB4398323233ABCC728CB2E0D1E2CCD36645E5915985D39ADAE871AA5B1CF3B001FDC7D9F0BD82FCE569A5C6BBD216E09C11CC6AAE067686E95BB28C441F189AFA83A9E117D944927C4A76FED756A35E5EE0B0DF4159187CFFE5F6EC19F8DAB04791E04FA0A3DF55EC95B96D7AEE099867987986D3FF1422DFDA40100C226F8A24468393F21E11D3B583DD8A5FEED01832ACD1E24E2D5A8CE16C24629B9964F8B4E0B2B0F30601FA688711065190665ACEA6A971DF8FB708C056D68540A78FA750B43E0D54086502D4B19171F61B51AC075F7F09260B95F59BF343E1210BCD39C04B13267122B6CD78D2578EA3534EDF243D0FD520A212EBA25407E2B8119068B2CD1D1BA672E5CC73BE9676C9E2D906D44DEBA9573E7FC909E69BD9CCAEBE6600E30D3588CB8EE2E5CE95C65F3FD24E4D527CE764FEF539639155086FE564B607A1082A74CDE886527F8D821E42AE88281A1CFF38EB5159955C1326C2D425D1C09205128E0386CA5C71B911896543799C3B1ADE8391E4370519DB17122A1A5EB6338208984E7347FC174D6DC4D59AF64767115A344A11E2049CDA0AE0BE6D7AE0592FC3468DB58E29CD3010E3EB17F736EE0E6062BF1F345A804F1733C73FE30C70B6FB625D00E529A400C069074235969485142D37F3D963D8492EA4026867ADE6108D982E20B188504A6724E6448992A3F7962AC2F01222BBE0715069FDE7D9DD93A6AC8AD321163FDBF9429EB2DD38F44BCA086AEA91642D4C85DFCF322F0B822B6EF73C570B564DD721FC3E73ED50A7FC88644C4340951C20B1E2C4A65B0BEC3C5E10E4C5F769CABD7E571748485A3BFD0D2204748DAE3E5187A7D97A7000000000000000000000000000000030D151D2328", + ).unwrap(); + + let sig = + MLDSA65::sign_mu_deterministic(&sk, None, mu.as_slice().try_into().unwrap(), [0u8; 32]) + .unwrap(); + assert_eq!(&sig, expected_sig.as_slice()); + + // test to pin MLDSA44_GAMMA2_MINUS_BETA (produces the wrong sig val if MLDSA44_GAMMA2_MINUS_BETA is wrong) + let sk = MLDSA44PrivateKey::from_bytes(&hex::decode("1B8664E94372A180CAD8C3D93F57FC16F452EF524D2DACEF0A50C0DDED23FBC052EA7330489B17CA2FE4234FB0F9CCA63B6185963852374EE10116AB38AAE2A922693A91188E5F5057AB18485D98B87EB21CCFDF5E9F4B5C5787F5043988EF6790A47D70515D93C48C28A855111D73F245898C877BA4E6A646057579A111F59C1BC629D82652D388901B2961C2C270A3B68054329299362691889122320418918820132D03C28D8C144200B825E0A88198A210031328D4884D1126690AC38D1B1510089051C8142D02B390C8440564040800290CA4480EC4424D1A960409A58010472D42346911A4401481884830411B122589808981222E63164D11C165DB064C0A430412374AD19271400846409271001748C9124894064952C06D1C316AC0484C19B69103A70950100208460458084C49224EE4484559048D514265D99220E2A41162C231D01620C31800D4A45008390A22A67018B36882049103440642364E09B3484A8448DA382E5CA810E01810182466D446681B489102384D8B18510397259A400A00436992C40C129285C9C05060486450988884386220808D919268233401A0B8405B0662E4C42119B74099B265E2400860462AA0B40D1A3248CC06318304489BC6114A484503A4010CB68D11860111984CA21410CC2666E0A02C94B01183182C904805A4320860B29122B66142886DC9222D24C171A42211C9A08814854023034582A25183B6711BC88DD0480292387220B041238790C8081203C0099436918BA2244A422959042D48260194322860928C00A844D21264A0A6240C9870C4A6042134209CA4099B128A1B3249249651A022281B44640AB36C029969C4C2710B396D0B964509B6209CC87150142A80B620CB28121A93684B382E01350A88160520983181322104426E83A6481014495C304DCB1826000390A4386103314E02B3008A14440AC78111C5704018021426650BC68C53C4708098481B9044C242680C124E2049284A3671C9B08911A77049982403C0300A83654B426D99345110B82C021092999429A0124E98184D50242061908981282661B28108A6651B22450A342C9C984C4B382DC4086292188D41C46CE2C491D902419C1231C2120CA040440CB12D22062DA3901102286C580066801228C9083101872554244902C62CCCB28502168CD8422E24416D1C20924A0660892826D2329092922C03451258B2099B2481183804243390C2B44C5AB485C814610A188503852988409203A580C9C0E30E0791B8E694262282FDD2FA69AD5CA94D015922BB63BC690FD546BC1ACFA0E79B02E7D1C1E92F419DA68C30CB12C736E216FC72C394883FBF40CFF538A43D08CE8A390DC7B78542199A54EEFE8FCD49225596FC97B27CF8BDDB57209E48FB628909CF889D38855CFE04AB04486378D17DC5780E81168B5E1DF462FC2D43860031B59D80AF95AFF0278210B9D96023E8A88FF0E4403D710895C830049AF4B8BCB57C5972D067A579C8C0021E5C870E1EE10629518191E283E796B58700061BA656B9385F54AA6917EEF52E60669B7B4F4FD36063DFB7822CDB4F19D9DA4E2F41E4BAE8C5274E7A9EBDB80BC9555687827A10224D9044DB2E45A89F582A716B4D3B131AF7DBCEA95DB88D84C73281D4CA7961F752FFA520BA3FDCC245331849768CE4D4B1FC330DCE474F31558764395F97FFC847BE9147DC22A3021F666A4327FF7FBEF007683A84C9FD99F5088703784AFFB3D86F216405BB9218DF05BB4CFA100145C9358EDE1A0F4C69AD23F7BA56FE8755F711067D7D43CF9FE0A93D642383DB3A0086CF7FCCA3FC59379D416DACBCEB7B49DFF554748EE36DFBCC07D15270A0388DA88360E6CA476DCFF56DE0B7B359609E58A728EE4B3ABCC296E42CB76034C7E13BD0B5208EE8CDF451F6CCCE87E9A21C3DCB28B8B9805CA7115065757B3C6D2A39B28B8610A3F30092C2E9F43A744372FFDF6B5F69DA6589860C520882FBEC130B28E1788572252A5995EB00B4B008931220B27488FEF5D04BC28C1FFA9DB5D0F1ED49CC4D775EB3DEC26ACD92BBB8A33A195A0C2F5AB7386BEA3AC9A9EE90C762E005DEF59348B0B1A6A373CCCD0BCD4125E0602EAA2BE057603020C2DC849513333C019A22210B55BCDAD54279BFEC5F998C1D14F7590B31B403564ED2E54AD007EDA4F2DE06AB0315E4C3384BE2C5392BB9FF1D41C770A767E4947FF7B609CFEAFF54DC672E866BDB861B81CAD3CC646DC325A5E1CD37C45FD0E29D6C2117CDF4C8B3082249C4F8D3E736A1CC0628D5FCB322711D78B881E7AABF4FE8967C5D584296DE520B818966A9490C82063D0C3C743208B25EB69BAEF6CC65E3CFF0C0C02997F328D2F1D307D421A87EE8A2737C8D5E7F63141DE2E03F5BB1EA20C829C4A00407F2E500BA2877DA65D64B29E42F097164F09F69C9587E4F1A1A88F619B6D19F210C01D35443F21C7C987658DCFF4163C8646F7671D04ACCE322E32513737B35944D2E70D62924F922A35AE86347843CA971FDFE616FF0DF4C5F77C699B14A555E0D41FF39BA7BD156751E73EC64A25E80E93F59217A5AC8B2162CBA9416E54F04447A99F8073D881BDA47B7D8CAEC135C0D09A028DD2A3EA74BAEFDCECA0D08B8FA701B9ED25B59F39BAB7ECA16A9ACD0446827829EBD9393E8DEC95C2395704F8CAED2155CBE604B047F94A650A981BAA93F070C3142011BF4F1561DFFC4D2F2B7144AC4CCC8DB17FBC3811C421961B156DF1FDE2893FE87902385B0FB5538E996FC40CC9B5995297DB905C262E25145A3C35A6B6F2014CC9DDAF4EE05DED72B3D436E4A6711BBA6B802D0CBC4ED44AB923644CF9E4BA4F24EFE1427E44D0A3FA98C1AA92F37E56440BA282AE0A77831650AE866C86BB2AA49EDDA7AF27B13A612F6A185265D93097679A7ED5A3A38B4B40F16CF5B11B99E2F5B529B940A4583D38B46D388167B34458CAF13682B6070D4FC37700E0222B04B9C1CCFE8FA0BB2B193E743E93C38DFB5029561287C782D9A0BAEE67705C1DF3AB2D8A456EB7C3F5E5D117108C3C15C3F4D2D382A6F69A5716621D004E5F510C4226E4B6BF76C36E649DA5D6EAE2690B1ADA69D8570DFADDE392CE5D9AF4AC94085903C52B0CAA61856150DACA070342754BC3C71AF32F8EB6D5CDF10F0B20471971569E7F1490A32E6F4DAF129A6CAE4DBC977C6B0D18102B9F18226ADF28F168B24D307753BA24574808444E488D812E9E97827261801AB2DD2F62EA4F4C552E54735A018D7ACCB666F6FBA8BF9EA8280876591B8E73D6303B2F1E585BB86F4DA1769503E144D3BD503BA460802D9287CB7B2238C2FBF3490DCDE4D35F02A63463A4F488632070345CA7C19604FED48A7102562965BFD87F2776357B3D1B17AE98CEEC0159FBBE604AEE9D92570E526CF6A5806D1BFD3092007D3DE29B9860DA1DE62184B4F576A8E515BAB346E2A2A4DB1338F4490400CA5A3EBE9AC3A8173C1991839F3A62DF7B903F777B0E5608C7396719639C8BBE3CF8CB371A43F7C436796259728ED11C3AA32BA97FB0B9C708785FD00345543EB8CF195A442A42F6AFBBBCF597CA8230B019C283777830E70C19DCF018C7C0561A7F2FE").unwrap()).unwrap(); + // because this is from the bc-test-data with busted mu values, need to fix the (busted) mu + let mu = hex::decode("35fd0d24022864a6f16159a654290be0c26688439c30c979d14b9553299429cd7c01f9f9835121149c254f2615805f64a32feb0607d87e9417f2900e0d86e351").unwrap(); + let expected_sig = hex::decode( + "20E8368CE256DCE0BD48E92EB0C35821EBE95B1388A861DAF45ACD482BBD95AA3FFBCB71B01E95FEA987EF538F300EDB7B8211698B5190AC5214C681EA39C78CA5EAA17AE994CDAEA07C186342E50A85A5FF292E19922286A96C42B4A998420C9CF0743F36ABF758E695166F04E0FADF9922C48188BB0F5AF0FCF2072C409AB2128ACA60E2780EB6DC1F12A979D85AE7484DA9F13534FD77C57554DC1BE883F1D1551E1FD750B03708D3FBAC87677100EABFDEAFD6DCAD15D1B7B7DC57928916F2EB68C8DC169274401E67A1CDD8E6A0F7122D6733A4FA86AC099446522C9E0ED60E244530A5DEE67E2A07E802145B783C59B9D873BB21A40477A307A84E2BF35EB1A66C42E18B8227B95623B1BB21590AEFF8D36C4B0EF6168D667037DC52B88C6F2FC5C1B1C232E2516B213B5F54A757880AF90ED73FE076A1C0613FF93415EC517632B7D10E2C1B7AB67B4DD50EF4E04C8910E088B8800DD50D04E276171C45C2B755BDE1BED2E740FA6D28D52130C6A6EB8BCFC3B3FFB815B8F82C1DCBF1320AE1B8627B2A893407159B8A1C684051E37A47E61776B9A80A53D3F41473A719B0D60CF8C25546C1EA4878362DA2C29F94617FEDDEC531A1D8E59BE133C033C0C85E3A476F7D0A27216E269A410A9EB7ED154A974C434C06024472D896D6EE24472D915C0C6544DFF73A0AE32C1365042906C54545EFC28EE2B272AE7A3D9F04EE7F975B1B1C04587CB42130421E026E58E45F27FA3ED02D8121F48C82587A968D0DC14C3C84E10F853D486DF6B3D997D5958BBB2A97F57CCC7702BB29EA359BD62E2ACF5A77D16E3A9B0B82BB94B3F6E75871D44D873E0A5BCF7BBF4131A0A9551FC1F504D25CAD2998FC213CD999A8F031667856E67E3FBC74D400C14F7C826FB676FB9735DDFCEDEC534E920386FD7CA810028DEDFE6902C65AC329753AE2AB7D8F63C154FDFF0FEEEB4E6C9026C4508C5A7973B1DC6746FB75E819E976DED75E6473D4DB7E6F018F587EAEA112080CEB7CD91CFC001763CACFCAD115D80246935D97DCD5820D3BD28FCB3582AB2AC40500C7636CC6A69A5542ED845103B21E0FBD05914C734F7048974298776CD8C5E8F2B018469668660ADD8211E3878F5A4F3E40A9BB27B846276F505718973E6AC5B15C0E1AC01D316F93CEF58D5E360909BC374276B4345011BED44C5F2159D5CDE2E0CFB4CDFECB51D676A46055D6C6A7F733E05D5D1B13B5F6F49FD435BADFA5965AB32275F8434C05E0DFF1DF18E5BD575F42F35B24A6E7C5A8A3BD4132AE96D67F2938738904D6C7C6DF612DFA2EB64FE9505EE47CD708D69222D24B5E109C36D4B7044C2BBC9E3554693915119737BAE15D7BA6B65C43E73D4552442D893ED78E1C08BF91231E5F1780079CE01A9C86FA8EC38A12EDEA62243F188AD182A988E7364BF1DEBD958121179346E0C4D164739C35D4D51D5E84587A5636E8FBBE116AC3F39AD92A5A9458C98CC56396D37519C7D229CCF732C254C8AB46101FB79038F06DD00EF23DE921FEF86CC17A7F6A17EDACA42B4910FC8119D1B5FC2DEE2D365BCDB7DCBB1281EAD990230915F8CAFB11E3C9D00E723DB001723B1DD7CF94568417EDEC07398A67FF7B27FF28F778DFF71C349871097BAD91D16688300834E5113573B982AC37FA2C75EF807FC7E616A92C386E6E49759B428556CACFD9E73A91F8EBB1DEE8863DA836EFB161714C68B3CE44034E0823A86C7B5AEF4B7DB7708E04DE158A94D1835573F7E973FF19DCFA95764B0263DFAC46DF55F39C3C98602BFCA12953A300F4702587EC845C65F40C46298008B1AFFC5CC4436516B74B9C740E05601CCF67F3E913F4E53DFFF4776E33EAD531B0DEE7C43692B99D6B4EFE9354675712BAA24235B62B64AC5D3EA1D1E7CBD1B14F67B45BC07F78E4F12E322C4060CA3A6D8F5323FE4B6239F5B01D4381D31F0610209EE83E3E726121E543525F10468006C0FBFF6F6E93E62ECE69E2A98F4E4CEABAC0DE867CE60EDA11C25DF160F367BAF6E54987F678F43847465D5AD1D9947CFA888CBA92B45D1A367890ABAC3FBD50F3E7ED4AF97C1F29C927C339C973A1009ECBB9F6954D4EB8FB30601C5052E7608F6EE48CE00B246E9F47A3E5991F250BF7506BD577C41F14E41570DF2C50E8087BB912E84C5F4E8C51A9870D027029809133FDB22F311E9A71F61CB71F6981D71F45AA80BC922777E655B63E6C14C8706BAA424B9C8C7CFFCC38DEAD82FD95D916E71AA3825B95088698A5C03B18B766299E4CCF71C4A959724308BC2AEDC7AF7C630B92A8B5CACD810A504A261DA7A527EBE24D23985809A5BE891C48926458E0B2890E5FA9695D5F48AF59CF21C37D150433BA777724AA7BDEDB170156BB3A6D9E56FBADA473B279996DEC61E80408636BF546F967FB0AF8ED9E5081F0A4A8B532E40D0C4BC55AC7A4F37F603CB6426B5D36B0B34D231C9AA83666B9C63DF5F497A67B0FB040D106C01BE00D0B1A3E4291F2156D1E637A3A9A766794D683AE106B639B5213337F8A1FBD924F7B4D9566A7C0A009B8E1925DC2FBC1FB3385A42FBB868388BBE6EF55942F9D5ED2C30992FFE514EEFA7F145E150F94C001E8D08904FC19401E871379B79AC79F39A7EF9E738BECA86C9078A6FCB62CD1D84E23708AD98FD2E759E7C841C40A2241DDF864D75B400CB5C6B03502D016C491615A05316F9586414694A67DE2C4D509025B1671FAEFF5B5CD262FBAB6234673E7D7D062016CE7D4B54C813A1AA10E6C1859C8EEEFDB81A138B2378EFB821DA5BAD135FE4E65363AF136F8E6C6D1640D4F8FF491B0FABF3E4D5668E931E39B5928E6ED7792719222E479FBCC018D755068E3C5C66E305E9C52BAAD0019B35B21F1A3B2F1DCEE98B9D0FB8E28E95CCD36838D65282B87F03D0CD5C096164B2C24E149D9128976688518790B30C087F9786C7F8F2E797082D839467C3AE8F076EC973D333515D12CA3CF9C50772AA3434829AB45B1F63EEE12769812B95F7C8ED33E5CBB49851142737562E9EED8ABD525D4C9423CD48BAF00B58343AD12F3EDD0D55A8895974FD99201CB1A0AE42EFC728A3D0D691B78FB85D3FB9210AC0D75B0BEC391AF0731921DF2E1DA5CA56FC6A68A0FE99525B85635665E0F290B98018201558C29F197100ECD37B9112924F60707E4B30BCBC5B7187A09DA0628FAFCCD930F545570FA835D911C9E35B1B73ADF15F7432ABAB1682201A93EDC842400381C645BFC729C8DE1C06AA2171EAACD88E919F9CA772EBEAFEF4D616FA5C973963181807844A3C84A57767F9AACB6BBCAD3FA24333D585C70737C7F8486888D919FA1A9B2C1D2DEDFF7FD1422536D8B8D9CABB0DCEC000B141C31393D4249566587B1D3DAE7FEFF000000000000000000000000000000000B232E40", + ).unwrap(); + + let sig = + MLDSA44::sign_mu_deterministic(&sk, None, mu.as_slice().try_into().unwrap(), [0u8; 32]) + .unwrap(); + assert_eq!(&sig, expected_sig.as_slice()); + + // test to pin MLDSA87_GAMMA2_MINUS_BETA (produces the wrong sig val if MLDSA87_GAMMA2_MINUS_BETA is wrong) + let sk = MLDSA87PrivateKey::from_bytes(&hex::decode("381286DCCAFF85CC31536E8CFA4D6FFF74AA12D0004D0BC826F54D3EF0F632090C06B8D3D562D3EA6C5D9E486AEE773AB7CB8A08382A055C6339BC650523ABA7176BB4169C6294FC52FEC2E43BA13210D3CAF23124DF2D0B4F7BE6327B48DE145071503F0E9EAEF1136A0005F6F5B03FCAD1AF83C10EA38D288934417E86F3D69CB69044064090063248C6895AB0019B080D99189012892CE008495A0609031005C42668C0C64540C80D80164D58388A18A40C0804720AB55020110293B09061348D4AC40182006E089750DBB02823938CA28688421000511066932200D8A2805BB05003255023064D9B184E83448282148103454A540020DA048ECCC24011904D6348801C004244300922A6314B860C0A4985A1184C18888112442D531690E280249486488C300909997119030DC09820E20664618089E4B01041208484349023058601A83123A63103050464226954006C1AA930884052A082402221690449411A83050B278AC4003200B521CC345083A20D60C84019330E00162941A82DCB446592844C024631611608E2842988023121B4844B288921187188C0011103201238280BA6112303490107629C9625C416445236708338929022084AC60C1B00648202324024315B867100104DD1228E58404C1082119AC245DBB204D0084E5BA20D20245159342821B004C8166DD3260419B10100486010A90CD122489914404C288624080E1B436C49A00C59942051108CA2466822A1714A982140366E24A1251C3421E3060A91C40098185223865114C38DD2002D00B03189A8409B940860A0701898488AA4449000621295280C10055292705A260A1C80211913281339421C0146E31030D9848C13202D99462E0C10881384302431712447418132860B948110112C5838898314825110711A982904260C599230102072C23666CAB669E406861C8469843012C49421241820DA12520BC3214B900DDBA004118808DCB465123085C1448E1C11859226128910840B1864E2286998880C90A6011B08495C180261C88DA1184621C109199389244925503625198490922030E28651C49685CA460410456A1B3862632809A202641BB341CB369054262C1C054E1042718348649A4605A2086041A2900A110151366CC49208C0C444A1C080D9408404C0708032851C43608CC4309CA6800A404E63481019B830CAB82944160461348E84184A24181013019188362C93246612C06463961000C3909B9600438685E1188E18C6059CA064844429108445C9000281266AE11804E01004591426E3328023C405E198710C16328896640C33825B2245C9082619236E44224C12126AE4B811C98845100732DA40825BB2316392895C1826218265639820D30022802220DB9624E29440E2042D2432424832295828461A9145D2988D21999010112D5A866D93A844CC42481282288C988422A791249001113270104045E12610A11411A3820D0BB64DE138019A046124C76412496E1BA004A3B08C49026E5C0069214144C8082913C83018940560228A0AC9258286444A102509361018C32111302A1BB5092047614C10511A422699A40041426ED40681DCB48122020A00336D53C284138751D9C851C238325C809101174013366D64A86019962022A38858B02C08078002170ACBA670213805D0222624A640A1A08D181350E0B42CE0225001A6886444011B29890B000092026C48B62508B60060926019953110B311D022215B006C80B008D18205133924DBC049544820C318069C102AC888645A248018C04D0A038010A891A4086461880C0B002A02B98D24220EDB246D21C76DA4126A58026190B2311935858B462102C144C9864998B2641210486110899896850816050B1980E2C6080886610B040D1A1880A4B80104C52564449021109291202863242120066A4CA208190222529491120886933206C49211CA000461162A00000E43022E98928881228E0C456511882102849149C21010920D8B126E10B42014234101A521A2C220813291D2344580324A1C29824906824B2060001060D0322A43208050342D1013010A094ED0C06448262050886013A07000037114948420A30D21055282A8911149125A342CDC880849C8288306650B16501489048C3626A408709C908DA0100AC90060D046718BA208190760CB8085A1008E4A222A01858192844C98B22164C8000199887457577111119A9C0D695E5008224F1F8D3B293D5067D12EA5D9B1ACD120060BE64483AA869B34C317DC13C55FA59FB1939381A21B8E043795F95E4ECB1592217A42B07F3C9731ED0ABFB2CD487CDCF6F956D5DECAA394378DCCB079CE795E96A9BDE857502A1EE1DBB3D6400EFE25F81EA9AA71A2124C05A0E97A271BF8B166385A3AB1E01E235AF62546933E80906B47627F0DD508B22CD0FB34DC2459B21C1437DDBEF93F603CEF19929437CDC85AEA07303F84662E64973F65BA31B0142FA866EBD1CD858AC538E9A598EB1A2A80D463E84125461926FD53DA52C9D53EC1C8EE2E5FC63A7D3259FC69C7A973421A9B4B3BCF4E938450C17ED123D7428C6D17CBC6EDCC4673750B726A36E187A42221BBE039A9EAE4A01528B5CA3074A7448193E8F29C65AB065A551FA7827ADD240C06AAD74692AEE6C44BDD6EAB9F0DC88D3564C3EA09AEA275507B8B085DC4B3BBA5EE7E9A35A3F25FBF2855A76383DC498935BEFC14D32A0EF90EB2F604C0DADFA4B5C7D8FD79BAA318F68B2A529944F87CC4FA80F60C4BC351A95E00EFD0A0D8ECD2F998F30030CF6D9BEEF2F7D63077DBAFC7AD5E7667F8FA671E5B69BC0B0435C80B28ED28D8D884FD60766E030A570282D8110419820D437FBA52C82CC99C94631B5C8AA9B681DEC98957A2E19C6123721B0DD4722D62F7722B3678E5752DD91C4D0EF6A932960EC1312724C56F58F21D0BFCA70340DD4C48ACC5606387663313472E8194B65FCC048A858D6480DD9C2B83841E85205E6C5E9A95776BA29835BBBF8E567200C84E5499CDFBA85355A2067A170BB5897889A73C204CA5A93118E51A4595281A80B952CB35D5589673270142CDE60402FCA4C8D9E437134DA79E7944522FB72D35F3FDFF2C15D4585AF10C3B64A7A40F2418A336F4EE66CCB98AB0D1B082C62802C87A9778B86D9F304847A76420507978FAC7987E8BF667A8E40F15FA1D9B0DD6299458BA196D24E9C6906C26D04708B08F99FDD9D110094D5B36F48213B213C66F5F55F97D4BFB500157E03A074AF090B7609204A71CD1642D11BF34C76400C124D55CF7A80DA98FFCF52AB2B229720357ED071616BD8E03D7276561ADA1A746C4EC37C0B21F964DCE7B1AA303A9CD7CB8108D7E18E32DB22BCB89FB7E6408324ABF03E296572648FD2E335CD4463A64EC5CBDC8AD7953618D8B2E309F04A74302562FDE3CFFE805C4BB4C661EF131D37674AD6F9C5BBF2290727AA67EA3848C48E3C3CA5A35009FB91C0CC383005604C096422407F4337C17D63D885592A5F15F9851F5BF27EB5AB5095B95AC69C2F36A1664B0AE71E4BB131C3A576B41155A2F997BCBCA494584788ACF2F5CCBAE28D09E69F3A39C2CC394B3465358E1F2A23D2E8A3083E3E26E90121553309A1B8E24932CB64FE659EBCF38AF0D1A8819FDA08CF4F2B03D79208061E113F9B89A8B9563E5163C6CBCC9BC2048C15E2AE39CC2B64ABD6DD6CCE5D01ACCD8CC08A4F266491446C1CB9337DAEEFC6777CBEB3291F60CBCC3194706492B7F953009DD5E70245180651DA918647F05A2902F4719F6D97CF3AF5D855FD1A1C76C29910D518771C604AD7B802E264F39BAF16AD772B0EC9DA32FCA34A85FE2E2447CAF2EE925EFF5A442EE9DA327C38EAA22B8E67DE4A7AFC312CBFDEB44856F09015237E59D26244631A8B14AFED134805DEF32FF49058C72C2A7A4B77518EE2B162DAF6B90F1C96AF5225453C5E4D8CD401C6FE1C626D4CA037F380BF288DC428B9680B595E10BDDC9D8A2D2BC85DC316BD9239D557535F66B3802FFEA1095DD4DC2F1A9A1E6190619BC5A2E063EF8C95066F5AF92B3D99A418E26C68F8AB464374FF3A789F07B2C5A99084C7275DDC481BC1BBBE8BD6171DABEC4A5B01F115EBFADAB26C9AC17A4D4EBE37BC294F0ADE593A0DD4AAC258D106457748FDF0059752442E4B96F1294D6D92F6D9657E131D0288EB3AED6C89390E05DC84FA5EC0EAE33F4DA4DF2A7614FF1B4ADBBBC9ACE849366AB82D5902BF7C5C66C095BD92DABB145BCDF8A744FAD2FCF44AD868C349A6DF2F509EE92687123E39DDA96A735AAF57C09F5EEEDFE3CFBC0B9C05B5D51E2F66F8C0E77AB0302AE603DEC8CFCC79485948C012BFDF7D0B9E8FF0D6545EBC781054183785D95173510E65E0E52CEDBB241D8E23C997DA523B974C53ED07A8DA63958768C9520DA7EE6B7093721EDD8633B6192E12D19065F63EB79504B92C7BC31F176DBF4FF21F5DDE5E414FE6555C8D76A2A77B3B3CE1EF545CFF4E776EF9B440506392ECBDED301490678328FFA2D83DE6099B750C3C99A9797A14FA372A57BB2CB72BDBFC640144D1C100899E2E12F2A23147C0B30E4F162184E1E69C1FA770B46D213F52CB4B36864289FCC3DAD1D54216EF9D9FC81B709D6045DCE41E91E5A0C8B5518EEAAE07D2574DB1D53ACA5B2A4A2FCD6237388A86B4C58B4200511103CCCB2420E62789C13F87C82A90751E121788274C028D2106D5FBB0D9D3128618FCD7DC5B4801C01A2410ADA95A7CA902A8F03E2D538D783AFEE2B7DC674C1A71DCAFCDFFCCB00273B942D79A5435DA1CFB2C44C3FB3A4E221AA1B2868EC9F4FA43A522562AAC34BBDA07D337A12748A1B5120D176CD117110D4A7061BA6AEF3EFBAD21E998B78192214380FD84A5D08F9B11282A15BD9226AA54F019213AB6C0BFE8291ADF09B680A16770EAFB2B7042274EEB896DC7B6903113862B1BD5C523787693E65B6C2A85845768A3BC54DD7A02C4ECE6CA3DBAD2D83DC65CA1DBF7C3744EE4E4748507DCE7F913F4332CF5214D8EFC1B4E67C86AEDF09D545C354FFE6B8A4B8F15E765A8F8CB13B24062A17084C52E82964CF6657C808326ABAAFF60545BADEA8A44DAF0A038EDFA6F4A66C57C721A6ED2E8736EF92F4BB8D230DA58DAD187ABF58DFD17FD11A9764D3608D288AA2EF3F1DE94DD7DF07E829D5D07CAB1FFADDDB4DD460D9FF3DAAE1D918AF2AE195B0F95CFD4E1543B4920B20CC808C6473C58933B370C45E47416E973F42FE6FC9C4FB5FA95DDE7D8325A8837648625F722EF95B0D08803FF0299E6A8108122A1C7172FA96ABB263240FC560CF3E1BFFCB2ACA913A9788D5C02EA0EFAD07BEF958A144B15A902AFD13A0AF976044065068C77100066F68982C0B98BB77F814AB624EED9C84531969DB1257C7AEC11DC682434809C7594A02116D6840DD99630629C150CB0CEFDA1E851F35C50CEEE6689B8450E6E9C4F8DAC4F487AE4DFCC60B8641A780BBD724036C21B38BEB1C8ED30EE9AA03DF899678FFFECE5200DD2270A9332B9557E577BB7E5B3378E4512F0BF439CA3BC16E8575DC46B2E497988C7C6F4EF529A730D7D418FF548AA7710F6F3230503D92A8B6228FD19CDC53F1A40165BA1F3EF1FC68BD5717AAF0810AA56AB90604BC0DF466D92403C1B9CCE0B2662D415EA69A1BE094AA5583DFC472480EE0430A93B21BE5B1BD4E27026DF5ACBE51D891B0E49F70DDE93FF43CE43A4F9B82AB951DE14A8A9BF4650FC38D769F4850688103D0D3E4A9BA645DAEC785A571D573ED6ABB22D987C2B52C5102CAB5B14610196B8D71DE69103E59B5C2A682390290B508A693CF87AD0214DC8DF2541FFAC89B38E19D703AC52D1624216FAB0227BC1045A34FD658111F92DB7B97FDF1CDC56FF7CABDA8BC26EF8347A92A2D8A3C56C150C5E3094F3D34D4CEC3C371602907419D8259DA0C74E32F63A9225852B5C9208785E56D0F4DB3349F5DBD06A10397965453D4E70D2E1E7FE56AD98143720BF25FD8204497D0A5EE1F37BD8AD4364D9BAAF2BE2BF519316D1314E0D4801CB90FE52F66A82FCB4708EFDE4FD45F70C38E70B59206C221B42E840B72FE1FD0DE5C7B0B8C0BD1B6561899FC5771764733108B69F81888FDA9E8CF60044F3BDD3289637F6ACBAA5B9B1145DFF8CE247B438320535C514BCBD445146BC00EF8E1DF3CDE218775DBB135FDBE92B7F303B1C344FDDC2E381420544DC9694DBB142704A0F092BA281537176F17B0C1561F0BE320D79677887DEE7C27E01232BAD43D49D9D1FC1A6D8C887443FDD756EE7561C24FFE0FAEEE66DE9A6F05CF687EF4CF456C7DD624DF017E11F66231793FE81C958AF0DD89C0D088B17DF94304659B79E9F9C5196BC1A9F22CB5698C751B829427CFB437B16FE1E3960A70B223E7C46CFD9F1C6195386A170AA07074D7BF9F634CD834939F8520B73D2648D1994CE80FC17E549DFC87CC6164A9CFD77E6E1968D9C9B582FB70736B6163D1E01C4632D594E3A3532E61E1CE87FE5AB5A2E11F98224E95D7136B34D814F138CC5C557690B57DF3EFCE187CE9A01D1CDDE645DD10814E3B3FDD69947A0746C68D9F77B9AFC61474C55CFF7EAB24AA5643E6B451A4C6D84C2E6E3D7015C6F00FB264D55D8B2CFF5F87DB3050E7DA34F7612EE116A088180BD0E1F3AA1BD63B2CC029B3E96C9EFF4C2D1A2C53F25084503639819E5A6E73BC07EFA5212CD9075A6547710A1237AC4991445ECC76D0C11BA557E0BDBD7651A4A6E66E3F9DBCD532C2301BDBDF63307AB425E6AB902164925BC424C5E7B2892DBBED51E0AB5BF33976243D5AB4DB7272EC292D1DBFC494372B51526C9DC1380C3BA563D2CCFA3A20EE7F800ED8C2A7D199D2EA6F1FF7F4B6B4AC51041C974D740FE7E1EA1D7311B81E5ADBB600CDF62BE0").unwrap()).unwrap(); + // because this is from the bc-test-data with busted mu values, need to fix the (busted) mu + let mu = hex::decode("d6961b5a9cc085939e493d4cfabfabb4442604d4058c4bfea3fb5c25cd7a3e4551b34bd9735be6e05925132e0aac4aa374534867289a6af4503baf449e6f6b73").unwrap(); + let expected_sig = hex::decode( + "1A9FF550C3F682A043D145B8A8ED2A2C2F92914E1D3E4F9C52EFD2A478EB028ED77C2E9C9450909F7A4032EA4059B51EAD6D9F61A8EA867AFCB794CA32AAD3180675DA90252E11952E936F2ECC62E1386BDCCD12DC22D23A2E21FCB009486376C85E2CA99C51D8F68AE93506120BEF1CCBC38E1A5DDF940E37DD0EB650503B969F9760D0F19448164FDBF0983077DA415648D70289BF6163C32DCC6AA8CC3B7ACF261237EC70D216FD157C1ACA6BA135470CCB4FFDC1710644AC9DD5A67E037FDBFDCE2A4627555D48AD1C8E537928B6A954FC006C5A34ED3483E2D241B618F59FD8481F69B6199E91A6545CFD485ED8665A82F75E36B27662562BC0D729D19426CD4AC19DAD71AE298FCFC278BB3B89B28E7713BAB58C4DC7BEEFA8444B6AC5CBF252F71CC7146D6EB43E0E9981C971BA9EA12BDD4BED8C20CB4BF91E805F93307AA6DEDFD083307CCF9FD540E674A40D68DB0538C8598CEE8A7186B65C4A71A4B053CACCA88887F96101547029FC74765DDE92ED69956B30C09BECEDE6DD947A7C25FF087C30AE4DACDB9496C51EEAC61C5C6B9D3AA48E255999A1F76811455B567188F26778D8C62758E1A9FA884CBCF72E04C7A6B4BDB13B208158C67D5B6A2CF60F8D27FDFE5D2E37947203D0C204D7937A10420DED707F73DE8771F7866B9537E1265101EDDC3D7C192A3B209B0E5A0D72495F1AB0C776FBBE892DA53A0638F98392E816B7C0DF714DDE62B473DEDE5876555112A554BA9EB37A363D78290C0B8CA56DDB600F0773CDF68898876BD34714F5F6C1FD38D50FDDFE778621133F14A1270E6C20E089753269D5B590318AD9AAEBD4BADA6EE5C48044CB11CFD14D153A8EE69335EA6D0C7435C33B94CE2C6E97F1D3CB80A70F66FD06F0A480FEE71B7B6A6E2DC7741269D85F05EAFDBD22D5DF71AFD9AB2D24F24729DF492926525F03336B515CCA54BB08989C16D42C1DEF1996E99659BB2F9F96004C258EF140E5306FD8CA549C99267B237584C733921192E89C8EBFB9934C3335B722F7E3EB519924A3F02A2B7E08D8C23F49C1F1262968D77887C1DB0D9941B203882C1703A8B345503E0E3CA472D7D7E11330D8B53704B8C59D12CF280857065031BA223B0D59C87C85AD214339DB60D6A2ECB4B908C07F0E2B285D0DEAA8CBF7B2BBCED1A3AA443C9580D1C7A6C5D618F6E1C5CB60E81F8E4397373850F5C18FECB489261932CCABA6CF42840DEB52A62CF0D829E33C2D4685ACB4F5CDD3A1B98D2777C09AF4DD926297E6B22037A37026062A6A42187976758B7406032B50A42D9028BFF12737F2E2CD1BAE8F78D10D87AAD2FCB4524B083D5542151A520A5B3C850F2879760875B0C034D714B7890B9BB7E86FFD946C9C4E583D0B7B1AE0A29553C50C3C4C30AEA7DE67BB9DC08E7845BA2ED6B6D6BBB1F65DAB6D269CE4B625B05A4BA5BB7272E4E7976AE6ACBE1AD43CD73DBBD215E9F603363919802497A7DC9B52C084BCD5E441C0B11FBB53142E56D1F182686614C97233A176741FE46E2F5BB749BE5A34790FF242331BA007EA53291487C96F998A2927C25863030DB6AD1860252DC2834DC3612E84F2499F39856DA4EADD223C5D2CC610026E54657ED6B9DD718DE6299979F76C0BED0933CE1F820D65F32614CE3DDFEF863A1A894534A9B93B5A0D54EBE30094BA323C5874BADBAAC2485D83363793D2CB96E129CB6C46C8A504797276C313034068539FCA7FD5929151B1D38F93243B93A772FCE64381534F9ECAF57899397E9DF6DF6CB9619B7CEC5EC498B020E708889C762776EA9436BD86B23550FD3F5D61CBDA5D1B40C629662D8D987B208B6A2015ACED52288A555E149688737323B5B5B0E6EDBE1D70A1D127113A39DAAABD281FC18109FDCD90C49FB5647E69A8EA116AAAAB1E0E5D73286DE32437B41ABAB779B20BDEDCD92F6D6D683587D33228D4A83DCCAFAFD6334680F4F9F370A5EDF4A70683BBEDBC064021DFEC2E47034275B8416D085B4086BB374FFF58E2F33E0D0920F638C9752098207C5E28341D8AD70DCA78CA47A2268F28B3350DD69B2EE15A7AF278C01EBE1DFAA8AD4739F7E5E408E2A39FCE7B8FCA51EDD592B8D2766FED9919425EC7CD80C4836795C924222D73AC0B380E00BFD20ABB704959CCA10B5EFFD9A0F72ADC823C74E90E6E95B2A3DE9D8B9F1C1A161D64E7393B259990D2BB4636B02A216ED12D2AA0B5E26C0BFB7D34B3230C6090DF5422F2A6E80DC8DE9DA3B5A1CDA4971E96C7DBB7F6992A60AD02B14C5E7CD6C9977DA0D3DA84CB07810325581F9D7BED0BEEC12E6C30338D903BF0F655EE8AF3779C87265911032433690CA981D0CCBC4F98869F316B6918B3135D405B34A3C7069112FF17E44554ECBEA8DC61269EFB5E58EF2AB1FEAA15B426879A344CE7DCE621082370F0F4F9B0E2602E942A1BB156B6DCEA3D9DD2B4FDE908E91B1913CC8BB6FB46961B9669EDEB713843C2ADB164EABB9D9615C4AB97AD17143891EEB6529C1C0E1542C0A36F70E93DA415D9ECACD6DAA5465840084C99F8F68CD236A8EE23B8A14EEB186909F6A9A4B1D1D2360DD548E57CBBF41C8A86FBCA6679E55341BAEA6F2F01898A7DE07E4BD91F6674DB7B7FD0667154DF75E543DABDDDBB4283BA0F4D8E38108BBD4BD65B82D9C84F0D8CD2E559D01E399C621056323B8CC79C53B8B8C9DEABAD2C0BE1DFC067EFCE38C591287D61545C36F36B81E7ACA5F00A68926C61090ABAA6B48F19DF830F4D94E65673CA03F50ECE3FA3BF6C235498D402E4B0521135255D85000260421C2D28D0D560C535CE6CBAA62E0491C5B305352CF1BA8E8A33F76F22D6CD72A15D07D6930DCB507EB88AFEDA15E810FC728602312B3053CFD2D8314DDE6A63083248480680A2882E173B8D89C14646FBD844A5C790DBA9B842A41F48B6BED1FB4AFE6826F95082A33C9568A40AA4B5B40CF1E723F90AF1A1DC5FE08C91B5D24B8BC1FFABA36B308A6C2184DBFC3B3D36916B2B744239D548F4F5A05254EC548F3437520609C5E562C82BEB9D3ECF1C1F9AF5E84DF9AEA3998B23D51741CDD936C24E23F1DEC3DC58BAE1FEF9A2F1B71A2604B699E212AFD349944DB53A39554CFA94E0EB747722674707A6F99F0204EE68AA20094DE58AC97F733FA76C928DBD668F80584F6FEDB7218BA74E126A0316B5FCF38F970484117B88BC8545ED9FD67466E7777E66B0F41F593E46135665096DA1CC75C90437A01379925F3327983207CE482F6AE3780B1265D6054CD2AE6B0418D2857D59FA7E94FC32AB5E94CD2C83452F420C8861F55B0A5A6BDC8C5C4157B33D6AE11C3C9C2E8919A3D09B9ED8FC09594D365A7E85F3D9647E324116518F6CDA1ADA250F194A87AFCE0D9D60B768AFFF8FCC7057AC73FB520DC707F9BC7408EFBCC3F85D5F618941441CED77C09B3E697141DE2AD6C3A4551884EE1E88D196E1AEA08285D47D81C219BE827D07891F8273BA4B3A26C3813EEA482674EA8EB4FCFBF04EC0C9428A7686816B53EE0749FDE60F50460B672D58C06F103304C28908FCD079A12574B170ABFC7E1F39FED5131D0672ECB3983810C316DD38E2FEF6DC99FB7FB5315F2D6496CB45E1385EABB69537628D9DFB0FAC95B5E5D4B05488B18E0068B0407CC737B0E711A0179EEDA1C7FAED33AFC5AF4B8C318B98244C97255563BAD20784AADE6A618388FFAE1C286A4C5EB891E236FE57FF414D619E8BAFFC84C225C59D02534721490A50BD39C163A1EEB6D811646E54D426AE9B3449C3E428ECEAC6E23F6CBD8EA06D9682385554DEC853C5C0ECAF26F5CF5BC6EABC4A957215FC497084E9A8CAFC00ACD228B4F8245F9230FAF8306635DB89663C5D9A31C648948500FBE0771E2BF15C52395468CF50C6C8953DBB6DE252E6649C6D2D060EE34F53960B5B7EA0981B7BEF3D0A1D64853D9EE06EC4D73F4A75DAC5E5A2B59682E6B8293E74ADA3322C1204FA66C6E8B5EAFD796305AA884249D00E3DAB6267A87844D595B3BEC430BB416BD4A4F38505F046425ECA84495DC0928DC4D9FA18BFB800C1B0578B2EECE2B97153A1C296E36036F9366E3443B236E4E124F82EC405CE2ACA48DF43E543C514727CE182B34D1131E366E394D07F9C72022FAD6E4AEB12CD675E7EB837A7EB13BF4D9DE553DD19DFBD29A6E2157B58D893F3E4B574AF01F103989D8FE2FAEE269BAED7094BE606678719E6E82DA97D3DB43B3FD72863180E1B4D7BE55A721301B1D81963BD37C3259D55FA6D43FBC217A77B38B83CEAA4853A921EC7F138F666BFBBFA49B4672DDBBF1CEC212B530771A39EA244B547F28375936A3E8F692216358A944542AF2D559D6FA216A5CC60DBC892D2BF38CAF1745F5CF6336754A21378F25DE5F472FDD8A1B33189B5134647F7965FE0E573C508B61980DF059B5D9B3AFFCE632230FE873F6462E533D4528DB8B03DD464D3020B5DC22525AE33AE1FC345DC2486D5647863ECE3DD49EAE7A5AE128AD8E39065F77EEE3456653E3FDA5135A45A365B8043F2F019825BA9CEBDBA96C8EEE7A8544DBBC60218776455843E5EC61D9DFD7DF3ACD6DF5E130A5D2F958F5E26F3B1A80029EBFD13F7EB24301750EB8608E7FC73882797E2342A37DF45486ACA0603D411592A13934799D203ECCC197C0F103180D5A0C1CCC3BFA3CF1DB199ED934B4A5B52CA9BA66FB9C97D85B06977C83533F16F6B7F3D6E5945B802362E7ED9C42E873017EA4E0C9E7AC96087AA7FD269D363A8BE8FD39A0234CA842ED209865164D39C4EF592E7223F3C3684EAE0A2AC757D3CBAFB2E873C52B09BAA4788190EDA773D946A5CC67D59C6EDF9EF20394D2F40DF35D20E5F67E1891C6E61798B1A8343E22B7FDF274A996F679A4EAA1505DA4EFE1C4D94A69EAECE3DA05D6CFB63294D7433D9885EE18EC94FF1F23DD6FD9F99BBA2D5BEF46E0A477CC434F86B2FD67FD55B06AA2B698A70FAA1894E925BCBB2E9F229A1858E789920A1AFB5FCDFBD4D3F904F87FD863372CB7C103BE365AD8848141AEE2534D42BCBF5A954F8BF62DF7C29F9ECAEAE7543B56E7188D440A0BB4760FD0ADC019796611E4A31152849818CCF0B146C9CCBCF4FBED7969BE81C49D92FB26A63249DB99EE6CBE935677AB40F50A267815FFBA5BFD7A72C5EBEE8DF1DDF713516F2E4550C9E74FB1C7E2CBA3ABCB91E315A1ABC8E9D48CED3971A0B73DFAE6955CF3CFDD925D86B4F3FD182327599379F9E13D7EA5E405C335B1D95B6DDE4DE69C492F98F51974BCB66C317439653A9014166C922E61150FF9902F9C22C29B7F15A27121BEB39835D835CB7135C3992E82C765A9131485893DC7C853937E1F0C9E0EB30E0502224195E318AB98C56E4211C926240BDB95A1C9345BF9AC10E2482950974895268C12DD92D0EC7E941A162A57106C0E2A3D36376488EB22694D4F68C0FD03FC8ABE4F5EDDF3E47A9E9F7612C2ADD9F847309DDBAD4DC71C3EB2838F29CAC79E8AAF3E88865C8C4FC5FDDDD5EE0CEAC09985CCCD5A5AA6D009BCE39AF3F8C8AF865F584AAF03FB8431F6BEBDA7EE1F1F0B3D672BC6840F3417F497CDE3CD3DC2A214C2C895F61A20A7CB184DC6DEC73002923A6B50B671329F2A16E611E0099B5F0438AFBDD2285D1AA097DD74C4E5D7CC6F75E4313C67E91C71AA49B5048780C0331BDB8917247BF059D9CCDF6FDC885C2950C6FB84BA8ABC463C2DD67629F8C28600081C10C75EE88DB112C432A516983A933A584A957646E2B5B4F9255055A0558574D46EE3E074CFF5FAEBFEAB61FFC994E5B9F46E16552590257B6C4FBF80597B11A705BA730FA495D31FCB0B3E568FA264A6C4512E9B598162AC242437DD47A319FE31A5D9622343B71286BE7E2CCC6709978044170B6B7C8F0F1D109B9D79F86C9A2B4E2758AA97569F47F242BC0233CC44E0E51A02EA805D15A30E193776174FA43787B621293CAA785B609AC5FB851A90647C6A35547AB985E5CCFF969EA452A047C5D7E6CC86404DE41FF4FDDD2B20474BEA70C843CE3A3B383E62F2BC234B4D4962275A3C73E530ED2CC8BF45CB2086FFAA5A299B5236A46066294D80B938A18D71B718B083E685173D10725DAEACCEAA6821A7C9362E5DA01DE8DA4DC54C4C7882E61056FCE7EDEE3A84E3414AA7055DAB55349D3DA50EE216C797C4E5B7D7A225D35E29B150F7499086442509ECC8240CE53A08032C97F6A07ACA7C599DDAE5E3B38E9D78FAF9FE804EA5AD23DE442794991D6633F3CE3D83B08F8FE5D862ECE02B8E710EDD552CE1CE465353A6A3FC014E60F0A8ACE5FD7E0A6E524FA7453E795B96C2DE5E2081460C3754DF5A782984D8CD5C7ED1200B45C45E58387EBA329B93F63143BE6D4CC911D305EAEB6685A07B1E4F982E17904B775F6255E8279FE7B5B445AA0523023313343478D9CA9B40E2048B2D1D637468C91D2EFFF0611164254646782C6D77197A1A7B2C9D5E0F0FD4E576E7782ED0C50ACBCD2192A5275808BEE000000000000000000000000000000090F16202A30353C", + ).unwrap(); + + let sig = + MLDSA87::sign_mu_deterministic(&sk, None, mu.as_slice().try_into().unwrap(), [0u8; 32]) + .unwrap(); + assert_eq!(&sig, expected_sig.as_slice()); } #[test] @@ -806,6 +975,30 @@ mod mldsa_tests { mb.do_update(b"jumped over the lazy dog"); let mu6 = mb.do_final(); assert_eq!(mu1, mu6); + + // test SerializedState for MuBuilder + + // serializing and resuming after init + let mb = MuBuilder::do_init(&pk_expanded.compute_tr(), None).unwrap(); + let serialized_state = mb.suspend(); + + let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); + mb_resumed.do_update(msg); + let mu_resumed = mb_resumed.do_final(); + assert_eq!(mu_resumed, mu1); + + // serializing and resuming partway through message consumption + let msg1 = b"The quick brown fox"; + let msg2 = b" jumped over the lazy dog"; + + let mut mb = MuBuilder::do_init(&pk_expanded.compute_tr(), None).unwrap(); + mb.do_update(msg1); + + let serialized_state = mb.suspend(); + let mut mb_resumed = MuBuilder::from_suspended(serialized_state).unwrap(); + mb_resumed.do_update(msg2); + let mu_resumed2 = mb_resumed.do_final(); + assert_eq!(mu_resumed2, mu1); } #[test] @@ -829,6 +1022,68 @@ mod mldsa_tests { MLDSA44::sign_mu_deterministic_out(&sk, None, &mu, [1u8; 32], &mut sig_buf).unwrap(); MLDSA44::verify(&pk, msg, None, &sig_buf).unwrap(); } + + #[test] + fn mubuilder_suspendable_state() { + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableState; + + // `tr` is the 64-byte public-key hash that seeds mu computation; its exact value doesn't + // matter for exercising serialization, only that it is fixed across the comparison. + let tr = [0x42u8; 64]; + let msg = b"Colorless green ideas sleep furiously"; + + // Put the builder into a mid-stream state (some, but not all, of the message absorbed). + let mut mb = MuBuilder::do_init(&tr, None).unwrap(); + mb.do_update(&msg[..10]); + + // Generic trait-conformance tests (version header present, [0,0,0] and future versions + // rejected). + TestFrameworkSuspendableState::new().test(&mb); + + // Serialize the in-progress state, then finish the original. + let serialized_state = mb.clone().suspend(); + mb.do_update(&msg[10..]); + let expected_mu = mb.do_final(); + + // Rebuild from the serialized state, feed it the identical remaining input, and confirm it + // produces the same mu. + let mut from_state = MuBuilder::from_suspended(serialized_state).unwrap(); + from_state.do_update(&msg[10..]); + assert_eq!(expected_mu, from_state.do_final()); + + // Anchor correctness to the existing one-shot path: streaming must match compute_mu. + let one_shot = MuBuilder::compute_mu(&tr, msg, None).unwrap(); + assert_eq!(expected_mu, one_shot); + + // A corrupt SHAKE256 `squeezing` byte must be rejected. Layout of the delegated SHAKE256 + // state: 3 version bytes + variant tag(1) + [u64;25](200) + data_queue(192) + // + bits_in_queue(8) + squeezing(1) -> the squeezing byte is at offset 3 + 1 + 400. + let mut busted = serialized_state; + busted[3 + 1 + 400] = 42; + match MuBuilder::from_suspended(busted) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error for a corrupt squeezing byte"), + } + } + + #[test] + fn serializable_state_mubuilder_rejects_wrong_variant() { + use bouncycastle_core::traits::XOF; + use bouncycastle_sha3::SHAKE128; + + // A MuBuilder is always backed by SHAKE256. A serialized SHAKE128 state has the same length + // (both are SHA3-family states), so this would deserialize into the wrong sponge if the + // variant tag weren't checked -- SHAKE128 (tag 5) must be rejected by MuBuilder (SHAKE256, + // tag 6). + let mut shake128 = SHAKE128::new(); + shake128.absorb(b"Colorless green ideas sleep furiously"); + let serialized_128 = shake128.suspend(); + + match MuBuilder::from_suspended(serialized_128) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHAKE128 state into a MuBuilder"), + } + } } struct Kat { diff --git a/crypto/mldsa/tests/wycheproof.rs b/crypto/mldsa/tests/wycheproof.rs new file mode 100644 index 0000000..93c9855 --- /dev/null +++ b/crypto/mldsa/tests/wycheproof.rs @@ -0,0 +1,1029 @@ +//! Test against the project wycheproof repo available at: +//! https://github.com/C2SP/wycheproof +//! Requires that the wycheproof repository is cloned and available for testing at "../wycheproof" +//! relative to the root of this git project. +//! +//! This test file exercises the following test sets: +//! +//! * mldsa_44_sign_noseed_test +//! * mldsa_44_sign_seed_test +//! * mldsa_44_verify_test +//! * mldsa_65_sign_noseed_test +//! * mldsa_65_sign_seed_test +//! * mldsa_65_verify_test +//! * mldsa_87_sign_noseed_test +//! * mldsa_87_sign_seed_test +//! * mldsa_87_verify_test + +#![allow(dead_code)] + +use bouncycastle_core::errors::SignatureError; +use bouncycastle_core::key_material::{ + KeyMaterial256, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; +use bouncycastle_core::traits::{ + SecurityStrength, SignaturePrivateKey, SignaturePublicKey, SignatureVerifier, +}; +use bouncycastle_hex as hex; +use bouncycastle_mldsa::{ + MLDSA44, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65, MLDSA65PrivateKey, MLDSA65PublicKey, + MLDSA87, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPublicKeyTrait, MLDSATrait, MuBuilder, +}; + +#[cfg(test)] +mod wycheproof { + use crate::{ + MLDSASignNoSeedTestCase, MLDSASignSeedTestCase, MLDSAVerifyTestCase, ParameterSet, + }; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../wycheproof/testvectors_v1"; + const TEST_DATA_PATH: &str = "../wycheproof/testvectors_v1"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + fn mldsa_44_sign_noseed_test() { + let contents = match get_test_data("mldsa_44_sign_noseed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSASignNoSeedTestCase::parse(contents, ParameterSet::Mldsa44); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa44(); + } + + println!("mldsa_44_sign_noseed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_44_sign_seed_test() { + let contents = match get_test_data("mldsa_44_sign_seed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa44); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa44(); + } + + println!("mldsa_44_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_44_verify_test() { + let contents = match get_test_data("mldsa_44_verify_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa44); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa44(); + } + + println!("mldsa_44_verify_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_65_sign_noseed_test() { + let contents = match get_test_data("mldsa_65_sign_noseed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSASignNoSeedTestCase::parse(contents, ParameterSet::Mldsa65); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa65(); + } + + println!("mldsa_65_sign_noseed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_65_sign_seed_test() { + let contents = match get_test_data("mldsa_65_sign_seed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa65); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa65(); + } + + println!("mldsa_65_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_65_verify_test() { + let contents = match get_test_data("mldsa_65_verify_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa65); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa65(); + } + + println!("mldsa_65_verify_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_87_sign_noseed_test() { + let contents = match get_test_data("mldsa_87_sign_noseed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + + let test_cases = MLDSASignNoSeedTestCase::parse(contents, ParameterSet::Mldsa87); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa87(); + } + + println!("mldsa_87_sign_noseed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_87_sign_seed_test() { + let contents = match get_test_data("mldsa_87_sign_seed_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSASignSeedTestCase::parse(contents, ParameterSet::Mldsa87); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa87(); + } + + println!("mldsa_87_sign_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mldsa_87_verify_test() { + let contents = match get_test_data("mldsa_87_verify_test.json") { + Ok(contents) => contents, + Err(_) => return, + }; + let test_cases = MLDSAVerifyTestCase::parse(contents, ParameterSet::Mldsa87); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mldsa87(); + } + + println!("mldsa_87_verify_test: all {} test cases passed.", num_test_cases); + } +} + +/* Structs for holding test data */ + +#[derive(Clone, Debug, PartialEq)] +enum ParameterSet { + Mldsa44, + Mldsa65, + Mldsa87, +} + +#[derive(Clone)] +struct MLDSASignNoSeedTestCase { + parameter_set: ParameterSet, + // testGroup-level fields, copied onto every test case in the group + private_key: String, + public_key: String, + // test-level fields + tc_id: u32, + comment: String, + msg: Option, + mu: String, + ctx: Option, + sig: String, + result: String, +} + +impl MLDSASignNoSeedTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + private_key: String::new(), + public_key: String::new(), + tc_id: 0, + comment: String::new(), + msg: None, + mu: String::new(), + ctx: None, + sig: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + // The private/public key are defined once per group and shared by + // every test in that group. + let private_key = group["privateKey"].as_str().unwrap_or("").to_string(); + let public_key = group["publicKey"].as_str().unwrap_or("").to_string(); + + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + private_key: private_key.clone(), + public_key: public_key.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + msg: match test["msg"].as_str() { + Some(msg) => Some(msg.to_string()), + None => None, + }, + mu: test["mu"].as_str().unwrap_or("").to_string(), + ctx: test["ctx"].as_str().map(|s| s.to_string()), + sig: test["sig"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mldsa44(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa44); + + /* Load the keys */ + + let sk = match MLDSA44PrivateKey::from_bytes(&hex::decode(&self.private_key).unwrap()) { + Ok(sk) => sk, + Err(SignatureError::DecodingError(_)) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to decode private key: {}", self.comment); + } + } + _ => { + panic!("something else went wrong"); + } + }; + + let pk = MLDSA44PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA44::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap() + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa65(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa65); + + /* Load the keys */ + + let sk = match MLDSA65PrivateKey::from_bytes(&hex::decode(&self.private_key).unwrap()) { + Ok(sk) => sk, + Err(SignatureError::DecodingError(_)) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to decode private key: {}", self.comment); + } + } + _ => { + panic!("something else went wrong"); + } + }; + + let pk = MLDSA65PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA65::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap() + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa87(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa87); + + /* Load the keys */ + + let sk = match MLDSA87PrivateKey::from_bytes(&hex::decode(&self.private_key).unwrap()) { + Ok(sk) => sk, + Err(SignatureError::DecodingError(_)) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to decode private key: {}", self.comment); + } + } + _ => { + panic!("something else went wrong"); + } + }; + + let pk = MLDSA87PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA87::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } +} + +#[derive(Clone)] +struct MLDSASignSeedTestCase { + parameter_set: ParameterSet, + // testGroup-level fields, copied onto every test case in the group + private_seed: String, + public_key: String, + // test-level fields + tc_id: u32, + comment: String, + msg: Option, + mu: String, + ctx: Option, + sig: String, + result: String, +} + +impl MLDSASignSeedTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + private_seed: String::new(), + public_key: String::new(), + tc_id: 0, + comment: String::new(), + msg: None, + mu: String::new(), + ctx: None, + sig: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + // The private/public key are defined once per group and shared by + // every test in that group. + let private_seed = group["privateSeed"].as_str().unwrap_or("").to_string(); + let public_key = group["publicKey"].as_str().unwrap_or("").to_string(); + + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + private_seed: private_seed.clone(), + public_key: public_key.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + msg: match test["msg"].as_str() { + Some(msg) => Some(msg.to_string()), + None => None, + }, + mu: test["mu"].as_str().unwrap_or("").to_string(), + ctx: test["ctx"].as_str().map(|s| s.to_string()), + sig: test["sig"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mldsa44(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa44); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (pk, sk) = match MLDSA44::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + panic!("{:?}", e) + } + }; + + let loaded_pk = + MLDSA44PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA44::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa65(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa65); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (pk, sk) = match MLDSA65::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + panic!("{:?}", e) + } + }; + + let loaded_pk = + MLDSA65PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA65::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } + + fn run_mldsa87(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa87); + + /* Load the keys */ + + let mut seed = match KeyMaterial256::from_bytes_as_type( + &hex::decode(&self.private_seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (pk, sk) = match MLDSA87::keygen_from_seed(&seed) { + Ok((pk, sk)) => (pk, sk), + Err(e) => { + panic!("{:?}", e) + } + }; + + let loaded_pk = + MLDSA87PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()).unwrap(); + assert_eq!(loaded_pk, pk); + + /* Compute the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + // build mu + let mu: [u8; 64] = if self.msg.is_none() { + // we can't compute it, so just take the one provided + hex::decode(&self.mu).unwrap().as_slice().try_into().unwrap() + } else { + match MuBuilder::compute_mu( + &pk.compute_tr(), + &hex::decode(self.msg.clone().unwrap()).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + ) { + Ok(mu) => mu, + Err(SignatureError::LengthError(_)) => { + if self.result == "invalid" { + /* good -- test passed */ + return; + } else { + panic!("failed to compute mu") + } + } + _ => panic!("failed to compute mu"), + } + }; + assert_eq!(mu, hex::decode(&self.mu).unwrap().as_slice()); + + // generate the signature using an all-zero signing nonce + let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); + + let res = MLDSA87::verify_mu( + &pk, + Some(&pk.A_hat()), + &mu, + &hex::decode(&self.sig).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + res.unwrap(); + } else { + assert!(res.is_err()); + }; + } +} + +#[derive(Clone)] +struct MLDSAVerifyTestCase { + parameter_set: ParameterSet, + // testGroup-level fields, copied onto every test case in the group + public_key: String, + // test-level fields + tc_id: u32, + comment: String, + msg: String, + ctx: Option, + sig: String, + result: String, +} + +impl MLDSAVerifyTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + public_key: String::new(), + tc_id: 0, + comment: String::new(), + msg: String::new(), + ctx: None, + sig: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + // The private/public key are defined once per group and shared by + // every test in that group. + let public_key = group["publicKey"].as_str().unwrap_or("").to_string(); + + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + public_key: public_key.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + msg: test["msg"].as_str().unwrap_or("").to_string(), + ctx: test["ctx"].as_str().map(|s| s.to_string()), + sig: test["sig"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mldsa44(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa44); + + /* Load the key */ + + let pk = match MLDSA44PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA44::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed:"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } + + fn run_mldsa65(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa65); + + /* Load the key */ + + let pk = match MLDSA65PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA65::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed:"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } + + fn run_mldsa87(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mldsa87); + + /* Load the key */ + + let pk = match MLDSA87PublicKey::from_bytes(&hex::decode(&self.public_key).unwrap()) { + Ok(pk) => pk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + }; + + /* Verify the signature */ + + let ctx_vec = self.ctx.as_ref().map(|ctx| hex::decode(ctx).unwrap()); + + match MLDSA87::verify( + &pk, + &hex::decode(&self.msg).unwrap(), + ctx_vec.as_ref().and_then(|ctx| Some(ctx.as_slice())), + &hex::decode(&self.sig).unwrap(), + ) { + Ok(()) => { + if self.result != "valid" { + panic!("signature passed when it should have failed"); + } + } + Err(e) => { + if self.result != "invalid" { + panic!("signature failed when it should have passed: {:?}", e); + } + } + } + } +} diff --git a/crypto/mldsa_lowmemory/tests/bc_test_data.rs b/crypto/mldsa_lowmemory/tests/bc_test_data.rs deleted file mode 100644 index d9be448..0000000 --- a/crypto/mldsa_lowmemory/tests/bc_test_data.rs +++ /dev/null @@ -1,696 +0,0 @@ -// Test against the bc-test-data repo -// Requires that the bc-test-data repository is cloned and available for testing at "../bc-test-data" -// relative to the root of this git project. - -use bouncycastle_core::errors::SignatureError; -use bouncycastle_core::traits::XOF; -use bouncycastle_sha3::SHAKE256; - -#[allow(unused_imports)] -#[allow(dead_code)] - -#[cfg(test)] -// mod bc_test_data { -// use std::{fs}; -// use bouncycastle_core::errors::SignatureError; -// use bouncycastle_hex as hex; -// use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial256, KeyType}; -// use bouncycastle_core::traits::{Hash, PHSignature, SecurityStrength, Signature, SignaturePrivateKey, SignaturePublicKey}; -// use bouncycastle_mldsa_lowmemory::{HashMLDSA44_with_SHA512, HashMLDSA65_with_SHA512, HashMLDSA87_with_SHA512, MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSATrait, MLDSA44, MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA65, MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA87, MLDSA87_PK_LEN, MLDSA87_SK_LEN}; -// use bouncycastle_sha2::SHA512; -// use crate::BustedMuBuilder; -// -// const TEST_DATA_PATH: &str = "../../../bc-test-data/pqc/crypto/mldsa"; -// -// #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_keyGen() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-keyGen.txt").unwrap(); -// let test_cases = KeyGenTestCase::parse(contents); -// -// for test_case in test_cases { -// test_case.run(); -// } -// } -// -// #[derive(Clone)] -// struct KeyGenTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// tc_id: u32, -// seed: String, -// pk: String, -// sk: String, -// } -// -// impl KeyGenTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, seed: String::new(), pk: String::new(), sk: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = KeyGenTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "seed" => test_case.seed = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "sk" => test_case.sk = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "keyGen"); -// -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA44_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA44_SK_LEN] = hex::decode(&self.seed).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// "ML-DSA-65" => { -// let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA65_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA65_SK_LEN] = hex::decode(&self.seed).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// "ML-DSA-87" => { -// let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA87_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA87_SK_LEN] = hex::decode(&self.seed).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // commented out because the bc test vectors are missing private key seeds, so I can't use them. -// // #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_sigGen() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-sigGen.txt").unwrap(); -// let test_cases = SigGenTestCase::parse(contents); -// -// let num_tests = test_cases.len(); -// for test_case in test_cases { -// test_case.run(); -// } -// -// println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); -// } -// -// #[derive(Clone)] -// struct SigGenTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// deterministic: bool, -// tc_id: u32, -// sk: String, -// message: String, -// rnd: String, -// signature: String, -// } -// -// impl SigGenTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), deterministic: false, tc_id: 0, sk: String::new(), message: String::new(), rnd: String::new(), signature: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = SigGenTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full(){ test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "deterministic" => test_case.deterministic = value.parse().unwrap(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "sk" => test_case.sk = value.to_string(), -// "message" => test_case.message = value.to_string(), -// "rnd" => test_case.rnd = value.to_string(), -// "signature" => test_case.signature = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "sigGen"); -// -// let rnd = if self.deterministic{ [0u8; 32] } else { hex::decode(&self.rnd).unwrap().as_slice().try_into().unwrap() }; -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let sk = MLDSA44PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA44::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap(), "ML-DSA-sigGen params: {}, vsId: {}, tgId: {}, tcId: {}", self.parameter_set, self.vs_id, self.tg_id, self.tc_id); -// }, -// "ML-DSA-65" => { -// let sk = MLDSA65PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA65::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); -// }, -// "ML-DSA-87" => { -// let sk = MLDSA87PrivateKey::from_bytes(&hex::decode(&self.sk).unwrap()).unwrap(); -// -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// // let mu = MLDSA87::compute_mu_from_tr( -// // &hex::decode(&self.message).unwrap(), -// // None, -// // sk.tr(), -// // ).unwrap(); -// let mut mb = BustedMuBuilder::do_init(&sk.tr()).unwrap(); -// mb.do_update(&hex::decode(&self.message).unwrap()); -// let mu = mb.do_final(); -// -// let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, rnd).unwrap(); -// assert_eq!(&sig, &*hex::decode(&self.signature).unwrap()); -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // This is also against the non-compliant mu that doesn't have a ctx, which I don't have an easy way to test -// // #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_sigVer() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-DSA-sigVer.txt").unwrap(); -// let test_cases = SigVerTestCase::parse(contents); -// -// for test_case in test_cases { -// test_case.run(); -// } -// } -// -// #[derive(Clone)] -// struct SigVerTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// pk: String, -// tc_id: u32, -// message: String, -// signature: String, -// test_passed: bool, -// } -// -// impl SigVerTestCase { -// fn new() -> Self { -// Self{ vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, pk: String::new(), message: String::new(), signature: String::new(), test_passed: false} -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = SigVerTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "message" => test_case.message = value.to_string(), -// "signature" => test_case.signature = value.to_string(), -// "testPassed" => test_case.test_passed = value.parse().unwrap(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "sigVer"); -// -// match self.parameter_set.as_str() { -// "ML-DSA-44" => { -// let pk = MLDSA44PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA44::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if !self.test_passed { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if self.test_passed { panic!("Verification failed when it shouldn't have! vsId: {}, tgId: {}, tcId: {}", self.vs_id, self.tg_id, self.tc_id) } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// "ML-DSA-65" => { -// let pk = MLDSA65PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA65::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if self.test_passed { /* good */ } else { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if !self.test_passed {} else { panic!("Verification failed when it should have!") } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// "ML-DSA-87" => { -// let pk = MLDSA87PublicKey::from_bytes(&hex::decode(&self.pk).unwrap()).unwrap(); -// -// match MLDSA87::verify(&pk, &hex::decode(&self.message).unwrap(), None, &hex::decode(&self.signature).unwrap()) { -// Ok(()) => if self.test_passed { /* good */ } else { panic!("Verification succeeded when it shouldn't have!") }, -// Err(SignatureError::SignatureVerificationFailed) => { if !self.test_passed {} else { panic!("Verification failed when it should have!") } }, -// _ => panic!("An unexpected error occurred") -// } -// }, -// val => panic!("Invalid parameter set: {}", val), -// -// } -// } -// } -// -// // not working, not totally sure why -// // #[test] -// #[allow(non_snake_case)] -// fn ML_DSA_rsp() { -// // MLDsa44 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa44.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa44"); -// } -// -// // MLDsa65 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa65.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa65"); -// } -// -// -// // MLDsa87 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa87.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa87"); -// } -// -// -// // MLDsa44 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa44sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MLDsa44"); -// } -// -// // MLDsa65 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa65sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MlDsa65"); -// } -// -// -// // MLDsa87 -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/mldsa87sha512.rsp").unwrap(); -// let test_cases = MldsaRspTestCase::::parse(contents); -// for test_case in test_cases { -// test_case.run("MlDsa87"); -// } -// } -// -// #[derive(Clone)] -// struct MldsaRspTestCase { -// count: u32, -// seed: String, -// mlen: u32, -// msg: String, -// pk: String, -// sk: String, -// smlen: u32, -// sm: String, -// message_hash: String, -// message_prime: String, -// context: String, -// } -// -// impl MldsaRspTestCase { -// fn new() -> Self { -// Self{ count: 0, seed: String::new(), mlen: 0, msg: String::new(), pk: String::new(), sk: String::new(), smlen: 0, sm: String::new(), message_hash: String::new(), message_prime: String::new(), context: String::new()} -// } -// -// fn is_full(&self) -> bool { -// !self.seed.is_empty() -// } -// -// fn parse(data: String) -> Vec> { -// let mut test_cases = Vec::<>::new(); -// let mut test_case = MldsaRspTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "count" => test_case.count = value.parse().unwrap(), -// "seed" => test_case.seed = value.to_string(), -// "mlen" => test_case.mlen = value.parse().unwrap(), -// "msg" => test_case.msg = value.to_string(), -// "pk" => test_case.pk = value.to_string(), -// "sk" => test_case.sk = value.to_string(), -// "smlen" => test_case.smlen = value.parse().unwrap(), -// "sm" => test_case.sm = value.to_string(), -// "message_hash" => test_case.message_hash = value.to_string(), -// "message_prime" => test_case.message_prime = value.to_string(), -// "context" => { -// test_case.context = value.to_string(); -// if test_case.context == "zero_length" || test_case.context == "none" { -// test_case.context = String::new(); -// } -// }, -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self, parameter_set: &str) { -// match parameter_set { -// "MLDsa44" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// -// let (pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA44_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA44_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA44_with_SHA512::sign_ph_deterministic(&sk, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA44_with_SHA512::verify(&pk, -// &*hex::decode(&self.msg).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).expect(&format!("paramSet: {}, is_hash: {}, count: {}", parameter_set, IS_HASH_MLDSA, self.count)); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// &sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap(), "paramSet: {}, count: {}", parameter_set, self.count); -// -// MLDSA44::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// "MlDsa65" | "MLDsa65" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// let (pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA65_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA65_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA65_with_SHA512::sign_ph_deterministic(&sk, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA65_with_SHA512::verify(&pk, -// &*hex::decode(&self.message_hash).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).expect(&format!("paramSet: {}, isHash: {}, count: {}", parameter_set, IS_HASH_MLDSA, self.count)); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// &sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// MLDSA65::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// "MLDsa87" => { -// let mut seed = KeyMaterial256::from_bytes_as_type( -// &hex::decode(&self.seed).unwrap(), -// KeyType::Seed, -// ).unwrap(); -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// let (pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLDSA87_PK_LEN] = hex::decode(&self.pk).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLDSA87_SK_LEN] = hex::decode(&self.sk).unwrap().try_into().unwrap(); -// assert_eq!(sk.encode(), sk_sized); -// -// -// if IS_HASH_MLDSA { -// // we're only testing SHA512 -// let ph: [u8; 64] = SHA512::new().hash(&hex::decode(&self.msg).unwrap()).as_slice().try_into().unwrap(); -// assert_eq!(ph, &*hex::decode(&self.message_hash).unwrap()); -// -// let sig = HashMLDSA87_with_SHA512::sign_ph_deterministic(&sk, Some(&*hex::decode(&self.context).unwrap()), &ph, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// HashMLDSA87_with_SHA512::verify(&pk, -// &*hex::decode(&self.message_hash).unwrap(), -// Some(&*hex::decode(&self.context).unwrap()), -// &sig).unwrap(); -// } else { -// // note: we're exposing a sign_mu_deterministic(), but not sign_deterministic() -// // so need to manually compute mu -// let mu = MLDSA65::compute_mu_from_tr( -// &sk.tr(), -// &hex::decode(&self.msg).unwrap(), -// Some(&hex::decode(&self.context).unwrap()), -// ).unwrap(); -// -// let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); -// assert_eq!(sig, &*hex::decode(&self.sm).unwrap()); -// -// MLDSA87::verify(&pk, &hex::decode(&self.msg).unwrap(), Some(&hex::decode(&self.context).unwrap()), &sig).unwrap(); -// } -// }, -// val => panic!("Invalid parameter set: {}", val), -// } -// } -// } -// } - - - -/// This builds a "busted" mu where the ctx is absent (not 0-length, but actually not there) -/// just for the sake of compatibility with the bc-test-data tests -pub struct BustedMuBuilder { - h: SHAKE256, -} - -impl BustedMuBuilder { - /// Algorithm 7 - /// 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀′, 64) - pub fn compute_mu(msg: &[u8], tr: &[u8; 64]) -> Result<[u8; 64], SignatureError> { - let mut mu_builder = Self::do_init(&tr)?; - mu_builder.do_update(msg); - let mu = mu_builder.do_final(); - - Ok(mu) - } - - /// This function requires the public key hash `tr`, which can be computed from the public key using [MLDSAPublicKey::compute_tr]. - pub fn do_init(tr: &[u8; 64], /*ctx: Option<&[u8]>*/) -> Result { - // let ctx = match ctx { - // Some(ctx) => ctx, - // None => &[] - // }; - - // Algorithm 2 - // 1: if |𝑐𝑡𝑥| > 255 then - // if ctx.len() > 255 { - // return Err(SignatureError::LengthError("ctx value is longer than 255 bytes")); - // } - - // Algorithm 7 - // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀', 64) - let mut mb = Self { h: SHAKE256::new() }; - mb.h.absorb(tr); - - // Algorithm 2 - // 10: 𝑀′ ← BytesToBits(IntegerToBytes(0, 1) ∥ IntegerToBytes(|𝑐𝑡𝑥|, 1) ∥ 𝑐𝑡𝑥) ∥ 𝑀 - // all done together - // mb.h.absorb(&[0u8]); // these are the busted lines -- bc-java just doesn't do these in the test code - // mb.h.absorb(&[ctx.len() as u8]); - // mb.h.absorb(ctx); - - // now ready to absorb M - Ok(mb) - } - - /// Stream a chunk of the message. - pub fn do_update(&mut self, msg_chunk: &[u8]) { - self.h.absorb(msg_chunk); - } - - /// Finalize and return the mu value. - pub fn do_final(mut self) -> [u8; 64] { - // Completion of - // Algorithm 7 - // 6: 𝜇 ← H(BytesToBits(𝑡𝑟)||𝑀 ′, 64) - let mut mu = [0u8; 64]; - self.h.squeeze_out(&mut mu); - - mu - } -} \ No newline at end of file diff --git a/crypto/mldsa_lowmemory/tests/mldsa_key_tests.rs b/crypto/mldsa_lowmemory/tests/mldsa_key_tests.rs deleted file mode 100644 index 4117319..0000000 --- a/crypto/mldsa_lowmemory/tests/mldsa_key_tests.rs +++ /dev/null @@ -1,181 +0,0 @@ -#[cfg(test)] -mod mldsa_key_tests { - #![allow(dead_code)] - #![allow(unused_imports)] - - use bouncycastle_core::key_material::{KeyMaterial256, KeyType}; - use bouncycastle_core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey}; - use bouncycastle_core_test_framework::signature::{TestFrameworkSignatureKeys}; - use bouncycastle_mldsa_lowmemory::{MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, MLDSAPrivateKeyTrait, MLDSAPublicKeyTrait, MLDSATrait, MLDSA44, MLDSA65, MLDSA87}; - use bouncycastle_mldsa_lowmemory::{MLDSA44_PK_LEN, MLDSA44_SK_LEN, MLDSA44_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA65_PK_LEN, MLDSA65_SK_LEN, MLDSA65_SIG_LEN}; - use bouncycastle_mldsa_lowmemory::{MLDSA87_PK_LEN, MLDSA87_SK_LEN, MLDSA87_SIG_LEN}; - use bouncycastle_hex as hex; - - - #[test] - fn core_framework_tests() { - let tf = TestFrameworkSignatureKeys::new(); - - tf.test_keys::(); - tf.test_keys::(); - tf.test_keys::(); - } - - #[test] - fn encode_decode() { - let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), - KeyType::Seed, - ).unwrap(); - - let (pk1, sk1) = MLDSA44::keygen_from_seed(&seed).unwrap(); - let pk1_bytes = pk1.encode(); - let sk1_bytes = sk1.encode(); - - let (pk2, sk2) = MLDSA44::keygen_from_seed(&seed).unwrap(); - let pk2_bytes = pk2.encode(); - assert_eq!(pk1_bytes, pk2_bytes); - - let sk2_bytes = sk2.encode(); - assert_eq!(sk1_bytes, sk2_bytes); - } - - #[test] - fn seed() { - let seed = KeyMaterial256::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), - KeyType::Seed, - ).unwrap(); - - let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - - assert_eq!(sk.seed(), &seed); - } - - #[test] - fn test_eq() { - - // MLDSA-44 - - let (pk, sk) = MLDSA44::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA44PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA44PrivateKey::from_bytes(&sk.encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA44PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA44PrivateKey::from_bytes(&bytes).unwrap()); - - - // MLDSA-65 - - let (pk, sk) = MLDSA65::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA65PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA65PrivateKey::from_bytes(&sk.encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA65PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA65PrivateKey::from_bytes(&bytes).unwrap()); - - - // MLDSA-87 - - let (pk, sk) = MLDSA87::keygen().unwrap(); - - // basic equality checks - assert_eq!(pk, pk); - assert_eq!(pk, pk.clone()); - assert_eq!(pk, MLDSA87PublicKey::from_bytes(&pk.encode()).unwrap()); - - assert_eq!(sk, sk); - assert_eq!(sk, sk.clone()); - assert_eq!(sk, MLDSA87PrivateKey::from_bytes(&sk.encode()).unwrap()); - - // inequality checks - let mut bytes = pk.encode(); - bytes[17] ^= 0x01; - assert_ne!(pk, MLDSA87PublicKey::from_bytes(&bytes).unwrap()); - - let mut bytes = sk.encode(); - bytes[17] ^= 0x01; - assert_ne!(sk, MLDSA87PrivateKey::from_bytes(&bytes).unwrap()); - } - - /// Tests that no private data is displayed - #[test] - fn test_display() { - let (pk44, sk44) = MLDSA44::keygen().unwrap(); - let (pk65, sk65) = MLDSA65::keygen().unwrap(); - let (pk87, sk87) = MLDSA87::keygen().unwrap(); - - - /*** MLDSAPublicKey ***/ - // fmt - - let pk_str = format!("{}", pk44); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-44, pub_key_hash (tr):")); - - let pk_str = format!("{}", pk65); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-65, pub_key_hash (tr):")); - - let pk_str = format!("{}", pk87); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-87, pub_key_hash (tr):")); - - // debug - let pk_str = format!("{:?}", pk44); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-44, pub_key_hash (tr):")); - - let pk_str = format!("{:?}", pk65); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-65, pub_key_hash (tr):")); - - let pk_str = format!("{:?}", pk87); - assert!(pk_str.contains("MLDSAPublicKey { alg: ML-DSA-87, pub_key_hash (tr):")); - - - - /*** MLDSAPrivateKey ***/ - // fmt - let sk_str = format!("{}", sk44); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-44, pub_key_hash (tr):")); - - let sk_str = format!("{}", sk65); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-65, pub_key_hash (tr):")); - - let sk_str = format!("{}", sk87); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-87, pub_key_hash (tr):")); - - // debug - let sk_str = format!("{:?}", sk44); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-44, pub_key_hash (tr):")); - - let sk_str = format!("{:?}", sk65); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-65, pub_key_hash (tr):")); - - let sk_str = format!("{:?}", sk87); - assert!(sk_str.contains("MLDSASeedPrivateKey { alg: ML-DSA-87, pub_key_hash (tr):")); - } -} \ No newline at end of file diff --git a/crypto/mlkem_lowmemory/Cargo.toml b/crypto/mlkem-lowmemory/Cargo.toml similarity index 96% rename from crypto/mlkem_lowmemory/Cargo.toml rename to crypto/mlkem-lowmemory/Cargo.toml index 73079c8..8ccb067 100644 --- a/crypto/mlkem_lowmemory/Cargo.toml +++ b/crypto/mlkem-lowmemory/Cargo.toml @@ -14,6 +14,7 @@ bouncycastle-core-test-framework.workspace = true bouncycastle-hex.workspace = true bouncycastle-rng.workspace = true criterion.workspace = true +serde_json = "1.0" [[bench]] name = "mlkem_benches" diff --git a/crypto/mlkem_lowmemory/benches/mlkem_benches.rs b/crypto/mlkem-lowmemory/benches/mlkem_benches.rs similarity index 89% rename from crypto/mlkem_lowmemory/benches/mlkem_benches.rs rename to crypto/mlkem-lowmemory/benches/mlkem_benches.rs index a708f3e..8ea83ba 100644 --- a/crypto/mlkem_lowmemory/benches/mlkem_benches.rs +++ b/crypto/mlkem-lowmemory/benches/mlkem_benches.rs @@ -1,9 +1,12 @@ -use criterion::{Criterion, criterion_group, criterion_main}; use bouncycastle_core::key_material::{KeyMaterial512, KeyType}; -use std::hint::black_box; -use bouncycastle_core::traits::KEM; +use bouncycastle_core::traits::KEMDecapsulator; use bouncycastle_hex as hex; -use bouncycastle_mlkem_lowmemory::{MLKEMTrait, MLKEM1024, MLKEM1024_CT_LEN, MLKEM512, MLKEM512_CT_LEN, MLKEM768, MLKEM768_CT_LEN, MLKEM_RND_LEN}; +use bouncycastle_mlkem_lowmemory::{ + MLKEM_RND_LEN, MLKEM512, MLKEM512_CT_LEN, MLKEM768, MLKEM768_CT_LEN, MLKEM1024, + MLKEM1024_CT_LEN, MLKEMTrait, +}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_mlkem_keygen(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen"); @@ -18,11 +21,11 @@ fn bench_mlkem_keygen(c: &mut Criterion) { group.throughput(criterion::Throughput::Elements(seeds.len() as u64)); group.bench_function("ML-KEM-512_lowmemory", |b| { - b.iter(|| { - for seed in seeds.iter() { - black_box(MLKEM512::keygen_from_seed(seed)).unwrap(); - } - }) + b.iter(|| { + for seed in seeds.iter() { + black_box(MLKEM512::keygen_from_seed(seed)).unwrap(); + } + }) }); group.bench_function("ML-KEM-768_lowmemory", |b| { @@ -50,12 +53,16 @@ fn bench_mlkem_encaps(c: &mut Criterion) { // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); // create a vector of signing nonces so that we're not measuring the time of the RNG const NUM_ELEMS: usize = 256; @@ -77,7 +84,6 @@ fn bench_mlkem_encaps(c: &mut Criterion) { }) }); - /*** ML-KEM-768 ***/ let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); @@ -113,12 +119,16 @@ fn bench_mlkem_decaps(c: &mut Criterion) { // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); const NUM_ELEMS: usize = 256; @@ -126,7 +136,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -138,7 +148,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-512_lowmemory", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM512::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM512::decaps(&sk, &cts[i])); } }) }); @@ -147,7 +157,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -159,7 +169,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-768_lowmemory", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM768::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM768::decaps(&sk, &cts[i])); } }) }); @@ -168,7 +178,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -180,7 +190,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-1024_lowmemory", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM1024::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM1024::decaps(&sk, &cts[i])); } }) }); diff --git a/crypto/mlkem_lowmemory/src/aux_functions.rs b/crypto/mlkem-lowmemory/src/aux_functions.rs similarity index 89% rename from crypto/mlkem_lowmemory/src/aux_functions.rs rename to crypto/mlkem-lowmemory/src/aux_functions.rs index a8784a6..2246b74 100644 --- a/crypto/mlkem_lowmemory/src/aux_functions.rs +++ b/crypto/mlkem-lowmemory/src/aux_functions.rs @@ -1,7 +1,7 @@ //! Implements auxiliary functions for ML-DSA as defined in Section 7 of FIPS 204. use crate::mlkem::{N, q, q_inv}; -use crate::polynomial::{Polynomial}; +use crate::polynomial::Polynomial; use bouncycastle_core::traits::XOF; use bouncycastle_sha3::{SHAKE128, SHAKE256}; @@ -9,27 +9,31 @@ use bouncycastle_sha3::{SHAKE128, SHAKE256}; /// Encodes an array of 𝑑-bit integers into a byte array for 1 ≤ 𝑑 ≤ 12. /// Input: integer array 𝐹 ∈ ℤ_M^256 , where 𝑚 = 2^𝑑 if 𝑑 < 12, and 𝑚 = 𝑞 if 𝑑 = 12. /// Output: byte array 𝐵 ∈ 𝔹32𝑑 . -pub(crate) fn byte_encode(F: &Polynomial) -> [u8; PACK_LEN] { - debug_assert_eq!(PACK_LEN, 32 * d); +pub(crate) fn byte_encode( + F: &Polynomial, + B: &mut [u8; PACK_LEN], +) { + B.fill(0); - let mut B = [0u8; PACK_LEN]; + debug_assert_eq!(PACK_LEN, 32 * d); - for i in 0 .. N { + for i in 0..N { let mut alpha = F[i]; // For efficiency, the library is happy to work with values outside the range [0..q], // but we need to reduce it for the canonical encoding. alpha = barrett_reduce(alpha); + alpha = cond_sub_q(alpha); for j in 0..d { // alpha % 2, but without using % for constant-time reasons + // although "& 1" may lead to other, more subtle timing issues. Research topic. let tmp = (alpha & 1) as u8; // 4: 𝑏[𝑖⋅𝑑 + 𝑗] ← 𝑎 mod 2 // constant-time note: yes, % is not constant-time, // but all of the values in (i*d + j) % 8 are loop indices and not part of the secret key. - B[(i*d + j)/8] |= tmp << ((i*d + j) % 8); - + B[(i * d + j) / 8] |= tmp << ((i * d + j) % 8); // 5: 𝑎 ← (𝑎 − 𝑏[𝑖⋅𝑑 + 𝑗])/2 // ▷ note 𝑎 − 𝑏[𝑖⋅𝑑 + 𝑗] is always even @@ -42,8 +46,6 @@ pub(crate) fn byte_encode(F: &Polynomial) alpha >>= 1; } } - - B } /// Algorithm 6 ByteDecode_d(𝐵) @@ -59,11 +61,12 @@ pub(crate) fn byte_decode(B: &[u8; PACK_L // 3: F[i] = SUM_j=0..d-1{ 𝑏[𝑖 ⋅ 𝑑 + 𝑗] ⋅ 2𝑗 } mod m for j in 0..d { // select the next bit, according to bitcount, then shift it up by j - F[i] |= (((B[(i*d + j)/8] >> (i*d + j)%8) & 1) as i16) << j; // there's supposed to be a `mod m` here, but that shouldn't matter; we'll check it below anyway. + F[i] |= (((B[(i * d + j) / 8] >> (i * d + j) % 8) & 1) as i16) << j; // there's supposed to be a `mod m` here, but that shouldn't matter; we'll check it below anyway. } // assert the mod m + // We'll relax these because it's being checked above in MLKEMPublicKey::pk_decode() debug_assert!(F[i] >= 0); - debug_assert!(F[i] <= if d<12 {2< Polynomial { // SHAKE is fairly inefficient if you just squeeze 3 bytes at a time, so we'll do a block. // size doesn't really matter, so long as it's a multiple of 3. // 288 seemed to be the sweet spot from playing with benchmarks - // It's probably around the average rejection rate, and 288 is a multiple of both 3 (required for this alg) + // It's probably around the average rejection rate, and 216 is a multiple of both 3 (required for this alg) // and 8 (efficient for SHAKE). - let mut C = [0u8; 288]; + let mut C = [0u8; 216]; xof.squeeze_out(&mut C); let mut idx: usize = 0; @@ -105,12 +108,12 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // 6: 𝑑1 ← 𝐶[0] + 256 ⋅ (𝐶[1] mod 16) // ▷ 0 ≤ 𝑑1 < 2^12 - let d1: i16 = (C[idx+0] as i16) | ((C[idx+1] as i32) << 8) as i16 & 0xFFF; + let d1: i16 = (C[idx] as i16) | ((C[idx + 1] as i32) << 8) as i16 & 0xFFF; debug_assert!(d1 < 2 << 12); // 7: 𝑑2 ← ⌊𝐶[1]/16⌋ + 16 ⋅ 𝐶[2] // ▷ 0 ≤ 𝑑2 < 2^12 - let d2: i16 = ((C[idx+1] as i16) >> 4) | ((C[idx+2] as i32) << 4) as i16 & 0xFFF; + let d2: i16 = ((C[idx + 1] as i16) >> 4) | ((C[idx + 2] as i32) << 4) as i16 & 0xFFF; debug_assert!(d2 < 2 << 12); // 8: if 𝑑1 < 𝑞 then @@ -149,8 +152,8 @@ pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { match eta { 2 => { - for i in 0..N/8 { - let t = u32::from_le_bytes(bytes[4*i .. 4*i + 4].try_into().unwrap()); + for i in 0..N / 8 { + let t = u32::from_le_bytes(bytes[4 * i..4 * i + 4].try_into().unwrap()); let mut d = t & 0x55555555; d += (t >> 1) & 0x55555555; for j in 0..8usize { @@ -165,7 +168,7 @@ pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { } } 3 => { - for i in 0..N/4 { + for i in 0..N / 4 { let t = little_endian_to_u24(bytes, 3 * i); let mut d = t & 0x00249249; d += (t >> 1) & 0x00249249; @@ -190,7 +193,6 @@ pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { /// SamplePolyCBD𝜂1(PRF𝜂1 (𝜎, 𝑁 )) /// Performs both the PRF and SamplePolyCBD steps pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial { - // Alg 13: 9: 𝐬[𝑖] ← SamplePolyCBD𝜂1(PRF𝜂1 (𝜎, 𝑁 )) // ▷ 𝐬[𝑖] ∈ ℤ256 sampled from CBD match eta { @@ -206,7 +208,7 @@ pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial }; sample_poly_cbd::(&buf) - }, + } 3 => { let buf = { let mut xof = SHAKE256::new(); @@ -218,8 +220,8 @@ pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial }; sample_poly_cbd::(&buf) - }, - _ => unreachable!() + } + _ => unreachable!(), } } @@ -274,13 +276,20 @@ pub(super) fn cond_sub_q(a: i16) -> i16 { tmp + ((tmp >> 15) & q) } - /// Multiplication of polynomials in Zq\[X]/(X^2-zeta) /// used for multiplication of elements in Rq in NTT domain /// /// Borrowed from: /// https://github.com/pq-crystals/kyber/blob/main/ref/ntt.c#L139 -pub(crate) fn ntt_base_mult(r: &mut [i16], off: usize, a0: i16, a1: i16, b0: i16, b1: i16, zeta: i16) { +pub(crate) fn ntt_base_mult( + r: &mut [i16], + off: usize, + a0: i16, + a1: i16, + b0: i16, + b1: i16, + zeta: i16, +) { let mut out_val0 = mul_mont(a1, b1); out_val0 = mul_mont(out_val0, zeta); out_val0 += mul_mont(a0, b0); diff --git a/crypto/mlkem_lowmemory/src/lib.rs b/crypto/mlkem-lowmemory/src/lib.rs similarity index 95% rename from crypto/mlkem_lowmemory/src/lib.rs rename to crypto/mlkem-lowmemory/src/lib.rs index 32b3254..d371326 100644 --- a/crypto/mlkem_lowmemory/src/lib.rs +++ b/crypto/mlkem-lowmemory/src/lib.rs @@ -154,8 +154,7 @@ //! ## Generating Keys //! //! ```rust -//! use bouncycastle_mlkem_lowmemory::MLKEM768; -//! use bouncycastle_core::traits::KEM; +//! use bouncycastle_mlkem_lowmemory::{MLKEM768, MLKEMTrait}; //! //! let (pk, sk) = MLKEM768::keygen().unwrap(); //! ``` @@ -185,7 +184,7 @@ //! //! ```rust //! use bouncycastle_mlkem_lowmemory::{MLKEM768, MLKEMTrait}; -//! use bouncycastle_core::traits::KEM; +//! use bouncycastle_core::traits::{KEMEncapsulator, KEMDecapsulator}; //! use bouncycastle_core::errors::KEMError; //! //! let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -224,20 +223,16 @@ //! constant-time after compilation. #![no_std] - #![forbid(missing_docs)] - #![forbid(unsafe_code)] #![allow(incomplete_features)] // needed because currently generic_const_exprs is experimental #![feature(generic_const_exprs)] #![feature(adt_const_params)] - // These are because I'm matching variable names exactly against FIPS 204, for example both 'K' and 'k', // or 'A' and 'a' are used and have specific meanings. // But need to tell the rust linter to not care. #![allow(non_snake_case)] #![allow(non_upper_case_globals)] - // so I can use private traits to hide internal stuff that needs to be generic within the // MLKEM implementation, but I don't want accessed from outside, such as FIPS-internal functions. #![allow(private_bounds)] @@ -246,32 +241,30 @@ #[allow(unused_imports)] use bouncycastle_core::key_material::KeyMaterialTrait; -// todo -- re-run mutants - -// todo -- crucible tests - +mod aux_functions; +mod low_memory_helpers; pub mod mlkem; mod mlkem_keys; mod polynomial; -mod aux_functions; -mod low_memory_helpers; /*** Exported types ***/ -pub use mlkem::{MLKEMTrait, MLKEM, MLKEM512, MLKEM768, MLKEM1024}; +pub use mlkem::{MLKEM, MLKEM512, MLKEM768, MLKEM1024, MLKEMTrait}; +pub use mlkem_keys::{ + MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey, MLKEMSeedPrivateKey, +}; +pub use mlkem_keys::{MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey, MLKEMPublicKey}; pub use mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait}; -pub use mlkem_keys::{MLKEMPublicKey, MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey}; -pub use mlkem_keys::{MLKEMSeedPrivateKey, MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey}; /*** Exported constants ***/ pub use mlkem::ML_KEM_512_NAME; pub use mlkem::ML_KEM_768_NAME; pub use mlkem::ML_KEM_1024_NAME; -pub use mlkem::{MLKEM_SEED_LEN, MLKEM_SS_LEN, MLKEM_RND_LEN}; +pub use mlkem::{MLKEM_RND_LEN, MLKEM_SEED_LEN, MLKEM_SS_LEN}; -pub use mlkem::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_CT_LEN}; -pub use mlkem::{MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN}; -pub use mlkem::{MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN}; +pub use mlkem::{MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN}; +pub use mlkem::{MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN}; +pub use mlkem::{MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN}; // re-export just so it's visible to unit tests -pub use polynomial::Polynomial; \ No newline at end of file +pub use polynomial::Polynomial; diff --git a/crypto/mlkem_lowmemory/src/low_memory_helpers.rs b/crypto/mlkem-lowmemory/src/low_memory_helpers.rs similarity index 61% rename from crypto/mlkem_lowmemory/src/low_memory_helpers.rs rename to crypto/mlkem-lowmemory/src/low_memory_helpers.rs index a7ab96d..3ad71fa 100644 --- a/crypto/mlkem_lowmemory/src/low_memory_helpers.rs +++ b/crypto/mlkem-lowmemory/src/low_memory_helpers.rs @@ -3,8 +3,8 @@ //! what it needs in pieces, which generally means handling the matrices and vectors row-wise or entry-wise. use crate::aux_functions::{byte_decode, byte_encode, sample_ntt, sample_poly_CBD}; -use crate::polynomial::{Polynomial}; -use crate::mlkem::{q, N, POLY_BYTES}; +use crate::mlkem::{N, POLY_BYTES, q}; +use crate::polynomial::Polynomial; /// Computes the element [i,j] of the A_hat public matrix pub(crate) fn expandA_elem(rho: &[u8; 32], i: usize, j: usize) -> Polynomial { @@ -27,10 +27,10 @@ pub(crate) fn compute_A_hat_dot_s_hat( A_i0 }; - for j in 1 .. k { + for j in 1..k { let mut A_ij = expandA_elem(rho, row, j); let mut s_j = sample_poly_CBD::(sigma, j as u8); - s_j.ntt(); // now s_hat_j + s_j.ntt(); // now s_hat_j A_ij.base_mult_montgomery(&s_j); t_hat_i.add(&A_ij); } @@ -46,7 +46,7 @@ pub(crate) fn compute_A_hat_dot_s_hat( pub(crate) fn compute_A_hat_dot_y_hat( rho: &[u8; 32], r: &[u8; 32], - row: usize + row: usize, ) -> Polynomial { // 4: for (𝑖 ← 0; 𝑖 < 𝑘; 𝑖++) // ▷ re-generate matrix 𝐀 ∈ (ℤ256_𝑞 )𝑘×𝑘 sampled in Alg. 13 @@ -68,7 +68,7 @@ pub(crate) fn compute_A_hat_dot_y_hat( A_0i }; - for j in 1 .. k { + for j in 1..k { let mut A_ji = expandA_elem(&rho, j, row); let mut y_j = sample_poly_CBD::(r, /*N*/ j as u8); y_j.ntt(); @@ -85,7 +85,7 @@ pub(crate) fn compute_A_hat_dot_y_hat( pub(crate) fn compute_t_hat_dot_y_hat_row( r: &[u8; 32], t_hat_i: &Polynomial, - row: usize + row: usize, ) -> Polynomial { let mut y_i = sample_poly_CBD::(r, /*N*/ row as u8); y_i.ntt(); @@ -95,23 +95,37 @@ pub(crate) fn compute_t_hat_dot_y_hat_row( y_i } -pub(crate) fn pack_t_hat_row(t_hat_i: &Polynomial, row: usize, t_hat_packed: &mut [u8; T_PACKED_LEN]) { - t_hat_packed[row * POLY_BYTES .. (row + 1) * POLY_BYTES].copy_from_slice( - &byte_encode::<12, POLY_BYTES>(&t_hat_i) +pub(crate) fn pack_t_hat_row( + t_hat_i: &Polynomial, + row: usize, + t_hat_packed: &mut [u8; T_PACKED_LEN], +) { + byte_encode::<12, POLY_BYTES>( + &t_hat_i, + t_hat_packed[row * POLY_BYTES..(row + 1) * POLY_BYTES].as_mut().try_into().unwrap(), ); } -pub(crate) fn unpack_t_hat_row(t_hat_packed: &[u8; T_PACKED_LEN], row: usize) -> Polynomial { +pub(crate) fn unpack_t_hat_row( + t_hat_packed: &[u8; T_PACKED_LEN], + row: usize, +) -> Polynomial { byte_decode::<12, POLY_BYTES>( - t_hat_packed[row * POLY_BYTES .. (row + 1)*POLY_BYTES].try_into().unwrap()) + t_hat_packed[row * POLY_BYTES..(row + 1) * POLY_BYTES].try_into().unwrap(), + ) } -pub(crate) fn pack_s_hat_row(s_hat_i: &Polynomial, row: usize, s_hat_packed: &mut [u8]) { - debug_assert!(s_hat_packed.len() >= k*POLY_BYTES); - - s_hat_packed[row*POLY_BYTES .. (row+1)*POLY_BYTES].copy_from_slice(&byte_encode::<12, POLY_BYTES>( +pub(crate) fn pack_s_hat_row( + s_hat_i: &Polynomial, + row: usize, + s_hat_packed: &mut [u8], +) { + debug_assert!(s_hat_packed.len() >= k * POLY_BYTES); + + byte_encode::<12, POLY_BYTES>( s_hat_i, - )); + s_hat_packed[row * POLY_BYTES..(row + 1) * POLY_BYTES].as_mut().try_into().unwrap(), + ); } /// This is an optimized version of @@ -121,7 +135,7 @@ pub(crate) fn pack_s_hat_row(s_hat_i: &Polynomial, row: usize, s pub(crate) fn compress_u_row( u_i: Polynomial, row: usize, - ct: &mut [u8; CT_LEN] + ct: &mut [u8; CT_LEN], ) { // make sure we have received a dv assert!(du == 10 || du == 11); @@ -130,66 +144,61 @@ pub(crate) fn compress_u_row( // let mut u_i = u_i.clone(); // u_i.conditional_sub_q(); - // figure out where in the ct array we're going to write to // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them let start: usize = row * (N * (du as usize) / 8); let end: usize = (row + 1) * (N * (du as usize) / 8); - let out = &mut ct[start .. end]; + let out = &mut ct[start..end]; let mut idx = 0; match du { - 10 => { // MLKEM512 and MLKEM 768 + 10 => { + // MLKEM512 and MLKEM 768 let mut t = [0i16; 4]; - for j in 0..N/4 { - // fill the temp array t - for (l, item) in t.iter_mut().enumerate() { - *item = (((((u_i[4 * j + l] as u32) << 10) as i32 - + (q as i32 / 2)) - / q as i32) - & 0x3FF) as i16; - } - - out[idx] = t[0] as u8; - out[idx + 1] = ((t[0] >> 8) | (t[1] << 2)) as u8; - out[idx + 2] = ((t[1] >> 6) | (t[2] << 4)) as u8; - out[idx + 3] = ((t[2] >> 4) | (t[3] << 6)) as u8; - out[idx + 4] = (t[3] >> 2) as u8; - idx += 5; + for j in 0..N / 4 { + // fill the temp array t + for (l, item) in t.iter_mut().enumerate() { + *item = (((((u_i[4 * j + l] as u32) << 10) as i32 + (q as i32 / 2)) / q as i32) + & 0x3FF) as i16; } - }, + + out[idx] = t[0] as u8; + out[idx + 1] = ((t[0] >> 8) | (t[1] << 2)) as u8; + out[idx + 2] = ((t[1] >> 6) | (t[2] << 4)) as u8; + out[idx + 3] = ((t[2] >> 4) | (t[3] << 6)) as u8; + out[idx + 4] = (t[3] >> 2) as u8; + idx += 5; + } + } 11 => { let mut t = [0i16; 8]; - for j in 0..N/8 { - for (l, item) in t.iter_mut().enumerate() { - *item = (((((u_i[8 * j + l] as u32) << 11) as i32 - + (q as i32 / 2)) - / q as i32) - & 0x7FF) as i16; - } - - out[idx] = t[0] as u8; - out[idx + 1] = ((t[0] >> 8) | (t[1] << 3)) as u8; - out[idx + 2] = ((t[1] >> 5) | (t[2] << 6)) as u8; - out[idx + 3] = (t[2] >> 2) as u8; - out[idx + 4] = ((t[2] >> 10) | (t[3] << 1)) as u8; - out[idx + 5] = ((t[3] >> 7) | (t[4] << 4)) as u8; - out[idx + 6] = ((t[4] >> 4) | (t[5] << 7)) as u8; - out[idx + 7] = (t[5] >> 1) as u8; - out[idx + 8] = ((t[5] >> 9) | (t[6] << 2)) as u8; - out[idx + 9] = ((t[6] >> 6) | (t[7] << 5)) as u8; - out[idx + 10] = (t[7] >> 3) as u8; - idx += 11; + for j in 0..N / 8 { + for (l, item) in t.iter_mut().enumerate() { + *item = (((((u_i[8 * j + l] as u32) << 11) as i32 + (q as i32 / 2)) / q as i32) + & 0x7FF) as i16; } - }, + + out[idx] = t[0] as u8; + out[idx + 1] = ((t[0] >> 8) | (t[1] << 3)) as u8; + out[idx + 2] = ((t[1] >> 5) | (t[2] << 6)) as u8; + out[idx + 3] = (t[2] >> 2) as u8; + out[idx + 4] = ((t[2] >> 10) | (t[3] << 1)) as u8; + out[idx + 5] = ((t[3] >> 7) | (t[4] << 4)) as u8; + out[idx + 6] = ((t[4] >> 4) | (t[5] << 7)) as u8; + out[idx + 7] = (t[5] >> 1) as u8; + out[idx + 8] = ((t[5] >> 9) | (t[6] << 2)) as u8; + out[idx + 9] = ((t[6] >> 6) | (t[7] << 5)) as u8; + out[idx + 10] = (t[7] >> 3) as u8; + idx += 11; + } + } _ => unreachable!(), } } - pub(crate) fn unpack_ciphertext_u_row( row: usize, - ct: &[u8; CT_LEN] + ct: &[u8; CT_LEN], ) -> Polynomial { let mut u_i = Polynomial::new(); @@ -200,82 +209,78 @@ pub(crate) fn unpack_ciphertext_u_row( // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them let start: usize = row * (N * (du as usize) / 8); let end: usize = (row + 1) * (N * (du as usize) / 8); - let compressed_u_i = &ct[start .. end]; + let compressed_u_i = &ct[start..end]; let mut idx = 0; match du { - 10 => { // MLKEM512 and MLKEM768 + 10 => { + // MLKEM512 and MLKEM768 let mut t = [0i16; 4]; - for j in 0..(N/4) { - t[0] = ((compressed_u_i[idx] as u16) - | (compressed_u_i[idx + 1] as u16) << 8) - as i16; + for j in 0..(N / 4) { + t[0] = + ((compressed_u_i[idx] as u16) | (compressed_u_i[idx + 1] as u16) << 8) as i16; t[1] = (((compressed_u_i[idx + 1] as u16) >> 2) - | (compressed_u_i[idx + 2] as u16) << 6) - as i16; + | (compressed_u_i[idx + 2] as u16) << 6) as i16; t[2] = (((compressed_u_i[idx + 2] as u16) >> 4) - | (compressed_u_i[idx + 3] as u16) << 4) - as i16; + | (compressed_u_i[idx + 3] as u16) << 4) as i16; t[3] = (((compressed_u_i[idx + 3] as u16) >> 6) - | (compressed_u_i[idx + 4] as u16) << 2) - as i16; + | (compressed_u_i[idx + 4] as u16) << 2) as i16; idx += 5; for (l, item) in t.iter().enumerate() { - u_i[4 * j + l] = - ((((*item & 0x3FF) as i32) * (q as i32) + 512) >> 10) as i16; + u_i[4 * j + l] = ((((*item & 0x3FF) as i32) * (q as i32) + 512) >> 10) as i16; } } - }, - 11 => { // MLKEM1024 + } + 11 => { + // MLKEM1024 let mut t = [0i16; 8]; - for j in 0..N/8 { - t[0] = (compressed_u_i[idx] as i32 - | ((compressed_u_i[idx + 1] as u16) as i32) << 8) + for j in 0..N / 8 { + t[0] = (compressed_u_i[idx] as i32 | ((compressed_u_i[idx + 1] as u16) as i32) << 8) as i16; t[1] = ((compressed_u_i[idx + 1] >> 3) as i32 - | ((compressed_u_i[idx + 2] as u16) as i32) << 5) - as i16; + | ((compressed_u_i[idx + 2] as u16) as i32) << 5) as i16; t[2] = ((compressed_u_i[idx + 2] >> 6) as i32 | ((compressed_u_i[idx + 3] as u16) as i32) << 2 | (((compressed_u_i[idx + 4] as i32) << 10) as u16) as i32) as i16; t[3] = ((compressed_u_i[idx + 4] >> 1) as i32 - | ((compressed_u_i[idx + 5] as u16) as i32) << 7) - as i16; + | ((compressed_u_i[idx + 5] as u16) as i32) << 7) as i16; t[4] = ((compressed_u_i[idx + 5] >> 4) as i32 - | ((compressed_u_i[idx + 6] as u16) as i32) << 4) - as i16; + | ((compressed_u_i[idx + 6] as u16) as i32) << 4) as i16; t[5] = ((compressed_u_i[idx + 6] >> 7) as i32 | ((compressed_u_i[idx + 7] as u16) as i32) << 1 | (((compressed_u_i[idx + 8] as i32) << 9) as u16) as i32) as i16; t[6] = ((compressed_u_i[idx + 8] >> 2) as i32 - | ((compressed_u_i[idx + 9] as u16) as i32) << 6) - as i16; + | ((compressed_u_i[idx + 9] as u16) as i32) << 6) as i16; t[7] = ((compressed_u_i[idx + 9] >> 5) as i32 | ((compressed_u_i[idx + 10] as u16) as i32) << 3) as i16; idx += 11; for (l, item) in t.iter().enumerate() { - u_i[8 * j + l] = - ((((*item & 0x7FF) as i32) * (q as i32) + 1024) >> 11) as i16; + u_i[8 * j + l] = ((((*item & 0x7FF) as i32) * (q as i32) + 1024) >> 11) as i16; } } - }, + } _ => unreachable!(), } u_i } -pub(crate) fn unpack_ciphertext_v( - c: &[u8; CT_LEN] +pub(crate) fn unpack_ciphertext_v< + const k: usize, + const CT_LEN: usize, + const du: i16, + const dv: i16, +>( + c: &[u8; CT_LEN], ) -> Polynomial { // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them - let lim: usize = k* (N * (du as usize) / 8); + let lim: usize = k * (N * (du as usize) / 8); let v = Polynomial::decompress_poly::(&c[lim..]); v -} \ No newline at end of file +} diff --git a/crypto/mlkem_lowmemory/src/mlkem.rs b/crypto/mlkem-lowmemory/src/mlkem.rs similarity index 75% rename from crypto/mlkem_lowmemory/src/mlkem.rs rename to crypto/mlkem-lowmemory/src/mlkem.rs index 13f9c9c..8c41bac 100644 --- a/crypto/mlkem_lowmemory/src/mlkem.rs +++ b/crypto/mlkem-lowmemory/src/mlkem.rs @@ -1,20 +1,28 @@ //! There are no advanced features in this low memory crate that are not already documented in the standard \[bouncycastle_mlkem] crate. - -use core::marker::PhantomData; -use bouncycastle_core::errors::{KEMError}; -use bouncycastle_core::key_material::{KeyMaterial, KeyType, KeyMaterialTrait}; -use bouncycastle_core::traits::{RNG, SecurityStrength, XOF, Algorithm, Hash, KEM}; -use bouncycastle_rng::{HashDRBG_SHA512}; +use crate::aux_functions::sample_poly_CBD; +use crate::low_memory_helpers::{ + compress_u_row, compute_A_hat_dot_y_hat, compute_t_hat_dot_y_hat_row, unpack_ciphertext_u_row, + unpack_ciphertext_v, unpack_t_hat_row, +}; +use crate::mlkem_keys::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, +}; +use crate::mlkem_keys::{MLKEMPrivateKeyInternalTrait, MLKEMPrivateKeyTrait}; +use crate::mlkem_keys::{MLKEMPublicKeyInternalTrait, MLKEMPublicKeyTrait}; +use crate::polynomial::Polynomial; +use bouncycastle_core::errors::{KEMError, RNGError}; +use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; +use bouncycastle_core::traits::{ + Algorithm, Hash, KEMDecapsulator, KEMEncapsulator, RNG, SecurityStrength, XOF, +}; +use bouncycastle_rng::HashDRBG_SHA512; use bouncycastle_sha3::{SHA3_256, SHA3_512, SHAKE256}; use bouncycastle_utils::ct::{conditional_copy_bytes, ct_eq_bytes}; -use crate::aux_functions::{sample_poly_CBD}; -use crate::low_memory_helpers::{compress_u_row, compute_A_hat_dot_y_hat, compute_t_hat_dot_y_hat_row, unpack_ciphertext_u_row, unpack_ciphertext_v, unpack_t_hat_row}; -use crate::mlkem_keys::{MLKEMPublicKeyTrait, MLKEMPublicKeyInternalTrait}; -use crate::mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPrivateKeyInternalTrait}; -use crate::mlkem_keys::{MLKEM512PublicKey, MLKEM512PrivateKey, MLKEM768PublicKey, MLKEM768PrivateKey, MLKEM1024PublicKey, MLKEM1024PrivateKey}; -use crate::polynomial::{Polynomial}; - +use core::marker::PhantomData; /*** Constants ***/ @@ -40,7 +48,6 @@ pub(crate) const q_inv: i32 = 62209; pub(crate) const ETA2: i16 = 2; pub(crate) const POLY_BYTES: usize = 384; - /* ML-KEM-512 params */ /// Length of the \[u8] holding a ML-KEM-512 public key. @@ -78,11 +85,9 @@ pub(crate) const MLKEM768_DV: i16 = 4; /// Maps to "required RBG strength (bits)" in FIPS 203 Table 2 pub(crate) const MLKEM768_LAMBDA: i16 = 192; - // internal derived values pub(crate) const MLKEM768_T_PACKED_LEN: usize = 12 * MLKEM768_k * 32; - /* ML-KEM-1024 params */ /// Length of the \[u8] holding a ML-KEM-1024 public key. @@ -100,19 +105,14 @@ pub(crate) const MLKEM1024_DV: i16 = 5; /// Maps to "required RBG strength (bits)" in FIPS 203 Table 2 pub(crate) const MLKEM1024_LAMBDA: i16 = 256; - // internal derived values pub(crate) const MLKEM1024_T_PACKED_LEN: usize = 12 * MLKEM1024_k * 32; - - // Typedefs just to make the algorithms look more like the FIPS 204 sample code. pub(crate) type G = SHA3_512; pub(crate) type H = SHA3_256; pub(crate) type J = SHAKE256; - - /*** Pub Types ***/ /// The ML-KEM-512 algorithm. @@ -190,8 +190,10 @@ pub struct MLKEM< const FULL_SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, - PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta1: i16, const du: i16, @@ -208,40 +210,33 @@ impl< const FULL_SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, - PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta1: i16, const du: i16, const dv: i16, const LAMBDA: i16, const T_PACKED_LEN: usize, -> MLKEM< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta1, - du, - dv, - LAMBDA, - T_PACKED_LEN, > + MLKEM< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + CT_LEN, + SS_LEN, + PK, + SK, + k, + eta1, + du, + dv, + LAMBDA, + T_PACKED_LEN, + > { - /// Should still be ok in FIPS mode - pub fn keygen_from_os_rng() -> Result< - (PK, SK), - KEMError, - > { - let mut seed = KeyMaterial::<64>::new(); - HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?; - // Self::keygen_internal(&seed) - Self::keygen_internal(&seed) - } /// Performs the first step of key generation to transform the single provided seed into a set of internal intermediate seeds. /// /// Unlike other interfaces across the library that take an &impl KeyMaterial, this one @@ -249,26 +244,25 @@ impl< /// the appropriate [SecurityStrength] for the requested ML-KEM parameter set. /// If you happen to have your seed in a larger KeyMaterial, you'll have to copy it using /// [KeyMaterial::from_key]. - pub(crate) fn keygen_internal( - seed: &KeyMaterial<64>, - ) -> Result< - (PK, SK), - KEMError, - > { + pub(crate) fn keygen_internal(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError> { let sk = SK::from_keymaterial(seed)?; let pk = sk.pk(); let pk = PK::new(pk.t_hat_packed, pk.rho); // stupid conversion, but it gets around these overly-generified rust types Ok((pk, sk)) } - /// Algorithm 14 K-PKE.Encrypt(ekPKE, 𝑚, 𝑟) /// Uses the encryption key to encrypt a plaintext message using the randomness 𝑟. /// Input: encryption key ekPKE ∈ 𝔹384𝑘+32 . /// Input: message 𝑚 ∈ 𝔹32 . /// Input: randomness 𝑟 ∈ 𝔹32 . /// Output: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). - fn pke_encrypt(t_hat_packed: &[u8; T_PACKED_LEN], rho: &[u8; 32], m: [u8; 32], r: &[u8; 32]) -> [u8; CT_LEN] { + fn pke_encrypt( + t_hat_packed: &[u8; T_PACKED_LEN], + rho: &[u8; 32], + m: [u8; 32], + r: &[u8; 32], + ) -> [u8; CT_LEN] { let mut ct = [0u8; CT_LEN]; // 1: 𝑁 ← 0 @@ -283,9 +277,9 @@ impl< // Note: you need y_hat twice: once here at line 19, and again at line 21. // We'll just generate it twice to save the memory of holding on to it. - for i in 0 .. k { + for i in 0..k { let mut u_i = compute_A_hat_dot_y_hat::(rho, &r, i); - + let e1_i = sample_poly_CBD::(&r, (k + i) as u8); u_i.add(&e1_i); u_i.poly_reduce(); @@ -302,23 +296,25 @@ impl< let mut v = compute_t_hat_dot_y_hat_row::( &r, &unpack_t_hat_row(t_hat_packed, 0), - /*row*/ 0); + /*row*/ 0, + ); - for i in 1 .. k { + for i in 1..k { let v_i = compute_t_hat_dot_y_hat_row::( &r, &unpack_t_hat_row(t_hat_packed, i), - /*row*/ i); + /*row*/ i, + ); v.add(&v_i); } // perform polynomial addition - let e2 = sample_poly_CBD::(&r, 2*k as u8); + let e2 = sample_poly_CBD::(&r, 2 * k as u8); v.add(&e2); - + let mu = Polynomial::from_msg(m); v.add(&mu); - + v.poly_reduce(); v.compress_poly::(&mut ct[CT_LEN - (N * (dv as usize) / 8)..]); @@ -334,20 +330,20 @@ impl< /// Output: shared secret key 𝐾 ∈ 𝔹32 . /// Output: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// - /// Unlike the more public function exposed by [KEM::encaps], this returns the shared secret as raw bytes + /// Unlike the more public function exposed by [KEMEncapsulator::encaps], this returns the shared secret as raw bytes /// instead of wrapped in an appropriately-set [KeyMaterialTrait], so you're on your own for handling it properly. /// /// Note: this is an internal function that allows the caller to specify the encapsulation /// randomness (which is the message `m` to be encrypted by the underlying PKE scheme). /// This function should not be used directly unless you really have a - /// good reason. [KEM::encaps] should be used in 99.9% of cases. + /// good reason. [KEMEncapsulator::encaps] should be used in 99.9% of cases. /// The reason this is exposed publicly is: A) for unit testing that requires access /// to the deterministically reproducible function, and B) for operational environments /// that wish to provide randomness from their own source instead of the built-in RNG in bc-rust. /// If you think you will be clever and invent some scheme that uses a deterministic KEM, /// then you will almost certainly end up with security problems. Please don't do this. pub fn encaps_internal(ek: &PK, m: [u8; 32]) -> ([u8; 32], [u8; CT_LEN]) { - debug_assert_eq!(CT_LEN, 32*( (du as usize)*k + (dv as usize))); + debug_assert_eq!(CT_LEN, 32 * ((du as usize) * k + (dv as usize))); // 1: (𝐾, 𝑟) ← G(𝑚‖H(ek)) // ▷ derive shared secret key 𝐾 and randomness 𝑟 @@ -400,7 +396,7 @@ impl< s_hat_i }; - for i in 1 .. k { + for i in 1..k { let mut s_hat_i = dk.compute_s_hat_row(i); { let mut u_prime_i = unpack_ciphertext_u_row::(i, &ct); @@ -438,11 +434,10 @@ impl< /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: shared secret key 𝐾 ∈ 𝔹32 . fn decaps_internal(dk: &SK, c: [u8; CT_LEN]) -> [u8; MLKEM_SS_LEN] { - // I have tried to keep this as clean as possible for correspondence with the FIPS, // but I have moved things around so that I can use unnamed scopes to limit how many // stack variables are alive at the same time. - + // 1: dkPKE ← dk[0 ∶ 384𝑘] ▷ extract (from KEM decaps key) the PKE decryption key // 2: ekPKE ← dk[384𝑘 ∶ 768𝑘 + 32] ▷ extract PKE encryption key // 3: ℎ ← dk[768𝑘 + 32 ∶ 768𝑘 + 64] ▷ extract hash of PKE encryption key @@ -500,7 +495,10 @@ impl< /// Alternative initialization of the streaming signer where you have your private key /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons. - pub fn decaps_from_seed(seed: &KeyMaterial<64>, ct: &[u8]) -> Result, KEMError> { + pub fn decaps_from_seed( + seed: &KeyMaterial<64>, + ct: &[u8], + ) -> Result, KEMError> { let sk = SK::from_keymaterial(seed)?; Self::decaps(&sk, ct) @@ -513,29 +511,48 @@ impl< const FULL_SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, - PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta1: i16, const du: i16, const dv: i16, const LAMBDA: i16, const T_PACKED_LEN: usize, -> MLKEMTrait for MLKEM< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta1, - du, - dv, - LAMBDA, - T_PACKED_LEN, -> { +> + MLKEMTrait< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + CT_LEN, + SS_LEN, + PK, + SK, + k, + eta1, + du, + dv, + LAMBDA, + T_PACKED_LEN, + > + for MLKEM< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + CT_LEN, + SS_LEN, + PK, + SK, + k, + eta1, + du, + dv, + LAMBDA, + T_PACKED_LEN, + > +{ /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError> { Self::keygen_internal(seed) @@ -549,10 +566,7 @@ impl< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<64>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - KEMError, - > { + ) -> Result<(PK, SK), KEMError> { let (pk, sk) = Self::keygen_internal(seed)?; let sk_from_bytes = SK::sk_decode(encoded_sk); @@ -572,10 +586,7 @@ impl< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [KEMError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), KEMError> { + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), KEMError> { let derived_pk = sk.pk(); if derived_pk.compute_hash() == pk.compute_hash() { Ok(()) @@ -592,15 +603,34 @@ pub trait MLKEMTrait< const FULL_SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, - PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta: i16, const du: i16, const dv: i16, const LAMBDA: i16, const T_PACKED_LEN: usize, -> : Sized { +>: Sized +{ + /// Generates a fresh key pair. + fn keygen() -> Result<(PK, SK), KEMError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::keygen_from_rng(&mut os_rng) + } + /// Run a keygen using the provided RNG implementation. + // Should still be ok in FIPS mode, provided that you're using the FIPS-approved RNG. + fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), KEMError> { + // Source the seed from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } + let mut seed = KeyMaterial::<64>::new(); + rng.fill_keymaterial_out(&mut seed)?; + Self::keygen_from_seed(&seed) + } /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError>; /// Imports a secret key from both a seed and an encoded_sk. @@ -612,10 +642,7 @@ pub trait MLKEMTrait< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<64>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - KEMError, - >; + ) -> Result<(PK, SK), KEMError>; /// Given a public key and a secret key, check that the public key matches the secret key. /// This is a sanity check that the public key was generated correctly from the secret key. /// @@ -624,10 +651,7 @@ pub trait MLKEMTrait< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [KEMError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), KEMError>; + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), KEMError>; } impl< @@ -636,61 +660,111 @@ impl< const FULL_SK_LEN: usize, const CT_LEN: usize, const SS_LEN: usize, - PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta: i16, const du: i16, const dv: i16, const LAMBDA: i16, const T_PACKED_LEN: usize, -> KEM for MLKEM< - PK_LEN, - SK_LEN, - FULL_SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta, - du, - dv, - LAMBDA, - T_PACKED_LEN, -> { - /// Generates a fresh key pair. - fn keygen() -> Result<(PK, SK), KEMError> { - Self::keygen_from_os_rng() +> KEMEncapsulator + for MLKEM< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + CT_LEN, + SS_LEN, + PK, + SK, + k, + eta, + du, + dv, + LAMBDA, + T_PACKED_LEN, + > +{ + fn encaps(pk: &PK) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::encaps_rng(pk, &mut os_rng) } - fn encaps(pk: &PK) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + fn encaps_rng( + pk: &PK, + rng: &mut dyn RNG, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + // Source the random message m from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } let mut m = [0u8; 32]; - HashDRBG_SHA512::new_from_os().next_bytes_out(&mut m)?; + rng.next_bytes_out(&mut m)?; let (ss_bytes, ct) = Self::encaps_internal(pk, m); - let mut ss_keymaterial = KeyMaterial::::from_bytes_as_type(&ss_bytes, KeyType::BytesFullEntropy)?; - ss_keymaterial.allow_hazardous_operations(); - ss_keymaterial.set_security_strength( SecurityStrength::from_bits(LAMBDA as usize) )?; - ss_keymaterial.drop_hazardous_operations(); + let mut ss_keymaterial = + KeyMaterial::::from_bytes_as_type(&ss_bytes, KeyType::CryptographicRandom)?; + do_hazardous_operations(&mut ss_keymaterial, |ss_keymaterial| { + ss_keymaterial.set_security_strength(SecurityStrength::from_bits(LAMBDA as usize)) + })?; Ok((ss_keymaterial, ct)) - } +} +impl< + const PK_LEN: usize, + const SK_LEN: usize, + const FULL_SK_LEN: usize, + const CT_LEN: usize, + const SS_LEN: usize, + PK: MLKEMPublicKeyTrait + + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, + const k: usize, + const eta: i16, + const du: i16, + const dv: i16, + const LAMBDA: i16, + const T_PACKED_LEN: usize, +> KEMDecapsulator + for MLKEM< + PK_LEN, + SK_LEN, + FULL_SK_LEN, + CT_LEN, + SS_LEN, + PK, + SK, + k, + eta, + du, + dv, + LAMBDA, + T_PACKED_LEN, + > +{ + /// Performs a decapsulation of the given ciphertext. + /// Returns the shared secret key. + /// The derived shared secret key is returned as a KeyMaterial with the SecurityStrength set to + /// the security level of the ML-KEM parameter set. + /// As ML-KEM is an implicitly-rejecting KEM, this returns an error only if the ciphertext is invalid (ie the wrong length).. fn decaps(sk: &SK, ct: &[u8]) -> Result, KEMError> { - if ct.len() != CT_LEN - { + if ct.len() != CT_LEN { return Err(KEMError::LengthError("Invalid ciphertext length")); } let ss_bytes = Self::decaps_internal(sk, ct.try_into().unwrap()); - let mut ss_keymaterial = KeyMaterial::::from_bytes_as_type(&ss_bytes, KeyType::BytesFullEntropy)?; - ss_keymaterial.allow_hazardous_operations(); - ss_keymaterial.set_security_strength( SecurityStrength::from_bits(LAMBDA as usize) )?; - ss_keymaterial.drop_hazardous_operations(); + let mut ss_keymaterial = + KeyMaterial::::from_bytes_as_type(&ss_bytes, KeyType::CryptographicRandom)?; + do_hazardous_operations(&mut ss_keymaterial, |ss_keymaterial| { + ss_keymaterial.set_security_strength(SecurityStrength::from_bits(LAMBDA as usize)) + })?; Ok(ss_keymaterial) } diff --git a/crypto/mlkem_lowmemory/src/mlkem_keys.rs b/crypto/mlkem-lowmemory/src/mlkem_keys.rs similarity index 76% rename from crypto/mlkem_lowmemory/src/mlkem_keys.rs rename to crypto/mlkem-lowmemory/src/mlkem_keys.rs index 5571ab9..36d36bd 100644 --- a/crypto/mlkem_lowmemory/src/mlkem_keys.rs +++ b/crypto/mlkem-lowmemory/src/mlkem_keys.rs @@ -1,17 +1,30 @@ -use crate::aux_functions::{sample_poly_CBD}; -use crate::mlkem::{POLY_BYTES, H, G}; +use crate::aux_functions::sample_poly_CBD; +use crate::low_memory_helpers::{ + compute_A_hat_dot_s_hat, pack_s_hat_row, pack_t_hat_row, unpack_t_hat_row, +}; +use crate::mlkem::{G, H, POLY_BYTES, q}; +use crate::mlkem::{ + MLKEM512_ETA1, MLKEM512_FULL_SK_LEN, MLKEM512_LAMBDA, MLKEM512_PK_LEN, MLKEM512_SK_LEN, + MLKEM512_T_PACKED_LEN, MLKEM512_k, +}; +use crate::mlkem::{ + MLKEM768_ETA1, MLKEM768_FULL_SK_LEN, MLKEM768_LAMBDA, MLKEM768_PK_LEN, MLKEM768_SK_LEN, + MLKEM768_T_PACKED_LEN, MLKEM768_k, +}; +use crate::mlkem::{ + MLKEM1024_ETA1, MLKEM1024_FULL_SK_LEN, MLKEM1024_LAMBDA, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, + MLKEM1024_T_PACKED_LEN, MLKEM1024_k, +}; +use crate::polynomial::Polynomial; use crate::{ML_KEM_512_NAME, ML_KEM_768_NAME, ML_KEM_1024_NAME}; -use crate::mlkem::{MLKEM512_k, MLKEM512_ETA1, MLKEM512_LAMBDA, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_FULL_SK_LEN, MLKEM512_T_PACKED_LEN}; -use crate::mlkem::{MLKEM768_k, MLKEM768_ETA1, MLKEM768_LAMBDA, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_FULL_SK_LEN, MLKEM768_T_PACKED_LEN}; -use crate::mlkem::{MLKEM1024_k, MLKEM1024_ETA1, MLKEM1024_LAMBDA, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_FULL_SK_LEN, MLKEM1024_T_PACKED_LEN}; -use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial, KeyType}; +use bouncycastle_core::errors::KEMError; +use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; use bouncycastle_core::traits::{Hash, KEMPrivateKey, KEMPublicKey, Secret, SecurityStrength}; -use bouncycastle_core::errors::{KEMError}; +use bouncycastle_sha3::SHA3_256; use core::fmt; use core::fmt::{Debug, Display, Formatter}; -use bouncycastle_sha3::SHA3_256; -use crate::low_memory_helpers::{compute_A_hat_dot_s_hat, pack_s_hat_row, pack_t_hat_row}; -use crate::polynomial::{Polynomial}; // imports just for docs @@ -20,16 +33,39 @@ use crate::polynomial::{Polynomial}; /// ML-KEM-512 Public Key pub type MLKEM512PublicKey = MLKEMPublicKey; /// ML-KEM-512 Private Key -pub type MLKEM512PrivateKey = MLKEMSeedPrivateKey; +pub type MLKEM512PrivateKey = MLKEMSeedPrivateKey< + MLKEM512_k, + MLKEM512_ETA1, + MLKEM512_LAMBDA, + MLKEM512_SK_LEN, + MLKEM512_FULL_SK_LEN, + MLKEM512_PK_LEN, + MLKEM512_T_PACKED_LEN, +>; /// ML-KEM-768 Public Key pub type MLKEM768PublicKey = MLKEMPublicKey; /// ML-KEM-768 Private Key -pub type MLKEM768PrivateKey = MLKEMSeedPrivateKey; +pub type MLKEM768PrivateKey = MLKEMSeedPrivateKey< + MLKEM768_k, + MLKEM768_ETA1, + MLKEM768_LAMBDA, + MLKEM768_SK_LEN, + MLKEM768_FULL_SK_LEN, + MLKEM768_PK_LEN, + MLKEM768_T_PACKED_LEN, +>; /// ML-KEM-1024 Public Key pub type MLKEM1024PublicKey = MLKEMPublicKey; /// ML-KEM-1024 Private Key -pub type MLKEM1024PrivateKey = MLKEMSeedPrivateKey; - +pub type MLKEM1024PrivateKey = MLKEMSeedPrivateKey< + MLKEM1024_k, + MLKEM1024_ETA1, + MLKEM1024_LAMBDA, + MLKEM1024_SK_LEN, + MLKEM1024_FULL_SK_LEN, + MLKEM1024_PK_LEN, + MLKEM1024_T_PACKED_LEN, +>; /// An ML-KEM public key. #[derive(Clone)] @@ -39,12 +75,15 @@ pub struct MLKEMPublicKey : KEMPublicKey { +pub trait MLKEMPublicKeyTrait: + KEMPublicKey +{ /// Algorithm 23 pkDecode(𝑝𝑘) /// Reverses the procedure pkEncode. /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑). /// Output: 𝜌 ∈ 𝔹32, 𝐭1 ∈ 𝑅𝑘 with coefficients in [0, 2bitlen (𝑞−1)−𝑑 − 1]. - fn pk_decode(pk: &[u8; PK_LEN]) -> Self; + // todo: go make the equivalent thing also throw an error in the non-optimized impl + fn pk_decode(pk: &[u8; PK_LEN]) -> Result; /// Get a ref to t_hat_packed byte array fn t_hat_packed(&self) -> &[u8; T_PACKED_LEN]; @@ -59,17 +98,34 @@ pub trait MLKEMPublicKeyTrait : MLKEMPublicKeyTrait { + const PK_LEN: usize, +>: MLKEMPublicKeyTrait +{ /// Not exposing a constructor publicly because you should have to get an instance either by /// running a keygen, or by decoding an existing key. fn new(t_hat: [u8; T_PACKED_LEN], rho: [u8; 32]) -> Self; } impl -MLKEMPublicKeyTrait for MLKEMPublicKey { - fn pk_decode(pk: &[u8; PK_LEN]) -> Self { - Self::new(pk[..T_PACKED_LEN].try_into().unwrap(), pk[T_PACKED_LEN..].try_into().unwrap()) + MLKEMPublicKeyTrait for MLKEMPublicKey +{ + fn pk_decode(pk: &[u8; PK_LEN]) -> Result { + let pk = Self::new( + pk[..T_PACKED_LEN].try_into().unwrap(), + pk[T_PACKED_LEN..].try_into().unwrap(), + ); + + // check that all entries are in range + for i in 0..k { + let p = unpack_t_hat_row(&pk.t_hat_packed, i); + for w in p.coeffs.iter() { + if *w >= q { + return Err(KEMError::DecodingError("Invalid public key")); + } + } + } + + Ok(pk) } fn t_hat_packed(&self) -> &[u8; T_PACKED_LEN] { @@ -94,20 +150,23 @@ MLKEMPublicKeyTrait for MLKEMPublicKey -MLKEMPublicKeyInternalTrait for MLKEMPublicKey { + MLKEMPublicKeyInternalTrait + for MLKEMPublicKey +{ fn new(t_hat_packed: [u8; T_PACKED_LEN], rho: [u8; 32]) -> Self { Self { rho, t_hat_packed } } } -impl -KEMPublicKey for MLKEMPublicKey { +impl KEMPublicKey + for MLKEMPublicKey +{ /// Algorithm 22 pkEncode(𝜌, 𝐭1) /// Encodes a public key for ML-DSA into a byte string. /// Input:𝜌 ∈ 𝔹32, 𝐭1 ∈ 𝑅𝑘 with coefficients in [0, 2bitlen (𝑞−1)−𝑑 − 1]. /// Output: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑). fn encode(&self) -> [u8; PK_LEN] { - debug_assert_eq!(PK_LEN, 32 + 12*k*32); + debug_assert_eq!(PK_LEN, 32 + 12 * k * 32); let mut pk = [0u8; PK_LEN]; self.encode_out(&mut pk); @@ -117,7 +176,9 @@ KEMPublicKey for MLKEMPublicKey { fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize { debug_assert_eq!(self.t_hat_packed.len(), T_PACKED_LEN); - out[.. T_PACKED_LEN].copy_from_slice(&self.t_hat_packed); + out.fill(0); + + out[..T_PACKED_LEN].copy_from_slice(&self.t_hat_packed); debug_assert_eq!(out[T_PACKED_LEN..].len(), 32); out[T_PACKED_LEN..].copy_from_slice(&self.rho); @@ -125,24 +186,30 @@ KEMPublicKey for MLKEMPublicKey { } fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PK_LEN { return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")) } + if bytes.len() != PK_LEN { + return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")); + } let bytes_sized: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); - Ok(Self::pk_decode(&bytes_sized)) + Self::pk_decode(&bytes_sized) } } -impl -Eq for MLKEMPublicKey { } +impl Eq + for MLKEMPublicKey +{ +} -impl -PartialEq for MLKEMPublicKey { +impl PartialEq + for MLKEMPublicKey +{ fn eq(&self, other: &Self) -> bool { bouncycastle_utils::ct::ct_eq_bytes(&self.encode(), &other.encode()) } } -impl -Debug for MLKEMPublicKey { +impl Debug + for MLKEMPublicKey +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -155,8 +222,9 @@ Debug for MLKEMPublicKey { } } -impl -Display for MLKEMPublicKey { +impl Display + for MLKEMPublicKey +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -169,10 +237,6 @@ Display for MLKEMPublicKey { } } - - - - /// An ML-KEM private key. #[derive(Clone)] pub struct MLKEMSeedPrivateKey< @@ -182,7 +246,7 @@ pub struct MLKEMSeedPrivateKey< const SK_LEN: usize, const FULL_SK_LEN: usize, const PK_LEN: usize, - const T_PACKED_LEN: usize + const T_PACKED_LEN: usize, > { rho: [u8; 32], sigma: [u8; 32], @@ -198,12 +262,13 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const PK_LEN: usize, - const T_PACKED_LEN: usize -> MLKEMSeedPrivateKey { + const T_PACKED_LEN: usize, +> MLKEMSeedPrivateKey +{ /// Create a new MLKEMSeedPrivateKey from a 64-byte KeyMaterial. /// Seed SecurityStrength must match algorithm security strength: 128-bit (ML-KEM-512), 192-bit (ML-KEM-768), or 256-bit (ML-KEM-1024). pub fn new(seed: &KeyMaterial<64>) -> Result { - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::CryptographicRandom) || seed.key_len() != 64 { return Err(KEMError::KeyGenError( @@ -241,7 +306,6 @@ impl< } } - /// General trait for all ML-KEM private keys types. pub trait MLKEMPrivateKeyTrait< const k: usize, @@ -249,7 +313,8 @@ pub trait MLKEMPrivateKeyTrait< const FULL_SK_LEN: usize, const PK_LEN: usize, const T_PACKED_LEN: usize, -> : KEMPrivateKey { +>: KEMPrivateKey +{ /// New from KeyMaterial. Can throw a KEMError if the KeyMaterial does not contain sufficient entropy. fn from_keymaterial(seed: &KeyMaterial<64>) -> Result; /// Get a ref to the seed, which there always will be for a MLKEMSeedPrivateKey @@ -281,14 +346,18 @@ pub trait MLKEMPrivateKeyTrait< /// /// As described on Algorithm 16 line /// 3: dk ← (dkPKE ‖ ek ‖ H(ek) ‖ 𝑧) - fn full_sk_encode_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize; + fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize; /// Decode the private key. fn sk_decode(sk: &[u8; SK_LEN]) -> Self; } -pub(crate) trait MLKEMPrivateKeyInternalTrait { - +pub(crate) trait MLKEMPrivateKeyInternalTrait< + const k: usize, + const SK_LEN: usize, + const PK_LEN: usize, + const T_PACKED_LEN: usize, +> +{ fn z(&self) -> &[u8; 32]; fn compute_s_hat_row(&self, idx: usize) -> Polynomial; @@ -299,7 +368,6 @@ pub(crate) trait MLKEMPrivateKeyInternalTrait [u8; T_PACKED_LEN]; } - impl< const k: usize, const eta1: i16, @@ -307,8 +375,10 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const PK_LEN: usize, - const T_PACKED_LEN: usize -> MLKEMPrivateKeyTrait for MLKEMSeedPrivateKey { + const T_PACKED_LEN: usize, +> MLKEMPrivateKeyTrait + for MLKEMSeedPrivateKey +{ fn from_keymaterial(seed: &KeyMaterial<64>) -> Result { Self::new(seed) } @@ -317,14 +387,15 @@ impl< tmp[..32].copy_from_slice(&self.seed_d); tmp[32..].copy_from_slice(&self.z); let mut seed = KeyMaterial::<64>::from_bytes_as_type(&tmp, KeyType::Seed).unwrap(); - seed.allow_hazardous_operations(); - seed.set_security_strength( match k { - 2 => SecurityStrength::_128bit, - 3 => SecurityStrength::_192bit, - 4 => SecurityStrength::_256bit, - _ => unreachable!("Invalid mlkem param set"), - }).unwrap(); - seed.drop_hazardous_operations(); + do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(match k { + 2 => SecurityStrength::_128bit, + 3 => SecurityStrength::_192bit, + 4 => SecurityStrength::_256bit, + _ => unreachable!("Invalid mlkem param set"), + }) + }) + .unwrap(); Some(seed) } @@ -348,7 +419,7 @@ impl< /// 3: dk ← (dkPKE ‖ ek ‖ H(ek) ‖ 𝑧) fn encode_full_sk(&self) -> [u8; FULL_SK_LEN] { let mut out = [0u8; FULL_SK_LEN]; - self.full_sk_encode_out(&mut out); + self.encode_full_sk_out(&mut out); out } @@ -359,7 +430,7 @@ impl< /// /// As described on Algorithm 16 line /// 3: dk ← (dkPKE ‖ ek ‖ H(ek) ‖ 𝑧) - fn full_sk_encode_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize { + fn encode_full_sk_out(&self, out: &mut [u8; FULL_SK_LEN]) -> usize { out.fill(0); let mut pos = 0usize; @@ -367,9 +438,6 @@ impl< /* dk_pke */ // Alg 13; line 20: dkPKE ← ByteEncode12(𝐬) for i in 0..k { - // out[i*POLY_BYTES .. (i+1)*POLY_BYTES].copy_from_slice(&byte_encode::<12, POLY_BYTES>( - // &self.compute_s_hat_row(i), - // )); pack_s_hat_row::(&self.compute_s_hat_row(i), i, out); } pos += k * POLY_BYTES; @@ -377,16 +445,15 @@ impl< /* ek */ // Alg 13; line 19: ekPKE ← ByteEncode12(𝐭)‖𝜌 let pk = self.pk(); - out[pos .. pos + PK_LEN].copy_from_slice(&pk.encode()); + out[pos..pos + PK_LEN].copy_from_slice(&pk.encode()); pos += PK_LEN; /* H(ek) */ - out[pos .. pos + 32].copy_from_slice(&pk.compute_hash()); + out[pos..pos + 32].copy_from_slice(&pk.compute_hash()); pos += 32; /* z */ - out[pos .. pos + 32].copy_from_slice(&self.z); - + out[pos..pos + 32].copy_from_slice(&self.z); FULL_SK_LEN } @@ -403,10 +470,13 @@ impl< const SK_LEN: usize, const FULL_SK_LEN: usize, const PK_LEN: usize, - const T_PACKED_LEN: usize -> MLKEMPrivateKeyInternalTrait for MLKEMSeedPrivateKey { - - fn z(&self) -> &[u8; 32] { &self.z } + const T_PACKED_LEN: usize, +> MLKEMPrivateKeyInternalTrait + for MLKEMSeedPrivateKey +{ + fn z(&self) -> &[u8; 32] { + &self.z + } fn compute_s_hat_row(&self, idx: usize) -> Polynomial { debug_assert!(idx < k); @@ -433,7 +503,7 @@ impl< fn t_hat_packed(&self) -> [u8; T_PACKED_LEN] { let mut t_hat_packed = [0u8; T_PACKED_LEN]; - for i in 0 .. k { + for i in 0..k { // first half of // 18: 𝐭_hat ← 𝐀_hat ∘ 𝐬_hat + 𝐞_hat let mut t_hat_i = compute_A_hat_dot_s_hat::(&self.rho, &self.sigma, i); @@ -449,7 +519,7 @@ impl< // Note: here n = k let mut e_i = sample_poly_CBD::(&self.sigma, (k + i) as u8); - e_i.ntt(); // technically now e_hat_i + e_i.ntt(); // technically now e_hat_i t_hat_i.add(&e_i); } t_hat_i.poly_reduce(); @@ -469,7 +539,9 @@ impl< const FULL_SK_LEN: usize, const PK_LEN: usize, const T_PACKED_LEN: usize, -> KEMPrivateKey for MLKEMSeedPrivateKey { +> KEMPrivateKey + for MLKEMSeedPrivateKey +{ /// Encode the private key as a 64-byte seed (d || z) fn encode(&self) -> [u8; SK_LEN] { let mut sk = [0u8; SK_LEN]; @@ -481,6 +553,8 @@ impl< fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { debug_assert_eq!(SK_LEN, 64); + out.fill(0); + out[..32].copy_from_slice(&self.seed_d); out[32..].copy_from_slice(&self.z); @@ -492,10 +566,10 @@ impl< return Err(KEMError::DecodingError("Invalid seed length")); } let mut keymat = KeyMaterial::<64>::from_bytes(bytes)?; - keymat.allow_hazardous_operations(); - keymat.set_key_type(KeyType::Seed)?; - keymat.set_security_strength(SecurityStrength::_256bit)?; - keymat.drop_hazardous_operations(); + do_hazardous_operations(&mut keymat, |keymat| { + keymat.set_key_type(KeyType::Seed)?; + keymat.set_security_strength(SecurityStrength::_256bit) + })?; Self::new(&keymat) } @@ -509,7 +583,9 @@ impl< const FULL_SK_LEN: usize, const PK_LEN: usize, const T_PACKED_LEN: usize, -> Eq for MLKEMSeedPrivateKey {} +> Eq for MLKEMSeedPrivateKey +{ +} impl< const k: usize, @@ -536,7 +612,9 @@ impl< const FULL_SK_LEN: usize, const PK_LEN: usize, const T_PACKED_LEN: usize, -> Secret for MLKEMSeedPrivateKey {} +> Secret for MLKEMSeedPrivateKey +{ +} /// Debug impl mainly to prevent the secret key from being printed in logs. impl< @@ -550,19 +628,14 @@ impl< > fmt::Debug for MLKEMSeedPrivateKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let alg = match k { - 2 => ML_KEM_512_NAME, - 3 => ML_KEM_768_NAME, - 4 => ML_KEM_1024_NAME, - _ => panic!("Unsupported key length"), - }; + let alg = match k { + 2 => ML_KEM_512_NAME, + 3 => ML_KEM_768_NAME, + 4 => ML_KEM_1024_NAME, + _ => panic!("Unsupported key length"), + }; let pk_hash = self.pk().compute_hash(); - write!( - f, - "MLKEMSeedPrivateKey {{ alg: {}, pub_key_hash: {:x?} }}", - alg, - &pk_hash, - ) + write!(f, "MLKEMSeedPrivateKey {{ alg: {}, pub_key_hash: {:x?} }}", alg, &pk_hash,) } } @@ -585,12 +658,7 @@ impl< _ => panic!("Unsupported key length"), }; let pk_hash = self.pk().compute_hash(); - write!( - f, - "MLKEMSeedPrivateKey {{ alg: {}, pub_key_hash: {:x?} }}", - alg, - &pk_hash, - ) + write!(f, "MLKEMSeedPrivateKey {{ alg: {}, pub_key_hash: {:x?} }}", alg, &pk_hash,) } } diff --git a/crypto/mlkem_lowmemory/src/polynomial.rs b/crypto/mlkem-lowmemory/src/polynomial.rs similarity index 84% rename from crypto/mlkem_lowmemory/src/polynomial.rs rename to crypto/mlkem-lowmemory/src/polynomial.rs index a8c7420..bec4ee4 100644 --- a/crypto/mlkem_lowmemory/src/polynomial.rs +++ b/crypto/mlkem-lowmemory/src/polynomial.rs @@ -1,18 +1,22 @@ //! Represents a polynomial over the ML-DSA ring. +use crate::aux_functions::{ + ZETAS, ZETAS_INV, barrett_reduce, montgomery_reduce, mul_mont, ntt_base_mult, +}; +use crate::mlkem::{N, q}; +use bouncycastle_core::traits::Secret; use core::fmt; use core::fmt::{Debug, Display, Formatter}; use core::ops::{Index, IndexMut}; -use bouncycastle_core::traits::Secret; -use crate::aux_functions::{barrett_reduce, cond_sub_q, montgomery_reduce, mul_mont, ntt_base_mult, ZETAS, ZETAS_INV}; -use crate::mlkem::{N, q}; /// A polynomial over the ML-KEM ring. /// Dev note: this doesn't strictly need to be pub ... ie there's no good reason for a caller to use this class directly, /// but in order to test the Debug and Display traits, you need STD, so those can't be tested from inline tests in this file /// and the real unit tests are in a different crate, so here we are. #[derive(Clone)] -pub struct Polynomial{ pub(crate) coeffs: [i16; N] } +pub struct Polynomial { + pub(crate) coeffs: [i16; N], +} /// Convenience function to avoid ".0" all over the place. impl Index for Polynomial { @@ -39,10 +43,10 @@ impl Polynomial { pub(crate) fn from_msg(m: [u8; 32]) -> Self { let mut w = Polynomial::new(); - for (i, b) in m.iter().enumerate().take(N/8) { + for (i, b) in m.iter().enumerate() { for j in 0..8 { let mask = -(((*b >> j) & 1) as i16); - w[8 * i + j] = mask /*as i32*/ & ((q + 1) / 2); + w[8 * i + j] = mask & ((q + 1) / 2); } } @@ -50,19 +54,19 @@ impl Polynomial { } /// Convert a Polynomial back into a message m - pub(crate) fn to_msg(mut self) -> [u8; 32] { - - const LOWER: i32 = q as i32 >> 2; // 832 + pub(crate) fn to_msg(self) -> [u8; 32] { + const LOWER: i32 = q as i32 >> 2; // 832 const UPPER: i32 = q as i32 - LOWER; // 2497 let mut msg = [0u8; 32]; // you would expect to use a full reduce() here, but since this is data coming from // out matrix math and not from an attacker, we can get away with the lighter cond_sub_q() - self.cond_sub_q(); + // Actually; further testing against the bc-test-data set of KATs shows that everything passes even with nothing + // self.cond_sub_q(); // for (i, item) in msg.iter_mut().enumerate().take(N/8) { - for i in 0 .. N/8 { + for i in 0..N / 8 { for j in 0..8 { let c_j = self[8 * i + j] as i32; let t = (((LOWER - c_j) & (c_j - UPPER)) >> 31) & 0x0000000000000001; @@ -98,18 +102,10 @@ impl Polynomial { /// Borrowed from: /// https://github.com/pq-crystals/kyber/blob/main/ref/poly.c#L290 pub(crate) fn base_mult_montgomery(&mut self, b: &Polynomial) { - for i in 0..(N/4) { + for i in 0..(N / 4) { let a1: i16 = self[4 * i]; let a2: i16 = self[4 * i + 1]; - ntt_base_mult( - &mut self.coeffs, - 4 * i, - a1, - a2, - b[4 * i], - b[4 * i + 1], - ZETAS[64 + i], - ); + ntt_base_mult(&mut self.coeffs, 4 * i, a1, a2, b[4 * i], b[4 * i + 1], ZETAS[64 + i]); let a1: i16 = self[4 * i + 2]; let a2: i16 = self[4 * i + 3]; @@ -162,12 +158,12 @@ impl Polynomial { // s.cond_sub_q(); match dv { - 4 => { // MLKEM512 and MLKEM768 - for i in 0..N/8 { + 4 => { + // MLKEM512 and MLKEM768 + for i in 0..N / 8 { // fill the temp array t for (j, item) in t.iter_mut().enumerate() { - *item = ((((self[8 * i + j] as i32) << 4) + (q as i32 /2)) - / (q as i32) + *item = ((((self[8 * i + j] as i32) << 4) + (q as i32 / 2)) / (q as i32) & 15) as u8; } @@ -177,13 +173,13 @@ impl Polynomial { out[idx + 3] = t[6] | (t[7] << 4); idx += 4; } - }, - 5 => { // MLKEM1024 - for i in 0..N/8 { + } + 5 => { + // MLKEM1024 + for i in 0..N / 8 { // fill the temp array t for (j, item) in t.iter_mut().enumerate() { - *item = (((((self[8 * i + j] as i32) << 5) + (q as i32 /2)) - / (q as i32)) + *item = (((((self[8 * i + j] as i32) << 5) + (q as i32 / 2)) / (q as i32)) & 31) as u8; } @@ -194,7 +190,7 @@ impl Polynomial { out[idx + 4] = (t[6] >> 2) | (t[7] << 3); idx += 5; } - }, + } _ => unreachable!(), }; } @@ -216,49 +212,47 @@ impl Polynomial { // if self.m_engine.poly_compressed_bytes() == 128 { match dv { - 4 => { // MLKEM512 and MLKEM768 - for i in 0..N/2 { + 4 => { + // MLKEM512 and MLKEM768 + for i in 0..N / 2 { v[2 * i] = - (((((compressed_v[idx] & 15) as i16) as i32 * (q as i32)) + 8) >> 4) - as i16; + (((((compressed_v[idx] & 15) as i16) as i32 * (q as i32)) + 8) >> 4) as i16; v[2 * i + 1] = - (((((compressed_v[idx] >> 4) as i16) as i32 * (q as i32)) + 8) >> 4) - as i16; + (((((compressed_v[idx] >> 4) as i16) as i32 * (q as i32)) + 8) >> 4) as i16; idx += 1; } - }, - 5 => { // MLKEM1024 + } + 5 => { + // MLKEM1024 let mut t = [0u8; 8]; - for i in 0..N/8 { + for i in 0..N / 8 { t[0] = compressed_v[idx]; - t[1] = - (compressed_v[idx] >> 5) | (compressed_v[idx + 1] << 3); + t[1] = (compressed_v[idx] >> 5) | (compressed_v[idx + 1] << 3); t[2] = compressed_v[idx + 1] >> 2; - t[3] = (compressed_v[idx + 1] >> 7) - | (compressed_v[idx + 2] << 1); - t[4] = (compressed_v[idx + 2] >> 4) - | (compressed_v[idx + 3] << 4); + t[3] = (compressed_v[idx + 1] >> 7) | (compressed_v[idx + 2] << 1); + t[4] = (compressed_v[idx + 2] >> 4) | (compressed_v[idx + 3] << 4); t[5] = compressed_v[idx + 3] >> 1; - t[6] = (compressed_v[idx + 3] >> 6) - | (compressed_v[idx + 4] << 2); + t[6] = (compressed_v[idx + 3] >> 6) | (compressed_v[idx + 4] << 2); t[7] = compressed_v[idx + 4] >> 3; idx += 5; for (j, item) in t.iter_mut().enumerate() { v[8 * i + j] = (((*item & 31) as i32 * (q as i32) + 16) >> 5) as i16; } } - }, + } _ => unreachable!(), } v } - pub(crate) fn cond_sub_q(&mut self) { - for i in 0..N { - self[i] = cond_sub_q(self[i]); - } - } + // not currently used, but I'll leave it here because it's useful for debugging if you want to output values + // that are normalized to [0,q] to compare against intermediate results from other libraries. + // pub(crate) fn cond_sub_q(&mut self) { + // for i in 0..N { + // self[i] = cond_sub_q(self[i]); + // } + // } /// Algorithm 9 NTT(𝑓) /// Computes the NTT representation 𝑓_hat of the given polynomial 𝑓 ∈ 𝑅𝑞. diff --git a/crypto/mlkem-lowmemory/tests/bc_test_data.rs b/crypto/mlkem-lowmemory/tests/bc_test_data.rs new file mode 100644 index 0000000..beb259c --- /dev/null +++ b/crypto/mlkem-lowmemory/tests/bc_test_data.rs @@ -0,0 +1,363 @@ +// Test against the bc-test-data repo +// Requires that the bc-test-data repository is cloned and available for testing at "../bc-test-data" +// relative to the root of this git project. + +// This whole file doesn't work because the bc-test-data repository only has full private keys and not seeds + +#[cfg(test)] +mod bc_test_data { + use bouncycastle_core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, + }; + use bouncycastle_core::traits::{KEMPublicKey, SecurityStrength}; + use bouncycastle_hex as hex; + use bouncycastle_mlkem_lowmemory::mlkem::{ + MLKEM512_FULL_SK_LEN, MLKEM768_FULL_SK_LEN, MLKEM1024_FULL_SK_LEN, + }; + use bouncycastle_mlkem_lowmemory::{ + MLKEM512, MLKEM512_PK_LEN, MLKEM768, MLKEM768_PK_LEN, MLKEM1024, MLKEM1024_PK_LEN, + MLKEMPrivateKeyTrait, MLKEMTrait, + }; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../bc-test-data/pqc/crypto/mlkem"; + const TEST_DATA_PATH: &str = "../bc-test-data/pqc/crypto/mlkem"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + #[allow(non_snake_case)] + fn ML_KEM_keyGen() { + let contents = match get_test_data("ML-KEM-keyGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = KeyGenTestCase::parse(contents); + + for test_case in test_cases { + test_case.run(); + } + } + + #[derive(Clone)] + struct KeyGenTestCase { + vs_id: u32, + algorithm: String, + mode: String, + revision: String, + is_sample: bool, + tg_id: u32, + test_type: String, + parameter_set: String, + tc_id: u32, + z: String, + d: String, + ek: String, + dk: String, + } + + impl KeyGenTestCase { + fn new() -> Self { + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + z: String::new(), + d: String::new(), + ek: String::new(), + dk: String::new(), + } + } + + fn is_full(&self) -> bool { + !self.algorithm.is_empty() + } + + fn parse(data: String) -> Vec { + let mut test_cases = Vec::::new(); + let mut test_case = KeyGenTestCase::new(); + for line in data.lines() { + let (tag, value) = match line.split_once(" = ") { + Some(pair) => pair, + None => { + if test_case.is_full() { + test_cases.push(test_case.clone()); + } + continue; + } + }; + + match tag { + "vsId" => test_case.vs_id = value.parse().unwrap(), + "algorithm" => test_case.algorithm = value.to_string(), + "mode" => test_case.mode = value.to_string(), + "revision" => test_case.revision = value.to_string(), + "isSample" => test_case.is_sample = value.parse().unwrap(), + "tgId" => test_case.tg_id = value.parse().unwrap(), + "testType" => test_case.test_type = value.to_string(), + "parameterSet" => test_case.parameter_set = value.to_string(), + "tcId" => test_case.tc_id = value.parse().unwrap(), + "z" => test_case.z = value.to_string(), + "d" => test_case.d = value.to_string(), + "ek" => test_case.ek = value.to_string(), + "dk" => test_case.dk = value.to_string(), + val => panic!("Invalid tag: {}", val), + } + } + + test_cases + } + + fn run(&self) { + assert_eq!(self.mode, "keyGen"); + + let mut seed_bytes = [0u8; 64]; + seed_bytes[..32].copy_from_slice(&*hex::decode(&self.d).unwrap()); + seed_bytes[32..].copy_from_slice(&*hex::decode(&self.z).unwrap()); + + let mut seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); + + // for the purposes of the test cases, accept an all-zero seed + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); + + match self.parameter_set.as_str() { + "ML-KEM-512" => { + let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLKEM512_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLKEM512_FULL_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode_full_sk(), sk_sized); + } + "ML-KEM-768" => { + let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLKEM768_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLKEM768_FULL_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode_full_sk(), sk_sized); + } + "ML-KEM-1024" => { + let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + let pk_sized: [u8; MLKEM1024_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); + assert_eq!(pk.encode(), pk_sized); + let sk_sized: [u8; MLKEM1024_FULL_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); + assert_eq!(sk.encode_full_sk(), sk_sized); + } + val => panic!("Invalid parameter set: {}", val), + } + } + } + + // Doesn't work here because the bc-test-data doesn't include seeds + // #[test] + // #[allow(non_snake_case)] + // fn ML_KEM_encapDecap() { + // let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-KEM-encapDecap.txt").unwrap(); + // let test_cases = EncapDecapTestCase::parse(contents); + // + // let num_tests = test_cases.len(); + // for test_case in test_cases { + // test_case.run(); + // } + // + // println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); + // } + + // #[derive(Clone)] + // struct EncapDecapTestCase { + // vs_id: u32, + // algorithm: String, + // mode: String, + // revision: String, + // is_sample: bool, + // tg_id: u32, + // test_type: String, + // parameter_set: String, + // function: String, + // tc_id: u32, + // ek: String, + // dk: String, + // m: String, + // c: String, + // k: String, + // } + // + // impl EncapDecapTestCase { + // fn new() -> Self { + // Self { vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), function: String::new(), tc_id: 0, ek: String::new(), dk: String::new(), m: String::new(), c: String::new(), k: String::new() } + // } + // + // fn is_full(&self) -> bool { + // !self.algorithm.is_empty() + // } + // + // fn parse(data: String) -> Vec { + // let mut test_cases = Vec::::new(); + // let mut test_case = EncapDecapTestCase::new(); + // for line in data.lines() { + // let (tag, value) = match line.split_once(" = ") { + // Some(pair) => pair, + // None => { + // if test_case.is_full() { test_cases.push(test_case.clone()); } + // continue; + // } + // }; + // + // match tag { + // "vsId" => test_case.vs_id = value.parse().unwrap(), + // "algorithm" => test_case.algorithm = value.to_string(), + // "mode" => test_case.mode = value.to_string(), + // "revision" => test_case.revision = value.to_string(), + // "isSample" => test_case.is_sample = value.parse().unwrap(), + // "tgId" => test_case.tg_id = value.parse().unwrap(), + // "testType" => test_case.test_type = value.to_string(), + // "parameterSet" => test_case.parameter_set = value.to_string(), + // "function" => test_case.function = value.to_string(), + // "tcId" => test_case.tc_id = value.parse().unwrap(), + // "ek" => test_case.ek = value.to_string(), + // "dk" => test_case.dk = value.to_string(), + // "m" => test_case.m = value.to_string(), + // "c" => test_case.c = value.to_string(), + // "k" => test_case.k = value.to_string(), + // val => panic!("Invalid tag: {}", val), + // } + // } + // + // test_cases + // } + // + // fn run(&self) { + // assert_eq!(self.mode, "encapDecap"); + // + // let mut seed = [0u8; 64]; + // seed[..32].copy_from_slice(&*hex::decode(&self.).unwrap()); + // + // match self.parameter_set.as_str() { + // "ML-KEM-512" => { + // match self.function.as_str() { + // "encapsulation" => { + // let pk = MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + // let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); + // let (ss, ct) = MLKEM512::encaps_internal(&pk, m); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // let expected_ct = hex::decode(&self.c).unwrap(); + // + // assert_eq!(ss, expected_ss.as_slice()); + // assert_eq!(ct, expected_ct.as_slice()); + // }, + // "decapsulation" => { + // let sk = MLKEM512PrivateKey::from_bytes(&hex::decode(&self.).unwrap()).unwrap(); + // let ct = hex::decode(&self.c).unwrap(); + // let ss = MLKEM512::decaps(&sk, ct.as_slice()).unwrap(); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); + // }, + // _ => panic!("Invalid function: {}", self.function), + // }; + // }, + // "ML-KEM-768" => { + // match self.function.as_str() { + // "encapsulation" => { + // let pk = MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + // let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); + // let (ss, ct) = MLKEM768::encaps_internal(&pk, m); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // let expected_ct = hex::decode(&self.c).unwrap(); + // + // assert_eq!(ss, expected_ss.as_slice()); + // assert_eq!(ct, expected_ct.as_slice()); + // }, + // "decapsulation" => { + // let sk = MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); + // let ct = hex::decode(&self.c).unwrap(); + // let ss = MLKEM768::decaps(&sk, ct.as_slice()).unwrap(); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); + // }, + // _ => panic!("Invalid function: {}", self.function), + // }; + // }, + // "ML-KEM-1024" => { + // match self.function.as_str() { + // "encapsulation" => { + // let pk = MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + // let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); + // let (ss, ct) = MLKEM1024::encaps_internal(&pk, m); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // let expected_ct = hex::decode(&self.c).unwrap(); + // + // assert_eq!(ss, expected_ss.as_slice()); + // assert_eq!(ct, expected_ct.as_slice()); + // }, + // "decapsulation" => { + // let sk = MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); + // let ct = hex::decode(&self.c).unwrap(); + // let ss = MLKEM1024::decaps(&sk, ct.as_slice()).unwrap(); + // + // let expected_ss = hex::decode(&self.k).unwrap(); + // assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); + // }, + // _ => panic!("Invalid function: {}", self.function), + // }; + // }, + // val => panic!("Invalid parameter set: {}", val), + // } + // } + // } + // } +} diff --git a/crypto/mlkem_lowmemory/tests/mlkem_key_tests.rs b/crypto/mlkem-lowmemory/tests/mlkem_key_tests.rs similarity index 85% rename from crypto/mlkem_lowmemory/tests/mlkem_key_tests.rs rename to crypto/mlkem-lowmemory/tests/mlkem_key_tests.rs index 01e708a..b370ec2 100644 --- a/crypto/mlkem_lowmemory/tests/mlkem_key_tests.rs +++ b/crypto/mlkem-lowmemory/tests/mlkem_key_tests.rs @@ -1,27 +1,36 @@ #[cfg(test)] mod mlkem_key_tests { - use bouncycastle_core::key_material::{KeyMaterial512, KeyType}; - use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, KEM}; - use bouncycastle_mlkem_lowmemory::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait, MLKEMTrait}; - use bouncycastle_mlkem_lowmemory::{MLKEM512, MLKEM768, MLKEM1024}; - use bouncycastle_mlkem_lowmemory::{MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEM1024PrivateKey, MLKEM1024PublicKey}; - use bouncycastle_mlkem_lowmemory::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN, MLKEM_SS_LEN}; + use bouncycastle_core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength}; use bouncycastle_hex as hex; use bouncycastle_mlkem_lowmemory::mlkem::MLKEM512_FULL_SK_LEN; + use bouncycastle_mlkem_lowmemory::{MLKEM512, MLKEM768, MLKEM1024}; + use bouncycastle_mlkem_lowmemory::{ + MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM1024_PK_LEN, + MLKEM1024_SK_LEN, + }; + use bouncycastle_mlkem_lowmemory::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, + }; + use bouncycastle_mlkem_lowmemory::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait, MLKEMTrait}; #[test] fn core_framework_tests() { - use bouncycastle_core_test_framework::kem::{TestFrameworkKEMKeys}; + use bouncycastle_core_test_framework::kem::TestFrameworkKEMKeys; let tf = TestFrameworkKEMKeys::new(); - tf.test_keys::(); - tf.test_keys::(); - tf.test_keys::(); + tf.test_keys::( + MLKEM512::keygen, + ); + tf.test_keys::( + MLKEM768::keygen, + ); + tf.test_keys::(MLKEM1024::keygen); } #[test] fn pk_from_sk() { - /* MLDSA44 */ let seed_bytes: [u8; MLKEM512_SK_LEN] = hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap().try_into().unwrap(); let expected_sk_bytes: [u8; MLKEM512_FULL_SK_LEN] = hex::decode("70554fd436344f2785b1b3b1bac184b6679003336c26f15a7de878c4825c6be03f3c4a480f75b7486aad31d3a00518623fd207ab528dd62721495835ae0062c367b74a71baf10aad0e8a2902076be31348beb15ccc0957cdebb4aff226756bbc601b6568ab784acbaeb34702f0f86a26202118b22b23f83558776c79c14dba983379c803e0dcc3160a11757030e69c6919798d81eb698a9a4483a99e5a5cb2c31c9a661799f3cc89c790706ea041629045d42a83aed88860e394c69187e2105d28cc14ec393592d67dd00aa43fe8b4eae4414002866b5c713c6a8d7d16cf78b819d6f12e9e5a74233908f0b15e3c4ba8329c5cdda55c84928e3aa8063e5aa9676403f91735b11010c7f593091364dc86445bc804840a9a21724212469f8a7b0ce0ac698eb86cad39a7f4824d9a5163aac21ee6808b053c8a3facb0b6744b5262bbcb26a43f664c8732b64cfc7acf099605f41c796060976ac433833fe00343fb1828300a424741116e4b45bb276ea81129a0db4c6e60bce611101e8c625474925e0222679308a3e7708d1972a7b423eb232851c36d2ed53d3ed3bb7500637061a5dc2292fa1c466c07354683328bec2c1ed2cb5c99b78eca0969038cf7c34dd118724e31cae086206b34302b520f5d177aded5b3cce02acce808ea26bcc072625fdb93f17458a5fc1d4da394380a1f57e9cc66109438a075f0d2813fcc4a199cc76db3823f270b0061594192940411a37ffbafae2c150165cec5c6bf73c595fb92cd15312607da070778652bd9944bc48bc7d1a534338bad0bad6656c5d502ce7850ab1587244eeb58f439ab5e08574a718c8aac3d77c798bba1542733be73448f23fb70c0e5353a27c88322c5218493afbb38086434d6d60a56ba887dd498c3ab26a0870993815aa6a40975f218adca1582d64ffc8652fbb3a9a6fbc304f91945fa4aaef2878fd715df70113d2379f44886f812c83ff2b719a69e1ec74ae4b15accd3aed5a53ce76a7b0982471633b973cb40a1a0015d0a424fa11a479c023017436d2a2900e993eb5a0a067400c7f4aadf201fc4fa31264a63bae95cc8d65c3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() @@ -40,7 +49,6 @@ mod mlkem_key_tests { // Also test the export to fully expanded sk assert_eq!(&sk.encode_full_sk(), &expected_sk_bytes); - // Decode and re-encode the pk, make sure you get the same thing let decoded_pk = MLKEM512PublicKey::from_bytes(&expected_pk_bytes).unwrap(); let pk_bytes = decoded_pk.encode(); @@ -58,12 +66,16 @@ mod mlkem_key_tests { // 3) does it reject a private key if the H(ek) is wrong? let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (_pk, mut sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); let expected_pk_bytes: [u8; MLKEM512_PK_LEN] = hex::decode("3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c").unwrap() @@ -73,7 +85,11 @@ mod mlkem_key_tests { // generation of KAT // let h_ek = pk.compute_hash(); // println!("H(ek) for public key: {}", hex::encode(h_ek)); - let expected_h_ek: [u8; 32] = hex::decode("82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea").unwrap().try_into().unwrap(); + let expected_h_ek: [u8; 32] = + hex::decode("82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea") + .unwrap() + .try_into() + .unwrap(); assert_eq!(pk.compute_hash(), expected_h_ek); assert_eq!(sk.pk_hash(), &expected_h_ek); @@ -82,12 +98,16 @@ mod mlkem_key_tests { #[test] fn encode_decode() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk1, sk1) = MLKEM512::keygen_from_seed(&seed).unwrap(); let pk1_bytes = pk1.encode(); @@ -106,22 +126,35 @@ mod mlkem_key_tests { #[test] fn seed() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); assert!(sk.seed().is_some()); assert_eq!(sk.seed().as_ref().unwrap(), &seed); + + // When you pop the seed out, its SecurityStrength will match the ML-DSA algorithm + let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_128bit); + + let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_192bit); + + let (_pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_256bit); } #[test] fn test_eq() { - // MLKEM512 let (pk, sk) = MLKEM512::keygen().unwrap(); @@ -144,7 +177,6 @@ mod mlkem_key_tests { bytes[17] ^= 0x01; assert_ne!(sk, MLKEM512PrivateKey::from_bytes(&bytes).unwrap()); - // MLKEM768 let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -167,7 +199,6 @@ mod mlkem_key_tests { bytes[17] ^= 0x01; assert_ne!(sk, MLKEM768PrivateKey::from_bytes(&bytes).unwrap()); - // MLKEM1024 let (pk, sk) = MLKEM1024::keygen().unwrap(); @@ -198,7 +229,6 @@ mod mlkem_key_tests { let (pk768, sk768) = MLKEM768::keygen().unwrap(); let (pk1024, sk1024) = MLKEM1024::keygen().unwrap(); - /*** MLDSAPublicKey ***/ // fmt @@ -221,8 +251,6 @@ mod mlkem_key_tests { let pk_str = format!("{:?}", pk1024); assert!(pk_str.contains("MLKEMPublicKey { alg: ML-KEM-1024, pub_key_hash:")); - - /*** MLDSAPrivateKey ***/ // fmt let sk_str = format!("{}", sk512); @@ -244,4 +272,4 @@ mod mlkem_key_tests { let sk_str = format!("{:?}", sk1024); assert!(sk_str.contains("MLKEMSeedPrivateKey { alg: ML-KEM-1024, pub_key_hash:")); } -} \ No newline at end of file +} diff --git a/crypto/mlkem_lowmemory/tests/mlkem_tests.rs b/crypto/mlkem-lowmemory/tests/mlkem_tests.rs similarity index 92% rename from crypto/mlkem_lowmemory/tests/mlkem_tests.rs rename to crypto/mlkem-lowmemory/tests/mlkem_tests.rs index 328c90c..7c37132 100644 --- a/crypto/mlkem_lowmemory/tests/mlkem_tests.rs +++ b/crypto/mlkem-lowmemory/tests/mlkem_tests.rs @@ -1,15 +1,30 @@ /// This performs tests using the public interfaces of the crate. #[cfg(test)] mod mlkem_tests { - use bouncycastle_core::errors::KEMError; - use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial512, KeyType}; - use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength, KEM, XOF}; - use bouncycastle_mlkem_lowmemory::{MLKEM512, MLKEM768, MLKEM1024, MLKEM_RND_LEN, MLKEM_SEED_LEN, Polynomial}; - use bouncycastle_mlkem_lowmemory::{MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEM1024PrivateKey, MLKEM1024PublicKey}; - use bouncycastle_mlkem_lowmemory::{MLKEMPrivateKeyTrait, MLKEMTrait}; - use bouncycastle_mlkem_lowmemory::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN, MLKEM512_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN, MLKEM_SS_LEN}; + use bouncycastle_core::errors::{KEMError, RNGError}; + use bouncycastle_core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, + }; + use bouncycastle_core::traits::{ + KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey, SecurityStrength, XOF, + }; + use bouncycastle_core_test_framework::FixedSeedRNG; use bouncycastle_hex as hex; - use bouncycastle_mlkem_lowmemory::mlkem::{MLKEM1024_FULL_SK_LEN, MLKEM512_FULL_SK_LEN, MLKEM768_FULL_SK_LEN}; + use bouncycastle_mlkem_lowmemory::mlkem::{ + MLKEM512_FULL_SK_LEN, MLKEM768_FULL_SK_LEN, MLKEM1024_FULL_SK_LEN, + }; + use bouncycastle_mlkem_lowmemory::{ + MLKEM_RND_LEN, MLKEM_SEED_LEN, MLKEM512, MLKEM768, MLKEM1024, Polynomial, + }; + use bouncycastle_mlkem_lowmemory::{ + MLKEM_SS_LEN, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768_CT_LEN, + MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, + }; + use bouncycastle_mlkem_lowmemory::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, + }; + use bouncycastle_mlkem_lowmemory::{MLKEMPrivateKeyTrait, MLKEMTrait}; use bouncycastle_sha3::SHAKE256; // #[test] @@ -32,13 +47,13 @@ mod mlkem_tests { #[test] fn core_framework_tests() { - use bouncycastle_core_test_framework::kem::{TestFrameworkKEM}; + use bouncycastle_core_test_framework::kem::TestFrameworkKEM; let tf = TestFrameworkKEM::new(false, true); - tf.test_kem::(false); - tf.test_kem::(false); - tf.test_kem::(false); + tf.test_kem::(MLKEM512::keygen, false); + tf.test_kem::(MLKEM768::keygen, false); + tf.test_kem::(MLKEM1024::keygen, false); } /// This runs the full bitflipping tests and takes about 1.5 mins.. @@ -58,16 +73,15 @@ mod mlkem_tests { fn rfc9935_keygen() { // note: same seed for MLKEM512, MLKEM768, MLKEM1024 let sk_seed_bytes: [u8; MLKEM_SEED_LEN] = hex::decode( - "000102030405060708090a0b0c0d0e0f + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap().try_into().unwrap(); - let seed = KeyMaterial512::from_bytes_as_type( - &sk_seed_bytes, - KeyType::Seed, - ).unwrap(); - - + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + let seed = KeyMaterial512::from_bytes_as_type(&sk_seed_bytes, KeyType::Seed).unwrap(); /* MLKEM512 */ let expected_full_sk_bytes: [u8; MLKEM512_FULL_SK_LEN] = hex::decode("70554fd436344f2785b1b3b1bac184b6679003336c26f15a7de878c4825c6be03f3c4a480f75b7486aad31d3a00518623fd207ab528dd62721495835ae0062c367b74a71baf10aad0e8a2902076be31348beb15ccc0957cdebb4aff226756bbc601b6568ab784acbaeb34702f0f86a26202118b22b23f83558776c79c14dba983379c803e0dcc3160a11757030e69c6919798d81eb698a9a4483a99e5a5cb2c31c9a661799f3cc89c790706ea041629045d42a83aed88860e394c69187e2105d28cc14ec393592d67dd00aa43fe8b4eae4414002866b5c713c6a8d7d16cf78b819d6f12e9e5a74233908f0b15e3c4ba8329c5cdda55c84928e3aa8063e5aa9676403f91735b11010c7f593091364dc86445bc804840a9a21724212469f8a7b0ce0ac698eb86cad39a7f4824d9a5163aac21ee6808b053c8a3facb0b6744b5262bbcb26a43f664c8732b64cfc7acf099605f41c796060976ac433833fe00343fb1828300a424741116e4b45bb276ea81129a0db4c6e60bce611101e8c625474925e0222679308a3e7708d1972a7b423eb232851c36d2ed53d3ed3bb7500637061a5dc2292fa1c466c07354683328bec2c1ed2cb5c99b78eca0969038cf7c34dd118724e31cae086206b34302b520f5d177aded5b3cce02acce808ea26bcc072625fdb93f17458a5fc1d4da394380a1f57e9cc66109438a075f0d2813fcc4a199cc76db3823f270b0061594192940411a37ffbafae2c150165cec5c6bf73c595fb92cd15312607da070778652bd9944bc48bc7d1a534338bad0bad6656c5d502ce7850ab1587244eeb58f439ab5e08574a718c8aac3d77c798bba1542733be73448f23fb70c0e5353a27c88322c5218493afbb38086434d6d60a56ba887dd498c3ab26a0870993815aa6a40975f218adca1582d64ffc8652fbb3a9a6fbc304f91945fa4aaef2878fd715df70113d2379f44886f812c83ff2b719a69e1ec74ae4b15accd3aed5a53ce76a7b0982471633b973cb40a1a0015d0a424fa11a479c023017436d2a2900e993eb5a0a067400c7f4aadf201fc4fa31264a63bae95cc8d65c3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() @@ -111,11 +125,10 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM512::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } - /* MLKEM768 */ let expected_full_sk_bytes: [u8; MLKEM768_FULL_SK_LEN] = hex::decode("27d2a77f33756f61208ef113abe82595873d4abc730e5b5d679529bf6a4ceb6383427231a8612f41550515acba52e48ead8b942833bbe6865d13d14a79d2c5c3e07f0a056d8de7aadfcaba058c493c80b37cab8c562753bb3ba6b6ec8297f885eaa7540d530015a84406e55b1366b577e236ce58a26d8a1eb5a44d542323c2167d9bf4a47f985699ca05bae43b8dec617f02380a3890afd4b8c7ec7ede26553a025f3ce5bc5d7a62130304235cb1ad4836b566b5b863bd9bdb45a2844a7047b6c8d383e448525e040b4dc8a2b48c6c37c96d62d43f3fd88e2881c40a205c9e248f652b592781a779f86880f2a147b67863f391cc1a5a908c0095e07212291e2ef8a36eb9a9c0c6073225b34703a4af049382c47573da68fde9245ad444e31b1fbdb521f1f61f37bc0cef292067e670d28a1ffd904f6f1190a996918a13037a6cabf3c373bf8296cd37ab33ba7746809cc3f8ade1b3639bd57bfcc69650aaaf1de198fc4c0463299e52c461780cc428fc5d04a5c51850cba6c2a5274340675793dda09be44c29e6395c65f85d2a0a7c6df411e6911b1f2cb6c351cd2e875f51b638be776097e93e2f2b2f83da0beef4aa85ba9e763ab64502a0ca5222e9eab5b3b7088ed52060e8c8269b943a71ab0ae1c5b1b687d2e019cf8036bcf9bf6e7bac3aaa36e41660faa4540f2648cd93a189ec5c2dea70bacaaa4ffc906f90810ea1b67bf24f2c78cf6ba881aaea61c0652bff95b1bae4426d1773b9cc2ca82c21e38c636e3b1c523244986b0be8a83f5dd5cf2d54762fb3c5ebf59b8e885302b1ce47033edf760f4e029be40b6d566b19dd758acd5c7412878131244f90172c53f26663c21d905301d48baf91c917cc7779e9d8802cc10d89a3705099a2ad3a3a8896743c1144698093be257dacb66dc785228b912c8d965d14aa28342c3ac4a93fefa532b20945ddc1020139c14d638b908c4ddde9a0645b95b2e4414d40bb79f04413830f15a873c28bb7059c2741002015f20408f058e715b0bf995b5380b7dd325a056ab97e659a2be0cdf6c33731c683a634b771e8c92a139aee4bb0e49c7077321d42fc199f7c1f298ca625d223a5c263a03cc48159b7812665b78637e4e18720b2c29a6b99f42766a4cbc4dc508ba94ba83b89c3a5c78f8bb26bbd9b79beb8c8182490f5793ee5b96013b74b7e169e29d162f1315464ea7d72436d89b755161192c81cc2dd1c8b8bba795ef426ee1cc01c37aaa37b2cff8b0a378b47cbd0b4d49398cfc2712959699fa0bd8cd84666acc61f541b84fa96b9c854e4e75e9144addb44b8566a57dfbb545ce423c03346f2b2c1a91780d152a8de1a4d4c9cacde7392c996888cc2399c02c38b3353adf8acab283924da00a05b76e738c72c930d6cba09ae168990faa1fef2226e780861d416eff402f4f759fc648ab1f97100109087f96e4b148d2cb31e4805314ea0cd95fb023eac0d989474ba4201d7b41d26f5394b217eea5b34b71a8b37931c0e594271e0b7c733257240233e7ba735603e425a87dee77079e37cb28a21764594ce5350d8da2b62a07174943032ec89c98809c73b6423d30c1d283a766a64d89703c3d629b497828d48320c346210797a298aa10d423c8dda069d02bc59e6cdf03a096b8b3da4cab9b80ca4a14907672ccef1ec4faf234a0bc5b7e9d473f2b3133b3b26a1d175cb67a7805919699c02f76531b99c5f89180704bb4ca4535c5b8972679c660a07c5e514b87009c862eb8f5157695efb3fc40a9def6b81c1cc02a249ae4f094ad0d9bd3485c1c1c68080520a7c8c632032cee738154e5c5176c07da56024776a430fe76eacf665a3f7b832102215bc82f10939c8355704336a8fac1d81e4bb0485aa5d7c74d6b59bbe5c5e972a0d8bac411b55b5d5557cd680a1a8f71b4eb86bc48c9a0509731a54bd9d7290b27963e4372dc9b199cfdcac0b01acd28a62395112e4c43648d622c48c8234d01440e8cc376c927f23a5afc9ac0474c662274e424525c8552ece3b3fe26516de901bc7d515bde89558e626c95c80b93342f8010004f39e6c6c94871c5e344cab3966c835f9a96a59afd31c40286b38b1c1a78470bab947518934453ce86736a919f1f5a6d510a86f5454fc3980cb5c765bd2bd5f7b36b1410d6635c8ceb47c4dda0d76a28eac939c71c3024804866c71626658442163c2c22117e50acefce6378a985652302a4ef0c2ce0cc716b7796e2b6b2e3777dfa1ac3da259a31b5a9b530f8cb638a81a62ac301849abaf95a7301bda30068909bfdb7e67dbccbb38a5551a25b1a3a0f685748ad5753d8880f0016c627486166384c5571fe2365900364d038311e2d875db366686932b5ec602430a369e87a6ef5c338786657825bd4c057aceb923eb0935e6905e63b4ced7f80857a773dd64b150d26612ea9ac12052db2017bf1843ccb4b3281b690dc728adfa85c00281b8e3c09287335f856b4fc2892f69a2f57921ada01914c40988662d57769662a786351b9b66493dab79594d986de2100d65ba0ff4ea58b81538d24a4435a258fac25404aa7f41f658b1385065e158dcb60115732720f40459aaac15e406953a90ac52997d1ccd070060efc65db9e653354467fad56ec713c86e7540c423acf2669f52fa6f4ac6888d871ef3e847c029a8aafbb92e17b24aa079b1f419ba6175b442afb11909d4a56b70a0335b28739218aa7c9348e2c3c2f3eb3d15a41e6417c0dd94bfeb21419b311a7bb13a180bbe833218a9a6b17447cc85f225859587a73077049acbcfd44d0f025438e15d1538270d586e1bf83192a9459cf63c0e972f85297679831ecf121509851cb8340f6f107b0fa1a0efd1b36a8189bc085c4f5cb784e553f41b918f80397ce1956f785bee377ca9aa8be6998ada30c26b7c3d8c6b55254cc96203b20c42aee0ac4e1ebb408e49a9e3f879d0ab0785eb7025425d1305a2299c015e120d163b0e19494ce57253d0246d182745cb8197ab7438b3c1bb7972bec5a306eba3567855c014699fef65ae54c770a0d85c18400cf642aedc660777ba4b138502bd5a7812f621f84a48296b98dd4322b6f15828b8a8f0e00a8ba44a53c3a8b143571b0740abd567daf1cde9c79c204b6d5e259d1766a31bbbcb4e6a05cf4502176b301c1c2f41247750157bcec85e809b30a4d60d7747cdd0f5b99aa8c826987517793aaa8080a0b124a8558df72bbe37b75f4edbb6be8216d6c633fb2b2280e25113d8695e43481c3eeb397eb192505229b67a201ea893c3e2cb32da8bc342fa4dea0578a24e16d8f8f9383a95b77050f4d9fd2f5733eec1d63ef3c23ebf9918173669a7202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() .try_into().unwrap(); @@ -158,11 +171,10 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM768::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } - /* MLKEM1024 */ let expected_full_sk_bytes: [u8; MLKEM1024_FULL_SK_LEN] = hex::decode("f77b7f6b15c73fe2cc546b67fb774ca19b42cd463ea9fbb984ca477a77b6c71087cbf051abe4736a9072c6e870c8311c55963f500a3c7b1b8f2a58558f49c62527b6c594b5e7acb3bcf597273a5743517d151208bd4aa61e75ba67b0bd594a994919627ac0a804d489e171336bc339f4666706e5134412b366823d50318c8bf261ab120a28a04fec01cc15f2b71912cee54aa8eed854694b6ba886b5eb7661e6d56aac213cc1d814d592b395554fae74476d34371163129bf864527250606cc21a53746b20997077bba155733b28a4e7fa0776399524763eb481ceaa11366c3474a04685f40c3f08b0424f40bff949a0ac92704c3ba0c6eb36f1f5b621d8bf2b6327beb57cd3facb94186fe3fc9ab0a1434bb291d2c9bb70723057e2254059656f565919a32cf74579de89681cd2c5a935a52b4aaa2d24cb5d5c9e20729ec5492ec36961efb8a28cbc00ac303523295f3d8036abc1603307ce70d7848a35657a5687dd589927ea63731626abb26ec4e431b8eb6b3b0bc1e82573ee73b1a021183183528108ae2eacaddb95b464a0b98469c319cc27bfa01bc31054a68c05502b1662b879fe98a1711c3426f6436cb0214cea379ac3a7e5fb60184a37c1da1eda61c6c39c1dd4e847845811f2a358a43731528536d4a3291b04158c2c3dc641624882678bc7805f58a9d94c7104567846a2044e65aece2a225372b6024799a5477d60237504aa5c0ac57bc70a3558c08c4de687ef1302b4fcb5594413d22cb959bc31be423450403c6bc57dc411b3fefac1052ac4bb162c44545a4ca80892657fa13a0b2c482ced629cc4999d969c593d4aadf073cc3e3a458e78a8aa039408e652be93b20c8b42ec5b0e50239dac726052851a6d15312ec39ed208b72209a577c6b2770112895749d5260e7dd446c0b0118c1000be6801d2611fcf00792a9cc4f4b49922f9a2d4b9c8fa5a5d0d60506631a7e971cee840b08fa63c13729d7ea5aac70352a984cdb669331cba758fe87ec3931b3e3161fcc747aa749424689feae14bf7c9a2ffba1302b212b80372d8e9049db69a3a1261d0a2859a9b4d57899e0ba41607a1b67a7c0e12923689f8c6395377d970c7490a4129611a1d05c3b7813bed945420723f7f9525a87793fafbbfca982e66bb80681c83248a89da084c19882f48f31e7fc09093a49e9fd09691b021edf463afc519b62853816118346115fb0b882cc6482f3c5cbcc1c1894697e1239598b34b2a9a7acd15244d0690c88194097a9beda585e87c437124624c210768e6215d376482653eb89947877c118d370c696a6ffcc1018ae413a08a8d0ffaa819945da7a167c229913290cad1c80a369258762610ea253e62dc24226a30c892c12136c326f13f4446664712b0b90bc063b4028593cbde06cdc22289e240c7e296b59172c1aeda8c99e0512d1a0163a942ea33148e6937c026029424b81b996b1df22ea0623ec65c6bf093500cf3bf35374adc392035ca7c583b99685bca541a0807b163acd0888be0385dea820da46e4dbb44d2e462c734b83a473fed1364273159257cc259a8c5676c1c76d41d56b9907ec1c3599c9e8907403a27a705e3619b04b0ad046e8ec8169c17b460d44c0c0c4464d044c946186bc725965083a892bcc495c0540311ff9b3e5192c303d88f8ba46a901c782ef02388f1b2addab6a5350fc3639700e3154337337e4a178d351cd2b56ee1f0bfea34aacfa33d2ec791e50752d4d034cb1c951572caaa5c4d90947b6b175a6dd3c62a77bb8f7ac9ae24719b53c2b120a2876986e217b72bd7cee44a7265b11cee1ab2261762b31a3738386969c0825fb79452e652e1142fc73c9df6fba411795b4717922b29ba2d53abe5a8c0dcc1601b096c96d7938fd5a68a8797c7b9477a86a472eb5da250cb2fec318d83c8f43bbe8e11c35e377d349366c85c4382597f6fc27a0051c0fb00b02c01ca20f9a427f172599477ca690cc1327e0f025f80ec338a80a159e308c12a27db1a7e1b960a99d37dfc22872e51930f28c651ab221f53abaee20bad9a3eabcbab913251bf135beb29617b5754333c4daadb2238341c2ad9378186280f6449440b784ba78f5dac44d8f65b3b7421950397c3913a2dd23ec6d1cb717b36a5fc95af191e278296948c1254ea86b4ec004b94c29450111191823b3514c9ac1ea3d9825ccb86393a2dfb04654fa2192d37bfad1c497c6502eee5ca80a73bfce0baf5a54a88585a401397a3d232f426a7afb082bc21a44317090eaac7592c2ea88a653c4491ea193931335f52e989a3c4cc56d9c553732d57c470fb41ab759b65d2d04445382fcd9c4e344a1128fa9e11e04358e192ed014b23232a7ee2b22e23717f44111ee33575399c37646da9813ec9b212afe94e5dc5c2330a7294cc1f4234a6d3fbb4f1685ab8892c04acb17cd1c170d7b0611b6a7176c794cc8c67f55fc923c2ad203100f365991882c30243d77813843b5ec7c964032263706092ecf00c7516be64e4598ca4226c069bb5e67e4175cf2286c8dd5c488a6c5861f31baa0bd0269470e8b551dd3bcd38c86c12f9cdb176c77dc8b6c02a701f478902c8553f694c0d82727b4c4a5c2c1041212aa1274808b82111b377ec75214e9b1978f76004d4139d98613f4b8e98d20af7b534073a509a959b7a7564f9b40ca218bf61829320a8502017954d328d7ac6c769ec29700756e7b0685b340d5e118059504a49a9a50a10198eb10a5784678eb427d7b4babb9552933b062897973e1318eaf0a0eac37584a65401b1703e042accd837531483f241cadcd1c1d378119e694429db199ac891e4c5343757085bb3ae783667350c4458d97672e861e80b1d2679510ea3a6f2360c77a46942c7a06a554d228080c84b47aef14db17620cb16c06ab30a1be4cda7082be9f87e9c211c46916349a5ba8eaa5201c7294a3c0885b53b657452108825ec646c90a04612324ee7d031afe5343132cbef67b6efb1a5ec2809b773538ce77b3d8b04eb0b3c2256011e4c716c19a8ba0752bf71492117649f0615c3290fc29a46fde4bd52db9286d603388244259c15a7ac2b640a60cc03376a5841a3fb8a473568fa9b1a267215f34c01697b0f0e627175d72105b7707c29b9e614bdc33a6f6c818a95370b427882d7b476796a9ec6eb993274cd9b2391a82ba45e3393d2e9ae9721ca9d6c1b988b5827713f90a6585de9433528c02b03ce10bb5f720138d0fbb4c30c1266b918e52925dfe17b37f95d22bca54f475919ac859098c0f0d08ac5875ef29b56fd141e6ef15f700a0b66f39595c588177373c4669b21bc071e4c3aa5f0b4a31b6258f35da24ac3cd29c7f2092410c5078355b138fb53a6b9ae6e0b9c08243e7baa45c47376eb8c7f13d4cf51aa736fa31540c9241f370da544bf9f9c28d9a57e2f2a7ca95a4e4b466e641ab3bcc76adf1139d567a6f12b52f3a65e7ec0aae26bcaa8c55833b04e59998ebc9a1930fbb6d2233c53d2c1f8b9518e3c2de73a19dee6b380a5b32971cf64e129fd6c1fa6e75d4a234501e966dd3a540af5c8f4f34a6b4a253ee28492566d5e67c6f55855fcb0506fb06c156744d9a03a31a26fa94cad14f157b7f303d07a69c773768fcb4d079c09059703a0c3a94de4b99ea3a2f16583d0f9170a3950db07b4f0bc30802927f9f7961b6259892636a9502a2705303637799dd344da451c1cf7bf67840ceb3079ab8c6b8c1927f64053c612450c45c9e603bc16666e596b3471e103b6f15447424d17022048111ffbd37e1c670f64f14b8a7b32b94c1a49b45dd2fc38cd5289d910ad63602cf5e13042c64ac6797b89fb551ad08e05a92d200cccb7e712ef23c9312cb350f029ab537e287347fd3075ac10906a783f1c6c07ccb88f41228c4be1c640f790b5c3a5d5d3ca792495d74bc461562658c07ac600276b924ab5bc9be1f0494cb76f82f460a7480972663381e169996061d799859ec54d4f5ca5c411c01db1597b165977669de13a928a34afbac258fea8c4764239c9421dc3119bf5b47699206978327b1c5345ef746a7983841f056e2534100ab24d4e9abbd0b17c6a95bd4c3c0e40f69e1612aceeb28b99086c95116e7204273893390bf46b899b36286b0ebf1947bb9884f732ca27da82b19b5dc0cc7f8885714910888b2310c4f9319d410b34e6433b9003e2176bb995257456106e8952163b8ba592530cc5aa0aeb43ad398fe9e97baa523d7a4431677c3d3af0719e475db85ca95af5089beabeb05b2faab4896ba60f81c88472a57b46a828826a0cdfb446f8189182d2bf5eac4ec1cc5deaf599c8a13e48235406d17ffddc8344b6c66984a868aa92fa02227a086950eb0c8701ed58dc628776b983882e117561349e5c131a7e116a0463861d7d18663c5627c38c7147ddaadfd48acd7a4535202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() .try_into().unwrap(); @@ -205,23 +217,23 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM1024::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } } - #[test] /// Mirror of bc-java MLKEMTest.java : testMLKEM fn test_mlkem() { let seed_bytes: [u8; MLKEM_SEED_LEN] = hex::decode( - "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 - 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7").unwrap().try_into().unwrap(); + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7", + ) + .unwrap() + .try_into() + .unwrap(); - let seed = KeyMaterial512::from_bytes_as_type( - &seed_bytes, - KeyType::Seed, - ).unwrap(); + let seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); let expected_full_sk_bytes: [u8; MLKEM1024_FULL_SK_LEN] = hex::decode("8C8B3722A82E550565521611EBBC63079944C9B1ABB3B0020FF12F631891A9C468D3A67BF6271280DA58D03CB042B3A461441637F929C273469AD15311E910DE18CB9537BA1BE42E98BB59E498A13FD440D0E69EE832B45CD95C382177D67096A18C07F1781663651BDCAC90DEDA3DDD143485864181C91FA2080F6DAB3F86204CEB64A7B4446895C03987A031CB4B6D9E0462FDA829172B6C012C638B29B5CD75A2C930A5596A3181C33A22D574D30261196BC350738D4FD9183A763336243ACED99B3221C71D8866895C4E52C119BF3280DAF80A95E15209A795C4435FBB3570FDB8AA9BF9AEFD43B094B781D5A81136DAB88B8799696556FEC6AE14B0BB8BE4695E9A124C2AB8FF4AB1229B8AAA8C6F41A60C34C7B56182C55C2C685E737C6CA00A23FB8A68C1CD61F30D3993A1653C1675AC5F0901A7160A73966408B8876B715396CFA4903FC69D60491F8146808C97CD5C533E71017909E97B835B86FF847B42A696375435E006061CF7A479463272114A89EB3EAF2246F0F8C104A14986828E0AD20420C9B37EA23F5C514949E77AD9E9AD12290DD1215E11DA274457AC86B1CE6864B122677F3718AA31B02580E64317178D38F25F609BC6C55BC374A1BF78EA8ECC219B30B74CBB3272A599238C93985170048F176775FB19962AC3B135AA59DB104F7114DBC2C2D42949ADECA6A85B323EE2B2B23A77D9DB235979A8E2D67CF7D2136BBBA71F269574B38888E1541340C19284074F9B7C8CF37EB01384E6E3822EC4882DFBBEC4E6098EF2B2FC177A1F0BCB65A57FDAA89315461BEB7885FB68B3CD096EDA596AC0E61DD7A9C507BC6345E0827DFCC8A3AC2DCE51AD731AA0EB932A6D0983992347CBEB3CD0D9C9719797CC21CF0062B0AD94CAD734C63E6B5D859CBE19F0368245351BF464D7505569790D2BB724D8659A9FEB1C7C473DC4D061E29863A2714BAC42ADCD1A8372776556F7928A7A44E94B6A25322D03C0A1622A7FD261522B7358F085BDFB60758762CB901031901B5EECF4920C81020A9B1781BCB9DD19A9DFB66458E7757C52CEC75B4BA740A24099CB56BB60A76B6901AA3E0169C9E83496D73C4C99435A28D613E97A1177F58B6CC595D3B2331E9CA7B57B74DC2C5277D26F2FE19240A55C35D6CFCA26C73E9A2D7C980D97960AE1A04698C16B398A5F20C35A0914145CE1674B71ABC6066A909A3E4B911E69D5A849430361F731B07246A6329B52361904225082D0AAC5B21D6B34862481A890C3C360766F04263603A6B73E802B1F70B2EB00046836B8F493BF10B90B8737C6C548449B294C47253BE26CA72336A632063AD3D0B48C8B0F4A34447EF13B764020DE739EB79ABA20E2BE1951825F293BEDD1089FCB0A91F560C8E17CDF52541DC2B81F972A7375B201F10C08D9B5BC8B95100054A3D0AAFF89BD08D6A0E7F2115A435231290460C9AD435A3B3CF35E52091EDD1890047BCC0AABB1ACEBC75F4A32BC1451ACC4969940788E89412188946C9143C5046BD1B458DF617C5DF533B052CD6038B7754034A23C2F7720134C7B4EACE01FAC0A2853A9285847ABBD06A3343A778AC6062E458BC5E61ECE1C0DE0206E6FE8A84034A7C5F1B005FB0A584051D3229B86C909AC5647B3D75569E05A88279D80E5C30F574DC327512C6BBE8101239EC62861F4BE67B05B9CDA9C545C13E7EB53CFF260AD9870199C21F8C63D64F0458A7141285023FEB829290872389644B0C3B73AC2C8E121A29BB1C43C19A233D56BED82740EB021C97B8EBBA40FF328B541760FCC372B52D3BC4FCBC06F424EAF253804D4CB46F41FF254C0C5BA483B44A87C219654555EC7C163C79B9CB760A2AD9BB722B93E0C28BD4B1685949C496EAB1AFF90919E3761B346838ABB2F01A91E554375AFDAAAF3826E6DB79FE7353A7A578A7C0598CE28B6D9915214236BBFFA6D45B6376A07924A39A7BE818286715C8A3C110CD76C02E0417AF138BDB95C3CCA798AC809ED69CFB672B6FDDC24D89C06A6558814AB0C21C62B2F84C0E3E0803DB337A4E0C7127A6B4C8C08B1D1A76BF07EB6E5B5BB47A16C74BC548375FB29CD789A5CFF91BDBD071859F4846E355BB0D29484E264DFF36C9177A7ACA78908879695CA87F25436BC12630724BB22F0CB64897FE5C41195280DA04184D4BC7B532A0F70A54D7757CDE6175A6843B861CB2BC4830C0012554CFC5D2C8A2027AA3CD967130E9B96241B11C4320C7649CC23A71BAFE691AFC08E680BCEF42907000718E4EACE8DA28214197BE1C269DA9CB541E1A3CE97CFADF9C6058780FE6793DBFA8218A2760B802B8DA2AA271A38772523A76736A7A31B9D3037AD21CEBB11A472B8792EB17558B940E70883F264592C689B240BB43D5408BF446432F412F4B9A5F6865CC252A43CF40A320391555591D67561FDD05353AB6B019B3A08A73353D51B6113AB2FA51D975648EE254AF89A230504A236A4658257740BDCBBE1708AB022C3C588A410DB3B9C308A06275BDF5B4859D3A2617A295E1A22F90198BAD0166F4A943417C5B831736CB2C8580ABFDE5714B586ABEEC0A175A08BC710C7A2895DE93AC438061BF7765D0D21CD418167CAF89D1EFC3448BCBB96D69B3E010C82D15CAB6CACC6799D3639669A5B21A633C865F8593B5B7BC800262BB837A924A6C5440E4FC73B41B23092C3912F4C6BEBB4C7B4C62908B03775666C22220DF9C88823E344C7308332345C8B795D34E8C051F21F5A21C214B69841358709B1C305B32CC2C3806AE9CCD3819FFF4507FE520FBFC27199BC23BE6B9B2D2AC1717579AC769279E2A7AAC68A371A47BA3A7DBE016F14E1A727333663C4A5CD1A0F8836CF7B5C49AC51485CA60345C990E06888720003731322C5B8CD5E6907FDA1157F468FD3FC20FA8175EEC95C291A262BA8C5BE990872418930852339D88A19B37FEFA3CFE82175C224407CA414BAEB37923B4D2D83134AE154E490A9B45A0563B06C953C3301450A2176A07C614A74E3478E48509F9A60AE945A8EBC7815121D90A3B0E07091A096CF02C57B25BCA58126AD0C629CE166A7EDB4B33221A0D3F72B85D562EC698B7D0A913D73806F1C5C87B38EC003CB303A3DC51B4B35356A67826D6EDAA8FEB93B98493B2D1C11B676A6AD9506A1AAAE13A824C7C08D1C6C2C4DBA9642C76EA7F6C8264B64A23CCCA9A74635FCBF03E00F1B5722B214376790793B2C4F0A13B5C40760B4218E1D2594DCB30A70D9C1782A5DD30576FA4144BFC8416EDA8118FC6472F56A979586F33BB070FB0F1B0B10BC4897EBE01BCA3893D4E16ADB25093A7417D0708C83A26322E22E6330091E30152BF823597C04CCF4CFC7331578F43A2726CCB428289A90C863259DD180C5FF142BEF41C7717094BE07856DA2B140FA67710967356AA47DFBC8D255B4722AB86D439B7E0A6090251D2D4C1ED5F20BBE6807BF65A90B7CB2EC0102AF02809DC9AC7D0A3ABC69C18365BCFF59185F33996887746185906C0191AED4407E139446459BE29C6822717644353D24AB6339156A9C424909F0A9025BB74720779BE43F16D81C8CC666E99710D8C68BB5CC4E12F314E925A551F09CC59003A1F88103C254BB978D75F394D3540E31E771CDA36E39EC54A62B5832664D821A72F1E6AFBBA27F84295B2694C498498E812BC8E9378FE541CEC5891B25062901CB7212E3CDC46179EC5BCEC10BC0B9311DE05074290687FD6A5392671654284CD9C8CC3EBA80EB3B662EB53EB75116704A1FEB5C2D056338532868DDF24EB8992AB8565D9E490CADF14804360DAA90718EAB616BAB0765D33987B47EFB6599C5563235E61E4BE670E97955AB292D9732CB8930948AC82DF230AC72297A23679D6B94C17F1359483254FEDC2F05819F0D069A443B78E3FC6C3EF4714B05A3FCA81CBBA60242A7060CD885D8F39981BB18092B23DAA59FD9578388688A09BBA079BC809A54843A60385E2310BBCBCC0213CE3DFAAB33B47F9D6305BC95C6107813C585C4B657BF30542833B14949F573C0612AD524BAAE69590C1277B86C286571BF66B3CFF46A3858C09906A794DF4A06E9D4B0A2E43F10F72A6C6C47E5646E2C799B71C33ED2F01EEB45938EB7A4E2E2908C53558A540D350369FA189C616943F7981D7618CF02A5B0A2BCC422E857D1A47871253D08293C1C179BCDC0437069107418205FDB9856623B8CA6B694C96C084B17F13BB6DF12B2CFBBC2B0E0C34B00D0FCD0AECFB27924F6984E747BE2A09D83A8664590A8077331491A4F7D720843F23E652C6FA840308DB4020337AAD37967034A9FB523B67CA70330F02D9EA20C1E84CB8E5757C9E1896B60581441ED618AA5B26DA56C0A5A73C4DCFD755E610B4FC81FF84E21D2E574DFD8CD0AE893AA7E125B44B924F45223EC09F2AD1141EA93A68050DBF699E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7").unwrap() .try_into().unwrap(); @@ -233,8 +245,16 @@ mod mlkem_tests { assert_eq!(sk.encode_full_sk(), expected_full_sk_bytes.as_slice()); assert_eq!(pk.encode(), expected_pk_bytes.as_slice()); - let message: [u8; MLKEM_RND_LEN] = hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72").unwrap().try_into().unwrap(); - let expected_shared_secret: [u8; MLKEM_SS_LEN] = hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058").unwrap().try_into().unwrap(); + let message: [u8; MLKEM_RND_LEN] = + hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72") + .unwrap() + .try_into() + .unwrap(); + let expected_shared_secret: [u8; MLKEM_SS_LEN] = + hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058") + .unwrap() + .try_into() + .unwrap(); let expected_ciphertext: [u8; MLKEM1024_CT_LEN] = hex::decode("8B9FE419250C5FB0463C8181FCF7CEC777136B738E015EBA31067AA4A8C378BBAC0121B88214F1AEB866E4F33C277099E09B4BF7E21CDDA30B5B32C18B0E9660C30601D85DAEC07AAF4B343EC5516FA501DD63088B999FB9A414C6CA593806C08CD4C775139BF0F0BF3676D773EDD56E616A13830D5F5FE35E515DBC84E43AAD0167D57E60A9DE30886ACD3F7F2006CAC26A7A07B4DADBEDFBED7F305764386AAD726D5B2BF14A376BAD8B4896688491733FB34E6EDEA10BFD5E448541CB6E69E3D87DF190AFA7FF62577775BAACEA444A6128A20200251D8FA759DC60FDA6A9730CFFE4997FE7EBCDD1644AE2D55290A4074CDD2CE53C18D22BC33671E68727A9B5A2FEAFB114A8045D96A56981E200A09661375987625ACC233EDE817AF1DEEAA21C7C4377423E73C5AF9BFF58A49DE6DAFD07A3E3BABD891F62BBA41D1856B8BC502CC86EE115A3598431E2B54AB0C5EACC3CE6A03090925C1FD5A251B00576763A963994A7A23EE12EBFC1B994F93C6144178F0BEF88245CE77CD32EF651826A6090AF561A5864DEC2A51D846F1F48F88B4B55F58C2373E0F67BDC95DC23A43E8546232A7B234E49F5226A3A63BDBCED7240FC81C2DB68AAEB2671A2FD231997BF8839C63A7F41F15E7242821D42E80BBC0F43FA9E353DE8B25ED8FFC242EB512C6A5260919AAE89A11176532BCCC762A520A37AEC4E7209AA81CEE0DD4ADD932C47EB8100BE98AA1DEEA9EA698115ADCED950A6C536D19AEB325CEA8C5245C0A2281533FB90809DC2BE90567EBE6AE229FE09B44DA2182585EA694D8A9AB33EBC24B44E09BD510F34B4140E1FB41162F9415F2D9106A0CEA00A26ED0920021F4E5BCFB3DABF5850DAB22B2E889D9611FBE06D0C899708EB5E5FAD2FBBE0D5C0BDE080F8E760EDFA037D55DA77F0F39591BF5B050C905FA538B7228E238A290DF340778DCBD6BE40A3B1DD455FB27ADBE176AEF6CC295BEA570BDC221BA14002E3B113B0EF237452FBC9F1AEC42E0D2B33F19832DB0A6171CAEB0B30EEAD3A54B704B761C7D4AFEA8F6AFC15156666A081C43AEB2E04FEECEF8AABA4049BD78B120B9ABA86A60342A0CF806411C473C26C4BE1540E3312388BCBC8523BA73F40EA28D5564274F3661D7ACAA0F1E8D0F28DCF6B501329963E6857FDB2AAE873A7D9D6C14821F6C0B6AA50AC449075CD6F2A256C5A05959DAB5A5912CC8E8F8B9F59941BFCCE6A28CBA74A20382B1FD3382D056547D5BC5EF4AAE62F96F038C595A4F901D6AE790F8978292AD1CC3A1E800B71A5BBE84533646655E3752FBD6B02B97B204E75D28A34C2F990FB8E8CD31CE6E683FA7E67DA03367E8D47DC626F060FBA2D0425004CAC2A61D982D2E3D85008624B45DB022CF51BA265B5E974712A9372EECAC0EA272B2FC56EBED0D32105521BA2C4A8FE0C678CE4E45902C7BA9D510BD47B2B5F931DD732F27DE9B42FD4AA39EAC765283A9965EE97C0D88E23EFA6F718242C67770B87BF8832858C1D13FC520870BD34F2B9C6FBFD1A528B744F814C93F4F4E87108316FE2AB06E02292DEA7FCF6FEFB17BF5AA7376A4A9BDB7C49BF709EB1E05D60EF14CD85A75239B97BCA9A6A3CC1B28F28979D612431BAAC1ACEE5EF62776B4D51B7EB0F63DF507760097223CA903E16E02DEB7FCABFBEC26DAEDC0ED4CC55726BDC31D1775112EF3C35D1DF928C6EB7830D8CA6570CB5CE348E3F26DDE864F20E5BE7B99E264EBC0E9D8DE9C6E4B7FE3CFBE673833CF7E8B3081529062CB6815C7C0766822B3B31E56BA1FC73FE3DED4B5D435BFCE2F2997C1D4B9CE293220DD461103BE084BF12076372668A69836769C1F6D8C32E2C7BC2E7D66714C814793A2970C90DD94DF14C89C60DD35B52A14778E137E750CE83AC3AAB667FCBDCBA38B7FA6D1C6BF7B99D957078176D9779A09F84B75FBC2A11769EF65532B09ACA4C9A3766B4A1FC717F94648FB8B8D9363E54F1C4201C075C18B1EAE098B83598089585ED9DC06B96E2D1C96DC738086EBBC26C3193B64139E1FC1DFB22A17893506EF7B35792B4EB00196693686EB5DEB3CEB436DD16D2D92A0FD31F468AF8662040F5257BFA0F14991C0D560999EEF775178D14955ADF091DD797AC1FDCEC7776055271C0F130562D0B0A6749B159DD0DB9AC69271AC719B83B683CE8B32342AC4AB257B0F8083C8CC86338AFA4D386C9848F413ED0").unwrap().try_into().unwrap(); // encaps @@ -251,11 +271,15 @@ mod mlkem_tests { #[test] fn test_decaps_from_seed() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + &hex::decode( + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7 - ").unwrap(), + ", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let expected_pk_bytes: [u8; MLKEM1024_PK_LEN] = hex::decode("A04184D4BC7B532A0F70A54D7757CDE6175A6843B861CB2BC4830C0012554CFC5D2C8A2027AA3CD967130E9B96241B11C4320C7649CC23A71BAFE691AFC08E680BCEF42907000718E4EACE8DA28214197BE1C269DA9CB541E1A3CE97CFADF9C6058780FE6793DBFA8218A2760B802B8DA2AA271A38772523A76736A7A31B9D3037AD21CEBB11A472B8792EB17558B940E70883F264592C689B240BB43D5408BF446432F412F4B9A5F6865CC252A43CF40A320391555591D67561FDD05353AB6B019B3A08A73353D51B6113AB2FA51D975648EE254AF89A230504A236A4658257740BDCBBE1708AB022C3C588A410DB3B9C308A06275BDF5B4859D3A2617A295E1A22F90198BAD0166F4A943417C5B831736CB2C8580ABFDE5714B586ABEEC0A175A08BC710C7A2895DE93AC438061BF7765D0D21CD418167CAF89D1EFC3448BCBB96D69B3E010C82D15CAB6CACC6799D3639669A5B21A633C865F8593B5B7BC800262BB837A924A6C5440E4FC73B41B23092C3912F4C6BEBB4C7B4C62908B03775666C22220DF9C88823E344C7308332345C8B795D34E8C051F21F5A21C214B69841358709B1C305B32CC2C3806AE9CCD3819FFF4507FE520FBFC27199BC23BE6B9B2D2AC1717579AC769279E2A7AAC68A371A47BA3A7DBE016F14E1A727333663C4A5CD1A0F8836CF7B5C49AC51485CA60345C990E06888720003731322C5B8CD5E6907FDA1157F468FD3FC20FA8175EEC95C291A262BA8C5BE990872418930852339D88A19B37FEFA3CFE82175C224407CA414BAEB37923B4D2D83134AE154E490A9B45A0563B06C953C3301450A2176A07C614A74E3478E48509F9A60AE945A8EBC7815121D90A3B0E07091A096CF02C57B25BCA58126AD0C629CE166A7EDB4B33221A0D3F72B85D562EC698B7D0A913D73806F1C5C87B38EC003CB303A3DC51B4B35356A67826D6EDAA8FEB93B98493B2D1C11B676A6AD9506A1AAAE13A824C7C08D1C6C2C4DBA9642C76EA7F6C8264B64A23CCCA9A74635FCBF03E00F1B5722B214376790793B2C4F0A13B5C40760B4218E1D2594DCB30A70D9C1782A5DD30576FA4144BFC8416EDA8118FC6472F56A979586F33BB070FB0F1B0B10BC4897EBE01BCA3893D4E16ADB25093A7417D0708C83A26322E22E6330091E30152BF823597C04CCF4CFC7331578F43A2726CCB428289A90C863259DD180C5FF142BEF41C7717094BE07856DA2B140FA67710967356AA47DFBC8D255B4722AB86D439B7E0A6090251D2D4C1ED5F20BBE6807BF65A90B7CB2EC0102AF02809DC9AC7D0A3ABC69C18365BCFF59185F33996887746185906C0191AED4407E139446459BE29C6822717644353D24AB6339156A9C424909F0A9025BB74720779BE43F16D81C8CC666E99710D8C68BB5CC4E12F314E925A551F09CC59003A1F88103C254BB978D75F394D3540E31E771CDA36E39EC54A62B5832664D821A72F1E6AFBBA27F84295B2694C498498E812BC8E9378FE541CEC5891B25062901CB7212E3CDC46179EC5BCEC10BC0B9311DE05074290687FD6A5392671654284CD9C8CC3EBA80EB3B662EB53EB75116704A1FEB5C2D056338532868DDF24EB8992AB8565D9E490CADF14804360DAA90718EAB616BAB0765D33987B47EFB6599C5563235E61E4BE670E97955AB292D9732CB8930948AC82DF230AC72297A23679D6B94C17F1359483254FEDC2F05819F0D069A443B78E3FC6C3EF4714B05A3FCA81CBBA60242A7060CD885D8F39981BB18092B23DAA59FD9578388688A09BBA079BC809A54843A60385E2310BBCBCC0213CE3DFAAB33B47F9D6305BC95C6107813C585C4B657BF30542833B14949F573C0612AD524BAAE69590C1277B86C286571BF66B3CFF46A3858C09906A794DF4A06E9D4B0A2E43F10F72A6C6C47E5646E2C799B71C33ED2F01EEB45938EB7A4E2E2908C53558A540D350369FA189C616943F7981D7618CF02A5B0A2BCC422E857D1A47871253D08293C1C179BCDC0437069107418205FDB9856623B8CA6B694C96C084B17F13BB6DF12B2CFBBC2B0E0C34B00D0FCD0AECFB27924F6984E747BE2A09D83A8664590A8077331491A4F7D720843F23E652C6FA840308DB4020337AAD37967034A9FB523B67CA70330F02D9EA20C1E84CB8E5757C9E1896B60581441ED618AA5B26DA56C0A5A73C4DCFD755E610B4FC81FF84E21").unwrap() .try_into().unwrap(); @@ -265,8 +289,16 @@ mod mlkem_tests { assert_eq!(pk.encode(), expected_pk_bytes.as_slice()); - let message: [u8; MLKEM_RND_LEN] = hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72").unwrap().try_into().unwrap(); - let expected_shared_secret: [u8; MLKEM_SS_LEN] = hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058").unwrap().try_into().unwrap(); + let message: [u8; MLKEM_RND_LEN] = + hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72") + .unwrap() + .try_into() + .unwrap(); + let expected_shared_secret: [u8; MLKEM_SS_LEN] = + hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058") + .unwrap() + .try_into() + .unwrap(); let expected_ciphertext: [u8; MLKEM1024_CT_LEN] = hex::decode("8B9FE419250C5FB0463C8181FCF7CEC777136B738E015EBA31067AA4A8C378BBAC0121B88214F1AEB866E4F33C277099E09B4BF7E21CDDA30B5B32C18B0E9660C30601D85DAEC07AAF4B343EC5516FA501DD63088B999FB9A414C6CA593806C08CD4C775139BF0F0BF3676D773EDD56E616A13830D5F5FE35E515DBC84E43AAD0167D57E60A9DE30886ACD3F7F2006CAC26A7A07B4DADBEDFBED7F305764386AAD726D5B2BF14A376BAD8B4896688491733FB34E6EDEA10BFD5E448541CB6E69E3D87DF190AFA7FF62577775BAACEA444A6128A20200251D8FA759DC60FDA6A9730CFFE4997FE7EBCDD1644AE2D55290A4074CDD2CE53C18D22BC33671E68727A9B5A2FEAFB114A8045D96A56981E200A09661375987625ACC233EDE817AF1DEEAA21C7C4377423E73C5AF9BFF58A49DE6DAFD07A3E3BABD891F62BBA41D1856B8BC502CC86EE115A3598431E2B54AB0C5EACC3CE6A03090925C1FD5A251B00576763A963994A7A23EE12EBFC1B994F93C6144178F0BEF88245CE77CD32EF651826A6090AF561A5864DEC2A51D846F1F48F88B4B55F58C2373E0F67BDC95DC23A43E8546232A7B234E49F5226A3A63BDBCED7240FC81C2DB68AAEB2671A2FD231997BF8839C63A7F41F15E7242821D42E80BBC0F43FA9E353DE8B25ED8FFC242EB512C6A5260919AAE89A11176532BCCC762A520A37AEC4E7209AA81CEE0DD4ADD932C47EB8100BE98AA1DEEA9EA698115ADCED950A6C536D19AEB325CEA8C5245C0A2281533FB90809DC2BE90567EBE6AE229FE09B44DA2182585EA694D8A9AB33EBC24B44E09BD510F34B4140E1FB41162F9415F2D9106A0CEA00A26ED0920021F4E5BCFB3DABF5850DAB22B2E889D9611FBE06D0C899708EB5E5FAD2FBBE0D5C0BDE080F8E760EDFA037D55DA77F0F39591BF5B050C905FA538B7228E238A290DF340778DCBD6BE40A3B1DD455FB27ADBE176AEF6CC295BEA570BDC221BA14002E3B113B0EF237452FBC9F1AEC42E0D2B33F19832DB0A6171CAEB0B30EEAD3A54B704B761C7D4AFEA8F6AFC15156666A081C43AEB2E04FEECEF8AABA4049BD78B120B9ABA86A60342A0CF806411C473C26C4BE1540E3312388BCBC8523BA73F40EA28D5564274F3661D7ACAA0F1E8D0F28DCF6B501329963E6857FDB2AAE873A7D9D6C14821F6C0B6AA50AC449075CD6F2A256C5A05959DAB5A5912CC8E8F8B9F59941BFCCE6A28CBA74A20382B1FD3382D056547D5BC5EF4AAE62F96F038C595A4F901D6AE790F8978292AD1CC3A1E800B71A5BBE84533646655E3752FBD6B02B97B204E75D28A34C2F990FB8E8CD31CE6E683FA7E67DA03367E8D47DC626F060FBA2D0425004CAC2A61D982D2E3D85008624B45DB022CF51BA265B5E974712A9372EECAC0EA272B2FC56EBED0D32105521BA2C4A8FE0C678CE4E45902C7BA9D510BD47B2B5F931DD732F27DE9B42FD4AA39EAC765283A9965EE97C0D88E23EFA6F718242C67770B87BF8832858C1D13FC520870BD34F2B9C6FBFD1A528B744F814C93F4F4E87108316FE2AB06E02292DEA7FCF6FEFB17BF5AA7376A4A9BDB7C49BF709EB1E05D60EF14CD85A75239B97BCA9A6A3CC1B28F28979D612431BAAC1ACEE5EF62776B4D51B7EB0F63DF507760097223CA903E16E02DEB7FCABFBEC26DAEDC0ED4CC55726BDC31D1775112EF3C35D1DF928C6EB7830D8CA6570CB5CE348E3F26DDE864F20E5BE7B99E264EBC0E9D8DE9C6E4B7FE3CFBE673833CF7E8B3081529062CB6815C7C0766822B3B31E56BA1FC73FE3DED4B5D435BFCE2F2997C1D4B9CE293220DD461103BE084BF12076372668A69836769C1F6D8C32E2C7BC2E7D66714C814793A2970C90DD94DF14C89C60DD35B52A14778E137E750CE83AC3AAB667FCBDCBA38B7FA6D1C6BF7B99D957078176D9779A09F84B75FBC2A11769EF65532B09ACA4C9A3766B4A1FC717F94648FB8B8D9363E54F1C4201C075C18B1EAE098B83598089585ED9DC06B96E2D1C96DC738086EBBC26C3193B64139E1FC1DFB22A17893506EF7B35792B4EB00196693686EB5DEB3CEB436DD16D2D92A0FD31F468AF8662040F5257BFA0F14991C0D560999EEF775178D14955ADF091DD797AC1FDCEC7776055271C0F130562D0B0A6749B159DD0DB9AC69271AC719B83B683CE8B32342AC4AB257B0F8083C8CC86338AFA4D386C9848F413ED0").unwrap().try_into().unwrap(); // encaps @@ -283,20 +315,21 @@ mod mlkem_tests { #[test] fn keygen_error_cases() { /* - Testing this condition: - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) - || seed.key_len() != 64 - */ + Testing this condition: + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + || seed.key_len() != 64 + */ // success case KeyType: seed - let seed_bytes: [u8; 64] = hex::decode("000102030405060708090a0b0c0d0e0f + let seed_bytes: [u8; 64] = hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap().try_into().unwrap(); - let mut seed = KeyMaterial512::from_bytes_as_type( - &seed_bytes, - KeyType::Seed, - ).unwrap(); - + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + let mut seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); /* MLKEM512 */ let expected_pk_bytes: [u8; MLKEM512_PK_LEN] = hex::decode("3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c").unwrap() @@ -306,37 +339,44 @@ mod mlkem_tests { assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // success case KeyType: BytesFullEntropy - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::BytesFullEntropy).unwrap(); + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::CryptographicRandom)) + .unwrap(); _ = MLKEM512::keygen_from_seed(&seed).unwrap(); - // Failure case: key type != Seed || BytesFullEntropy let mac_seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); match MLKEM512::keygen_from_seed(&mac_seed) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } // Failure case: key is undersized let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e").unwrap(), + 303132333435363738393a3b3c3d3e", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); assert_eq!(seed.key_len(), 63); match MLKEM512::keygen_from_seed(&seed) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } } @@ -367,18 +407,21 @@ mod mlkem_tests { /// cest that a corrupted ct returns the implicit rejection value K_bar = J(z||c) fn test_implicit_rejection() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + &hex::decode( + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7 - ").unwrap(), + ", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // encaps let (_ss, ct) = MLKEM512::encaps(&pk).unwrap(); - // decaps with busted ciphertext let mut busted_ciphertext = ct.clone(); busted_ciphertext[17] ^= 0xFF; @@ -397,7 +440,7 @@ mod mlkem_tests { _ = shake.squeeze_out(&mut buf); assert_eq!(ss.ref_to_bytes(), buf); - }, + } _ => panic!("This should have succeeded but with the wrong ss."), } } @@ -407,7 +450,6 @@ mod mlkem_tests { /// /// This test satisfies testing condition #1 in the Decapsulation Input checks in FIPS 203 section 7.3 fn test_boundary_conditions() { - // ct too long / too short // // satisfies testing condition #1 in the Decapsulation Input checks in FIPS 203 section 7.3 @@ -421,7 +463,7 @@ mod mlkem_tests { // too short match MLKEM512::decaps(&sk, &ct[..MLKEM512_CT_LEN - 1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -429,7 +471,7 @@ mod mlkem_tests { ct_too_long[..MLKEM512_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM512_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM512::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } @@ -439,7 +481,7 @@ mod mlkem_tests { // too short match MLKEM768::decaps(&sk, &ct[..MLKEM512_CT_LEN - 1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -447,7 +489,7 @@ mod mlkem_tests { ct_too_long[..MLKEM768_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM768_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM768::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } @@ -457,7 +499,7 @@ mod mlkem_tests { // too short match MLKEM1024::decaps(&sk, &ct[..MLKEM1024_CT_LEN - 1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -465,7 +507,7 @@ mod mlkem_tests { ct_too_long[..MLKEM1024_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM1024_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM1024::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } } @@ -484,7 +526,7 @@ mod mlkem_tests { let p = Polynomial::new(); assert_eq!(format!("{:?}", p), "Polynomial (data masked)"); } - + #[test] fn keypair_consistency_check() { // this is common to all parameter sets, so I'll just test MLKEM512 @@ -496,11 +538,11 @@ mod mlkem_tests { // failure case: different but valid key let (pk2, sk2) = MLKEM512::keygen().unwrap(); match MLKEM512::keypair_consistency_check(&pk, &sk2) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; match MLKEM512::keypair_consistency_check(&pk2, &sk) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; @@ -509,10 +551,190 @@ mod mlkem_tests { pk_bytes[17] ^= 0x01; let pk2 = MLKEM512PublicKey::from_bytes(&pk_bytes).unwrap(); match MLKEM512::keypair_consistency_check(&pk2, &sk) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; } + + #[test] + fn keygen_from_rng_tests() { + /* keygen from seed must match keygen from rng, for a fixed-seed rng */ + // An arbitrary fixed 64-byte seed (bytes 0x00..=0x3f). + let seed_bytes: [u8; 64] = hex::decode( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\ + 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + + // The seed as fed directly to keygen_from_seed. + let seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); + + // ML-KEM-512 + let (pk_seed, sk_seed) = MLKEM512::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM512::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-512 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-512 sk from RNG must match sk from seed"); + + // ML-KEM-768 + let (pk_seed, sk_seed) = MLKEM768::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM768::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-768 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-768 sk from RNG must match sk from seed"); + + // ML-KEM-1024 + let (pk_seed, sk_seed) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM1024::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-1024 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-1024 sk from RNG must match sk from seed"); + + /* Test that keygen rejects a seed from a weak RNG */ + + // ML-KEM-512 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_112bit); + match MLKEM512::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-512 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_128bit); + _ = MLKEM512::keygen_from_rng(&mut rng).unwrap(); + + // ML-KEM-768 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_128bit); + match MLKEM768::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-768 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_192bit); + _ = MLKEM768::keygen_from_rng(&mut rng).unwrap(); + + // ML-KEM-1024 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_192bit); + match MLKEM1024::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-1024 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_256bit); + _ = MLKEM1024::keygen_from_rng(&mut rng).unwrap(); + } + + #[test] + fn encaps_rng_tests() { + // Proves that `encaps_rng` is just `encaps_internal` with the message `m` sourced from the + // RNG: when the RNG hands back exactly the bytes that `m` would be, the two must produce the + // same shared secret and ciphertext. + // + // Determinism of `encaps_rng` itself is covered by the shared KEM test + // framework via `core_framework_tests`.) + + // An arbitrary fixed 64-byte seed; FixedSeedRNG::next_bytes_out hands back its leading + // 32 bytes as the encapsulation message `m`. + let seed_bytes: [u8; 64] = hex::decode( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\ + 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + let m: [u8; MLKEM_RND_LEN] = seed_bytes[..MLKEM_RND_LEN].try_into().unwrap(); + + // ML-KEM-512 + let (pk512, _sk) = MLKEM512::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM512::encaps_internal(&pk512, m); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM512::encaps_rng(&pk512, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-512 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-512 shared secret must match encaps_internal" + ); + + // ML-KEM-768 + let (pk768, _sk) = MLKEM768::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM768::encaps_internal(&pk768, m); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM768::encaps_rng(&pk768, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-768 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-768 shared secret must match encaps_internal" + ); + + // ML-KEM-1024 + let (pk1024, _sk) = MLKEM1024::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM1024::encaps_internal(&pk1024, m); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM1024::encaps_rng(&pk1024, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-1024 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-1024 shared secret must match encaps_internal" + ); + + // Ensure that it rejects an RNG at a lower security level + let mut fake_rng = FixedSeedRNG::new([0u8; 64]); + + // fails + fake_rng.set_security_strength(SecurityStrength::_112bit); + match MLKEM512::encaps_rng(&pk512, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_128bit); + _ = MLKEM512::encaps_rng(&pk512, &mut fake_rng).unwrap(); + + // fails + fake_rng.set_security_strength(SecurityStrength::_128bit); + match MLKEM768::encaps_rng(&pk768, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_192bit); + _ = MLKEM768::encaps_rng(&pk768, &mut fake_rng).unwrap(); + + // fails + fake_rng.set_security_strength(SecurityStrength::_192bit); + match MLKEM1024::encaps_rng(&pk1024, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_256bit); + _ = MLKEM1024::encaps_rng(&pk1024, &mut fake_rng).unwrap(); + } } // struct Kat { diff --git a/crypto/mlkem-lowmemory/tests/wycheproof.rs b/crypto/mlkem-lowmemory/tests/wycheproof.rs new file mode 100644 index 0000000..a026b9b --- /dev/null +++ b/crypto/mlkem-lowmemory/tests/wycheproof.rs @@ -0,0 +1,747 @@ +//! Test against the project wycheproof repo available at: +//! https://github.com/C2SP/wycheproof +//! Requires that the wycheproof repository is cloned and available for testing at "../wycheproof" +//! relative to the root of this git project. +//! +//! This test file exercises the following test sets: +//! +//! * mlkem_512_encaps_test.json +//! * mlkem_512_keygen_seed_test.json +//! * mlkem_512_test.json +//! * mlkem_768_encaps_test.json +//! * mlkem_768_keygen_seed_test.json +//! * mlkem_768_test.json +//! * mlkem_1024_encaps_test.json +//! * mlkem_1024_keygen_seed_test.json +//! * mlkem_1024_test.json +//! +//! Note: the following test sets are not tested because this low memory implementation requires +//! seed-based private keys and has no functionality for operating on non-seed private keys. +//! +//! * mlkem_512_semi_expanded_decaps_test.json +//! * mlkem_768_semi_expanded_decaps_test.json +//! * mlkem_1024_semi_expanded_decaps_test.json + +#![allow(dead_code)] + +use bouncycastle_core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; +use bouncycastle_core::traits::{KEMDecapsulator, KEMPublicKey, SecurityStrength}; +use bouncycastle_hex as hex; +use bouncycastle_mlkem_lowmemory::{ + MLKEM512, MLKEM512PublicKey, MLKEM768, MLKEM768PublicKey, MLKEM1024, MLKEM1024PublicKey, + MLKEMPrivateKeyTrait, MLKEMTrait, +}; + +#[cfg(test)] +mod wycheproof { + use crate::{MLKEMEncapsTestCase, MLKEMKeygenSeedTestCase, MLKEMTestCase, ParameterSet}; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../wycheproof/testvectors_v1"; + const TEST_DATA_PATH: &str = "../wycheproof/testvectors_v1"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + fn mlkem_512_encaps_test() { + let contents = match get_test_data("mlkem_512_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_512_keygen_seed_test() { + let contents = match get_test_data("mlkem_512_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_512_test() { + let contents = match get_test_data("mlkem_512_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_encaps_test() { + let contents = match get_test_data("mlkem_768_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_keygen_seed_test() { + let contents = match get_test_data("mlkem_768_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_test() { + let contents = match get_test_data("mlkem_768_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_encaps_test() { + let contents = match get_test_data("mlkem_1024_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_keygen_seed_test() { + let contents = match get_test_data("mlkem_1024_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_test() { + let contents = match get_test_data("mlkem_1024_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_test: all {} test cases passed.", num_test_cases); + } +} + +/* Structs for holding test data */ + +#[derive(Clone, Debug, PartialEq)] +enum ParameterSet { + Mlkem512, + Mlkem768, + Mlkem1024, +} + +#[derive(Clone)] +struct MLKEMEncapsTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + m: String, + ek: String, + c: String, + k: String, + result: String, +} + +impl MLKEMEncapsTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + m: String::new(), + ek: String::new(), + c: String::new(), + k: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + m: test["m"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + c: test["c"].as_str().unwrap_or("").to_string(), + k: test["K"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + /* Load the key */ + + let ek = match MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + // there's some weird stuff in the wycheproof tests that give a 64 byte m, even though + // it's only allowed to be 32 bytes. + // I guess we treat that as an error that we're meant to catch? + let m: [u8; 32] = match hex::decode(&self.m).unwrap().try_into() { + Ok(m) => m, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Invalid message length: {:?}", e); + } + } + }; + + let (k, ct) = MLKEM512::encaps_internal(&ek, m); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + /* Load the key */ + + let ek = match MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + let (k, ct) = + MLKEM768::encaps_internal(&ek, hex::decode(&self.m).unwrap().try_into().unwrap()); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + /* Load the key */ + + let ek = match MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + let (k, ct) = + MLKEM1024::encaps_internal(&ek, hex::decode(&self.m).unwrap().try_into().unwrap()); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } +} + +#[derive(Clone)] +struct MLKEMKeygenSeedTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + seed: String, + ek: String, + dk: String, + result: String, +} + +impl MLKEMKeygenSeedTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + seed: String::new(), + ek: String::new(), + dk: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + seed: test["seed"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + dk: test["dk"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk.encode_full_sk(), hex::decode(&self.dk).unwrap().as_slice()); + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk.encode_full_sk(), hex::decode(&self.dk).unwrap().as_slice()); + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk.encode_full_sk(), hex::decode(&self.dk).unwrap().as_slice()); + } +} + +#[derive(Clone)] +struct MLKEMTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + seed: String, + ek: String, + c: String, + k: String, + result: String, +} + +impl MLKEMTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + seed: String::new(), + ek: String::new(), + c: String::new(), + k: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + seed: test["seed"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + c: test["c"].as_str().unwrap_or("").to_string(), + k: test["K"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (ek, dk) = match MLKEM512::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM512::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (ek, dk) = match MLKEM768::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM768::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + do_hazardous_operations(&mut seed, |seed| seed.set_key_type(KeyType::Seed)).unwrap(); + match do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(SecurityStrength::_256bit) + }) { + Ok(_) => (), + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e) + } + } + } + + let (ek, dk) = match MLKEM1024::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM1024::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } +} diff --git a/crypto/mlkem/Cargo.toml b/crypto/mlkem/Cargo.toml index 8e6bb95..8235352 100644 --- a/crypto/mlkem/Cargo.toml +++ b/crypto/mlkem/Cargo.toml @@ -14,6 +14,7 @@ bouncycastle-core-test-framework.workspace = true bouncycastle-hex.workspace = true bouncycastle-rng.workspace = true criterion.workspace = true +serde_json = "1.0" [[bench]] name = "mlkem_benches" diff --git a/crypto/mlkem/benches/mlkem_benches.rs b/crypto/mlkem/benches/mlkem_benches.rs index d6a78da..b445005 100644 --- a/crypto/mlkem/benches/mlkem_benches.rs +++ b/crypto/mlkem/benches/mlkem_benches.rs @@ -1,9 +1,13 @@ -use criterion::{Criterion, criterion_group, criterion_main}; use bouncycastle_core::key_material::{KeyMaterial512, KeyType}; -use std::hint::black_box; -use bouncycastle_core::traits::KEM; +use bouncycastle_core::traits::KEMDecapsulator; use bouncycastle_hex as hex; -use bouncycastle_mlkem::{MLKEM1024PrivateKeyExpanded, MLKEM512PrivateKeyExpanded, MLKEM768PrivateKeyExpanded, MLKEMPublicKeyTrait, MLKEMTrait, MLKEM1024, MLKEM1024_CT_LEN, MLKEM512, MLKEM512_CT_LEN, MLKEM768, MLKEM768_CT_LEN, MLKEM_RND_LEN}; +use bouncycastle_mlkem::{ + MLKEM_RND_LEN, MLKEM512, MLKEM512_CT_LEN, MLKEM512PrivateKeyExpanded, MLKEM768, + MLKEM768_CT_LEN, MLKEM768PrivateKeyExpanded, MLKEM1024, MLKEM1024_CT_LEN, + MLKEM1024PrivateKeyExpanded, MLKEMPublicKeyTrait, MLKEMTrait, +}; +use criterion::{Criterion, criterion_group, criterion_main}; +use std::hint::black_box; fn bench_mlkem_keygen(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen"); @@ -18,11 +22,11 @@ fn bench_mlkem_keygen(c: &mut Criterion) { group.throughput(criterion::Throughput::Elements(seeds.len() as u64)); group.bench_function("ML-KEM-512", |b| { - b.iter(|| { - for seed in seeds.iter() { - black_box(MLKEM512::keygen_from_seed(seed)).unwrap(); - } - }) + b.iter(|| { + for seed in seeds.iter() { + black_box(MLKEM512::keygen_from_seed(seed)).unwrap(); + } + }) }); group.bench_function("ML-KEM-768", |b| { @@ -44,7 +48,6 @@ fn bench_mlkem_keygen(c: &mut Criterion) { group.finish(); } - fn bench_mlkem_keygen_and_expand(c: &mut Criterion) { let mut group = c.benchmark_group("KeyGen_and_expand"); @@ -69,7 +72,7 @@ fn bench_mlkem_keygen_and_expand(c: &mut Criterion) { group.bench_function("ML-KEM-768", |b| { b.iter(|| { for seed in seeds.iter() { - let(_pk, sk) = MLKEM768::keygen_from_seed(seed).unwrap(); + let (_pk, sk) = MLKEM768::keygen_from_seed(seed).unwrap(); black_box(MLKEM768PrivateKeyExpanded::from(&sk)); } }) @@ -78,7 +81,7 @@ fn bench_mlkem_keygen_and_expand(c: &mut Criterion) { group.bench_function("ML-KEM-1024", |b| { b.iter(|| { for seed in seeds.iter() { - let(_pk, sk) = MLKEM1024::keygen_from_seed(seed).unwrap(); + let (_pk, sk) = MLKEM1024::keygen_from_seed(seed).unwrap(); black_box(MLKEM1024PrivateKeyExpanded::from(&sk)); } }) @@ -93,12 +96,16 @@ fn bench_mlkem_encaps(c: &mut Criterion) { // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); // create a vector of signing nonces so that we're not measuring the time of the RNG const NUM_ELEMS: usize = 256; @@ -120,7 +127,6 @@ fn bench_mlkem_encaps(c: &mut Criterion) { }) }); - /*** ML-KEM-768 ***/ let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); @@ -150,19 +156,22 @@ fn bench_mlkem_encaps(c: &mut Criterion) { group.finish(); } - fn bench_mlkem_encaps_for_expanded(c: &mut Criterion) { let mut group = c.benchmark_group("Encaps_for_expanded_key"); // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); // create a vector of signing nonces so that we're not measuring the time of the RNG const NUM_ELEMS: usize = 256; @@ -185,7 +194,6 @@ fn bench_mlkem_encaps_for_expanded(c: &mut Criterion) { }) }); - /*** ML-KEM-768 ***/ let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); let a_hat = pk.A_hat(); @@ -223,12 +231,16 @@ fn bench_mlkem_decaps(c: &mut Criterion) { // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); const NUM_ELEMS: usize = 256; @@ -236,7 +248,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -248,7 +260,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-512", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM512::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM512::decaps(&sk, &cts[i])); } }) }); @@ -257,7 +269,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -269,7 +281,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-768", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM768::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM768::decaps(&sk, &cts[i])); } }) }); @@ -278,7 +290,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -290,7 +302,7 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.bench_function("ML-KEM-1024", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM1024::decaps(&sk, &cts[i]) ); + _ = black_box(MLKEM1024::decaps(&sk, &cts[i])); } }) }); @@ -298,19 +310,22 @@ fn bench_mlkem_decaps(c: &mut Criterion) { group.finish(); } - fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let mut group = c.benchmark_group("Decaps_with_expanded_key"); // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); const NUM_ELEMS: usize = 256; @@ -319,7 +334,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -331,7 +346,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { group.bench_function("ML-KEM-512", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM512::decaps_with_expanded_key(&sk_expanded, &cts[i]) ); + _ = black_box(MLKEM512::decaps_with_expanded_key(&sk_expanded, &cts[i])); } }) }); @@ -341,7 +356,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let sk_expanded = MLKEM768PrivateKeyExpanded::from(&sk); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -353,7 +368,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { group.bench_function("ML-KEM-768", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM768::decaps_with_expanded_key(&sk_expanded, &cts[i]) ); + _ = black_box(MLKEM768::decaps_with_expanded_key(&sk_expanded, &cts[i])); } }) }); @@ -363,7 +378,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { let sk_expanded = MLKEM1024PrivateKeyExpanded::from(&sk); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -375,7 +390,7 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { group.bench_function("ML-KEM-1024", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM1024::decaps_with_expanded_key(&sk_expanded, &cts[i]) ); + _ = black_box(MLKEM1024::decaps_with_expanded_key(&sk_expanded, &cts[i])); } }) }); @@ -383,19 +398,22 @@ fn bench_mlkem_decaps_with_expanded_key(c: &mut Criterion) { group.finish(); } - fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { let mut group = c.benchmark_group("decaps_from_seed"); // set up the seeds outside of the timing loop // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); const NUM_ELEMS: usize = 256; @@ -403,7 +421,7 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM512_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -415,7 +433,7 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { group.bench_function("ML-KEM-512", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM512::decaps_from_seed(&seed, &cts[i]) ); + _ = black_box(MLKEM512::decaps_from_seed(&seed, &cts[i])); } }) }); @@ -424,7 +442,7 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM768_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" @@ -436,7 +454,7 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { group.bench_function("ML-KEM-768", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM768::decaps_from_seed(&seed, &cts[i]) ); + _ = black_box(MLKEM768::decaps_from_seed(&seed, &cts[i])); } }) }); @@ -445,11 +463,11 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); // create a bunch of ciphertexts to decaps - let mut cts = [ [0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; + let mut cts = [[0u8; MLKEM1024_CT_LEN]; NUM_ELEMS]; for i in 0..NUM_ELEMS { // create each ct with a unique nonce // encaps_internal() returns (ss, ct) ... we only want ct, hence the ".1" - cts[i].copy_from_slice(&MLKEM1024::encaps_internal(&pk, None,[i as u8; MLKEM_RND_LEN]).1); + cts[i].copy_from_slice(&MLKEM1024::encaps_internal(&pk, None, [i as u8; MLKEM_RND_LEN]).1); } group.throughput(criterion::Throughput::Elements(NUM_ELEMS as u64)); @@ -457,7 +475,7 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { group.bench_function("ML-KEM-1024", |b| { b.iter(|| { for i in 0..NUM_ELEMS { - _ = black_box( MLKEM1024::decaps_from_seed(&seed, &cts[i]) ); + _ = black_box(MLKEM1024::decaps_from_seed(&seed, &cts[i])); } }) }); @@ -465,9 +483,16 @@ fn bench_mlkem_decaps_from_seed(c: &mut Criterion) { group.finish(); } -criterion_group!(benches, bench_mlkem_keygen, bench_mlkem_keygen_and_expand, - bench_mlkem_encaps, bench_mlkem_encaps_for_expanded, - bench_mlkem_decaps, bench_mlkem_decaps_with_expanded_key, bench_mlkem_decaps_from_seed); +criterion_group!( + benches, + bench_mlkem_keygen, + bench_mlkem_keygen_and_expand, + bench_mlkem_encaps, + bench_mlkem_encaps_for_expanded, + bench_mlkem_decaps, + bench_mlkem_decaps_with_expanded_key, + bench_mlkem_decaps_from_seed +); criterion_main!(benches); diff --git a/crypto/mlkem/src/aux_functions.rs b/crypto/mlkem/src/aux_functions.rs index 264654f..7fd9711 100644 --- a/crypto/mlkem/src/aux_functions.rs +++ b/crypto/mlkem/src/aux_functions.rs @@ -1,12 +1,10 @@ //! Implements auxiliary functions for ML-DSA as defined in Section 7 of FIPS 204. +use crate::matrix::Vector; use crate::mlkem::{N, q, q_inv}; -use crate::polynomial::{Polynomial}; +use crate::{Matrix, Polynomial}; use bouncycastle_core::traits::XOF; use bouncycastle_sha3::{SHAKE128, SHAKE256}; -use crate::Matrix; -use crate::matrix::Vector; - pub(crate) fn expandA(rho: &[u8; 32]) -> Matrix { let mut A_hat = Matrix::::new(); @@ -24,14 +22,15 @@ pub(crate) fn expandA(rho: &[u8; 32]) -> Matrix { /// Algorithm 5 ByteEncode_d(𝐹) /// Encodes an array of 𝑑-bit integers into a byte array for 1 ≤ 𝑑 ≤ 12. -/// Input: integer array 𝐹 ∈ ℤ_M^256 , where 𝑚 = 2^𝑑 if 𝑑 < 12, and 𝑚 = 𝑞 if 𝑑 = 12. -/// Output: byte array 𝐵 ∈ 𝔹32𝑑 . -pub(crate) fn byte_encode(F: &Polynomial) -> [u8; PACK_LEN] { +/// Input: integer array 𝐹 ∈ ℤ_M^256, where 𝑚 = 2^𝑑 if 𝑑 < 12, and 𝑚 = 𝑞 if 𝑑 = 12. +/// Output: byte array 𝐵 ∈ 𝔹32𝑑. +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. +pub fn byte_encode(F: &Polynomial) -> [u8; PACK_LEN] { debug_assert_eq!(PACK_LEN, 32 * d); let mut B = [0u8; PACK_LEN]; - for i in 0 .. N { + for i in 0..N { let mut alpha = F[i]; // For efficiency, the library is happy to work with values outside the range [0..q], @@ -40,13 +39,13 @@ pub(crate) fn byte_encode(F: &Polynomial) for j in 0..d { // alpha % 2, but without using % for constant-time reasons + // although "& 1" may lead to other, more subtle timing issues. Research topic. let tmp = (alpha & 1) as u8; // 4: 𝑏[𝑖⋅𝑑 + 𝑗] ← 𝑎 mod 2 // constant-time note: yes, % is not constant-time, // but all of the values in (i*d + j) % 8 are loop indices and not part of the secret key. - B[(i*d + j)/8] |= tmp << ((i*d + j) % 8); - + B[(i * d + j) / 8] |= tmp << ((i * d + j) % 8); // 5: 𝑎 ← (𝑎 − 𝑏[𝑖⋅𝑑 + 𝑗])/2 // ▷ note 𝑎 − 𝑏[𝑖⋅𝑑 + 𝑗] is always even @@ -67,7 +66,8 @@ pub(crate) fn byte_encode(F: &Polynomial) /// Decodes a byte array into an array of 𝑑-bit integers for 1 ≤ 𝑑 ≤ 12. /// Input: byte array 𝐵 ∈ 𝔹32𝑑 . /// Output: integer array 𝐹 ∈ ℤ256 , where 𝑚 = 2𝑑 if 𝑑 < 12 and 𝑚 = 𝑞 if 𝑑 = 12. -pub(crate) fn byte_decode(B: &[u8; PACK_LEN]) -> Polynomial { +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. +pub fn byte_decode(B: &[u8; PACK_LEN]) -> Polynomial { debug_assert_eq!(PACK_LEN, 32 * d); let mut F = Polynomial::new(); @@ -76,7 +76,7 @@ pub(crate) fn byte_decode(B: &[u8; PACK_L // 3: F[i] = SUM_j=0..d-1{ 𝑏[𝑖 ⋅ 𝑑 + 𝑗] ⋅ 2𝑗 } mod m for j in 0..d { // select the next bit, according to bitcount, then shift it up by j - F[i] |= (((B[(i*d + j)/8] >> (i*d + j)%8) & 1) as i16) << j; // there's supposed to be a `mod m` here, but that shouldn't matter; we'll check it below anyway. + F[i] |= (((B[(i * d + j) / 8] >> (i * d + j) % 8) & 1) as i16) << j; // there's supposed to be a `mod m` here, but that shouldn't matter; we'll check it below anyway. } } @@ -87,7 +87,8 @@ pub(crate) fn byte_decode(B: &[u8; PACK_L /// Takes a 32-byte seed and two indices as input and outputs a pseudorandom element of 𝑇𝑞. /// Input: byte array 𝐵 ∈ 𝔹34 . ▷ a 32-byte seed along with two indices /// Output: array 𝑎_hat ∈ ℤ256 ▷ the coefficients of the NTT of a polynomial -pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. +pub fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { let mut a_hat = Polynomial::new(); // 1: ctx ← XOF.Init() @@ -102,9 +103,9 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // SHAKE is fairly inefficient if you just squeeze 3 bytes at a time, so we'll do a block. // size doesn't really matter, so long as it's a multiple of 3. // 288 seemed to be the sweet spot from playing with benchmarks - // It's probably around the average rejection rate, and 288 is a multiple of both 3 (required for this alg) + // It's probably around the average rejection rate, and 216 is a multiple of both 3 (required for this alg) // and 8 (efficient for SHAKE). - let mut C = [0u8; 288]; + let mut C = [0u8; 216]; xof.squeeze_out(&mut C); let mut idx: usize = 0; @@ -119,12 +120,12 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // 6: 𝑑1 ← 𝐶[0] + 256 ⋅ (𝐶[1] mod 16) // ▷ 0 ≤ 𝑑1 < 2^12 - let d1: i16 = (C[idx+0] as i16) | ((C[idx+1] as i32) << 8) as i16 & 0xFFF; + let d1: i16 = (C[idx] as i16) | ((C[idx + 1] as i32) << 8) as i16 & 0xFFF; debug_assert!(d1 < 2 << 12); // 7: 𝑑2 ← ⌊𝐶[1]/16⌋ + 16 ⋅ 𝐶[2] // ▷ 0 ≤ 𝑑2 < 2^12 - let d2: i16 = ((C[idx+1] as i16) >> 4) | ((C[idx+2] as i32) << 4) as i16 & 0xFFF; + let d2: i16 = ((C[idx + 1] as i16) >> 4) | ((C[idx + 2] as i32) << 4) as i16 & 0xFFF; debug_assert!(d2 < 2 << 12); // 8: if 𝑑1 < 𝑞 then @@ -156,15 +157,16 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { /// Takes a seed as input and outputs a pseudorandom sample from the distribution D𝜂(𝑅𝑞). /// Input: byte array 𝐵 ∈ 𝔹64𝜂 . /// Output: array 𝑓 ∈ ℤ256 ▷ the coefficients of the sampled polynomial -pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. +pub fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { debug_assert_eq!(bytes.len(), 64 * eta as usize); let mut f = Polynomial::new(); match eta { 2 => { - for i in 0..N/8 { - let t = u32::from_le_bytes(bytes[4*i .. 4*i + 4].try_into().unwrap()); + for i in 0..N / 8 { + let t = u32::from_le_bytes(bytes[4 * i..4 * i + 4].try_into().unwrap()); let mut d = t & 0x55555555; d += (t >> 1) & 0x55555555; for j in 0..8usize { @@ -179,7 +181,7 @@ pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { } } 3 => { - for i in 0..N/4 { + for i in 0..N / 4 { let t = little_endian_to_u24(bytes, 3 * i); let mut d = t & 0x00249249; d += (t >> 1) & 0x00249249; @@ -204,7 +206,6 @@ pub(crate) fn sample_poly_cbd(bytes: &[u8]) -> Polynomial { /// SamplePolyCBD𝜂1(PRF𝜂1 (𝜎, 𝑁 )) /// Performs both the PRF and SamplePolyCBD steps pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial { - // Alg 13: 9: 𝐬[𝑖] ← SamplePolyCBD𝜂1(PRF𝜂1 (𝜎, 𝑁 )) // ▷ 𝐬[𝑖] ∈ ℤ256 sampled from CBD match eta { @@ -220,7 +221,7 @@ pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial }; sample_poly_cbd::(&buf) - }, + } 3 => { let buf = { let mut xof = SHAKE256::new(); @@ -232,13 +233,16 @@ pub(crate) fn sample_poly_CBD(b: &[u8; 32], n: u8) -> Polynomial }; sample_poly_cbd::(&buf) - }, - _ => unreachable!() + } + _ => unreachable!(), } } /// Internal helper for keygen since both s_hat and e_hat have identical sampling code -pub(crate) fn sample_vector_CBD(b: &[u8; 32], mut n: u8) -> Vector { +pub(crate) fn sample_vector_CBD( + b: &[u8; 32], + mut n: u8, +) -> Vector { let mut v = Vector::::new(); for i in 0..k { @@ -297,18 +301,27 @@ pub(crate) fn barrett_reduce(a: i16) -> i16 { a - (((t as i32) * q as i32) as i16) } -pub(super) fn cond_sub_q(a: i16) -> i16 { - let tmp = a - q; - tmp + ((tmp >> 15) & q) -} - +// not currently used, but I'll leave it here because it's useful for debugging if you want to output values +// that are normalized to [0,q] to compare against intermediate results from other libraries. +// pub(super) fn cond_sub_q(a: i16) -> i16 { +// let tmp = a - q; +// tmp + ((tmp >> 15) & q) +// } /// Multiplication of polynomials in Zq\[X]/(X^2-zeta) /// used for multiplication of elements in Rq in NTT domain /// /// Borrowed from: /// https://github.com/pq-crystals/kyber/blob/main/ref/ntt.c#L139 -pub(crate) fn ntt_base_mult(r: &mut [i16], off: usize, a0: i16, a1: i16, b0: i16, b1: i16, zeta: i16) { +pub(crate) fn ntt_base_mult( + r: &mut [i16], + off: usize, + a0: i16, + a1: i16, + b0: i16, + b1: i16, + zeta: i16, +) { let mut out_val0 = mul_mont(a1, b1); out_val0 = mul_mont(out_val0, zeta); out_val0 += mul_mont(a0, b0); @@ -319,29 +332,46 @@ pub(crate) fn ntt_base_mult(r: &mut [i16], off: usize, a0: i16, a1: i16, b0: i16 r[off + 1] = out_val1; } -pub(crate) fn pack_ciphertext(u: &Vector, v: &Polynomial) -> [u8; CT_LEN] { +pub(crate) fn pack_ciphertext( + u: &Vector, + v: &Polynomial, +) -> [u8; CT_LEN] { let mut out = [0u8; CT_LEN]; // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them - let lim: usize = k* (N * (du as usize) / 8); + let lim: usize = k * (N * (du as usize) / 8); u.compress_pol_vec::(&mut out[..lim]); v.compress_poly::(&mut out[lim..]); out } -pub(crate) fn unpack_ciphertext_u(c: &[u8; CT_LEN]) -> Vector { +pub(crate) fn unpack_ciphertext_u< + const k: usize, + const CT_LEN: usize, + const du: i16, + const dv: i16, +>( + c: &[u8; CT_LEN], +) -> Vector { // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them - let lim: usize = k* (N * (du as usize) / 8); + let lim: usize = k * (N * (du as usize) / 8); let u = Vector::::decompress_pol_vec::(&c[..lim]); u } -pub(crate) fn unpack_ciphertext_v(c: &[u8; CT_LEN]) -> Polynomial { +pub(crate) fn unpack_ciphertext_v< + const k: usize, + const CT_LEN: usize, + const du: i16, + const dv: i16, +>( + c: &[u8; CT_LEN], +) -> Polynomial { // each of the N i16's will take du bits, so a polynomial takes N * du bits, then we have k of them - let lim: usize = k* (N * (du as usize) / 8); + let lim: usize = k * (N * (du as usize) / 8); let v = Polynomial::decompress_poly::(&c[lim..]); diff --git a/crypto/mlkem/src/lib.rs b/crypto/mlkem/src/lib.rs index 117627d..c3dd524 100644 --- a/crypto/mlkem/src/lib.rs +++ b/crypto/mlkem/src/lib.rs @@ -45,8 +45,7 @@ //! ## Generating Keys //! //! ```rust -//! use bouncycastle_mlkem::MLKEM768; -//! use bouncycastle_core::traits::KEM; +//! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait}; //! //! let (pk, sk) = MLKEM768::keygen().unwrap(); //! ``` @@ -76,7 +75,7 @@ //! //! ```rust //! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait}; -//! use bouncycastle_core::traits::KEM; +//! use bouncycastle_core::traits::{KEMEncapsulator, KEMDecapsulator}; //! use bouncycastle_core::errors::KEMError; //! //! let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -138,60 +137,58 @@ //! constant-time after compilation. #![no_std] - #![forbid(missing_docs)] - #![forbid(unsafe_code)] #![allow(incomplete_features)] // needed because currently generic_const_exprs is experimental #![feature(generic_const_exprs)] #![feature(adt_const_params)] - // These are because I'm matching variable names exactly against FIPS 204, for example both 'K' and 'k', // or 'A' and 'a' are used and have specific meanings. // But need to tell the rust linter to not care. #![allow(non_snake_case)] #![allow(non_upper_case_globals)] - // so I can use private traits to hide internal stuff that needs to be generic within the // MLKEM implementation, but I don't want accessed from outside, such as FIPS-internal functions. #![allow(private_bounds)] - // imports needed just for docs #[allow(unused_imports)] use bouncycastle_core::key_material::KeyMaterialTrait; -// todo -- re-run mutants - -// todo -- crucible tests - +pub mod aux_functions; +mod matrix; pub mod mlkem; mod mlkem_keys; -mod polynomial; -mod aux_functions; -mod matrix; - +pub mod polynomial; /*** Exported types ***/ -pub use mlkem::{MLKEMTrait, MLKEM, MLKEM512, MLKEM768, MLKEM1024}; +pub use mlkem::{MLKEM, MLKEM512, MLKEM768, MLKEM1024, MLKEMTrait}; +pub use mlkem_keys::{ + MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey, MLKEMPrivateKey, +}; +pub use mlkem_keys::{ + MLKEM512PrivateKeyExpanded, MLKEM768PrivateKeyExpanded, MLKEM1024PrivateKeyExpanded, + MLKEMPrivateKeyExpanded, +}; +pub use mlkem_keys::{MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey, MLKEMPublicKey}; +pub use mlkem_keys::{ + MLKEM512PublicKeyExpanded, MLKEM768PublicKeyExpanded, MLKEM1024PublicKeyExpanded, + MLKEMPublicKeyExpanded, +}; pub use mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait}; -pub use mlkem_keys::{MLKEMPublicKey, MLKEM512PublicKey, MLKEM768PublicKey, MLKEM1024PublicKey}; -pub use mlkem_keys::{MLKEMPublicKeyExpanded, MLKEM512PublicKeyExpanded, MLKEM768PublicKeyExpanded, MLKEM1024PublicKeyExpanded}; -pub use mlkem_keys::{MLKEMPrivateKey, MLKEM512PrivateKey, MLKEM768PrivateKey, MLKEM1024PrivateKey}; -pub use mlkem_keys::{MLKEMPrivateKeyExpanded, MLKEM512PrivateKeyExpanded, MLKEM768PrivateKeyExpanded, MLKEM1024PrivateKeyExpanded}; /*** Exported constants ***/ pub use mlkem::ML_KEM_512_NAME; pub use mlkem::ML_KEM_768_NAME; pub use mlkem::ML_KEM_1024_NAME; -pub use mlkem::{MLKEM_SEED_LEN, MLKEM_SS_LEN, MLKEM_RND_LEN}; +pub use mlkem::{MLKEM_RND_LEN, MLKEM_SEED_LEN, MLKEM_SS_LEN}; -pub use mlkem::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_CT_LEN}; -pub use mlkem::{MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN}; -pub use mlkem::{MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN}; +pub use mlkem::{MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN}; +pub use mlkem::{MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN}; +pub use mlkem::{MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN}; pub use matrix::Matrix; // re-export just so it's visible to unit tests -pub use polynomial::Polynomial; \ No newline at end of file +pub use polynomial::Polynomial; diff --git a/crypto/mlkem/src/matrix.rs b/crypto/mlkem/src/matrix.rs index 381e7be..100d6e0 100644 --- a/crypto/mlkem/src/matrix.rs +++ b/crypto/mlkem/src/matrix.rs @@ -3,16 +3,18 @@ use core::ops::{Index, IndexMut}; -use crate::mlkem::{q, N}; +use crate::mlkem::{N, q}; use crate::polynomial; -use crate::polynomial::{Polynomial}; +use crate::polynomial::Polynomial; #[derive(Clone)] /// A matrix over the ML-KEM ring. -pub struct Matrix{ /*pub(crate)*/ mat: [[Polynomial; l]; k] } +pub struct Matrix { + /*pub(crate)*/ mat: [[Polynomial; l]; k], +} /// Convenience function to avoid ".0" all over the place. -impl Index for Matrix { +impl Index for Matrix { type Output = [Polynomial; l]; fn index(&self, index: usize) -> &Self::Output { @@ -20,7 +22,7 @@ impl Index for Matrix { } } /// Convenience function to avoid ".0" all over the place. -impl IndexMut for Matrix { +impl IndexMut for Matrix { fn index_mut(&mut self, index: usize) -> &mut Self::Output { &mut self.mat[index] } @@ -28,7 +30,7 @@ impl IndexMut for Matrix { impl Matrix { pub(crate) fn new() -> Self { - Self{ mat: [[(); l]; k].map(|_| [(); l].map(|_| Polynomial::new())) } + Self { mat: [[(); l]; k].map(|_| [(); l].map(|_| Polynomial::new())) } } /// FIPS 204 Algorithm 48 MatrixVectorNTT(𝐌, 𝐯) @@ -41,16 +43,16 @@ impl Matrix { /// transpose: False will multiply A, where as True will multiply A^T pub(crate) fn matrix_vector_ntt(&self, v: &Vector) -> Vector { let mut w = Vector::::new(); - for i in 0 .. k { + for i in 0..k { // split out the 0 case to skip a no-op add_ntt() - w[i] = if transpose{ + w[i] = if transpose { polynomial::base_mult_montgomery(&self.mat[0][i], &v[0]) } else { polynomial::base_mult_montgomery(&self.mat[i][0], &v[0]) }; let mut w1: Polynomial; - for j in 1 .. l { + for j in 1..l { // dot product a vector into a matrix: multiply the input vector // into each row of the matrix, then sum the results to produce a vector of // length k. @@ -79,9 +81,10 @@ impl Matrix { // Technically all matrices and some vectors are only part of the public key and might not need to be zeroized, // but I'll leave it zeroizing for now and leave this as a potential future optimization. - #[derive(Clone)] -pub(crate) struct Vector{ pub(crate) vec: [Polynomial; k] } +pub(crate) struct Vector { + pub(crate) vec: [Polynomial; k], +} /// Convenience function to avoid ".0" all over the place. impl Index for Vector { @@ -98,10 +101,9 @@ impl IndexMut for Vector { } } -impl Vector -{ +impl Vector { pub(crate) fn new() -> Self { - Self {vec: [(); k].map(|_| Polynomial::new()) } + Self { vec: [(); k].map(|_| Polynomial::new()) } } /// Algorithm 46 AddVectorNTT(𝐯, 𝐰)̂ @@ -110,7 +112,7 @@ impl Vector /// Output: u_hat ∈ T^ℓ_𝑞. /// Add another vector to this vector pub(crate) fn add_vector_ntt(&mut self, s: &Self) { - for i in 0 ..k { + for i in 0..k { // perform montgomery addition of each polynomial in the vector self[i].add(&s[i]); } @@ -138,7 +140,7 @@ impl Vector } } - pub(crate) fn ntt(&mut self){ + pub(crate) fn ntt(&mut self) { for i in 0..k { self[i].ntt(); } @@ -151,7 +153,7 @@ impl Vector } pub(crate) fn convert_to_mont(&mut self) { - for i in 0 ..k { + for i in 0..k { self[i].convert_to_mont(); } } @@ -165,7 +167,7 @@ impl Vector // make sure we were given the right size output buffer // each of the N i16's will take dv bits - debug_assert_eq!(out.len(), k *(N * (du as usize) / 8)); + debug_assert_eq!(out.len(), k * (N * (du as usize) / 8)); // bc-java has a conditional_sub_q() here, but I pass all unit tests without it, so I'm taking it out for performance. // let mut s = self.clone(); @@ -173,10 +175,11 @@ impl Vector let mut idx = 0; match du { - 10 => { // MLKEM512 and MLKEM 768 + 10 => { + // MLKEM512 and MLKEM 768 let mut t = [0i16; 4]; for i in 0..k { - for j in 0..N/4 { + for j in 0..N / 4 { // fill the temp array t for (l, item) in t.iter_mut().enumerate() { *item = (((((self[i][4 * j + l] as u32) << 10) as i32 @@ -193,11 +196,11 @@ impl Vector idx += 5; } } - }, + } 11 => { let mut t = [0i16; 8]; for i in 0..k { - for j in 0..N/8 { + for j in 0..N / 8 { for (l, item) in t.iter_mut().enumerate() { *item = (((((self[i][8 * j + l] as u32) << 11) as i32 + (q as i32 / 2)) @@ -219,7 +222,7 @@ impl Vector idx += 11; } } - }, + } _ => unreachable!(), } } @@ -232,17 +235,17 @@ impl Vector // make sure we were given the right size output buffer // each of the N i16's will take dv bits - debug_assert_eq!(compressed_u.len(), k *(N * (du as usize) / 8)); + debug_assert_eq!(compressed_u.len(), k * (N * (du as usize) / 8)); let mut idx = 0; match du { - 10 => { // MLKEM512 and MLKEM768 + 10 => { + // MLKEM512 and MLKEM768 let mut t = [0i16; 4]; for i in 0..k { - for j in 0..(N/4) { - t[0] = ((compressed_u[idx] as u16) - | (compressed_u[idx + 1] as u16) << 8) + for j in 0..(N / 4) { + t[0] = ((compressed_u[idx] as u16) | (compressed_u[idx + 1] as u16) << 8) as i16; t[1] = (((compressed_u[idx + 1] as u16) >> 2) | (compressed_u[idx + 2] as u16) << 6) @@ -260,11 +263,12 @@ impl Vector } } } - }, - 11 => { // MLKEM1024 + } + 11 => { + // MLKEM1024 let mut t = [0i16; 8]; for i in 0..k { - for j in 0..N/8 { + for j in 0..N / 8 { t[0] = (compressed_u[idx] as i32 | ((compressed_u[idx + 1] as u16) as i32) << 8) as i16; @@ -298,10 +302,10 @@ impl Vector } } } - }, + } _ => unreachable!(), } u } -} \ No newline at end of file +} diff --git a/crypto/mlkem/src/mlkem.rs b/crypto/mlkem/src/mlkem.rs index 70f4352..adf42da 100644 --- a/crypto/mlkem/src/mlkem.rs +++ b/crypto/mlkem/src/mlkem.rs @@ -30,7 +30,6 @@ //! ```rust //! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait}; //! use bouncycastle_mlkem::{MLKEM768PublicKeyExpanded, MLKEM768PrivateKeyExpanded}; -//! use bouncycastle_core::traits::KEM; //! use bouncycastle_core::errors::KEMError; //! //! let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -60,7 +59,7 @@ //! //! ```rust //! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait}; -//! use bouncycastle_core::traits::KEM; +//! use bouncycastle_core::traits::KEMEncapsulator; //! use bouncycastle_core::errors::KEMError; //! use bouncycastle_core::key_material::{KeyMaterial512, KeyType}; //! use bouncycastle_hex as hex; @@ -104,7 +103,7 @@ //! //! ```rust //! use bouncycastle_mlkem::{MLKEM768, MLKEMTrait}; -//! use bouncycastle_core::traits::{KEM}; +//! use bouncycastle_core::traits::KEMDecapsulator; //! use bouncycastle_core::errors::KEMError; //! use bouncycastle_core::key_material::KeyMaterialTrait; //! @@ -125,25 +124,35 @@ //! assert_eq!(ss, ss1.ref_to_bytes()); //! ``` - -use core::marker::PhantomData; -use bouncycastle_core::errors::{KEMError}; -use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial, KeyType}; -use bouncycastle_core::traits::{RNG, SecurityStrength, XOF, Algorithm, Hash, KEM}; -use bouncycastle_rng::{HashDRBG_SHA512}; -use bouncycastle_sha3::{SHA3_256, SHA3_512, SHAKE256}; -use bouncycastle_utils::ct::{conditional_copy_bytes, ct_eq_bytes}; -use crate::aux_functions::{sample_poly_CBD, sample_vector_CBD, pack_ciphertext, expandA, unpack_ciphertext_u, unpack_ciphertext_v}; +use crate::MLKEMPublicKeyExpanded; +use crate::aux_functions::{ + expandA, pack_ciphertext, sample_poly_CBD, sample_vector_CBD, unpack_ciphertext_u, + unpack_ciphertext_v, +}; use crate::matrix::{Matrix, Vector}; -use crate::mlkem_keys::{MLKEMPublicKeyTrait, MLKEMPublicKeyInternalTrait, MLKEMPrivateKeyExpanded}; -use crate::mlkem_keys::{MLKEMPrivateKeyTrait, MLKEMPrivateKeyInternalTrait}; -use crate::mlkem_keys::{MLKEM512PublicKey, MLKEM512PrivateKey, MLKEM768PublicKey, MLKEM768PrivateKey, MLKEM1024PublicKey, MLKEM1024PrivateKey}; -use crate::{MLKEMPublicKeyExpanded}; +use crate::mlkem_keys::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, +}; +use crate::mlkem_keys::{ + MLKEMPrivateKeyExpanded, MLKEMPublicKeyInternalTrait, MLKEMPublicKeyTrait, +}; +use crate::mlkem_keys::{MLKEMPrivateKeyInternalTrait, MLKEMPrivateKeyTrait}; use crate::polynomial::Polynomial; - +use bouncycastle_core::errors::KEMError; +use bouncycastle_core::errors::RNGError; +use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; +use bouncycastle_core::traits::{ + Algorithm, Hash, KEMDecapsulator, KEMEncapsulator, RNG, SecurityStrength, XOF, +}; +use bouncycastle_rng::HashDRBG_SHA512; +use bouncycastle_sha3::{SHA3_256, SHA3_512, SHAKE256}; +use bouncycastle_utils::ct::{conditional_copy_bytes, ct_eq_bytes}; +use core::marker::PhantomData; /*** Constants ***/ - /// pub const ML_KEM_512_NAME: &str = "ML-KEM-512"; /// @@ -166,7 +175,6 @@ pub(crate) const q_inv: i32 = 62209; pub(crate) const ETA2: i16 = 2; pub(crate) const POLY_BYTES: usize = 384; - /* ML-KEM-512 params */ /// Length of the \[u8] holding a ML-KEM-512 public key. @@ -197,7 +205,6 @@ pub(crate) const MLKEM768_DV: i16 = 4; /// Maps to "required RBG strength (bits)" in FIPS 203 Table 2 pub(crate) const MLKEM768_LAMBDA: i16 = 192; - /* ML-KEM-1024 params */ /// Length of the \[u8] holding a ML-KEM-1024 public key. @@ -213,15 +220,11 @@ pub(crate) const MLKEM1024_DV: i16 = 5; /// Maps to "required RBG strength (bits)" in FIPS 203 Table 2 pub(crate) const MLKEM1024_LAMBDA: i16 = 256; - - // Typedefs just to make the algorithms look more like the FIPS 204 sample code. pub(crate) type G = SHA3_512; pub(crate) type H = SHA3_256; pub(crate) type J = SHAKE256; - - /*** Pub Types ***/ /// The ML-KEM-512 algorithm. @@ -293,7 +296,8 @@ pub struct MLKEM< const CT_LEN: usize, const SS_LEN: usize, PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta: i16, const du: i16, @@ -309,48 +313,23 @@ impl< const CT_LEN: usize, const SS_LEN: usize, PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta1: i16, const du: i16, const dv: i16, const LAMBDA: i16, -> MLKEM< - PK_LEN, - SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta1, - du, - dv, - LAMBDA, -> +> MLKEM { - /// Should still be ok in FIPS mode - pub fn keygen_from_os_rng() -> Result< - (PK, SK), - KEMError, - > { - let mut seed = KeyMaterial::<64>::new(); - HashDRBG_SHA512::new_from_os().fill_keymaterial_out(&mut seed)?; - Self::keygen_internal(&seed) - } /// Algorithm 16 ML-KEM.KeyGen_internal(𝑑, 𝑧) /// Uses randomness to generate an encapsulation key and a corresponding decapsulation key. /// Input: randomness 𝑑 ∈ 𝔹32 . /// Input: randomness 𝑧 ∈ 𝔹32 . /// Output: encapsulation key ek ∈ 𝔹384𝑘+32 . /// Output: decapsulation key dk ∈ 𝔹768𝑘+96 . - pub(crate) fn keygen_internal( - seed: &KeyMaterial<64>, - ) -> Result< - (PK, SK), - KEMError, - > { - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + pub(crate) fn keygen_internal(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError> { + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::CryptographicRandom) || seed.key_len() != 64 { return Err(KEMError::KeyGenError( @@ -359,7 +338,9 @@ impl< } if seed.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { - return Err(KEMError::KeyGenError("Seed SecurityStrength must match algorithm security strength")); + return Err(KEMError::KeyGenError( + "Seed SecurityStrength must match algorithm security strength", + )); } // 1: (ekPKE, dkPKE) ← K-PKE.KeyGen(𝑑) @@ -369,12 +350,15 @@ impl< // 3: dk ← (dkPKE‖ek‖H(ek)‖𝑧) ▷ KEM decaps key includes PKE decryption key // 4: return (ek, dk) let pk_hash = pk.compute_hash(); - Ok((pk.clone(), SK::new( - s_hat, - pk, - pk_hash, - seed.ref_to_bytes()[32..].try_into().unwrap(), - Some(seed.ref_to_bytes()[..32].try_into().unwrap())) + Ok(( + pk.clone(), + SK::new( + s_hat, + pk, + pk_hash, + seed.ref_to_bytes()[32..].try_into().unwrap(), + Some(seed.ref_to_bytes()[..32].try_into().unwrap()), + ), )) } @@ -403,7 +387,6 @@ impl< // Note: in the definition of PRF_eta on page 18, it's said to be one byte. // since the number of loops here is static; we can hard-code the N values rather than using a counter - // 8: for (𝑖 ← 0; 𝑖 < 𝑘; 𝑖++) // ▷ generate 𝐬 ∈ (ℤ256)^k // 9: 𝐬[𝑖] ← SamplePolyCBD𝜂1(PRF𝜂1 (𝜎, 𝑁 )) @@ -445,7 +428,6 @@ impl< t_hat.add_vector_ntt(&e); } - // Clear the secret data before returning memory to the OS sigma.fill(0u8); @@ -462,11 +444,10 @@ impl< /// Input: message 𝑚 ∈ 𝔹32 . /// Input: randomness 𝑟 ∈ 𝔹32 . /// Output: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). - fn pke_encrypt(ek: &PK, A_hat: &Matrix, m: [u8; 32], r: &[u8; 32]) -> [u8; CT_LEN] { + fn pke_encrypt(ek: &PK, A_hat: &Matrix, m: [u8; 32], r: &[u8; 32]) -> [u8; CT_LEN] { // 1: 𝑁 ← 0 // since the number of loops here is static; we can hard-code the N values rather than using a counter - // 2: 𝐭 ← ByteDecode12(ekPKE[0 ∶ 384𝑘]) // 3: 𝜌 ← ekPKE[384𝑘 ∶ 384𝑘 + 32] // not necessary here because ek is already decoded @@ -516,7 +497,7 @@ impl< // 17: 𝑒2 ← SamplePolyCBD𝜂2(PRF𝜂2 (𝑟, 𝑁)) // ▷ sample 𝑒2 ∈ ℤ256 from CBD // note: here n = 2k - let e2 = sample_poly_CBD::(&r, 2*k as u8); + let e2 = sample_poly_CBD::(&r, 2 * k as u8); v.add(&e2); let mu = Polynomial::from_msg(m); @@ -524,7 +505,6 @@ impl< v.poly_reduce(); - pack_ciphertext::(&u, &v) } @@ -544,20 +524,24 @@ impl< /// Alternatively, you can use a [MLKEMPublicKeyExpanded] with [MLKEM::encaps_for_expanded_key]. /// If you specify None, the function will compute A_hat internally and everything will work fine. /// - /// Unlike the more public function exposed by [KEM::encaps], this returns the shared secret as raw bytes + /// Unlike the more public function exposed by [KEMEncapsulator::encaps], this returns the shared secret as raw bytes /// instead of wrapped in an appropriately-set [KeyMaterialTrait], so you're on your own for handling it properly. /// /// Note: this is an internal function that allows the caller to specify the encapsulation /// randomness (which is the message `m` to be encrypted by the underlying PKE scheme). /// This function should not be used directly unless you really have a - /// good reason. [KEM::encaps] should be used in 99.9% of cases. + /// good reason. [KEMEncapsulator::encaps] should be used in 99.9% of cases. /// The reason this is exposed publicly is: A) for unit testing that requires access /// to the deterministically reproducible function, and B) for operational environments /// that wish to provide randomness from their own source instead of the built-in RNG in bc-rust. /// If you think you will be clever and invent some scheme that uses a deterministic KEM, /// then you will almost certainly end up with security problems. Please don't do this. - pub fn encaps_internal(ek: &PK, A_hat: Option<&Matrix>, m: [u8; 32]) -> ([u8; 32], [u8; CT_LEN]) { - debug_assert_eq!(CT_LEN, 32*( (du as usize)*k + (dv as usize))); + pub fn encaps_internal( + ek: &PK, + A_hat: Option<&Matrix>, + m: [u8; 32], + ) -> ([u8; 32], [u8; CT_LEN]) { + debug_assert_eq!(CT_LEN, 32 * ((du as usize) * k + (dv as usize))); // 1: (𝐾, 𝑟) ← G(𝑚‖H(ek)) // ▷ derive shared secret key 𝐾 and randomness 𝑟 @@ -580,12 +564,8 @@ impl< // To allow for pre-computing A_hat for multiple encapsulations, we will either take // A_hat passed in, or compute it fresh. let ct = match A_hat { - Some(A_hat) => { - Self::pke_encrypt(ek, A_hat, m, &r) - } - None => { - Self::pke_encrypt(ek, &ek.A_hat(), m, &r) - } + Some(A_hat) => Self::pke_encrypt(ek, A_hat, m, &r), + None => Self::pke_encrypt(ek, &ek.A_hat(), m, &r), }; (K, ct) @@ -633,12 +613,15 @@ impl< /// Input: decapsulation key dk ∈ 𝔹768𝑘+96 . /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: shared secret key 𝐾 ∈ 𝔹32 . - fn decaps_internal(dk: &SK, A_hat: Option<&Matrix>, c: [u8; CT_LEN]) -> [u8; MLKEM_SS_LEN] { - + fn decaps_internal( + dk: &SK, + A_hat: Option<&Matrix>, + c: [u8; CT_LEN], + ) -> [u8; MLKEM_SS_LEN] { // I have tried to keep this as clean as possible for correspondence with the FIPS, // but I have moved things around so that I can use unnamed scopes to limit how many // stack variables are alive at the same time. - + // 1: dkPKE ← dk[0 ∶ 384𝑘] ▷ extract (from KEM decaps key) the PKE decryption key // 2: ekPKE ← dk[384𝑘 ∶ 768𝑘 + 32] ▷ extract PKE encryption key // 3: ℎ ← dk[768𝑘 + 32 ∶ 768𝑘 + 64] ▷ extract hash of PKE encryption key @@ -687,12 +670,8 @@ impl< // To allow for pre-computing A_hat for multiple encapsulations, we will either take // A_hat passed in, or compute it fresh. let c_prime = match A_hat { - Some(A_hat) => { - Self::pke_encrypt(dk.pk(), A_hat, m_prime, &r_prime) - } - None => { - Self::pke_encrypt(dk.pk(), &dk.pk().A_hat(), m_prime, &r_prime) - } + Some(A_hat) => Self::pke_encrypt(dk.pk(), A_hat, m_prime, &r_prime), + None => Self::pke_encrypt(dk.pk(), &dk.pk().A_hat(), m_prime, &r_prime), }; // 9: if 𝑐 ≠ 𝑐′ then @@ -707,7 +686,10 @@ impl< /// Alternative initialization of the streaming signer where you have your private key /// as a seed and you want to delay its expansion as late as possible for memory-usage reasons. // todo -- should we build a fully-stitched-together decaps-from-seed ... or not? - pub fn decaps_from_seed(seed: &KeyMaterial<64>, ct: &[u8]) -> Result, KEMError> { + pub fn decaps_from_seed( + seed: &KeyMaterial<64>, + ct: &[u8], + ) -> Result, KEMError> { let (_pk, sk) = Self::keygen_from_seed(seed)?; Self::decaps(&sk, ct) @@ -720,25 +702,16 @@ impl< const CT_LEN: usize, const SS_LEN: usize, PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta1: i16, const du: i16, const dv: i16, const LAMBDA: i16, -> MLKEMTrait for MLKEM< - PK_LEN, - SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta1, - du, - dv, - LAMBDA, -> { +> MLKEMTrait + for MLKEM +{ /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError> { Self::keygen_internal(seed) @@ -752,10 +725,7 @@ impl< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<64>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - KEMError, - > { + ) -> Result<(PK, SK), KEMError> { let (pk, sk) = Self::keygen_internal(seed)?; let sk_from_bytes = SK::sk_decode(encoded_sk)?; @@ -775,10 +745,7 @@ impl< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [KEMError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), KEMError> { + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), KEMError> { let derived_pk = sk.pk(); if derived_pk.compute_hash() == pk.compute_hash() { Ok(()) @@ -787,16 +754,30 @@ impl< } } - fn encaps_for_expanded_key(pk: &MLKEMPublicKeyExpanded) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + fn encaps_for_expanded_key( + pk: &MLKEMPublicKeyExpanded, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::encaps_for_expanded_key_rng(pk, &mut os_rng) + } + + fn encaps_for_expanded_key_rng( + pk: &MLKEMPublicKeyExpanded, + rng: &mut dyn RNG, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + // Source the random message m from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } let mut m = [0u8; 32]; - HashDRBG_SHA512::new_from_os().next_bytes_out(&mut m)?; + rng.next_bytes_out(&mut m)?; let (ss, ct) = Self::encaps_internal(&pk.ek, Some(&pk.A_hat), m); - let mut key = KeyMaterial::::from_bytes_as_type(&ss, KeyType::BytesFullEntropy)?; - key.allow_hazardous_operations(); - key.set_security_strength( SecurityStrength::from_bits(LAMBDA as usize) )?; - key.drop_hazardous_operations(); + let mut key = KeyMaterial::::from_bytes_as_type(&ss, KeyType::CryptographicRandom)?; + do_hazardous_operations(&mut key, |key| { + key.set_security_strength(SecurityStrength::from_bits(LAMBDA as usize)) + })?; Ok((key, ct)) } @@ -808,7 +789,7 @@ impl< /* decapsulation inputs checks described on FIPS 203 section 7.3 */ // 1. (Ciphertext type check) If 𝑐 is not a byte array of length 32(𝑑𝑢 𝑘 + 𝑑𝑣) for the values of 𝑑𝑢, // 𝑑𝑣, and 𝑘 specified by the relevant parameter set, then input checking has failed. - debug_assert_eq!(CT_LEN, 32*( (du as usize)*k + (dv as usize))); + debug_assert_eq!(CT_LEN, 32 * ((du as usize) * k + (dv as usize))); if ct.len() != CT_LEN { return Err(KEMError::LengthError("Ciphertext has the incorrect length")); @@ -821,14 +802,13 @@ impl< // 3. Check that the H(ek) stored in the private key matches the ek also stored in the private key. // Again, this is handled by the MLKEMPrivateKey trait. - /* the actual decaps operation */ let K = Self::decaps_internal(&sk.dk, Some(&sk.A_hat), ct.try_into().unwrap()); - let mut key = KeyMaterial::::from_bytes_as_type(&K, KeyType::BytesFullEntropy)?; - key.allow_hazardous_operations(); - key.set_security_strength( SecurityStrength::from_bits(LAMBDA as usize) )?; - key.drop_hazardous_operations(); + let mut key = KeyMaterial::::from_bytes_as_type(&K, KeyType::CryptographicRandom)?; + do_hazardous_operations(&mut key, |key| { + key.set_security_strength(SecurityStrength::from_bits(LAMBDA as usize)) + })?; Ok(key) } @@ -841,13 +821,31 @@ pub trait MLKEMTrait< const CT_LEN: usize, const SS_LEN: usize, PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta: i16, const du: i16, const dv: i16, const LAMBDA: i16, -> : Sized { +>: Sized +{ + /// Generates a fresh key pair. + fn keygen() -> Result<(PK, SK), KEMError> { + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::keygen_from_rng(&mut os_rng) + } + /// Run a keygen using the provided RNG implementation. + // Should still be ok in FIPS mode, provided that you're using the FIPS-approved RNG. + fn keygen_from_rng(rng: &mut dyn RNG) -> Result<(PK, SK), KEMError> { + // Source the seed from the provided RNG + if rng.security_strength() < SecurityStrength::from_bits(LAMBDA as usize) { + return Err(RNGError::SecurityStrengthInsufficientForAlgorithm)?; + } + let mut seed = KeyMaterial::<64>::new(); + rng.fill_keymaterial_out(&mut seed)?; + Self::keygen_from_seed(&seed) + } /// Imports a secret key from a seed. fn keygen_from_seed(seed: &KeyMaterial<64>) -> Result<(PK, SK), KEMError>; /// Imports a secret key from both a seed and an encoded_sk. @@ -859,10 +857,7 @@ pub trait MLKEMTrait< fn keygen_from_seed_and_encoded( seed: &KeyMaterial<64>, encoded_sk: &[u8; SK_LEN], - ) -> Result< - (PK, SK), - KEMError, - >; + ) -> Result<(PK, SK), KEMError>; /// Given a public key and a secret key, check that the public key matches the secret key. /// This is a sanity check that the public key was generated correctly from the secret key. /// @@ -871,17 +866,20 @@ pub trait MLKEMTrait< /// (in which case a keygen_from_seed is run and then the pk's compared). /// /// Returns either `()` or [KEMError::ConsistencyCheckFailed]. - fn keypair_consistency_check( - pk: &PK, - sk: &SK, - ) -> Result<(), KEMError>; + fn keypair_consistency_check(pk: &PK, sk: &SK) -> Result<(), KEMError>; - /// Same as [KEM::encaps], but acts on an [MLKEMPublicKeyExpanded]. + /// Same as [KEMEncapsulator::encaps], but acts on an [MLKEMPublicKeyExpanded]. fn encaps_for_expanded_key( - pk: &MLKEMPublicKeyExpanded + pk: &MLKEMPublicKeyExpanded, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError>; + + /// Same as [KEM::encaps], but acts on an [MLKEMPublicKeyExpanded] and uses a provided RNG. + fn encaps_for_expanded_key_rng( + pk: &MLKEMPublicKeyExpanded, + rng: &mut dyn RNG, ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError>; - /// Same as [KEM::decaps], but acts on an [MLKEMPrivateKeyExpanded]. + /// Same as [KEMDecapsulator::decaps], but acts on an [MLKEMPrivateKeyExpanded]. fn decaps_with_expanded_key( sk: &MLKEMPrivateKeyExpanded, ct: &[u8], @@ -894,30 +892,16 @@ impl< const CT_LEN: usize, const SS_LEN: usize, PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const k: usize, const eta: i16, const du: i16, const dv: i16, const LAMBDA: i16, -> KEM for MLKEM< - PK_LEN, - SK_LEN, - CT_LEN, - SS_LEN, - PK, - SK, - k, - eta, - du, - dv, - LAMBDA, -> { - /// Generates a fresh key pair. - fn keygen() -> Result<(PK, SK), KEMError> { - Self::keygen_from_os_rng() - } - +> KEMEncapsulator + for MLKEM +{ /// Performs an encapsulation against the given public key, using the library's default internal RNG. /// Returns (shared_secret_key, ciphertext) /// The derived shared secret key is returned as a KeyMaterial with the SecurityStrength set to @@ -929,14 +913,43 @@ impl< /// Output: shared secret key 𝐾 ∈ 𝔹32 . /// Output: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). fn encaps(pk: &PK) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { - Self::encaps_for_expanded_key(&MLKEMPublicKeyExpanded::::from(pk)) + let mut os_rng = HashDRBG_SHA512::new_from_os(); + Self::encaps_rng(pk, &mut os_rng) } + fn encaps_rng( + pk: &PK, + rng: &mut dyn RNG, + ) -> Result<(KeyMaterial, [u8; CT_LEN]), KEMError> { + Self::encaps_for_expanded_key_rng(&MLKEMPublicKeyExpanded::::from(pk), rng) + } +} + +impl< + const PK_LEN: usize, + const SK_LEN: usize, + const CT_LEN: usize, + const SS_LEN: usize, + PK: MLKEMPublicKeyTrait + MLKEMPublicKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, + const k: usize, + const eta: i16, + const du: i16, + const dv: i16, + const LAMBDA: i16, +> KEMDecapsulator + for MLKEM +{ /// Performs a decapsulation of the given ciphertext. /// Returns the shared secret key. /// The derived shared secret key is returned as a KeyMaterial with the SecurityStrength set to /// the security level of the ML-KEM parameter set. + /// As ML-KEM is an implicitly-rejecting KEM, this returns an error only if the ciphertext is invalid (ie the wrong length).. fn decaps(sk: &SK, ct: &[u8]) -> Result, KEMError> { - Self::decaps_with_expanded_key(&MLKEMPrivateKeyExpanded::::from(sk), ct) + Self::decaps_with_expanded_key( + &MLKEMPrivateKeyExpanded::::from(sk), + ct, + ) } } diff --git a/crypto/mlkem/src/mlkem_keys.rs b/crypto/mlkem/src/mlkem_keys.rs index bcf75ba..10fc00b 100644 --- a/crypto/mlkem/src/mlkem_keys.rs +++ b/crypto/mlkem/src/mlkem_keys.rs @@ -1,54 +1,75 @@ use crate::aux_functions::{byte_decode, byte_encode, expandA}; use crate::matrix::{Matrix, Vector}; -use crate::mlkem::{POLY_BYTES, H, q}; +use crate::mlkem::{H, POLY_BYTES, q}; +use crate::mlkem::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_k}; +use crate::mlkem::{MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_k}; +use crate::mlkem::{MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_k}; use crate::{ML_KEM_512_NAME, ML_KEM_768_NAME, ML_KEM_1024_NAME}; -use crate::mlkem::{MLKEM512_k, MLKEM512_PK_LEN, MLKEM512_SK_LEN}; -use crate::mlkem::{MLKEM768_k, MLKEM768_PK_LEN, MLKEM768_SK_LEN}; -use crate::mlkem::{MLKEM1024_k, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN}; -use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial, KeyType}; -use bouncycastle_core::traits::{Hash, KEMPrivateKey, KEMPublicKey, Secret, SecurityStrength}; use bouncycastle_core::errors::KEMError; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; +use bouncycastle_core::traits::{Hash, KEMPrivateKey, KEMPublicKey, Secret, SecurityStrength}; +use bouncycastle_sha3::SHA3_256; use core::fmt; use core::fmt::{Debug, Display, Formatter}; -use bouncycastle_sha3::SHA3_256; - // imports just for docs #[allow(unused_imports)] use crate::mlkem::MLKEMTrait; - - /* Pub Types */ /// ML-KEM-512 Public Key pub type MLKEM512PublicKey = MLKEMPublicKey; /// ML-KEM-512 Private Key -pub type MLKEM512PrivateKey = MLKEMPrivateKey; +pub type MLKEM512PrivateKey = + MLKEMPrivateKey; /// ML-KEM-768 Public Key pub type MLKEM768PublicKey = MLKEMPublicKey; /// ML-KEM-768 Private Key -pub type MLKEM768PrivateKey = MLKEMPrivateKey; +pub type MLKEM768PrivateKey = + MLKEMPrivateKey; /// ML-KEM-1024 Public Key pub type MLKEM1024PublicKey = MLKEMPublicKey; /// ML-KEM-1024 Private Key -pub type MLKEM1024PrivateKey = MLKEMPrivateKey; - +pub type MLKEM1024PrivateKey = + MLKEMPrivateKey; /* Pre-expanded keys for repeated operations */ /// ML-KEM-512 Public Key with a pre-expanded public matrix A for repeated encaps operations. -pub type MLKEM512PublicKeyExpanded = MLKEMPublicKeyExpanded; +pub type MLKEM512PublicKeyExpanded = + MLKEMPublicKeyExpanded; /// ML-KEM-512 Private Key with a pre-expanded public matrix A for repeated decaps operations. -pub type MLKEM512PrivateKeyExpanded = MLKEMPrivateKeyExpanded; +pub type MLKEM512PrivateKeyExpanded = MLKEMPrivateKeyExpanded< + MLKEM512_k, + MLKEM512PublicKey, + MLKEM512PrivateKey, + MLKEM512_SK_LEN, + MLKEM512_PK_LEN, +>; /// ML-KEM-768 Public Key with a pre-expanded public matrix A for repeated encaps operations. -pub type MLKEM768PublicKeyExpanded = MLKEMPublicKeyExpanded; +pub type MLKEM768PublicKeyExpanded = + MLKEMPublicKeyExpanded; /// ML-KEM-768 Private Key with a pre-expanded public matrix A for repeated decaps operations. -pub type MLKEM768PrivateKeyExpanded = MLKEMPrivateKeyExpanded; +pub type MLKEM768PrivateKeyExpanded = MLKEMPrivateKeyExpanded< + MLKEM768_k, + MLKEM768PublicKey, + MLKEM768PrivateKey, + MLKEM768_SK_LEN, + MLKEM768_PK_LEN, +>; /// ML-KEM-1024 Public Key with a pre-expanded public matrix A for repeated encaps operations. -pub type MLKEM1024PublicKeyExpanded = MLKEMPublicKeyExpanded; +pub type MLKEM1024PublicKeyExpanded = + MLKEMPublicKeyExpanded; /// ML-KEM-1024 Private Key with a pre-expanded public matrix A for repeated decaps operations. -pub type MLKEM1024PrivateKeyExpanded = MLKEMPrivateKeyExpanded; +pub type MLKEM1024PrivateKeyExpanded = MLKEMPrivateKeyExpanded< + MLKEM1024_k, + MLKEM1024PublicKey, + MLKEM1024PrivateKey, + MLKEM1024_SK_LEN, + MLKEM1024_PK_LEN, +>; /// An ML-KEM public key. #[derive(Clone)] @@ -58,7 +79,7 @@ pub struct MLKEMPublicKey { } /// General trait for all ML-KEM public keys types. -pub trait MLKEMPublicKeyTrait : KEMPublicKey { +pub trait MLKEMPublicKeyTrait: KEMPublicKey { /// Algorithm 23 pkDecode(𝑝𝑘) /// Reverses the procedure pkEncode. /// Input: Public key 𝑝𝑘 ∈ 𝔹32+32𝑘(bitlen (𝑞−1)−𝑑). @@ -70,16 +91,20 @@ pub trait MLKEMPublicKeyTrait : KEMPublicKe fn compute_hash(&self) -> [u8; 32]; } -pub(crate) trait MLKEMPublicKeyInternalTrait : MLKEMPublicKeyTrait { +pub(crate) trait MLKEMPublicKeyInternalTrait: + MLKEMPublicKeyTrait +{ /// Not exposing a constructor publicly because you should have to get an instance either by /// running a keygen, or by decoding an existing key. - fn new(t_hat: Vector, rho: [u8; 32], ) -> Self; + fn new(t_hat: Vector, rho: [u8; 32]) -> Self; /// Get a ref to t1 fn t_hat(&self) -> &Vector; } -impl MLKEMPublicKeyTrait for MLKEMPublicKey { +impl MLKEMPublicKeyTrait + for MLKEMPublicKey +{ fn pk_decode(pk: &[u8; PK_LEN]) -> Result { let (pk_chunks, last_chunk) = pk.as_chunks::(); @@ -88,7 +113,7 @@ impl MLKEMPublicKeyTrait for MLK debug_assert_eq!(last_chunk.len(), 32); let t_hat = { - let mut t_hat = Vector::::new(); + let mut t_hat = Vector::::new(); for (t_i, pk_chunk) in t_hat.vec.iter_mut().zip(pk_chunks) { t_i.coeffs.copy_from_slice(&byte_decode::<12, POLY_BYTES>(pk_chunk).coeffs); @@ -104,7 +129,6 @@ impl MLKEMPublicKeyTrait for MLK return Err(KEMError::DecodingError("Invalid or corrupted key")); } } - } t_hat @@ -126,15 +150,19 @@ impl MLKEMPublicKeyTrait for MLK } } -impl MLKEMPublicKeyInternalTrait for MLKEMPublicKey { +impl MLKEMPublicKeyInternalTrait + for MLKEMPublicKey +{ fn new(t_hat: Vector, rho: [u8; 32]) -> Self { Self { rho, t_hat } } - fn t_hat(&self) -> &Vector { &self.t_hat } + fn t_hat(&self) -> &Vector { + &self.t_hat + } } -impl KEMPublicKey for MLKEMPublicKey { +impl KEMPublicKey for MLKEMPublicKey { /// Encodes the public key as per FIPS 203 Algorithm 13 /// 19: ekPKE ← ByteEncode12(𝐭)‖𝜌 fn encode(&self) -> [u8; PK_LEN] { @@ -146,8 +174,10 @@ impl KEMPublicKey for MLKEMPublicK /// Encodes the public key as per FIPS 203 Algorithm 13 /// 19: ekPKE ← ByteEncode12(𝐭)‖𝜌 fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize { - debug_assert_eq!(PK_LEN, 12*k*32 + 32); - debug_assert_eq!(POLY_BYTES, 12*32); + debug_assert_eq!(PK_LEN, 12 * k * 32 + 32); + debug_assert_eq!(POLY_BYTES, 12 * 32); + + out.fill(0); let (pk_chunks, last_chunk) = out.as_chunks_mut::(); @@ -164,13 +194,15 @@ impl KEMPublicKey for MLKEMPublicK } fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PK_LEN { return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")) } + if bytes.len() != PK_LEN { + return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")); + } let bytes_sized: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); Self::pk_decode(&bytes_sized) } } -impl Eq for MLKEMPublicKey { } +impl Eq for MLKEMPublicKey {} impl PartialEq for MLKEMPublicKey { fn eq(&self, other: &Self) -> bool { @@ -208,21 +240,23 @@ impl Display for MLKEMPublicKey /// against the same public key, which causes the MLKEMPublicKey struct to take up more memory, but results /// in more efficient repeated encaps() operations. #[derive(Clone)] -pub struct MLKEMPublicKeyExpanded, const PK_LEN: usize> { +pub struct MLKEMPublicKeyExpanded< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const PK_LEN: usize, +> { pub(crate) ek: PK, pub(crate) A_hat: Matrix, } impl, const PK_LEN: usize> -MLKEMPublicKeyInternalTrait for MLKEMPublicKeyExpanded { + MLKEMPublicKeyInternalTrait for MLKEMPublicKeyExpanded +{ fn new(t_hat: Vector, rho: [u8; 32]) -> Self { let ek = PK::new(t_hat, rho); let A_hat = ek.A_hat(); - Self { - ek, - A_hat, - } + Self { ek, A_hat } } fn t_hat(&self) -> &Vector { @@ -231,7 +265,8 @@ MLKEMPublicKeyInternalTrait for MLKEMPublicKeyExpanded } impl, const PK_LEN: usize> -KEMPublicKey for MLKEMPublicKeyExpanded { + KEMPublicKey for MLKEMPublicKeyExpanded +{ fn encode(&self) -> [u8; PK_LEN] { let mut pk = [0u8; PK_LEN]; self.encode_out(&mut pk); @@ -240,28 +275,36 @@ KEMPublicKey for MLKEMPublicKeyExpanded { } fn encode_out(&self, out: &mut [u8; PK_LEN]) -> usize { + out.fill(0); + self.ek.encode_out(out) } fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != PK_LEN { return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")) } + if bytes.len() != PK_LEN { + return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")); + } let bytes_sized: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); Self::pk_decode(&bytes_sized) } } -impl, const PK_LEN: usize> -PartialEq for MLKEMPublicKeyExpanded { +impl, const PK_LEN: usize> PartialEq + for MLKEMPublicKeyExpanded +{ fn eq(&self, other: &Self) -> bool { self.encode() == other.encode() } } -impl, const PK_LEN: usize> -Eq for MLKEMPublicKeyExpanded {} +impl, const PK_LEN: usize> Eq + for MLKEMPublicKeyExpanded +{ +} -impl, const PK_LEN: usize> -Debug for MLKEMPublicKeyExpanded { +impl, const PK_LEN: usize> Debug + for MLKEMPublicKeyExpanded +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -274,8 +317,9 @@ Debug for MLKEMPublicKeyExpanded { } } -impl, const PK_LEN: usize> -Display for MLKEMPublicKeyExpanded { +impl, const PK_LEN: usize> Display + for MLKEMPublicKeyExpanded +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -289,7 +333,8 @@ Display for MLKEMPublicKeyExpanded { } impl, const PK_LEN: usize> -MLKEMPublicKeyTrait for MLKEMPublicKeyExpanded { + MLKEMPublicKeyTrait for MLKEMPublicKeyExpanded +{ fn pk_decode(pk: &[u8; PK_LEN]) -> Result { let ek = PK::pk_decode(pk)?; let A_hat = ek.A_hat(); @@ -306,23 +351,17 @@ MLKEMPublicKeyTrait for MLKEMPublicKeyExpanded { } impl, const PK_LEN: usize> From<&PK> -for MLKEMPublicKeyExpanded { + for MLKEMPublicKeyExpanded +{ /// Fully expands the intermediate values needed for performing multiple encaps operations /// against the same public key, which causes the MLKEMPublicKey struct to take up fn from(ek: &PK) -> Self { let A_hat = ek.A_hat(); - Self { - ek: ek.clone(), - A_hat, - } + Self { ek: ek.clone(), A_hat } } } - - - - /// An ML-KEM private key. #[derive(Clone)] pub struct MLKEMPrivateKey< @@ -343,35 +382,37 @@ impl< PK: MLKEMPublicKeyInternalTrait, const SK_LEN: usize, const PK_LEN: usize, -> MLKEMPrivateKey { +> MLKEMPrivateKey +{ /// As described on Algorithm 16 line /// 3: dk ← (dkPKE ‖ ek ‖ H(ek) ‖ 𝑧) fn sk_encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { - debug_assert_eq!(SK_LEN, /* dk_pke*/12*k*32 + /*ek*/PK_LEN + /*H(ek)*/32 + /*z*/32); + out.fill(0); + + debug_assert_eq!(SK_LEN, /* dk_pke*/ 12*k*32 + /*ek*/PK_LEN + /*H(ek)*/32 + /*z*/32); let mut pos = 0usize; /* dk_pke */ // Alg 13; line 20: dkPKE ← ByteEncode12(𝐬) for i in 0..k { - out[i*POLY_BYTES .. (i+1)*POLY_BYTES].copy_from_slice(&byte_encode::<12, POLY_BYTES>( - &self.s_hat[i] - )); + out[i * POLY_BYTES..(i + 1) * POLY_BYTES] + .copy_from_slice(&byte_encode::<12, POLY_BYTES>(&self.s_hat[i])); } pos += k * POLY_BYTES; /* ek */ // Alg 13; line 19: ekPKE ← ByteEncode12(𝐭)‖𝜌 debug_assert_eq!(self.ek.encode().len(), PK_LEN); - out[pos .. pos + PK_LEN].copy_from_slice(&self.ek.encode()); + out[pos..pos + PK_LEN].copy_from_slice(&self.ek.encode()); pos += PK_LEN; /* H(ek) */ - out[pos .. pos + 32].copy_from_slice(&self.pk_hash); + out[pos..pos + 32].copy_from_slice(&self.pk_hash); pos += 32; /* z */ - out[pos .. pos + 32].copy_from_slice(&self.z); + out[pos..pos + 32].copy_from_slice(&self.z); debug_assert_eq!(pos + 32, SK_LEN); SK_LEN @@ -383,7 +424,9 @@ pub trait MLKEMPrivateKeyTrait< const k: usize, PK: MLKEMPublicKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize> : KEMPrivateKey { + const PK_LEN: usize, +>: KEMPrivateKey +{ /// Get a ref to the seed, if there is one stored with this private key fn seed(&self) -> Option>; @@ -395,16 +438,16 @@ pub trait MLKEMPrivateKeyTrait< fn sk_decode(sk: &[u8; SK_LEN]) -> Result; } -pub(crate) trait MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, const PK_LEN: usize> { +pub(crate) trait MLKEMPrivateKeyInternalTrait< + const k: usize, + PK: MLKEMPublicKeyTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> +{ /// Not exposing a constructor publicly because you should have to get an instance either by /// running a keygen, or by decoding an existing key. - fn new( - s_hat: Vector, - ek: PK, - h: [u8; 32], - z: [u8; 32], - seed_d: Option<[u8; 32]>, - ) -> Self; + fn new(s_hat: Vector, ek: PK, h: [u8; 32], z: [u8; 32], seed_d: Option<[u8; 32]>) -> Self; /// Get a ref to s_hat fn s_hat(&self) -> &Vector; @@ -412,9 +455,13 @@ pub(crate) trait MLKEMPrivateKeyInternalTrait &[u8; 32]; } - -impl, const SK_LEN: usize, const PK_LEN: usize> - MLKEMPrivateKeyTrait for MLKEMPrivateKey { +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> MLKEMPrivateKeyTrait for MLKEMPrivateKey +{ fn seed(&self) -> Option> { if self.seed_d.is_none() { None @@ -423,14 +470,17 @@ impl, const SK_LEN: u tmp[..32].copy_from_slice(&self.seed_d.unwrap()); tmp[32..].copy_from_slice(&self.z); let mut seed = KeyMaterial::<64>::from_bytes_as_type(&tmp, KeyType::Seed).unwrap(); - seed.allow_hazardous_operations(); - seed.set_security_strength( match k { - 2 => SecurityStrength::_128bit, - 3 => SecurityStrength::_192bit, - 4 => SecurityStrength::_256bit, - _ => unreachable!("Invalid mlkem param set"), - }).unwrap(); - seed.drop_hazardous_operations(); + + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_security_strength(match k { + 2 => SecurityStrength::_128bit, + 3 => SecurityStrength::_192bit, + 4 => SecurityStrength::_256bit, + _ => unreachable!("Invalid mlkem param set"), + }) + }) + .unwrap(); + Some(seed) } } @@ -444,7 +494,7 @@ impl, const SK_LEN: u } fn sk_decode(sk: &[u8; SK_LEN]) -> Result { - debug_assert_eq!(SK_LEN, /* dk_pke*/12*k*32 + /*ek*/PK_LEN + /*H(ek)*/32 + /*z*/32); + debug_assert_eq!(SK_LEN, /* dk_pke*/ 12*k*32 + /*ek*/PK_LEN + /*H(ek)*/32 + /*z*/32); let mut pos = 0usize; @@ -453,7 +503,7 @@ impl, const SK_LEN: u // for (s_i, sk_chunk) in s_hat.0.iter_mut().zip(sk_chunks) { for i in 0..k { s_hat[i] = byte_decode::<12, POLY_BYTES>( - sk[i*POLY_BYTES .. (i+1)*POLY_BYTES].try_into().unwrap() + sk[i * POLY_BYTES..(i + 1) * POLY_BYTES].try_into().unwrap(), ); // FIPS 203 says: @@ -461,9 +511,9 @@ impl, const SK_LEN: u // segment of its input into an integer modulo 212 = 4096 and then reduces the result // modulo 𝑞. This is no longer a one-to-one operation. Indeed, some 12-bit segments could // correspond to an integer greater than 𝑞 − 1 = 3328 but less than 4096." - // Since we are here in the d=12 case, we can and should check that all coeffs are less than q-1 + // Since we are here in the d=12 case, we can and should check that all coeffs are less than q for coeff in s_hat[i].coeffs.iter() { - if *coeff < -q || *coeff >= q { + if *coeff < 0 || *coeff >= q { return Err(KEMError::DecodingError("Invalid or corrupted key")); } } @@ -471,29 +521,36 @@ impl, const SK_LEN: u pos += k * POLY_BYTES; /* ek */ - let ek = PK::pk_decode(sk[pos .. pos + PK_LEN].try_into().unwrap())?; + let ek = PK::pk_decode(sk[pos..pos + PK_LEN].try_into().unwrap())?; pos += PK_LEN; /* H(ek) */ - let h_pk: [u8; 32] = sk[pos .. pos + 32].try_into().unwrap(); + let h_pk: [u8; 32] = sk[pos..pos + 32].try_into().unwrap(); pos += 32; // This satisfies the "Decapsulation input check #3) in FIPS 203 section 7.3. // We're doing it here on key load rather than as part of the decapsulation for performance - // because if you're doing multiple decapsulations, you only need to perform this check once. + // because if you're doing multiple decapsulations, you only need to perform this check once. if h_pk != ek.compute_hash() { - return Err(KEMError::ConsistencyCheckFailed("Corrupted private key: computed hash of ek != h_ek stored in private key")); + return Err(KEMError::ConsistencyCheckFailed( + "Corrupted private key: computed hash of ek != h_ek stored in private key", + )); } /* z */ - let z: [u8; 32] = sk[pos .. pos + 32].try_into().unwrap(); + let z: [u8; 32] = sk[pos..pos + 32].try_into().unwrap(); Ok(Self::new(s_hat, ek, h_pk, z, None)) } } -impl, const SK_LEN: usize, const PK_LEN: usize> - MLKEMPrivateKeyInternalTrait for MLKEMPrivateKey { +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> MLKEMPrivateKeyInternalTrait for MLKEMPrivateKey +{ /// Note to future maintainers: FIPS 203 section 7.3 requires that ek be hashed and compared to pk_hash. fn new( s_hat: Vector, @@ -502,22 +559,25 @@ impl, const SK_LEN: u z: [u8; 32], seed_d: Option<[u8; 32]>, ) -> Self { - Self { - s_hat, - ek, - pk_hash, - z, - seed_d: seed_d.clone(), - } + Self { s_hat, ek, pk_hash, z, seed_d: seed_d.clone() } } - fn s_hat(&self) -> &Vector { &self.s_hat } + fn s_hat(&self) -> &Vector { + &self.s_hat + } - fn z(&self) -> &[u8; 32] { &self.z } + fn z(&self) -> &[u8; 32] { + &self.z + } } -impl, const SK_LEN: usize, const PK_LEN: usize -> KEMPrivateKey for MLKEMPrivateKey { +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> KEMPrivateKey for MLKEMPrivateKey +{ fn encode(&self) -> [u8; SK_LEN] { let mut out = [0u8; SK_LEN]; self.encode_out(&mut out); @@ -526,22 +586,39 @@ impl, const SK_LEN: u } fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + self.sk_encode_out(out) } fn from_bytes(bytes: &[u8]) -> Result { - if bytes.len() != SK_LEN { return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")) } + if bytes.len() != SK_LEN { + return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")); + } + if bytes.len() != SK_LEN { + return Err(KEMError::DecodingError("Provided key bytes are the incorrect length")); + } let bytes_sized: [u8; SK_LEN] = bytes[..SK_LEN].try_into().unwrap(); Self::sk_decode(&bytes_sized) } } -impl, const SK_LEN: usize, const PK_LEN: usize> - Eq for MLKEMPrivateKey {} +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> Eq for MLKEMPrivateKey +{ +} -impl, const SK_LEN: usize, const PK_LEN: usize> - PartialEq for MLKEMPrivateKey +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> PartialEq for MLKEMPrivateKey { fn eq(&self, other: &Self) -> bool { let self_encoded = self.encode(); @@ -550,20 +627,30 @@ impl, const SK_LEN: u } } -impl, const SK_LEN: usize, const PK_LEN: usize> -Secret for MLKEMPrivateKey {} +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> Secret for MLKEMPrivateKey +{ +} /// Debug impl mainly to prevent the secret key from being printed in logs. -impl, const SK_LEN: usize, const PK_LEN: usize> - fmt::Debug for MLKEMPrivateKey +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> fmt::Debug for MLKEMPrivateKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - let alg = match k { - 2 => ML_KEM_512_NAME, - 3 => ML_KEM_768_NAME, - 4 => ML_KEM_1024_NAME, - _ => panic!("Unsupported key length"), - }; + let alg = match k { + 2 => ML_KEM_512_NAME, + 3 => ML_KEM_768_NAME, + 4 => ML_KEM_1024_NAME, + _ => panic!("Unsupported key length"), + }; write!( f, "MLKEMPrivateKey {{ alg: {}, pub_key_hash: {:x?}, has_seed: {} }}", @@ -575,8 +662,12 @@ impl, const SK_LEN: u } /// Display impl mainly to prevent the secret key from being printed in logs. -impl, const SK_LEN: usize, const PK_LEN: usize> - Display for MLKEMPrivateKey +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> Display for MLKEMPrivateKey { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { @@ -596,19 +687,23 @@ impl, const SK_LEN: u } /// Zeroizing drop -impl, const SK_LEN: usize, const PK_LEN: usize> -Drop for MLKEMPrivateKey +impl< + const k: usize, + PK: MLKEMPublicKeyInternalTrait, + const SK_LEN: usize, + const PK_LEN: usize, +> Drop for MLKEMPrivateKey { fn drop(&mut self) { // s_hat, has its own zeroizing drop self.pk_hash.fill(0u8); self.z.fill(0u8); - if self.seed_d.is_some() { self.seed_d.as_mut().unwrap().fill(0u8); } + if self.seed_d.is_some() { + self.seed_d.as_mut().unwrap().fill(0u8); + } } } - - /// A fully expanded ML-KEM private key that includes the intermediate values needed for performing /// multiple decaps operations with the same private key, which causes the private key struct to /// take up more memory, but results in more efficient repeated decaps() operations. @@ -616,48 +711,50 @@ Drop for MLKEMPrivateKey pub struct MLKEMPrivateKeyExpanded< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize + const PK_LEN: usize, > { _phantom: core::marker::PhantomData, pub(crate) dk: SK, - pub(crate) A_hat: Matrix, + pub(crate) A_hat: Matrix, } impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> From<&SK> -for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> From<&SK> for MLKEMPrivateKeyExpanded +{ /// Fully expands the intermediate values needed for performing multiple encaps operations /// against the same public key, which causes the MLKEMPublicKey struct to take up fn from(dk: &SK) -> Self { let A_hat = dk.pk().A_hat(); - Self { - _phantom: core::marker::PhantomData, - dk: dk.clone(), - A_hat, - } + Self { _phantom: core::marker::PhantomData, dk: dk.clone(), A_hat } } } impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> KEMPrivateKey for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> KEMPrivateKey for MLKEMPrivateKeyExpanded +{ fn encode(&self) -> [u8; SK_LEN] { self.dk.encode() } fn encode_out(&self, out: &mut [u8; SK_LEN]) -> usize { + out.fill(0); + self.dk.encode_out(out) } @@ -669,10 +766,12 @@ impl< impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> PartialEq for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> PartialEq for MLKEMPrivateKeyExpanded +{ fn eq(&self, other: &Self) -> bool { self.dk.eq(&other.dk) } @@ -681,26 +780,34 @@ impl< impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> Eq for MLKEMPrivateKeyExpanded {} + const PK_LEN: usize, +> Eq for MLKEMPrivateKeyExpanded +{ +} impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> Secret for MLKEMPrivateKeyExpanded {} + const PK_LEN: usize, +> Secret for MLKEMPrivateKeyExpanded +{ +} impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> Drop for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> Drop for MLKEMPrivateKeyExpanded +{ fn drop(&mut self) { // Nothing to do since self.sk already impls zeroizing Drop } @@ -709,10 +816,12 @@ impl< impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> Debug for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> Debug for MLKEMPrivateKeyExpanded +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -733,10 +842,12 @@ impl< impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> Display for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> Display for MLKEMPrivateKeyExpanded +{ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { let alg = match k { 2 => ML_KEM_512_NAME, @@ -757,10 +868,13 @@ impl< impl< const k: usize, PK: MLKEMPublicKeyInternalTrait, - SK: MLKEMPrivateKeyTrait + MLKEMPrivateKeyInternalTrait, + SK: MLKEMPrivateKeyTrait + + MLKEMPrivateKeyInternalTrait, const SK_LEN: usize, - const PK_LEN: usize -> MLKEMPrivateKeyTrait for MLKEMPrivateKeyExpanded { + const PK_LEN: usize, +> MLKEMPrivateKeyTrait + for MLKEMPrivateKeyExpanded +{ fn seed(&self) -> Option> { self.dk.seed() } @@ -777,10 +891,6 @@ impl< let dk = SK::sk_decode(sk)?; let A_hat = dk.pk().A_hat(); - Ok(Self { - _phantom: core::marker::PhantomData, - dk: dk.clone(), - A_hat, - }) + Ok(Self { _phantom: core::marker::PhantomData, dk: dk.clone(), A_hat }) } } diff --git a/crypto/mlkem/src/polynomial.rs b/crypto/mlkem/src/polynomial.rs index ae518fc..60ccaac 100644 --- a/crypto/mlkem/src/polynomial.rs +++ b/crypto/mlkem/src/polynomial.rs @@ -4,16 +4,19 @@ use core::fmt; use core::fmt::{Debug, Display, Formatter}; use core::ops::{Index, IndexMut}; -use bouncycastle_core::traits::Secret; -use crate::aux_functions::{barrett_reduce, cond_sub_q, montgomery_reduce, mul_mont, ntt_base_mult, ZETAS, ZETAS_INV}; +use crate::aux_functions::{ + ZETAS, ZETAS_INV, barrett_reduce, montgomery_reduce, mul_mont, ntt_base_mult, +}; use crate::mlkem::{N, q}; +use bouncycastle_core::traits::Secret; /// A polynomial over the ML-KEM ring. -/// Dev note: this doesn't strictly need to be pub ... ie there's no good reason for a caller to use this class directly, -/// but in order to test the Debug and Display traits, you need STD, so those can't be tested from inline tests in this file -/// and the real unit tests are in a different crate, so here we are. +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. #[derive(Clone)] -pub struct Polynomial{ pub(crate) coeffs: [i16; N] } +pub struct Polynomial { + /// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. + pub coeffs: [i16; N], +} /// Convenience function to avoid ".0" all over the place. impl Index for Polynomial { @@ -40,10 +43,10 @@ impl Polynomial { pub(crate) fn from_msg(m: [u8; 32]) -> Self { let mut w = Polynomial::new(); - for (i, b) in m.iter().enumerate().take(N/8) { + for (i, b) in m.iter().enumerate() { for j in 0..8 { let mask = -(((*b >> j) & 1) as i16); - w[8 * i + j] = mask /*as i32*/ & ((q + 1) / 2); + w[8 * i + j] = mask & ((q + 1) / 2); } } @@ -51,22 +54,22 @@ impl Polynomial { } /// Convert a Polynomial back into a message m - pub(crate) fn to_msg(mut self) -> [u8; 32] { - - const LOWER: i32 = q as i32 >> 2; // 832 + pub(crate) fn to_msg(self) -> [u8; 32] { + const LOWER: i32 = q as i32 >> 2; // 832 const UPPER: i32 = q as i32 - LOWER; // 2497 let mut msg = [0u8; 32]; // you would expect to use a full reduce() here, but since this is data coming from - // out matrix math and not from an attacker, we can get away with the lighter cond_sub_q() - self.cond_sub_q(); + // out matrix math and not from an attacker, we can get away with the lighter cond_sub_q(). + // Actually; further testing against the bc-test-data set of KATs shows that everything passes even with nothing + // self.cond_sub_q(); // for (i, item) in msg.iter_mut().enumerate().take(N/8) { - for i in 0 .. N/8 { + for i in 0..N / 8 { for j in 0..8 { let c_j = self[8 * i + j] as i32; - let t = (((LOWER - c_j) & (c_j - UPPER)) >> 31) & 0x0000000000000001; + let t = (((LOWER - c_j) & (c_j - UPPER)) >> 31) & 0x01; msg[i] |= (t << j) as u8; } } @@ -131,12 +134,12 @@ impl Polynomial { // s.cond_sub_q(); match dv { - 4 => { // MLKEM512 and MLKEM768 - for i in 0..N/8 { + 4 => { + // MLKEM512 and MLKEM768 + for i in 0..N / 8 { // fill the temp array t for (j, item) in t.iter_mut().enumerate() { - *item = ((((self[8 * i + j] as i32) << 4) + (q as i32 /2)) - / (q as i32) + *item = ((((self[8 * i + j] as i32) << 4) + (q as i32 / 2)) / (q as i32) & 15) as u8; } @@ -146,13 +149,13 @@ impl Polynomial { out[idx + 3] = t[6] | (t[7] << 4); idx += 4; } - }, - 5 => { // MLKEM1024 - for i in 0..N/8 { + } + 5 => { + // MLKEM1024 + for i in 0..N / 8 { // fill the temp array t for (j, item) in t.iter_mut().enumerate() { - *item = (((((self[8 * i + j] as i32) << 5) + (q as i32 /2)) - / (q as i32)) + *item = (((((self[8 * i + j] as i32) << 5) + (q as i32 / 2)) / (q as i32)) & 31) as u8; } @@ -163,7 +166,7 @@ impl Polynomial { out[idx + 4] = (t[6] >> 2) | (t[7] << 3); idx += 5; } - }, + } _ => unreachable!(), }; } @@ -185,55 +188,54 @@ impl Polynomial { // if self.m_engine.poly_compressed_bytes() == 128 { match dv { - 4 => { // MLKEM512 and MLKEM768 - for i in 0..N/2 { + 4 => { + // MLKEM512 and MLKEM768 + for i in 0..N / 2 { v[2 * i] = - (((((compressed_v[idx] & 15) as i16) as i32 * (q as i32)) + 8) >> 4) - as i16; + (((((compressed_v[idx] & 15) as i16) as i32 * (q as i32)) + 8) >> 4) as i16; v[2 * i + 1] = - (((((compressed_v[idx] >> 4) as i16) as i32 * (q as i32)) + 8) >> 4) - as i16; + (((((compressed_v[idx] >> 4) as i16) as i32 * (q as i32)) + 8) >> 4) as i16; idx += 1; } - }, - 5 => { // MLKEM1024 + } + 5 => { + // MLKEM1024 let mut t = [0u8; 8]; - for i in 0..N/8 { + for i in 0..N / 8 { t[0] = compressed_v[idx]; - t[1] = - (compressed_v[idx] >> 5) | (compressed_v[idx + 1] << 3); + t[1] = (compressed_v[idx] >> 5) | (compressed_v[idx + 1] << 3); t[2] = compressed_v[idx + 1] >> 2; - t[3] = (compressed_v[idx + 1] >> 7) - | (compressed_v[idx + 2] << 1); - t[4] = (compressed_v[idx + 2] >> 4) - | (compressed_v[idx + 3] << 4); + t[3] = (compressed_v[idx + 1] >> 7) | (compressed_v[idx + 2] << 1); + t[4] = (compressed_v[idx + 2] >> 4) | (compressed_v[idx + 3] << 4); t[5] = compressed_v[idx + 3] >> 1; - t[6] = (compressed_v[idx + 3] >> 6) - | (compressed_v[idx + 4] << 2); + t[6] = (compressed_v[idx + 3] >> 6) | (compressed_v[idx + 4] << 2); t[7] = compressed_v[idx + 4] >> 3; idx += 5; for (j, item) in t.iter_mut().enumerate() { v[8 * i + j] = (((*item & 31) as i32 * (q as i32) + 16) >> 5) as i16; } } - }, + } _ => unreachable!(), } v } - pub(crate) fn cond_sub_q(&mut self) { - for i in 0..N { - self[i] = cond_sub_q(self[i]); - } - } + // not currently used, but I'll leave it here because it's useful for debugging if you want to output values + // that are normalized to [0,q] to compare against intermediate results from other libraries. + // pub(crate) fn cond_sub_q(&mut self) { + // for i in 0..N { + // self[i] = cond_sub_q(self[i]); + // } + // } /// Algorithm 9 NTT(𝑓) /// Computes the NTT representation 𝑓_hat of the given polynomial 𝑓 ∈ 𝑅𝑞. /// Input: array 𝑓 ∈ ℤ256 ▷ the coefficients of the input polynomial /// Output: array 𝑓_hat ∈ ℤ256 ▷ the coefficients of the NTT of the input polynomial - pub(crate) fn ntt(&mut self) { + /// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. + pub fn ntt(&mut self) { let mut len = 128; let mut k = 1; @@ -259,8 +261,9 @@ impl Polynomial { /// Computes the polynomial 𝑓 ∈ 𝑅𝑞 that corresponds to the given NTT representation 𝑓 ∈ 𝑇𝑞. /// Input: array 𝑓 ∈ ℤ256 ▷ the coefficients of input NTT representation /// Output: array 𝑓 ∈ ℤ256 ▷ the coefficients of the inverse NTT of the input - pub(crate) fn inv_ntt(&mut self) { - // FIPS 203 ALg 10 wants you to copy f_hat into f, and then act of f + /// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. + pub fn inv_ntt(&mut self) { + // FIPS 203 Alg 10 wants you to copy f_hat into f, and then act on f // but we're going to do this in-place for memory-saving reasons. let mut len = 2; @@ -296,11 +299,12 @@ impl Polynomial { /// Multiplication of two polynomials in NTT domain /// /// Borrowed from: -/// https://github.com/pq-crystals/kyber/blob/main/ref/poly.c#L290 -pub(crate) fn base_mult_montgomery(a: &Polynomial, b: &Polynomial) -> Polynomial { +/// +/// Note: this is exposed publicly only for testing purposes and there is no good reason to use it in production code. +pub fn base_mult_montgomery(a: &Polynomial, b: &Polynomial) -> Polynomial { let mut r = Polynomial::new(); - for i in 0..(N/4) { + for i in 0..(N / 4) { ntt_base_mult( &mut r.coeffs, 4 * i, diff --git a/crypto/mlkem/tests/bc_test_data.rs b/crypto/mlkem/tests/bc_test_data.rs index d85ee4a..bfb0a64 100644 --- a/crypto/mlkem/tests/bc_test_data.rs +++ b/crypto/mlkem/tests/bc_test_data.rs @@ -4,18 +4,66 @@ #[cfg(test)] mod bc_test_data { - use std::{fs}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{ + KEMDecapsulator, KEMPrivateKey, KEMPublicKey, SecurityStrength, + }; use bouncycastle_hex as hex; - use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial512, KeyType}; - use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength, KEM}; - use bouncycastle_mlkem::{MLKEM1024PrivateKey, MLKEM1024PublicKey, MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEMTrait, MLKEM1024, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM512, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768, MLKEM768_PK_LEN, MLKEM768_SK_LEN}; + use bouncycastle_mlkem::{ + MLKEM512, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512PrivateKey, MLKEM512PublicKey, + MLKEM768, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024PrivateKey, MLKEM1024PublicKey, + MLKEMTrait, + }; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../bc-test-data/pqc/crypto/mlkem"; + const TEST_DATA_PATH: &str = "../bc-test-data/pqc/crypto/mlkem"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } - const TEST_DATA_PATH: &str = "../../../bc-test-data/pqc/crypto/mlkem"; + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + Ok(contents) + } #[test] #[allow(non_snake_case)] fn ML_KEM_keyGen() { - let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-KEM-keyGen.txt").unwrap(); + let contents = match get_test_data("ML-KEM-keyGen.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = KeyGenTestCase::parse(contents); for test_case in test_cases { @@ -42,7 +90,21 @@ mod bc_test_data { impl KeyGenTestCase { fn new() -> Self { - Self { vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, z: String::new(), d: String::new(), ek: String::new(), dk: String::new() } + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + tc_id: 0, + z: String::new(), + d: String::new(), + ek: String::new(), + dk: String::new(), + } } fn is_full(&self) -> bool { @@ -56,7 +118,9 @@ mod bc_test_data { let (tag, value) = match line.split_once(" = ") { Some(pair) => pair, None => { - if test_case.is_full() { test_cases.push(test_case.clone()); } + if test_case.is_full() { + test_cases.push(test_case.clone()); + } continue; } }; @@ -86,42 +150,46 @@ mod bc_test_data { assert_eq!(self.mode, "keyGen"); let mut seed_bytes = [0u8; 64]; - seed_bytes[..32].copy_from_slice(&hex::decode(&self.d).unwrap()); - seed_bytes[32..].copy_from_slice(&hex::decode(&self.z).unwrap()); + seed_bytes[..32].copy_from_slice(&*hex::decode(&self.d).unwrap()); + seed_bytes[32..].copy_from_slice(&*hex::decode(&self.z).unwrap()); - let mut seed = KeyMaterial512::from_bytes_as_type( - &seed_bytes, - KeyType::Seed, - ).unwrap(); + let mut seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); // for the purposes of the test cases, accept an all-zero seed - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::Seed).unwrap(); - seed.set_security_strength(SecurityStrength::_256bit).unwrap(); - seed.drop_hazardous_operations(); + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed)?; + seed.set_security_strength(SecurityStrength::_256bit) + }) + .unwrap(); match self.parameter_set.as_str() { "ML-KEM-512" => { let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - let pk_sized: [u8; MLKEM512_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); + let pk_sized: [u8; MLKEM512_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); assert_eq!(pk.encode(), pk_sized); - let sk_sized: [u8; MLKEM512_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); + let sk_sized: [u8; MLKEM512_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); assert_eq!(sk.encode(), sk_sized); - }, + } "ML-KEM-768" => { let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); - let pk_sized: [u8; MLKEM768_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); + let pk_sized: [u8; MLKEM768_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); assert_eq!(pk.encode(), pk_sized); - let sk_sized: [u8; MLKEM768_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); + let sk_sized: [u8; MLKEM768_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); assert_eq!(sk.encode(), sk_sized); - }, + } "ML-KEM-1024" => { let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); - let pk_sized: [u8; MLKEM1024_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); + let pk_sized: [u8; MLKEM1024_PK_LEN] = + hex::decode(&self.ek).unwrap().try_into().unwrap(); assert_eq!(pk.encode(), pk_sized); - let sk_sized: [u8; MLKEM1024_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); + let sk_sized: [u8; MLKEM1024_SK_LEN] = + hex::decode(&self.dk).unwrap().try_into().unwrap(); assert_eq!(sk.encode(), sk_sized); - }, + } val => panic!("Invalid parameter set: {}", val), } } @@ -130,7 +198,11 @@ mod bc_test_data { #[test] #[allow(non_snake_case)] fn ML_KEM_encapDecap() { - let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-KEM-encapDecap.txt").unwrap(); + let contents = match get_test_data("ML-KEM-encapDecap.txt") { + Ok(contents) => contents, + Err(()) => return, + }; + let test_cases = EncapDecapTestCase::parse(contents); let num_tests = test_cases.len(); @@ -162,7 +234,23 @@ mod bc_test_data { impl EncapDecapTestCase { fn new() -> Self { - Self { vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), function: String::new(), tc_id: 0, ek: String::new(), dk: String::new(), m: String::new(), c: String::new(), k: String::new() } + Self { + vs_id: 0, + algorithm: String::new(), + mode: String::new(), + revision: String::new(), + is_sample: false, + tg_id: 0, + test_type: String::new(), + parameter_set: String::new(), + function: String::new(), + tc_id: 0, + ek: String::new(), + dk: String::new(), + m: String::new(), + c: String::new(), + k: String::new(), + } } fn is_full(&self) -> bool { @@ -176,7 +264,9 @@ mod bc_test_data { let (tag, value) = match line.split_once(" = ") { Some(pair) => pair, None => { - if test_case.is_full() { test_cases.push(test_case.clone()); } + if test_case.is_full() { + test_cases.push(test_case.clone()); + } continue; } }; @@ -211,7 +301,8 @@ mod bc_test_data { "ML-KEM-512" => { match self.function.as_str() { "encapsulation" => { - let pk = MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + let pk = MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) + .unwrap(); let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); let (ss, ct) = MLKEM512::encaps_internal(&pk, None, m); @@ -220,22 +311,25 @@ mod bc_test_data { assert_eq!(ss, expected_ss.as_slice()); assert_eq!(ct, expected_ct.as_slice()); - }, + } "decapsulation" => { - let sk = MLKEM512PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); + let sk = + MLKEM512PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) + .unwrap(); let ct = hex::decode(&self.c).unwrap(); let ss = MLKEM512::decaps(&sk, ct.as_slice()).unwrap(); let expected_ss = hex::decode(&self.k).unwrap(); assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); - }, + } _ => panic!("Invalid function: {}", self.function), }; - }, + } "ML-KEM-768" => { match self.function.as_str() { "encapsulation" => { - let pk = MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + let pk = MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) + .unwrap(); let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); let (ss, ct) = MLKEM768::encaps_internal(&pk, None, m); @@ -244,22 +338,26 @@ mod bc_test_data { assert_eq!(ss, expected_ss.as_slice()); assert_eq!(ct, expected_ct.as_slice()); - }, + } "decapsulation" => { - let sk = MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); + let sk = + MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) + .unwrap(); let ct = hex::decode(&self.c).unwrap(); let ss = MLKEM768::decaps(&sk, ct.as_slice()).unwrap(); let expected_ss = hex::decode(&self.k).unwrap(); assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); - }, + } _ => panic!("Invalid function: {}", self.function), }; - }, + } "ML-KEM-1024" => { match self.function.as_str() { "encapsulation" => { - let pk = MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); + let pk = + MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) + .unwrap(); let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); let (ss, ct) = MLKEM1024::encaps_internal(&pk, None, m); @@ -268,20 +366,22 @@ mod bc_test_data { assert_eq!(ss, expected_ss.as_slice()); assert_eq!(ct, expected_ct.as_slice()); - }, + } "decapsulation" => { - let sk = MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); + let sk = + MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) + .unwrap(); let ct = hex::decode(&self.c).unwrap(); let ss = MLKEM1024::decaps(&sk, ct.as_slice()).unwrap(); let expected_ss = hex::decode(&self.k).unwrap(); assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); - }, + } _ => panic!("Invalid function: {}", self.function), }; - }, + } val => panic!("Invalid parameter set: {}", val), } } } -} \ No newline at end of file +} diff --git a/crypto/mlkem/tests/mlkem_key_tests.rs b/crypto/mlkem/tests/mlkem_key_tests.rs index 830196f..e6bdca1 100644 --- a/crypto/mlkem/tests/mlkem_key_tests.rs +++ b/crypto/mlkem/tests/mlkem_key_tests.rs @@ -1,28 +1,37 @@ #[cfg(test)] mod mlkem_key_tests { use bouncycastle_core::errors::KEMError; - use bouncycastle_core::key_material::{KeyMaterial512, KeyType}; - use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, KEM}; - use bouncycastle_mlkem::{MLKEMPrivateKeyTrait, MLKEMPublicKeyTrait, MLKEMTrait}; - use bouncycastle_mlkem::{MLKEM512, MLKEM768, MLKEM1024}; - use bouncycastle_mlkem::{MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEM1024PrivateKey, MLKEM1024PublicKey}; - use bouncycastle_mlkem::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512_CT_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN, MLKEM_SS_LEN}; + use bouncycastle_core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength}; use bouncycastle_hex as hex; - + use bouncycastle_mlkem::{MLKEM512, MLKEM768, MLKEM1024}; + use bouncycastle_mlkem::{ + MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM512PrivateKeyExpanded, MLKEM512PublicKeyExpanded, + MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEMPrivateKeyTrait, + MLKEMPublicKeyTrait, MLKEMTrait, + }; + use bouncycastle_mlkem::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, + }; #[test] fn core_framework_tests() { - use bouncycastle_core_test_framework::kem::{TestFrameworkKEMKeys}; + use bouncycastle_core_test_framework::kem::TestFrameworkKEMKeys; + let tf = TestFrameworkKEMKeys::new(); - tf.test_keys::(); - tf.test_keys::(); - tf.test_keys::(); + tf.test_keys::( + MLKEM512::keygen, + ); + tf.test_keys::( + MLKEM768::keygen, + ); + tf.test_keys::(MLKEM1024::keygen); } #[test] fn pk_from_sk() { - /* MLDSA44 */ let expected_sk_bytes: [u8; MLKEM512_SK_LEN] = hex::decode("70554fd436344f2785b1b3b1bac184b6679003336c26f15a7de878c4825c6be03f3c4a480f75b7486aad31d3a00518623fd207ab528dd62721495835ae0062c367b74a71baf10aad0e8a2902076be31348beb15ccc0957cdebb4aff226756bbc601b6568ab784acbaeb34702f0f86a26202118b22b23f83558776c79c14dba983379c803e0dcc3160a11757030e69c6919798d81eb698a9a4483a99e5a5cb2c31c9a661799f3cc89c790706ea041629045d42a83aed88860e394c69187e2105d28cc14ec393592d67dd00aa43fe8b4eae4414002866b5c713c6a8d7d16cf78b819d6f12e9e5a74233908f0b15e3c4ba8329c5cdda55c84928e3aa8063e5aa9676403f91735b11010c7f593091364dc86445bc804840a9a21724212469f8a7b0ce0ac698eb86cad39a7f4824d9a5163aac21ee6808b053c8a3facb0b6744b5262bbcb26a43f664c8732b64cfc7acf099605f41c796060976ac433833fe00343fb1828300a424741116e4b45bb276ea81129a0db4c6e60bce611101e8c625474925e0222679308a3e7708d1972a7b423eb232851c36d2ed53d3ed3bb7500637061a5dc2292fa1c466c07354683328bec2c1ed2cb5c99b78eca0969038cf7c34dd118724e31cae086206b34302b520f5d177aded5b3cce02acce808ea26bcc072625fdb93f17458a5fc1d4da394380a1f57e9cc66109438a075f0d2813fcc4a199cc76db3823f270b0061594192940411a37ffbafae2c150165cec5c6bf73c595fb92cd15312607da070778652bd9944bc48bc7d1a534338bad0bad6656c5d502ce7850ab1587244eeb58f439ab5e08574a718c8aac3d77c798bba1542733be73448f23fb70c0e5353a27c88322c5218493afbb38086434d6d60a56ba887dd498c3ab26a0870993815aa6a40975f218adca1582d64ffc8652fbb3a9a6fbc304f91945fa4aaef2878fd715df70113d2379f44886f812c83ff2b719a69e1ec74ae4b15accd3aed5a53ce76a7b0982471633b973cb40a1a0015d0a424fa11a479c023017436d2a2900e993eb5a0a067400c7f4aadf201fc4fa31264a63bae95cc8d65c3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() .try_into().unwrap(); @@ -35,7 +44,6 @@ mod mlkem_key_tests { assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); - // Decode and re-encode the pk, make sure you get the same thing let decoded_pk = MLKEM512PublicKey::from_bytes(&expected_pk_bytes).unwrap(); let pk_bytes = decoded_pk.encode(); @@ -54,47 +62,69 @@ mod mlkem_key_tests { // 3) does it reject a private key if the H(ek) is wrong? let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // generation of KAT // let h_ek = pk.compute_hash(); // println!("H(ek) for public key: {}", hex::encode(h_ek)); - let expected_h_ek: [u8; 32] = hex::decode("82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea").unwrap().try_into().unwrap(); + let expected_h_ek: [u8; 32] = + hex::decode("82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea") + .unwrap() + .try_into() + .unwrap(); assert_eq!(pk.compute_hash(), expected_h_ek); assert_eq!(sk.pk_hash(), &expected_h_ek); - // 3) does it reject a private key if the H(ek) is wrong? let mut sk_bytes: [u8; MLKEM512_SK_LEN] = sk.encode(); // h is: // dk[768𝑘 + 32 ∶ 768𝑘 + 64] // k for MLKEM512 is 2 - sk_bytes[768 * 2 .. (768 * 2) + 32].fill(1); + sk_bytes[768 * 2..(768 * 2) + 32].fill(1); // now try loading it match MLKEM512PrivateKey::from_bytes(&sk_bytes) { Ok(_) => panic!("Expected error loading private key with invalid H(ek)"), - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Unexpected error loading private key with invalid H(ek)"), } + + // check that pk and sk give the same pk_hash + assert_eq!(pk.compute_hash(), expected_h_ek); + assert_eq!(sk.pk_hash(), &expected_h_ek); + + /* and with Expanded Keys */ + let pk_expanded = MLKEM512PublicKeyExpanded::from(&pk); + assert_eq!(pk_expanded.compute_hash(), expected_h_ek); + + let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk); + assert_eq!(sk_expanded.pk_hash(), &expected_h_ek); } #[test] fn encode_decode() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk1, sk1) = MLKEM512::keygen_from_seed(&seed).unwrap(); let pk1_bytes = pk1.encode(); @@ -108,28 +138,68 @@ mod mlkem_key_tests { let sk2_bytes = sk2.encode(); assert_eq!(sk2_bytes.len(), MLKEM512_SK_LEN); assert_eq!(sk1_bytes, sk2_bytes); + + /* Expanded Keys */ + let pk_expanded = MLKEM512PublicKeyExpanded::from(&pk1); + assert_eq!(pk_expanded.encode(), pk1_bytes); + + let mut pk_expanded_bytes = [0u8; MLKEM512_PK_LEN]; + let bytes_written = pk_expanded.encode_out(&mut pk_expanded_bytes); + assert_eq!(bytes_written, MLKEM512_PK_LEN); + + let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk1); + assert_eq!(sk_expanded.encode(), sk1_bytes); + + let mut sk_expanded_bytes = [0u8; MLKEM512_SK_LEN]; + let bytes_written = sk_expanded.encode_out(&mut sk_expanded_bytes); + assert_eq!(bytes_written, MLKEM512_SK_LEN); } #[test] fn seed() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f - 101112131415161718191a1b1c1d1e1f - 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + &hex::decode( + "000102030405060708090a0b0c0d0e0f + 101112131415161718191a1b1c1d1e1f + 202122232425262728292a2b2c2d2e2f + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); assert!(sk.seed().is_some()); assert_eq!(sk.seed().as_ref().unwrap(), &seed); - - + + // When you pop the seed out, its SecurityStrength will match the ML-DSA algorithm + let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_128bit); + + let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_192bit); + + let (_pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + assert_eq!(sk.seed().unwrap().security_strength(), SecurityStrength::_256bit); // now load a key from bytes so that it doesn't have a seed + let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); let sk_bytes = sk.encode(); - let sk2 = MLKEM512PrivateKey::from_bytes(&sk_bytes).unwrap(); - assert!(sk2.seed().is_none()); + let sk_no_seed = MLKEM512PrivateKey::from_bytes(&sk_bytes).unwrap(); + assert!(sk_no_seed.seed().is_none()); + + /* Expanded key */ + let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk); + match sk_expanded.seed() { + Some(s) => assert_eq!(s, seed), + None => panic!("Expected expanded key to have seed"), + } + + // now try an expanded key that doesn't have a seed + let sk_expanded_no_seed = MLKEM512PrivateKeyExpanded::from(&sk_no_seed); + assert!(sk_expanded_no_seed.seed().is_none()); } #[test] @@ -140,19 +210,18 @@ mod mlkem_key_tests { // so let's test these conditions in both private key s_hat and public key t_hat match MLKEM512PrivateKey::from_bytes(&[255u8; MLKEM512_SK_LEN]) { - Err(KEMError::DecodingError(_)) => { /* good */ }, - _ => panic!("Expected malformed key to be rejected") + Err(KEMError::DecodingError(_)) => { /* good */ } + _ => panic!("Expected malformed key to be rejected"), }; match MLKEM512PublicKey::from_bytes(&[255u8; MLKEM512_PK_LEN]) { - Err(KEMError::DecodingError(_)) => { /* good */ }, - _ => panic!("Expected malformed key to be rejected") + Err(KEMError::DecodingError(_)) => { /* good */ } + _ => panic!("Expected malformed key to be rejected"), }; } #[test] fn test_eq() { - // MLKEM512 let (pk, sk) = MLKEM512::keygen().unwrap(); @@ -175,7 +244,6 @@ mod mlkem_key_tests { bytes[17] ^= 0x01; assert_ne!(sk, MLKEM512PrivateKey::from_bytes(&bytes).unwrap()); - // MLKEM768 let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -198,7 +266,6 @@ mod mlkem_key_tests { bytes[17] ^= 0x01; assert_ne!(sk, MLKEM768PrivateKey::from_bytes(&bytes).unwrap()); - // MLKEM1024 let (pk, sk) = MLKEM1024::keygen().unwrap(); @@ -220,6 +287,32 @@ mod mlkem_key_tests { let mut bytes = sk.encode(); bytes[17] ^= 0x01; assert_ne!(sk, MLKEM1024PrivateKey::from_bytes(&bytes).unwrap()); + + /* Expanded keys */ + + let (pk, sk) = MLKEM512::keygen().unwrap(); + let pk_expanded = MLKEM512PublicKeyExpanded::from_bytes(&pk.encode()).unwrap(); + let sk_expanded = MLKEM512PrivateKeyExpanded::from_bytes(&sk.encode()).unwrap(); + + // basic equality checks + assert_eq!(pk_expanded, pk_expanded); + assert_eq!(pk_expanded, pk_expanded.clone()); + assert_eq!(pk_expanded, MLKEM512PublicKeyExpanded::from_bytes(&pk.encode()).unwrap()); + assert_eq!(pk_expanded.encode(), pk.encode()); + + assert_eq!(sk_expanded, sk_expanded); + assert_eq!(sk_expanded, sk_expanded.clone()); + assert_eq!(sk_expanded, MLKEM512PrivateKeyExpanded::from_bytes(&sk.encode()).unwrap()); + assert_eq!(sk_expanded.encode(), sk.encode()); + + // inequality checks + let mut bytes = pk.encode(); + bytes[17] ^= 0x01; + assert_ne!(pk_expanded, MLKEM512PublicKeyExpanded::from_bytes(&bytes).unwrap()); + + let mut bytes = sk.encode(); + bytes[17] ^= 0x01; + assert_ne!(sk_expanded, MLKEM512PrivateKeyExpanded::from_bytes(&bytes).unwrap()); } /// Tests that no private data is displayed @@ -229,7 +322,6 @@ mod mlkem_key_tests { let (pk768, sk768) = MLKEM768::keygen().unwrap(); let (pk1024, sk1024) = MLKEM1024::keygen().unwrap(); - /*** MLDSAPublicKey ***/ // fmt @@ -252,8 +344,6 @@ mod mlkem_key_tests { let pk_str = format!("{:?}", pk1024); assert!(pk_str.contains("MLKEMPublicKey { alg: ML-KEM-1024, pub_key_hash:")); - - /*** MLDSAPrivateKey ***/ // fmt let sk_str = format!("{}", sk512); @@ -279,9 +369,9 @@ mod mlkem_key_tests { /// Tests that no private data is displayed #[test] fn test_display_expanded_key() { - use bouncycastle_mlkem::{MLKEM512PublicKeyExpanded, MLKEM512PrivateKeyExpanded}; - use bouncycastle_mlkem::{MLKEM768PublicKeyExpanded, MLKEM768PrivateKeyExpanded}; - use bouncycastle_mlkem::{MLKEM1024PublicKeyExpanded, MLKEM1024PrivateKeyExpanded}; + use bouncycastle_mlkem::{MLKEM512PrivateKeyExpanded, MLKEM512PublicKeyExpanded}; + use bouncycastle_mlkem::{MLKEM768PrivateKeyExpanded, MLKEM768PublicKeyExpanded}; + use bouncycastle_mlkem::{MLKEM1024PrivateKeyExpanded, MLKEM1024PublicKeyExpanded}; let (pk512, sk512) = MLKEM512::keygen().unwrap(); let pk512 = MLKEM512PublicKeyExpanded::from(&pk512); @@ -295,7 +385,6 @@ mod mlkem_key_tests { let pk1024 = MLKEM1024PublicKeyExpanded::from(&pk1024); let sk1024 = MLKEM1024PrivateKeyExpanded::from(&sk1024); - /*** MLDSAPublicKey ***/ // fmt @@ -318,8 +407,6 @@ mod mlkem_key_tests { let pk_str = format!("{:?}", pk1024); assert!(pk_str.contains("MLKEMPublicKeyExpanded { alg: ML-KEM-1024, pub_key_hash:")); - - /*** MLDSAPrivateKey ***/ // fmt let sk_str = format!("{}", sk512); diff --git a/crypto/mlkem/tests/mlkem_tests.rs b/crypto/mlkem/tests/mlkem_tests.rs index 71137fe..429b5b5 100644 --- a/crypto/mlkem/tests/mlkem_tests.rs +++ b/crypto/mlkem/tests/mlkem_tests.rs @@ -1,14 +1,24 @@ /// This performs tests using the public interfaces of the crate. #[cfg(test)] mod mlkem_tests { - use bouncycastle_core::errors::KEMError; - use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial512, KeyType}; - use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength, KEM, XOF}; - use bouncycastle_mlkem::{MLKEM512, MLKEM768, MLKEM1024, MLKEM_RND_LEN, Polynomial}; - use bouncycastle_mlkem::{MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEM1024PrivateKey, MLKEM1024PublicKey}; - use bouncycastle_mlkem::{MLKEMPrivateKeyTrait, MLKEMTrait}; - use bouncycastle_mlkem::{MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM768_CT_LEN, MLKEM512_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM1024_CT_LEN, MLKEM_SS_LEN}; + use bouncycastle_core::errors::{KEMError, RNGError}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; + use bouncycastle_core::traits::{ + KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, KEMPublicKey, SecurityStrength, XOF, + }; + use bouncycastle_core_test_framework::FixedSeedRNG; use bouncycastle_hex as hex; + use bouncycastle_mlkem::{MLKEM_RND_LEN, MLKEM512, MLKEM768, MLKEM1024, Polynomial}; + use bouncycastle_mlkem::{ + MLKEM_SS_LEN, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768_CT_LEN, + MLKEM768_PK_LEN, MLKEM768_SK_LEN, MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, + }; + use bouncycastle_mlkem::{ + MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, + MLKEM1024PrivateKey, MLKEM1024PublicKey, + }; + use bouncycastle_mlkem::{MLKEMPrivateKeyTrait, MLKEMTrait}; use bouncycastle_sha3::SHAKE256; // #[test] @@ -31,13 +41,13 @@ mod mlkem_tests { #[test] fn core_framework_tests() { - use bouncycastle_core_test_framework::kem::{TestFrameworkKEM}; + use bouncycastle_core_test_framework::kem::TestFrameworkKEM; let tf = TestFrameworkKEM::new(false, true); - tf.test_kem::(false); - tf.test_kem::(false); - tf.test_kem::(false); + tf.test_kem::(MLKEM512::keygen, false); + tf.test_kem::(MLKEM768::keygen, false); + tf.test_kem::(MLKEM1024::keygen, false); } /// This runs the full bitflipping tests and takes about 30s.. @@ -57,13 +67,16 @@ mod mlkem_tests { fn rfc9935_keygen() { // note: same seed for MLKEM512, MLKEM768, MLKEM1024 let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); - + ) + .unwrap(); /* MLKEM512 */ let expected_sk_bytes: [u8; MLKEM512_SK_LEN] = hex::decode("70554fd436344f2785b1b3b1bac184b6679003336c26f15a7de878c4825c6be03f3c4a480f75b7486aad31d3a00518623fd207ab528dd62721495835ae0062c367b74a71baf10aad0e8a2902076be31348beb15ccc0957cdebb4aff226756bbc601b6568ab784acbaeb34702f0f86a26202118b22b23f83558776c79c14dba983379c803e0dcc3160a11757030e69c6919798d81eb698a9a4483a99e5a5cb2c31c9a661799f3cc89c790706ea041629045d42a83aed88860e394c69187e2105d28cc14ec393592d67dd00aa43fe8b4eae4414002866b5c713c6a8d7d16cf78b819d6f12e9e5a74233908f0b15e3c4ba8329c5cdda55c84928e3aa8063e5aa9676403f91735b11010c7f593091364dc86445bc804840a9a21724212469f8a7b0ce0ac698eb86cad39a7f4824d9a5163aac21ee6808b053c8a3facb0b6744b5262bbcb26a43f664c8732b64cfc7acf099605f41c796060976ac433833fe00343fb1828300a424741116e4b45bb276ea81129a0db4c6e60bce611101e8c625474925e0222679308a3e7708d1972a7b423eb232851c36d2ed53d3ed3bb7500637061a5dc2292fa1c466c07354683328bec2c1ed2cb5c99b78eca0969038cf7c34dd118724e31cae086206b34302b520f5d177aded5b3cce02acce808ea26bcc072625fdb93f17458a5fc1d4da394380a1f57e9cc66109438a075f0d2813fcc4a199cc76db3823f270b0061594192940411a37ffbafae2c150165cec5c6bf73c595fb92cd15312607da070778652bd9944bc48bc7d1a534338bad0bad6656c5d502ce7850ab1587244eeb58f439ab5e08574a718c8aac3d77c798bba1542733be73448f23fb70c0e5353a27c88322c5218493afbb38086434d6d60a56ba887dd498c3ab26a0870993815aa6a40975f218adca1582d64ffc8652fbb3a9a6fbc304f91945fa4aaef2878fd715df70113d2379f44886f812c83ff2b719a69e1ec74ae4b15accd3aed5a53ce76a7b0982471633b973cb40a1a0015d0a424fa11a479c023017436d2a2900e993eb5a0a067400c7f4aadf201fc4fa31264a63bae95cc8d65c3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() @@ -103,11 +116,10 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM512::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => {/* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } - /* MLKEM768 */ let expected_sk_bytes: [u8; MLKEM768_SK_LEN] = hex::decode("27d2a77f33756f61208ef113abe82595873d4abc730e5b5d679529bf6a4ceb6383427231a8612f41550515acba52e48ead8b942833bbe6865d13d14a79d2c5c3e07f0a056d8de7aadfcaba058c493c80b37cab8c562753bb3ba6b6ec8297f885eaa7540d530015a84406e55b1366b577e236ce58a26d8a1eb5a44d542323c2167d9bf4a47f985699ca05bae43b8dec617f02380a3890afd4b8c7ec7ede26553a025f3ce5bc5d7a62130304235cb1ad4836b566b5b863bd9bdb45a2844a7047b6c8d383e448525e040b4dc8a2b48c6c37c96d62d43f3fd88e2881c40a205c9e248f652b592781a779f86880f2a147b67863f391cc1a5a908c0095e07212291e2ef8a36eb9a9c0c6073225b34703a4af049382c47573da68fde9245ad444e31b1fbdb521f1f61f37bc0cef292067e670d28a1ffd904f6f1190a996918a13037a6cabf3c373bf8296cd37ab33ba7746809cc3f8ade1b3639bd57bfcc69650aaaf1de198fc4c0463299e52c461780cc428fc5d04a5c51850cba6c2a5274340675793dda09be44c29e6395c65f85d2a0a7c6df411e6911b1f2cb6c351cd2e875f51b638be776097e93e2f2b2f83da0beef4aa85ba9e763ab64502a0ca5222e9eab5b3b7088ed52060e8c8269b943a71ab0ae1c5b1b687d2e019cf8036bcf9bf6e7bac3aaa36e41660faa4540f2648cd93a189ec5c2dea70bacaaa4ffc906f90810ea1b67bf24f2c78cf6ba881aaea61c0652bff95b1bae4426d1773b9cc2ca82c21e38c636e3b1c523244986b0be8a83f5dd5cf2d54762fb3c5ebf59b8e885302b1ce47033edf760f4e029be40b6d566b19dd758acd5c7412878131244f90172c53f26663c21d905301d48baf91c917cc7779e9d8802cc10d89a3705099a2ad3a3a8896743c1144698093be257dacb66dc785228b912c8d965d14aa28342c3ac4a93fefa532b20945ddc1020139c14d638b908c4ddde9a0645b95b2e4414d40bb79f04413830f15a873c28bb7059c2741002015f20408f058e715b0bf995b5380b7dd325a056ab97e659a2be0cdf6c33731c683a634b771e8c92a139aee4bb0e49c7077321d42fc199f7c1f298ca625d223a5c263a03cc48159b7812665b78637e4e18720b2c29a6b99f42766a4cbc4dc508ba94ba83b89c3a5c78f8bb26bbd9b79beb8c8182490f5793ee5b96013b74b7e169e29d162f1315464ea7d72436d89b755161192c81cc2dd1c8b8bba795ef426ee1cc01c37aaa37b2cff8b0a378b47cbd0b4d49398cfc2712959699fa0bd8cd84666acc61f541b84fa96b9c854e4e75e9144addb44b8566a57dfbb545ce423c03346f2b2c1a91780d152a8de1a4d4c9cacde7392c996888cc2399c02c38b3353adf8acab283924da00a05b76e738c72c930d6cba09ae168990faa1fef2226e780861d416eff402f4f759fc648ab1f97100109087f96e4b148d2cb31e4805314ea0cd95fb023eac0d989474ba4201d7b41d26f5394b217eea5b34b71a8b37931c0e594271e0b7c733257240233e7ba735603e425a87dee77079e37cb28a21764594ce5350d8da2b62a07174943032ec89c98809c73b6423d30c1d283a766a64d89703c3d629b497828d48320c346210797a298aa10d423c8dda069d02bc59e6cdf03a096b8b3da4cab9b80ca4a14907672ccef1ec4faf234a0bc5b7e9d473f2b3133b3b26a1d175cb67a7805919699c02f76531b99c5f89180704bb4ca4535c5b8972679c660a07c5e514b87009c862eb8f5157695efb3fc40a9def6b81c1cc02a249ae4f094ad0d9bd3485c1c1c68080520a7c8c632032cee738154e5c5176c07da56024776a430fe76eacf665a3f7b832102215bc82f10939c8355704336a8fac1d81e4bb0485aa5d7c74d6b59bbe5c5e972a0d8bac411b55b5d5557cd680a1a8f71b4eb86bc48c9a0509731a54bd9d7290b27963e4372dc9b199cfdcac0b01acd28a62395112e4c43648d622c48c8234d01440e8cc376c927f23a5afc9ac0474c662274e424525c8552ece3b3fe26516de901bc7d515bde89558e626c95c80b93342f8010004f39e6c6c94871c5e344cab3966c835f9a96a59afd31c40286b38b1c1a78470bab947518934453ce86736a919f1f5a6d510a86f5454fc3980cb5c765bd2bd5f7b36b1410d6635c8ceb47c4dda0d76a28eac939c71c3024804866c71626658442163c2c22117e50acefce6378a985652302a4ef0c2ce0cc716b7796e2b6b2e3777dfa1ac3da259a31b5a9b530f8cb638a81a62ac301849abaf95a7301bda30068909bfdb7e67dbccbb38a5551a25b1a3a0f685748ad5753d8880f0016c627486166384c5571fe2365900364d038311e2d875db366686932b5ec602430a369e87a6ef5c338786657825bd4c057aceb923eb0935e6905e63b4ced7f80857a773dd64b150d26612ea9ac12052db2017bf1843ccb4b3281b690dc728adfa85c00281b8e3c09287335f856b4fc2892f69a2f57921ada01914c40988662d57769662a786351b9b66493dab79594d986de2100d65ba0ff4ea58b81538d24a4435a258fac25404aa7f41f658b1385065e158dcb60115732720f40459aaac15e406953a90ac52997d1ccd070060efc65db9e653354467fad56ec713c86e7540c423acf2669f52fa6f4ac6888d871ef3e847c029a8aafbb92e17b24aa079b1f419ba6175b442afb11909d4a56b70a0335b28739218aa7c9348e2c3c2f3eb3d15a41e6417c0dd94bfeb21419b311a7bb13a180bbe833218a9a6b17447cc85f225859587a73077049acbcfd44d0f025438e15d1538270d586e1bf83192a9459cf63c0e972f85297679831ecf121509851cb8340f6f107b0fa1a0efd1b36a8189bc085c4f5cb784e553f41b918f80397ce1956f785bee377ca9aa8be6998ada30c26b7c3d8c6b55254cc96203b20c42aee0ac4e1ebb408e49a9e3f879d0ab0785eb7025425d1305a2299c015e120d163b0e19494ce57253d0246d182745cb8197ab7438b3c1bb7972bec5a306eba3567855c014699fef65ae54c770a0d85c18400cf642aedc660777ba4b138502bd5a7812f621f84a48296b98dd4322b6f15828b8a8f0e00a8ba44a53c3a8b143571b0740abd567daf1cde9c79c204b6d5e259d1766a31bbbcb4e6a05cf4502176b301c1c2f41247750157bcec85e809b30a4d60d7747cdd0f5b99aa8c826987517793aaa8080a0b124a8558df72bbe37b75f4edbb6be8216d6c633fb2b2280e25113d8695e43481c3eeb397eb192505229b67a201ea893c3e2cb32da8bc342fa4dea0578a24e16d8f8f9383a95b77050f4d9fd2f5733eec1d63ef3c23ebf9918173669a7202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() .try_into().unwrap(); @@ -120,14 +132,12 @@ mod mlkem_tests { assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); - // Decode and re-encode the pk, make sure you get the same thing let expected_pk = MLKEM768PublicKey::from_bytes(&expected_pk_bytes).unwrap(); let pk_bytes = expected_pk.encode(); assert_eq!(pk_bytes.len(), expected_pk_bytes.len()); assert_eq!(pk_bytes, expected_pk_bytes.as_slice()); - // run keygen from seed let (derived_pk, derived_sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); let sk_bytes = derived_sk.encode(); @@ -148,11 +158,10 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM768::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => {/* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } - /* MLKEM1024 */ let expected_sk_bytes: [u8; MLKEM1024_SK_LEN] = hex::decode("f77b7f6b15c73fe2cc546b67fb774ca19b42cd463ea9fbb984ca477a77b6c71087cbf051abe4736a9072c6e870c8311c55963f500a3c7b1b8f2a58558f49c62527b6c594b5e7acb3bcf597273a5743517d151208bd4aa61e75ba67b0bd594a994919627ac0a804d489e171336bc339f4666706e5134412b366823d50318c8bf261ab120a28a04fec01cc15f2b71912cee54aa8eed854694b6ba886b5eb7661e6d56aac213cc1d814d592b395554fae74476d34371163129bf864527250606cc21a53746b20997077bba155733b28a4e7fa0776399524763eb481ceaa11366c3474a04685f40c3f08b0424f40bff949a0ac92704c3ba0c6eb36f1f5b621d8bf2b6327beb57cd3facb94186fe3fc9ab0a1434bb291d2c9bb70723057e2254059656f565919a32cf74579de89681cd2c5a935a52b4aaa2d24cb5d5c9e20729ec5492ec36961efb8a28cbc00ac303523295f3d8036abc1603307ce70d7848a35657a5687dd589927ea63731626abb26ec4e431b8eb6b3b0bc1e82573ee73b1a021183183528108ae2eacaddb95b464a0b98469c319cc27bfa01bc31054a68c05502b1662b879fe98a1711c3426f6436cb0214cea379ac3a7e5fb60184a37c1da1eda61c6c39c1dd4e847845811f2a358a43731528536d4a3291b04158c2c3dc641624882678bc7805f58a9d94c7104567846a2044e65aece2a225372b6024799a5477d60237504aa5c0ac57bc70a3558c08c4de687ef1302b4fcb5594413d22cb959bc31be423450403c6bc57dc411b3fefac1052ac4bb162c44545a4ca80892657fa13a0b2c482ced629cc4999d969c593d4aadf073cc3e3a458e78a8aa039408e652be93b20c8b42ec5b0e50239dac726052851a6d15312ec39ed208b72209a577c6b2770112895749d5260e7dd446c0b0118c1000be6801d2611fcf00792a9cc4f4b49922f9a2d4b9c8fa5a5d0d60506631a7e971cee840b08fa63c13729d7ea5aac70352a984cdb669331cba758fe87ec3931b3e3161fcc747aa749424689feae14bf7c9a2ffba1302b212b80372d8e9049db69a3a1261d0a2859a9b4d57899e0ba41607a1b67a7c0e12923689f8c6395377d970c7490a4129611a1d05c3b7813bed945420723f7f9525a87793fafbbfca982e66bb80681c83248a89da084c19882f48f31e7fc09093a49e9fd09691b021edf463afc519b62853816118346115fb0b882cc6482f3c5cbcc1c1894697e1239598b34b2a9a7acd15244d0690c88194097a9beda585e87c437124624c210768e6215d376482653eb89947877c118d370c696a6ffcc1018ae413a08a8d0ffaa819945da7a167c229913290cad1c80a369258762610ea253e62dc24226a30c892c12136c326f13f4446664712b0b90bc063b4028593cbde06cdc22289e240c7e296b59172c1aeda8c99e0512d1a0163a942ea33148e6937c026029424b81b996b1df22ea0623ec65c6bf093500cf3bf35374adc392035ca7c583b99685bca541a0807b163acd0888be0385dea820da46e4dbb44d2e462c734b83a473fed1364273159257cc259a8c5676c1c76d41d56b9907ec1c3599c9e8907403a27a705e3619b04b0ad046e8ec8169c17b460d44c0c0c4464d044c946186bc725965083a892bcc495c0540311ff9b3e5192c303d88f8ba46a901c782ef02388f1b2addab6a5350fc3639700e3154337337e4a178d351cd2b56ee1f0bfea34aacfa33d2ec791e50752d4d034cb1c951572caaa5c4d90947b6b175a6dd3c62a77bb8f7ac9ae24719b53c2b120a2876986e217b72bd7cee44a7265b11cee1ab2261762b31a3738386969c0825fb79452e652e1142fc73c9df6fba411795b4717922b29ba2d53abe5a8c0dcc1601b096c96d7938fd5a68a8797c7b9477a86a472eb5da250cb2fec318d83c8f43bbe8e11c35e377d349366c85c4382597f6fc27a0051c0fb00b02c01ca20f9a427f172599477ca690cc1327e0f025f80ec338a80a159e308c12a27db1a7e1b960a99d37dfc22872e51930f28c651ab221f53abaee20bad9a3eabcbab913251bf135beb29617b5754333c4daadb2238341c2ad9378186280f6449440b784ba78f5dac44d8f65b3b7421950397c3913a2dd23ec6d1cb717b36a5fc95af191e278296948c1254ea86b4ec004b94c29450111191823b3514c9ac1ea3d9825ccb86393a2dfb04654fa2192d37bfad1c497c6502eee5ca80a73bfce0baf5a54a88585a401397a3d232f426a7afb082bc21a44317090eaac7592c2ea88a653c4491ea193931335f52e989a3c4cc56d9c553732d57c470fb41ab759b65d2d04445382fcd9c4e344a1128fa9e11e04358e192ed014b23232a7ee2b22e23717f44111ee33575399c37646da9813ec9b212afe94e5dc5c2330a7294cc1f4234a6d3fbb4f1685ab8892c04acb17cd1c170d7b0611b6a7176c794cc8c67f55fc923c2ad203100f365991882c30243d77813843b5ec7c964032263706092ecf00c7516be64e4598ca4226c069bb5e67e4175cf2286c8dd5c488a6c5861f31baa0bd0269470e8b551dd3bcd38c86c12f9cdb176c77dc8b6c02a701f478902c8553f694c0d82727b4c4a5c2c1041212aa1274808b82111b377ec75214e9b1978f76004d4139d98613f4b8e98d20af7b534073a509a959b7a7564f9b40ca218bf61829320a8502017954d328d7ac6c769ec29700756e7b0685b340d5e118059504a49a9a50a10198eb10a5784678eb427d7b4babb9552933b062897973e1318eaf0a0eac37584a65401b1703e042accd837531483f241cadcd1c1d378119e694429db199ac891e4c5343757085bb3ae783667350c4458d97672e861e80b1d2679510ea3a6f2360c77a46942c7a06a554d228080c84b47aef14db17620cb16c06ab30a1be4cda7082be9f87e9c211c46916349a5ba8eaa5201c7294a3c0885b53b657452108825ec646c90a04612324ee7d031afe5343132cbef67b6efb1a5ec2809b773538ce77b3d8b04eb0b3c2256011e4c716c19a8ba0752bf71492117649f0615c3290fc29a46fde4bd52db9286d603388244259c15a7ac2b640a60cc03376a5841a3fb8a473568fa9b1a267215f34c01697b0f0e627175d72105b7707c29b9e614bdc33a6f6c818a95370b427882d7b476796a9ec6eb993274cd9b2391a82ba45e3393d2e9ae9721ca9d6c1b988b5827713f90a6585de9433528c02b03ce10bb5f720138d0fbb4c30c1266b918e52925dfe17b37f95d22bca54f475919ac859098c0f0d08ac5875ef29b56fd141e6ef15f700a0b66f39595c588177373c4669b21bc071e4c3aa5f0b4a31b6258f35da24ac3cd29c7f2092410c5078355b138fb53a6b9ae6e0b9c08243e7baa45c47376eb8c7f13d4cf51aa736fa31540c9241f370da544bf9f9c28d9a57e2f2a7ca95a4e4b466e641ab3bcc76adf1139d567a6f12b52f3a65e7ec0aae26bcaa8c55833b04e59998ebc9a1930fbb6d2233c53d2c1f8b9518e3c2de73a19dee6b380a5b32971cf64e129fd6c1fa6e75d4a234501e966dd3a540af5c8f4f34a6b4a253ee28492566d5e67c6f55855fcb0506fb06c156744d9a03a31a26fa94cad14f157b7f303d07a69c773768fcb4d079c09059703a0c3a94de4b99ea3a2f16583d0f9170a3950db07b4f0bc30802927f9f7961b6259892636a9502a2705303637799dd344da451c1cf7bf67840ceb3079ab8c6b8c1927f64053c612450c45c9e603bc16666e596b3471e103b6f15447424d17022048111ffbd37e1c670f64f14b8a7b32b94c1a49b45dd2fc38cd5289d910ad63602cf5e13042c64ac6797b89fb551ad08e05a92d200cccb7e712ef23c9312cb350f029ab537e287347fd3075ac10906a783f1c6c07ccb88f41228c4be1c640f790b5c3a5d5d3ca792495d74bc461562658c07ac600276b924ab5bc9be1f0494cb76f82f460a7480972663381e169996061d799859ec54d4f5ca5c411c01db1597b165977669de13a928a34afbac258fea8c4764239c9421dc3119bf5b47699206978327b1c5345ef746a7983841f056e2534100ab24d4e9abbd0b17c6a95bd4c3c0e40f69e1612aceeb28b99086c95116e7204273893390bf46b899b36286b0ebf1947bb9884f732ca27da82b19b5dc0cc7f8885714910888b2310c4f9319d410b34e6433b9003e2176bb995257456106e8952163b8ba592530cc5aa0aeb43ad398fe9e97baa523d7a4431677c3d3af0719e475db85ca95af5089beabeb05b2faab4896ba60f81c88472a57b46a828826a0cdfb446f8189182d2bf5eac4ec1cc5deaf599c8a13e48235406d17ffddc8344b6c66984a868aa92fa02227a086950eb0c8701ed58dc628776b983882e117561349e5c131a7e116a0463861d7d18663c5627c38c7147ddaadfd48acd7a4535202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() .try_into().unwrap(); @@ -165,14 +174,12 @@ mod mlkem_tests { assert_eq!(sk_bytes.len(), expected_sk_bytes.len()); assert_eq!(sk_bytes, expected_sk_bytes.as_slice()); - // Decode and re-encode the pk, make sure you get the same thing let expected_pk = MLKEM1024PublicKey::from_bytes(&expected_pk_bytes).unwrap(); let pk_bytes = expected_pk.encode(); assert_eq!(pk_bytes.len(), expected_pk_bytes.len()); assert_eq!(pk_bytes, expected_pk_bytes.as_slice()); - // run keygen from seed let (derived_pk, derived_sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); let sk_bytes = derived_sk.encode(); @@ -192,21 +199,23 @@ mod mlkem_tests { let mut wrong_sk_bytes = sk_bytes.clone(); wrong_sk_bytes[4..8].copy_from_slice(&[0u8, 0u8, 0u8, 0u8]); match MLKEM1024::keygen_from_seed_and_encoded(&seed, &wrong_sk_bytes) { - Err(KEMError::KeyGenError(_)) => {/* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("sk_from_seed_and_encoded should fail with InvalidSignature"), } } - #[test] /// Mirror of bc-java MLKEMTest.java : testMLKEM fn test_mlkem() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 - 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7" - ).unwrap(), + &hex::decode( + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let expected_sk_bytes: [u8; MLKEM1024_SK_LEN] = hex::decode("8C8B3722A82E550565521611EBBC63079944C9B1ABB3B0020FF12F631891A9C468D3A67BF6271280DA58D03CB042B3A461441637F929C273469AD15311E910DE18CB9537BA1BE42E98BB59E498A13FD440D0E69EE832B45CD95C382177D67096A18C07F1781663651BDCAC90DEDA3DDD143485864181C91FA2080F6DAB3F86204CEB64A7B4446895C03987A031CB4B6D9E0462FDA829172B6C012C638B29B5CD75A2C930A5596A3181C33A22D574D30261196BC350738D4FD9183A763336243ACED99B3221C71D8866895C4E52C119BF3280DAF80A95E15209A795C4435FBB3570FDB8AA9BF9AEFD43B094B781D5A81136DAB88B8799696556FEC6AE14B0BB8BE4695E9A124C2AB8FF4AB1229B8AAA8C6F41A60C34C7B56182C55C2C685E737C6CA00A23FB8A68C1CD61F30D3993A1653C1675AC5F0901A7160A73966408B8876B715396CFA4903FC69D60491F8146808C97CD5C533E71017909E97B835B86FF847B42A696375435E006061CF7A479463272114A89EB3EAF2246F0F8C104A14986828E0AD20420C9B37EA23F5C514949E77AD9E9AD12290DD1215E11DA274457AC86B1CE6864B122677F3718AA31B02580E64317178D38F25F609BC6C55BC374A1BF78EA8ECC219B30B74CBB3272A599238C93985170048F176775FB19962AC3B135AA59DB104F7114DBC2C2D42949ADECA6A85B323EE2B2B23A77D9DB235979A8E2D67CF7D2136BBBA71F269574B38888E1541340C19284074F9B7C8CF37EB01384E6E3822EC4882DFBBEC4E6098EF2B2FC177A1F0BCB65A57FDAA89315461BEB7885FB68B3CD096EDA596AC0E61DD7A9C507BC6345E0827DFCC8A3AC2DCE51AD731AA0EB932A6D0983992347CBEB3CD0D9C9719797CC21CF0062B0AD94CAD734C63E6B5D859CBE19F0368245351BF464D7505569790D2BB724D8659A9FEB1C7C473DC4D061E29863A2714BAC42ADCD1A8372776556F7928A7A44E94B6A25322D03C0A1622A7FD261522B7358F085BDFB60758762CB901031901B5EECF4920C81020A9B1781BCB9DD19A9DFB66458E7757C52CEC75B4BA740A24099CB56BB60A76B6901AA3E0169C9E83496D73C4C99435A28D613E97A1177F58B6CC595D3B2331E9CA7B57B74DC2C5277D26F2FE19240A55C35D6CFCA26C73E9A2D7C980D97960AE1A04698C16B398A5F20C35A0914145CE1674B71ABC6066A909A3E4B911E69D5A849430361F731B07246A6329B52361904225082D0AAC5B21D6B34862481A890C3C360766F04263603A6B73E802B1F70B2EB00046836B8F493BF10B90B8737C6C548449B294C47253BE26CA72336A632063AD3D0B48C8B0F4A34447EF13B764020DE739EB79ABA20E2BE1951825F293BEDD1089FCB0A91F560C8E17CDF52541DC2B81F972A7375B201F10C08D9B5BC8B95100054A3D0AAFF89BD08D6A0E7F2115A435231290460C9AD435A3B3CF35E52091EDD1890047BCC0AABB1ACEBC75F4A32BC1451ACC4969940788E89412188946C9143C5046BD1B458DF617C5DF533B052CD6038B7754034A23C2F7720134C7B4EACE01FAC0A2853A9285847ABBD06A3343A778AC6062E458BC5E61ECE1C0DE0206E6FE8A84034A7C5F1B005FB0A584051D3229B86C909AC5647B3D75569E05A88279D80E5C30F574DC327512C6BBE8101239EC62861F4BE67B05B9CDA9C545C13E7EB53CFF260AD9870199C21F8C63D64F0458A7141285023FEB829290872389644B0C3B73AC2C8E121A29BB1C43C19A233D56BED82740EB021C97B8EBBA40FF328B541760FCC372B52D3BC4FCBC06F424EAF253804D4CB46F41FF254C0C5BA483B44A87C219654555EC7C163C79B9CB760A2AD9BB722B93E0C28BD4B1685949C496EAB1AFF90919E3761B346838ABB2F01A91E554375AFDAAAF3826E6DB79FE7353A7A578A7C0598CE28B6D9915214236BBFFA6D45B6376A07924A39A7BE818286715C8A3C110CD76C02E0417AF138BDB95C3CCA798AC809ED69CFB672B6FDDC24D89C06A6558814AB0C21C62B2F84C0E3E0803DB337A4E0C7127A6B4C8C08B1D1A76BF07EB6E5B5BB47A16C74BC548375FB29CD789A5CFF91BDBD071859F4846E355BB0D29484E264DFF36C9177A7ACA78908879695CA87F25436BC12630724BB22F0CB64897FE5C41195280DA04184D4BC7B532A0F70A54D7757CDE6175A6843B861CB2BC4830C0012554CFC5D2C8A2027AA3CD967130E9B96241B11C4320C7649CC23A71BAFE691AFC08E680BCEF42907000718E4EACE8DA28214197BE1C269DA9CB541E1A3CE97CFADF9C6058780FE6793DBFA8218A2760B802B8DA2AA271A38772523A76736A7A31B9D3037AD21CEBB11A472B8792EB17558B940E70883F264592C689B240BB43D5408BF446432F412F4B9A5F6865CC252A43CF40A320391555591D67561FDD05353AB6B019B3A08A73353D51B6113AB2FA51D975648EE254AF89A230504A236A4658257740BDCBBE1708AB022C3C588A410DB3B9C308A06275BDF5B4859D3A2617A295E1A22F90198BAD0166F4A943417C5B831736CB2C8580ABFDE5714B586ABEEC0A175A08BC710C7A2895DE93AC438061BF7765D0D21CD418167CAF89D1EFC3448BCBB96D69B3E010C82D15CAB6CACC6799D3639669A5B21A633C865F8593B5B7BC800262BB837A924A6C5440E4FC73B41B23092C3912F4C6BEBB4C7B4C62908B03775666C22220DF9C88823E344C7308332345C8B795D34E8C051F21F5A21C214B69841358709B1C305B32CC2C3806AE9CCD3819FFF4507FE520FBFC27199BC23BE6B9B2D2AC1717579AC769279E2A7AAC68A371A47BA3A7DBE016F14E1A727333663C4A5CD1A0F8836CF7B5C49AC51485CA60345C990E06888720003731322C5B8CD5E6907FDA1157F468FD3FC20FA8175EEC95C291A262BA8C5BE990872418930852339D88A19B37FEFA3CFE82175C224407CA414BAEB37923B4D2D83134AE154E490A9B45A0563B06C953C3301450A2176A07C614A74E3478E48509F9A60AE945A8EBC7815121D90A3B0E07091A096CF02C57B25BCA58126AD0C629CE166A7EDB4B33221A0D3F72B85D562EC698B7D0A913D73806F1C5C87B38EC003CB303A3DC51B4B35356A67826D6EDAA8FEB93B98493B2D1C11B676A6AD9506A1AAAE13A824C7C08D1C6C2C4DBA9642C76EA7F6C8264B64A23CCCA9A74635FCBF03E00F1B5722B214376790793B2C4F0A13B5C40760B4218E1D2594DCB30A70D9C1782A5DD30576FA4144BFC8416EDA8118FC6472F56A979586F33BB070FB0F1B0B10BC4897EBE01BCA3893D4E16ADB25093A7417D0708C83A26322E22E6330091E30152BF823597C04CCF4CFC7331578F43A2726CCB428289A90C863259DD180C5FF142BEF41C7717094BE07856DA2B140FA67710967356AA47DFBC8D255B4722AB86D439B7E0A6090251D2D4C1ED5F20BBE6807BF65A90B7CB2EC0102AF02809DC9AC7D0A3ABC69C18365BCFF59185F33996887746185906C0191AED4407E139446459BE29C6822717644353D24AB6339156A9C424909F0A9025BB74720779BE43F16D81C8CC666E99710D8C68BB5CC4E12F314E925A551F09CC59003A1F88103C254BB978D75F394D3540E31E771CDA36E39EC54A62B5832664D821A72F1E6AFBBA27F84295B2694C498498E812BC8E9378FE541CEC5891B25062901CB7212E3CDC46179EC5BCEC10BC0B9311DE05074290687FD6A5392671654284CD9C8CC3EBA80EB3B662EB53EB75116704A1FEB5C2D056338532868DDF24EB8992AB8565D9E490CADF14804360DAA90718EAB616BAB0765D33987B47EFB6599C5563235E61E4BE670E97955AB292D9732CB8930948AC82DF230AC72297A23679D6B94C17F1359483254FEDC2F05819F0D069A443B78E3FC6C3EF4714B05A3FCA81CBBA60242A7060CD885D8F39981BB18092B23DAA59FD9578388688A09BBA079BC809A54843A60385E2310BBCBCC0213CE3DFAAB33B47F9D6305BC95C6107813C585C4B657BF30542833B14949F573C0612AD524BAAE69590C1277B86C286571BF66B3CFF46A3858C09906A794DF4A06E9D4B0A2E43F10F72A6C6C47E5646E2C799B71C33ED2F01EEB45938EB7A4E2E2908C53558A540D350369FA189C616943F7981D7618CF02A5B0A2BCC422E857D1A47871253D08293C1C179BCDC0437069107418205FDB9856623B8CA6B694C96C084B17F13BB6DF12B2CFBBC2B0E0C34B00D0FCD0AECFB27924F6984E747BE2A09D83A8664590A8077331491A4F7D720843F23E652C6FA840308DB4020337AAD37967034A9FB523B67CA70330F02D9EA20C1E84CB8E5757C9E1896B60581441ED618AA5B26DA56C0A5A73C4DCFD755E610B4FC81FF84E21D2E574DFD8CD0AE893AA7E125B44B924F45223EC09F2AD1141EA93A68050DBF699E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7").unwrap() .try_into().unwrap(); @@ -218,8 +227,16 @@ mod mlkem_tests { assert_eq!(sk.encode(), expected_sk_bytes.as_slice()); assert_eq!(pk.encode(), expected_pk_bytes.as_slice()); - let message: [u8; MLKEM_RND_LEN] = hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72").unwrap().try_into().unwrap(); - let expected_shared_secret: [u8; MLKEM_SS_LEN] = hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058").unwrap().try_into().unwrap(); + let message: [u8; MLKEM_RND_LEN] = + hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72") + .unwrap() + .try_into() + .unwrap(); + let expected_shared_secret: [u8; MLKEM_SS_LEN] = + hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058") + .unwrap() + .try_into() + .unwrap(); let expected_ciphertext: [u8; MLKEM1024_CT_LEN] = hex::decode("8B9FE419250C5FB0463C8181FCF7CEC777136B738E015EBA31067AA4A8C378BBAC0121B88214F1AEB866E4F33C277099E09B4BF7E21CDDA30B5B32C18B0E9660C30601D85DAEC07AAF4B343EC5516FA501DD63088B999FB9A414C6CA593806C08CD4C775139BF0F0BF3676D773EDD56E616A13830D5F5FE35E515DBC84E43AAD0167D57E60A9DE30886ACD3F7F2006CAC26A7A07B4DADBEDFBED7F305764386AAD726D5B2BF14A376BAD8B4896688491733FB34E6EDEA10BFD5E448541CB6E69E3D87DF190AFA7FF62577775BAACEA444A6128A20200251D8FA759DC60FDA6A9730CFFE4997FE7EBCDD1644AE2D55290A4074CDD2CE53C18D22BC33671E68727A9B5A2FEAFB114A8045D96A56981E200A09661375987625ACC233EDE817AF1DEEAA21C7C4377423E73C5AF9BFF58A49DE6DAFD07A3E3BABD891F62BBA41D1856B8BC502CC86EE115A3598431E2B54AB0C5EACC3CE6A03090925C1FD5A251B00576763A963994A7A23EE12EBFC1B994F93C6144178F0BEF88245CE77CD32EF651826A6090AF561A5864DEC2A51D846F1F48F88B4B55F58C2373E0F67BDC95DC23A43E8546232A7B234E49F5226A3A63BDBCED7240FC81C2DB68AAEB2671A2FD231997BF8839C63A7F41F15E7242821D42E80BBC0F43FA9E353DE8B25ED8FFC242EB512C6A5260919AAE89A11176532BCCC762A520A37AEC4E7209AA81CEE0DD4ADD932C47EB8100BE98AA1DEEA9EA698115ADCED950A6C536D19AEB325CEA8C5245C0A2281533FB90809DC2BE90567EBE6AE229FE09B44DA2182585EA694D8A9AB33EBC24B44E09BD510F34B4140E1FB41162F9415F2D9106A0CEA00A26ED0920021F4E5BCFB3DABF5850DAB22B2E889D9611FBE06D0C899708EB5E5FAD2FBBE0D5C0BDE080F8E760EDFA037D55DA77F0F39591BF5B050C905FA538B7228E238A290DF340778DCBD6BE40A3B1DD455FB27ADBE176AEF6CC295BEA570BDC221BA14002E3B113B0EF237452FBC9F1AEC42E0D2B33F19832DB0A6171CAEB0B30EEAD3A54B704B761C7D4AFEA8F6AFC15156666A081C43AEB2E04FEECEF8AABA4049BD78B120B9ABA86A60342A0CF806411C473C26C4BE1540E3312388BCBC8523BA73F40EA28D5564274F3661D7ACAA0F1E8D0F28DCF6B501329963E6857FDB2AAE873A7D9D6C14821F6C0B6AA50AC449075CD6F2A256C5A05959DAB5A5912CC8E8F8B9F59941BFCCE6A28CBA74A20382B1FD3382D056547D5BC5EF4AAE62F96F038C595A4F901D6AE790F8978292AD1CC3A1E800B71A5BBE84533646655E3752FBD6B02B97B204E75D28A34C2F990FB8E8CD31CE6E683FA7E67DA03367E8D47DC626F060FBA2D0425004CAC2A61D982D2E3D85008624B45DB022CF51BA265B5E974712A9372EECAC0EA272B2FC56EBED0D32105521BA2C4A8FE0C678CE4E45902C7BA9D510BD47B2B5F931DD732F27DE9B42FD4AA39EAC765283A9965EE97C0D88E23EFA6F718242C67770B87BF8832858C1D13FC520870BD34F2B9C6FBFD1A528B744F814C93F4F4E87108316FE2AB06E02292DEA7FCF6FEFB17BF5AA7376A4A9BDB7C49BF709EB1E05D60EF14CD85A75239B97BCA9A6A3CC1B28F28979D612431BAAC1ACEE5EF62776B4D51B7EB0F63DF507760097223CA903E16E02DEB7FCABFBEC26DAEDC0ED4CC55726BDC31D1775112EF3C35D1DF928C6EB7830D8CA6570CB5CE348E3F26DDE864F20E5BE7B99E264EBC0E9D8DE9C6E4B7FE3CFBE673833CF7E8B3081529062CB6815C7C0766822B3B31E56BA1FC73FE3DED4B5D435BFCE2F2997C1D4B9CE293220DD461103BE084BF12076372668A69836769C1F6D8C32E2C7BC2E7D66714C814793A2970C90DD94DF14C89C60DD35B52A14778E137E750CE83AC3AAB667FCBDCBA38B7FA6D1C6BF7B99D957078176D9779A09F84B75FBC2A11769EF65532B09ACA4C9A3766B4A1FC717F94648FB8B8D9363E54F1C4201C075C18B1EAE098B83598089585ED9DC06B96E2D1C96DC738086EBBC26C3193B64139E1FC1DFB22A17893506EF7B35792B4EB00196693686EB5DEB3CEB436DD16D2D92A0FD31F468AF8662040F5257BFA0F14991C0D560999EEF775178D14955ADF091DD797AC1FDCEC7776055271C0F130562D0B0A6749B159DD0DB9AC69271AC719B83B683CE8B32342AC4AB257B0F8083C8CC86338AFA4D386C9848F413ED0").unwrap().try_into().unwrap(); // encaps @@ -235,13 +252,16 @@ mod mlkem_tests { #[test] fn test_decaps_from_seed() { - let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + &hex::decode( + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7 - ").unwrap(), + ", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let expected_pk_bytes: [u8; MLKEM1024_PK_LEN] = hex::decode("A04184D4BC7B532A0F70A54D7757CDE6175A6843B861CB2BC4830C0012554CFC5D2C8A2027AA3CD967130E9B96241B11C4320C7649CC23A71BAFE691AFC08E680BCEF42907000718E4EACE8DA28214197BE1C269DA9CB541E1A3CE97CFADF9C6058780FE6793DBFA8218A2760B802B8DA2AA271A38772523A76736A7A31B9D3037AD21CEBB11A472B8792EB17558B940E70883F264592C689B240BB43D5408BF446432F412F4B9A5F6865CC252A43CF40A320391555591D67561FDD05353AB6B019B3A08A73353D51B6113AB2FA51D975648EE254AF89A230504A236A4658257740BDCBBE1708AB022C3C588A410DB3B9C308A06275BDF5B4859D3A2617A295E1A22F90198BAD0166F4A943417C5B831736CB2C8580ABFDE5714B586ABEEC0A175A08BC710C7A2895DE93AC438061BF7765D0D21CD418167CAF89D1EFC3448BCBB96D69B3E010C82D15CAB6CACC6799D3639669A5B21A633C865F8593B5B7BC800262BB837A924A6C5440E4FC73B41B23092C3912F4C6BEBB4C7B4C62908B03775666C22220DF9C88823E344C7308332345C8B795D34E8C051F21F5A21C214B69841358709B1C305B32CC2C3806AE9CCD3819FFF4507FE520FBFC27199BC23BE6B9B2D2AC1717579AC769279E2A7AAC68A371A47BA3A7DBE016F14E1A727333663C4A5CD1A0F8836CF7B5C49AC51485CA60345C990E06888720003731322C5B8CD5E6907FDA1157F468FD3FC20FA8175EEC95C291A262BA8C5BE990872418930852339D88A19B37FEFA3CFE82175C224407CA414BAEB37923B4D2D83134AE154E490A9B45A0563B06C953C3301450A2176A07C614A74E3478E48509F9A60AE945A8EBC7815121D90A3B0E07091A096CF02C57B25BCA58126AD0C629CE166A7EDB4B33221A0D3F72B85D562EC698B7D0A913D73806F1C5C87B38EC003CB303A3DC51B4B35356A67826D6EDAA8FEB93B98493B2D1C11B676A6AD9506A1AAAE13A824C7C08D1C6C2C4DBA9642C76EA7F6C8264B64A23CCCA9A74635FCBF03E00F1B5722B214376790793B2C4F0A13B5C40760B4218E1D2594DCB30A70D9C1782A5DD30576FA4144BFC8416EDA8118FC6472F56A979586F33BB070FB0F1B0B10BC4897EBE01BCA3893D4E16ADB25093A7417D0708C83A26322E22E6330091E30152BF823597C04CCF4CFC7331578F43A2726CCB428289A90C863259DD180C5FF142BEF41C7717094BE07856DA2B140FA67710967356AA47DFBC8D255B4722AB86D439B7E0A6090251D2D4C1ED5F20BBE6807BF65A90B7CB2EC0102AF02809DC9AC7D0A3ABC69C18365BCFF59185F33996887746185906C0191AED4407E139446459BE29C6822717644353D24AB6339156A9C424909F0A9025BB74720779BE43F16D81C8CC666E99710D8C68BB5CC4E12F314E925A551F09CC59003A1F88103C254BB978D75F394D3540E31E771CDA36E39EC54A62B5832664D821A72F1E6AFBBA27F84295B2694C498498E812BC8E9378FE541CEC5891B25062901CB7212E3CDC46179EC5BCEC10BC0B9311DE05074290687FD6A5392671654284CD9C8CC3EBA80EB3B662EB53EB75116704A1FEB5C2D056338532868DDF24EB8992AB8565D9E490CADF14804360DAA90718EAB616BAB0765D33987B47EFB6599C5563235E61E4BE670E97955AB292D9732CB8930948AC82DF230AC72297A23679D6B94C17F1359483254FEDC2F05819F0D069A443B78E3FC6C3EF4714B05A3FCA81CBBA60242A7060CD885D8F39981BB18092B23DAA59FD9578388688A09BBA079BC809A54843A60385E2310BBCBCC0213CE3DFAAB33B47F9D6305BC95C6107813C585C4B657BF30542833B14949F573C0612AD524BAAE69590C1277B86C286571BF66B3CFF46A3858C09906A794DF4A06E9D4B0A2E43F10F72A6C6C47E5646E2C799B71C33ED2F01EEB45938EB7A4E2E2908C53558A540D350369FA189C616943F7981D7618CF02A5B0A2BCC422E857D1A47871253D08293C1C179BCDC0437069107418205FDB9856623B8CA6B694C96C084B17F13BB6DF12B2CFBBC2B0E0C34B00D0FCD0AECFB27924F6984E747BE2A09D83A8664590A8077331491A4F7D720843F23E652C6FA840308DB4020337AAD37967034A9FB523B67CA70330F02D9EA20C1E84CB8E5757C9E1896B60581441ED618AA5B26DA56C0A5A73C4DCFD755E610B4FC81FF84E21").unwrap() .try_into().unwrap(); @@ -251,8 +271,16 @@ mod mlkem_tests { assert_eq!(pk.encode(), expected_pk_bytes.as_slice()); - let message: [u8; MLKEM_RND_LEN] = hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72").unwrap().try_into().unwrap(); - let expected_shared_secret: [u8; MLKEM_SS_LEN] = hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058").unwrap().try_into().unwrap(); + let message: [u8; MLKEM_RND_LEN] = + hex::decode("59C5154C04AE43AAFF32700F081700389D54BEC4C37C088B1C53F66212B12C72") + .unwrap() + .try_into() + .unwrap(); + let expected_shared_secret: [u8; MLKEM_SS_LEN] = + hex::decode("5CF38F578AC4AE95FBFED574B3D8EBF7CB1DC9074F22277360E36D775347C058") + .unwrap() + .try_into() + .unwrap(); let expected_ciphertext: [u8; MLKEM1024_CT_LEN] = hex::decode("8B9FE419250C5FB0463C8181FCF7CEC777136B738E015EBA31067AA4A8C378BBAC0121B88214F1AEB866E4F33C277099E09B4BF7E21CDDA30B5B32C18B0E9660C30601D85DAEC07AAF4B343EC5516FA501DD63088B999FB9A414C6CA593806C08CD4C775139BF0F0BF3676D773EDD56E616A13830D5F5FE35E515DBC84E43AAD0167D57E60A9DE30886ACD3F7F2006CAC26A7A07B4DADBEDFBED7F305764386AAD726D5B2BF14A376BAD8B4896688491733FB34E6EDEA10BFD5E448541CB6E69E3D87DF190AFA7FF62577775BAACEA444A6128A20200251D8FA759DC60FDA6A9730CFFE4997FE7EBCDD1644AE2D55290A4074CDD2CE53C18D22BC33671E68727A9B5A2FEAFB114A8045D96A56981E200A09661375987625ACC233EDE817AF1DEEAA21C7C4377423E73C5AF9BFF58A49DE6DAFD07A3E3BABD891F62BBA41D1856B8BC502CC86EE115A3598431E2B54AB0C5EACC3CE6A03090925C1FD5A251B00576763A963994A7A23EE12EBFC1B994F93C6144178F0BEF88245CE77CD32EF651826A6090AF561A5864DEC2A51D846F1F48F88B4B55F58C2373E0F67BDC95DC23A43E8546232A7B234E49F5226A3A63BDBCED7240FC81C2DB68AAEB2671A2FD231997BF8839C63A7F41F15E7242821D42E80BBC0F43FA9E353DE8B25ED8FFC242EB512C6A5260919AAE89A11176532BCCC762A520A37AEC4E7209AA81CEE0DD4ADD932C47EB8100BE98AA1DEEA9EA698115ADCED950A6C536D19AEB325CEA8C5245C0A2281533FB90809DC2BE90567EBE6AE229FE09B44DA2182585EA694D8A9AB33EBC24B44E09BD510F34B4140E1FB41162F9415F2D9106A0CEA00A26ED0920021F4E5BCFB3DABF5850DAB22B2E889D9611FBE06D0C899708EB5E5FAD2FBBE0D5C0BDE080F8E760EDFA037D55DA77F0F39591BF5B050C905FA538B7228E238A290DF340778DCBD6BE40A3B1DD455FB27ADBE176AEF6CC295BEA570BDC221BA14002E3B113B0EF237452FBC9F1AEC42E0D2B33F19832DB0A6171CAEB0B30EEAD3A54B704B761C7D4AFEA8F6AFC15156666A081C43AEB2E04FEECEF8AABA4049BD78B120B9ABA86A60342A0CF806411C473C26C4BE1540E3312388BCBC8523BA73F40EA28D5564274F3661D7ACAA0F1E8D0F28DCF6B501329963E6857FDB2AAE873A7D9D6C14821F6C0B6AA50AC449075CD6F2A256C5A05959DAB5A5912CC8E8F8B9F59941BFCCE6A28CBA74A20382B1FD3382D056547D5BC5EF4AAE62F96F038C595A4F901D6AE790F8978292AD1CC3A1E800B71A5BBE84533646655E3752FBD6B02B97B204E75D28A34C2F990FB8E8CD31CE6E683FA7E67DA03367E8D47DC626F060FBA2D0425004CAC2A61D982D2E3D85008624B45DB022CF51BA265B5E974712A9372EECAC0EA272B2FC56EBED0D32105521BA2C4A8FE0C678CE4E45902C7BA9D510BD47B2B5F931DD732F27DE9B42FD4AA39EAC765283A9965EE97C0D88E23EFA6F718242C67770B87BF8832858C1D13FC520870BD34F2B9C6FBFD1A528B744F814C93F4F4E87108316FE2AB06E02292DEA7FCF6FEFB17BF5AA7376A4A9BDB7C49BF709EB1E05D60EF14CD85A75239B97BCA9A6A3CC1B28F28979D612431BAAC1ACEE5EF62776B4D51B7EB0F63DF507760097223CA903E16E02DEB7FCABFBEC26DAEDC0ED4CC55726BDC31D1775112EF3C35D1DF928C6EB7830D8CA6570CB5CE348E3F26DDE864F20E5BE7B99E264EBC0E9D8DE9C6E4B7FE3CFBE673833CF7E8B3081529062CB6815C7C0766822B3B31E56BA1FC73FE3DED4B5D435BFCE2F2997C1D4B9CE293220DD461103BE084BF12076372668A69836769C1F6D8C32E2C7BC2E7D66714C814793A2970C90DD94DF14C89C60DD35B52A14778E137E750CE83AC3AAB667FCBDCBA38B7FA6D1C6BF7B99D957078176D9779A09F84B75FBC2A11769EF65532B09ACA4C9A3766B4A1FC717F94648FB8B8D9363E54F1C4201C075C18B1EAE098B83598089585ED9DC06B96E2D1C96DC738086EBBC26C3193B64139E1FC1DFB22A17893506EF7B35792B4EB00196693686EB5DEB3CEB436DD16D2D92A0FD31F468AF8662040F5257BFA0F14991C0D560999EEF775178D14955ADF091DD797AC1FDCEC7776055271C0F130562D0B0A6749B159DD0DB9AC69271AC719B83B683CE8B32342AC4AB257B0F8083C8CC86338AFA4D386C9848F413ED0").unwrap().try_into().unwrap(); // encaps @@ -269,19 +297,22 @@ mod mlkem_tests { #[test] fn keygen_error_cases() { /* - Testing this condition: - if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) - || seed.key_len() != 64 - */ + Testing this condition: + if !(seed.key_type() == KeyType::Seed || seed.key_type() == KeyType::BytesFullEntropy) + || seed.key_len() != 64 + */ // success case KeyType: seed let mut seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); - + ) + .unwrap(); /* MLKEM512 */ let expected_sk_bytes: [u8; MLKEM512_SK_LEN] = hex::decode("70554fd436344f2785b1b3b1bac184b6679003336c26f15a7de878c4825c6be03f3c4a480f75b7486aad31d3a00518623fd207ab528dd62721495835ae0062c367b74a71baf10aad0e8a2902076be31348beb15ccc0957cdebb4aff226756bbc601b6568ab784acbaeb34702f0f86a26202118b22b23f83558776c79c14dba983379c803e0dcc3160a11757030e69c6919798d81eb698a9a4483a99e5a5cb2c31c9a661799f3cc89c790706ea041629045d42a83aed88860e394c69187e2105d28cc14ec393592d67dd00aa43fe8b4eae4414002866b5c713c6a8d7d16cf78b819d6f12e9e5a74233908f0b15e3c4ba8329c5cdda55c84928e3aa8063e5aa9676403f91735b11010c7f593091364dc86445bc804840a9a21724212469f8a7b0ce0ac698eb86cad39a7f4824d9a5163aac21ee6808b053c8a3facb0b6744b5262bbcb26a43f664c8732b64cfc7acf099605f41c796060976ac433833fe00343fb1828300a424741116e4b45bb276ea81129a0db4c6e60bce611101e8c625474925e0222679308a3e7708d1972a7b423eb232851c36d2ed53d3ed3bb7500637061a5dc2292fa1c466c07354683328bec2c1ed2cb5c99b78eca0969038cf7c34dd118724e31cae086206b34302b520f5d177aded5b3cce02acce808ea26bcc072625fdb93f17458a5fc1d4da394380a1f57e9cc66109438a075f0d2813fcc4a199cc76db3823f270b0061594192940411a37ffbafae2c150165cec5c6bf73c595fb92cd15312607da070778652bd9944bc48bc7d1a534338bad0bad6656c5d502ce7850ab1587244eeb58f439ab5e08574a718c8aac3d77c798bba1542733be73448f23fb70c0e5353a27c88322c5218493afbb38086434d6d60a56ba887dd498c3ab26a0870993815aa6a40975f218adca1582d64ffc8652fbb3a9a6fbc304f91945fa4aaef2878fd715df70113d2379f44886f812c83ff2b719a69e1ec74ae4b15accd3aed5a53ce76a7b0982471633b973cb40a1a0015d0a424fa11a479c023017436d2a2900e993eb5a0a067400c7f4aadf201fc4fa31264a63bae95cc8d65c3995815e597d104355cf29aa5333c93251869d5bcdbe487124f602b8b6a66c16c4761648ad765cf5d8006b515e905a7f0ac076b0c62efa328153e7ca5701699f1305f1e6bc6f90b0e49b693512b6ce992a8b8016ddfc1a662c7e3f9619cbd869dd771af30896ccd5918ac6cb77466c5e779996d67ff9aabc97503f2c7b7e2d000d86450fb1807ca4cabda465825a31c789a1b7a491ab3872765d320d0b71920fa213c94093416b83b8124e69f65e62cb5000dcc37aa9a0fff73970c4772f357d24189ca6f5305568c0e2376a3762a68c605e563c5d209572e0fc7532ca294729535567b5fc413c5e8792d2464536cc808f98add74664f141566f9016a90a541829a98a0464ce41a8bb44c2d4fa3c2c209460728ef14a1a7c4c9b98d12203b4cc3529160a9ab2d7838f7ff6b53ae05aa31a7d646b7afa6c45932526a3c3755619be994c211c2a31c05b3447836cb2150be1829dae6b04c5535cff546e392ba797411720f924f490a5ac5495f21356d550b782a64c1688b6b655bcc7842197a434c2f6563b5b7f09a78bcc488232783561d16f4cbab6755400050781570c66604b817ad1252294736e8b01861a4b5a74519b8b6fe51489a5072392e587626c713776575d33806a1c8e2732af97c2680f51666331c4eb8bbc0431c4f96832daf1b3c45528fba153f6c78b1c198702947ccd337727a46fb53ba11de5cb4191346859516cb6ad72400f3cf209b236aef35a580ac87eb3e30fafd66973ca8a7dd2675af41f7a17b61433cd1af80f7708869f665488497980b1ac10a0cdcb636a00ed8681b35e429124ca80350725b85f83a5eac3a4a3cc1600903e65293560b9b336e5af0d529dac1a048119302cb7a9bcc110b94851bf02117f199dc485a852b7473f09b831a6831d5b54c0b790d225cf6bb92d9462a26cdb33dda5123c7aaf0e26a0b83655eea28bf3a8074725018fd6bae4b601cf61baab71a7a3d35197a343e74b4a272c125d540896426d85b7958d3b38a6ba987ec37225c7b44cdb12dde4539b4ab082363683f04bf7a09cc5c41dfe830a1b162e0b324334362f084a14467723344badd000f8d8c537c48f998f05307cebd1ede0b81c3bc59a065a1b6d63b26c82f101ff648063b376e2bb6c5b7455f655a50c2feadade150efa0e0e6f365aea202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f").unwrap() @@ -293,42 +324,51 @@ mod mlkem_tests { assert_eq!(derived_pk.encode(), expected_pk_bytes.as_slice()); // success case KeyType: BytesFullEntropy - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::BytesFullEntropy).unwrap(); - _ = MLKEM512::keygen_from_seed(&seed).unwrap(); + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::CryptographicRandom) + }) + .unwrap(); + _ = MLKEM512::keygen_from_seed(&seed).unwrap(); // Failure case: key type != Seed || BytesFullEntropy let mac_seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e3f").unwrap(), + 303132333435363738393a3b3c3d3e3f", + ) + .unwrap(), KeyType::MACKey, - ).unwrap(); + ) + .unwrap(); match MLKEM512::keygen_from_seed(&mac_seed) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } // Failure case: key is undersized let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("000102030405060708090a0b0c0d0e0f + &hex::decode( + "000102030405060708090a0b0c0d0e0f 101112131415161718191a1b1c1d1e1f 202122232425262728292a2b2c2d2e2f - 303132333435363738393a3b3c3d3e").unwrap(), + 303132333435363738393a3b3c3d3e", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); assert_eq!(seed.key_len(), 63); match MLKEM512::keygen_from_seed(&seed) { - Err(KEMError::KeyGenError(_)) => { /* good */ }, + Err(KEMError::KeyGenError(_)) => { /* good */ } _ => panic!("expected KeyGenError"), } } - /// test the "Decapsulation input checks" in Section 7.3 #[test] fn test_decapsulation_input_checks() { @@ -336,7 +376,6 @@ mod mlkem_tests { // 𝑑𝑣, and 𝑘 specified by the relevant parameter set, then input checking has failed. // This is already tested in [test_boundary_conditions] - // 2. (Decapsulation key type check) If dk is not a byte array of length 768𝑘 + 96 for the value of // 𝑘 specified by the relevant parameter set, then input checking has failed. // This does not need to be tested because of the static-sizing of the SK array. @@ -354,23 +393,23 @@ mod mlkem_tests { // just to check our array index math, make sure we know how to extract the public key ek from the sk bytes // and that it loads properly and gives the same key. - let pk2_encoded: &[u8] = &sk_encoded[384*MLKEM512_k .. 768*MLKEM512_k + 32]; + let pk2_encoded: &[u8] = &sk_encoded[384 * MLKEM512_k..768 * MLKEM512_k + 32]; let pk2 = MLKEM512PublicKey::from_bytes(pk2_encoded).unwrap(); assert_eq!(pk1, pk2); // flip some bits in the range that contains ek, but in a way that still results in a valid public key ek #[allow(non_upper_case_globals)] const MLKEM512_k: usize = 2; - sk_encoded[384*MLKEM512_k + 2] ^= 0x0F; + sk_encoded[384 * MLKEM512_k + 2] ^= 0x0F; // check that this is still a valid public key, but different from pk1 - let pk3_encoded: &[u8] = &sk_encoded[384*MLKEM512_k .. 768*MLKEM512_k + 32]; + let pk3_encoded: &[u8] = &sk_encoded[384 * MLKEM512_k..768 * MLKEM512_k + 32]; let pk3 = MLKEM512PublicKey::from_bytes(pk3_encoded).unwrap(); assert_ne!(pk1, pk3); // Now for the important part: sk will refuse to load with a KEMError::ConsistencyCheckFailed match MLKEM512PrivateKey::from_bytes(&sk_encoded) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } Err(e) => panic!("expected ConsistencyCheckFailed, got {:?}", e), Ok(_) => panic!("expected ConsistencyCheckFailed, got Ok()"), } @@ -402,18 +441,21 @@ mod mlkem_tests { /// cest that a corrupted ct returns the implicit rejection value K_bar = J(z||c) fn test_implicit_rejection() { let seed = KeyMaterial512::from_bytes_as_type( - &hex::decode("49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 + &hex::decode( + "49AC8B99BB1E6A8EA818261F8BE68BDEAA52897E7EC6C40B530BC760AB77DCE3 99E3246884181F8E1DD44E0C7629093330221FD67D9B7D6E1510B2DBAD8762F7 - ").unwrap(), + ", + ) + .unwrap(), KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // encaps let (_ss, ct) = MLKEM512::encaps(&pk).unwrap(); - // decaps with busted ciphertext let mut busted_ciphertext = ct.clone(); busted_ciphertext[17] ^= 0xFF; @@ -429,13 +471,12 @@ mod mlkem_tests { shake.absorb(&seed.ref_to_bytes()[32..64]); shake.absorb(&busted_ciphertext); let mut buf = [0u8; 32]; - _ = shake.squeeze_out(&mut buf); + _ = shake.squeeze_out(&mut buf); assert_eq!(ss.ref_to_bytes(), buf); - }, + } _ => panic!("This should have succeeded but with the wrong ss."), } - } #[test] @@ -443,7 +484,6 @@ mod mlkem_tests { /// /// This test satisfies testing condition #1 in the Decapsulation Input checks in FIPS 203 section 7.3 fn test_boundary_conditions() { - // ct too long / too short // // satisfies testing condition #1 in the Decapsulation Input checks in FIPS 203 section 7.3 @@ -453,11 +493,11 @@ mod mlkem_tests { // MLKEM512 let (pk, sk) = MLKEM512::keygen().unwrap(); - let (_ss, ct) = MLKEM512::encaps(&pk).unwrap(); + let (_ss, ct) = MLKEM512::encaps(&pk).unwrap(); // too short - match MLKEM512::decaps(&sk, &ct[..MLKEM512_CT_LEN-1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + match MLKEM512::decaps(&sk, &ct[..MLKEM512_CT_LEN - 1]) { + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -465,17 +505,17 @@ mod mlkem_tests { ct_too_long[..MLKEM512_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM512_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM512::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // MLKEM768 let (pk, sk) = MLKEM768::keygen().unwrap(); - let (_ss, ct) = MLKEM768::encaps(&pk).unwrap(); + let (_ss, ct) = MLKEM768::encaps(&pk).unwrap(); // too short - match MLKEM768::decaps(&sk, &ct[..MLKEM512_CT_LEN-1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + match MLKEM768::decaps(&sk, &ct[..MLKEM512_CT_LEN - 1]) { + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -483,17 +523,17 @@ mod mlkem_tests { ct_too_long[..MLKEM768_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM768_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM768::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // MLKEM1024 let (pk, sk) = MLKEM1024::keygen().unwrap(); - let (_ss, ct) = MLKEM1024::encaps(&pk).unwrap(); + let (_ss, ct) = MLKEM1024::encaps(&pk).unwrap(); // too short - match MLKEM1024::decaps(&sk, &ct[..MLKEM1024_CT_LEN-1]) { - Err(KEMError::LengthError(_)) => { /* good */ }, + match MLKEM1024::decaps(&sk, &ct[..MLKEM1024_CT_LEN - 1]) { + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } // too long @@ -501,7 +541,7 @@ mod mlkem_tests { ct_too_long[..MLKEM1024_CT_LEN].copy_from_slice(&ct); ct_too_long[MLKEM1024_CT_LEN..].copy_from_slice(&[1u8, 0u8]); match MLKEM1024::decaps(&sk, &ct_too_long) { - Err(KEMError::LengthError(_)) => { /* good */ }, + Err(KEMError::LengthError(_)) => { /* good */ } _ => panic!("Expected error for sig too short"), } } @@ -520,7 +560,7 @@ mod mlkem_tests { let p = Polynomial::new(); assert_eq!(format!("{:?}", p), "Polynomial (data masked)"); } - + #[test] fn keypair_consistency_check() { // this is common to all parameter sets, so I'll just test MLKEM512 @@ -532,11 +572,11 @@ mod mlkem_tests { // failure case: different but valid key let (pk2, sk2) = MLKEM512::keygen().unwrap(); match MLKEM512::keypair_consistency_check(&pk, &sk2) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; match MLKEM512::keypair_consistency_check(&pk2, &sk) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; @@ -545,17 +585,18 @@ mod mlkem_tests { pk_bytes[17] ^= 0x01; let pk2 = MLKEM512PublicKey::from_bytes(&pk_bytes).unwrap(); match MLKEM512::keypair_consistency_check(&pk2, &sk) { - Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ }, + Err(KEMError::ConsistencyCheckFailed(_)) => { /* good */ } _ => panic!("Expected error for different key"), }; } #[test] fn test_expanded_keys() { - use bouncycastle_mlkem::{MLKEM512, MLKEM512PublicKeyExpanded, MLKEM512PrivateKeyExpanded}; - use bouncycastle_mlkem::{MLKEM768, MLKEM768PublicKeyExpanded, MLKEM768PrivateKeyExpanded}; - use bouncycastle_mlkem::{MLKEM1024, MLKEM1024PublicKeyExpanded, MLKEM1024PrivateKeyExpanded}; - + use bouncycastle_mlkem::{MLKEM512, MLKEM512PrivateKeyExpanded, MLKEM512PublicKeyExpanded}; + use bouncycastle_mlkem::{MLKEM768, MLKEM768PrivateKeyExpanded, MLKEM768PublicKeyExpanded}; + use bouncycastle_mlkem::{ + MLKEM1024, MLKEM1024PrivateKeyExpanded, MLKEM1024PublicKeyExpanded, + }; /*** ML-KEM-512 ***/ @@ -571,7 +612,6 @@ mod mlkem_tests { }; assert_eq!(ss, ss1); - /*** ML-KEM-768 ***/ let (pk, sk) = MLKEM768::keygen().unwrap(); @@ -586,7 +626,6 @@ mod mlkem_tests { }; assert_eq!(ss, ss1); - /*** ML-KEM-1024 ***/ let (pk, sk) = MLKEM1024::keygen().unwrap(); @@ -601,6 +640,191 @@ mod mlkem_tests { }; assert_eq!(ss, ss1); } + + #[test] + fn keygen_from_rng_tests() { + /* keygen from seed must match keygen from rng, for a fixed-seed rng */ + // An arbitrary fixed 64-byte seed (bytes 0x00..=0x3f). + let seed_bytes: [u8; 64] = hex::decode( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\ + 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + + // The seed as fed directly to keygen_from_seed. + let seed = KeyMaterial512::from_bytes_as_type(&seed_bytes, KeyType::Seed).unwrap(); + + // ML-KEM-512 + let (pk_seed, sk_seed) = MLKEM512::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM512::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-512 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-512 sk from RNG must match sk from seed"); + + // ML-KEM-768 + let (pk_seed, sk_seed) = MLKEM768::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM768::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-768 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-768 sk from RNG must match sk from seed"); + + // ML-KEM-1024 + let (pk_seed, sk_seed) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (pk_rng, sk_rng) = MLKEM1024::keygen_from_rng(&mut rng).unwrap(); + assert_eq!(pk_rng, pk_seed, "ML-KEM-1024 pk from RNG must match pk from seed"); + assert_eq!(sk_rng, sk_seed, "ML-KEM-1024 sk from RNG must match sk from seed"); + + /* Test that keygen rejects a seed from a weak RNG */ + + // ML-KEM-512 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_112bit); + match MLKEM512::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-512 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_128bit); + _ = MLKEM512::keygen_from_rng(&mut rng).unwrap(); + + // ML-KEM-768 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_128bit); + match MLKEM768::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-768 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_192bit); + _ = MLKEM768::keygen_from_rng(&mut rng).unwrap(); + + // ML-KEM-1024 -- RNG too weak + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_192bit); + match MLKEM1024::keygen_from_rng(&mut rng) { + Err(KEMError::RNGError(RNGError::SecurityStrengthInsufficientForAlgorithm)) => { /* good */ + } + _ => { + panic!("should have returned RNGError::RNGSecurityStrengthInsufficientForAlgorithm") + } + } + + // ML-KEM-1024 -- RNG just right + let mut rng = FixedSeedRNG::new([0u8; 64]); + rng.set_security_strength(SecurityStrength::_256bit); + _ = MLKEM1024::keygen_from_rng(&mut rng).unwrap(); + } + + #[test] + fn encaps_rng_tests() { + use bouncycastle_mlkem::{ + // Prove that `encaps_for_expanded_key_rng` is just `encaps_internal` with the message `m` + // sourced from the RNG: when the RNG hands back exactly the bytes that `m` would be, the two + // must produce the same shared secret and ciphertext. + MLKEM512PublicKeyExpanded, + MLKEM768PublicKeyExpanded, + MLKEM1024PublicKeyExpanded, + }; + + // An arbitrary fixed 64-byte seed; FixedSeedRNG::next_bytes_out hands back its leading + // 32 bytes as the encapsulation message `m`. + let seed_bytes: [u8; 64] = hex::decode( + "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\ + 202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", + ) + .unwrap() + .try_into() + .unwrap(); + let m: [u8; MLKEM_RND_LEN] = seed_bytes[..MLKEM_RND_LEN].try_into().unwrap(); + + // ML-KEM-512 + let (pk512, _sk) = MLKEM512::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM512::encaps_internal(&pk512, None, m); + let pk_expanded = MLKEM512PublicKeyExpanded::from(&pk512); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM512::encaps_for_expanded_key_rng(&pk_expanded, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-512 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-512 shared secret must match encaps_internal" + ); + + // ML-KEM-768 + let (pk768, _sk) = MLKEM768::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM768::encaps_internal(&pk768, None, m); + let pk_expanded = MLKEM768PublicKeyExpanded::from(&pk768); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM768::encaps_for_expanded_key_rng(&pk_expanded, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-768 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-768 shared secret must match encaps_internal" + ); + + // ML-KEM-1024 + let (pk1024, _sk) = MLKEM1024::keygen().unwrap(); + let (ss_ref, ct_ref) = MLKEM1024::encaps_internal(&pk1024, None, m); + let pk_expanded = MLKEM1024PublicKeyExpanded::from(&pk1024); + let mut rng = FixedSeedRNG::new(seed_bytes); + let (ss, ct) = MLKEM1024::encaps_for_expanded_key_rng(&pk_expanded, &mut rng).unwrap(); + assert_eq!(ct, ct_ref, "ML-KEM-1024 ciphertext must match encaps_internal"); + assert_eq!( + ss_ref, + ss.ref_to_bytes(), + "ML-KEM-1024 shared secret must match encaps_internal" + ); + + // Ensure that it rejects an RNG at a lower security level + let mut fake_rng = FixedSeedRNG::new([0u8; 64]); + + // fails + fake_rng.set_security_strength(SecurityStrength::_112bit); + match MLKEM512::encaps_rng(&pk512, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_128bit); + _ = MLKEM512::encaps_rng(&pk512, &mut fake_rng).unwrap(); + + // fails + fake_rng.set_security_strength(SecurityStrength::_128bit); + match MLKEM768::encaps_rng(&pk768, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_192bit); + _ = MLKEM768::encaps_rng(&pk768, &mut fake_rng).unwrap(); + + // fails + fake_rng.set_security_strength(SecurityStrength::_192bit); + match MLKEM1024::encaps_rng(&pk1024, &mut fake_rng) { + Err(KEMError::RNGError(_)) => { /* good */ } + _ => panic!("unexpected error"), + } + + // succeeds + fake_rng.set_security_strength(SecurityStrength::_256bit); + _ = MLKEM1024::encaps_rng(&pk1024, &mut fake_rng).unwrap(); + } } // struct Kat { diff --git a/crypto/mlkem/tests/wycheproof.rs b/crypto/mlkem/tests/wycheproof.rs new file mode 100644 index 0000000..cb50268 --- /dev/null +++ b/crypto/mlkem/tests/wycheproof.rs @@ -0,0 +1,899 @@ +//! Test against the project wycheproof repo available at: +//! https://github.com/C2SP/wycheproof +//! Requires that the wycheproof repository is cloned and available for testing at "../wycheproof" +//! relative to the root of this git project. +//! +//! This test file exercises the following test sets: +//! +//! * mlkem_512_encaps_test.json +//! * mlkem_512_keygen_seed_test.json +//! * mlkem_512_semi_expanded_decaps_test.json +//! * mlkem_512_test.json +//! * mlkem_768_encaps_test.json +//! * mlkem_768_keygen_seed_test.json +//! * mlkem_768_semi_expanded_decaps_test.json +//! * mlkem_768_test.json +//! * mlkem_1024_encaps_test.json +//! * mlkem_1024_keygen_seed_test.json +//! * mlkem_1024_semi_expanded_decaps_test.json +//! * mlkem_1024_test.json + +#![allow(dead_code)] + +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterial512, KeyMaterialTrait, KeyType}; +use bouncycastle_core::traits::{KEMDecapsulator, KEMPrivateKey, KEMPublicKey, SecurityStrength}; +use bouncycastle_hex as hex; +use bouncycastle_mlkem::{ + MLKEM512, MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768, MLKEM768PrivateKey, + MLKEM768PublicKey, MLKEM1024, MLKEM1024PrivateKey, MLKEM1024PublicKey, MLKEMTrait, +}; + +#[cfg(test)] +mod wycheproof { + use crate::{ + MLKEMEncapsTestCase, MLKEMKeygenSeedTestCase, MLKEMSemiExpandedDecapsTestCase, + MLKEMTestCase, ParameterSet, + }; + use std::fs; + use std::path::Path; + use std::sync::Once; + + const TEST_DATA_PATH_RELATIVE: &str = "../../../wycheproof/testvectors_v1"; + const TEST_DATA_PATH: &str = "../wycheproof/testvectors_v1"; + + static TEST_DATA_CHECK: Once = Once::new(); + + fn get_test_data(filename: &str) -> Result { + let found: u8; + if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + found = 1; + } else if Path::new(TEST_DATA_PATH).exists() { + found = 2; + } else { + found = 3; + }; + + // just print once + TEST_DATA_CHECK.call_once(|| match found { + 1 => println!("wycheproof found at: {:?}", TEST_DATA_PATH_RELATIVE), + 2 => println!("wycheproof found at: {:?}", TEST_DATA_PATH), + _ => println!("WARNING: wycheproof directory not found; tests will be skipped"), + }); + + if !found == 3 { + return Err(()); + } + + let contents = if Path::new(TEST_DATA_PATH_RELATIVE).exists() { + fs::read_to_string(TEST_DATA_PATH_RELATIVE.to_string() + "/" + filename).unwrap() + } else if Path::new(TEST_DATA_PATH).exists() { + fs::read_to_string(TEST_DATA_PATH.to_string() + "/" + filename).unwrap() + } else { + return Err(()); + }; + + Ok(contents) + } + + #[test] + fn mlkem_512_encaps_test() { + let contents = match get_test_data("mlkem_512_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_512_keygen_seed_test() { + let contents = match get_test_data("mlkem_512_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_512_semi_expanded_decaps_test() { + let contents = match get_test_data("mlkem_512_semi_expanded_decaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMSemiExpandedDecapsTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_semi_expanded_decaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_512_test() { + let contents = match get_test_data("mlkem_512_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem512); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem512(); + } + + println!("mlkem_512_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_encaps_test() { + let contents = match get_test_data("mlkem_768_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_keygen_seed_test() { + let contents = match get_test_data("mlkem_768_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_semi_expanded_decaps_test() { + let contents = match get_test_data("mlkem_768_semi_expanded_decaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMSemiExpandedDecapsTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_semi_expanded_decaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_768_test() { + let contents = match get_test_data("mlkem_768_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem768); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem768(); + } + + println!("mlkem_768_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_encaps_test() { + let contents = match get_test_data("mlkem_1024_encaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMEncapsTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_encaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_keygen_seed_test() { + let contents = match get_test_data("mlkem_1024_keygen_seed_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMKeygenSeedTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_keygen_seed_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_semi_expanded_decaps_test() { + let contents = match get_test_data("mlkem_1024_semi_expanded_decaps_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMSemiExpandedDecapsTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_semi_expanded_decaps_test: all {} test cases passed.", num_test_cases); + } + + #[test] + fn mlkem_1024_test() { + let contents = match get_test_data("mlkem_1024_test.json") { + Ok(contents) => contents, + Err(()) => return, + }; + + let test_cases = MLKEMTestCase::parse(contents, ParameterSet::Mlkem1024); + + let num_test_cases = test_cases.len(); + for test_case in test_cases { + test_case.run_mlkem1024(); + } + + println!("mlkem_1024_test: all {} test cases passed.", num_test_cases); + } +} + +/* Structs for holding test data */ + +#[derive(Clone, Debug, PartialEq)] +enum ParameterSet { + Mlkem512, + Mlkem768, + Mlkem1024, +} + +#[derive(Clone)] +struct MLKEMEncapsTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + m: String, + ek: String, + c: String, + k: String, + result: String, +} + +impl MLKEMEncapsTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + m: String::new(), + ek: String::new(), + c: String::new(), + k: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + m: test["m"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + c: test["c"].as_str().unwrap_or("").to_string(), + k: test["K"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + /* Load the key */ + + let ek = match MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + let (k, ct) = + MLKEM512::encaps_internal(&ek, None, hex::decode(&self.m).unwrap().try_into().unwrap()); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + /* Load the key */ + + let ek = match MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + let (k, ct) = + MLKEM768::encaps_internal(&ek, None, hex::decode(&self.m).unwrap().try_into().unwrap()); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + /* Load the key */ + + let ek = match MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()) { + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("{:?}", e); + } + } + Ok(pk) => pk, + }; + + /* Perform the deterministic encaps and compare results */ + + let (k, ct) = MLKEM1024::encaps_internal( + &ek, + None, + hex::decode(&self.m).unwrap().try_into().unwrap(), + ); + + if self.result == "valid" { + assert_eq!(k, hex::decode(&self.k).unwrap().as_slice()); + assert_eq!(ct, hex::decode(&self.c).unwrap().as_slice()); + } else { + // is there anything to test here? + } + } +} + +#[derive(Clone)] +struct MLKEMKeygenSeedTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + seed: String, + ek: String, + dk: String, + result: String, +} + +impl MLKEMKeygenSeedTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + seed: String::new(), + ek: String::new(), + dk: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + seed: test["seed"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + dk: test["dk"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk, MLKEM512PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap()); + assert_eq!(&dk.encode(), hex::decode(&self.dk).unwrap().as_slice()); + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk, MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap()); + assert_eq!(&dk.encode(), hex::decode(&self.dk).unwrap().as_slice()); + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + // currently, the wycheproof tests contain only valid tests, so just run them; no errors to check + + let seed = + KeyMaterial512::from_bytes_as_type(&hex::decode(&self.seed).unwrap(), KeyType::Seed) + .unwrap(); + + let (ek, dk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + + assert_eq!(ek, MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap()); + assert_eq!(&ek.encode(), hex::decode(&self.ek).unwrap().as_slice()); + + assert_eq!(dk, MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap()); + assert_eq!(&dk.encode(), hex::decode(&self.dk).unwrap().as_slice()); + } +} + +#[derive(Clone)] +struct MLKEMSemiExpandedDecapsTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + dk: String, + c: String, + result: String, +} + +impl MLKEMSemiExpandedDecapsTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + dk: String::new(), + c: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + dk: test["dk"].as_str().unwrap_or("").to_string(), + c: test["c"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + /* Load the private key */ + let _dk = match MLKEM512PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) { + Ok(dk) => dk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load private key: {:?}", e); + } + } + }; + + // these tests provide c, but not the output key k, + // so there's really no reason to perform the decaps since there's really nothing to check it against + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + /* Load the private key */ + let _dk = match MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) { + Ok(dk) => dk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load private key: {:?}", e); + } + } + }; + + // these tests provide c, but not the output key k, + // so there's really no reason to perform the decaps since there's really nothing to check it against + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + /* Load the private key */ + let _dk = match MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()) { + Ok(dk) => dk, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load private key: {:?}", e); + } + } + }; + + // these tests provide c, but not the output key k, + // so there's really no reason to perform the decaps since there's really nothing to check it against + } +} + +#[derive(Clone)] +struct MLKEMTestCase { + parameter_set: ParameterSet, + tc_id: u32, + comment: String, + seed: String, + ek: String, + c: String, + k: String, + result: String, +} + +impl MLKEMTestCase { + fn new(parameter_set: ParameterSet) -> Self { + Self { + parameter_set, + tc_id: 0, + comment: String::new(), + seed: String::new(), + ek: String::new(), + c: String::new(), + k: String::new(), + result: String::new(), + } + } + + fn parse(data: String, parameter_set: ParameterSet) -> Vec { + let json: serde_json::Value = + serde_json::from_str(&data).expect("test data is not valid JSON"); + + let mut test_cases = Vec::::new(); + + let groups = json["testGroups"].as_array().expect("testGroups is not an array"); + for group in groups { + let tests = group["tests"].as_array().expect("tests is not an array"); + for test in tests { + test_cases.push(Self { + parameter_set: parameter_set.clone(), + tc_id: test["tcId"].as_u64().expect("tcId missing") as u32, + comment: test["comment"].as_str().unwrap_or("").to_string(), + seed: test["seed"].as_str().unwrap_or("").to_string(), + ek: test["ek"].as_str().unwrap_or("").to_string(), + c: test["c"].as_str().unwrap_or("").to_string(), + k: test["K"].as_str().unwrap_or("").to_string(), + result: test["result"].as_str().unwrap_or("").to_string(), + }); + } + } + + test_cases + } + + fn run_mlkem512(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem512); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (ek, dk) = match MLKEM512::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM512::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } + + fn run_mlkem768(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem768); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (ek, dk) = match MLKEM768::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM768::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } + + fn run_mlkem1024(&self) { + assert_eq!(self.parameter_set, ParameterSet::Mlkem1024); + + /* Load the private key */ + let mut seed = match KeyMaterial512::from_bytes_as_type( + &hex::decode(&self.seed).unwrap(), + KeyType::Seed, + ) { + Ok(seed) => seed, + Err(e) => { + if self.result == "invalid" { + /* good */ + return; + } else { + panic!("Failed to load seed: {:?}", e); + } + } + }; + // allow an all-zero seed for testing + key_material::do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match seed.set_security_strength(SecurityStrength::_256bit) { + Ok(_) => Ok(()), + Err(e) => { + if self.result == "invalid" { + /* good */ + Ok(()) + } else { + panic!("{:?}", e) + } + } + } + }) + .unwrap(); + + let (ek, dk) = match MLKEM1024::keygen_from_seed(&seed) { + Ok((ek, dk)) => (ek, dk), + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to generate key pair: {:?}", e); + } + } + }; + + // check that the derived ek matches the provided one + assert_eq!(&ek.encode(), &hex::decode(&self.ek).unwrap().as_slice()); + + // these tests don't provide m, so can't test deterministic encaps + + // test decaps + let k = match MLKEM1024::decaps(&dk, &hex::decode(&self.c).unwrap().as_slice()) { + Ok(k) => k, + Err(e) => { + if self.result == "invalid" { + return; + } else { + panic!("Failed to decapsulate: {:?}", e); + } + } + }; + + assert_eq!(k.ref_to_bytes(), hex::decode(&self.k).unwrap().as_slice()); + } +} diff --git a/crypto/mlkem_lowmemory/tests/bc_test_data.rs b/crypto/mlkem_lowmemory/tests/bc_test_data.rs deleted file mode 100644 index 848a60f..0000000 --- a/crypto/mlkem_lowmemory/tests/bc_test_data.rs +++ /dev/null @@ -1,293 +0,0 @@ -// Test against the bc-test-data repo -// Requires that the bc-test-data repository is cloned and available for testing at "../bc-test-data" -// relative to the root of this git project. - -// This whole file doesn't work because the bc-test-data repository only has full private keys and not seeds - -// #[cfg(test)] -// mod bc_test_data { -// use std::{fs}; -// use bouncycastle_hex as hex; -// use bouncycastle_core::key_material::{KeyMaterialTrait, KeyMaterial512, KeyType}; -// use bouncycastle_core::traits::{KEMPrivateKey, KEMPublicKey, SecurityStrength, KEM}; -// use bouncycastle_mlkem_lowmemory::{MLKEM1024PrivateKey, MLKEM1024PublicKey, MLKEM512PrivateKey, MLKEM512PublicKey, MLKEM768PrivateKey, MLKEM768PublicKey, MLKEMPrivateKeyTrait, MLKEMTrait, MLKEM1024, MLKEM1024_PK_LEN, MLKEM1024_SK_LEN, MLKEM512, MLKEM512_PK_LEN, MLKEM512_SK_LEN, MLKEM768, MLKEM768_PK_LEN, MLKEM768_SK_LEN}; -// use bouncycastle_mlkem_lowmemory::mlkem::{MLKEM1024_FULL_SK_LEN, MLKEM512_FULL_SK_LEN, MLKEM768_FULL_SK_LEN}; -// -// const TEST_DATA_PATH: &str = "../../../bc-test-data/pqc/crypto/mlkem"; -// -// #[test] -// #[allow(non_snake_case)] -// fn ML_KEM_keyGen() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-KEM-keyGen.txt").unwrap(); -// let test_cases = KeyGenTestCase::parse(contents); -// -// for test_case in test_cases { -// test_case.run(); -// } -// } -// -// #[derive(Clone)] -// struct KeyGenTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// tc_id: u32, -// z: String, -// d: String, -// ek: String, -// dk: String, -// } -// -// impl KeyGenTestCase { -// fn new() -> Self { -// Self { vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), tc_id: 0, z: String::new(), d: String::new(), ek: String::new(), dk: String::new() } -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = KeyGenTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "z" => test_case.z = value.to_string(), -// "d" => test_case.d = value.to_string(), -// "ek" => test_case.ek = value.to_string(), -// "dk" => test_case.dk = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "keyGen"); -// -// let mut seed_bytes = [0u8; 64]; -// seed_bytes[..32].copy_from_slice(&hex::decode(&self.d).unwrap()); -// seed_bytes[32..].copy_from_slice(&hex::decode(&self.z).unwrap()); -// -// let mut seed = KeyMaterial512::from_bytes_as_type( -// &seed_bytes, -// KeyType::Seed, -// ).unwrap(); -// -// // for the purposes of the test cases, accept an all-zero seed -// seed.allow_hazardous_operations(); -// seed.set_key_type(KeyType::Seed).unwrap(); -// seed.set_security_strength(SecurityStrength::_256bit).unwrap(); -// seed.drop_hazardous_operations(); -// -// match self.parameter_set.as_str() { -// "ML-KEM-512" => { -// let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLKEM512_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLKEM512_FULL_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); -// assert_eq!(sk.full_sk_encode(), sk_sized); -// }, -// "ML-KEM-768" => { -// let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLKEM768_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLKEM768_FULL_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); -// assert_eq!(sk.full_sk_encode(), sk_sized); -// }, -// "ML-KEM-1024" => { -// let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); -// let pk_sized: [u8; MLKEM1024_PK_LEN] = hex::decode(&self.ek).unwrap().try_into().unwrap(); -// assert_eq!(pk.encode(), pk_sized); -// let sk_sized: [u8; MLKEM1024_FULL_SK_LEN] = hex::decode(&self.dk).unwrap().try_into().unwrap(); -// assert_eq!(sk.full_sk_encode(), sk_sized); -// }, -// val => panic!("Invalid parameter set: {}", val), -// } -// } -// } -// -// #[test] -// #[allow(non_snake_case)] -// fn ML_KEM_encapDecap() { -// let contents = fs::read_to_string(TEST_DATA_PATH.to_string() + "/ML-KEM-encapDecap.txt").unwrap(); -// let test_cases = EncapDecapTestCase::parse(contents); -// -// let num_tests = test_cases.len(); -// for test_case in test_cases { -// test_case.run(); -// } -// -// println!("SUCCESS! ML-DSA-sigGen test cases passed: {}!", num_tests); -// } -// -// #[derive(Clone)] -// struct EncapDecapTestCase { -// vs_id: u32, -// algorithm: String, -// mode: String, -// revision: String, -// is_sample: bool, -// tg_id: u32, -// test_type: String, -// parameter_set: String, -// function: String, -// tc_id: u32, -// ek: String, -// dk: String, -// m: String, -// c: String, -// k: String, -// } -// -// impl EncapDecapTestCase { -// fn new() -> Self { -// Self { vs_id: 0, algorithm: String::new(), mode: String::new(), revision: String::new(), is_sample: false, tg_id: 0, test_type: String::new(), parameter_set: String::new(), function: String::new(), tc_id: 0, ek: String::new(), dk: String::new(), m: String::new(), c: String::new(), k: String::new() } -// } -// -// fn is_full(&self) -> bool { -// !self.algorithm.is_empty() -// } -// -// fn parse(data: String) -> Vec { -// let mut test_cases = Vec::::new(); -// let mut test_case = EncapDecapTestCase::new(); -// for line in data.lines() { -// let (tag, value) = match line.split_once(" = ") { -// Some(pair) => pair, -// None => { -// if test_case.is_full() { test_cases.push(test_case.clone()); } -// continue; -// } -// }; -// -// match tag { -// "vsId" => test_case.vs_id = value.parse().unwrap(), -// "algorithm" => test_case.algorithm = value.to_string(), -// "mode" => test_case.mode = value.to_string(), -// "revision" => test_case.revision = value.to_string(), -// "isSample" => test_case.is_sample = value.parse().unwrap(), -// "tgId" => test_case.tg_id = value.parse().unwrap(), -// "testType" => test_case.test_type = value.to_string(), -// "parameterSet" => test_case.parameter_set = value.to_string(), -// "function" => test_case.function = value.to_string(), -// "tcId" => test_case.tc_id = value.parse().unwrap(), -// "ek" => test_case.ek = value.to_string(), -// "dk" => test_case.dk = value.to_string(), -// "m" => test_case.m = value.to_string(), -// "c" => test_case.c = value.to_string(), -// "k" => test_case.k = value.to_string(), -// val => panic!("Invalid tag: {}", val), -// } -// } -// -// test_cases -// } -// -// fn run(&self) { -// assert_eq!(self.mode, "encapDecap"); -// -// let mut seed = [0u8; 64]; -// seed[..32].copy_from_slice(&hex::decode(&self.).unwrap()); -// -// match self.parameter_set.as_str() { -// "ML-KEM-512" => { -// match self.function.as_str() { -// "encapsulation" => { -// let pk = MLKEM512PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); -// let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); -// let (ss, ct) = MLKEM512::encaps_internal(&pk, m); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// let expected_ct = hex::decode(&self.c).unwrap(); -// -// assert_eq!(ss, expected_ss.as_slice()); -// assert_eq!(ct, expected_ct.as_slice()); -// }, -// "decapsulation" => { -// let sk = MLKEM512PrivateKey::from_bytes(&hex::decode(&self.).unwrap()).unwrap(); -// let ct = hex::decode(&self.c).unwrap(); -// let ss = MLKEM512::decaps(&sk, ct.as_slice()).unwrap(); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); -// }, -// _ => panic!("Invalid function: {}", self.function), -// }; -// }, -// "ML-KEM-768" => { -// match self.function.as_str() { -// "encapsulation" => { -// let pk = MLKEM768PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); -// let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); -// let (ss, ct) = MLKEM768::encaps_internal(&pk, m); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// let expected_ct = hex::decode(&self.c).unwrap(); -// -// assert_eq!(ss, expected_ss.as_slice()); -// assert_eq!(ct, expected_ct.as_slice()); -// }, -// "decapsulation" => { -// let sk = MLKEM768PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); -// let ct = hex::decode(&self.c).unwrap(); -// let ss = MLKEM768::decaps(&sk, ct.as_slice()).unwrap(); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); -// }, -// _ => panic!("Invalid function: {}", self.function), -// }; -// }, -// "ML-KEM-1024" => { -// match self.function.as_str() { -// "encapsulation" => { -// let pk = MLKEM1024PublicKey::from_bytes(&hex::decode(&self.ek).unwrap()).unwrap(); -// let m: [u8; 32] = hex::decode(&self.m).unwrap().try_into().unwrap(); -// let (ss, ct) = MLKEM1024::encaps_internal(&pk, m); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// let expected_ct = hex::decode(&self.c).unwrap(); -// -// assert_eq!(ss, expected_ss.as_slice()); -// assert_eq!(ct, expected_ct.as_slice()); -// }, -// "decapsulation" => { -// let sk = MLKEM1024PrivateKey::from_bytes(&hex::decode(&self.dk).unwrap()).unwrap(); -// let ct = hex::decode(&self.c).unwrap(); -// let ss = MLKEM1024::decaps(&sk, ct.as_slice()).unwrap(); -// -// let expected_ss = hex::decode(&self.k).unwrap(); -// assert_eq!(ss.ref_to_bytes(), expected_ss.as_slice()); -// }, -// _ => panic!("Invalid function: {}", self.function), -// }; -// }, -// val => panic!("Invalid parameter set: {}", val), -// } -// } -// } -// } \ No newline at end of file diff --git a/crypto/rng/Cargo.toml b/crypto/rng/Cargo.toml index 9b7bfd9..4147f1c 100644 --- a/crypto/rng/Cargo.toml +++ b/crypto/rng/Cargo.toml @@ -3,6 +3,14 @@ name = "bouncycastle-rng" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the `Vec`-returning `next_bytes` / `generate` APIs. Kept consistent with the +# (gated) `RNG::next_bytes` trait method. Note this crate is not `no_std` (it needs OS entropy via +# `getrandom`); the feature only toggles the `Vec`-returning convenience methods. On by default. +default = ["alloc"] +# Forward to every internal dep so the `alloc` gates stay consistent across the graph. +alloc = ["bouncycastle-core/alloc", "bouncycastle-sha2/alloc"] + [dependencies] bouncycastle-core.workspace = true bouncycastle-sha2.workspace = true diff --git a/crypto/rng/benches/hash_drbg_benches.rs b/crypto/rng/benches/hash_drbg_benches.rs index 0efa434..1aa6138 100644 --- a/crypto/rng/benches/hash_drbg_benches.rs +++ b/crypto/rng/benches/hash_drbg_benches.rs @@ -1,8 +1,8 @@ use bouncycastle_core::key_material::{KeyMaterial0, KeyMaterial256, KeyMaterial512, KeyType}; use bouncycastle_core::traits::{RNG, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use bouncycastle_rng::{HashDRBG_SHA256, HashDRBG_SHA512, Sp80090ADrbg}; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use std::hint::black_box; fn bench_hash_drbg_sha256(c: &mut Criterion) { diff --git a/crypto/rng/src/hash_drbg80090a.rs b/crypto/rng/src/hash_drbg80090a.rs index 4614692..f1821ee 100644 --- a/crypto/rng/src/hash_drbg80090a.rs +++ b/crypto/rng/src/hash_drbg80090a.rs @@ -6,7 +6,9 @@ use crate::Sp80090ADrbg; use bouncycastle_core::errors::{KeyMaterialError, RNGError}; -use bouncycastle_core::key_material::{KeyMaterial512, KeyType, KeyMaterialTrait}; +use bouncycastle_core::key_material::{ + KeyMaterial512, KeyMaterialTrait, KeyType, do_hazardous_operations, +}; use bouncycastle_core::traits::{Hash, HashAlgParams, RNG, SecurityStrength}; use bouncycastle_sha2::{SHA256, SHA512}; use bouncycastle_utils::min; @@ -67,6 +69,7 @@ const LARGEST_HASHER_OUTPUT_LEN: usize = 64; #[allow(private_bounds)] /// Implementation of the Hash_DRBG algorithm as specified in NIST SP 800-90Ar1. pub struct HashDRBG80090A { + _phantom: core::marker::PhantomData, // Rust is stupid. What's the point of having a generic parameter if we can't use constants inside it? // state: WorkingState, state: WorkingState, @@ -123,6 +126,7 @@ impl HashDRBG80090A { /// and relies on you to provide a strong seed.** pub fn new_unititialized() -> Self { Self { + _phantom: core::marker::PhantomData, state: WorkingState:: { v: [0u8; LARGEST_HASHER_OUTPUT_LEN], c: [0u8; LARGEST_HASHER_OUTPUT_LEN], @@ -139,20 +143,23 @@ impl HashDRBG80090A { /// Creates a new instance using the local OS RNG as a source of seed entropy. pub fn new_from_os() -> Self { let mut seed = KeyMaterial512::new(); - seed.allow_hazardous_operations(); - seed.set_key_type(KeyType::Seed).unwrap(); - match H::HASH { - SupportedHash::SHA256 => { - getrandom::fill(&mut seed.mut_ref_to_bytes().unwrap()[..32]).unwrap(); - seed.set_key_len(32).unwrap(); - seed.set_security_strength(SecurityStrength::_128bit).unwrap(); - } - SupportedHash::SHA512 => { - getrandom::fill(&mut seed.mut_ref_to_bytes().unwrap()).unwrap(); - seed.set_key_len(64).unwrap(); - seed.set_security_strength(SecurityStrength::_256bit).unwrap(); + do_hazardous_operations(&mut seed, |seed| { + seed.set_key_type(KeyType::Seed).unwrap(); + match H::HASH { + SupportedHash::SHA256 => { + getrandom::fill(&mut seed.ref_to_bytes_mut().unwrap()[..32]).unwrap(); + seed.set_key_len(32).unwrap(); + seed.set_security_strength(SecurityStrength::_128bit).unwrap(); + } + SupportedHash::SHA512 => { + getrandom::fill(&mut seed.ref_to_bytes_mut().unwrap()).unwrap(); + seed.set_key_len(64).unwrap(); + seed.set_security_strength(SecurityStrength::_256bit).unwrap(); + } } - } + Ok(()) + }) + .unwrap(); let mut rng = Self::new_unititialized(); let ss = seed.security_strength().clone(); @@ -266,7 +273,11 @@ impl Sp80090ADrbg for HashDRBG80090A { Ok(()) } - fn reseed(&mut self, seed: &impl KeyMaterialTrait, additional_input: &[u8]) -> Result<(), RNGError> { + fn reseed( + &mut self, + seed: &K, + additional_input: &[u8], + ) -> Result<(), RNGError> { // Hash_DRBG Reseed Process: // 1. seed_material = 0x01 || V || entropy_input || additional_input. // 2. seed = Hash_df (seed_material, seedlen). @@ -339,6 +350,7 @@ impl Sp80090ADrbg for HashDRBG80090A { Ok(()) } + #[cfg(feature = "alloc")] fn generate(&mut self, additional_input: &[u8], len: usize) -> Result, RNGError> { let mut out = vec![0u8; len]; self.generate_out(additional_input, &mut out)?; @@ -376,6 +388,8 @@ impl Sp80090ADrbg for HashDRBG80090A { return Err(RNGError::ReseedRequired); } + out.fill(0); + // 2. If (additional_input ≠ Null), then do // 2.1 w = Hash (0x02 || V || additional_input). // 2.2 V = (V + w) mod 2^seedlen. @@ -449,20 +463,32 @@ impl Sp80090ADrbg for HashDRBG80090A { Ok(out.len()) } - fn generate_keymaterial_out( + fn generate_keymaterial_out( &mut self, additional_input: &[u8], - out: &mut impl KeyMaterialTrait, + out: &mut K, ) -> Result { - out.allow_hazardous_operations(); - let bytes_written = self.generate_out(additional_input, out.mut_ref_to_bytes().unwrap())?; - - out.set_key_len(bytes_written)?; - out.set_key_type(KeyType::BytesFullEntropy)?; - let new_security_strength = - min(&self.admin_info.strength, &SecurityStrength::from_bits(bytes_written * 8)).clone(); - out.set_security_strength(new_security_strength)?; - out.drop_hazardous_operations(); + let mut ret: Result = Ok(0); + do_hazardous_operations(out, |out| { + let out_ref = out.ref_to_bytes_mut()?; + ret = self.generate_out(additional_input, out_ref); + Ok(()) + })?; + + let bytes_written = match ret { + Err(e) => return Err(e), + Ok(bytes_written) => bytes_written, + }; + + do_hazardous_operations(out, |out| { + out.set_key_len(bytes_written)?; + out.set_key_type(KeyType::CryptographicRandom)?; + let new_security_strength = + min(&self.admin_info.strength, &SecurityStrength::from_bits(bytes_written * 8)) + .clone(); + out.set_security_strength(new_security_strength)?; + Ok(()) + })?; Ok(bytes_written) } } @@ -475,8 +501,11 @@ impl RNG for HashDRBG80090A { // todo!() // } - fn add_seed_keymaterial(&mut self, additional_seed: impl KeyMaterialTrait) -> Result<(), RNGError> { - self.reseed(&additional_seed, "add_seed_keymaterial".as_bytes()) + fn add_seed_keymaterial( + &mut self, + additional_seed: &dyn KeyMaterialTrait, + ) -> Result<(), RNGError> { + self.reseed(additional_seed, "add_seed_keymaterial".as_bytes()) } fn next_int(&mut self) -> Result { @@ -485,15 +514,18 @@ impl RNG for HashDRBG80090A { Ok(u32::from_le_bytes(out)) } + #[cfg(feature = "alloc")] fn next_bytes(&mut self, len: usize) -> Result, RNGError> { self.generate("next_bytes".as_bytes(), len) } fn next_bytes_out(&mut self, out: &mut [u8]) -> Result { + out.fill(0); + self.generate_out("next_bytes_out".as_bytes(), out) } - fn fill_keymaterial_out(&mut self, out: &mut impl KeyMaterialTrait) -> Result { + fn fill_keymaterial_out(&mut self, out: &mut dyn KeyMaterialTrait) -> Result { self.generate_keymaterial_out("fill_keymaterial".as_bytes(), out) } @@ -521,6 +553,8 @@ fn hash_df( panic!("hash_df can't produce that much output!") } + out.fill(0); + // out is "temp" in SP 800-90Ar1 let no_of_bits_to_return: u32 = (out.len() * 8) as u32; let len = u32::div_ceil(out.len() as u32, H::OUTPUT_LEN as u32); @@ -572,7 +606,19 @@ fn test_hash_df() { assert_ne!(out, [0u8; 100]); // repeatability test // println!("out: {:?}", out); - assert_eq!(out, [150u8, 177u8, 87u8, 145u8, 138u8, 4u8, 164u8, 14u8, 162u8, 43u8, 159u8, 152u8, 121u8, 117u8, 6u8, 18u8, 253u8, 84u8, 41u8, 64u8, 40u8, 209u8, 16u8, 176u8, 106u8, 115u8, 172u8, 193u8, 246u8, 228u8, 208u8, 79u8, 37u8, 31u8, 134u8, 141u8, 200u8, 7u8, 42u8, 199u8, 229u8, 236u8, 236u8, 186u8, 28u8, 87u8, 200u8, 14u8, 127u8, 36u8, 132u8, 23u8, 36u8, 150u8, 23u8, 215u8, 247u8, 121u8, 175u8, 82u8, 99u8, 187u8, 235u8, 25u8, 213u8, 18u8, 106u8, 22u8, 4u8, 99u8, 1u8, 184u8, 211u8, 160u8, 177u8, 67u8, 78u8, 181u8, 69u8, 51u8, 117u8, 2u8, 72u8, 36u8, 134u8, 72u8, 2u8, 9u8, 105u8, 149u8, 136u8, 35u8, 81u8, 114u8, 142u8, 80u8, 94u8, 42u8, 85u8, 155]); + assert_eq!( + out, + [ + 150u8, 177u8, 87u8, 145u8, 138u8, 4u8, 164u8, 14u8, 162u8, 43u8, 159u8, 152u8, 121u8, + 117u8, 6u8, 18u8, 253u8, 84u8, 41u8, 64u8, 40u8, 209u8, 16u8, 176u8, 106u8, 115u8, + 172u8, 193u8, 246u8, 228u8, 208u8, 79u8, 37u8, 31u8, 134u8, 141u8, 200u8, 7u8, 42u8, + 199u8, 229u8, 236u8, 236u8, 186u8, 28u8, 87u8, 200u8, 14u8, 127u8, 36u8, 132u8, 23u8, + 36u8, 150u8, 23u8, 215u8, 247u8, 121u8, 175u8, 82u8, 99u8, 187u8, 235u8, 25u8, 213u8, + 18u8, 106u8, 22u8, 4u8, 99u8, 1u8, 184u8, 211u8, 160u8, 177u8, 67u8, 78u8, 181u8, 69u8, + 51u8, 117u8, 2u8, 72u8, 36u8, 134u8, 72u8, 2u8, 9u8, 105u8, 149u8, 136u8, 35u8, 81u8, + 114u8, 142u8, 80u8, 94u8, 42u8, 85u8, 155 + ] + ); // Test success with out.len() at the maximum allowed for SHA256 (255 * 32 = 8160) let mut out_max_sha256 = vec![0u8; 255 * 32]; @@ -614,6 +660,8 @@ fn hashgen(v: &[u8], out: &mut [u8]) { // 6. Return (returned_bits). // 1. m = ceil(requested_no_of_bits / outlen) + out.fill(0); + let m = u32::div_ceil(out.len() as u32, H::OUTPUT_LEN as u32); // requested_no_of_bits = out.len() diff --git a/crypto/rng/src/lib.rs b/crypto/rng/src/lib.rs index dbe62fb..9ed81ec 100644 --- a/crypto/rng/src/lib.rs +++ b/crypto/rng/src/lib.rs @@ -28,8 +28,6 @@ //! cryptographic application. #![forbid(unsafe_code)] -#![allow(incomplete_features)] // Need this because generic_const_exprs is currently experimental. -#![feature(generic_const_exprs)] use crate::hash_drbg80090a::{ HashDRBG80090A, HashDRBG80090AParams_SHA256, HashDRBG80090AParams_SHA512, @@ -94,9 +92,9 @@ pub trait Sp80090ADrbg { /// Reseeds the DRBG with the provided seed. /// TODO: this needs to be thought out to take some sort of EntropySource object that'll work well with DRBGs that require frequent reseeding. - fn reseed( + fn reseed( &mut self, - seed: &impl KeyMaterialTrait, + seed: &K, additional_input: &[u8], ) -> Result<(), RNGError>; @@ -115,6 +113,9 @@ pub trait Sp80090ADrbg { /// not pass FIPS certification. /// /// Throws a [RNGError::InsufficientSeedEntropy] if `len` exceeds [SecurityStrength]. + /// + /// `no_std` / no-alloc alternative: [Sp80090ADrbg::generate_out]. + #[cfg(feature = "alloc")] fn generate(&mut self, additional_input: &[u8], len: usize) -> Result, RNGError>; /// As per [Sp80090ADrbg::generate], but writes to the provided output slice. @@ -127,10 +128,10 @@ pub trait Sp80090ADrbg { /// The output [KeyMaterialTrait] is filled to capacity. /// Throws a [RNGError::InsufficientSeedEntropy] if the capacity of the output KeyMaterial exceeds [SecurityStrength]. /// Retruns the number of bits output. - fn generate_keymaterial_out( + fn generate_keymaterial_out( &mut self, additional_input: &[u8], - out: &mut impl KeyMaterialTrait, + out: &mut K, ) -> Result; // TODO -- implement FIPS health tests diff --git a/crypto/rng/tests/hash_drbg80090a_tests.rs b/crypto/rng/tests/hash_drbg80090a_tests.rs index 8ab4408..c03f71b 100644 --- a/crypto/rng/tests/hash_drbg80090a_tests.rs +++ b/crypto/rng/tests/hash_drbg80090a_tests.rs @@ -1,7 +1,9 @@ #[cfg(test)] mod tests { use bouncycastle_core::errors::{KeyMaterialError, RNGError}; - use bouncycastle_core::key_material::{KeyMaterial0, KeyMaterial256, KeyMaterial, KeyType, KeyMaterialTrait}; + use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial0, KeyMaterial256, KeyMaterialTrait, KeyType, + }; use bouncycastle_core::traits::{RNG, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; use bouncycastle_rng::Sp80090ADrbg; @@ -284,7 +286,8 @@ mod tests { let mut out = KeyMaterial256::new(); match rng.generate_keymaterial_out(&[], &mut out) { Err(RNGError::Uninitialized) => { /*good*/ } - _ => panic!("Expected Uninitialized error"), + Err(e) => panic!("{:?}", e), + Ok(_) => panic!("Expected Uninitialized error"), } // Skipping tests for max lengths of seeds and personalization strings, because they're in the gigabyte range and that'll blow up the test suite. @@ -299,7 +302,7 @@ mod tests { KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::Seed).unwrap(); /* test add_seed_keymaterial */ - rng.add_seed_keymaterial(seed).unwrap(); + rng.add_seed_keymaterial(&seed).unwrap(); /* test next_int */ let out1: u32 = rng.next_int().unwrap(); diff --git a/crypto/sha2/Cargo.toml b/crypto/sha2/Cargo.toml index a620a6a..b1de7f4 100644 --- a/crypto/sha2/Cargo.toml +++ b/crypto/sha2/Cargo.toml @@ -3,6 +3,12 @@ name = "bouncycastle-sha2" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the `Vec`-returning convenience APIs (and turns on the same feature in +# bouncycastle-core). On by default; disable with `default-features = false` for `no_std` use. +default = ["alloc"] +alloc = ["bouncycastle-core/alloc"] + [dependencies] bouncycastle-core.workspace = true bouncycastle-utils.workspace = true diff --git a/crypto/sha2/src/lib.rs b/crypto/sha2/src/lib.rs index b3582fc..36c095b 100644 --- a/crypto/sha2/src/lib.rs +++ b/crypto/sha2/src/lib.rs @@ -18,8 +18,8 @@ //! for example if input is received in chunks and not all available at the same time: //! //! ``` -//! use bouncycastle_core::traits::Hash; //! use bouncycastle_sha2 as sha2; +//! use bouncycastle_core::traits::Hash; //! //! let data: &[u8] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F //! \x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F @@ -33,17 +33,59 @@ //! //! let output: Vec = sha2.do_final(); //! ``` +//! +//! # Suspending and resuming execution +//! +//! When hashing a large message, it can be advantageous to be able to suspend the operation +//! to a cache and resume it later; for example if waiting for the message to stream over a slow network +//! connection. +//! +//! For this reason, all SHA2 algorithms impl [Suspendable]. +//! +//! ```rust +//! use bouncycastle_sha2 as sha2; +//! use bouncycastle_core::traits::{Hash, Suspendable}; +//! +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let mut sha2 = sha2::SHA256::new(); +//! sha2.do_update(msg_part1); +//! +//! // suspend the in-progress extract while "waiting" for the second part of the message. +//! let serialized_state = sha2.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! // ... later, possibly on another host: resume from the serialized state. +//! let mut sha2_resumed = sha2::SHA256::from_suspended(serialized_state).unwrap(); +//! sha2_resumed.do_update(msg_part2); +//! let h: Vec = sha2_resumed.do_final(); +//! ``` +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] #![allow(private_bounds)] +// The `Vec`-returning convenience APIs (`hash`, `do_final`, ...) live behind the default-on `alloc` +// feature. `no_std` users without an allocator should use the `*_out(&mut [u8])` / `*_array::()` +// APIs from the [bouncycastle_core::traits::Hash] trait instead. +#[cfg(feature = "alloc")] +extern crate alloc; + mod sha256; mod sha512; pub use self::sha256::SHA256Internal; -pub use self::sha512::Sha512Internal; +pub use self::sha512::SHA512Internal; use bouncycastle_core::traits::{Algorithm, HashAlgParams, SecurityStrength}; +/*** Imports needed for docs ***/ +#[allow(unused_imports)] +use bouncycastle_core::traits::Suspendable; + /*** String constants ***/ pub const SHA224_NAME: &str = "SHA224"; pub const SHA256_NAME: &str = "SHA256"; @@ -53,21 +95,18 @@ pub const SHA512_NAME: &str = "SHA512"; /*** pub types ***/ pub type SHA224 = SHA256Internal; pub type SHA256 = SHA256Internal; -pub type SHA384 = Sha512Internal; -pub type SHA512 = Sha512Internal; +pub type SHA384 = SHA512Internal; +pub type SHA512 = SHA512Internal; /*** Param traits ***/ trait SHA2Params: HashAlgParams {} -impl Algorithm for SHA224 { - const ALG_NAME: &'static str = SHA224_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; -} impl HashAlgParams for SHA224 { const OUTPUT_LEN: usize = 28; const BLOCK_LEN: usize = 64; } +#[derive(Clone)] pub struct SHA224Params; impl Algorithm for SHA224Params { const ALG_NAME: &'static str = SHA224_NAME; @@ -79,14 +118,11 @@ impl HashAlgParams for SHA224Params { } impl SHA2Params for SHA224Params {} -impl Algorithm for SHA256 { - const ALG_NAME: &'static str = SHA256_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; -} impl HashAlgParams for SHA256 { const OUTPUT_LEN: usize = 32; const BLOCK_LEN: usize = 64; } +#[derive(Clone)] pub struct SHA256Params; impl Algorithm for SHA256Params { const ALG_NAME: &'static str = SHA256_NAME; @@ -98,14 +134,11 @@ impl HashAlgParams for SHA256Params { } impl SHA2Params for SHA256Params {} -impl Algorithm for SHA384 { - const ALG_NAME: &'static str = SHA384_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; -} impl HashAlgParams for SHA384 { const OUTPUT_LEN: usize = 48; const BLOCK_LEN: usize = 128; } +#[derive(Clone)] pub struct SHA384Params; impl Algorithm for SHA384Params { const ALG_NAME: &'static str = SHA384_NAME; @@ -117,11 +150,8 @@ impl HashAlgParams for SHA384Params { } impl SHA2Params for SHA384Params {} +#[derive(Clone)] pub struct SHA512Params; -impl Algorithm for SHA512 { - const ALG_NAME: &'static str = SHA512_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; -} impl HashAlgParams for SHA512 { const OUTPUT_LEN: usize = 64; const BLOCK_LEN: usize = 128; @@ -135,3 +165,6 @@ impl HashAlgParams for SHA512Params { const BLOCK_LEN: usize = 128; } impl SHA2Params for SHA512Params {} + +pub use sha256::SUSPENDED_SHA256_STATE_LEN; +pub use sha512::SUSPENDED_SHA512_STATE_LEN; diff --git a/crypto/sha2/src/sha256.rs b/crypto/sha2/src/sha256.rs index 10d45e1..eec1db8 100644 --- a/crypto/sha2/src/sha256.rs +++ b/crypto/sha2/src/sha256.rs @@ -1,8 +1,12 @@ -use crate::{SHA2Params}; -use core::slice; -use bouncycastle_core::errors::HashError; -use bouncycastle_core::traits::{Hash, SecurityStrength}; +use crate::SHA2Params; +use bouncycastle_core::errors::{HashError, SuspendableError}; +use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; +use bouncycastle_core::traits::{Algorithm, Hash, SecurityStrength, Suspendable}; use bouncycastle_utils::min; +use core::slice; + +#[cfg(feature = "alloc")] +use alloc::{vec, vec::Vec}; const SHA256_K: [u32; 64] = [ 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, @@ -45,11 +49,9 @@ fn theta1(x: u32) -> u32 { x.rotate_right(17) ^ x.rotate_right(19) ^ (x >> 10) } -// todo -- cleanup -// #[derive(Clone, Copy)] #[derive(Clone)] pub(crate) struct Sha256State { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [u32; 8], } @@ -63,14 +65,14 @@ impl Sha256State { pub(crate) fn new() -> Self { match PARAMS::OUTPUT_LEN * 8 { 224 => Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [ 0xC1059ED8, 0x367CD507, 0x3070DD17, 0xF70E5939, 0xFFC00B31, 0x68581511, 0x64F98FA7, 0xBEFA4FA4, ], }, 256 => Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [ 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19, @@ -145,11 +147,9 @@ impl Sha256State { } } -// todo -- cleanup -// #[derive(Clone, Copy)] #[derive(Clone)] pub struct SHA256Internal { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, state: Sha256State, byte_count: u64, x_buf: [u8; 64], @@ -160,12 +160,13 @@ pub struct SHA256Internal { impl Drop for SHA256Internal { fn drop(&mut self) { self.x_buf.fill(0); - }} + } +} impl SHA256Internal { pub fn new() -> Self { Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, state: Sha256State::::new(), byte_count: 0, x_buf: [0; 64], @@ -180,6 +181,11 @@ impl Default for SHA256Internal { } } +impl Algorithm for SHA256Internal { + const ALG_NAME: &'static str = PARAMS::ALG_NAME; + const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH; +} + impl Hash for SHA256Internal { /// As per FIPS 180-4 Figure 1 fn block_bitlen(&self) -> usize { @@ -190,6 +196,7 @@ impl Hash for SHA256Internal { PARAMS::OUTPUT_LEN } + #[cfg(feature = "alloc")] fn hash(self, data: &[u8]) -> Vec { let mut output = vec![0u8; PARAMS::OUTPUT_LEN]; self.hash_out(data, &mut output); @@ -197,6 +204,8 @@ impl Hash for SHA256Internal { } fn hash_out(mut self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + self.do_update(data); self.do_final_out(output) } @@ -234,6 +243,7 @@ impl Hash for SHA256Internal { self.x_buf_off = remaining; } + #[cfg(feature = "alloc")] fn do_final(self) -> Vec { let mut output = vec![0u8; PARAMS::OUTPUT_LEN]; self.do_final_out(&mut output); @@ -241,6 +251,8 @@ impl Hash for SHA256Internal { } fn do_final_out(mut self, output: &mut [u8]) -> usize { + output.fill(0); + let n = *min(&output.len(), &PARAMS::OUTPUT_LEN); let bit_len: u64 = self.byte_count << 3; @@ -275,6 +287,7 @@ impl Hash for SHA256Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: /// TODO: Could implement if there is demand. + #[cfg(feature = "alloc")] #[allow(unused)] fn do_final_partial_bits( self, @@ -301,3 +314,76 @@ impl Hash for SHA256Internal { SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2) } } + +/// Length in bytes of the serialized state of SHA224 and SHA256. +pub const SUSPENDED_SHA256_STATE_LEN: usize = 108; + +impl Suspendable for SHA256Internal { + fn suspend(self) -> [u8; SUSPENDED_SHA256_STATE_LEN] { + debug_assert_eq!(SUSPENDED_SHA256_STATE_LEN, 108); + + let mut out_to_return = [0u8; SUSPENDED_SHA256_STATE_LEN]; + + // insert the version tag + let out: &mut [u8; 105] = add_lib_ver(&mut out_to_return).try_into().unwrap(); + + // state.h: [u32; 8] + // 4 * 8 = 32 + for i in 0..8 { + out[i * 4..(i * 4) + 4].copy_from_slice(&self.state.h[i].to_le_bytes()); + } + + // byte_count: u64 + out[32..40].copy_from_slice(&self.byte_count.to_le_bytes()); + + // x_buf: [u8; 64] + out[40..104].copy_from_slice(&self.x_buf); + + // x_buf_off: usize + // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 64 + debug_assert!(self.x_buf_off < 64); + out[104] = self.x_buf_off as u8; + + out_to_return + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA256_STATE_LEN], + ) -> Result { + debug_assert_eq!(SUSPENDED_SHA256_STATE_LEN, 108); + + // check the version tag + // At the moment, we have no not_before version to specify. + let input: &[u8; 105] = check_lib_ver(&serialized_state, None)?.try_into().unwrap(); + + // state.h: [u32; 8] + // 4 * 8 = 32 + let mut h = [0u32; 8]; + for i in 0..8 { + h[i] = u32::from_le_bytes(input[i * 4..(i * 4) + 4].try_into().unwrap()); + } + + // byte_count: u64 + let byte_count: u64 = u64::from_le_bytes(input[32..40].try_into().unwrap()); + + // x_buf: [u8; 64] + let x_buf: [u8; 64] = input[40..104].try_into().unwrap(); + + // x_buf_off: usize + // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 64 + let x_buf_off: usize = input[104] as usize; + if x_buf_off >= 64 { + return Err(SuspendableError::InvalidData); + } + + // Construct the object + let state = Sha256State { _params: core::marker::PhantomData, h }; + Ok(SHA256Internal { + _params: core::marker::PhantomData, + state, + byte_count, + x_buf, + x_buf_off, + }) + } +} diff --git a/crypto/sha2/src/sha512.rs b/crypto/sha2/src/sha512.rs index 66d5265..10fe255 100644 --- a/crypto/sha2/src/sha512.rs +++ b/crypto/sha2/src/sha512.rs @@ -1,8 +1,12 @@ use crate::SHA2Params; -use core::slice; -use bouncycastle_core::errors::HashError; -use bouncycastle_core::traits::{Hash, SecurityStrength}; +use bouncycastle_core::errors::{HashError, SuspendableError}; +use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; +use bouncycastle_core::traits::{Algorithm, Hash, SecurityStrength, Suspendable}; use bouncycastle_utils::min; +use core::slice; + +#[cfg(feature = "alloc")] +use alloc::{vec, vec::Vec}; const SHA512_K: [u64; 80] = [ 0x428A2F98D728AE22, 0x7137449123EF65CD, 0xB5C0FBCFEC4D3B2F, 0xE9B5DBA58189DBBC, @@ -61,7 +65,7 @@ fn theta1(x: u64) -> u64 { // #[derive(Clone, Copy)] #[derive(Clone)] pub(crate) struct Sha512State { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [u64; 8], } @@ -75,14 +79,14 @@ impl Sha512State { pub(crate) fn new() -> Self { match PARAMS::OUTPUT_LEN * 8 { 384 => Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [ 0xCBBB9D5DC1059ED8, 0x629A292A367CD507, 0x9159015A3070DD17, 0x152FECD8F70E5939, 0x67332667FFC00B31, 0x8EB44A8768581511, 0xDB0C2E0D64F98FA7, 0x47B5481DBEFA4FA4, ], }, 512 => Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, h: [ 0x6A09E667F3BCC908, 0xBB67AE8584CAA73B, 0x3C6EF372FE94F82B, 0xA54FF53A5F1D36F1, 0x510E527FADE682D1, 0x9B05688C2B3E6C1F, 0x1F83D9ABFB41BD6B, 0x5BE0CD19137E2179, @@ -160,24 +164,24 @@ impl Sha512State { // todo -- cleanup // #[derive(Clone, Copy)] #[derive(Clone)] -pub struct Sha512Internal { - _params: std::marker::PhantomData, +pub struct SHA512Internal { + _params: core::marker::PhantomData, state: Sha512State, byte_count: u64, // NOTE We only support 2^67 bits, not the full 2^128 x_buf: [u8; 128], x_buf_off: usize, } -impl Drop for Sha512Internal { +impl Drop for SHA512Internal { fn drop(&mut self) { self.x_buf.fill(0); } } -impl Sha512Internal { +impl SHA512Internal { pub fn new() -> Self { Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, state: Sha512State::::new(), byte_count: 0, x_buf: [0; 128], @@ -186,13 +190,18 @@ impl Sha512Internal { } } -impl Default for Sha512Internal { +impl Default for SHA512Internal { fn default() -> Self { Self::new() } } -impl Hash for Sha512Internal { +impl Algorithm for SHA512Internal { + const ALG_NAME: &'static str = PARAMS::ALG_NAME; + const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH; +} + +impl Hash for SHA512Internal { /// As per FIPS 180-4 Figure 1 fn block_bitlen(&self) -> usize { 1024 @@ -202,6 +211,7 @@ impl Hash for Sha512Internal { PARAMS::OUTPUT_LEN } + #[cfg(feature = "alloc")] fn hash(self, data: &[u8]) -> Vec { let mut output = vec![0u8; self.output_len()]; self.hash_out(data, &mut output); @@ -209,6 +219,8 @@ impl Hash for Sha512Internal { } fn hash_out(mut self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + self.do_update(data); self.do_final_out(output) } @@ -245,6 +257,7 @@ impl Hash for Sha512Internal { self.x_buf_off = remaining; } + #[cfg(feature = "alloc")] fn do_final(self) -> Vec { let mut output = vec![0u8; PARAMS::OUTPUT_LEN]; self.do_final_out(&mut output); @@ -252,6 +265,8 @@ impl Hash for Sha512Internal { } fn do_final_out(mut self, output: &mut [u8]) -> usize { + output.fill(0); + let n = *min(&output.len(), &PARAMS::OUTPUT_LEN); let bit_len_hi: u64 = self.byte_count >> 61; @@ -287,6 +302,7 @@ impl Hash for Sha512Internal { /// TODO: This is defined in FIPS 180-4 s. 5.1.2 /// TODO: /// TODO: Could implement if there is demand. + #[cfg(feature = "alloc")] #[allow(unused)] fn do_final_partial_bits( self, @@ -313,3 +329,74 @@ impl Hash for Sha512Internal { SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2) } } + +/// Length in bytes of the serialized state of SHA384 and SHA512. +pub const SUSPENDED_SHA512_STATE_LEN: usize = 204; + +impl Suspendable for SHA512Internal { + fn suspend(self) -> [u8; SUSPENDED_SHA512_STATE_LEN] { + debug_assert_eq!(SUSPENDED_SHA512_STATE_LEN, 204); + + let mut out_to_return = [0u8; SUSPENDED_SHA512_STATE_LEN]; + + // insert the version tag + let out: &mut [u8; 201] = add_lib_ver(&mut out_to_return).try_into().unwrap(); + + // state.h: [u64; 8] + // 8 * 8 = 64 + for i in 0..8 { + out[i * 8..(i * 8) + 8].copy_from_slice(&self.state.h[i].to_le_bytes()); + } + + // byte_count: u64 + out[64..72].copy_from_slice(&self.byte_count.to_le_bytes()); + + // x_buf: [u8; 128] + out[72..200].copy_from_slice(&self.x_buf); + + // x_buf_off: usize + // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 128 + debug_assert!(self.x_buf_off < 128); + out[200] = self.x_buf_off as u8; + + out_to_return + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA512_STATE_LEN], + ) -> Result { + // check the version tag + // At the moment, we have no not_before version to specify. + let input: &[u8; 201] = check_lib_ver(&serialized_state, None)?.try_into().unwrap(); + + // state.h: [u64; 8] + // 8 * 8 = 64 + let mut h = [0u64; 8]; + for i in 0..8 { + h[i] = u64::from_le_bytes(input[i * 8..(i * 8) + 8].try_into().unwrap()); + } + + // byte_count: u64 + let byte_count: u64 = u64::from_le_bytes(input[64..72].try_into().unwrap()); + + // x_buf: [u8; 128] + let x_buf: [u8; 128] = input[72..200].try_into().unwrap(); + + // x_buf_off: usize + // in general, a usize should be serialized into a u64, but in this case, it can't ever be larger than 128 + let x_buf_off: usize = input[200] as usize; + if x_buf_off >= 128 { + return Err(SuspendableError::InvalidData); + } + + // Construct the object + let state = Sha512State { _params: core::marker::PhantomData, h }; + Ok(SHA512Internal { + _params: core::marker::PhantomData, + state, + byte_count, + x_buf, + x_buf_off, + }) + } +} diff --git a/crypto/sha2/tests/sha2_tests.rs b/crypto/sha2/tests/sha2_tests.rs index 7d3e239..38b4e2f 100644 --- a/crypto/sha2/tests/sha2_tests.rs +++ b/crypto/sha2/tests/sha2_tests.rs @@ -1,5 +1,6 @@ #[cfg(test)] mod sha2_tests { + use bouncycastle_core::errors::SuspendableError; use bouncycastle_core::traits::{Algorithm, Hash, HashAlgParams, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; use bouncycastle_core_test_framework::hash::TestFrameworkHash; @@ -89,4 +90,68 @@ mod sha2_tests { assert_eq!(SHA384::default().max_security_strength(), SecurityStrength::_192bit); assert_eq!(SHA512::default().max_security_strength(), SecurityStrength::_256bit); } + + #[test] + fn suspendable_state() { + use bouncycastle_core::traits::Suspendable; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableState; + + let str = "Colorless green ideas sleep furiously"; + + // SHA256 + let mut sha256 = SHA256::new(); + sha256.do_update(str.as_bytes()); + + // do the default tests + let test_framework = TestFrameworkSuspendableState::new(); + test_framework.test(&sha256); + + // now let's serialize the in-progress state + let serialized_state = sha256.clone().suspend(); + assert_eq!(serialized_state.len(), SUSPENDED_SHA256_STATE_LEN); + + // finish the hash + let output = sha256.do_final(); + + // then load from state and finish the hash and make sure we get the same thing + let sha2_from_state = SHA256::from_suspended(serialized_state).unwrap(); + let output2 = sha2_from_state.do_final(); + assert_eq!(output, output2); + + // also, give it a busted x_buf_off, just to satisfy mutants that that's been tested + let mut busted_state = serialized_state.clone(); + busted_state[3 + 104] = 65; + match SHA256::from_suspended(busted_state) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error"), + } + + // SHA512 + let mut sha512 = SHA512::new(); + sha512.do_update(str.as_bytes()); + + // do the default tests + let test_framework = TestFrameworkSuspendableState::new(); + test_framework.test(&sha512); + + // now let's serialize the in-progress state + let serialized_state = sha512.clone().suspend(); + assert_eq!(serialized_state.len(), SUSPENDED_SHA512_STATE_LEN); + + // finish the hash + let output = sha512.do_final(); + + // then load from state and finish the hash and make sure we get the same thing + let sha2_from_state = SHA512::from_suspended(serialized_state).unwrap(); + let output2 = sha2_from_state.do_final(); + assert_eq!(output, output2); + + // also, give it a busted x_buf_off, just to satisfy mutants that that's been tested + let mut busted_state = serialized_state.clone(); + busted_state[3 + 200] = 129; + match SHA512::from_suspended(busted_state) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error"), + } + } } diff --git a/crypto/sha3/Cargo.toml b/crypto/sha3/Cargo.toml index df5b28a..1de1ad7 100644 --- a/crypto/sha3/Cargo.toml +++ b/crypto/sha3/Cargo.toml @@ -3,6 +3,12 @@ name = "bouncycastle-sha3" version = "0.1.1" edition.workspace = true +[features] +# `alloc` enables the `Vec`/`Box`-returning convenience APIs (and turns on the same feature in +# bouncycastle-core). On by default; disable with `default-features = false` for `no_std` use. +default = ["alloc"] +alloc = ["bouncycastle-core/alloc"] + [dependencies] bouncycastle-core.workspace = true bouncycastle-utils.workspace = true diff --git a/crypto/sha3/benches/sha3_benches.rs b/crypto/sha3/benches/sha3_benches.rs index a0d0577..e2006a6 100644 --- a/crypto/sha3/benches/sha3_benches.rs +++ b/crypto/sha3/benches/sha3_benches.rs @@ -1,5 +1,5 @@ -use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use bouncycastle_rng as rng; +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; use std::hint::black_box; use bouncycastle_core::traits::{Hash, RNG, XOF}; diff --git a/crypto/sha3/src/keccak.rs b/crypto/sha3/src/keccak.rs index 55ee617..fd63544 100644 --- a/crypto/sha3/src/keccak.rs +++ b/crypto/sha3/src/keccak.rs @@ -1,4 +1,6 @@ -use bouncycastle_core::errors::HashError; +use bouncycastle_core::errors::{HashError, SuspendableError}; +use bouncycastle_core::key_material::KeyType; +use bouncycastle_core::traits::SecurityStrength; const KECCAK_ROUND_CONSTANTS: [u64; 24] = [ 0x0000000000000001, 0x0000000000008082, 0x800000000000808A, 0x8000000080008000, @@ -187,13 +189,14 @@ impl Drop for KeccakState { } } +// This is pub(crate) so that the SerializableState handlers can unpack it. #[derive(Clone)] -pub(super) struct KeccakDigest { +pub(crate) struct KeccakDigest { state: KeccakState, pub data_queue: [u8; 192], rate: usize, pub bits_in_queue: usize, - pub(super) squeezing: bool, + pub squeezing: bool, } #[derive(Clone)] @@ -210,11 +213,6 @@ impl KeccakDigest { pub(super) fn new(size: KeccakSize) -> Self { let rate = 1600 - ((size as usize) << 1); - // todo I think this check is not needed since the fixed set of allowed sizes can't yield an invalid rate, but I'll leave this here for now. - // if rate == 0 || rate >= 1600 || (rate & 63) != 0 { - // return Err(HashError::InvalidLength("invalid rate value")); - // } - Self { state: KeccakState::new(rate), data_queue: [0u8; 192], @@ -271,6 +269,8 @@ impl KeccakDigest { /// Panics if the output buffer is too small. /// Returns the number of bytes written. pub(super) fn squeeze(&mut self, out: &mut [u8]) -> usize { + out.fill(0); + if !self.squeezing { self.pad_and_switch_to_squeezing_phase(); } @@ -341,6 +341,149 @@ impl KeccakDigest { } } +/*** State serialization ***/ +// +// The SHA3 and SHAKE public objects have identical state: a [KeccakDigest] plus three pieces of +// KDF metadata. The helpers below serialize that shared state so the `SerializableState` impls in +// `sha3.rs` and `shake.rs` are just thin wrappers that add/check the library version header. + +/// Number of bytes needed to serialize a [KeccakDigest]'s mutable state. +/// +/// The `rate` is intentionally NOT serialized: it is fully determined by the SHA3/SHAKE variant and +/// is re-supplied at deserialization time (see [KeccakDigest::from_serialized_state]). +/// +/// Layout (all integers little-endian): +/// [0 .. 200) state.buf [u64; 25] +/// [200 .. 392) data_queue [u8; 192] +/// [392 .. 400) bits_in_queue usize serialized as u64 +/// [400 .. 401) squeezing bool (0 or 1) +const KECCAK_SERIALIZED_LEN: usize = 200 + 192 + 8 + 1; + +/// Number of bytes needed to serialize the shared SHA3-family state (a variant tag, a [KeccakDigest], +/// plus the three KDF metadata fields), excluding the library version header. +/// +/// The leading variant tag distinguishes every SHA3/SHAKE variant — crucially including same-rate +/// pairs such as SHA3-256 and SHAKE256 — so a serialized state can never be deserialized into a +/// different algorithm (which would silently apply the wrong domain separation). +/// +/// Layout (all integers little-endian): +/// [0 .. 1) variant tag (see `STATE_TAG` on the param traits) +/// [1 .. 1 + KECCAK_SERIALIZED_LEN) keccak digest state +/// [.. + 1) kdf_key_type (1 byte enum tag) +/// [.. + 1) kdf_security_strength (1 byte enum tag) +/// [.. + 8) kdf_entropy usize serialized as u64 +pub(crate) const SHA3_FAMILY_STATE_LEN: usize = 1 + KECCAK_SERIALIZED_LEN + 10; + +/// Length in bytes of the serialized state of a SHA3 or SHAKE instance. +pub const SUSPENDED_SHA3_STATE_LEN: usize = 3 + SHA3_FAMILY_STATE_LEN; + +impl KeccakDigest { + /// Serializes this digest's mutable state into `out`. The `rate` is deliberately omitted; see + /// [KECCAK_SERIALIZED_LEN]. + fn serialize_state(&self, out: &mut [u8; KECCAK_SERIALIZED_LEN]) { + // state.buf: [u64; 25] + for i in 0..25 { + out[i * 8..(i * 8) + 8].copy_from_slice(&self.state.buf[i].to_le_bytes()); + } + + // data_queue: [u8; 192] + out[200..392].copy_from_slice(&self.data_queue); + + // bits_in_queue: usize + out[392..400].copy_from_slice(&(self.bits_in_queue as u64).to_le_bytes()); + + // squeezing: bool + out[400] = self.squeezing as u8; + } + + /// Reconstructs a [KeccakDigest] from a state produced by [KeccakDigest::serialize_state]. + /// + /// `rate` is supplied by the caller (derived from its algorithm parameters) rather than read + /// from the serialized bytes, since the rate is fully determined by the SHA3/SHAKE variant. The + /// caller is responsible for having already verified the variant tag so that this `rate` is the + /// correct one for the serialized state. + fn from_serialized_state( + input: &[u8; KECCAK_SERIALIZED_LEN], + rate: usize, + ) -> Result { + // state.buf: [u64; 25] + let mut buf = [0u64; 25]; + for i in 0..25 { + buf[i] = u64::from_le_bytes(input[i * 8..(i * 8) + 8].try_into().unwrap()); + } + + // data_queue: [u8; 192] + let data_queue: [u8; 192] = input[200..392].try_into().unwrap(); + + // bits_in_queue: usize. It can never legitimately exceed the rate (which is at most 168 + // bytes, well within data_queue's 192-byte capacity). + let bits_in_queue = u64::from_le_bytes(input[392..400].try_into().unwrap()) as usize; + if bits_in_queue > rate { + return Err(SuspendableError::InvalidData); + } + + // squeezing: bool + let squeezing = match input[400] { + 0 => false, + 1 => true, + _ => return Err(SuspendableError::InvalidData), + }; + + Ok(Self { state: KeccakState { buf, rate }, data_queue, rate, bits_in_queue, squeezing }) + } +} + +/// Serializes the state shared by all SHA3-family objects (the `variant_tag`, a [KeccakDigest], plus +/// the three KDF metadata fields) into `out`. See [SHA3_FAMILY_STATE_LEN] for the layout. +pub(crate) fn serialize_sha3_family_state( + out: &mut [u8; SHA3_FAMILY_STATE_LEN], + variant_tag: u8, + keccak: &KeccakDigest, + kdf_key_type: KeyType, + kdf_security_strength: SecurityStrength, + kdf_entropy: usize, +) { + out[0] = variant_tag; + + let keccak_out: &mut [u8; KECCAK_SERIALIZED_LEN] = + (&mut out[1..1 + KECCAK_SERIALIZED_LEN]).try_into().unwrap(); + keccak.serialize_state(keccak_out); + + out[1 + KECCAK_SERIALIZED_LEN] = kdf_key_type as u8; + out[1 + KECCAK_SERIALIZED_LEN + 1] = kdf_security_strength as u8; + out[1 + KECCAK_SERIALIZED_LEN + 2..1 + KECCAK_SERIALIZED_LEN + 10] + .copy_from_slice(&(kdf_entropy as u64).to_le_bytes()); +} + +/// Reconstructs the shared SHA3-family state from a buffer produced by [serialize_sha3_family_state]. +/// +/// `expected_variant_tag` and `rate` are both derived from the caller's algorithm parameters. The +/// tag is checked against the serialized one first: this is what prevents a state from one variant +/// being loaded into another (e.g. SHA3-256 vs SHAKE256, which share a rate but differ in domain +/// separation). Only once the tag matches is `rate` guaranteed to be the correct one to rebuild with. +pub(crate) fn deserialize_sha3_family_state( + input: &[u8; SHA3_FAMILY_STATE_LEN], + expected_variant_tag: u8, + rate: usize, +) -> Result<(KeccakDigest, KeyType, SecurityStrength, usize), SuspendableError> { + if input[0] != expected_variant_tag { + return Err(SuspendableError::InvalidData); + } + + let keccak_in: &[u8; KECCAK_SERIALIZED_LEN] = + input[1..1 + KECCAK_SERIALIZED_LEN].try_into().unwrap(); + let keccak = KeccakDigest::from_serialized_state(keccak_in, rate)?; + + // KeyType and SecurityStrength each own their canonical 1-byte encoding (`as u8` / `TryFrom`). + let kdf_key_type = KeyType::try_from(input[1 + KECCAK_SERIALIZED_LEN])?; + let kdf_security_strength = SecurityStrength::try_from(input[1 + KECCAK_SERIALIZED_LEN + 1])?; + let kdf_entropy = u64::from_le_bytes( + input[1 + KECCAK_SERIALIZED_LEN + 2..1 + KECCAK_SERIALIZED_LEN + 10].try_into().unwrap(), + ) as usize; + + Ok((keccak, kdf_key_type, kdf_security_strength, kdf_entropy)) +} + #[cfg(test)] mod keccak_tests { use super::*; diff --git a/crypto/sha3/src/lib.rs b/crypto/sha3/src/lib.rs index 42ecac4..5dc832e 100644 --- a/crypto/sha3/src/lib.rs +++ b/crypto/sha3/src/lib.rs @@ -98,15 +98,53 @@ //! let output_key = sha3::SHA3_256::new().derive_key(&input_key, b"Additional input").unwrap(); //!``` //! In the previous example, since [KeyMaterial::from_bytes] cannot know the amount of entropy in the input data, -//! it automatically tags it as [KeyType::BytesLowEntropy], and thus [SHA3::derive_key] produces an output key -//! which also has type [KeyType::BytesLowEntropy]. +//! it automatically tags it as [KeyType::Unknown], and thus [SHA3::derive_key] produces an output key +//! which also has type [KeyType::Unknown]. //! This would also be the case even if the input had type -//! [KeyType::BytesFullEntropy] since the input [KeyMaterial] is 16 bytes but [SHA3_256] needs at least 32 bytes of +//! [KeyType::CryptographicRandom] since the input [KeyMaterial] is 16 bytes but [SHA3_256] needs at least 32 bytes of //! full-entropy input key material in order to be able to produce full entropy output key material. +//! +//! # Suspending and resuming execution +//! +//! When hashing a large message, it can be advantageous to be able to suspend the operation +//! to a cache and resume it later; for example if waiting for the message to stream over a slow network +//! connection. +//! +//! For this reason, all SHA3 algorithms impl [Suspendable]. +//! +//!```rust +//! use bouncycastle_sha3 as sha3; +//! use bouncycastle_core::traits::{Hash, Suspendable}; +//! +//! let msg_part1 = b"The quick brown fox"; +//! let msg_part2 = b" jumped over the lazy dog"; +//! +//! let mut sha3 = sha3::SHA3_256::new(); +//! sha3.do_update(msg_part1); +//! +//! // suspend the in-progress extract while "waiting" for the second part of the message. +//! let serialized_state = sha3.suspend(); +//! +//! // ... +//! // do other things in the meantime +//! // ... +//! +//! // ... later, possibly on another host: resume from the serialized state. +//! let mut sha3_resumed = sha3::SHA3_256::from_suspended(serialized_state).unwrap(); +//! sha3_resumed.do_update(msg_part2); +//! let h: Vec = sha3_resumed.do_final(); +//! ``` +#![cfg_attr(not(test), no_std)] #![forbid(unsafe_code)] #![allow(private_bounds)] +// The `Vec`/`Box`-returning convenience APIs (`hash`, `do_final`, `hash_xof`, `squeeze`, `derive_key`, +// ...) live behind the default-on `alloc` feature. `no_std` users without an allocator should use the +// `*_out(&mut [u8])` / `*_array::()` APIs instead. +#[cfg(feature = "alloc")] +extern crate alloc; + use crate::keccak::KeccakSize; use bouncycastle_core::traits::{Algorithm, HashAlgParams, SecurityStrength}; @@ -114,7 +152,7 @@ use bouncycastle_core::traits::{Algorithm, HashAlgParams, SecurityStrength}; #[allow(unused_imports)] use bouncycastle_core::key_material::{KeyMaterial, KeyType}; #[allow(unused_imports)] -use bouncycastle_core::traits::{Hash, KDF, XOF}; +use bouncycastle_core::traits::{Hash, KDF, Suspendable, XOF}; // end of doc-only imports mod keccak; @@ -132,6 +170,8 @@ pub const SHAKE256_NAME: &str = "SHAKE256"; /*** pub types ***/ pub use sha3::SHA3; pub use shake::SHAKE; + +pub use keccak::SUSPENDED_SHA3_STATE_LEN; pub type SHA3_224 = SHA3; pub type SHA3_256 = SHA3; pub type SHA3_384 = SHA3; @@ -144,18 +184,18 @@ pub type SHAKE256 = SHAKE; /// Private trait on purpose so that only the NIST-approved params can be used. trait SHA3Params: HashAlgParams { const SIZE: KeccakSize; + /// A tag, unique across all SHA3 *and* SHAKE variants, identifying which variant produced a + /// serialized state. Distinguishing same-rate variants (e.g. SHA3-256 vs SHAKE256) requires + /// this to be distinct from every value used by [SHAKEParams::STATE_TAG]. Never reuse a value. + const STATE_TAG: u8; } -// TODO: more elegant to macro this? -impl Algorithm for SHA3_224 { - const ALG_NAME: &'static str = SHA3_224_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_112bit; -} impl HashAlgParams for SHA3_224 { const OUTPUT_LEN: usize = 28; // const BLOCK_LEN: usize = 64; const BLOCK_LEN: usize = 144; // FIPS 202 Table 3 } +#[derive(Clone)] pub struct SHA3_224Params; impl Algorithm for SHA3_224Params { const ALG_NAME: &'static str = SHA3_224_NAME; @@ -168,17 +208,15 @@ impl HashAlgParams for SHA3_224Params { } impl SHA3Params for SHA3_224Params { const SIZE: KeccakSize = KeccakSize::_224; + const STATE_TAG: u8 = 1; } -impl Algorithm for SHA3_256 { - const ALG_NAME: &'static str = SHA3_256_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_128bit; -} impl HashAlgParams for SHA3_256 { const OUTPUT_LEN: usize = 32; // const BLOCK_LEN: usize = 64; const BLOCK_LEN: usize = 136; // FIPS 202 Table 3 } +#[derive(Clone)] pub struct SHA3_256Params; impl Algorithm for SHA3_256Params { const ALG_NAME: &'static str = SHA3_256_NAME; @@ -191,13 +229,11 @@ impl HashAlgParams for SHA3_256Params { } impl SHA3Params for SHA3_256Params { const SIZE: KeccakSize = KeccakSize::_256; + const STATE_TAG: u8 = 2; } +#[derive(Clone)] pub struct SHA3_384Params; -impl Algorithm for SHA3_384 { - const ALG_NAME: &'static str = SHA3_384_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_192bit; -} impl HashAlgParams for SHA3_384 { const OUTPUT_LEN: usize = 48; // const BLOCK_LEN: usize = 128; @@ -214,13 +250,11 @@ impl HashAlgParams for SHA3_384Params { } impl SHA3Params for SHA3_384Params { const SIZE: KeccakSize = KeccakSize::_384; + const STATE_TAG: u8 = 3; } +#[derive(Clone)] pub struct SHA3_512Params; -impl Algorithm for SHA3_512 { - const ALG_NAME: &'static str = SHA3_512_NAME; - const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::_256bit; -} impl HashAlgParams for SHA3_512 { const OUTPUT_LEN: usize = 64; // const BLOCK_LEN: usize = 128; @@ -237,11 +271,15 @@ impl HashAlgParams for SHA3_512Params { } impl SHA3Params for SHA3_512Params { const SIZE: KeccakSize = KeccakSize::_512; + const STATE_TAG: u8 = 4; } trait SHAKEParams: Algorithm { const SIZE: KeccakSize; + /// See [SHA3Params::STATE_TAG]. Must be distinct from every SHA3 *and* SHAKE variant's tag. + const STATE_TAG: u8; } +#[derive(Clone)] pub struct SHAKE128Params; impl Algorithm for SHAKE128Params { const ALG_NAME: &'static str = SHAKE128_NAME; @@ -249,8 +287,10 @@ impl Algorithm for SHAKE128Params { } impl SHAKEParams for SHAKE128Params { const SIZE: KeccakSize = KeccakSize::_128; + const STATE_TAG: u8 = 5; } +#[derive(Clone)] pub struct SHAKE256Params; impl Algorithm for SHAKE256Params { const ALG_NAME: &'static str = SHAKE256_NAME; @@ -258,4 +298,5 @@ impl Algorithm for SHAKE256Params { } impl SHAKEParams for SHAKE256Params { const SIZE: KeccakSize = KeccakSize::_256; + const STATE_TAG: u8 = 6; } diff --git a/crypto/sha3/src/sha3.rs b/crypto/sha3/src/sha3.rs index a55ac26..ac150c6 100644 --- a/crypto/sha3/src/sha3.rs +++ b/crypto/sha3/src/sha3.rs @@ -1,13 +1,24 @@ -use bouncycastle_core::errors::{HashError, KDFError}; -use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Hash, SecurityStrength, KDF}; -use bouncycastle_utils::{max, min}; -use crate::keccak::KeccakDigest; use crate::SHA3Params; +use crate::keccak::{ + KeccakDigest, SHA3_FAMILY_STATE_LEN, SUSPENDED_SHA3_STATE_LEN, deserialize_sha3_family_state, + serialize_sha3_family_state, +}; +use bouncycastle_core::errors::{HashError, KDFError, SuspendableError}; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterialTrait, KeyType}; +// `KeyMaterial` (the concrete type) is only used by the alloc-gated `derive_key_final_internal`. +#[cfg(feature = "alloc")] +use bouncycastle_core::key_material::KeyMaterial; +use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; +use bouncycastle_core::traits::{Algorithm, Hash, KDF, SecurityStrength, Suspendable}; +use bouncycastle_utils::{max, min}; + +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec, vec::Vec}; #[derive(Clone)] pub struct SHA3 { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, keccak: KeccakDigest, kdf_key_type: KeyType, kdf_security_strength: SecurityStrength, @@ -19,7 +30,7 @@ pub struct SHA3 { impl SHA3 { pub fn new() -> Self { Self { - _params: std::marker::PhantomData, + _params: core::marker::PhantomData, keccak: KeccakDigest::new(PARAMS::SIZE), kdf_key_type: KeyType::Zeroized, kdf_security_strength: SecurityStrength::None, @@ -29,6 +40,8 @@ impl SHA3 { /// Swallows errors and simply returns an empty Vec if the hashes fails for whatever reason. fn hash_internal(mut self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + self.do_update(data); self.do_final_out(output) } @@ -46,12 +59,13 @@ impl SHA3 { &self.kdf_security_strength, &SecurityStrength::from_bits(PARAMS::OUTPUT_LEN * 8 / 2), ) - .clone(); + .clone(); } self.do_update(key.ref_to_bytes()) } + #[cfg(feature = "alloc")] fn derive_key_final_internal( self, additional_input: &[u8], @@ -71,7 +85,7 @@ impl SHA3 { // it requires full-entropy input that is at least block length. // TODO: citation needed, which NIST spec did I get this from? if self.kdf_entropy < PARAMS::OUTPUT_LEN { - self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone(); + self.kdf_key_type = min(&self.kdf_key_type, &KeyType::Unknown).clone(); self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level. } @@ -79,20 +93,34 @@ impl SHA3 { let mut key_type = self.kdf_key_type.clone(); let output_security_strength = self.kdf_security_strength.clone(); - output_key.allow_hazardous_operations(); - let bytes_written = self.do_final_out(output_key.mut_ref_to_bytes()?); - output_key.set_key_len(bytes_written)?; - - // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. + let mut bytes_written: usize = 0; + key_material::do_hazardous_operations(output_key, |output_key| { + bytes_written = self.do_final_out(output_key.ref_to_bytes_mut()?); + output_key.set_key_len(bytes_written)?; + Ok(()) + }) + .expect( + "both mut_ref_to_bytes() and set_key_len() should be infallible within a hazop block", + ); + + // since we've done some computation, the result will not actually be zeroized, + // even if all input key material was zeroized. if key_type == KeyType::Zeroized { - key_type = KeyType::BytesLowEntropy; + key_type = KeyType::Unknown; } - output_key.set_key_type(key_type)?; - output_key.set_security_strength( - min(&output_security_strength, &SecurityStrength::from_bits(bytes_written * 8)).clone(), - )?; - output_key.drop_hazardous_operations(); - output_key.truncate(min(&output_key.key_len(), &PARAMS::OUTPUT_LEN).clone())?; + key_material::do_hazardous_operations(&mut *output_key, |output_key| { + output_key.set_key_type(key_type)?; + output_key.set_security_strength( + min(&output_security_strength, &SecurityStrength::from_bits(bytes_written * 8)).clone(), + ) + }) + .expect( + "both set_key_type() and set_security_strength() should be infallible within a hazop block", + ); + + output_key + .set_key_len(min(&output_key.key_len(), &PARAMS::OUTPUT_LEN).clone()) + .expect("should be infallible to truncate key length"); Ok(bytes_written) } } @@ -103,6 +131,11 @@ impl Default for SHA3 { } } +impl Algorithm for SHA3 { + const ALG_NAME: &'static str = PARAMS::ALG_NAME; + const MAX_SECURITY_STRENGTH: SecurityStrength = PARAMS::MAX_SECURITY_STRENGTH; +} + impl Hash for SHA3 { /// As per FIPS 202 Table 3. /// Required, for example, to compute the pad lengths in HMAC. @@ -114,6 +147,7 @@ impl Hash for SHA3 { PARAMS::OUTPUT_LEN } + #[cfg(feature = "alloc")] fn hash(self, data: &[u8]) -> Vec { let mut output: Vec = vec![0u8; PARAMS::OUTPUT_LEN]; _ = self.hash_internal(data, &mut output[..]); @@ -121,6 +155,8 @@ impl Hash for SHA3 { } fn hash_out(self, data: &[u8], mut output: &mut [u8]) -> usize { + output.fill(0); + self.hash_internal(data, &mut output) } @@ -128,6 +164,7 @@ impl Hash for SHA3 { self.keccak.absorb(data) } + #[cfg(feature = "alloc")] fn do_final(self) -> Vec { let dbg_rslt_len = self.output_len(); let mut output: Vec = vec![0u8; self.output_len()]; @@ -140,6 +177,8 @@ impl Hash for SHA3 { // todo -- why doesn't this take a &mut [u8; HASH_LEN] ? // That's probably more user-friendly than this auto-truncating that I have here. fn do_final_out(mut self, output: &mut [u8]) -> usize { + output.fill(0); + self.keccak.absorb_bits(0x02, 2).expect("do_final_out: keccak.absorb_bits failed."); // this shouldn't fail because by construction you can only enter this function once, and this is the only way to absorb partial bits. let bytes_written = if output.len() <= self.output_len() { @@ -152,6 +191,7 @@ impl Hash for SHA3 { bytes_written } + #[cfg(feature = "alloc")] fn do_final_partial_bits( self, partial_byte: u8, @@ -172,6 +212,8 @@ impl Hash for SHA3 { num_partial_bits: usize, output: &mut [u8], ) -> Result { + output.fill(0); + // Mutants note: yep, this is just bit-setting into empty space, so it doesn't matter whether it's OR or XOR. let mut final_input: u16 = ((partial_byte as u16) & ((1 << num_partial_bits) - 1)) | (0x02 << num_partial_bits); @@ -199,6 +241,7 @@ impl KDF for SHA3 { /// Returns a [KeyMaterial]. /// For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials, /// it requires full-entropy input that is at least the bit size (ie 256 bits for SHA3-256, etc). + #[cfg(feature = "alloc")] fn derive_key( mut self, key: &impl KeyMaterialTrait, @@ -219,6 +262,7 @@ impl KDF for SHA3 { self.derive_key_out_final_internal(additional_input, output_key) } + #[cfg(feature = "alloc")] fn derive_key_from_multiple( mut self, keys: &[&impl KeyMaterialTrait], @@ -246,4 +290,47 @@ impl KDF for SHA3 { fn max_security_strength(&self) -> SecurityStrength { SecurityStrength::from_bytes(PARAMS::OUTPUT_LEN / 2) } -} \ No newline at end of file +} + +impl Suspendable for SHA3 { + fn suspend(self) -> [u8; SUSPENDED_SHA3_STATE_LEN] { + let mut out_to_return = [0u8; SUSPENDED_SHA3_STATE_LEN]; + + // insert the version tag + let out: &mut [u8; SHA3_FAMILY_STATE_LEN] = + add_lib_ver(&mut out_to_return).try_into().unwrap(); + + serialize_sha3_family_state( + out, + PARAMS::STATE_TAG, + &self.keccak, + self.kdf_key_type, + self.kdf_security_strength, + self.kdf_entropy, + ); + + out_to_return + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA3_STATE_LEN], + ) -> Result { + // check the version tag. At the moment, we have no not_before version to specify. + let input: &[u8; SHA3_FAMILY_STATE_LEN] = + check_lib_ver(&serialized_state, None)?.try_into().unwrap(); + + // The variant tag rejects states from any other SHA3/SHAKE variant; the rate is then the + // correct one to rebuild with (both are fully determined by the algorithm parameters). + let rate = 1600 - ((PARAMS::SIZE as usize) << 1); + let (keccak, kdf_key_type, kdf_security_strength, kdf_entropy) = + deserialize_sha3_family_state(input, PARAMS::STATE_TAG, rate)?; + + Ok(SHA3 { + _params: core::marker::PhantomData, + keccak, + kdf_key_type, + kdf_security_strength, + kdf_entropy, + }) + } +} diff --git a/crypto/sha3/src/shake.rs b/crypto/sha3/src/shake.rs index f7cd88e..e3dab39 100644 --- a/crypto/sha3/src/shake.rs +++ b/crypto/sha3/src/shake.rs @@ -1,10 +1,22 @@ -use bouncycastle_core::errors::{HashError, KDFError}; -use bouncycastle_core::key_material::{KeyMaterial, KeyMaterialTrait, KeyType}; -use bouncycastle_core::traits::{Algorithm, SecurityStrength, KDF, XOF}; -use bouncycastle_utils::{max, min}; -use crate::keccak::KeccakDigest; use crate::SHAKEParams; +use crate::keccak::{ + KeccakDigest, SHA3_FAMILY_STATE_LEN, SUSPENDED_SHA3_STATE_LEN, deserialize_sha3_family_state, + serialize_sha3_family_state, +}; +use bouncycastle_core::errors::{HashError, KDFError, SuspendableError}; +use bouncycastle_core::key_material; +use bouncycastle_core::key_material::{KeyMaterialTrait, KeyType}; +// `KeyMaterial` and `KeccakSize` are only used by the alloc-gated `derive_key_final_internal`. +#[cfg(feature = "alloc")] +use crate::keccak::KeccakSize; +#[cfg(feature = "alloc")] +use bouncycastle_core::key_material::KeyMaterial; +use bouncycastle_core::suspendable_state::{add_lib_ver, check_lib_ver}; +use bouncycastle_core::traits::{Algorithm, KDF, SecurityStrength, Suspendable, XOF}; +use bouncycastle_utils::{max, min}; +#[cfg(feature = "alloc")] +use alloc::{boxed::Box, vec, vec::Vec}; /// Note: FIPS 202 section 7 states: /// @@ -22,7 +34,7 @@ use crate::SHAKEParams; /// as such if the provided message includes the requested length, SHAKE does not implement the [Hash] trait. #[derive(Clone)] pub struct SHAKE { - _phantomdata: std::marker::PhantomData, + _phantomdata: core::marker::PhantomData, keccak: KeccakDigest, kdf_key_type: KeyType, kdf_security_strength: SecurityStrength, @@ -39,7 +51,7 @@ impl Algorithm for SHAKE { impl SHAKE { pub fn new() -> Self { Self { - _phantomdata: std::marker::PhantomData, + _phantomdata: core::marker::PhantomData, keccak: KeccakDigest::new(PARAMS::SIZE), kdf_key_type: KeyType::Zeroized, kdf_security_strength: SecurityStrength::None, @@ -48,12 +60,15 @@ impl SHAKE { } /// Swallows errors and simply returns an empty Vec if the hashes fails for whatever reason. + #[cfg(feature = "alloc")] fn hash_internal(mut self, data: &[u8], result_len: usize) -> Vec { self.absorb(data); self.squeeze(result_len) } fn hash_internal_out(mut self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + self.absorb(data); self.squeeze_out(output) } @@ -67,14 +82,17 @@ impl SHAKE { self.kdf_entropy += key.key_len(); self.kdf_security_strength = max(&self.kdf_security_strength, &key.security_strength()).clone(); - self.kdf_security_strength = - min(&self.kdf_security_strength, &SecurityStrength::from_bits(PARAMS::SIZE as usize)) - .clone(); + self.kdf_security_strength = min( + &self.kdf_security_strength, + &SecurityStrength::from_bits(PARAMS::SIZE as usize), + ) + .clone(); } self.absorb(key.ref_to_bytes()) } + #[cfg(feature = "alloc")] fn derive_key_final_internal( mut self, additional_input: &[u8], @@ -84,8 +102,13 @@ impl SHAKE { let mut output_key = KeyMaterial::<64>::new(); self.derive_key_out_final_internal(additional_input, &mut output_key)?; + // truncate // 128 => 32, 256 => 64 - output_key.truncate(2 * (PARAMS::SIZE as usize) / 8)?; + match PARAMS::SIZE { + KeccakSize::_128 => output_key.set_key_len(32).expect("truncate should be infallible"), + KeccakSize::_256 => output_key.set_key_len(64).expect("truncate should be infallible"), + _ => unreachable!(), + } Ok(Box::new(output_key)) } @@ -100,31 +123,78 @@ impl SHAKE { // TODO: intuitivitely this makes sense since SHAKE256 and SHA3-256 are both KECCAK[512], and SHAKE128 is KECCAK[256], // TODO: but I would rather find an actual reference for this "fully-seeded" threshold. if self.kdf_entropy < 2 * (PARAMS::SIZE as usize) / 8 { - self.kdf_key_type = min(&self.kdf_key_type, &KeyType::BytesLowEntropy).clone(); + self.kdf_key_type = min(&self.kdf_key_type, &KeyType::Unknown).clone(); self.kdf_security_strength = SecurityStrength::None; // BytesLowEntropy can't have a securtiy level. } self.absorb(additional_input); - // let mut buf = [0u8; 64]; - output_key.allow_hazardous_operations(); - let bytes_written = self.squeeze_out(output_key.mut_ref_to_bytes().expect("We just set .allow_hazardous_operations(), so this should be fine.")); - output_key.set_key_len(bytes_written)?; + let mut bytes_written: usize = 0; + key_material::do_hazardous_operations(output_key, |output_key| { + bytes_written = self.squeeze_out( + output_key.ref_to_bytes_mut().expect("Infallible within do_hazardous_operations"), + ); + output_key.set_key_len(bytes_written) + })?; // since we've done some computation, the result will not actually be zeroized, even if all input key material was zeroized. if self.kdf_key_type == KeyType::Zeroized { - self.kdf_key_type = KeyType::BytesLowEntropy; + self.kdf_key_type = KeyType::Unknown; } - output_key.set_key_type(self.kdf_key_type)?; - output_key.set_security_strength( - min(&self.kdf_security_strength, &SecurityStrength::from_bits(bytes_written * 8)) - .clone(), - )?; - output_key.drop_hazardous_operations(); + key_material::do_hazardous_operations(output_key, |output_key| { + output_key.set_key_type(self.kdf_key_type)?; + output_key.set_security_strength( + min(&self.kdf_security_strength, &SecurityStrength::from_bits(bytes_written * 8)) + .clone(), + ) + })?; Ok(bytes_written) } } +impl Suspendable for SHAKE { + fn suspend(self) -> [u8; SUSPENDED_SHA3_STATE_LEN] { + let mut out_to_return = [0u8; SUSPENDED_SHA3_STATE_LEN]; + + // insert the version tag + let out: &mut [u8; SHA3_FAMILY_STATE_LEN] = + add_lib_ver(&mut out_to_return).try_into().unwrap(); + + serialize_sha3_family_state( + out, + PARAMS::STATE_TAG, + &self.keccak, + self.kdf_key_type, + self.kdf_security_strength, + self.kdf_entropy, + ); + + out_to_return + } + + fn from_suspended( + serialized_state: [u8; SUSPENDED_SHA3_STATE_LEN], + ) -> Result { + // check the version tag. At the moment, we have no not_before version to specify. + let input: &[u8; SHA3_FAMILY_STATE_LEN] = + check_lib_ver(&serialized_state, None)?.try_into().unwrap(); + + // The variant tag rejects states from any other SHA3/SHAKE variant; the rate is then the + // correct one to rebuild with (both are fully determined by the algorithm parameters). + let rate = 1600 - ((PARAMS::SIZE as usize) << 1); + let (keccak, kdf_key_type, kdf_security_strength, kdf_entropy) = + deserialize_sha3_family_state(input, PARAMS::STATE_TAG, rate)?; + + Ok(SHAKE { + _phantomdata: core::marker::PhantomData, + keccak, + kdf_key_type, + kdf_security_strength, + kdf_entropy, + }) + } +} + impl KDF for SHAKE { /// Returns a [KeyMaterial]. /// For the KDF to be considered "fully-seeded" and be capable of outputting full-entropy KeyMaterials, @@ -133,6 +203,7 @@ impl KDF for SHAKE { /// To produce longer keys, use [KDF::derive_key_out]. /// To produce shorter keys, either use [KDF::derive_key_out] or truncate this result down with /// [KeyMaterial::truncate]. + #[cfg(feature = "alloc")] fn derive_key( mut self, key: &impl KeyMaterialTrait, @@ -160,6 +231,7 @@ impl KDF for SHAKE { /// Returns a 32 byte key for SHAKE128 and a 64 byte key for SHAKE256. /// To produce longer keys, use [KDF::derive_key_out]. /// To produce shorter keys, either use [KDF::derive_key_out] or truncate this result down with [KeyMaterial::truncate]. + #[cfg(feature = "alloc")] fn derive_key_from_multiple( mut self, keys: &[&impl KeyMaterialTrait], @@ -195,11 +267,14 @@ impl Default for SHAKE { } impl XOF for SHAKE { + #[cfg(feature = "alloc")] fn hash_xof(self, data: &[u8], result_len: usize) -> Vec { self.hash_internal(data, result_len) } fn hash_xof_out(self, data: &[u8], output: &mut [u8]) -> usize { + output.fill(0); + self.hash_internal_out(data, output) } @@ -232,6 +307,7 @@ impl XOF for SHAKE { Ok(()) } + #[cfg(feature = "alloc")] fn squeeze(&mut self, num_bytes: usize) -> Vec { let mut out: Vec = vec![0u8; num_bytes]; self.squeeze_out(&mut out); @@ -239,6 +315,8 @@ impl XOF for SHAKE { } fn squeeze_out(&mut self, output: &mut [u8]) -> usize { + output.fill(0); + if !self.keccak.squeezing { self.keccak.absorb_bits(0x0F, 4).expect("Absorb_bits failed"); }; @@ -262,6 +340,8 @@ impl XOF for SHAKE { return Err(HashError::InvalidLength("must be in the range [0,7]")); } + *output = 0; + let mut buf = [0u8; 1]; self.keccak.squeeze(&mut buf); *output = buf[0] >> 8 - num_bits; @@ -271,4 +351,4 @@ impl XOF for SHAKE { fn max_security_strength(&self) -> SecurityStrength { SecurityStrength::from_bits(PARAMS::SIZE as usize) } -} \ No newline at end of file +} diff --git a/crypto/sha3/tests/sha3_tests.rs b/crypto/sha3/tests/sha3_tests.rs index 787e9fb..535e2dd 100644 --- a/crypto/sha3/tests/sha3_tests.rs +++ b/crypto/sha3/tests/sha3_tests.rs @@ -1,12 +1,17 @@ #[cfg(test)] mod sha3_tests { use super::sha3_test_helpers::*; - use bouncycastle_core::key_material::{KeyMaterial256, KeyMaterial512, KeyMaterial, KeyType, KeyMaterialTrait,}; + use bouncycastle_core::key_material; + use bouncycastle_core::key_material::{ + KeyMaterial, KeyMaterial256, KeyMaterial512, KeyMaterialTrait, KeyType, + }; use bouncycastle_core::traits::{Hash, HashAlgParams, KDF, SecurityStrength}; use bouncycastle_core_test_framework::DUMMY_SEED_512; use bouncycastle_core_test_framework::hash::TestFrameworkHash; use bouncycastle_core_test_framework::kdf::TestFrameworkKDF; - use bouncycastle_sha3::{SHA3_224, SHA3_256, SHA3_384, SHA3_512}; + use bouncycastle_sha3::{ + SHA3_224, SHA3_256, SHA3_384, SHA3_512, SHAKE256, SUSPENDED_SHA3_STATE_LEN, + }; #[test] fn test_constants() { @@ -20,10 +25,10 @@ mod sha3_tests { assert_eq!(SHA3_384::BLOCK_LEN, 104); assert_eq!(SHA3_512::BLOCK_LEN, 72); - assert_eq!(SHA3_224::new().block_bitlen(), 144*8); - assert_eq!(SHA3_256::new().block_bitlen(), 136*8); - assert_eq!(SHA3_384::new().block_bitlen(), 104*8); - assert_eq!(SHA3_512::new().block_bitlen(), 72*8); + assert_eq!(SHA3_224::new().block_bitlen(), 144 * 8); + assert_eq!(SHA3_256::new().block_bitlen(), 136 * 8); + assert_eq!(SHA3_384::new().block_bitlen(), 104 * 8); + assert_eq!(SHA3_512::new().block_bitlen(), 72 * 8); } #[test] @@ -66,10 +71,10 @@ mod sha3_tests { assert_eq!(&out, b"\x58\x4c\xc7\x02\xc2\x22\x9a\x0a\xbc\x78\x9b\xfa\x64\xb4\x27\x1f\xb8\xf0\xbb\x78\x67\x15\x88\xb9\xef\x1d\x09\x3e\xa3\xd4\x72\x58\x4c\x6d\x43\xb5\x68\x33\x59\x47\x2f\x44\x1b\x33\x85\x6f\x68\x28\x59\xf0\xc3\x95\x4b\x56\x80\x8f\xd1\xfb\xa0\xb5\x9c\x9d\x19\x54"); assert_eq!(bytes_written, 64); - // check that if you feed it an output slice that's bigger than it needs, that it doesn't touch the extra bytes. + // check that the bytes of an oversized output buffer past the digest length get zeroized. let mut out = DUMMY_SEED_512.clone(); SHA3_256::new().hash_out(DUMMY_SEED_512, &mut out); - assert_eq!(&out[32..], &DUMMY_SEED_512[32..]); + assert!(out[32..].iter().all(|&b| b == 0)); } #[test] @@ -246,38 +251,38 @@ mod sha3_tests { // Exact entropy let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHA3_256::new().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial256::from_bytes(b"\x05\x0a\x48\x73\x3b\xd5\xc2\x75\x6b\xa9\x5c\x58\x28\xcc\x83\xee\x16\xfa\xbc\xd3\xc0\x86\x88\x5b\x77\x44\xf8\x4a\x0f\x9e\x0d\x94").unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); assert_eq!(derived_key.security_strength(), SecurityStrength::_128bit); assert_eq!(derived_key.ref_to_bytes(), expected_key.ref_to_bytes()); // more entropy than needed -- single input key let key_material = - KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::BytesFullEntropy) + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHA3_256::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); assert_eq!(derived_key.security_strength(), SecurityStrength::_128bit); // more entropy than needed -- single input key // but if you use SHA512 then you get SecurityStrength::_256bit let key_material = - KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::BytesFullEntropy) + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHA3_512::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); assert_eq!(derived_key.security_strength(), SecurityStrength::_256bit); // more entropy than needed -- multiple input keys let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_material]; let derived_key = SHA3_256::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); assert_eq!(derived_key.security_strength(), SecurityStrength::_128bit); // more entropy than needed -- multiple input keys of different full-entropy types; @@ -297,41 +302,40 @@ mod sha3_tests { assert_eq!(key_material.key_type(), KeyType::Zeroized); // it should do it, but return a zeroized output key, regardless of the additional_input let derived_key = SHA3_256::new().derive_key(&key_material, &[1u8; 100]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); assert_eq!(derived_key.security_strength(), SecurityStrength::None); // less entropy than needed -- various permutations, but not exhaustive let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHA3_256::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); assert_eq!(derived_key.security_strength(), SecurityStrength::None); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_material]; let derived_key = SHA3_512::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); assert_eq!(derived_key.security_strength(), SecurityStrength::None); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHA3_224::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); assert_eq!(derived_key.security_strength(), SecurityStrength::None); let key_low_entropy = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesLowEntropy) - .unwrap(); + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::Unknown).unwrap(); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_low_entropy]; let derived_key = SHA3_256::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); assert_eq!(derived_key.security_strength(), SecurityStrength::None); } @@ -342,7 +346,7 @@ mod sha3_tests { let input_seed = KeyMaterial256::from_bytes(&DUMMY_SEED_512[..32]).expect("Error happened"); let mut output_seed = SHA3_256::new().derive_key(&input_seed, b"nytimes.com").expect("Error happened"); - match output_seed.convert_key_type(KeyType::MACKey) { + match output_seed.set_key_type(KeyType::MACKey) { Ok(_) => { panic!( "Should have failed to convert key type because the input was BytesLowEntropy" @@ -356,20 +360,20 @@ mod sha3_tests { let mut output_seed = SHA3_256::new() .derive_key(&input_seed, b"some addtional input to the KDF") .expect("Error happened"); - output_seed.allow_hazardous_operations(); - output_seed.convert_key_type(KeyType::MACKey).unwrap(); - output_seed.drop_hazardous_operations(); // not strictly necessary because KeyMaterial - // will automatically drop allow_hazardous_conversions() after performing one. + key_material::do_hazardous_operations(&mut *output_seed, |output_seed| { + output_seed.set_key_type(KeyType::MACKey) + }) + .unwrap(); assert_eq!(output_seed.key_type(), KeyType::MACKey); // This works because we explicitly tag the input data as BytesFullEntropy. // This is the preferred and better way to do it. let input_seed = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::CryptographicRandom) .expect("Error happened"); let output_seed = SHA3_256::new().derive_key(&input_seed, b"nytimes.com").expect("Error happened"); - assert_eq!(output_seed.key_type(), KeyType::BytesFullEntropy); + assert_eq!(output_seed.key_type(), KeyType::CryptographicRandom); } #[test] @@ -392,6 +396,63 @@ mod sha3_tests { run_test_vectors(read_test_vectors("tests/data/SHA3TestVectors.txt")); } + #[test] + fn serializable_state() { + use bouncycastle_core::errors::SuspendableError; + use bouncycastle_core::traits::Suspendable; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableState; + + let str = "Colorless green ideas sleep furiously"; + + // A helper that exercises the full round-trip for one SHA3 variant. + fn round_trip + Clone>(mut hash: H, input: &[u8]) { + hash.do_update(input); + + // do the default trait-conformance tests + TestFrameworkSuspendableState::new().test(&hash); + + // serialize the in-progress state, then finish the original + let serialized_state = hash.clone().suspend(); + assert_eq!(serialized_state.len(), SUSPENDED_SHA3_STATE_LEN); + + let expected = hash.do_final(); + + // rebuild from the serialized state and confirm it produces the same digest + let from_state = H::from_suspended(serialized_state).unwrap(); + assert_eq!(expected, from_state.do_final()); + + // a corrupt `squeezing` byte (last byte of the keccak state) must be rejected. + // Layout: 3 version bytes + variant tag(1) + [u64;25](200) + data_queue(192) + // + bits_in_queue(8) + squeezing(1) + let mut busted = serialized_state; + busted[3 + 1 + 400] = 42; + match H::from_suspended(busted) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error for a corrupt squeezing byte"), + } + } + + round_trip(SHA3_224::new(), str.as_bytes()); + round_trip(SHA3_256::new(), str.as_bytes()); + round_trip(SHA3_384::new(), str.as_bytes()); + round_trip(SHA3_512::new(), str.as_bytes()); + + // A state serialized by one variant must be rejected by a different variant (mismatched + // variant tag). SHA3-256 and SHAKE256 share the same rate (1088), so this cross-family case + // is only caught by the tag, not the rate -- it is the exact bug the tag exists to prevent. + let mut sha3_256 = SHA3_256::new(); + sha3_256.do_update(str.as_bytes()); + let serialized_256 = sha3_256.suspend(); + match SHA3_512::from_suspended(serialized_256) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHA3-256 state into SHA3-512"), + } + match SHAKE256::from_suspended(serialized_256) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHA3-256 state into SHAKE256"), + } + } + fn run_test_vectors(test_vectors: Vec) { for tc in test_vectors { match tc.algorithm { diff --git a/crypto/sha3/tests/shake_tests.rs b/crypto/sha3/tests/shake_tests.rs index e87ea0d..0140874 100644 --- a/crypto/sha3/tests/shake_tests.rs +++ b/crypto/sha3/tests/shake_tests.rs @@ -9,7 +9,7 @@ mod shake_tests { use bouncycastle_core::traits::{KDF, SecurityStrength, XOF}; use bouncycastle_core_test_framework::DUMMY_SEED_512; use bouncycastle_core_test_framework::kdf::TestFrameworkKDF; - use bouncycastle_sha3::{SHAKE128, SHAKE256}; + use bouncycastle_sha3::{SHA3_256, SHAKE128, SHAKE256}; #[test] fn test_xof_partial_bit_output() { @@ -161,27 +161,27 @@ mod shake_tests { fn kdf_input_entropy() { // Exact entropy let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHAKE128::new().derive_key(&key_material, &[0u8; 0]).unwrap(); let expected_key = KeyMaterial256::from_bytes(b"\x06\x6a\x36\x1d\xc6\x75\xf8\x56\xce\xcd\xc0\x2b\x25\x21\x8a\x10\xce\xc0\xce\xcf\x79\x85\x9e\xc0\xfe\xc3\xd4\x09\xe5\x84\x7a\x92").unwrap(); assert_eq!(derived_key.ref_to_bytes(), expected_key.ref_to_bytes()); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); // more entropy than needed -- single input key let key_material = - KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::BytesFullEntropy) + KeyMaterial512::from_bytes_as_type(&DUMMY_SEED_512[..64], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHAKE128::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); // // more entropy than needed -- multiple input keys let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_material]; let derived_key = SHAKE128::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesFullEntropy); + assert_eq!(derived_key.key_type(), KeyType::CryptographicRandom); // more entropy than needed -- multiple input keys of different full-entropy types; // should get the type of the first one @@ -197,37 +197,36 @@ mod shake_tests { // // less entropy than needed -- various permutations, but not exhaustive let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..31], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..31], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHAKE128::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_material]; let derived_key = SHAKE256::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..8], KeyType::CryptographicRandom) .unwrap(); let derived_key = SHAKE128::new().derive_key(&key_material, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); let key_low_entropy = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::BytesLowEntropy) - .unwrap(); + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..32], KeyType::Unknown).unwrap(); let key_material = - KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::BytesFullEntropy) + KeyMaterial256::from_bytes_as_type(&DUMMY_SEED_512[..16], KeyType::CryptographicRandom) .unwrap(); let keys = [&key_material, &key_low_entropy]; let derived_key = SHAKE128::new().derive_key_from_multiple(&keys, &[0u8; 0]).unwrap(); - assert_eq!(derived_key.key_type(), KeyType::BytesLowEntropy); + assert_eq!(derived_key.key_type(), KeyType::Unknown); } #[test] - fn test_security_strength() { + fn security_strength() { assert_eq!(KDF::max_security_strength(&SHAKE128::default()), SecurityStrength::_128bit); assert_eq!(XOF::max_security_strength(&SHAKE128::default()), SecurityStrength::_128bit); assert_eq!(KDF::max_security_strength(&SHAKE256::default()), SecurityStrength::_256bit); @@ -239,6 +238,73 @@ mod shake_tests { run_test_vectors(read_test_vectors("tests/data/SHAKETestVectors.txt")); } + #[test] + fn suspendable_state() { + use bouncycastle_core::errors::SuspendableError; + use bouncycastle_core::traits::Suspendable; + use bouncycastle_core_test_framework::suspendable_state::TestFrameworkSuspendableState; + + let str = "Colorless green ideas sleep furiously"; + + // A helper that exercises the full round-trip for one SHAKE variant. + fn round_trip + Clone>(mut shake: X, input: &[u8]) { + shake.absorb(input); + + // do the default trait-conformance tests + TestFrameworkSuspendableState::new().test(&shake); + + // Test #1 + // serialize the in-progress (absorbing) state, then squeeze from the original and compare + let serialized_state = shake.clone().suspend(); + let expected = shake.squeeze(64); + + // rebuild from the serialized state and confirm it produces the same output + let mut from_state = X::from_suspended(serialized_state).unwrap(); + assert_eq!(expected, from_state.squeeze(64)); + + // Test #2 + // serialize the in-progress (squeezing) state, then squeeze more from the original and compare + let serialized_state = shake.clone().suspend(); + let expected = shake.squeeze(64); + + // rebuild from the serialized state and confirm it produces the same output + let mut from_state = X::from_suspended(serialized_state).unwrap(); + assert_eq!(expected, from_state.squeeze(64)); + + // a corrupt `squeezing` byte (last byte of the keccak state) must be rejected. + // Layout: 3 version bytes + variant tag(1) + [u64;25](200) + data_queue(192) + // + bits_in_queue(8) + squeezing(1) + let mut busted = serialized_state; + busted[3 + 1 + 400] = 42; + match X::from_suspended(busted) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error for a corrupt squeezing byte"), + } + } + + round_trip(SHAKE128::new(), str.as_bytes()); + round_trip(SHAKE256::new(), str.as_bytes()); + + // A state serialized by one variant must be rejected by a different variant (mismatched + // variant tag). The SHAKE256 -> SHA3-256 case is the important one: they share the same rate + // (1088), so only the variant tag distinguishes them. + let mut shake128 = SHAKE128::new(); + shake128.absorb(str.as_bytes()); + let serialized_128 = shake128.suspend(); + match SHAKE256::from_suspended(serialized_128) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHAKE128 state into SHAKE256"), + } + + let mut shake256 = SHAKE256::new(); + shake256.absorb(str.as_bytes()); + let serialized_256 = shake256.suspend(); + match SHA3_256::from_suspended(serialized_256) { + Err(SuspendableError::InvalidData) => { /* good */ } + _ => panic!("Expected an error when loading a SHAKE256 state into SHA3-256"), + } + } + fn run_test_vectors(test_vectors: Vec) { for tc in test_vectors { //println!("SHA3-{} {}-bits", &tc.algorithm, &tc.bits); diff --git a/crypto/utils/src/ct.rs b/crypto/utils/src/ct.rs index 24ca9bf..96b1950 100644 --- a/crypto/utils/src/ct.rs +++ b/crypto/utils/src/ct.rs @@ -38,7 +38,9 @@ impl Condition { // MikeO: TODO: there are a bunch of impls in here that seem to be generic and not related to i64, // MikeO: TODO: could those be moved to a generic impl for Condition ? - pub const TRUE: Self = Self(1); + /// TRUE is the bit vector of all 1's + pub const TRUE: Self = Self(-1); + /// FALSE is the bit vector of all 0's pub const FALSE: Self = Self(0); pub const fn from_bool() -> Self { @@ -111,13 +113,31 @@ impl Condition { *dst = self.select(src, *dst); } - // MikeO: TODO: I have no idea what this does, .negate(-1) seems to give -3 ?? Is that a bug? /// Conditionally negate the value. + /// + /// negate(-1) gives -3 + /// + /// `value` is `-1` (i.e., all bits are `1`, `...1111`) + /// + /// Condition `self.0` is 1 (`...0001`) (assuming `TRUE`) + /// + /// XOR operation was executed as `value ^ self.0` + /// + /// Then `...1111 XOR ...0001 = ...1110` (i.e., `-2`) + /// + /// Subtraction operation is `wrapping_sub(self.0)` + /// + /// Then `-2 - 1 = -3` + /// + /// As a result, `1`, which is the negation of `-1`, should be returned, but `-3` is output. + /// + /// Therefore, if the [Self::TRUE] constant value of the i64 [Condition] implementation is changed to `-1`, + /// the test also runs normally. pub const fn negate(self, value: i64) -> i64 { (value ^ self.0).wrapping_sub(self.0) } - const fn or_halves(value: i64) -> i64 { + pub const fn or_halves(value: i64) -> i64 { (value | (value >> 32)) & 0xFFFFFFFF } @@ -136,93 +156,36 @@ impl Condition { } } -// TODO: ... this doesn't ... work. We should get this working and then then do u8. +// TODO: We should do Condition. // TODO: then and change Hex and Base64 to use this. // TODO: (there's probably no noticeable performance difference u8 and u64 bit ops on a 64-bit machine, // TODO: but there would be on a 8, 16, or 32-bit machine.) -// impl Condition { -// pub const TRUE: Self = Self(1); -// pub const FALSE: Self = Self(0); -// -// pub const fn new() -> Self { -// Self((VALUE as u64).wrapping_neg()) -// } -// -// pub const fn from_bool(value: bool) -> Self { -// Self((value as u64).wrapping_neg()) -// } -// -// pub const fn is_bit_set(value: u64, bit: u64) -> Self { -// Self(((value >> bit) & 1).wrapping_neg()) -// } -// -// // MikeO: TODO ?? What does "negative" mean for an unsigned value? -// pub const fn is_negative(value: u64) -> Self { -// Self(((value as i64) >> 63) as u64) -// } -// -// pub const fn is_not_zero(value: u64) -> Self { -// Self::is_negative(Self::or_halves(value).wrapping_neg()) -// } -// -// pub const fn is_zero(value: u64) -> Self { -// Self::is_negative(Self::or_halves(value).wrapping_sub(1)) -// } -// -// // MikeO: TODO: I borrowed this formula from Botan, but rust complains about u64 subtraction overflow if x < y, so this works in C but won't work in rust. -// // MikeO: TODO: I played with u64.wrapping_sub(y) but that doesn't work either. -// pub const fn is_lt(x: u64, y: u64) -> Self { -// Self::is_zero(x ^ ((x ^ y) | (x.wrapping_sub(y)) ^ x)) -// } -// -// // Note: haven't found a clever way to make this const, since it either needs a (non-const) not (!) or a boolean OR is_zero. -// // pub fn is_lte(x: i64, y: i64) -> Self { !Self::is_gt(x, y) } -// -// // pub const fn is_gt(x: i64, y: i64) -> Self { Self::is_lt(y, x) } -// -// // Note: haven't found a clever way to make this const, since it either needs a (non-const) not (!) or a boolean OR is_zero. -// // pub fn is_gte(x: i64, y: i64) -> Self { !Self::is_lt(x, y) } -// -// pub fn is_in_list(value: u64, list: &[u64]) -> Self { -// // Research question: is this actually constant-time? -// // A clever compiler might turn this into a short-circuiting loop. -// // A quick google search shows that rust doesn't have the ability to annotate specific code blocks -// // as no-optimize; the only option is to insert direct assembly. -// -// let mut c = Self::FALSE; -// for i in 0..list.len() { -// let diff = value ^ list[i]; -// c |= Condition::::is_zero(diff); -// } -// -// c -// } -// -// pub fn mov(self, src: u64, dst: &mut u64) { -// *dst = self.select(src, *dst); -// } -// -// // MikeO: TODO: This needs a docstring because I have no idea what this does. -// pub const fn negate(self, value: u64) -> u64 { -// (value ^ self.0).wrapping_sub(self.0) -// } -// -// const fn or_halves(value: u64) -> u64 { -// (value & 0xFFFFFFFF) | (value >> 32) -// } -// -// pub const fn select(self, true_value: u64, false_value: u64) -> u64 { -// (true_value & self.0) | (false_value & !self.0) -// } -// -// pub const fn swap(self, lhs: u64, rhs: u64) -> (u64, u64) { -// (self.select(rhs, lhs), self.select(lhs, rhs)) -// } -// -// pub const fn to_bool_var(self) -> bool { -// self.0 != 0 -// } -// } +impl Condition { + /// TRUE is the bit vector of all 1's + pub const TRUE: Self = Self(u64::MAX); + /// FALSE is the bit vector of all 0's + pub const FALSE: Self = Self(0); + + // this is the core logic for constant-time mask generation for unsigned integers + // Unlike signed integers where we can rely on Two's Complement via negation `-(v as i64)`, + // for u64 we must use wrapping subtraction to achieve the all-ones bit pattern (u64::MAX) for true + pub const fn from_bool() -> Self { + // If VALUE is true (1) -> 0 - 1 = u64::MAX (All 1s) + // If VALUE is false (0) -> 0 - 0 = 0 (All 0s) + Self(0u64.wrapping_sub(VALUE as u64)) + } + + // the select function manually for u64 + // although a fully generic impl would be the ultimate long-term goal + pub fn select(self, a: u64, b: u64) -> u64 { + let mask = self.0; + (a & mask) | (b & !mask) + } + + pub fn is_true(&self) -> bool { + self.0 != 0 + } +} impl BitAnd for Condition where @@ -327,22 +290,22 @@ pub fn conditional_copy_bytes( a: &[u8; LEN], b: &[u8; LEN], out: &mut [u8; LEN], - take_a: bool) { - - // we want the behaviour of + take_a: bool, +) { + // we want the behaviour of // if take_a { 0xFF } else { 0x00 } // but without using any branches that could leak timing signals - let mask: u8 = (take_a as u8) | - (take_a as u8) <<1 | - (take_a as u8) <<2 | - (take_a as u8) <<3 | - (take_a as u8) <<4 | - (take_a as u8) <<5 | - (take_a as u8) <<6 | - (take_a as u8) <<7; - + let mask: u8 = (take_a as u8) + | (take_a as u8) << 1 + | (take_a as u8) << 2 + | (take_a as u8) << 3 + | (take_a as u8) << 4 + | (take_a as u8) << 5 + | (take_a as u8) << 6 + | (take_a as u8) << 7; + debug_assert_eq!(mask, if take_a { 0xFF } else { 0x00 }); - + for i in 0..LEN { out[i] = std::hint::black_box(a[i] & mask) | std::hint::black_box(b[i] & !mask); } diff --git a/crypto/utils/tests/ct_tests.rs b/crypto/utils/tests/ct_tests.rs index c9d2712..8e9be76 100644 --- a/crypto/utils/tests/ct_tests.rs +++ b/crypto/utils/tests/ct_tests.rs @@ -143,10 +143,7 @@ mod i64_tests { let c1 = Condition::::TRUE; assert_eq!(c1.negate(1), -1); assert_eq!(c1.negate(0), 0); - - // MikeO: TODO: I don't understand what this function does well enough to test it. - // MikeO: TODO: is this failing test a real bug? - // assert_eq!(c1.negate(-1), 1); + assert_eq!(c1.negate(-1), 1); let c2 = Condition::::FALSE; assert_eq!(c2.negate(1), 1); @@ -154,10 +151,35 @@ mod i64_tests { assert_eq!(c2.negate(-1), -1); } - // MikeO: TODO: I don't understand what this function does well enough to test it. #[test] fn test_or_halves() { - // todo + // 0 input -> 0 output + assert_eq!(Condition::::or_halves(0), 0); + + // Lower 32 bits should be preserved + assert_eq!(Condition::::or_halves(1), 1); + assert_eq!(Condition::::or_halves(0x12345678), 0x12345678); + + // Upper 32 bits should be folded into lower 32 bits + // (1 << 32) OR (1 << 32 >> 32) => 0 OR 1 => 1 + assert_eq!(Condition::::or_halves(1 << 32), 1); + + // Mixed case: Upper 0x10000000 | Lower 0x00000001 => 0x10000001 + assert_eq!(Condition::::or_halves(0x10000000_00000001), 0x10000001); + + // Negative number check (-1) + // -1 is 0xFFFF...FFFF + // (-1 >> 32) is -1 (Arithmetic shift preserves sign) + // (-1 | -1) is -1 + // -1 & 0xFFFFFFFF is 0x00000000FFFFFFFF (i64 value: 4294967295) + assert_eq!(Condition::::or_halves(-1), 0xFFFFFFFF); + + // i64::MIN check (Only MSB set) + // i64::MIN = 0x80000000_00000000 + // (val >> 32) = 0xFFFFFFFF_80000000 (Sign extension) + // (val | shifted) = 0xFFFFFFFF_80000000 + // (& mask) = 0x00000000_80000000 + assert_eq!(Condition::::or_halves(i64::MIN), 0x80000000); } #[test] @@ -187,179 +209,67 @@ mod i64_tests { } } -// #[cfg(test)] -// mod u64_tests { -// use super::*; -// -// #[test] -// fn const_tests() { -// assert_eq!(Condition::::TRUE.to_bool_var(), true); -// assert_eq!(Condition::::FALSE.to_bool_var(), false); -// } -// -// #[test] -// fn from_bool() { -// assert_eq!(Condition::::new::().to_bool_var(), true); -// assert_eq!(Condition::::new::().to_bool_var(), false); -// -// let btrue: bool = true; -// let bfalse: bool = false; -// assert_eq!(Condition::::from_bool(btrue).to_bool_var(), true); -// assert_eq!(Condition::::from_bool(bfalse).to_bool_var(), false); -// } -// -// #[test] -// fn is_bit_set() { -// assert_eq!(Condition::::is_bit_set(1, 0).to_bool_var(), true); -// assert_eq!(Condition::::is_bit_set(1, 1).to_bool_var(), false); -// assert_eq!(Condition::::is_bit_set(8, 3).to_bool_var(), true); -// } -// -// // MikeO: TODO ?? What does "negative" mean for an unsigned value? -// #[test] -// fn is_negative() { -// // assert_eq!(Condition::::is_negative(-1).to_bool_var(), true); // << This doesn't compile, for obvious reasons. -// assert_eq!(Condition::::is_negative(0).to_bool_var(), false); -// assert_eq!(Condition::::is_negative(1).to_bool_var(), false); -// assert_eq!(Condition::::is_negative(1 << 12).to_bool_var(), false); -// } -// -// #[test] -// fn is_not_zero() { -// assert_eq!(Condition::::is_not_zero(1).to_bool_var(), true); -// assert_eq!(Condition::::is_not_zero(0).to_bool_var(), false); -// assert_eq!(Condition::::is_not_zero(1 << 12).to_bool_var(), true); -// } -// -// #[test] -// fn is_zero() { -// assert_eq!(Condition::::is_zero(1).to_bool_var(), false); -// assert_eq!(Condition::::is_zero(0).to_bool_var(), true); -// assert_eq!(Condition::::is_zero(1 << 12).to_bool_var(), false); -// } -// -// // TODO: turn this back on once implemented -// #[test] -// fn is_lt() { -// assert_eq!(Condition::::is_lt(1, 2).to_bool_var(), true); -// assert_eq!(Condition::::is_lt(2, 1).to_bool_var(), false); -// assert_eq!(Condition::::is_lt(2, 2).to_bool_var(), false); -// assert_eq!(Condition::::is_lt(0, 1).to_bool_var(), true); -// -// let mut i: u64 = 0; -// assert_eq!(Condition::::is_lt(i, 1).to_bool_var(), true); -// i = 1; -// assert_eq!(Condition::::is_lt(i, 1).to_bool_var(), false); -// } -// -// // TODO: turn this back on once implemented -// // #[test] -// // fn is_lte() { -// // assert_eq!(Condition::::is_lte(1, 2).to_bool_var(), true); -// // assert_eq!(Condition::::is_lte(2, 1).to_bool_var(), false); -// // assert_eq!(Condition::::is_lte(2, 2).to_bool_var(), true); -// // assert_eq!(Condition::::is_lte(0, 1).to_bool_var(), true); -// // assert_eq!(Condition::::is_lte(-100, -99).to_bool_var(), true); -// // assert_eq!(Condition::::is_lte(-98, 98).to_bool_var(), true); -// // } -// -// // #[test] -// // fn is_gt() { -// // assert_eq!(Condition::::is_gt(1, 2).to_bool_var(), false); -// // assert_eq!(Condition::::is_gt(2, 1).to_bool_var(), true); -// // assert_eq!(Condition::::is_gt(2, 2).to_bool_var(), false); -// // assert_eq!(Condition::::is_gt(0, 1).to_bool_var(), false); -// // assert_eq!(Condition::::is_gt(-100, -99).to_bool_var(), false); -// // assert_eq!(Condition::::is_gt(-98, 98).to_bool_var(), false); -// // } -// -// // #[test] -// // fn is_gte() { -// // assert_eq!(Condition::::is_gte(1, 2).to_bool_var(), false); -// // assert_eq!(Condition::::is_gte(2, 1).to_bool_var(), true); -// // assert_eq!(Condition::::is_gte(2, 2).to_bool_var(), true); -// // assert_eq!(Condition::::is_gte(0, 1).to_bool_var(), false); -// // assert_eq!(Condition::::is_gte(-100, -99).to_bool_var(), false); -// // assert_eq!(Condition::::is_gte(-98, 98).to_bool_var(), false); -// // } -// -// // TODO: turn this back on once implemented -// // #[test] -// // fn is_in_range() { -// // assert_eq!(Condition::::is_within_range(1, 0, 2).to_bool_var(), true); -// // assert_eq!(Condition::::is_within_range(2, 0, 1).to_bool_var(), false); -// // assert_eq!(Condition::::is_within_range(1, -5, 2).to_bool_var(), true); -// // assert_eq!(Condition::::is_within_range(0, -5, 5).to_bool_var(), true); -// // assert_eq!(Condition::::is_within_range(1, 0, 0).to_bool_var(), false); -// // } -// -// #[test] -// fn is_in_list() { -// assert_eq!(Condition::::is_in_list(1, &[1, 2, 3]).to_bool_var(), true); -// assert_eq!(Condition::::is_in_list(4, &[1, 2, 3]).to_bool_var(), false); -// assert_eq!(Condition::::is_in_list(3, &[1, 2, 3, 3, 3, 3]).to_bool_var(), true); -// } -// -// #[test] -// fn test_mov() { -// let src = 1u64; -// let mut dst = 2u64; -// let c1 = Condition::::TRUE; -// c1.mov(src, &mut dst); -// assert_eq!(dst, 1); -// -// let c2 = Condition::::FALSE; -// dst = 2; -// c2.mov(src, &mut dst); -// assert_eq!(dst, 2); -// } -// -// // MikeO: TODO: I don't understand what this function does well enough to test it. -// // #[test] -// // fn test_negate() { -// // let c1 = Condition::::TRUE; -// // assert_eq!(c1.negate(1), -1); -// // assert_eq!(c1.negate(0), 0); -// // assert_eq!(c1.negate(-1), 1); -// // -// // let c2 = Condition::::FALSE; -// // assert_eq!(c2.negate(1), 1); -// // assert_eq!(c2.negate(0), 0); -// // assert_eq!(c2.negate(-1),-1); -// // } -// -// // MikeO: TODO: I don't understand what this function does well enough to test it. -// #[test] -// fn test_or_halves() { -// todo!() -// } -// -// #[test] -// fn test_select() { -// let c = Condition::::TRUE; -// assert_eq!(c.select(1, 2), 1); -// assert_eq!((!c).select(1, 2), 2); -// -// // or the inverse behaviour if you start with 'false'. -// let cfalse = Condition::::FALSE; -// assert_eq!(cfalse.select(1, 2), 2); -// assert_eq!((!cfalse).select(1, 2), 1); -// } -// -// #[test] -// fn test_swap() { -// let c = Condition::::from_bool::(); -// let (lhs, rhs) = c.swap(1, 2); -// assert_eq!(lhs, 2); -// assert_eq!(rhs, 1); -// -// // or the inverse behaviour if you start with 'false'. -// let c = Condition::::from_bool::(); -// let (lhs, rhs) = c.swap(1, 2); -// assert_eq!(lhs, 1); -// assert_eq!(rhs, 2); -// } -// } +#[cfg(test)] +mod u64_tests { + use super::*; + + #[test] + fn const_tests() { + // Ensure TRUE/FALSE are correctly interpreted as boolean. + assert_eq!(Condition::::TRUE.is_true(), true); + assert_eq!(Condition::::FALSE.is_true(), false); + } + + #[test] + fn from_bool() { + // Compile-time const generics check + assert_eq!(Condition::::from_bool::().is_true(), true); + assert_eq!(Condition::::from_bool::().is_true(), false); + } + + #[test] + fn select() { + let t = Condition::::TRUE; + let f = Condition::::FALSE; + + let val1: u64 = 0xDEADBEEFCAFEBABE; + let val2: u64 = 0x0000000000000000; + + // This test is CRITICAL. + // If TRUE was defined as '1' (like i64), this would fail because 'select' relies on bitwise mask. + // It requires TRUE to be u64::MAX (all 1s) to preserve the full bits of val1. + assert_eq!(t.select(val1, val2), val1); + assert_eq!(f.select(val1, val2), val2); + + // Cross check with from_bool + let t_gen = Condition::::from_bool::(); + assert_eq!(t_gen.select(val1, val2), val1); + } + + #[test] + fn bit_ops() { + let t = Condition::::TRUE; + let f = Condition::::FALSE; + + // NOT + assert_eq!((!t).is_true(), false); + assert_eq!((!f).is_true(), true); + + // AND + assert_eq!((t & t).is_true(), true); + assert_eq!((t & f).is_true(), false); + assert_eq!((f & f).is_true(), false); + + // OR + assert_eq!((t | t).is_true(), true); + assert_eq!((t | f).is_true(), true); + assert_eq!((f | f).is_true(), false); + + // XOR + assert_eq!((t ^ t).is_true(), false); + assert_eq!((t ^ f).is_true(), true); + } +} #[cfg(test)] mod generic_impl_tests { diff --git a/crypto/utils/tests/test_utils.rs b/crypto/utils/tests/test_utils.rs index e239bbc..13fda29 100644 --- a/crypto/utils/tests/test_utils.rs +++ b/crypto/utils/tests/test_utils.rs @@ -16,7 +16,7 @@ fn test_max_min() { // Test with KeyMaterial KeyTypes assert_eq!( - *max(&KeyType::BytesLowEntropy, &KeyType::BytesFullEntropy), - KeyType::BytesFullEntropy + *max(&KeyType::Unknown, &KeyType::CryptographicRandom), + KeyType::CryptographicRandom ); } diff --git a/mem_usage_benches/bench_mldsa_mem_usage.rs b/mem_usage_benches/bench_mldsa_mem_usage.rs index 6b648fe..f8fe792 100644 --- a/mem_usage_benches/bench_mldsa_mem_usage.rs +++ b/mem_usage_benches/bench_mldsa_mem_usage.rs @@ -21,50 +21,83 @@ #![allow(unused_imports)] use bouncycastle::core::key_material::{KeyMaterial256, KeyType}; -use bouncycastle::core::traits::{Signature, SignaturePrivateKey, SignaturePublicKey}; -use bouncycastle::hex as hex; +use bouncycastle::core::traits::{SignaturePrivateKey, SignaturePublicKey, SignatureVerifier}; +use bouncycastle::hex; use bouncycastle::mldsa::MLDSA44_SIG_LEN; /// This prints the in-memory size of all the public and private key structs fn print_struct_sizes() { - use core::mem::size_of; use bouncycastle::mldsa; use bouncycastle::mldsa_lowmemory; - + use core::mem::size_of; println!("\nML-DSA-44"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); - + println!( + "size_of: {}", + size_of::() + ); println!("\nML-DSA-65"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-DSA-87"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("\n\nlowmemory"); println!("\nML-KEM-512_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); - + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-768_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-1024_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); } /// This exists so I can use /usr/bin/time to measure the base memory footprint of the cargo bench harness @@ -75,91 +108,121 @@ fn bench_do_nothing() { } fn bench_mldsa44_keygen() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA44}; + use bouncycastle::mldsa::{MLDSA44, MLDSATrait}; eprintln!("MLDSA44/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa44_lowmem_keygen() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA44}; + use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSATrait}; eprintln!("MLDSA44_lowmemory/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa65_keygen() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA65}; + use bouncycastle::mldsa::{MLDSA65, MLDSATrait}; eprintln!("MLDSA65/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa65_lowmemory_keygen() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA65}; + use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSATrait}; eprintln!("MLDSA65_lowmemory/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa87_keygen() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA87}; + use bouncycastle::mldsa::{MLDSA87, MLDSATrait}; eprintln!("MLDSA87/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa87_lowmemory_keygen() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA87}; + use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSATrait}; eprintln!("MLDSA87_lowmemory/KeyGen"); let seed = KeyMaterial256::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mldsa44_sign() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA44, MLDSA44PrivateKey, MLDSA44_SK_LEN}; + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSATrait}; eprintln!("MLDSA44/Sign"); @@ -172,7 +235,180 @@ fn bench_mldsa44_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA44PrivateKey::from_bytes(&[0xd7,0xb2,0xb4,0x72,0x54,0xaa,0xe0,0xdb,0x45,0xe7,0x93,0x0d,0x4a,0x98,0xd2,0xc9,0x7d,0x8f,0x13,0x97,0xd1,0x78,0x9d,0xaf,0xa1,0x70,0x24,0xb3,0x16,0xe9,0xbe,0xc9,0x39,0xce,0x0f,0x7f,0x77,0xf8,0xdb,0x56,0x44,0xdc,0xda,0x36,0x6b,0xfe,0x47,0x34,0xbd,0x95,0xf4,0x35,0xff,0x9a,0x61,0x3a,0xa5,0x4a,0xa4,0x1c,0x2c,0x69,0x4c,0x04,0x32,0x9a,0x07,0xb1,0xfa,0xbb,0x48,0xf5,0x2a,0x30,0x9f,0x11,0xa1,0x89,0x8f,0x84,0x8e,0x23,0x22,0xff,0xe6,0x23,0xec,0x81,0x0d,0xb3,0xbe,0xe3,0x36,0x85,0x85,0x4a,0x88,0x26,0x9d,0xa3,0x20,0xd5,0x12,0x0b,0xfc,0xfe,0x89,0xa1,0x8e,0x30,0xf7,0x11,0x4d,0x83,0xaa,0x40,0x4a,0x64,0x6b,0x6c,0x99,0x73,0x89,0x86,0x0d,0x12,0x52,0x2e,0xe0,0x00,0x6e,0x23,0x84,0x81,0x91,0x86,0x61,0x9b,0x26,0x0d,0x11,0x86,0x64,0xd4,0xa6,0x28,0x22,0x18,0x44,0x82,0x40,0x28,0x98,0x14,0x61,0x48,0xa6,0x61,0x4c,0x42,0x48,0xa1,0x92,0x08,0xc2,0x38,0x29,0x51,0x24,0x48,0x08,0xa1,0x25,0xc2,0x08,0x31,0x08,0xc4,0x71,0x20,0x14,0x09,0x14,0x83,0x6c,0x18,0xa7,0x80,0x84,0x10,0x6e,0xc9,0xc0,0x70,0x22,0xb5,0x64,0x08,0xb0,0x61,0x0c,0x07,0x04,0x98,0x12,0x44,0x51,0x88,0x69,0x59,0x00,0x46,0x22,0x93,0x20,0x41,0x06,0x2e,0x42,0xb6,0x4c,0x01,0x16,0x49,0x14,0x28,0x4c,0x41,0xa8,0x51,0x80,0x46,0x0a,0x51,0x16,0x51,0x5a,0x08,0x20,0x02,0x22,0x44,0xdc,0x98,0x49,0xd1,0x32,0x51,0xe1,0x30,0x65,0xd3,0xc0,0x85,0x92,0xa8,0x51,0x12,0xa1,0x64,0x00,0x39,0x22,0x09,0x46,0x62,0x1c,0xc7,0x0c,0xd9,0x08,0x6d,0xd0,0x06,0x26,0x52,0x40,0x85,0x80,0x44,0x30,0x91,0x06,0x2c,0x50,0xc8,0x09,0x24,0xc5,0x84,0x1a,0x96,0x6d,0x4a,0x98,0x2c,0x99,0x06,0x6d,0xa4,0x44,0x32,0x20,0xa7,0x64,0x5a,0x32,0x6e,0x11,0xb5,0x70,0x20,0x92,0x61,0x24,0x13,0x8e,0x04,0x85,0x2c,0x0a,0x48,0x72,0xc8,0xa0,0x51,0xd3,0x08,0x2a,0x99,0x20,0x80,0x58,0x24,0x20,0x24,0x07,0x4e,0x59,0x14,0x88,0x10,0xa4,0x64,0x60,0xc0,0x6d,0xe0,0xb2,0x8d,0x1b,0x19,0x09,0x20,0x34,0x22,0xc0,0x24,0x41,0x09,0x43,0x71,0x0a,0x21,0x20,0x61,0xa2,0x01,0x52,0x22,0x52,0x1b,0x80,0x80,0x9a,0x34,0x00,0x13,0x93,0x4d,0xd3,0x32,0x29,0x22,0x17,0x0a,0x98,0x92,0x69,0x1a,0x14,0x51,0x20,0x27,0x21,0x9c,0xc0,0x20,0x62,0xa2,0x81,0x48,0x18,0x69,0x1a,0x85,0x4d,0x83,0x44,0x69,0x5b,0x20,0x41,0x03,0x12,0x42,0xcb,0x18,0x46,0x01,0xa9,0x0d,0x0c,0x02,0x31,0x83,0xb0,0x21,0x5a,0x22,0x4a,0xc8,0x92,0x05,0xd9,0x90,0x69,0x04,0x30,0x6a,0x4b,0x06,0x4a,0xd2,0xb2,0x01,0x1c,0x40,0x40,0x81,0x42,0x32,0x52,0x32,0x72,0x54,0xa6,0x40,0x5a,0x18,0x10,0x0c,0x32,0x12,0x92,0xc2,0x80,0x52,0x12,0x62,0x5c,0x82,0x28,0x0b,0xb4,0x6c,0x03,0x42,0x8d,0x53,0x10,0x0c,0x14,0x01,0x0e,0xe1,0x36,0x52,0x88,0x84,0x24,0x91,0x02,0x0a,0x63,0x46,0x26,0x20,0x06,0x29,0x11,0xc2,0x28,0xd0,0x20,0x48,0x02,0xb3,0x6c,0xa2,0x36,0x09,0x5a,0x86,0x48,0xcb,0xb4,0x61,0x8b,0x46,0x62,0xc4,0x40,0x82,0x1a,0x89,0x09,0x10,0x02,0x4d,0x24,0xb2,0x45,0x20,0x12,0x25,0x24,0xc9,0x05,0x88,0x28,0x8c,0xc9,0xc0,0x4d,0x59,0x48,0x22,0x0a,0x27,0x6e,0xc1,0x34,0x64,0x4c,0x90,0x60,0x5b,0x44,0x50,0x82,0x86,0x49,0x43,0x88,0x04,0x43,0xb2,0x8c,0x60,0x30,0x80,0xa2,0x88,0x2d,0x84,0xa4,0x6d,0x8c,0xa6,0x29,0xd0,0xc6,0x84,0x42,0x06,0x46,0x89,0x88,0x51,0x00,0xa9,0x8d,0x01,0x49,0x8d,0xe4,0x38,0x0d,0xa4,0x06,0x8d,0xd3,0x94,0x71,0x42,0xb2,0x6c,0x1a,0x84,0x61,0x1b,0xa3,0x28,0x42,0xb4,0x28,0x08,0xa0,0x71,0x1a,0xc5,0x31,0xe0,0xa0,0x4c,0x01,0x37,0x65,0x24,0x28,0x62,0x14,0x28,0x90,0x09,0x10,0x61,0xd9,0x40,0x22,0x1b,0x33,0x60,0x09,0x02,0x92,0xd0,0x24,0x81,0x20,0x04,0x08,0x49,0x18,0x44,0xa3,0x22,0x2d,0x5c,0x88,0x44,0x14,0x98,0x08,0xa4,0x46,0x61,0x01,0x95,0x64,0x0b,0x39,0x0a,0x0c,0x94,0x50,0xca,0x40,0x6a,0xd2,0xb2,0x20,0xc0,0x38,0x01,0x82,0x30,0x8e,0x13,0xb9,0x08,0x91,0x80,0x84,0x14,0x88,0x29,0xc0,0x18,0x91,0x12,0x35,0x0d,0xa0,0x24,0x22,0xe2,0x04,0x06,0xd9,0xc2,0x85,0x04,0x28,0x12,0x1c,0xc9,0x89,0x18,0x02,0x72,0xd2,0x40,0x29,0xc2,0x08,0x12,0xd8,0x06,0x2a,0x99,0x94,0x71,0x9b,0xb8,0x68,0x23,0x84,0x29,0x1a,0x22,0x89,0x14,0x45,0x11,0xdc,0x82,0x44,0x50,0x96,0x45,0x0c,0x44,0x84,0xc0,0xb2,0x04,0x9a,0xa6,0x05,0x43,0x86,0x2c,0x44,0x32,0x6e,0x88,0x44,0x21,0x20,0xa8,0x4c,0x9a,0x30,0x70,0xe3,0xb8,0x2d,0x63,0x26,0x88,0x03,0x25,0x49,0x03,0x43,0x8c,0x48,0xa8,0x09,0xca,0x14,0x72,0x53,0x34,0x4e,0x12,0x43,0x08,0x1b,0xa7,0x04,0x59,0x30,0x22,0xd9,0x94,0x80,0xe2,0x34,0x22,0x81,0x42,0x12,0x9c,0x30,0x2a,0x94,0x34,0x26,0x61,0x04,0x45,0x24,0x26,0x28,0x13,0x46,0x09,0x4a,0x32,0x6d,0x11,0x28,0x09,0x18,0xb8,0x25,0x62,0x28,0x11,0x13,0x41,0x0d,0x41,0xb2,0x11,0x90,0x84,0x4c,0x8b,0x12,0x12,0xa2,0xc6,0x88,0xc9,0xc0,0x30,0x22,0x06,0x06,0xd2,0x18,0x8e,0x84,0x86,0x30,0x90,0x44,0x52,0x12,0x88,0x31,0xd9,0x20,0x71,0x13,0xc5,0x28,0x43,0x06,0x0e,0x03,0x30,0x60,0xcc,0xa6,0x84,0x58,0x26,0x52,0x4c,0x88,0x01,0x1e,0xf7,0x25,0x62,0xc8,0x5f,0xfa,0x43,0xac,0xfa,0x49,0x21,0x7f,0x2b,0x17,0x2d,0x7b,0xbc,0x14,0x62,0x0e,0x6d,0x98,0x0a,0x71,0xaa,0xbb,0xdf,0x0c,0x45,0xe9,0xa2,0x06,0xec,0xb1,0x42,0x3f,0xee,0x15,0xde,0xcc,0x17,0x60,0x13,0x00,0x14,0x9d,0x92,0x23,0xcd,0x6e,0x6c,0x6e,0x1f,0xa8,0xe4,0x1f,0xc7,0xc6,0x49,0x38,0xab,0x68,0x90,0x5f,0xd3,0xdc,0xda,0x50,0xd8,0x70,0x82,0xe7,0xd0,0xd7,0x1d,0x1b,0xc9,0xb2,0xb8,0x4c,0x85,0x52,0x3c,0xa8,0xfe,0x6c,0xad,0x29,0x4a,0xdf,0x83,0xbe,0x15,0xb1,0x08,0xff,0x72,0x1d,0x0c,0xc8,0x7b,0xc3,0xdd,0x3a,0x75,0x90,0x18,0x4b,0x0e,0x84,0x56,0x63,0xa9,0x1f,0xc9,0xe1,0xc3,0xc5,0x3a,0x61,0xd8,0x67,0x42,0x0b,0x04,0xf0,0x92,0x35,0x57,0x53,0xbc,0x65,0xa0,0x63,0x68,0xfd,0x41,0x29,0x5f,0xd0,0x99,0x24,0x13,0x2c,0x6f,0x91,0xf6,0x79,0x64,0xc1,0x42,0x67,0x4a,0x72,0x5c,0x34,0x39,0x14,0xc4,0xce,0xcf,0x58,0xc0,0x74,0xbc,0xaf,0x45,0x58,0xc9,0x7b,0xf7,0x91,0x1e,0x07,0xaa,0x6d,0x09,0x38,0xf2,0xee,0x2b,0xb3,0xc1,0xa8,0xc5,0x95,0xd6,0x35,0xe8,0x43,0x42,0xfd,0xea,0x01,0xdc,0x24,0xb2,0x11,0xad,0x2f,0xc2,0x81,0xcf,0x77,0xe5,0x91,0x10,0xc7,0xab,0xc5,0x4b,0xf0,0xc8,0x6d,0x48,0x0b,0x9b,0xe2,0x76,0x47,0x1d,0xc9,0xd6,0x03,0xce,0xe9,0x8c,0xfd,0xab,0x3e,0x9f,0xcf,0xb7,0x03,0x79,0x35,0x60,0x54,0x9e,0xa4,0x45,0x0f,0xa7,0xb3,0x3f,0xb9,0x16,0x9c,0x44,0xb4,0xd2,0x5f,0xb9,0xc4,0x57,0xf4,0x97,0x91,0xcd,0x3d,0xa0,0x3e,0xac,0x96,0x09,0x58,0x13,0xc1,0x05,0x13,0x2c,0xcd,0xa4,0xe6,0x3e,0x49,0x22,0x8c,0xd2,0x3d,0x8a,0x1f,0x37,0x85,0x6f,0x14,0x2d,0x93,0xb9,0x0d,0xb0,0x9f,0x82,0xaf,0x89,0x25,0x8c,0x63,0xaa,0xb8,0x04,0x7a,0x80,0xc0,0x36,0xc9,0x35,0x7e,0xa2,0x04,0x6f,0x8d,0xc6,0x35,0x4f,0x0c,0x52,0x95,0xf3,0x42,0xbb,0x41,0x7d,0x3c,0xfe,0xb0,0xb1,0xfd,0x33,0x62,0x2c,0x29,0xe1,0x4c,0xbb,0xd9,0x2e,0x13,0x63,0xc6,0x5e,0xbd,0x45,0x04,0xb7,0x51,0x23,0x29,0xb9,0x67,0x0e,0x32,0xe1,0xb2,0xc6,0x7a,0x54,0xe7,0xf1,0xa5,0x5f,0x8b,0x9f,0x9e,0xa0,0x4e,0x8c,0xa3,0xa7,0x05,0xe6,0x2a,0x3c,0x5e,0x63,0x73,0x74,0xaf,0xb7,0xae,0xb6,0xdd,0xea,0x61,0x2c,0xde,0x28,0xf0,0x1a,0x20,0x2d,0x7a,0xa4,0xe3,0x47,0x22,0xd2,0x7d,0xd3,0xf9,0xb8,0x98,0x94,0xd0,0x19,0xfd,0x5d,0x4d,0x71,0x19,0xef,0xe3,0x72,0x3b,0xba,0x10,0x4c,0xb8,0xbb,0x09,0x81,0xe0,0x74,0xde,0x3a,0xfe,0x20,0x0d,0xaa,0xae,0xad,0x82,0x6c,0xc4,0x5f,0x24,0x4d,0xbf,0x43,0x1a,0xfa,0xb3,0x4e,0xfb,0xdf,0x78,0x24,0x74,0xd2,0xfd,0x57,0x11,0x8f,0x64,0x62,0x14,0x93,0x4e,0xd9,0x9c,0xba,0x3b,0x00,0x3e,0x8d,0x67,0xa3,0x83,0x6f,0x6f,0x19,0xfc,0x41,0x91,0x0c,0xe5,0x16,0x3e,0xe3,0xae,0x99,0xeb,0x84,0xd5,0x14,0xeb,0x76,0x1e,0x63,0x68,0x4e,0xa5,0x6f,0x97,0x91,0xd2,0xdd,0x4a,0xac,0x6e,0x61,0x68,0xb9,0x48,0xc8,0x17,0xf7,0x5a,0x22,0x2a,0xcb,0x0e,0x8c,0xdc,0x03,0xcc,0x4a,0xfe,0x8f,0x67,0x15,0x7e,0x1a,0x36,0x3b,0x7f,0xae,0xff,0x9f,0x17,0x2b,0x98,0x91,0x36,0x77,0xc5,0xa1,0xdd,0x08,0x5e,0x9e,0xe4,0xc2,0x20,0x52,0xc1,0xaf,0x58,0x19,0x31,0x16,0x67,0x3d,0xcd,0x3b,0xfc,0x5f,0x34,0xb8,0x55,0xdc,0xc6,0xc7,0x78,0x85,0x64,0x9e,0x9e,0x71,0xf4,0x3d,0x4a,0xea,0x0f,0x4b,0x72,0xca,0x7e,0xda,0x05,0x78,0xba,0x13,0xd3,0x1a,0x65,0x8d,0x2d,0x06,0x0a,0x9a,0x66,0xff,0x69,0xed,0x1b,0xe7,0x99,0x7a,0x2f,0xb1,0xd2,0x72,0x3d,0x38,0xf9,0xbf,0xab,0xe1,0x8f,0x8e,0x7b,0x3c,0xda,0x90,0x6e,0x4e,0x9b,0x5e,0x94,0x2c,0x8e,0xae,0xb2,0x96,0x07,0x0e,0xbf,0xd3,0x64,0x94,0x7a,0x94,0x0c,0xc9,0x78,0xbe,0xd6,0x6b,0x37,0x74,0x9e,0x6d,0x5d,0xcd,0x7b,0xe8,0xc4,0x94,0x44,0x0e,0x2b,0x84,0xce,0xcf,0xef,0xb9,0x8c,0x0b,0xed,0xfb,0x3c,0x41,0xe3,0x35,0x9d,0x2c,0xd7,0x19,0x7f,0xbe,0x72,0x0c,0x48,0xaa,0x6c,0x6b,0x64,0x65,0xc1,0xee,0x63,0xe3,0x56,0x9c,0x2a,0xdc,0x74,0x44,0x91,0x37,0x0b,0x7f,0x78,0x26,0xfe,0x0b,0x77,0xa1,0xd1,0x9d,0x64,0x10,0x1d,0x03,0x2b,0x91,0x81,0x06,0xb4,0x2d,0x2e,0xf7,0x37,0x47,0xe5,0x60,0x1f,0xe4,0xba,0x50,0xf2,0x3e,0xde,0x52,0x1f,0x03,0x1a,0x81,0x7d,0x15,0x29,0x4a,0x43,0x72,0x2e,0x83,0x78,0x78,0x4b,0x6d,0xb0,0xcf,0x1b,0xa9,0xe8,0xae,0x91,0x1d,0x92,0x01,0xb9,0xce,0x9c,0xc3,0x01,0x9c,0x6f,0x5c,0x27,0xcb,0x98,0xda,0x26,0x14,0x4b,0x64,0x22,0x5a,0x7c,0x93,0x2b,0x30,0xf7,0x61,0xe7,0x8a,0x2d,0x59,0xa1,0xd8,0xb8,0x3e,0xc6,0x34,0x4a,0x2f,0x6d,0xd4,0x7e,0x76,0x57,0x06,0xd0,0x0b,0xf4,0xa7,0x9a,0x6a,0x92,0x6c,0x3b,0xa9,0x1d,0x81,0x2c,0x8f,0x2c,0x79,0x7a,0xb1,0x79,0x67,0x09,0xe5,0xd1,0x68,0x56,0x77,0x82,0x93,0x52,0x9f,0x02,0x86,0xd0,0x15,0xc3,0xb5,0x39,0x96,0x19,0x64,0x2a,0x33,0x3e,0x9e,0x59,0x3d,0x6e,0x3f,0x53,0x53,0x99,0x42,0x08,0xe9,0xe6,0xa3,0x32,0x85,0x1d,0x7f,0x65,0x25,0x22,0xa9,0x28,0xb9,0x17,0xe2,0x7e,0x2d,0x6d,0x42,0x13,0x7d,0xfe,0x2e,0xbf,0xa6,0xfb,0x1c,0x67,0xb2,0x6c,0x02,0x54,0x52,0x86,0x85,0xf7,0xeb,0xdb,0xe3,0x15,0xa6,0x8e,0xaa,0x2d,0xa7,0x69,0xe8,0xa9,0xf4,0x2d,0x3e,0x60,0x00,0x7c,0x71,0x33,0x09,0x26,0xb2,0xc0,0x01,0x2d,0x83,0xea,0xd4,0xe4,0xfd,0x1e,0xd8,0x72,0xcc,0xd1,0x97,0x22,0x01,0xd2,0xb0,0x27,0xf3,0x54,0x5a,0xc2,0xd3,0x0c,0xd7,0x8b,0xc1,0xd7,0x40,0xfe,0xcc,0xbc,0x6f,0xc2,0xa0,0x44,0x6c,0x6e,0x30,0xea,0xc5,0x1f,0x5a,0x69,0x09,0x8a,0xa2,0xd4,0x47,0xf2,0x08,0x5b,0x4e,0x4e,0x4b,0x92,0xcc,0xc2,0x69,0x21,0xd2,0xde,0x47,0x85,0x18,0xcd,0x09,0x0c,0xe2,0x67,0xae,0xa2,0xd2,0x7a,0xda,0x57,0xfd,0x88,0xb4,0x97,0x6d,0x89,0xfb,0x84,0x3c,0xdc,0xcf,0x49,0xa7,0x6c,0xa2,0x67,0x9e,0x68,0x01,0xbf,0xa7,0xfb,0x03,0x18,0x96,0xfb,0x50,0x62,0x97,0x04,0xb9,0x92,0x39,0x36,0xbb,0x5d,0xd3,0x85,0x31,0x11,0x21,0xca,0xdf,0xb1,0x19,0x95,0xe5,0x9b,0x73,0x03,0x4c,0xf6,0x7e,0xd0,0x3a,0xb8,0x13,0x86,0x76,0x48,0xd0,0x25,0x82,0x80,0x87,0xe9,0x49,0xa9,0xaf,0xd1,0x6b,0x95,0xd7,0x2d,0x99,0xb1,0xed,0xca,0x25,0x7a,0xac,0x13,0x2f,0xfb,0x7a,0x07,0x09,0xae,0xd5,0xa9,0xc0,0xff,0x05,0xfb,0x0f,0x2b,0xbf,0x28,0x40,0x9e,0xed,0x7b,0x5f,0x58,0x01,0xbe,0x96,0x4c,0xed,0x01,0x9e,0x1c,0xb7,0x85,0x1d,0x38,0x51,0xf1,0x02,0x90,0x67,0x4e,0x19,0xff,0xb0,0x08,0xb3,0x01,0xc4,0xac,0xf6,0x41,0xa2,0xbb,0x14,0x21,0x6e,0x1d,0x69,0xca,0xbf,0x52,0xb5,0xef,0x22,0x74,0x96,0xb0,0xf3,0x07,0x99,0xa8,0x55,0xd1,0x17,0xfa,0xd3,0x74,0x4a,0x6f,0xa3,0x35,0x03,0xea,0x79,0x8b,0x52,0xdd,0xd7,0xee,0x54,0x26,0x60,0x9d,0xbf,0xcd,0x3f,0x0c,0x13,0xb1,0x64,0xd6,0xc0,0x51,0xf7,0xed,0x4a,0x11,0x97,0x19,0xa7,0x12,0xe3,0x88,0xd3,0x28,0x40,0x20,0x81,0xff,0x13,0x54,0xb5,0x54,0xd2,0xc2,0x37,0xaf,0xed,0x3b,0x15,0x1c,0x4b,0xa8,0xe9,0xf4,0xbd,0xeb,0x84,0x99,0xa3,0x06,0x6e,0x26,0xbb,0xc6,0x9e,0x8a,0xf0,0x89,0xde,0xc7,0x17,0x31,0xd1,0xdc,0x52,0x9e,0xab,0x17,0xef,0x73,0x74,0x73,0x4c,0x0f,0xe4,0x75,0x49,0x4c,0x83,0x83,0x6b,0xdd,0x34,0xa0,0x3b,0x9b,0xc8,0x99,0x14,0x71,0x60,0x61,0xbf,0xb9,0x8e,0xc6,0xe6,0x1c,0x3e,0xd4,0x43,0x8e,0xdc,0xaf,0x25,0x24,0x3c,0x64,0x70,0x86,0xb9,0xea,0x70,0x18,0xb0,0xd9,0xa8,0xa0,0xb0,0x0c,0xec,0xb0,0x0a,0xbd,0xe2,0x49,0x8d,0x69,0xc2,0x33,0x61,0x01,0xa7,0x72,0xcb,0xe4,0xf5,0x71,0x52,0x3f,0x51,0xbd,0x05,0x88,0x2c,0xdf,0x35,0x8b,0x84,0x9c,0xc1,0x40,0xaa,0x1f,0xaf,0x22,0x42,0x3a,0x12,0x85,0x1c,0xe0,0xe3,0x3f,0xd4,0x89,0x75,0xa4,0x95,0x9f,0xa5,0xc5,0xfe,0x41,0x8c,0x93,0x90,0x81,0x91,0xab,0x6e,0x74,0x1b,0x77,0xbf,0xe0,0x2c,0xbd,0x69,0x8e,0xe7,0x95,0xc4,0x66,0xd6,0x15,0x61,0x9e,0x64,0x41,0x38,0x2c,0x6e,0xac,0x01,0x83,0x4e,0xe9,0xab,0x73,0xce,0xa8,0x0b,0xbe,0x23,0x5c,0x78,0xda,0x91,0xbd,0x79,0xb6,0xf8,0x2f,0x89,0x97,0x85,0xd6,0x87,0x00,0xd3,0x93,0xe6,0x75,0xc2,0x22,0x4d,0x6b,0x7a,0x1a,0xd2,0x13,0x20,0x49,0x56,0x79,0xad,0xae,0xd7,0x01,0x67,0xb5,0x08,0x66,0x71,0x3a,0x53,0x10,0x9d,0xb7,0xb6,0xf7,0xd8,0x13,0x04,0xec,0xdf,0xd8,0x3b,0x31,0x9b,0x1e,0xf2,0x48,0x30,0x6b,0x45,0xad,0x29,0xe7,0xdd,0xcc,0x86,0x3d,0xac,0x56,0x04,0x8b,0x5d,0x69,0xea,0x17,0x50,0x11,0xf7,0x61,0x4c,0x00,0xa8,0x6a,0x86,0x3c,0xde,0x18,0x72,0xa8,0x93,0x28,0x78,0xb9,0xac,0x7e,0x1a,0xc5,0xbd,0xa4,0x99,0x7b,0x72,0x06,0x4f,0x0c,0xd7,0x5f,0x4c,0x81,0x4e,0x03,0x4d,0xe1,0x1a,0xcb,0x90,0x13,0xcf,0x7e,0xa9,0x26,0xb4,0xe7,0xea,0xac,0xe0,0x70,0xc7,0xba,0x21,0x88,0xef,0xad,0x2e,0x43,0x1e,0x12,0x23,0xd4,0x5d,0xd0,0x5c,0x4d,0x84,0x03,0xc2,0xe4,0x5c,0xee,0x64,0x13,0xec,0xbe,0x75,0x27,0xe8,0x73,0xe4,0x55,0xc4,0xe6,0x10,0xa6,0x18,0x39,0xaa,0xcc,0x0b,0xd5,0x6d,0x24,0x83,0xe7,0x8f,0x29,0x8b,0x66,0xa4,0x78,0xeb,0x2f,0x55,0x8c,0xba,0xfc,0xa8,0x6b,0xe8,0x47,0xba,0xeb,0x02,0xc5,0xb2,0x16,0xc8,0xcd,0x88,0xfe,0xa4,0xdf,0x24,0x9b,0x09,0xe6,0x70,0xa2,0x07,0x03,0xab,0xac,0x24,0xb0,0xa9,0x1a,0xbc,0x4a,0x56,0x46,0x60,0x14,0x42,0xba,0x10,0xbe,0xcf,0xd3,0x09,0x93,0x88,0x00,0x51,0xd0,0x7f,0x56,0xa0,0x5a,0x93,0x79,0xe7,0xa8,0xe6,0xbe,0xfe,0xe3,0xf2,0x2f,0xaa,0x10,0x63,0x98,0xf7,0x70,0x60,0x06,0xe4,0x2e,0x9b,0xe1,0xef,0x89,0xd2,0x5c,0x27,0x2f,0x11,0xa9,0x50,0x95,0xc5,0x87,0xd7,0x13,0x73,0x22,0x84,0xde,0x9d,0xbd,0x3c,0x72,0x17,0xb0,0x68,0x9e,0x21,0xd8,0xeb,0x0f,0xf6,0x96,0x68]).unwrap(); + let sk = MLDSA44PrivateKey::from_bytes(&[ + 0xD7, 0xB2, 0xB4, 0x72, 0x54, 0xAA, 0xE0, 0xDB, 0x45, 0xE7, 0x93, 0x0D, 0x4A, 0x98, 0xD2, + 0xC9, 0x7D, 0x8F, 0x13, 0x97, 0xD1, 0x78, 0x9D, 0xAF, 0xA1, 0x70, 0x24, 0xB3, 0x16, 0xE9, + 0xBE, 0xC9, 0x39, 0xCE, 0x0F, 0x7F, 0x77, 0xF8, 0xDB, 0x56, 0x44, 0xDC, 0xDA, 0x36, 0x6B, + 0xFE, 0x47, 0x34, 0xBD, 0x95, 0xF4, 0x35, 0xFF, 0x9A, 0x61, 0x3A, 0xA5, 0x4A, 0xA4, 0x1C, + 0x2C, 0x69, 0x4C, 0x04, 0x32, 0x9A, 0x07, 0xB1, 0xFA, 0xBB, 0x48, 0xF5, 0x2A, 0x30, 0x9F, + 0x11, 0xA1, 0x89, 0x8F, 0x84, 0x8E, 0x23, 0x22, 0xFF, 0xE6, 0x23, 0xEC, 0x81, 0x0D, 0xB3, + 0xBE, 0xE3, 0x36, 0x85, 0x85, 0x4A, 0x88, 0x26, 0x9D, 0xA3, 0x20, 0xD5, 0x12, 0x0B, 0xFC, + 0xFE, 0x89, 0xA1, 0x8E, 0x30, 0xF7, 0x11, 0x4D, 0x83, 0xAA, 0x40, 0x4A, 0x64, 0x6B, 0x6C, + 0x99, 0x73, 0x89, 0x86, 0x0D, 0x12, 0x52, 0x2E, 0xE0, 0x00, 0x6E, 0x23, 0x84, 0x81, 0x91, + 0x86, 0x61, 0x9B, 0x26, 0x0D, 0x11, 0x86, 0x64, 0xD4, 0xA6, 0x28, 0x22, 0x18, 0x44, 0x82, + 0x40, 0x28, 0x98, 0x14, 0x61, 0x48, 0xA6, 0x61, 0x4C, 0x42, 0x48, 0xA1, 0x92, 0x08, 0xC2, + 0x38, 0x29, 0x51, 0x24, 0x48, 0x08, 0xA1, 0x25, 0xC2, 0x08, 0x31, 0x08, 0xC4, 0x71, 0x20, + 0x14, 0x09, 0x14, 0x83, 0x6C, 0x18, 0xA7, 0x80, 0x84, 0x10, 0x6E, 0xC9, 0xC0, 0x70, 0x22, + 0xB5, 0x64, 0x08, 0xB0, 0x61, 0x0C, 0x07, 0x04, 0x98, 0x12, 0x44, 0x51, 0x88, 0x69, 0x59, + 0x00, 0x46, 0x22, 0x93, 0x20, 0x41, 0x06, 0x2E, 0x42, 0xB6, 0x4C, 0x01, 0x16, 0x49, 0x14, + 0x28, 0x4C, 0x41, 0xA8, 0x51, 0x80, 0x46, 0x0A, 0x51, 0x16, 0x51, 0x5A, 0x08, 0x20, 0x02, + 0x22, 0x44, 0xDC, 0x98, 0x49, 0xD1, 0x32, 0x51, 0xE1, 0x30, 0x65, 0xD3, 0xC0, 0x85, 0x92, + 0xA8, 0x51, 0x12, 0xA1, 0x64, 0x00, 0x39, 0x22, 0x09, 0x46, 0x62, 0x1C, 0xC7, 0x0C, 0xD9, + 0x08, 0x6D, 0xD0, 0x06, 0x26, 0x52, 0x40, 0x85, 0x80, 0x44, 0x30, 0x91, 0x06, 0x2C, 0x50, + 0xC8, 0x09, 0x24, 0xC5, 0x84, 0x1A, 0x96, 0x6D, 0x4A, 0x98, 0x2C, 0x99, 0x06, 0x6D, 0xA4, + 0x44, 0x32, 0x20, 0xA7, 0x64, 0x5A, 0x32, 0x6E, 0x11, 0xB5, 0x70, 0x20, 0x92, 0x61, 0x24, + 0x13, 0x8E, 0x04, 0x85, 0x2C, 0x0A, 0x48, 0x72, 0xC8, 0xA0, 0x51, 0xD3, 0x08, 0x2A, 0x99, + 0x20, 0x80, 0x58, 0x24, 0x20, 0x24, 0x07, 0x4E, 0x59, 0x14, 0x88, 0x10, 0xA4, 0x64, 0x60, + 0xC0, 0x6D, 0xE0, 0xB2, 0x8D, 0x1B, 0x19, 0x09, 0x20, 0x34, 0x22, 0xC0, 0x24, 0x41, 0x09, + 0x43, 0x71, 0x0A, 0x21, 0x20, 0x61, 0xA2, 0x01, 0x52, 0x22, 0x52, 0x1B, 0x80, 0x80, 0x9A, + 0x34, 0x00, 0x13, 0x93, 0x4D, 0xD3, 0x32, 0x29, 0x22, 0x17, 0x0A, 0x98, 0x92, 0x69, 0x1A, + 0x14, 0x51, 0x20, 0x27, 0x21, 0x9C, 0xC0, 0x20, 0x62, 0xA2, 0x81, 0x48, 0x18, 0x69, 0x1A, + 0x85, 0x4D, 0x83, 0x44, 0x69, 0x5B, 0x20, 0x41, 0x03, 0x12, 0x42, 0xCB, 0x18, 0x46, 0x01, + 0xA9, 0x0D, 0x0C, 0x02, 0x31, 0x83, 0xB0, 0x21, 0x5A, 0x22, 0x4A, 0xC8, 0x92, 0x05, 0xD9, + 0x90, 0x69, 0x04, 0x30, 0x6A, 0x4B, 0x06, 0x4A, 0xD2, 0xB2, 0x01, 0x1C, 0x40, 0x40, 0x81, + 0x42, 0x32, 0x52, 0x32, 0x72, 0x54, 0xA6, 0x40, 0x5A, 0x18, 0x10, 0x0C, 0x32, 0x12, 0x92, + 0xC2, 0x80, 0x52, 0x12, 0x62, 0x5C, 0x82, 0x28, 0x0B, 0xB4, 0x6C, 0x03, 0x42, 0x8D, 0x53, + 0x10, 0x0C, 0x14, 0x01, 0x0E, 0xE1, 0x36, 0x52, 0x88, 0x84, 0x24, 0x91, 0x02, 0x0A, 0x63, + 0x46, 0x26, 0x20, 0x06, 0x29, 0x11, 0xC2, 0x28, 0xD0, 0x20, 0x48, 0x02, 0xB3, 0x6C, 0xA2, + 0x36, 0x09, 0x5A, 0x86, 0x48, 0xCB, 0xB4, 0x61, 0x8B, 0x46, 0x62, 0xC4, 0x40, 0x82, 0x1A, + 0x89, 0x09, 0x10, 0x02, 0x4D, 0x24, 0xB2, 0x45, 0x20, 0x12, 0x25, 0x24, 0xC9, 0x05, 0x88, + 0x28, 0x8C, 0xC9, 0xC0, 0x4D, 0x59, 0x48, 0x22, 0x0A, 0x27, 0x6E, 0xC1, 0x34, 0x64, 0x4C, + 0x90, 0x60, 0x5B, 0x44, 0x50, 0x82, 0x86, 0x49, 0x43, 0x88, 0x04, 0x43, 0xB2, 0x8C, 0x60, + 0x30, 0x80, 0xA2, 0x88, 0x2D, 0x84, 0xA4, 0x6D, 0x8C, 0xA6, 0x29, 0xD0, 0xC6, 0x84, 0x42, + 0x06, 0x46, 0x89, 0x88, 0x51, 0x00, 0xA9, 0x8D, 0x01, 0x49, 0x8D, 0xE4, 0x38, 0x0D, 0xA4, + 0x06, 0x8D, 0xD3, 0x94, 0x71, 0x42, 0xB2, 0x6C, 0x1A, 0x84, 0x61, 0x1B, 0xA3, 0x28, 0x42, + 0xB4, 0x28, 0x08, 0xA0, 0x71, 0x1A, 0xC5, 0x31, 0xE0, 0xA0, 0x4C, 0x01, 0x37, 0x65, 0x24, + 0x28, 0x62, 0x14, 0x28, 0x90, 0x09, 0x10, 0x61, 0xD9, 0x40, 0x22, 0x1B, 0x33, 0x60, 0x09, + 0x02, 0x92, 0xD0, 0x24, 0x81, 0x20, 0x04, 0x08, 0x49, 0x18, 0x44, 0xA3, 0x22, 0x2D, 0x5C, + 0x88, 0x44, 0x14, 0x98, 0x08, 0xA4, 0x46, 0x61, 0x01, 0x95, 0x64, 0x0B, 0x39, 0x0A, 0x0C, + 0x94, 0x50, 0xCA, 0x40, 0x6A, 0xD2, 0xB2, 0x20, 0xC0, 0x38, 0x01, 0x82, 0x30, 0x8E, 0x13, + 0xB9, 0x08, 0x91, 0x80, 0x84, 0x14, 0x88, 0x29, 0xC0, 0x18, 0x91, 0x12, 0x35, 0x0D, 0xA0, + 0x24, 0x22, 0xE2, 0x04, 0x06, 0xD9, 0xC2, 0x85, 0x04, 0x28, 0x12, 0x1C, 0xC9, 0x89, 0x18, + 0x02, 0x72, 0xD2, 0x40, 0x29, 0xC2, 0x08, 0x12, 0xD8, 0x06, 0x2A, 0x99, 0x94, 0x71, 0x9B, + 0xB8, 0x68, 0x23, 0x84, 0x29, 0x1A, 0x22, 0x89, 0x14, 0x45, 0x11, 0xDC, 0x82, 0x44, 0x50, + 0x96, 0x45, 0x0C, 0x44, 0x84, 0xC0, 0xB2, 0x04, 0x9A, 0xA6, 0x05, 0x43, 0x86, 0x2C, 0x44, + 0x32, 0x6E, 0x88, 0x44, 0x21, 0x20, 0xA8, 0x4C, 0x9A, 0x30, 0x70, 0xE3, 0xB8, 0x2D, 0x63, + 0x26, 0x88, 0x03, 0x25, 0x49, 0x03, 0x43, 0x8C, 0x48, 0xA8, 0x09, 0xCA, 0x14, 0x72, 0x53, + 0x34, 0x4E, 0x12, 0x43, 0x08, 0x1B, 0xA7, 0x04, 0x59, 0x30, 0x22, 0xD9, 0x94, 0x80, 0xE2, + 0x34, 0x22, 0x81, 0x42, 0x12, 0x9C, 0x30, 0x2A, 0x94, 0x34, 0x26, 0x61, 0x04, 0x45, 0x24, + 0x26, 0x28, 0x13, 0x46, 0x09, 0x4A, 0x32, 0x6D, 0x11, 0x28, 0x09, 0x18, 0xB8, 0x25, 0x62, + 0x28, 0x11, 0x13, 0x41, 0x0D, 0x41, 0xB2, 0x11, 0x90, 0x84, 0x4C, 0x8B, 0x12, 0x12, 0xA2, + 0xC6, 0x88, 0xC9, 0xC0, 0x30, 0x22, 0x06, 0x06, 0xD2, 0x18, 0x8E, 0x84, 0x86, 0x30, 0x90, + 0x44, 0x52, 0x12, 0x88, 0x31, 0xD9, 0x20, 0x71, 0x13, 0xC5, 0x28, 0x43, 0x06, 0x0E, 0x03, + 0x30, 0x60, 0xCC, 0xA6, 0x84, 0x58, 0x26, 0x52, 0x4C, 0x88, 0x01, 0x1E, 0xF7, 0x25, 0x62, + 0xC8, 0x5F, 0xFA, 0x43, 0xAC, 0xFA, 0x49, 0x21, 0x7F, 0x2B, 0x17, 0x2D, 0x7B, 0xBC, 0x14, + 0x62, 0x0E, 0x6D, 0x98, 0x0A, 0x71, 0xAA, 0xBB, 0xDF, 0x0C, 0x45, 0xE9, 0xA2, 0x06, 0xEC, + 0xB1, 0x42, 0x3F, 0xEE, 0x15, 0xDE, 0xCC, 0x17, 0x60, 0x13, 0x00, 0x14, 0x9D, 0x92, 0x23, + 0xCD, 0x6E, 0x6C, 0x6E, 0x1F, 0xA8, 0xE4, 0x1F, 0xC7, 0xC6, 0x49, 0x38, 0xAB, 0x68, 0x90, + 0x5F, 0xD3, 0xDC, 0xDA, 0x50, 0xD8, 0x70, 0x82, 0xE7, 0xD0, 0xD7, 0x1D, 0x1B, 0xC9, 0xB2, + 0xB8, 0x4C, 0x85, 0x52, 0x3C, 0xA8, 0xFE, 0x6C, 0xAD, 0x29, 0x4A, 0xDF, 0x83, 0xBE, 0x15, + 0xB1, 0x08, 0xFF, 0x72, 0x1D, 0x0C, 0xC8, 0x7B, 0xC3, 0xDD, 0x3A, 0x75, 0x90, 0x18, 0x4B, + 0x0E, 0x84, 0x56, 0x63, 0xA9, 0x1F, 0xC9, 0xE1, 0xC3, 0xC5, 0x3A, 0x61, 0xD8, 0x67, 0x42, + 0x0B, 0x04, 0xF0, 0x92, 0x35, 0x57, 0x53, 0xBC, 0x65, 0xA0, 0x63, 0x68, 0xFD, 0x41, 0x29, + 0x5F, 0xD0, 0x99, 0x24, 0x13, 0x2C, 0x6F, 0x91, 0xF6, 0x79, 0x64, 0xC1, 0x42, 0x67, 0x4A, + 0x72, 0x5C, 0x34, 0x39, 0x14, 0xC4, 0xCE, 0xCF, 0x58, 0xC0, 0x74, 0xBC, 0xAF, 0x45, 0x58, + 0xC9, 0x7B, 0xF7, 0x91, 0x1E, 0x07, 0xAA, 0x6D, 0x09, 0x38, 0xF2, 0xEE, 0x2B, 0xB3, 0xC1, + 0xA8, 0xC5, 0x95, 0xD6, 0x35, 0xE8, 0x43, 0x42, 0xFD, 0xEA, 0x01, 0xDC, 0x24, 0xB2, 0x11, + 0xAD, 0x2F, 0xC2, 0x81, 0xCF, 0x77, 0xE5, 0x91, 0x10, 0xC7, 0xAB, 0xC5, 0x4B, 0xF0, 0xC8, + 0x6D, 0x48, 0x0B, 0x9B, 0xE2, 0x76, 0x47, 0x1D, 0xC9, 0xD6, 0x03, 0xCE, 0xE9, 0x8C, 0xFD, + 0xAB, 0x3E, 0x9F, 0xCF, 0xB7, 0x03, 0x79, 0x35, 0x60, 0x54, 0x9E, 0xA4, 0x45, 0x0F, 0xA7, + 0xB3, 0x3F, 0xB9, 0x16, 0x9C, 0x44, 0xB4, 0xD2, 0x5F, 0xB9, 0xC4, 0x57, 0xF4, 0x97, 0x91, + 0xCD, 0x3D, 0xA0, 0x3E, 0xAC, 0x96, 0x09, 0x58, 0x13, 0xC1, 0x05, 0x13, 0x2C, 0xCD, 0xA4, + 0xE6, 0x3E, 0x49, 0x22, 0x8C, 0xD2, 0x3D, 0x8A, 0x1F, 0x37, 0x85, 0x6F, 0x14, 0x2D, 0x93, + 0xB9, 0x0D, 0xB0, 0x9F, 0x82, 0xAF, 0x89, 0x25, 0x8C, 0x63, 0xAA, 0xB8, 0x04, 0x7A, 0x80, + 0xC0, 0x36, 0xC9, 0x35, 0x7E, 0xA2, 0x04, 0x6F, 0x8D, 0xC6, 0x35, 0x4F, 0x0C, 0x52, 0x95, + 0xF3, 0x42, 0xBB, 0x41, 0x7D, 0x3C, 0xFE, 0xB0, 0xB1, 0xFD, 0x33, 0x62, 0x2C, 0x29, 0xE1, + 0x4C, 0xBB, 0xD9, 0x2E, 0x13, 0x63, 0xC6, 0x5E, 0xBD, 0x45, 0x04, 0xB7, 0x51, 0x23, 0x29, + 0xB9, 0x67, 0x0E, 0x32, 0xE1, 0xB2, 0xC6, 0x7A, 0x54, 0xE7, 0xF1, 0xA5, 0x5F, 0x8B, 0x9F, + 0x9E, 0xA0, 0x4E, 0x8C, 0xA3, 0xA7, 0x05, 0xE6, 0x2A, 0x3C, 0x5E, 0x63, 0x73, 0x74, 0xAF, + 0xB7, 0xAE, 0xB6, 0xDD, 0xEA, 0x61, 0x2C, 0xDE, 0x28, 0xF0, 0x1A, 0x20, 0x2D, 0x7A, 0xA4, + 0xE3, 0x47, 0x22, 0xD2, 0x7D, 0xD3, 0xF9, 0xB8, 0x98, 0x94, 0xD0, 0x19, 0xFD, 0x5D, 0x4D, + 0x71, 0x19, 0xEF, 0xE3, 0x72, 0x3B, 0xBA, 0x10, 0x4C, 0xB8, 0xBB, 0x09, 0x81, 0xE0, 0x74, + 0xDE, 0x3A, 0xFE, 0x20, 0x0D, 0xAA, 0xAE, 0xAD, 0x82, 0x6C, 0xC4, 0x5F, 0x24, 0x4D, 0xBF, + 0x43, 0x1A, 0xFA, 0xB3, 0x4E, 0xFB, 0xDF, 0x78, 0x24, 0x74, 0xD2, 0xFD, 0x57, 0x11, 0x8F, + 0x64, 0x62, 0x14, 0x93, 0x4E, 0xD9, 0x9C, 0xBA, 0x3B, 0x00, 0x3E, 0x8D, 0x67, 0xA3, 0x83, + 0x6F, 0x6F, 0x19, 0xFC, 0x41, 0x91, 0x0C, 0xE5, 0x16, 0x3E, 0xE3, 0xAE, 0x99, 0xEB, 0x84, + 0xD5, 0x14, 0xEB, 0x76, 0x1E, 0x63, 0x68, 0x4E, 0xA5, 0x6F, 0x97, 0x91, 0xD2, 0xDD, 0x4A, + 0xAC, 0x6E, 0x61, 0x68, 0xB9, 0x48, 0xC8, 0x17, 0xF7, 0x5A, 0x22, 0x2A, 0xCB, 0x0E, 0x8C, + 0xDC, 0x03, 0xCC, 0x4A, 0xFE, 0x8F, 0x67, 0x15, 0x7E, 0x1A, 0x36, 0x3B, 0x7F, 0xAE, 0xFF, + 0x9F, 0x17, 0x2B, 0x98, 0x91, 0x36, 0x77, 0xC5, 0xA1, 0xDD, 0x08, 0x5E, 0x9E, 0xE4, 0xC2, + 0x20, 0x52, 0xC1, 0xAF, 0x58, 0x19, 0x31, 0x16, 0x67, 0x3D, 0xCD, 0x3B, 0xFC, 0x5F, 0x34, + 0xB8, 0x55, 0xDC, 0xC6, 0xC7, 0x78, 0x85, 0x64, 0x9E, 0x9E, 0x71, 0xF4, 0x3D, 0x4A, 0xEA, + 0x0F, 0x4B, 0x72, 0xCA, 0x7E, 0xDA, 0x05, 0x78, 0xBA, 0x13, 0xD3, 0x1A, 0x65, 0x8D, 0x2D, + 0x06, 0x0A, 0x9A, 0x66, 0xFF, 0x69, 0xED, 0x1B, 0xE7, 0x99, 0x7A, 0x2F, 0xB1, 0xD2, 0x72, + 0x3D, 0x38, 0xF9, 0xBF, 0xAB, 0xE1, 0x8F, 0x8E, 0x7B, 0x3C, 0xDA, 0x90, 0x6E, 0x4E, 0x9B, + 0x5E, 0x94, 0x2C, 0x8E, 0xAE, 0xB2, 0x96, 0x07, 0x0E, 0xBF, 0xD3, 0x64, 0x94, 0x7A, 0x94, + 0x0C, 0xC9, 0x78, 0xBE, 0xD6, 0x6B, 0x37, 0x74, 0x9E, 0x6D, 0x5D, 0xCD, 0x7B, 0xE8, 0xC4, + 0x94, 0x44, 0x0E, 0x2B, 0x84, 0xCE, 0xCF, 0xEF, 0xB9, 0x8C, 0x0B, 0xED, 0xFB, 0x3C, 0x41, + 0xE3, 0x35, 0x9D, 0x2C, 0xD7, 0x19, 0x7F, 0xBE, 0x72, 0x0C, 0x48, 0xAA, 0x6C, 0x6B, 0x64, + 0x65, 0xC1, 0xEE, 0x63, 0xE3, 0x56, 0x9C, 0x2A, 0xDC, 0x74, 0x44, 0x91, 0x37, 0x0B, 0x7F, + 0x78, 0x26, 0xFE, 0x0B, 0x77, 0xA1, 0xD1, 0x9D, 0x64, 0x10, 0x1D, 0x03, 0x2B, 0x91, 0x81, + 0x06, 0xB4, 0x2D, 0x2E, 0xF7, 0x37, 0x47, 0xE5, 0x60, 0x1F, 0xE4, 0xBA, 0x50, 0xF2, 0x3E, + 0xDE, 0x52, 0x1F, 0x03, 0x1A, 0x81, 0x7D, 0x15, 0x29, 0x4A, 0x43, 0x72, 0x2E, 0x83, 0x78, + 0x78, 0x4B, 0x6D, 0xB0, 0xCF, 0x1B, 0xA9, 0xE8, 0xAE, 0x91, 0x1D, 0x92, 0x01, 0xB9, 0xCE, + 0x9C, 0xC3, 0x01, 0x9C, 0x6F, 0x5C, 0x27, 0xCB, 0x98, 0xDA, 0x26, 0x14, 0x4B, 0x64, 0x22, + 0x5A, 0x7C, 0x93, 0x2B, 0x30, 0xF7, 0x61, 0xE7, 0x8A, 0x2D, 0x59, 0xA1, 0xD8, 0xB8, 0x3E, + 0xC6, 0x34, 0x4A, 0x2F, 0x6D, 0xD4, 0x7E, 0x76, 0x57, 0x06, 0xD0, 0x0B, 0xF4, 0xA7, 0x9A, + 0x6A, 0x92, 0x6C, 0x3B, 0xA9, 0x1D, 0x81, 0x2C, 0x8F, 0x2C, 0x79, 0x7A, 0xB1, 0x79, 0x67, + 0x09, 0xE5, 0xD1, 0x68, 0x56, 0x77, 0x82, 0x93, 0x52, 0x9F, 0x02, 0x86, 0xD0, 0x15, 0xC3, + 0xB5, 0x39, 0x96, 0x19, 0x64, 0x2A, 0x33, 0x3E, 0x9E, 0x59, 0x3D, 0x6E, 0x3F, 0x53, 0x53, + 0x99, 0x42, 0x08, 0xE9, 0xE6, 0xA3, 0x32, 0x85, 0x1D, 0x7F, 0x65, 0x25, 0x22, 0xA9, 0x28, + 0xB9, 0x17, 0xE2, 0x7E, 0x2D, 0x6D, 0x42, 0x13, 0x7D, 0xFE, 0x2E, 0xBF, 0xA6, 0xFB, 0x1C, + 0x67, 0xB2, 0x6C, 0x02, 0x54, 0x52, 0x86, 0x85, 0xF7, 0xEB, 0xDB, 0xE3, 0x15, 0xA6, 0x8E, + 0xAA, 0x2D, 0xA7, 0x69, 0xE8, 0xA9, 0xF4, 0x2D, 0x3E, 0x60, 0x00, 0x7C, 0x71, 0x33, 0x09, + 0x26, 0xB2, 0xC0, 0x01, 0x2D, 0x83, 0xEA, 0xD4, 0xE4, 0xFD, 0x1E, 0xD8, 0x72, 0xCC, 0xD1, + 0x97, 0x22, 0x01, 0xD2, 0xB0, 0x27, 0xF3, 0x54, 0x5A, 0xC2, 0xD3, 0x0C, 0xD7, 0x8B, 0xC1, + 0xD7, 0x40, 0xFE, 0xCC, 0xBC, 0x6F, 0xC2, 0xA0, 0x44, 0x6C, 0x6E, 0x30, 0xEA, 0xC5, 0x1F, + 0x5A, 0x69, 0x09, 0x8A, 0xA2, 0xD4, 0x47, 0xF2, 0x08, 0x5B, 0x4E, 0x4E, 0x4B, 0x92, 0xCC, + 0xC2, 0x69, 0x21, 0xD2, 0xDE, 0x47, 0x85, 0x18, 0xCD, 0x09, 0x0C, 0xE2, 0x67, 0xAE, 0xA2, + 0xD2, 0x7A, 0xDA, 0x57, 0xFD, 0x88, 0xB4, 0x97, 0x6D, 0x89, 0xFB, 0x84, 0x3C, 0xDC, 0xCF, + 0x49, 0xA7, 0x6C, 0xA2, 0x67, 0x9E, 0x68, 0x01, 0xBF, 0xA7, 0xFB, 0x03, 0x18, 0x96, 0xFB, + 0x50, 0x62, 0x97, 0x04, 0xB9, 0x92, 0x39, 0x36, 0xBB, 0x5D, 0xD3, 0x85, 0x31, 0x11, 0x21, + 0xCA, 0xDF, 0xB1, 0x19, 0x95, 0xE5, 0x9B, 0x73, 0x03, 0x4C, 0xF6, 0x7E, 0xD0, 0x3A, 0xB8, + 0x13, 0x86, 0x76, 0x48, 0xD0, 0x25, 0x82, 0x80, 0x87, 0xE9, 0x49, 0xA9, 0xAF, 0xD1, 0x6B, + 0x95, 0xD7, 0x2D, 0x99, 0xB1, 0xED, 0xCA, 0x25, 0x7A, 0xAC, 0x13, 0x2F, 0xFB, 0x7A, 0x07, + 0x09, 0xAE, 0xD5, 0xA9, 0xC0, 0xFF, 0x05, 0xFB, 0x0F, 0x2B, 0xBF, 0x28, 0x40, 0x9E, 0xED, + 0x7B, 0x5F, 0x58, 0x01, 0xBE, 0x96, 0x4C, 0xED, 0x01, 0x9E, 0x1C, 0xB7, 0x85, 0x1D, 0x38, + 0x51, 0xF1, 0x02, 0x90, 0x67, 0x4E, 0x19, 0xFF, 0xB0, 0x08, 0xB3, 0x01, 0xC4, 0xAC, 0xF6, + 0x41, 0xA2, 0xBB, 0x14, 0x21, 0x6E, 0x1D, 0x69, 0xCA, 0xBF, 0x52, 0xB5, 0xEF, 0x22, 0x74, + 0x96, 0xB0, 0xF3, 0x07, 0x99, 0xA8, 0x55, 0xD1, 0x17, 0xFA, 0xD3, 0x74, 0x4A, 0x6F, 0xA3, + 0x35, 0x03, 0xEA, 0x79, 0x8B, 0x52, 0xDD, 0xD7, 0xEE, 0x54, 0x26, 0x60, 0x9D, 0xBF, 0xCD, + 0x3F, 0x0C, 0x13, 0xB1, 0x64, 0xD6, 0xC0, 0x51, 0xF7, 0xED, 0x4A, 0x11, 0x97, 0x19, 0xA7, + 0x12, 0xE3, 0x88, 0xD3, 0x28, 0x40, 0x20, 0x81, 0xFF, 0x13, 0x54, 0xB5, 0x54, 0xD2, 0xC2, + 0x37, 0xAF, 0xED, 0x3B, 0x15, 0x1C, 0x4B, 0xA8, 0xE9, 0xF4, 0xBD, 0xEB, 0x84, 0x99, 0xA3, + 0x06, 0x6E, 0x26, 0xBB, 0xC6, 0x9E, 0x8A, 0xF0, 0x89, 0xDE, 0xC7, 0x17, 0x31, 0xD1, 0xDC, + 0x52, 0x9E, 0xAB, 0x17, 0xEF, 0x73, 0x74, 0x73, 0x4C, 0x0F, 0xE4, 0x75, 0x49, 0x4C, 0x83, + 0x83, 0x6B, 0xDD, 0x34, 0xA0, 0x3B, 0x9B, 0xC8, 0x99, 0x14, 0x71, 0x60, 0x61, 0xBF, 0xB9, + 0x8E, 0xC6, 0xE6, 0x1C, 0x3E, 0xD4, 0x43, 0x8E, 0xDC, 0xAF, 0x25, 0x24, 0x3C, 0x64, 0x70, + 0x86, 0xB9, 0xEA, 0x70, 0x18, 0xB0, 0xD9, 0xA8, 0xA0, 0xB0, 0x0C, 0xEC, 0xB0, 0x0A, 0xBD, + 0xE2, 0x49, 0x8D, 0x69, 0xC2, 0x33, 0x61, 0x01, 0xA7, 0x72, 0xCB, 0xE4, 0xF5, 0x71, 0x52, + 0x3F, 0x51, 0xBD, 0x05, 0x88, 0x2C, 0xDF, 0x35, 0x8B, 0x84, 0x9C, 0xC1, 0x40, 0xAA, 0x1F, + 0xAF, 0x22, 0x42, 0x3A, 0x12, 0x85, 0x1C, 0xE0, 0xE3, 0x3F, 0xD4, 0x89, 0x75, 0xA4, 0x95, + 0x9F, 0xA5, 0xC5, 0xFE, 0x41, 0x8C, 0x93, 0x90, 0x81, 0x91, 0xAB, 0x6E, 0x74, 0x1B, 0x77, + 0xBF, 0xE0, 0x2C, 0xBD, 0x69, 0x8E, 0xE7, 0x95, 0xC4, 0x66, 0xD6, 0x15, 0x61, 0x9E, 0x64, + 0x41, 0x38, 0x2C, 0x6E, 0xAC, 0x01, 0x83, 0x4E, 0xE9, 0xAB, 0x73, 0xCE, 0xA8, 0x0B, 0xBE, + 0x23, 0x5C, 0x78, 0xDA, 0x91, 0xBD, 0x79, 0xB6, 0xF8, 0x2F, 0x89, 0x97, 0x85, 0xD6, 0x87, + 0x00, 0xD3, 0x93, 0xE6, 0x75, 0xC2, 0x22, 0x4D, 0x6B, 0x7A, 0x1A, 0xD2, 0x13, 0x20, 0x49, + 0x56, 0x79, 0xAD, 0xAE, 0xD7, 0x01, 0x67, 0xB5, 0x08, 0x66, 0x71, 0x3A, 0x53, 0x10, 0x9D, + 0xB7, 0xB6, 0xF7, 0xD8, 0x13, 0x04, 0xEC, 0xDF, 0xD8, 0x3B, 0x31, 0x9B, 0x1E, 0xF2, 0x48, + 0x30, 0x6B, 0x45, 0xAD, 0x29, 0xE7, 0xDD, 0xCC, 0x86, 0x3D, 0xAC, 0x56, 0x04, 0x8B, 0x5D, + 0x69, 0xEA, 0x17, 0x50, 0x11, 0xF7, 0x61, 0x4C, 0x00, 0xA8, 0x6A, 0x86, 0x3C, 0xDE, 0x18, + 0x72, 0xA8, 0x93, 0x28, 0x78, 0xB9, 0xAC, 0x7E, 0x1A, 0xC5, 0xBD, 0xA4, 0x99, 0x7B, 0x72, + 0x06, 0x4F, 0x0C, 0xD7, 0x5F, 0x4C, 0x81, 0x4E, 0x03, 0x4D, 0xE1, 0x1A, 0xCB, 0x90, 0x13, + 0xCF, 0x7E, 0xA9, 0x26, 0xB4, 0xE7, 0xEA, 0xAC, 0xE0, 0x70, 0xC7, 0xBA, 0x21, 0x88, 0xEF, + 0xAD, 0x2E, 0x43, 0x1E, 0x12, 0x23, 0xD4, 0x5D, 0xD0, 0x5C, 0x4D, 0x84, 0x03, 0xC2, 0xE4, + 0x5C, 0xEE, 0x64, 0x13, 0xEC, 0xBE, 0x75, 0x27, 0xE8, 0x73, 0xE4, 0x55, 0xC4, 0xE6, 0x10, + 0xA6, 0x18, 0x39, 0xAA, 0xCC, 0x0B, 0xD5, 0x6D, 0x24, 0x83, 0xE7, 0x8F, 0x29, 0x8B, 0x66, + 0xA4, 0x78, 0xEB, 0x2F, 0x55, 0x8C, 0xBA, 0xFC, 0xA8, 0x6B, 0xE8, 0x47, 0xBA, 0xEB, 0x02, + 0xC5, 0xB2, 0x16, 0xC8, 0xCD, 0x88, 0xFE, 0xA4, 0xDF, 0x24, 0x9B, 0x09, 0xE6, 0x70, 0xA2, + 0x07, 0x03, 0xAB, 0xAC, 0x24, 0xB0, 0xA9, 0x1A, 0xBC, 0x4A, 0x56, 0x46, 0x60, 0x14, 0x42, + 0xBA, 0x10, 0xBE, 0xCF, 0xD3, 0x09, 0x93, 0x88, 0x00, 0x51, 0xD0, 0x7F, 0x56, 0xA0, 0x5A, + 0x93, 0x79, 0xE7, 0xA8, 0xE6, 0xBE, 0xFE, 0xE3, 0xF2, 0x2F, 0xAA, 0x10, 0x63, 0x98, 0xF7, + 0x70, 0x60, 0x06, 0xE4, 0x2E, 0x9B, 0xE1, 0xEF, 0x89, 0xD2, 0x5C, 0x27, 0x2F, 0x11, 0xA9, + 0x50, 0x95, 0xC5, 0x87, 0xD7, 0x13, 0x73, 0x22, 0x84, 0xDE, 0x9D, 0xBD, 0x3C, 0x72, 0x17, + 0xB0, 0x68, 0x9E, 0x21, 0xD8, 0xEB, 0x0F, 0xF6, 0x96, 0x68, + ]) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -182,7 +418,7 @@ fn bench_mldsa44_sign() { } fn bench_mldsa44_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA44, MLDSA44PrivateKey, MLDSA44_SK_LEN}; + use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSATrait}; eprintln!("MLDSA44_lowmemory/Sign"); @@ -195,7 +431,12 @@ fn bench_mldsa44_lowmemory_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA44PrivateKey::from_bytes(&[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f]).unwrap(); + let sk = MLDSA44PrivateKey::from_bytes(&[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, + ]) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -205,7 +446,7 @@ fn bench_mldsa44_lowmemory_sign() { } fn bench_mldsa65_sign() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA65, MLDSA65PrivateKey, MLDSA65_SK_LEN}; + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSATrait}; eprintln!("MLDSA65/Sign"); @@ -219,7 +460,278 @@ fn bench_mldsa65_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA65PrivateKey::from_bytes(&[0x48,0x68,0x3d,0x91,0x97,0x8e,0x31,0xeb,0x3d,0xdd,0xb8,0xb0,0x47,0x34,0x82,0xd2,0xb8,0x8a,0x5f,0x62,0x59,0x49,0xfd,0x8f,0x58,0xa5,0x61,0xe6,0x96,0xbd,0x4c,0x27,0xd8,0x53,0xfa,0x69,0xb8,0x19,0x90,0x23,0xe8,0xcd,0x67,0x8d,0xd9,0xfa,0xbf,0x90,0x47,0x64,0x6f,0xfd,0x0c,0xb3,0xcc,0x7f,0x79,0x58,0x05,0xa7,0x1e,0x70,0xd2,0x37,0x1b,0x05,0x63,0xe3,0xcd,0x33,0x46,0x14,0x9c,0x8c,0x9e,0xbc,0xf2,0x3b,0x0a,0x4e,0x5a,0x90,0x0e,0xea,0x9c,0x65,0x62,0x79,0x0a,0x7c,0x63,0xe3,0x86,0x63,0xda,0xa2,0xdd,0xdb,0x6e,0x48,0x0d,0xc4,0x05,0xa1,0xe7,0x01,0x94,0x8b,0x74,0x84,0x1e,0xf5,0xcc,0x1c,0x3f,0x2b,0xf3,0x27,0x97,0x2e,0x95,0x10,0x51,0x0c,0xd5,0x37,0x5e,0xcc,0x08,0x55,0x71,0x77,0x11,0x87,0x22,0x21,0x86,0x23,0x81,0x00,0x04,0x24,0x77,0x80,0x61,0x47,0x50,0x07,0x50,0x17,0x17,0x03,0x55,0x04,0x51,0x51,0x25,0x47,0x18,0x38,0x04,0x61,0x75,0x72,0x22,0x44,0x10,0x88,0x68,0x60,0x86,0x46,0x01,0x27,0x47,0x56,0x71,0x80,0x87,0x06,0x66,0x86,0x43,0x32,0x44,0x41,0x22,0x04,0x36,0x38,0x66,0x75,0x02,0x82,0x36,0x34,0x24,0x43,0x22,0x05,0x73,0x64,0x10,0x64,0x55,0x54,0x77,0x22,0x75,0x56,0x81,0x43,0x36,0x14,0x62,0x55,0x08,0x20,0x64,0x37,0x68,0x54,0x68,0x75,0x43,0x53,0x75,0x10,0x68,0x71,0x83,0x33,0x80,0x54,0x75,0x05,0x25,0x80,0x75,0x28,0x18,0x84,0x38,0x11,0x08,0x72,0x60,0x20,0x20,0x08,0x58,0x83,0x01,0x83,0x61,0x13,0x82,0x82,0x12,0x06,0x17,0x11,0x57,0x87,0x68,0x78,0x88,0x78,0x64,0x37,0x54,0x60,0x16,0x57,0x15,0x50,0x84,0x71,0x88,0x66,0x07,0x27,0x32,0x88,0x06,0x64,0x74,0x18,0x56,0x76,0x21,0x80,0x31,0x82,0x76,0x64,0x15,0x78,0x24,0x50,0x25,0x64,0x66,0x43,0x11,0x35,0x04,0x36,0x47,0x80,0x12,0x66,0x73,0x14,0x30,0x11,0x66,0x06,0x55,0x86,0x47,0x18,0x36,0x88,0x63,0x50,0x38,0x47,0x86,0x11,0x01,0x20,0x23,0x56,0x11,0x61,0x37,0x86,0x07,0x85,0x32,0x12,0x40,0x07,0x54,0x78,0x82,0x30,0x43,0x66,0x61,0x16,0x60,0x42,0x55,0x41,0x82,0x85,0x60,0x53,0x67,0x78,0x56,0x38,0x43,0x44,0x30,0x63,0x26,0x10,0x77,0x07,0x31,0x78,0x42,0x72,0x14,0x11,0x16,0x53,0x03,0x85,0x27,0x68,0x67,0x46,0x01,0x50,0x82,0x37,0x35,0x32,0x07,0x66,0x10,0x75,0x04,0x68,0x12,0x48,0x06,0x66,0x03,0x03,0x26,0x52,0x31,0x24,0x45,0x40,0x88,0x00,0x31,0x80,0x88,0x76,0x72,0x17,0x30,0x71,0x82,0x47,0x21,0x51,0x27,0x80,0x11,0x65,0x44,0x74,0x86,0x61,0x72,0x23,0x33,0x80,0x86,0x60,0x64,0x46,0x83,0x52,0x15,0x84,0x20,0x36,0x80,0x11,0x80,0x21,0x18,0x18,0x33,0x17,0x73,0x54,0x53,0x48,0x81,0x00,0x44,0x86,0x53,0x67,0x43,0x70,0x57,0x72,0x58,0x83,0x34,0x60,0x38,0x42,0x32,0x85,0x68,0x10,0x06,0x04,0x26,0x04,0x25,0x84,0x56,0x02,0x35,0x68,0x20,0x51,0x83,0x86,0x38,0x43,0x24,0x21,0x22,0x42,0x45,0x64,0x58,0x58,0x67,0x71,0x45,0x72,0x85,0x04,0x78,0x87,0x17,0x18,0x06,0x18,0x83,0x60,0x86,0x86,0x41,0x56,0x50,0x81,0x16,0x50,0x26,0x46,0x70,0x06,0x08,0x26,0x62,0x27,0x38,0x31,0x72,0x40,0x72,0x57,0x30,0x07,0x27,0x28,0x86,0x20,0x66,0x75,0x88,0x68,0x26,0x07,0x06,0x40,0x20,0x33,0x03,0x43,0x66,0x31,0x55,0x46,0x42,0x45,0x34,0x56,0x67,0x18,0x73,0x45,0x65,0x83,0x70,0x22,0x50,0x84,0x68,0x56,0x28,0x80,0x70,0x36,0x70,0x84,0x62,0x37,0x17,0x10,0x06,0x57,0x17,0x58,0x47,0x78,0x70,0x86,0x55,0x53,0x78,0x22,0x35,0x14,0x46,0x77,0x28,0x56,0x73,0x03,0x22,0x87,0x00,0x14,0x33,0x20,0x61,0x71,0x58,0x45,0x52,0x66,0x32,0x50,0x26,0x51,0x33,0x47,0x77,0x38,0x03,0x55,0x16,0x43,0x13,0x47,0x35,0x10,0x66,0x27,0x51,0x75,0x74,0x02,0x46,0x88,0x81,0x70,0x67,0x43,0x46,0x81,0x86,0x01,0x76,0x52,0x45,0x33,0x30,0x87,0x21,0x04,0x34,0x34,0x01,0x03,0x22,0x87,0x63,0x51,0x55,0x26,0x50,0x81,0x30,0x77,0x45,0x44,0x41,0x68,0x15,0x41,0x83,0x63,0x64,0x11,0x20,0x40,0x26,0x87,0x30,0x43,0x67,0x77,0x12,0x80,0x88,0x46,0x35,0x54,0x53,0x00,0x62,0x45,0x81,0x04,0x58,0x36,0x51,0x24,0x84,0x27,0x80,0x34,0x51,0x66,0x63,0x58,0x43,0x78,0x56,0x01,0x46,0x51,0x15,0x74,0x23,0x21,0x43,0x66,0x85,0x22,0x47,0x77,0x31,0x34,0x50,0x17,0x83,0x62,0x42,0x05,0x50,0x00,0x64,0x84,0x47,0x12,0x34,0x40,0x88,0x00,0x60,0x47,0x35,0x40,0x57,0x83,0x33,0x63,0x08,0x21,0x06,0x15,0x22,0x52,0x07,0x24,0x88,0x51,0x34,0x86,0x37,0x06,0x76,0x22,0x58,0x85,0x71,0x26,0x56,0x73,0x47,0x68,0x16,0x46,0x46,0x84,0x25,0x87,0x08,0x12,0x27,0x05,0x50,0x08,0x38,0x32,0x00,0x23,0x20,0x80,0x66,0x34,0x53,0x36,0x00,0x33,0x46,0x85,0x72,0x47,0x06,0x35,0x54,0x00,0x35,0x77,0x12,0x27,0x52,0x30,0x71,0x42,0x53,0x68,0x74,0x37,0x45,0x70,0x05,0x66,0x43,0x22,0x44,0x82,0x85,0x20,0x72,0x18,0x33,0x30,0x20,0x53,0x37,0x33,0x40,0x77,0x27,0x80,0x55,0x25,0x30,0x63,0x52,0x50,0x40,0x67,0x33,0x46,0x13,0x18,0x07,0x28,0x07,0x17,0x24,0x83,0x77,0x63,0x45,0x73,0x18,0x58,0x51,0x60,0x23,0x33,0x44,0x36,0x25,0x16,0x43,0x38,0x16,0x08,0x58,0x77,0x34,0x62,0x42,0x88,0x30,0x07,0x03,0x65,0x85,0x37,0x55,0x00,0x75,0x52,0x31,0x50,0x37,0x02,0x13,0x24,0x63,0x04,0x37,0x08,0x68,0x06,0x36,0x15,0x03,0x03,0x00,0x43,0x58,0x63,0x57,0x08,0x02,0x11,0x06,0x64,0x73,0x46,0x35,0x22,0x62,0x03,0x30,0x43,0x80,0x21,0x08,0x52,0x87,0x57,0x83,0x21,0x07,0x88,0x67,0x48,0x08,0x56,0x34,0x74,0x36,0x73,0x42,0x84,0x05,0x84,0x66,0x84,0x14,0x37,0x00,0x55,0x10,0x87,0x34,0x26,0x44,0x77,0x21,0x12,0x73,0x84,0x73,0x65,0x26,0x47,0x25,0x77,0x14,0x47,0x04,0x17,0x86,0x44,0x26,0x02,0x47,0x11,0x87,0x40,0x81,0x22,0x16,0x60,0x58,0x47,0x17,0x81,0x37,0x06,0x76,0x80,0x81,0x70,0x58,0x18,0x55,0x85,0x47,0x13,0x63,0x42,0x10,0x75,0x58,0x01,0x63,0x58,0x35,0x85,0x18,0x44,0x03,0x84,0x71,0x10,0x33,0x87,0x42,0x62,0x82,0x47,0x74,0x13,0x65,0x54,0x42,0x70,0x73,0x46,0x35,0x77,0x75,0x00,0x66,0x25,0x62,0x68,0x42,0x02,0x12,0x46,0x83,0x86,0x46,0x16,0x64,0x60,0x31,0x22,0x53,0x88,0x84,0x54,0x00,0x84,0x57,0x34,0x46,0x47,0x54,0x47,0x25,0x60,0x54,0x61,0x66,0x84,0x66,0x30,0x88,0x06,0x38,0x27,0x15,0x63,0x28,0x71,0x83,0x84,0x06,0x52,0x24,0x76,0x81,0x16,0x06,0x62,0x13,0x03,0x30,0x18,0x68,0x02,0x80,0x13,0x84,0x63,0x05,0x05,0x65,0x72,0x38,0x75,0x83,0x65,0x72,0x32,0x30,0x68,0x80,0x46,0x12,0x26,0x06,0x65,0x16,0x75,0x57,0x05,0x32,0x41,0x32,0x27,0x67,0x35,0x17,0x08,0x01,0x53,0x00,0x16,0x28,0x46,0x01,0x34,0x88,0x77,0x01,0x11,0x88,0x15,0x57,0x13,0x15,0x46,0x43,0x11,0x70,0x47,0x32,0x88,0x28,0x56,0x36,0x82,0x34,0x55,0x50,0x41,0x86,0x27,0x65,0x63,0x11,0x11,0x68,0x75,0x05,0x10,0x42,0x54,0x41,0x44,0x27,0x85,0x22,0x11,0x17,0x17,0x88,0x15,0x36,0x85,0x15,0x74,0x47,0x16,0x62,0x55,0x36,0x55,0x83,0x63,0x02,0x50,0x28,0x55,0x76,0x87,0x53,0x27,0x13,0x71,0x03,0x72,0x37,0x05,0x71,0x47,0x61,0x71,0x36,0x51,0x84,0x12,0x42,0x36,0x64,0x44,0x66,0x41,0x43,0x52,0x05,0x21,0x08,0x51,0x57,0x03,0x33,0x63,0x86,0x02,0x58,0x42,0x66,0x28,0x14,0x81,0x10,0x54,0x62,0x68,0x17,0x30,0x38,0x75,0x64,0x33,0x21,0x65,0x88,0x56,0x86,0x63,0x63,0x28,0x13,0x40,0x62,0x54,0x01,0x20,0x40,0x88,0x65,0x47,0x88,0x61,0x71,0x65,0x76,0x23,0x72,0x62,0x34,0x86,0x70,0x30,0x11,0x51,0x15,0x63,0x20,0x50,0x75,0x35,0x02,0x12,0x21,0x08,0x42,0x65,0x31,0x43,0x55,0x67,0x11,0x15,0x25,0x72,0x01,0x06,0x85,0x36,0x30,0x15,0x05,0x57,0x58,0x60,0x58,0x78,0x43,0x14,0x31,0x32,0x78,0x78,0x80,0x87,0x38,0x47,0x88,0x63,0x78,0x81,0x81,0x38,0x73,0x42,0x61,0x78,0x38,0x85,0x24,0x66,0x77,0x33,0x50,0x60,0x21,0x15,0x14,0x64,0x23,0x82,0x32,0x68,0x01,0x35,0x44,0x07,0x83,0x47,0x53,0x85,0x53,0x57,0x52,0x83,0x23,0x35,0x18,0x76,0x01,0x15,0x21,0x34,0x32,0x57,0x73,0x33,0x36,0x55,0x18,0x86,0x15,0x81,0x61,0x68,0x24,0x18,0x42,0x21,0x22,0x30,0x84,0x14,0x48,0x15,0x12,0x01,0x10,0x30,0x24,0x77,0x72,0x42,0x54,0x43,0x66,0x06,0x77,0x17,0x70,0x76,0x03,0x01,0x45,0x25,0x40,0x35,0x00,0x18,0x38,0x73,0x23,0x77,0x35,0x26,0x50,0x86,0x35,0x71,0x13,0x73,0x44,0x81,0x60,0x52,0x77,0x45,0x65,0x53,0x73,0x00,0x85,0x83,0x77,0x85,0x03,0x51,0x21,0x11,0x54,0x80,0x62,0x88,0x50,0x18,0x02,0x68,0x13,0x86,0x52,0x05,0x34,0x68,0x01,0x32,0x07,0x24,0x18,0x03,0x21,0x30,0x05,0x72,0x38,0x64,0x07,0x64,0x27,0x11,0x41,0x01,0x83,0x85,0x25,0x51,0x06,0x32,0x60,0x71,0x04,0x86,0x51,0x76,0x83,0x38,0x28,0x57,0x27,0x62,0x35,0x45,0x18,0x73,0x50,0x83,0x13,0x28,0x86,0x37,0x66,0x61,0x42,0x63,0x11,0x67,0x50,0x33,0x11,0x25,0x53,0x76,0x41,0x76,0x03,0x14,0x33,0x17,0x72,0x12,0x23,0x44,0x18,0xa8,0x2e,0x4f,0x5c,0x9e,0xa0,0xfa,0xf9,0x9e,0xb0,0x4d,0x78,0xa7,0x33,0x27,0x11,0x11,0x7c,0x33,0xf1,0x8e,0xca,0x21,0xf8,0x74,0x33,0x76,0xad,0xa5,0x21,0x98,0x04,0xa7,0xed,0x9a,0x55,0x57,0xfc,0xd6,0x7a,0x35,0x50,0xb3,0xa4,0xb8,0xc5,0x88,0x62,0x9c,0x02,0x14,0x75,0xfa,0x3d,0x56,0xd5,0xd6,0xcf,0xbb,0x1a,0x09,0xbd,0xa8,0xd1,0x4d,0xe6,0x22,0xdd,0xff,0x16,0xd8,0xbc,0x99,0xb1,0x42,0x78,0xa8,0xaf,0x1d,0x76,0xbe,0xd1,0x57,0x67,0x2d,0xd9,0xc3,0x23,0x16,0xf9,0x7e,0x8d,0xaa,0xde,0xf8,0xd9,0xda,0x69,0x58,0x67,0x25,0x56,0x7f,0xb9,0x6b,0x59,0x99,0x0d,0x4b,0xf0,0xbc,0x9c,0x19,0x5b,0x90,0xb7,0x42,0x95,0xf5,0x67,0x5b,0x24,0x25,0x7c,0x27,0x10,0xc1,0x75,0xb0,0x15,0x3f,0x29,0x11,0x32,0x8c,0x2e,0xb7,0xab,0xb9,0xad,0x46,0xe7,0x0a,0x8b,0x53,0xc3,0x9e,0xa6,0x42,0xce,0xe4,0xb3,0xcb,0x42,0x62,0x0e,0x86,0x3c,0xe8,0xb6,0x50,0xce,0x8a,0xdc,0xd9,0x23,0x72,0x1a,0x16,0x87,0x02,0x3c,0x67,0x3a,0x8c,0xbb,0x6b,0x03,0xd5,0x1c,0xd1,0x97,0xe8,0xc3,0x46,0xeb,0xad,0xce,0x93,0x95,0x0f,0x88,0xce,0xe2,0x01,0xdb,0x9e,0x32,0x08,0x43,0xe2,0x9f,0x30,0x0d,0x9a,0x19,0x50,0x0d,0x70,0xa4,0xca,0xf2,0x72,0xc6,0x9e,0x4e,0xef,0x69,0xfb,0xb8,0xa5,0x5e,0xfd,0x7c,0xa2,0xbe,0xd9,0x90,0xd2,0xd3,0xb5,0x82,0x84,0x8f,0x9c,0x45,0xc2,0xab,0xc5,0x4c,0xfc,0x47,0xd3,0x4f,0x06,0xc0,0xff,0xa5,0x6f,0xcd,0x76,0x2a,0xb9,0xcb,0xa9,0x14,0x6d,0x77,0x25,0x21,0x89,0x63,0xb2,0x40,0xd7,0x2b,0x6d,0x22,0xc9,0x31,0x71,0xfb,0xd4,0x77,0x88,0xb7,0x6e,0x72,0x04,0x2d,0xef,0x08,0x78,0xd2,0x3d,0xf6,0x31,0xa1,0xa1,0xe5,0xa6,0x02,0x76,0x86,0xde,0x5b,0x4a,0x10,0xe9,0x10,0x69,0xc8,0xf2,0xba,0x02,0x59,0xb0,0x4d,0x64,0x09,0xda,0x96,0x56,0x7c,0xa5,0x2d,0xa4,0x97,0x02,0x6e,0x58,0x3a,0x0e,0xce,0xfc,0x1f,0x01,0xe6,0xb9,0x88,0xe2,0x1f,0x97,0x67,0xa2,0xb7,0xe1,0x67,0x2d,0xeb,0x9a,0x1e,0x2a,0x3f,0xcc,0x86,0x3a,0xa9,0x15,0x17,0xc3,0x34,0x62,0x06,0x01,0xb4,0xfe,0x79,0x73,0x0e,0x93,0x49,0x35,0xf4,0xb6,0xfb,0xc4,0xe3,0x26,0x95,0x14,0x5c,0x2b,0x5f,0x6a,0x12,0x7f,0xec,0xc0,0xa2,0x77,0x45,0x1e,0xbc,0x3f,0xd5,0x23,0x44,0x4f,0x9e,0xe7,0xc9,0xc3,0x45,0x34,0xf3,0x56,0xdb,0x54,0x4f,0xc3,0x1c,0x1b,0xfd,0xe5,0xf6,0x5c,0x77,0xea,0x2f,0x7c,0x2e,0xae,0x4c,0x55,0xeb,0xaf,0x10,0x42,0x71,0xc5,0x66,0xfd,0x4e,0xba,0xc7,0x1c,0x7a,0x62,0xc7,0x49,0x52,0x81,0x7a,0xe6,0x75,0x50,0x4d,0x95,0x99,0xb1,0xb7,0x62,0xb6,0xac,0xa1,0x68,0xa8,0x32,0x48,0xc9,0xd9,0xad,0xb0,0xce,0xb1,0x55,0x6e,0x57,0x59,0x49,0x0b,0xbc,0x0c,0x79,0x00,0x79,0x5a,0xd7,0x21,0x23,0x03,0x8b,0x66,0x2f,0x64,0xf1,0x06,0xa9,0x99,0x36,0x81,0xa2,0x5d,0x59,0xaf,0x7b,0xc9,0x7a,0x23,0x5b,0xe9,0x28,0x4c,0x5b,0xc4,0x5a,0x6c,0x90,0xcb,0x1c,0x29,0x99,0xc6,0x63,0xd9,0x6b,0x47,0x8e,0x23,0x07,0xf8,0x55,0x48,0x95,0x7d,0x65,0x74,0x0e,0x26,0x73,0xe9,0xeb,0xd1,0x35,0x28,0x29,0x03,0x8f,0x46,0x2b,0x8f,0xd3,0xb5,0x68,0x1d,0xa5,0x5c,0x02,0x52,0x52,0x38,0x53,0x52,0x5e,0xa0,0xad,0x64,0x7e,0x71,0xac,0x2c,0x5a,0x88,0x93,0xe6,0x03,0xac,0x97,0xe5,0x6c,0x04,0xce,0xb2,0xf2,0x6f,0x5c,0x5b,0x4b,0x6d,0x94,0xab,0x81,0x13,0x80,0xfd,0x00,0xf2,0x20,0x8f,0xe8,0x65,0x35,0x08,0x6a,0xeb,0xfd,0x35,0xc2,0x91,0x20,0x62,0x4c,0x04,0xfb,0xb6,0x11,0x39,0x29,0xd9,0xc5,0x56,0x35,0x02,0x53,0x76,0x6c,0x20,0x9f,0xdb,0xa8,0x3c,0x95,0xfc,0xcd,0x34,0x2a,0x28,0x09,0x93,0x55,0xd0,0x0b,0xc8,0x63,0xf4,0xee,0xf5,0x96,0xeb,0x0b,0x42,0xeb,0xcc,0x7c,0x79,0x49,0x1c,0xce,0xae,0x20,0x5e,0xa0,0xb8,0x05,0x9f,0xbb,0x8a,0x57,0x26,0xc5,0x94,0x9d,0x2b,0x15,0xe7,0xe2,0x9c,0x51,0xfc,0x9b,0x02,0xee,0x1a,0x4f,0xc3,0x57,0xb5,0xf1,0xbe,0xf9,0xc4,0xad,0xd4,0x6a,0x2a,0x92,0x0c,0x2f,0xbf,0x08,0xa3,0x7e,0xb1,0x51,0x4b,0xfa,0x15,0x11,0x0a,0x43,0x92,0xa7,0x4c,0x6f,0x13,0xc5,0x0c,0x5c,0xff,0xd9,0x75,0x31,0x09,0x8d,0x7c,0xd2,0x3b,0x60,0xeb,0x35,0xc4,0xa4,0x28,0xb4,0x6c,0x55,0x38,0x6e,0x10,0x10,0xc4,0xba,0x7f,0x70,0xe4,0xc7,0xec,0xb7,0x57,0x5f,0x30,0x63,0xa7,0x1e,0x84,0xdf,0xdc,0xf0,0x9a,0x58,0xb2,0xcd,0xb0,0xf9,0x9f,0x27,0xed,0x37,0x86,0x10,0xd2,0x5c,0xba,0xd7,0xbf,0xa6,0xba,0x0d,0x59,0x18,0x9c,0xfe,0x88,0xea,0xb9,0xb4,0x6d,0x7e,0x6d,0xb0,0x30,0x7e,0xab,0xe4,0x19,0x8e,0x99,0xbd,0x71,0xf7,0x79,0xab,0x66,0x58,0x1e,0x09,0x12,0xfc,0x7b,0x1d,0x25,0x85,0x24,0x5e,0x9a,0x12,0x68,0x7a,0x97,0x5c,0xd5,0xe8,0xe1,0xdc,0xc0,0x45,0xd5,0xf8,0x91,0xc4,0xc6,0x85,0xdb,0x07,0xcf,0x81,0xe7,0x73,0x89,0xb3,0x63,0xeb,0x6b,0xdf,0xe3,0x9b,0x27,0xff,0x84,0xc9,0x7e,0xef,0xee,0x16,0x2e,0x3b,0x45,0x1f,0xe6,0x91,0x47,0x19,0xcb,0x64,0x36,0xd8,0x55,0x96,0x0f,0xf9,0x15,0xd7,0xce,0xa6,0xad,0xea,0xfd,0xfc,0x1c,0x05,0x78,0x6c,0x49,0xf9,0x23,0xa4,0x74,0xff,0xdf,0xc3,0x15,0x3a,0x06,0xe6,0xed,0x0b,0x0a,0xd2,0x20,0xd7,0x25,0x24,0x43,0x4d,0x52,0x73,0xc0,0xaa,0xb6,0xdd,0xe4,0xe9,0x14,0x76,0xd5,0x81,0xa2,0x69,0x5a,0x60,0xde,0x6d,0x9f,0x44,0xd7,0x7a,0xa0,0x82,0x66,0xe9,0x38,0xee,0xb4,0xa9,0x59,0x7c,0x9b,0x64,0x98,0x60,0x59,0xe4,0x92,0x62,0xa4,0xea,0xb2,0x45,0x4e,0x14,0x01,0x5a,0xd0,0x53,0x6c,0x42,0x73,0x3a,0x5d,0x77,0xd7,0x99,0x5c,0x2a,0x20,0x44,0x60,0x09,0xeb,0xfe,0x56,0x32,0xc8,0x0c,0x08,0xed,0x2b,0x97,0xaf,0x35,0x06,0x64,0x89,0xf5,0x97,0xeb,0x1b,0x1f,0x11,0xf0,0x4f,0x60,0xe0,0xc9,0x04,0x01,0x59,0xc4,0x4a,0xb3,0xe6,0x0e,0x0a,0x15,0x22,0x9d,0x19,0x12,0x28,0xbe,0xd1,0x7b,0xbc,0x3a,0xc9,0x39,0xb3,0xc6,0x7c,0xee,0x13,0x5f,0x35,0x2c,0x27,0x21,0x6c,0x9c,0x31,0xf7,0x2a,0x3e,0x87,0x04,0x0c,0x5f,0x61,0x93,0x06,0xeb,0x0b,0x6c,0xca,0x2a,0x9c,0xe7,0xb2,0x2a,0x16,0x94,0xd0,0x0c,0xa9,0xc0,0x5e,0x31,0x51,0x26,0x45,0x7f,0x26,0xce,0x84,0xf9,0x61,0x72,0x41,0x86,0x07,0x82,0xf8,0x64,0xb4,0x73,0xd8,0x40,0x17,0x49,0x19,0x02,0xb1,0xbd,0xc8,0xcd,0xc5,0x80,0x0d,0xd4,0x61,0x27,0xfb,0x80,0xa7,0x1c,0x09,0x5b,0x47,0x3a,0x56,0x25,0x29,0xb3,0xb1,0xe7,0xe4,0x37,0xe1,0x58,0xa5,0xf6,0x66,0x6e,0x99,0x74,0xd0,0x05,0xb0,0x62,0xc2,0x30,0x9e,0x6d,0xce,0x98,0xf9,0xb6,0x58,0xc6,0xe3,0xf9,0xa2,0x16,0xd5,0x8c,0x8c,0x91,0x42,0xbd,0x1c,0x8c,0x85,0xa9,0xda,0x87,0x2e,0xbb,0xfa,0xd3,0xfe,0xa9,0xd9,0xab,0xa2,0xb6,0x8c,0x0e,0x8f,0x19,0xc6,0xff,0x5f,0x00,0x58,0x4d,0x45,0xda,0xf9,0xd6,0xc9,0xd6,0x9e,0xd0,0x4b,0x8d,0xa8,0xd6,0x87,0x25,0x8b,0x77,0x80,0x79,0x27,0x61,0x2c,0x53,0x04,0x46,0xfe,0xa7,0x69,0x7a,0xe3,0xf9,0x26,0x69,0x89,0x29,0xbc,0x6a,0x5a,0x8c,0xf3,0xe2,0x02,0x4c,0x0f,0x0c,0x5e,0xe5,0x7b,0x58,0x69,0xbf,0x98,0x18,0x81,0xca,0xf9,0xe3,0x66,0x5f,0xc7,0xf7,0xef,0xc6,0x78,0x92,0x9f,0x87,0xa5,0x6e,0xaa,0x42,0xea,0x4d,0x1f,0xf6,0x69,0x18,0x22,0xdd,0x79,0xa4,0x70,0x96,0xb7,0x76,0xd1,0xd8,0xf0,0x14,0x56,0xe5,0x87,0x3b,0x07,0x38,0x40,0x6c,0x38,0x2c,0x57,0x3a,0xe9,0xcd,0xe2,0xd9,0xe7,0xf2,0x31,0xb6,0xcc,0x5c,0x67,0x6e,0x7c,0xf4,0x39,0x63,0x37,0x30,0x13,0xa5,0x80,0x75,0x38,0x1f,0xf0,0x94,0x9b,0xe0,0x84,0x54,0x6d,0x72,0xe4,0xf8,0xa3,0xe5,0xfe,0x4a,0xa5,0x09,0x1a,0xdd,0x23,0x4e,0x2a,0xfe,0x00,0x30,0xb1,0xb6,0x63,0xae,0x9d,0x2d,0x32,0x41,0x09,0x86,0xb9,0x40,0x2a,0xaa,0xf2,0x46,0x5b,0x74,0xa5,0xe2,0xd0,0xbc,0x38,0xe3,0xa9,0x2b,0xbd,0xdd,0x8a,0x1f,0xed,0x7b,0x94,0x8c,0x23,0xcc,0xe6,0xf8,0xc0,0x8f,0xe3,0x56,0x83,0x5b,0xa6,0x5b,0x0f,0x98,0x40,0x68,0x61,0x6e,0xf4,0x81,0x38,0xef,0xd8,0x9b,0xf3,0x57,0xa5,0x4d,0x2e,0xbb,0xf3,0x76,0xcb,0xdc,0xc6,0x9c,0x5f,0x1f,0x61,0xc6,0x4d,0x27,0x94,0xbc,0x06,0xcc,0xb9,0xab,0xdf,0x66,0xe2,0x50,0x85,0xd8,0xc8,0x30,0xe2,0xae,0x3b,0x0f,0xe0,0xf0,0x7a,0x7a,0xf8,0xb9,0x32,0x0b,0xf3,0x42,0x97,0x09,0x97,0xd6,0x7d,0x7c,0x12,0x59,0x3a,0x8f,0xbf,0xad,0xe6,0x35,0xaa,0xc5,0x30,0x83,0xa7,0x02,0x2c,0x47,0xd5,0xf7,0x7a,0x52,0xb5,0x7b,0x59,0x8d,0xa9,0x39,0x2a,0xe6,0xd8,0x6a,0xfc,0x46,0xfc,0x06,0x45,0x51,0x81,0xb9,0xc7,0x5a,0x64,0x6d,0xc2,0x1f,0x81,0xe4,0xbf,0x21,0x37,0x53,0xde,0x73,0x7f,0xd2,0xa1,0x40,0x02,0x79,0x20,0xad,0xd3,0x5a,0x22,0x3f,0x9f,0x5f,0x44,0x65,0xce,0xb6,0x0c,0x03,0xed,0x04,0x55,0xa3,0x33,0xa5,0xcc,0x83,0xad,0xbf,0x43,0xf1,0xf4,0x2c,0x2c,0xcb,0x83,0x28,0xc2,0x1c,0x7a,0xb7,0xfa,0xed,0x2b,0x21,0xcf,0xad,0xe2,0xda,0x55,0x22,0x3a,0xaa,0xb2,0xaf,0x9b,0x41,0xc7,0x33,0x23,0x41,0x74,0x63,0x41,0xb3,0x9a,0xa2,0xf4,0x38,0x15,0x65,0x0f,0x54,0x80,0x51,0x14,0x24,0xcf,0xa6,0x90,0x17,0x79,0xc4,0xd1,0x8b,0x63,0x8c,0xc0,0x28,0x7a,0xaa,0xf3,0x16,0x80,0x33,0x8d,0x20,0xb1,0x7c,0x74,0x49,0xfd,0xc6,0xa2,0x78,0xa8,0xd9,0x6a,0x82,0xee,0x4c,0x4e,0xca,0x40,0x12,0x5e,0x2d,0x65,0x29,0x00,0x71,0xc7,0xae,0xf1,0xbe,0x6a,0x99,0x15,0x98,0xfb,0x9d,0x59,0x51,0x25,0x23,0xbc,0xd4,0xb3,0x8c,0x56,0x6b,0x8e,0x80,0xa7,0x3a,0xe3,0x33,0xe1,0x34,0x41,0x43,0x27,0xef,0x1d,0x83,0xc4,0x7c,0x49,0xdf,0xe7,0x93,0x6d,0xf1,0x33,0x8a,0x5e,0x24,0x77,0x87,0x86,0x8f,0xc8,0x4f,0xdc,0xb9,0x5a,0xc8,0x9c,0x18,0x5c,0x4b,0xb5,0xfd,0x57,0xb2,0x33,0x8a,0xc4,0x2b,0x41,0xc1,0x0a,0x82,0x3d,0xf3,0x96,0x24,0xf3,0x6b,0x15,0xa2,0xf0,0x67,0x58,0x4e,0x06,0xca,0x2e,0x08,0xcc,0xaf,0xf1,0x61,0x8f,0xe0,0x1d,0xd0,0x6d,0xf3,0x51,0x2e,0x0b,0x72,0x4d,0xec,0x85,0x06,0xda,0x24,0x21,0x5a,0xca,0xcc,0x2c,0x51,0xb8,0x2a,0xd8,0xd3,0x02,0x00,0x2f,0xb4,0x10,0x68,0xb1,0xda,0x4f,0x8b,0xb1,0x47,0x98,0x7b,0x35,0x16,0xba,0xd5,0xdb,0xdd,0xf0,0x13,0x18,0xfd,0x3f,0xa9,0xbc,0x43,0x70,0x2a,0xc4,0x98,0xc7,0x19,0xd9,0x5f,0x2e,0x84,0x1b,0x62,0x2a,0x5e,0x48,0x48,0xa3,0xc5,0xc2,0x62,0x95,0x99,0x92,0xea,0x7a,0x7d,0x72,0xca,0x8a,0x36,0x80,0x28,0xf4,0x97,0xdf,0xad,0x93,0x35,0x5c,0xbb,0x1b,0xb9,0x78,0x6d,0x14,0xff,0x2c,0xf5,0x90,0x31,0x78,0x48,0xf9,0x58,0x56,0x42,0x71,0x10,0xdd,0xa3,0x6f,0x51,0x92,0xa8,0x16,0xce,0x9c,0x88,0x16,0xcc,0x7b,0xbf,0xc8,0x04,0xef,0xc4,0x00,0x85,0xa3,0x85,0x0b,0x89,0xf1,0xe7,0xfe,0x56,0x56,0xdb,0xa4,0x10,0xf9,0x06,0xa9,0x7c,0x32,0x33,0x6c,0x1a,0xe7,0xe8,0x17,0x37,0xa8,0x3e,0x08,0x73,0x54,0xe4,0x28,0xda,0x85,0x38,0xd9,0x48,0xdb,0xf5,0xdf,0xac,0xb5,0x9d,0xd2,0xb5,0xfd,0x3b,0xc8,0x03,0xf4,0xba,0x43,0x2c,0x9a,0x73,0x9d,0xf2,0xcf,0xa9,0xed,0x94,0x84,0x32,0x0f,0x97,0xed,0xff,0x1a,0x48,0xc6,0xb8,0x6b,0x30,0x02,0xcf,0xb7,0x72,0xdd,0x5e,0x56,0x2b,0xc4,0xc3,0xd6,0x83,0xed,0x96,0x4b,0x61,0x99,0xfa,0x05,0x14,0xb0,0x79,0x0d,0x95,0x80,0x95,0xb7,0xb8,0x5c,0x6b,0xe8,0x75,0xfb,0xb5,0x59,0xe1,0x93,0x01,0x46,0xcc,0xea,0x63,0xa3,0x88,0xa1,0x94,0xfe,0x09,0xc3,0xde,0xa0,0x3b,0xe5,0x2d,0xe2,0x7e,0x90,0x10,0x17,0xaf,0xe8,0x09,0xaf,0x63,0x0a,0x73,0x82,0xbf,0x5c,0x4c,0xd4,0xd1,0xb8,0xf4,0x15,0x79,0xfb,0x43,0x48,0xed,0xe4,0xca,0x05,0xf4,0xcd,0x3f,0x13,0x9a,0x31,0xb2,0x54,0x4e,0x51,0x6d,0xbe,0x40,0x86,0xb9,0xbb,0x4b,0x2b,0xed,0x47,0xe2,0xd2,0x30,0x98,0x2d,0xd5,0x19,0x24,0x29,0xd3,0x77,0xb7,0xc0,0x74,0x5c,0xc0,0x68,0xe2,0xf5,0xa4,0xaa,0x04,0xc7,0xff,0x87,0x20,0x9e,0xd1,0x25,0x99,0x76,0xa0,0xfc,0x9b,0x25,0xe9,0xe8,0x51,0xd4,0xe3,0x50,0x2c,0x02,0xc8,0x5d,0x6d,0xff,0x02,0x9e,0x21,0x1d,0x01,0xeb,0xf0,0xe9,0xe7,0x18,0x8d,0x56,0x8f,0x84,0x37,0xd8,0x13,0xb0,0xf1,0x22,0xf2,0xfb,0x17,0x60,0x3b,0x69,0x3e,0xd9,0xc3,0x8f,0x17,0xcf,0xd5,0x0b,0x81,0x5e,0x6d,0x9d,0xfc,0x0e,0xd2,0xcc,0xf1,0x9f,0x63,0x99,0x27,0x4a,0x14,0x20,0xf2,0x35,0xa5,0x9d,0x8b,0xf7,0x24,0x34,0x5e,0x14,0xe4,0x5d,0x9e,0x4b,0xe8,0x93,0x4d,0xfc,0x3f,0xa9,0x26,0x78,0xdb,0x61,0xd7,0x11,0x8b,0xf5,0x3c,0xb8,0xa2,0x22,0x5b,0x33,0x5f,0x7e,0xae,0x50,0xe3,0xf9,0x41,0x23,0x76,0x28,0xdb,0x76,0xd8,0xea,0x38,0xf7,0x7a,0x72,0xaf,0x3a,0x26,0xc8,0x1f,0xe4,0x35,0x23,0xb3,0x35,0x53,0x5a,0x5d,0x1d,0xb7,0xc3,0x8f,0x34,0x10,0x82,0xbb,0x57,0x34,0xd0,0x89,0xe8,0xae,0x30,0x9c,0xfd,0xa3,0xa0,0xbc,0xb5,0xcd,0x5b,0x09,0x71,0x13,0xc8,0xed,0xf9,0x61,0x6a,0xa4,0xf6,0xe6,0x63,0x1b,0x91,0x25,0x27,0x6f,0xb3,0xf6,0x80,0xa3,0x43,0x41,0xc3,0xdb,0x66,0x8d,0xc6,0xca,0xd4,0x5f,0xc9,0x3b,0x27,0x08,0xca,0x2a,0xf7,0x5c,0xcc,0xe7,0x34,0xfd,0x19,0x1c,0x50,0x08,0x9d,0xad,0x53,0x98,0x2f,0xdd,0xae,0x02,0x53,0x1f,0xf9,0x3e,0x1f,0x21,0xff,0x39,0x5f,0xc0,0xa1,0x28,0x74,0xed,0xf0,0x6b,0x6f,0x96,0x47,0xe9,0x5a,0x73,0x24,0x58,0x6c,0x71,0xdf,0xd9,0x1d,0x90,0x1d,0x62,0x18,0x58,0x19,0x0f,0xec,0xd0,0x0c,0xcd,0x11,0x0b,0xba,0xc5,0x9f,0x96,0xcb,0x88,0x4c,0x3c,0x93,0x99,0x47,0x48,0xa5,0x6f,0x41,0x28,0x3b,0xfc,0x41,0xfb,0x89,0x05,0x21,0x53,0xa8,0x94,0x58,0x8c,0x3c,0xb9,0x01,0x7f,0x3d,0x66,0x32,0x6c,0x98,0x56,0x37,0xe5,0x75,0xac,0xb8,0x12,0x34,0x63,0x42,0x65,0x40,0x25,0xd6,0x02,0xde,0x3b,0xa9,0x40,0xc1,0x9a,0xc1,0xa6,0x33,0xdf,0xfd,0xa9,0x77,0xb5,0x29,0xb8,0x01,0x3e,0x19,0xc1,0xd6,0xd0,0x68,0x0f,0x4d,0xae,0x62,0xc9,0x24,0x45,0x0a,0xe6,0x6a,0xab,0x82,0xf2,0x14,0x73,0x06,0x1d,0xab,0x3d,0x62,0xb2,0x47,0xf9,0x07,0xe3,0x55,0x19,0x39,0xad,0x3f,0x54,0x65,0xe9,0xd0,0x8a,0x82,0xbf,0xea,0x17,0xee,0xa1,0xb6,0xb2,0xb9,0x23,0x75,0x74,0x77,0xf9,0x93,0x00,0x0b,0x2f,0x43,0xb7,0x0f,0x28,0xaa,0xab,0x1f,0xe9,0xa2,0x6a,0xd1,0xfd,0x33,0x61,0x61,0x6c,0x0b,0x0e,0x24,0x2f,0xe7,0x66,0x04,0xb7,0x03,0x3a,0x1f,0x30,0xe9,0x7e,0x28,0xf5,0x26,0xca,0x3c,0x88,0x0f,0xe2,0xb8,0xd9,0xd1,0xb0,0xc9,0xff,0x18,0x8b,0x31,0xcb,0x9d,0x97,0x42,0x5a,0xca,0xb9,0xb2,0x16,0xd9,0x8a,0x6a,0xe3,0x55,0xe5,0x83,0xda,0x71,0xe8,0x86,0x4e,0xe3,0xd1,0x6b,0x07,0x59,0x79,0x61,0x90,0xef,0x54,0x5c,0x1e,0x62,0xbf,0xef,0x92,0xaf,0x6c,0xa1,0x47,0xb1,0x32,0x44,0xd6,0xc8,0x92,0xfc,0x8e,0xf2,0x23,0xab,0x3f,0x43,0xf9,0x24,0xc2,0xf4,0x66,0x09,0x7e,0xe8]).unwrap(); + let sk = MLDSA65PrivateKey::from_bytes(&[ + 0x48, 0x68, 0x3D, 0x91, 0x97, 0x8E, 0x31, 0xEB, 0x3D, 0xDD, 0xB8, 0xB0, 0x47, 0x34, 0x82, + 0xD2, 0xB8, 0x8A, 0x5F, 0x62, 0x59, 0x49, 0xFD, 0x8F, 0x58, 0xA5, 0x61, 0xE6, 0x96, 0xBD, + 0x4C, 0x27, 0xD8, 0x53, 0xFA, 0x69, 0xB8, 0x19, 0x90, 0x23, 0xE8, 0xCD, 0x67, 0x8D, 0xD9, + 0xFA, 0xBF, 0x90, 0x47, 0x64, 0x6F, 0xFD, 0x0C, 0xB3, 0xCC, 0x7F, 0x79, 0x58, 0x05, 0xA7, + 0x1E, 0x70, 0xD2, 0x37, 0x1B, 0x05, 0x63, 0xE3, 0xCD, 0x33, 0x46, 0x14, 0x9C, 0x8C, 0x9E, + 0xBC, 0xF2, 0x3B, 0x0A, 0x4E, 0x5A, 0x90, 0x0E, 0xEA, 0x9C, 0x65, 0x62, 0x79, 0x0A, 0x7C, + 0x63, 0xE3, 0x86, 0x63, 0xDA, 0xA2, 0xDD, 0xDB, 0x6E, 0x48, 0x0D, 0xC4, 0x05, 0xA1, 0xE7, + 0x01, 0x94, 0x8B, 0x74, 0x84, 0x1E, 0xF5, 0xCC, 0x1C, 0x3F, 0x2B, 0xF3, 0x27, 0x97, 0x2E, + 0x95, 0x10, 0x51, 0x0C, 0xD5, 0x37, 0x5E, 0xCC, 0x08, 0x55, 0x71, 0x77, 0x11, 0x87, 0x22, + 0x21, 0x86, 0x23, 0x81, 0x00, 0x04, 0x24, 0x77, 0x80, 0x61, 0x47, 0x50, 0x07, 0x50, 0x17, + 0x17, 0x03, 0x55, 0x04, 0x51, 0x51, 0x25, 0x47, 0x18, 0x38, 0x04, 0x61, 0x75, 0x72, 0x22, + 0x44, 0x10, 0x88, 0x68, 0x60, 0x86, 0x46, 0x01, 0x27, 0x47, 0x56, 0x71, 0x80, 0x87, 0x06, + 0x66, 0x86, 0x43, 0x32, 0x44, 0x41, 0x22, 0x04, 0x36, 0x38, 0x66, 0x75, 0x02, 0x82, 0x36, + 0x34, 0x24, 0x43, 0x22, 0x05, 0x73, 0x64, 0x10, 0x64, 0x55, 0x54, 0x77, 0x22, 0x75, 0x56, + 0x81, 0x43, 0x36, 0x14, 0x62, 0x55, 0x08, 0x20, 0x64, 0x37, 0x68, 0x54, 0x68, 0x75, 0x43, + 0x53, 0x75, 0x10, 0x68, 0x71, 0x83, 0x33, 0x80, 0x54, 0x75, 0x05, 0x25, 0x80, 0x75, 0x28, + 0x18, 0x84, 0x38, 0x11, 0x08, 0x72, 0x60, 0x20, 0x20, 0x08, 0x58, 0x83, 0x01, 0x83, 0x61, + 0x13, 0x82, 0x82, 0x12, 0x06, 0x17, 0x11, 0x57, 0x87, 0x68, 0x78, 0x88, 0x78, 0x64, 0x37, + 0x54, 0x60, 0x16, 0x57, 0x15, 0x50, 0x84, 0x71, 0x88, 0x66, 0x07, 0x27, 0x32, 0x88, 0x06, + 0x64, 0x74, 0x18, 0x56, 0x76, 0x21, 0x80, 0x31, 0x82, 0x76, 0x64, 0x15, 0x78, 0x24, 0x50, + 0x25, 0x64, 0x66, 0x43, 0x11, 0x35, 0x04, 0x36, 0x47, 0x80, 0x12, 0x66, 0x73, 0x14, 0x30, + 0x11, 0x66, 0x06, 0x55, 0x86, 0x47, 0x18, 0x36, 0x88, 0x63, 0x50, 0x38, 0x47, 0x86, 0x11, + 0x01, 0x20, 0x23, 0x56, 0x11, 0x61, 0x37, 0x86, 0x07, 0x85, 0x32, 0x12, 0x40, 0x07, 0x54, + 0x78, 0x82, 0x30, 0x43, 0x66, 0x61, 0x16, 0x60, 0x42, 0x55, 0x41, 0x82, 0x85, 0x60, 0x53, + 0x67, 0x78, 0x56, 0x38, 0x43, 0x44, 0x30, 0x63, 0x26, 0x10, 0x77, 0x07, 0x31, 0x78, 0x42, + 0x72, 0x14, 0x11, 0x16, 0x53, 0x03, 0x85, 0x27, 0x68, 0x67, 0x46, 0x01, 0x50, 0x82, 0x37, + 0x35, 0x32, 0x07, 0x66, 0x10, 0x75, 0x04, 0x68, 0x12, 0x48, 0x06, 0x66, 0x03, 0x03, 0x26, + 0x52, 0x31, 0x24, 0x45, 0x40, 0x88, 0x00, 0x31, 0x80, 0x88, 0x76, 0x72, 0x17, 0x30, 0x71, + 0x82, 0x47, 0x21, 0x51, 0x27, 0x80, 0x11, 0x65, 0x44, 0x74, 0x86, 0x61, 0x72, 0x23, 0x33, + 0x80, 0x86, 0x60, 0x64, 0x46, 0x83, 0x52, 0x15, 0x84, 0x20, 0x36, 0x80, 0x11, 0x80, 0x21, + 0x18, 0x18, 0x33, 0x17, 0x73, 0x54, 0x53, 0x48, 0x81, 0x00, 0x44, 0x86, 0x53, 0x67, 0x43, + 0x70, 0x57, 0x72, 0x58, 0x83, 0x34, 0x60, 0x38, 0x42, 0x32, 0x85, 0x68, 0x10, 0x06, 0x04, + 0x26, 0x04, 0x25, 0x84, 0x56, 0x02, 0x35, 0x68, 0x20, 0x51, 0x83, 0x86, 0x38, 0x43, 0x24, + 0x21, 0x22, 0x42, 0x45, 0x64, 0x58, 0x58, 0x67, 0x71, 0x45, 0x72, 0x85, 0x04, 0x78, 0x87, + 0x17, 0x18, 0x06, 0x18, 0x83, 0x60, 0x86, 0x86, 0x41, 0x56, 0x50, 0x81, 0x16, 0x50, 0x26, + 0x46, 0x70, 0x06, 0x08, 0x26, 0x62, 0x27, 0x38, 0x31, 0x72, 0x40, 0x72, 0x57, 0x30, 0x07, + 0x27, 0x28, 0x86, 0x20, 0x66, 0x75, 0x88, 0x68, 0x26, 0x07, 0x06, 0x40, 0x20, 0x33, 0x03, + 0x43, 0x66, 0x31, 0x55, 0x46, 0x42, 0x45, 0x34, 0x56, 0x67, 0x18, 0x73, 0x45, 0x65, 0x83, + 0x70, 0x22, 0x50, 0x84, 0x68, 0x56, 0x28, 0x80, 0x70, 0x36, 0x70, 0x84, 0x62, 0x37, 0x17, + 0x10, 0x06, 0x57, 0x17, 0x58, 0x47, 0x78, 0x70, 0x86, 0x55, 0x53, 0x78, 0x22, 0x35, 0x14, + 0x46, 0x77, 0x28, 0x56, 0x73, 0x03, 0x22, 0x87, 0x00, 0x14, 0x33, 0x20, 0x61, 0x71, 0x58, + 0x45, 0x52, 0x66, 0x32, 0x50, 0x26, 0x51, 0x33, 0x47, 0x77, 0x38, 0x03, 0x55, 0x16, 0x43, + 0x13, 0x47, 0x35, 0x10, 0x66, 0x27, 0x51, 0x75, 0x74, 0x02, 0x46, 0x88, 0x81, 0x70, 0x67, + 0x43, 0x46, 0x81, 0x86, 0x01, 0x76, 0x52, 0x45, 0x33, 0x30, 0x87, 0x21, 0x04, 0x34, 0x34, + 0x01, 0x03, 0x22, 0x87, 0x63, 0x51, 0x55, 0x26, 0x50, 0x81, 0x30, 0x77, 0x45, 0x44, 0x41, + 0x68, 0x15, 0x41, 0x83, 0x63, 0x64, 0x11, 0x20, 0x40, 0x26, 0x87, 0x30, 0x43, 0x67, 0x77, + 0x12, 0x80, 0x88, 0x46, 0x35, 0x54, 0x53, 0x00, 0x62, 0x45, 0x81, 0x04, 0x58, 0x36, 0x51, + 0x24, 0x84, 0x27, 0x80, 0x34, 0x51, 0x66, 0x63, 0x58, 0x43, 0x78, 0x56, 0x01, 0x46, 0x51, + 0x15, 0x74, 0x23, 0x21, 0x43, 0x66, 0x85, 0x22, 0x47, 0x77, 0x31, 0x34, 0x50, 0x17, 0x83, + 0x62, 0x42, 0x05, 0x50, 0x00, 0x64, 0x84, 0x47, 0x12, 0x34, 0x40, 0x88, 0x00, 0x60, 0x47, + 0x35, 0x40, 0x57, 0x83, 0x33, 0x63, 0x08, 0x21, 0x06, 0x15, 0x22, 0x52, 0x07, 0x24, 0x88, + 0x51, 0x34, 0x86, 0x37, 0x06, 0x76, 0x22, 0x58, 0x85, 0x71, 0x26, 0x56, 0x73, 0x47, 0x68, + 0x16, 0x46, 0x46, 0x84, 0x25, 0x87, 0x08, 0x12, 0x27, 0x05, 0x50, 0x08, 0x38, 0x32, 0x00, + 0x23, 0x20, 0x80, 0x66, 0x34, 0x53, 0x36, 0x00, 0x33, 0x46, 0x85, 0x72, 0x47, 0x06, 0x35, + 0x54, 0x00, 0x35, 0x77, 0x12, 0x27, 0x52, 0x30, 0x71, 0x42, 0x53, 0x68, 0x74, 0x37, 0x45, + 0x70, 0x05, 0x66, 0x43, 0x22, 0x44, 0x82, 0x85, 0x20, 0x72, 0x18, 0x33, 0x30, 0x20, 0x53, + 0x37, 0x33, 0x40, 0x77, 0x27, 0x80, 0x55, 0x25, 0x30, 0x63, 0x52, 0x50, 0x40, 0x67, 0x33, + 0x46, 0x13, 0x18, 0x07, 0x28, 0x07, 0x17, 0x24, 0x83, 0x77, 0x63, 0x45, 0x73, 0x18, 0x58, + 0x51, 0x60, 0x23, 0x33, 0x44, 0x36, 0x25, 0x16, 0x43, 0x38, 0x16, 0x08, 0x58, 0x77, 0x34, + 0x62, 0x42, 0x88, 0x30, 0x07, 0x03, 0x65, 0x85, 0x37, 0x55, 0x00, 0x75, 0x52, 0x31, 0x50, + 0x37, 0x02, 0x13, 0x24, 0x63, 0x04, 0x37, 0x08, 0x68, 0x06, 0x36, 0x15, 0x03, 0x03, 0x00, + 0x43, 0x58, 0x63, 0x57, 0x08, 0x02, 0x11, 0x06, 0x64, 0x73, 0x46, 0x35, 0x22, 0x62, 0x03, + 0x30, 0x43, 0x80, 0x21, 0x08, 0x52, 0x87, 0x57, 0x83, 0x21, 0x07, 0x88, 0x67, 0x48, 0x08, + 0x56, 0x34, 0x74, 0x36, 0x73, 0x42, 0x84, 0x05, 0x84, 0x66, 0x84, 0x14, 0x37, 0x00, 0x55, + 0x10, 0x87, 0x34, 0x26, 0x44, 0x77, 0x21, 0x12, 0x73, 0x84, 0x73, 0x65, 0x26, 0x47, 0x25, + 0x77, 0x14, 0x47, 0x04, 0x17, 0x86, 0x44, 0x26, 0x02, 0x47, 0x11, 0x87, 0x40, 0x81, 0x22, + 0x16, 0x60, 0x58, 0x47, 0x17, 0x81, 0x37, 0x06, 0x76, 0x80, 0x81, 0x70, 0x58, 0x18, 0x55, + 0x85, 0x47, 0x13, 0x63, 0x42, 0x10, 0x75, 0x58, 0x01, 0x63, 0x58, 0x35, 0x85, 0x18, 0x44, + 0x03, 0x84, 0x71, 0x10, 0x33, 0x87, 0x42, 0x62, 0x82, 0x47, 0x74, 0x13, 0x65, 0x54, 0x42, + 0x70, 0x73, 0x46, 0x35, 0x77, 0x75, 0x00, 0x66, 0x25, 0x62, 0x68, 0x42, 0x02, 0x12, 0x46, + 0x83, 0x86, 0x46, 0x16, 0x64, 0x60, 0x31, 0x22, 0x53, 0x88, 0x84, 0x54, 0x00, 0x84, 0x57, + 0x34, 0x46, 0x47, 0x54, 0x47, 0x25, 0x60, 0x54, 0x61, 0x66, 0x84, 0x66, 0x30, 0x88, 0x06, + 0x38, 0x27, 0x15, 0x63, 0x28, 0x71, 0x83, 0x84, 0x06, 0x52, 0x24, 0x76, 0x81, 0x16, 0x06, + 0x62, 0x13, 0x03, 0x30, 0x18, 0x68, 0x02, 0x80, 0x13, 0x84, 0x63, 0x05, 0x05, 0x65, 0x72, + 0x38, 0x75, 0x83, 0x65, 0x72, 0x32, 0x30, 0x68, 0x80, 0x46, 0x12, 0x26, 0x06, 0x65, 0x16, + 0x75, 0x57, 0x05, 0x32, 0x41, 0x32, 0x27, 0x67, 0x35, 0x17, 0x08, 0x01, 0x53, 0x00, 0x16, + 0x28, 0x46, 0x01, 0x34, 0x88, 0x77, 0x01, 0x11, 0x88, 0x15, 0x57, 0x13, 0x15, 0x46, 0x43, + 0x11, 0x70, 0x47, 0x32, 0x88, 0x28, 0x56, 0x36, 0x82, 0x34, 0x55, 0x50, 0x41, 0x86, 0x27, + 0x65, 0x63, 0x11, 0x11, 0x68, 0x75, 0x05, 0x10, 0x42, 0x54, 0x41, 0x44, 0x27, 0x85, 0x22, + 0x11, 0x17, 0x17, 0x88, 0x15, 0x36, 0x85, 0x15, 0x74, 0x47, 0x16, 0x62, 0x55, 0x36, 0x55, + 0x83, 0x63, 0x02, 0x50, 0x28, 0x55, 0x76, 0x87, 0x53, 0x27, 0x13, 0x71, 0x03, 0x72, 0x37, + 0x05, 0x71, 0x47, 0x61, 0x71, 0x36, 0x51, 0x84, 0x12, 0x42, 0x36, 0x64, 0x44, 0x66, 0x41, + 0x43, 0x52, 0x05, 0x21, 0x08, 0x51, 0x57, 0x03, 0x33, 0x63, 0x86, 0x02, 0x58, 0x42, 0x66, + 0x28, 0x14, 0x81, 0x10, 0x54, 0x62, 0x68, 0x17, 0x30, 0x38, 0x75, 0x64, 0x33, 0x21, 0x65, + 0x88, 0x56, 0x86, 0x63, 0x63, 0x28, 0x13, 0x40, 0x62, 0x54, 0x01, 0x20, 0x40, 0x88, 0x65, + 0x47, 0x88, 0x61, 0x71, 0x65, 0x76, 0x23, 0x72, 0x62, 0x34, 0x86, 0x70, 0x30, 0x11, 0x51, + 0x15, 0x63, 0x20, 0x50, 0x75, 0x35, 0x02, 0x12, 0x21, 0x08, 0x42, 0x65, 0x31, 0x43, 0x55, + 0x67, 0x11, 0x15, 0x25, 0x72, 0x01, 0x06, 0x85, 0x36, 0x30, 0x15, 0x05, 0x57, 0x58, 0x60, + 0x58, 0x78, 0x43, 0x14, 0x31, 0x32, 0x78, 0x78, 0x80, 0x87, 0x38, 0x47, 0x88, 0x63, 0x78, + 0x81, 0x81, 0x38, 0x73, 0x42, 0x61, 0x78, 0x38, 0x85, 0x24, 0x66, 0x77, 0x33, 0x50, 0x60, + 0x21, 0x15, 0x14, 0x64, 0x23, 0x82, 0x32, 0x68, 0x01, 0x35, 0x44, 0x07, 0x83, 0x47, 0x53, + 0x85, 0x53, 0x57, 0x52, 0x83, 0x23, 0x35, 0x18, 0x76, 0x01, 0x15, 0x21, 0x34, 0x32, 0x57, + 0x73, 0x33, 0x36, 0x55, 0x18, 0x86, 0x15, 0x81, 0x61, 0x68, 0x24, 0x18, 0x42, 0x21, 0x22, + 0x30, 0x84, 0x14, 0x48, 0x15, 0x12, 0x01, 0x10, 0x30, 0x24, 0x77, 0x72, 0x42, 0x54, 0x43, + 0x66, 0x06, 0x77, 0x17, 0x70, 0x76, 0x03, 0x01, 0x45, 0x25, 0x40, 0x35, 0x00, 0x18, 0x38, + 0x73, 0x23, 0x77, 0x35, 0x26, 0x50, 0x86, 0x35, 0x71, 0x13, 0x73, 0x44, 0x81, 0x60, 0x52, + 0x77, 0x45, 0x65, 0x53, 0x73, 0x00, 0x85, 0x83, 0x77, 0x85, 0x03, 0x51, 0x21, 0x11, 0x54, + 0x80, 0x62, 0x88, 0x50, 0x18, 0x02, 0x68, 0x13, 0x86, 0x52, 0x05, 0x34, 0x68, 0x01, 0x32, + 0x07, 0x24, 0x18, 0x03, 0x21, 0x30, 0x05, 0x72, 0x38, 0x64, 0x07, 0x64, 0x27, 0x11, 0x41, + 0x01, 0x83, 0x85, 0x25, 0x51, 0x06, 0x32, 0x60, 0x71, 0x04, 0x86, 0x51, 0x76, 0x83, 0x38, + 0x28, 0x57, 0x27, 0x62, 0x35, 0x45, 0x18, 0x73, 0x50, 0x83, 0x13, 0x28, 0x86, 0x37, 0x66, + 0x61, 0x42, 0x63, 0x11, 0x67, 0x50, 0x33, 0x11, 0x25, 0x53, 0x76, 0x41, 0x76, 0x03, 0x14, + 0x33, 0x17, 0x72, 0x12, 0x23, 0x44, 0x18, 0xA8, 0x2E, 0x4F, 0x5C, 0x9E, 0xA0, 0xFA, 0xF9, + 0x9E, 0xB0, 0x4D, 0x78, 0xA7, 0x33, 0x27, 0x11, 0x11, 0x7C, 0x33, 0xF1, 0x8E, 0xCA, 0x21, + 0xF8, 0x74, 0x33, 0x76, 0xAD, 0xA5, 0x21, 0x98, 0x04, 0xA7, 0xED, 0x9A, 0x55, 0x57, 0xFC, + 0xD6, 0x7A, 0x35, 0x50, 0xB3, 0xA4, 0xB8, 0xC5, 0x88, 0x62, 0x9C, 0x02, 0x14, 0x75, 0xFA, + 0x3D, 0x56, 0xD5, 0xD6, 0xCF, 0xBB, 0x1A, 0x09, 0xBD, 0xA8, 0xD1, 0x4D, 0xE6, 0x22, 0xDD, + 0xFF, 0x16, 0xD8, 0xBC, 0x99, 0xB1, 0x42, 0x78, 0xA8, 0xAF, 0x1D, 0x76, 0xBE, 0xD1, 0x57, + 0x67, 0x2D, 0xD9, 0xC3, 0x23, 0x16, 0xF9, 0x7E, 0x8D, 0xAA, 0xDE, 0xF8, 0xD9, 0xDA, 0x69, + 0x58, 0x67, 0x25, 0x56, 0x7F, 0xB9, 0x6B, 0x59, 0x99, 0x0D, 0x4B, 0xF0, 0xBC, 0x9C, 0x19, + 0x5B, 0x90, 0xB7, 0x42, 0x95, 0xF5, 0x67, 0x5B, 0x24, 0x25, 0x7C, 0x27, 0x10, 0xC1, 0x75, + 0xB0, 0x15, 0x3F, 0x29, 0x11, 0x32, 0x8C, 0x2E, 0xB7, 0xAB, 0xB9, 0xAD, 0x46, 0xE7, 0x0A, + 0x8B, 0x53, 0xC3, 0x9E, 0xA6, 0x42, 0xCE, 0xE4, 0xB3, 0xCB, 0x42, 0x62, 0x0E, 0x86, 0x3C, + 0xE8, 0xB6, 0x50, 0xCE, 0x8A, 0xDC, 0xD9, 0x23, 0x72, 0x1A, 0x16, 0x87, 0x02, 0x3C, 0x67, + 0x3A, 0x8C, 0xBB, 0x6B, 0x03, 0xD5, 0x1C, 0xD1, 0x97, 0xE8, 0xC3, 0x46, 0xEB, 0xAD, 0xCE, + 0x93, 0x95, 0x0F, 0x88, 0xCE, 0xE2, 0x01, 0xDB, 0x9E, 0x32, 0x08, 0x43, 0xE2, 0x9F, 0x30, + 0x0D, 0x9A, 0x19, 0x50, 0x0D, 0x70, 0xA4, 0xCA, 0xF2, 0x72, 0xC6, 0x9E, 0x4E, 0xEF, 0x69, + 0xFB, 0xB8, 0xA5, 0x5E, 0xFD, 0x7C, 0xA2, 0xBE, 0xD9, 0x90, 0xD2, 0xD3, 0xB5, 0x82, 0x84, + 0x8F, 0x9C, 0x45, 0xC2, 0xAB, 0xC5, 0x4C, 0xFC, 0x47, 0xD3, 0x4F, 0x06, 0xC0, 0xFF, 0xA5, + 0x6F, 0xCD, 0x76, 0x2A, 0xB9, 0xCB, 0xA9, 0x14, 0x6D, 0x77, 0x25, 0x21, 0x89, 0x63, 0xB2, + 0x40, 0xD7, 0x2B, 0x6D, 0x22, 0xC9, 0x31, 0x71, 0xFB, 0xD4, 0x77, 0x88, 0xB7, 0x6E, 0x72, + 0x04, 0x2D, 0xEF, 0x08, 0x78, 0xD2, 0x3D, 0xF6, 0x31, 0xA1, 0xA1, 0xE5, 0xA6, 0x02, 0x76, + 0x86, 0xDE, 0x5B, 0x4A, 0x10, 0xE9, 0x10, 0x69, 0xC8, 0xF2, 0xBA, 0x02, 0x59, 0xB0, 0x4D, + 0x64, 0x09, 0xDA, 0x96, 0x56, 0x7C, 0xA5, 0x2D, 0xA4, 0x97, 0x02, 0x6E, 0x58, 0x3A, 0x0E, + 0xCE, 0xFC, 0x1F, 0x01, 0xE6, 0xB9, 0x88, 0xE2, 0x1F, 0x97, 0x67, 0xA2, 0xB7, 0xE1, 0x67, + 0x2D, 0xEB, 0x9A, 0x1E, 0x2A, 0x3F, 0xCC, 0x86, 0x3A, 0xA9, 0x15, 0x17, 0xC3, 0x34, 0x62, + 0x06, 0x01, 0xB4, 0xFE, 0x79, 0x73, 0x0E, 0x93, 0x49, 0x35, 0xF4, 0xB6, 0xFB, 0xC4, 0xE3, + 0x26, 0x95, 0x14, 0x5C, 0x2B, 0x5F, 0x6A, 0x12, 0x7F, 0xEC, 0xC0, 0xA2, 0x77, 0x45, 0x1E, + 0xBC, 0x3F, 0xD5, 0x23, 0x44, 0x4F, 0x9E, 0xE7, 0xC9, 0xC3, 0x45, 0x34, 0xF3, 0x56, 0xDB, + 0x54, 0x4F, 0xC3, 0x1C, 0x1B, 0xFD, 0xE5, 0xF6, 0x5C, 0x77, 0xEA, 0x2F, 0x7C, 0x2E, 0xAE, + 0x4C, 0x55, 0xEB, 0xAF, 0x10, 0x42, 0x71, 0xC5, 0x66, 0xFD, 0x4E, 0xBA, 0xC7, 0x1C, 0x7A, + 0x62, 0xC7, 0x49, 0x52, 0x81, 0x7A, 0xE6, 0x75, 0x50, 0x4D, 0x95, 0x99, 0xB1, 0xB7, 0x62, + 0xB6, 0xAC, 0xA1, 0x68, 0xA8, 0x32, 0x48, 0xC9, 0xD9, 0xAD, 0xB0, 0xCE, 0xB1, 0x55, 0x6E, + 0x57, 0x59, 0x49, 0x0B, 0xBC, 0x0C, 0x79, 0x00, 0x79, 0x5A, 0xD7, 0x21, 0x23, 0x03, 0x8B, + 0x66, 0x2F, 0x64, 0xF1, 0x06, 0xA9, 0x99, 0x36, 0x81, 0xA2, 0x5D, 0x59, 0xAF, 0x7B, 0xC9, + 0x7A, 0x23, 0x5B, 0xE9, 0x28, 0x4C, 0x5B, 0xC4, 0x5A, 0x6C, 0x90, 0xCB, 0x1C, 0x29, 0x99, + 0xC6, 0x63, 0xD9, 0x6B, 0x47, 0x8E, 0x23, 0x07, 0xF8, 0x55, 0x48, 0x95, 0x7D, 0x65, 0x74, + 0x0E, 0x26, 0x73, 0xE9, 0xEB, 0xD1, 0x35, 0x28, 0x29, 0x03, 0x8F, 0x46, 0x2B, 0x8F, 0xD3, + 0xB5, 0x68, 0x1D, 0xA5, 0x5C, 0x02, 0x52, 0x52, 0x38, 0x53, 0x52, 0x5E, 0xA0, 0xAD, 0x64, + 0x7E, 0x71, 0xAC, 0x2C, 0x5A, 0x88, 0x93, 0xE6, 0x03, 0xAC, 0x97, 0xE5, 0x6C, 0x04, 0xCE, + 0xB2, 0xF2, 0x6F, 0x5C, 0x5B, 0x4B, 0x6D, 0x94, 0xAB, 0x81, 0x13, 0x80, 0xFD, 0x00, 0xF2, + 0x20, 0x8F, 0xE8, 0x65, 0x35, 0x08, 0x6A, 0xEB, 0xFD, 0x35, 0xC2, 0x91, 0x20, 0x62, 0x4C, + 0x04, 0xFB, 0xB6, 0x11, 0x39, 0x29, 0xD9, 0xC5, 0x56, 0x35, 0x02, 0x53, 0x76, 0x6C, 0x20, + 0x9F, 0xDB, 0xA8, 0x3C, 0x95, 0xFC, 0xCD, 0x34, 0x2A, 0x28, 0x09, 0x93, 0x55, 0xD0, 0x0B, + 0xC8, 0x63, 0xF4, 0xEE, 0xF5, 0x96, 0xEB, 0x0B, 0x42, 0xEB, 0xCC, 0x7C, 0x79, 0x49, 0x1C, + 0xCE, 0xAE, 0x20, 0x5E, 0xA0, 0xB8, 0x05, 0x9F, 0xBB, 0x8A, 0x57, 0x26, 0xC5, 0x94, 0x9D, + 0x2B, 0x15, 0xE7, 0xE2, 0x9C, 0x51, 0xFC, 0x9B, 0x02, 0xEE, 0x1A, 0x4F, 0xC3, 0x57, 0xB5, + 0xF1, 0xBE, 0xF9, 0xC4, 0xAD, 0xD4, 0x6A, 0x2A, 0x92, 0x0C, 0x2F, 0xBF, 0x08, 0xA3, 0x7E, + 0xB1, 0x51, 0x4B, 0xFA, 0x15, 0x11, 0x0A, 0x43, 0x92, 0xA7, 0x4C, 0x6F, 0x13, 0xC5, 0x0C, + 0x5C, 0xFF, 0xD9, 0x75, 0x31, 0x09, 0x8D, 0x7C, 0xD2, 0x3B, 0x60, 0xEB, 0x35, 0xC4, 0xA4, + 0x28, 0xB4, 0x6C, 0x55, 0x38, 0x6E, 0x10, 0x10, 0xC4, 0xBA, 0x7F, 0x70, 0xE4, 0xC7, 0xEC, + 0xB7, 0x57, 0x5F, 0x30, 0x63, 0xA7, 0x1E, 0x84, 0xDF, 0xDC, 0xF0, 0x9A, 0x58, 0xB2, 0xCD, + 0xB0, 0xF9, 0x9F, 0x27, 0xED, 0x37, 0x86, 0x10, 0xD2, 0x5C, 0xBA, 0xD7, 0xBF, 0xA6, 0xBA, + 0x0D, 0x59, 0x18, 0x9C, 0xFE, 0x88, 0xEA, 0xB9, 0xB4, 0x6D, 0x7E, 0x6D, 0xB0, 0x30, 0x7E, + 0xAB, 0xE4, 0x19, 0x8E, 0x99, 0xBD, 0x71, 0xF7, 0x79, 0xAB, 0x66, 0x58, 0x1E, 0x09, 0x12, + 0xFC, 0x7B, 0x1D, 0x25, 0x85, 0x24, 0x5E, 0x9A, 0x12, 0x68, 0x7A, 0x97, 0x5C, 0xD5, 0xE8, + 0xE1, 0xDC, 0xC0, 0x45, 0xD5, 0xF8, 0x91, 0xC4, 0xC6, 0x85, 0xDB, 0x07, 0xCF, 0x81, 0xE7, + 0x73, 0x89, 0xB3, 0x63, 0xEB, 0x6B, 0xDF, 0xE3, 0x9B, 0x27, 0xFF, 0x84, 0xC9, 0x7E, 0xEF, + 0xEE, 0x16, 0x2E, 0x3B, 0x45, 0x1F, 0xE6, 0x91, 0x47, 0x19, 0xCB, 0x64, 0x36, 0xD8, 0x55, + 0x96, 0x0F, 0xF9, 0x15, 0xD7, 0xCE, 0xA6, 0xAD, 0xEA, 0xFD, 0xFC, 0x1C, 0x05, 0x78, 0x6C, + 0x49, 0xF9, 0x23, 0xA4, 0x74, 0xFF, 0xDF, 0xC3, 0x15, 0x3A, 0x06, 0xE6, 0xED, 0x0B, 0x0A, + 0xD2, 0x20, 0xD7, 0x25, 0x24, 0x43, 0x4D, 0x52, 0x73, 0xC0, 0xAA, 0xB6, 0xDD, 0xE4, 0xE9, + 0x14, 0x76, 0xD5, 0x81, 0xA2, 0x69, 0x5A, 0x60, 0xDE, 0x6D, 0x9F, 0x44, 0xD7, 0x7A, 0xA0, + 0x82, 0x66, 0xE9, 0x38, 0xEE, 0xB4, 0xA9, 0x59, 0x7C, 0x9B, 0x64, 0x98, 0x60, 0x59, 0xE4, + 0x92, 0x62, 0xA4, 0xEA, 0xB2, 0x45, 0x4E, 0x14, 0x01, 0x5A, 0xD0, 0x53, 0x6C, 0x42, 0x73, + 0x3A, 0x5D, 0x77, 0xD7, 0x99, 0x5C, 0x2A, 0x20, 0x44, 0x60, 0x09, 0xEB, 0xFE, 0x56, 0x32, + 0xC8, 0x0C, 0x08, 0xED, 0x2B, 0x97, 0xAF, 0x35, 0x06, 0x64, 0x89, 0xF5, 0x97, 0xEB, 0x1B, + 0x1F, 0x11, 0xF0, 0x4F, 0x60, 0xE0, 0xC9, 0x04, 0x01, 0x59, 0xC4, 0x4A, 0xB3, 0xE6, 0x0E, + 0x0A, 0x15, 0x22, 0x9D, 0x19, 0x12, 0x28, 0xBE, 0xD1, 0x7B, 0xBC, 0x3A, 0xC9, 0x39, 0xB3, + 0xC6, 0x7C, 0xEE, 0x13, 0x5F, 0x35, 0x2C, 0x27, 0x21, 0x6C, 0x9C, 0x31, 0xF7, 0x2A, 0x3E, + 0x87, 0x04, 0x0C, 0x5F, 0x61, 0x93, 0x06, 0xEB, 0x0B, 0x6C, 0xCA, 0x2A, 0x9C, 0xE7, 0xB2, + 0x2A, 0x16, 0x94, 0xD0, 0x0C, 0xA9, 0xC0, 0x5E, 0x31, 0x51, 0x26, 0x45, 0x7F, 0x26, 0xCE, + 0x84, 0xF9, 0x61, 0x72, 0x41, 0x86, 0x07, 0x82, 0xF8, 0x64, 0xB4, 0x73, 0xD8, 0x40, 0x17, + 0x49, 0x19, 0x02, 0xB1, 0xBD, 0xC8, 0xCD, 0xC5, 0x80, 0x0D, 0xD4, 0x61, 0x27, 0xFB, 0x80, + 0xA7, 0x1C, 0x09, 0x5B, 0x47, 0x3A, 0x56, 0x25, 0x29, 0xB3, 0xB1, 0xE7, 0xE4, 0x37, 0xE1, + 0x58, 0xA5, 0xF6, 0x66, 0x6E, 0x99, 0x74, 0xD0, 0x05, 0xB0, 0x62, 0xC2, 0x30, 0x9E, 0x6D, + 0xCE, 0x98, 0xF9, 0xB6, 0x58, 0xC6, 0xE3, 0xF9, 0xA2, 0x16, 0xD5, 0x8C, 0x8C, 0x91, 0x42, + 0xBD, 0x1C, 0x8C, 0x85, 0xA9, 0xDA, 0x87, 0x2E, 0xBB, 0xFA, 0xD3, 0xFE, 0xA9, 0xD9, 0xAB, + 0xA2, 0xB6, 0x8C, 0x0E, 0x8F, 0x19, 0xC6, 0xFF, 0x5F, 0x00, 0x58, 0x4D, 0x45, 0xDA, 0xF9, + 0xD6, 0xC9, 0xD6, 0x9E, 0xD0, 0x4B, 0x8D, 0xA8, 0xD6, 0x87, 0x25, 0x8B, 0x77, 0x80, 0x79, + 0x27, 0x61, 0x2C, 0x53, 0x04, 0x46, 0xFE, 0xA7, 0x69, 0x7A, 0xE3, 0xF9, 0x26, 0x69, 0x89, + 0x29, 0xBC, 0x6A, 0x5A, 0x8C, 0xF3, 0xE2, 0x02, 0x4C, 0x0F, 0x0C, 0x5E, 0xE5, 0x7B, 0x58, + 0x69, 0xBF, 0x98, 0x18, 0x81, 0xCA, 0xF9, 0xE3, 0x66, 0x5F, 0xC7, 0xF7, 0xEF, 0xC6, 0x78, + 0x92, 0x9F, 0x87, 0xA5, 0x6E, 0xAA, 0x42, 0xEA, 0x4D, 0x1F, 0xF6, 0x69, 0x18, 0x22, 0xDD, + 0x79, 0xA4, 0x70, 0x96, 0xB7, 0x76, 0xD1, 0xD8, 0xF0, 0x14, 0x56, 0xE5, 0x87, 0x3B, 0x07, + 0x38, 0x40, 0x6C, 0x38, 0x2C, 0x57, 0x3A, 0xE9, 0xCD, 0xE2, 0xD9, 0xE7, 0xF2, 0x31, 0xB6, + 0xCC, 0x5C, 0x67, 0x6E, 0x7C, 0xF4, 0x39, 0x63, 0x37, 0x30, 0x13, 0xA5, 0x80, 0x75, 0x38, + 0x1F, 0xF0, 0x94, 0x9B, 0xE0, 0x84, 0x54, 0x6D, 0x72, 0xE4, 0xF8, 0xA3, 0xE5, 0xFE, 0x4A, + 0xA5, 0x09, 0x1A, 0xDD, 0x23, 0x4E, 0x2A, 0xFE, 0x00, 0x30, 0xB1, 0xB6, 0x63, 0xAE, 0x9D, + 0x2D, 0x32, 0x41, 0x09, 0x86, 0xB9, 0x40, 0x2A, 0xAA, 0xF2, 0x46, 0x5B, 0x74, 0xA5, 0xE2, + 0xD0, 0xBC, 0x38, 0xE3, 0xA9, 0x2B, 0xBD, 0xDD, 0x8A, 0x1F, 0xED, 0x7B, 0x94, 0x8C, 0x23, + 0xCC, 0xE6, 0xF8, 0xC0, 0x8F, 0xE3, 0x56, 0x83, 0x5B, 0xA6, 0x5B, 0x0F, 0x98, 0x40, 0x68, + 0x61, 0x6E, 0xF4, 0x81, 0x38, 0xEF, 0xD8, 0x9B, 0xF3, 0x57, 0xA5, 0x4D, 0x2E, 0xBB, 0xF3, + 0x76, 0xCB, 0xDC, 0xC6, 0x9C, 0x5F, 0x1F, 0x61, 0xC6, 0x4D, 0x27, 0x94, 0xBC, 0x06, 0xCC, + 0xB9, 0xAB, 0xDF, 0x66, 0xE2, 0x50, 0x85, 0xD8, 0xC8, 0x30, 0xE2, 0xAE, 0x3B, 0x0F, 0xE0, + 0xF0, 0x7A, 0x7A, 0xF8, 0xB9, 0x32, 0x0B, 0xF3, 0x42, 0x97, 0x09, 0x97, 0xD6, 0x7D, 0x7C, + 0x12, 0x59, 0x3A, 0x8F, 0xBF, 0xAD, 0xE6, 0x35, 0xAA, 0xC5, 0x30, 0x83, 0xA7, 0x02, 0x2C, + 0x47, 0xD5, 0xF7, 0x7A, 0x52, 0xB5, 0x7B, 0x59, 0x8D, 0xA9, 0x39, 0x2A, 0xE6, 0xD8, 0x6A, + 0xFC, 0x46, 0xFC, 0x06, 0x45, 0x51, 0x81, 0xB9, 0xC7, 0x5A, 0x64, 0x6D, 0xC2, 0x1F, 0x81, + 0xE4, 0xBF, 0x21, 0x37, 0x53, 0xDE, 0x73, 0x7F, 0xD2, 0xA1, 0x40, 0x02, 0x79, 0x20, 0xAD, + 0xD3, 0x5A, 0x22, 0x3F, 0x9F, 0x5F, 0x44, 0x65, 0xCE, 0xB6, 0x0C, 0x03, 0xED, 0x04, 0x55, + 0xA3, 0x33, 0xA5, 0xCC, 0x83, 0xAD, 0xBF, 0x43, 0xF1, 0xF4, 0x2C, 0x2C, 0xCB, 0x83, 0x28, + 0xC2, 0x1C, 0x7A, 0xB7, 0xFA, 0xED, 0x2B, 0x21, 0xCF, 0xAD, 0xE2, 0xDA, 0x55, 0x22, 0x3A, + 0xAA, 0xB2, 0xAF, 0x9B, 0x41, 0xC7, 0x33, 0x23, 0x41, 0x74, 0x63, 0x41, 0xB3, 0x9A, 0xA2, + 0xF4, 0x38, 0x15, 0x65, 0x0F, 0x54, 0x80, 0x51, 0x14, 0x24, 0xCF, 0xA6, 0x90, 0x17, 0x79, + 0xC4, 0xD1, 0x8B, 0x63, 0x8C, 0xC0, 0x28, 0x7A, 0xAA, 0xF3, 0x16, 0x80, 0x33, 0x8D, 0x20, + 0xB1, 0x7C, 0x74, 0x49, 0xFD, 0xC6, 0xA2, 0x78, 0xA8, 0xD9, 0x6A, 0x82, 0xEE, 0x4C, 0x4E, + 0xCA, 0x40, 0x12, 0x5E, 0x2D, 0x65, 0x29, 0x00, 0x71, 0xC7, 0xAE, 0xF1, 0xBE, 0x6A, 0x99, + 0x15, 0x98, 0xFB, 0x9D, 0x59, 0x51, 0x25, 0x23, 0xBC, 0xD4, 0xB3, 0x8C, 0x56, 0x6B, 0x8E, + 0x80, 0xA7, 0x3A, 0xE3, 0x33, 0xE1, 0x34, 0x41, 0x43, 0x27, 0xEF, 0x1D, 0x83, 0xC4, 0x7C, + 0x49, 0xDF, 0xE7, 0x93, 0x6D, 0xF1, 0x33, 0x8A, 0x5E, 0x24, 0x77, 0x87, 0x86, 0x8F, 0xC8, + 0x4F, 0xDC, 0xB9, 0x5A, 0xC8, 0x9C, 0x18, 0x5C, 0x4B, 0xB5, 0xFD, 0x57, 0xB2, 0x33, 0x8A, + 0xC4, 0x2B, 0x41, 0xC1, 0x0A, 0x82, 0x3D, 0xF3, 0x96, 0x24, 0xF3, 0x6B, 0x15, 0xA2, 0xF0, + 0x67, 0x58, 0x4E, 0x06, 0xCA, 0x2E, 0x08, 0xCC, 0xAF, 0xF1, 0x61, 0x8F, 0xE0, 0x1D, 0xD0, + 0x6D, 0xF3, 0x51, 0x2E, 0x0B, 0x72, 0x4D, 0xEC, 0x85, 0x06, 0xDA, 0x24, 0x21, 0x5A, 0xCA, + 0xCC, 0x2C, 0x51, 0xB8, 0x2A, 0xD8, 0xD3, 0x02, 0x00, 0x2F, 0xB4, 0x10, 0x68, 0xB1, 0xDA, + 0x4F, 0x8B, 0xB1, 0x47, 0x98, 0x7B, 0x35, 0x16, 0xBA, 0xD5, 0xDB, 0xDD, 0xF0, 0x13, 0x18, + 0xFD, 0x3F, 0xA9, 0xBC, 0x43, 0x70, 0x2A, 0xC4, 0x98, 0xC7, 0x19, 0xD9, 0x5F, 0x2E, 0x84, + 0x1B, 0x62, 0x2A, 0x5E, 0x48, 0x48, 0xA3, 0xC5, 0xC2, 0x62, 0x95, 0x99, 0x92, 0xEA, 0x7A, + 0x7D, 0x72, 0xCA, 0x8A, 0x36, 0x80, 0x28, 0xF4, 0x97, 0xDF, 0xAD, 0x93, 0x35, 0x5C, 0xBB, + 0x1B, 0xB9, 0x78, 0x6D, 0x14, 0xFF, 0x2C, 0xF5, 0x90, 0x31, 0x78, 0x48, 0xF9, 0x58, 0x56, + 0x42, 0x71, 0x10, 0xDD, 0xA3, 0x6F, 0x51, 0x92, 0xA8, 0x16, 0xCE, 0x9C, 0x88, 0x16, 0xCC, + 0x7B, 0xBF, 0xC8, 0x04, 0xEF, 0xC4, 0x00, 0x85, 0xA3, 0x85, 0x0B, 0x89, 0xF1, 0xE7, 0xFE, + 0x56, 0x56, 0xDB, 0xA4, 0x10, 0xF9, 0x06, 0xA9, 0x7C, 0x32, 0x33, 0x6C, 0x1A, 0xE7, 0xE8, + 0x17, 0x37, 0xA8, 0x3E, 0x08, 0x73, 0x54, 0xE4, 0x28, 0xDA, 0x85, 0x38, 0xD9, 0x48, 0xDB, + 0xF5, 0xDF, 0xAC, 0xB5, 0x9D, 0xD2, 0xB5, 0xFD, 0x3B, 0xC8, 0x03, 0xF4, 0xBA, 0x43, 0x2C, + 0x9A, 0x73, 0x9D, 0xF2, 0xCF, 0xA9, 0xED, 0x94, 0x84, 0x32, 0x0F, 0x97, 0xED, 0xFF, 0x1A, + 0x48, 0xC6, 0xB8, 0x6B, 0x30, 0x02, 0xCF, 0xB7, 0x72, 0xDD, 0x5E, 0x56, 0x2B, 0xC4, 0xC3, + 0xD6, 0x83, 0xED, 0x96, 0x4B, 0x61, 0x99, 0xFA, 0x05, 0x14, 0xB0, 0x79, 0x0D, 0x95, 0x80, + 0x95, 0xB7, 0xB8, 0x5C, 0x6B, 0xE8, 0x75, 0xFB, 0xB5, 0x59, 0xE1, 0x93, 0x01, 0x46, 0xCC, + 0xEA, 0x63, 0xA3, 0x88, 0xA1, 0x94, 0xFE, 0x09, 0xC3, 0xDE, 0xA0, 0x3B, 0xE5, 0x2D, 0xE2, + 0x7E, 0x90, 0x10, 0x17, 0xAF, 0xE8, 0x09, 0xAF, 0x63, 0x0A, 0x73, 0x82, 0xBF, 0x5C, 0x4C, + 0xD4, 0xD1, 0xB8, 0xF4, 0x15, 0x79, 0xFB, 0x43, 0x48, 0xED, 0xE4, 0xCA, 0x05, 0xF4, 0xCD, + 0x3F, 0x13, 0x9A, 0x31, 0xB2, 0x54, 0x4E, 0x51, 0x6D, 0xBE, 0x40, 0x86, 0xB9, 0xBB, 0x4B, + 0x2B, 0xED, 0x47, 0xE2, 0xD2, 0x30, 0x98, 0x2D, 0xD5, 0x19, 0x24, 0x29, 0xD3, 0x77, 0xB7, + 0xC0, 0x74, 0x5C, 0xC0, 0x68, 0xE2, 0xF5, 0xA4, 0xAA, 0x04, 0xC7, 0xFF, 0x87, 0x20, 0x9E, + 0xD1, 0x25, 0x99, 0x76, 0xA0, 0xFC, 0x9B, 0x25, 0xE9, 0xE8, 0x51, 0xD4, 0xE3, 0x50, 0x2C, + 0x02, 0xC8, 0x5D, 0x6D, 0xFF, 0x02, 0x9E, 0x21, 0x1D, 0x01, 0xEB, 0xF0, 0xE9, 0xE7, 0x18, + 0x8D, 0x56, 0x8F, 0x84, 0x37, 0xD8, 0x13, 0xB0, 0xF1, 0x22, 0xF2, 0xFB, 0x17, 0x60, 0x3B, + 0x69, 0x3E, 0xD9, 0xC3, 0x8F, 0x17, 0xCF, 0xD5, 0x0B, 0x81, 0x5E, 0x6D, 0x9D, 0xFC, 0x0E, + 0xD2, 0xCC, 0xF1, 0x9F, 0x63, 0x99, 0x27, 0x4A, 0x14, 0x20, 0xF2, 0x35, 0xA5, 0x9D, 0x8B, + 0xF7, 0x24, 0x34, 0x5E, 0x14, 0xE4, 0x5D, 0x9E, 0x4B, 0xE8, 0x93, 0x4D, 0xFC, 0x3F, 0xA9, + 0x26, 0x78, 0xDB, 0x61, 0xD7, 0x11, 0x8B, 0xF5, 0x3C, 0xB8, 0xA2, 0x22, 0x5B, 0x33, 0x5F, + 0x7E, 0xAE, 0x50, 0xE3, 0xF9, 0x41, 0x23, 0x76, 0x28, 0xDB, 0x76, 0xD8, 0xEA, 0x38, 0xF7, + 0x7A, 0x72, 0xAF, 0x3A, 0x26, 0xC8, 0x1F, 0xE4, 0x35, 0x23, 0xB3, 0x35, 0x53, 0x5A, 0x5D, + 0x1D, 0xB7, 0xC3, 0x8F, 0x34, 0x10, 0x82, 0xBB, 0x57, 0x34, 0xD0, 0x89, 0xE8, 0xAE, 0x30, + 0x9C, 0xFD, 0xA3, 0xA0, 0xBC, 0xB5, 0xCD, 0x5B, 0x09, 0x71, 0x13, 0xC8, 0xED, 0xF9, 0x61, + 0x6A, 0xA4, 0xF6, 0xE6, 0x63, 0x1B, 0x91, 0x25, 0x27, 0x6F, 0xB3, 0xF6, 0x80, 0xA3, 0x43, + 0x41, 0xC3, 0xDB, 0x66, 0x8D, 0xC6, 0xCA, 0xD4, 0x5F, 0xC9, 0x3B, 0x27, 0x08, 0xCA, 0x2A, + 0xF7, 0x5C, 0xCC, 0xE7, 0x34, 0xFD, 0x19, 0x1C, 0x50, 0x08, 0x9D, 0xAD, 0x53, 0x98, 0x2F, + 0xDD, 0xAE, 0x02, 0x53, 0x1F, 0xF9, 0x3E, 0x1F, 0x21, 0xFF, 0x39, 0x5F, 0xC0, 0xA1, 0x28, + 0x74, 0xED, 0xF0, 0x6B, 0x6F, 0x96, 0x47, 0xE9, 0x5A, 0x73, 0x24, 0x58, 0x6C, 0x71, 0xDF, + 0xD9, 0x1D, 0x90, 0x1D, 0x62, 0x18, 0x58, 0x19, 0x0F, 0xEC, 0xD0, 0x0C, 0xCD, 0x11, 0x0B, + 0xBA, 0xC5, 0x9F, 0x96, 0xCB, 0x88, 0x4C, 0x3C, 0x93, 0x99, 0x47, 0x48, 0xA5, 0x6F, 0x41, + 0x28, 0x3B, 0xFC, 0x41, 0xFB, 0x89, 0x05, 0x21, 0x53, 0xA8, 0x94, 0x58, 0x8C, 0x3C, 0xB9, + 0x01, 0x7F, 0x3D, 0x66, 0x32, 0x6C, 0x98, 0x56, 0x37, 0xE5, 0x75, 0xAC, 0xB8, 0x12, 0x34, + 0x63, 0x42, 0x65, 0x40, 0x25, 0xD6, 0x02, 0xDE, 0x3B, 0xA9, 0x40, 0xC1, 0x9A, 0xC1, 0xA6, + 0x33, 0xDF, 0xFD, 0xA9, 0x77, 0xB5, 0x29, 0xB8, 0x01, 0x3E, 0x19, 0xC1, 0xD6, 0xD0, 0x68, + 0x0F, 0x4D, 0xAE, 0x62, 0xC9, 0x24, 0x45, 0x0A, 0xE6, 0x6A, 0xAB, 0x82, 0xF2, 0x14, 0x73, + 0x06, 0x1D, 0xAB, 0x3D, 0x62, 0xB2, 0x47, 0xF9, 0x07, 0xE3, 0x55, 0x19, 0x39, 0xAD, 0x3F, + 0x54, 0x65, 0xE9, 0xD0, 0x8A, 0x82, 0xBF, 0xEA, 0x17, 0xEE, 0xA1, 0xB6, 0xB2, 0xB9, 0x23, + 0x75, 0x74, 0x77, 0xF9, 0x93, 0x00, 0x0B, 0x2F, 0x43, 0xB7, 0x0F, 0x28, 0xAA, 0xAB, 0x1F, + 0xE9, 0xA2, 0x6A, 0xD1, 0xFD, 0x33, 0x61, 0x61, 0x6C, 0x0B, 0x0E, 0x24, 0x2F, 0xE7, 0x66, + 0x04, 0xB7, 0x03, 0x3A, 0x1F, 0x30, 0xE9, 0x7E, 0x28, 0xF5, 0x26, 0xCA, 0x3C, 0x88, 0x0F, + 0xE2, 0xB8, 0xD9, 0xD1, 0xB0, 0xC9, 0xFF, 0x18, 0x8B, 0x31, 0xCB, 0x9D, 0x97, 0x42, 0x5A, + 0xCA, 0xB9, 0xB2, 0x16, 0xD9, 0x8A, 0x6A, 0xE3, 0x55, 0xE5, 0x83, 0xDA, 0x71, 0xE8, 0x86, + 0x4E, 0xE3, 0xD1, 0x6B, 0x07, 0x59, 0x79, 0x61, 0x90, 0xEF, 0x54, 0x5C, 0x1E, 0x62, 0xBF, + 0xEF, 0x92, 0xAF, 0x6C, 0xA1, 0x47, 0xB1, 0x32, 0x44, 0xD6, 0xC8, 0x92, 0xFC, 0x8E, 0xF2, + 0x23, 0xAB, 0x3F, 0x43, 0xF9, 0x24, 0xC2, 0xF4, 0x66, 0x09, 0x7E, 0xE8, + ]) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -229,7 +741,7 @@ fn bench_mldsa65_sign() { } fn bench_mldsa65_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA65, MLDSA65PrivateKey, MLDSA65_SK_LEN}; + use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSATrait}; eprintln!("MLDSA65_lowmemory/Sign"); @@ -242,7 +754,12 @@ fn bench_mldsa65_lowmemory_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA65PrivateKey::from_bytes(&[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f]).unwrap(); + let sk = MLDSA65PrivateKey::from_bytes(&[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, + ]) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -252,7 +769,7 @@ fn bench_mldsa65_lowmemory_sign() { } fn bench_mldsa87_sign() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA87, MLDSA87PrivateKey}; + use bouncycastle::mldsa::{MLDSA87, MLDSA87PrivateKey, MLDSATrait}; eprintln!("MLDSA87/Sign"); @@ -268,7 +785,336 @@ fn bench_mldsa87_sign() { let msg = b"The quick brown fox jumped over the lazy dog"; // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); - let sk = MLDSA87PrivateKey::from_bytes(&[0x97,0x92,0xbc,0xec,0x2f,0x24,0x30,0x68,0x6a,0x82,0xfc,0xcf,0x3c,0x2f,0x5f,0xf6,0x65,0xe7,0x71,0xd7,0xab,0x41,0xb9,0x02,0x58,0xcf,0xa7,0xe9,0x0e,0xc9,0x71,0x24,0xd8,0xe9,0xee,0x4e,0x90,0xa1,0x6c,0x60,0x2f,0x5e,0xc9,0xbc,0x38,0x51,0x7d,0xc3,0x0e,0x32,0x9d,0x5a,0xb2,0x76,0x73,0xbd,0x85,0xf4,0xc9,0xb0,0x30,0x0f,0x77,0x63,0x89,0x88,0x67,0x50,0xb5,0x7c,0x24,0xdb,0x3f,0xc0,0x12,0xe6,0x1e,0xde,0x59,0x75,0x33,0x37,0x37,0x4f,0xa7,0x12,0x49,0x91,0x54,0x9a,0xf2,0x43,0x49,0x6d,0x06,0x37,0xcb,0x3b,0xe0,0x5a,0x59,0x48,0x23,0x5b,0xf7,0x98,0x75,0xf8,0x96,0xd8,0xfe,0x0c,0xab,0x30,0xc8,0x49,0x48,0xdb,0x4d,0x63,0x15,0xaa,0xaf,0x16,0x0a,0xc6,0x24,0x36,0x64,0x22,0x01,0x48,0x16,0x11,0x09,0x11,0x2c,0x94,0x02,0x89,0x22,0x45,0x2c,0x62,0xb8,0x45,0x00,0x45,0x2a,0x08,0x96,0x70,0x90,0x12,0x6e,0x14,0x93,0x70,0xd4,0x46,0x10,0x84,0x44,0x51,0x58,0x96,0x91,0x0c,0xa9,0x29,0x82,0xb2,0x41,0xc9,0x08,0x71,0xc4,0x28,0x68,0x04,0x96,0x89,0x48,0x40,0x85,0x9b,0x22,0x6d,0x1c,0x28,0x64,0x59,0x12,0x41,0x9c,0xb8,0x91,0x84,0x04,0x89,0x44,0x90,0x05,0xcb,0x34,0x62,0xa0,0x86,0x90,0x40,0x26,0x92,0x20,0x99,0x29,0x13,0x05,0x69,0x5c,0x34,0x68,0xa4,0x32,0x8e,0x19,0x26,0x92,0x59,0x46,0x10,0x09,0xa4,0x49,0x23,0x42,0x4d,0x12,0x36,0x61,0x58,0x10,0x65,0x01,0x28,0x90,0x1a,0x33,0x4c,0x99,0x86,0x31,0xd3,0xa2,0x49,0x09,0x82,0x25,0x43,0x14,0x28,0xc0,0x38,0x81,0x03,0x15,0x4d,0x5b,0x28,0x86,0x08,0x87,0x48,0x23,0x31,0x52,0x94,0x22,0x25,0xc3,0xc0,0x4d,0xa4,0x98,0x21,0x98,0x40,0x20,0xd1,0x42,0x86,0xcb,0x40,0x70,0x5b,0xb0,0x71,0x9c,0x96,0x2c,0xc1,0x12,0x06,0x53,0x46,0x09,0x0c,0x45,0x02,0x14,0x46,0x6e,0x91,0xb4,0x21,0x54,0xb0,0x8c,0xe4,0x46,0x42,0x9a,0x20,0x8c,0x01,0x21,0x25,0x13,0x41,0x05,0x5a,0x40,0x22,0x13,0xc9,0x0c,0xa0,0x18,0x40,0x52,0xc2,0x30,0xcb,0x34,0x2c,0x4b,0xc8,0x68,0x1b,0xa4,0x60,0x49,0x84,0x84,0x63,0x30,0x29,0x4a,0xa0,0x69,0x5b,0x80,0x04,0xd2,0x38,0x0a,0x14,0x26,0x4c,0xe2,0xb2,0x44,0x8b,0xa2,0x11,0x24,0x46,0x49,0xc4,0x14,0x52,0x0b,0x42,0x71,0x03,0xb2,0x10,0x92,0x28,0x80,0x01,0x24,0x88,0xe3,0x08,0x11,0x0a,0x05,0x28,0x19,0xc4,0x81,0x00,0x20,0x22,0xdc,0x44,0x68,0x42,0x12,0x22,0x44,0x00,0x2a,0xc9,0x26,0x6a,0x0c,0x87,0x31,0xe0,0xc0,0x44,0x99,0x14,0x84,0x18,0x36,0x0d,0x11,0x37,0x42,0x22,0x18,0x8c,0x63,0xb2,0x91,0x0c,0x98,0x08,0xa1,0xa0,0x01,0x08,0x92,0x44,0x04,0x13,0x24,0x5c,0x98,0x71,0x82,0x84,0x71,0x84,0x32,0x52,0x51,0xb0,0x31,0x9c,0x42,0x2e,0x1a,0xa8,0x28,0x02,0x08,0x91,0x01,0xc0,0x89,0x0b,0xc7,0x05,0x8a,0x24,0x65,0x22,0xc2,0x64,0x4c,0x88,0x91,0x5c,0x82,0x68,0x13,0xa5,0x64,0x50,0x20,0x86,0x21,0x04,0x02,0x91,0x94,0x44,0x48,0x22,0x30,0x22,0x39,0x4a,0x02,0x98,0x84,0x09,0xa2,0x88,0x19,0x19,0x29,0x44,0x48,0x8c,0x22,0x95,0x0c,0xa1,0x04,0x72,0x04,0x87,0x70,0x12,0x21,0x10,0x42,0x06,0x84,0x1b,0x49,0x85,0x89,0x06,0x4a,0xd3,0x36,0x08,0xdb,0xc0,0x40,0x58,0xb6,0x51,0x0c,0xa7,0x09,0x8c,0x24,0x61,0x99,0x90,0x64,0x8c,0xc2,0x90,0x5b,0x24,0x90,0x10,0xa3,0x49,0x03,0x25,0x61,0x43,0x32,0x8a,0x11,0x98,0x44,0xc8,0xb2,0x20,0x04,0x38,0x41,0x10,0x47,0x2c,0x19,0xc6,0x44,0x1c,0x25,0x2c,0x04,0x88,0x30,0xd9,0x46,0x69,0x9b,0x20,0x00,0x1b,0x46,0x82,0x5a,0xa4,0x80,0x5b,0xa0,0x49,0x18,0x90,0x25,0x00,0x26,0x80,0x0b,0xc2,0x31,0x5a,0x40,0x72,0x54,0xc6,0x20,0xc1,0xb0,0x31,0x24,0xb1,0x4d,0x10,0x95,0x28,0x14,0x00,0x0a,0xa0,0xc8,0x4d,0x54,0xa2,0x88,0x23,0x98,0x81,0x60,0x90,0x04,0x02,0x16,0x2c,0x13,0x21,0x40,0x91,0x86,0x8d,0x08,0xc2,0x91,0x91,0x14,0x26,0xd0,0xb4,0x0c,0x09,0xc6,0x60,0x51,0x44,0x2e,0x04,0x11,0x26,0x00,0x29,0x11,0x93,0xc2,0x08,0x63,0xa4,0x31,0x22,0x00,0x28,0xc1,0x14,0x08,0x0c,0x40,0x2c,0x41,0x14,0x06,0x9c,0x20,0x72,0x54,0x22,0x06,0x8b,0x08,0x4d,0xa1,0x48,0x69,0x10,0x30,0x41,0x1c,0x28,0x49,0x44,0x18,0x8e,0xcc,0x96,0x48,0xd9,0x42,0x50,0x1b,0x06,0x49,0x0c,0x45,0x88,0x23,0x04,0x0d,0x20,0x30,0x2e,0x23,0x85,0x2c,0x14,0x07,0x30,0x40,0xb6,0x85,0x4c,0xc0,0x20,0x44,0x86,0x20,0x19,0x00,0x6c,0x54,0x02,0x48,0xcc,0x88,0x6c,0x59,0x06,0x32,0x49,0x14,0x84,0x04,0xc7,0x50,0x13,0x49,0x28,0xe4,0x06,0x09,0xd3,0xc6,0x10,0xc8,0x28,0x4c,0x23,0x39,0x44,0x52,0xa4,0x64,0xcc,0xa8,0x49,0x44,0x38,0x32,0x0a,0x89,0x84,0x00,0x34,0x2c,0x22,0x85,0x8d,0x10,0x31,0x09,0x09,0x32,0x65,0x1c,0x89,0x8c,0x40,0x40,0x29,0x21,0x85,0x00,0x09,0xa1,0x6d,0x84,0xc0,0x64,0xe2,0x02,0x2d,0x48,0x04,0x40,0x12,0x09,0x8e,0xe0,0x42,0x2e,0x93,0x44,0x08,0x12,0x10,0x6a,0x01,0x84,0x05,0x92,0x30,0x8a,0xcb,0x34,0x8e,0xa2,0x26,0x2e,0x5c,0x86,0x11,0x0b,0x35,0x08,0x18,0x10,0x00,0x02,0x34,0x26,0x24,0x23,0x89,0xd1,0x84,0x00,0x24,0x46,0x60,0x13,0xb2,0x49,0x24,0x28,0x46,0x18,0x02,0x71,0xa0,0x38,0x90,0x89,0x14,0x44,0xd3,0x96,0x2d,0xa3,0x18,0x40,0x23,0x27,0x21,0xc0,0x18,0x50,0x43,0xc8,0x04,0x41,0x42,0x8d,0x5c,0x26,0x41,0x44,0xa2,0x6d,0x48,0x12,0x0e,0x40,0x32,0x25,0x0b,0x14,0x82,0x0a,0x48,0x2e,0xcb,0x82,0x88,0x03,0xa3,0x60,0x1b,0x25,0x26,0x8c,0xb8,0x20,0x24,0xb0,0x85,0x98,0x04,0x21,0x08,0xa7,0x2c,0x83,0x38,0x64,0x54,0x32,0x89,0x01,0x04,0x01,0x23,0x49,0x84,0x02,0x95,0x69,0xd1,0xa4,0x4d,0x13,0xa4,0x0c,0x91,0x46,0x0d,0x61,0x94,0x80,0x90,0x38,0x45,0x5c,0xc6,0x50,0x01,0x17,0x20,0x53,0xc6,0x28,0x9b,0x18,0x10,0x41,0x12,0x68,0x90,0x12,0x21,0xc0,0x10,0x84,0x42,0x16,0x92,0x53,0x82,0x29,0x81,0x26,0x49,0xd8,0xa4,0x05,0x9a,0x26,0x24,0x24,0x03,0x29,0xe0,0x40,0x26,0xd2,0x02,0x48,0x11,0x24,0x68,0x11,0x99,0x89,0x98,0x14,0x85,0xc9,0x20,0x0d,0x50,0x12,0x8c,0x1c,0x08,0x10,0x02,0x10,0x00,0x00,0x95,0x28,0xc1,0x28,0x90,0x59,0xb4,0x85,0xd3,0x14,0x65,0x0a,0x40,0x6e,0x11,0x29,0x65,0x18,0xc2,0x4c,0x21,0x34,0x65,0xd8,0x30,0x69,0x10,0x30,0x52,0x19,0x31,0x66,0x8c,0x18,0x8a,0xc8,0xc0,0x08,0x4c,0x98,0x30,0xa3,0xa6,0x20,0x41,0x16,0x22,0x18,0x05,0x2e,0x22,0x25,0x2a,0x64,0xb8,0x25,0x0a,0xb3,0x01,0x63,0x20,0x84,0x09,0x80,0x09,0x19,0x28,0x0e,0x02,0x11,0x01,0xa3,0x94,0x11,0xe3,0x98,0x6c,0x58,0x20,0x21,0x09,0x41,0x10,0x60,0x36,0x22,0x08,0x06,0x71,0x12,0xc2,0x85,0x5a,0xa0,0x85,0xc0,0xc6,0x84,0x5c,0x38,0x06,0xcb,0xb6,0x69,0x14,0x84,0x84,0x53,0x22,0x82,0xa1,0xa6,0x40,0xcc,0x86,0x00,0xc4,0x26,0x22,0xa0,0xa8,0x08,0x98,0x34,0x72,0xd4,0x20,0x41,0x43,0xc4,0x90,0x48,0x16,0x68,0x1b,0x16,0x52,0x1a,0x37,0x02,0x50,0x20,0x42,0x48,0x48,0x8a,0x20,0x11,0x41,0xe2,0x00,0x6c,0xc0,0xc2,0x0c,0x14,0x06,0x49,0x11,0x31,0x4d,0x19,0x06,0x0a,0x89,0x46,0x09,0x1b,0x81,0x65,0x44,0xc8,0x00,0x82,0x06,0x70,0x00,0x16,0x72,0xcc,0x24,0x50,0x8a,0x42,0x89,0x9c,0x96,0x90,0x64,0x28,0x70,0x92,0xb2,0x68,0x98,0x26,0x62,0x61,0x94,0x40,0xc1,0x16,0x89,0xd8,0x42,0x64,0x1a,0x21,0x4e,0x62,0x90,0x64,0x21,0xc8,0x24,0x8b,0x28,0x6d,0x5c,0x42,0x92,0xa0,0xc6,0x4d,0x0c,0x85,0x80,0xcc,0x88,0x4d,0xd4,0x42,0x8d,0x42,0x34,0x8a,0x0b,0x04,0x51,0xc3,0x26,0x86,0x24,0x25,0x81,0x12,0x35,0x06,0xa0,0x44,0x04,0xc8,0x94,0x81,0x5b,0xb4,0x31,0x1c,0x08,0x06,0x5c,0x24,0x08,0x03,0x27,0x6a,0x20,0xc2,0x25,0xe1,0x80,0x90,0x19,0xb4,0x6d,0xa3,0x46,0x0c,0x4b,0x18,0x60,0x50,0xc6,0x2c,0x1b,0x92,0x2d,0x11,0x15,0x04,0xa2,0x00,0x04,0x21,0x48,0x2e,0xd8,0x16,0x06,0xd2,0x10,0x8a,0x83,0xa2,0x25,0x08,0x31,0x0d,0x09,0x38,0x51,0xd9,0x48,0x49,0x0b,0x16,0x4c,0x23,0x32,0x25,0x19,0x19,0x02,0x4a,0x44,0x09,0xd1,0xb2,0x21,0x0b,0x83,0x2c,0x23,0x25,0x85,0x93,0x16,0x85,0x44,0xa0,0x44,0x1b,0x83,0x50,0x02,0x22,0x72,0x4b,0x04,0x80,0x9b,0x14,0x65,0x21,0x93,0x60,0x18,0x13,0x0a,0xd9,0x46,0x0d,0x22,0x45,0x61,0xc8,0xb4,0x40,0xa1,0x42,0x2d,0x02,0xb8,0x09,0x00,0x14,0x44,0x9b,0xb6,0x11,0x0b,0x97,0x8c,0x40,0x10,0x4a,0x82,0x14,0x6a,0xda,0x90,0x05,0x1c,0x02,0x8e,0x0c,0x19,0x72,0xa3,0xb4,0x8d,0x24,0x30,0x50,0x11,0x87,0x09,0x64,0xc6,0x28,0xe4,0x18,0x92,0x98,0xb4,0x6c,0x61,0x16,0x51,0x40,0x46,0x0e,0x1c,0x32,0x48,0xda,0x20,0x51,0x88,0x36,0x8a,0x23,0xb1,0x21,0x82,0x90,0x28,0x1a,0x15,0x32,0xe2,0x18,0x61,0x92,0x04,0x8e,0x13,0xb6,0x90,0x13,0x13,0x68,0xc9,0x84,0x68,0x4c,0x40,0x6d,0x0b,0x33,0x00,0x81,0x46,0x4d,0xd2,0x38,0x0c,0x04,0x96,0x81,0xa4,0x88,0x50,0x02,0x90,0x85,0x22,0xb0,0x04,0xd3,0xa4,0x71,0xd2,0x80,0x10,0xca,0x96,0x40,0x51,0xa6,0x41,0xa4,0x84,0x28,0xe0,0x08,0x52,0x0b,0x30,0x8c,0xd2,0x38,0x0a,0x0c,0x29,0x51,0xc3,0x82,0x09,0xca,0x20,0x91,0xd8,0x36,0x92,0xa3,0xa6,0x28,0x92,0x42,0x22,0xa2,0x16,0x01,0x1a,0x34,0x86,0x37,0xd9,0xa6,0x59,0x16,0x98,0x81,0xec,0x21,0xcf,0x48,0x11,0x86,0x9d,0x1d,0x7f,0x13,0x9f,0x05,0x37,0xe9,0x6f,0x11,0x84,0x58,0x54,0x05,0xfd,0x17,0x80,0x8a,0xf1,0xe0,0x62,0x39,0xd3,0xb3,0x4e,0x5a,0xca,0x8b,0xf1,0x36,0x96,0x77,0xb4,0x47,0xac,0x71,0x8a,0xc4,0x7d,0x85,0x0c,0x4d,0x77,0xb0,0xbe,0x31,0xdc,0x9f,0x50,0x8e,0x39,0x78,0xf2,0x42,0x74,0xab,0x01,0x85,0xf7,0x27,0xab,0xdf,0xf5,0x9f,0x44,0x90,0x37,0x1b,0xf0,0x46,0x10,0xe3,0x64,0xe6,0x4e,0xc8,0x75,0xef,0x9d,0x20,0xdc,0x94,0x07,0x7e,0x1e,0x16,0x63,0x27,0xa8,0x79,0xb8,0xab,0x51,0x61,0x60,0xb2,0xa3,0xf7,0x74,0x37,0xb9,0xb3,0xcc,0x7d,0x17,0xae,0xad,0xdc,0x84,0xdb,0x62,0x74,0x6a,0x35,0xac,0x09,0x6f,0x78,0x2f,0x62,0xa7,0xf0,0x1a,0xa6,0xd6,0x69,0x3d,0xee,0xc9,0x0b,0x23,0xc6,0x69,0x85,0xa0,0x23,0x07,0xe0,0xa1,0xca,0xe5,0x98,0xa6,0x73,0x24,0xdb,0xa0,0xf5,0x2f,0x22,0x43,0x22,0x75,0xe9,0x32,0x57,0x06,0x5c,0x3b,0x7e,0x5e,0x1c,0xfe,0x1d,0xfd,0x4d,0x0d,0xf0,0x86,0xdf,0x21,0x24,0x34,0x14,0xa2,0xd2,0x7e,0x20,0x23,0x0a,0x82,0x9b,0xe4,0xeb,0x4c,0x82,0xc1,0x6d,0x35,0xf7,0x8b,0x0e,0x5e,0x19,0x83,0x32,0xe0,0x00,0x74,0xbb,0x64,0x61,0x2f,0xab,0x17,0xd4,0xc8,0x97,0x1c,0xb6,0x8e,0x5e,0xda,0xb0,0x36,0x9f,0x11,0x57,0xb3,0x46,0x9a,0xbd,0x83,0x84,0xe2,0xd9,0x55,0x3f,0x1b,0x78,0xe7,0x86,0xe1,0xee,0x9d,0x0b,0x98,0xd3,0x9f,0x83,0xcc,0xec,0xf3,0x7d,0x1e,0xbd,0x3a,0x9d,0x63,0xae,0xc7,0x66,0x16,0x4a,0x10,0x17,0x1a,0x4f,0xd8,0xc6,0x3d,0xaf,0x18,0x2c,0x42,0x12,0x58,0xc5,0xf5,0x29,0xaa,0x55,0xcb,0x7e,0xba,0xe2,0xe1,0x65,0x23,0x15,0xe1,0xf7,0x1e,0x8a,0x74,0x13,0x14,0x10,0xd0,0x32,0x47,0xed,0xe1,0x1d,0x34,0xdb,0x91,0xf6,0xf0,0x8a,0xa2,0x47,0x8f,0xd7,0x89,0x67,0x9c,0x04,0x94,0x9f,0x71,0xbc,0x01,0x71,0xe0,0x7e,0x3a,0x8b,0xb5,0x75,0x3d,0xbb,0xda,0xa4,0x11,0xa6,0x35,0x0a,0xb4,0x6e,0xef,0xbf,0x86,0xfc,0x55,0x1c,0x29,0xef,0xe4,0xcd,0xd7,0x66,0x1d,0x5c,0xf6,0xc3,0xdb,0x22,0xd0,0xce,0xdd,0xe5,0x99,0x85,0x44,0x59,0xd9,0x7f,0x20,0xdf,0x74,0x55,0xbd,0xf3,0x56,0xa1,0x98,0xd0,0xf7,0xeb,0x6d,0x34,0x11,0x1f,0xc9,0x40,0xb2,0x5c,0x05,0x43,0xb7,0x88,0xed,0xda,0x9d,0x26,0x81,0x0e,0xac,0x3d,0x6c,0xc9,0xc5,0x13,0x27,0xc2,0xcf,0x83,0xe8,0x87,0xd4,0x08,0x9e,0x19,0x69,0x5e,0x11,0xad,0xd8,0x37,0xf6,0xf4,0x40,0xcc,0x36,0x0f,0x93,0xf3,0x2f,0xee,0x8a,0x96,0x63,0x71,0x2c,0x6b,0xbd,0x38,0xc8,0x4a,0xb7,0xb5,0x48,0x23,0xec,0x36,0x3e,0xb7,0xe4,0x2e,0xb5,0x9f,0xc1,0xfc,0xe6,0x0f,0xbd,0x55,0x30,0x7b,0x3e,0xc8,0x5f,0xd9,0xda,0xf3,0x20,0x6d,0x7b,0x4b,0x39,0x17,0xf1,0xc8,0xb7,0xa9,0x2e,0x3c,0x67,0xd8,0x98,0x80,0xfd,0xf2,0xe4,0x7f,0x5a,0x0c,0x99,0x45,0x95,0xdb,0x17,0x0a,0xf4,0x1b,0xab,0xf5,0xa2,0x5b,0x4d,0xc1,0xc4,0x2d,0xd6,0xa9,0xdb,0x27,0x1e,0x76,0x4d,0xe2,0xfb,0x01,0x5a,0x49,0xa8,0x50,0xc7,0x91,0x9b,0xe4,0x70,0x06,0xa3,0x36,0xe2,0xe3,0x25,0xfd,0xe5,0x3a,0xc5,0x99,0x55,0x4d,0x0a,0x7d,0xe4,0xef,0x45,0xec,0x40,0xc3,0x9d,0x6b,0xaf,0xf3,0x11,0xbe,0xee,0x75,0xd8,0x9e,0x02,0xad,0x31,0xf4,0xbe,0x4b,0xd2,0x0a,0xe9,0x19,0x4f,0x5e,0xdd,0xda,0xa6,0x65,0x07,0x76,0x11,0x6e,0x9f,0x27,0x0f,0x77,0x71,0x4a,0xd7,0xa8,0xe8,0x9a,0xce,0xf7,0x4b,0x7f,0xf7,0xd8,0xdb,0xec,0x27,0xf8,0x02,0x0a,0x98,0x52,0x47,0xe2,0xcd,0xac,0xef,0x48,0x94,0xa4,0xd6,0x8b,0xa3,0x7c,0xa9,0x12,0xd6,0xbe,0x73,0x50,0x1c,0x99,0x51,0x81,0xe5,0xb7,0x77,0x23,0x35,0x0b,0x36,0x31,0xda,0x37,0x00,0xe1,0x3f,0xd3,0x66,0xe1,0x31,0xbf,0x06,0xb3,0x6e,0xb6,0xb0,0x34,0x50,0x93,0x20,0x9f,0x0a,0x7b,0xef,0xfa,0xe1,0xfd,0xd8,0x75,0xb0,0x06,0x87,0xc1,0x16,0x3c,0x35,0x3d,0x7d,0x2a,0xc9,0x09,0x37,0xb3,0x4e,0x97,0x8e,0x92,0xf8,0x21,0xad,0xc9,0x66,0x22,0x02,0xec,0xe8,0x9a,0x17,0xe7,0xbb,0x65,0xae,0x17,0xd8,0x3b,0x90,0xdb,0xbe,0x6a,0x50,0x1a,0x4e,0x13,0x45,0xbe,0xe4,0xe5,0xa5,0xb5,0x3a,0xf2,0xe5,0xba,0x3d,0x1e,0xf3,0xf4,0xe0,0x5a,0xdf,0x0b,0x3a,0x4c,0xf2,0xe5,0x30,0x36,0x0f,0xee,0x64,0x92,0x99,0x02,0xb5,0x71,0xf6,0xfd,0x2e,0x30,0x56,0x52,0xa4,0xcb,0x01,0x0f,0x79,0xf8,0x15,0xe1,0x8f,0x2b,0xbb,0x8c,0xc8,0x9f,0xa6,0xfc,0x76,0xf7,0x7c,0x89,0xe2,0x93,0xcf,0x17,0x5a,0x0b,0x19,0x58,0x00,0xfe,0x72,0xd2,0xcc,0xdd,0x7d,0x75,0xe5,0xbd,0x90,0xbc,0x6a,0xc4,0x35,0xd6,0xa4,0x40,0xef,0x85,0x2e,0x9a,0x1c,0x8c,0x53,0xde,0x03,0xbf,0x19,0x33,0x65,0xd7,0x35,0xaa,0xf2,0x9c,0x51,0x62,0xa6,0x17,0xe3,0x64,0xe7,0xf9,0x44,0x16,0x8d,0x0f,0xb4,0x8f,0xef,0x40,0x55,0x8f,0x45,0x42,0x97,0xcc,0x3d,0xd5,0x08,0x66,0x2c,0xf2,0x3f,0xb8,0x8e,0x19,0x54,0xaa,0x45,0xd1,0xc5,0xe1,0x15,0xbc,0xc3,0x6f,0x05,0xb3,0xe0,0x98,0xd5,0x55,0x22,0x0f,0x40,0xbe,0x26,0x29,0xb3,0x45,0x07,0xb8,0x46,0x4c,0x54,0xc2,0x7b,0x5d,0xec,0x78,0xda,0x8f,0x22,0x65,0x05,0x14,0x79,0x7a,0xf8,0x6a,0x25,0x12,0xbc,0xb7,0xe2,0x92,0x33,0x79,0xef,0x6d,0x73,0xc1,0x37,0x00,0x6c,0x1b,0x38,0xf5,0x1e,0x37,0xf9,0x35,0x85,0xe2,0x90,0x41,0xa3,0xe4,0xe3,0xaf,0x46,0x00,0x7c,0xe1,0x3b,0x8b,0x5f,0x7b,0x17,0xd5,0xd6,0x5d,0x7d,0x56,0x68,0xe4,0x27,0xbc,0xbe,0x7e,0xc1,0xd7,0xc4,0x08,0xc0,0x54,0xa4,0x8c,0x1a,0xe7,0x97,0xbf,0x99,0xac,0xbc,0x8d,0x26,0x07,0x52,0x29,0x35,0xfd,0x66,0x5e,0xa7,0x82,0x2d,0x93,0x0f,0x23,0xea,0xbf,0xf7,0x83,0xbb,0x23,0x69,0x75,0x69,0xe2,0x04,0xb9,0x43,0x14,0x1e,0x00,0xc0,0x88,0x10,0x95,0x6b,0xe0,0x52,0x53,0x65,0xdb,0xab,0x54,0xed,0x48,0xcb,0x76,0x96,0x4c,0xcd,0xf5,0xcb,0xd3,0xae,0xe7,0x28,0x2d,0x4a,0x00,0x00,0xd2,0x78,0x4d,0x7b,0x8f,0xab,0x16,0xb2,0xf7,0xf0,0xd5,0x22,0x57,0x32,0xb1,0xef,0xbc,0x4e,0xb1,0xcf,0xed,0xeb,0x43,0xfd,0xe7,0x9b,0x69,0xec,0xc0,0xfb,0xea,0xa1,0xe6,0xb4,0x07,0x28,0x67,0x3b,0xd4,0xb2,0xe9,0x8a,0x0d,0x4a,0x8f,0x02,0xf8,0x53,0x95,0x07,0x30,0xf2,0x8d,0x35,0xeb,0x12,0xfc,0xc7,0x97,0x68,0xb8,0xe1,0x8e,0x4b,0xda,0x0e,0x58,0xa3,0x31,0xa2,0xf7,0x1d,0x7c,0xcc,0x2d,0x45,0x1b,0x32,0xb1,0xc6,0x5c,0x31,0x2a,0xcf,0x47,0xee,0x51,0x3b,0x21,0x95,0x4c,0x41,0xc0,0x0c,0x87,0x38,0x72,0xee,0x94,0xcf,0x14,0xf4,0x60,0x37,0x42,0x53,0x61,0xf4,0xbd,0xb5,0x48,0x21,0xf7,0x11,0x46,0x0c,0xeb,0xae,0x8c,0x07,0x50,0x8a,0x92,0x19,0xf8,0x8f,0xa6,0xbe,0xda,0xa6,0x78,0xee,0xd5,0x01,0x94,0x4a,0x16,0xae,0x6f,0x7b,0x5b,0xb7,0xa2,0xe1,0xe3,0x57,0xe7,0x0d,0x7b,0x98,0x46,0x1a,0x2c,0x71,0xcb,0x0f,0xa7,0x62,0xd6,0xad,0x98,0x24,0x08,0x1d,0x37,0xf2,0x92,0xfd,0x4b,0xe8,0xb8,0x4c,0x36,0x11,0x0d,0xc7,0x44,0x36,0x02,0x01,0xbe,0xeb,0xe0,0xbd,0x6c,0x9d,0x05,0xe8,0x69,0x25,0x6d,0x2f,0xf3,0xf9,0x95,0x17,0xb7,0xef,0xd2,0xa3,0x37,0x74,0x05,0x6c,0xb5,0x67,0x16,0x75,0xa8,0xb4,0x92,0xe9,0xf5,0xf2,0x62,0x0e,0xb8,0xef,0x93,0x81,0xd3,0xd1,0xdf,0x19,0x93,0x8b,0x7b,0x5f,0xfa,0xac,0x59,0xbc,0x81,0x10,0xfa,0x87,0xba,0x8d,0x7a,0x3d,0x01,0x65,0xf8,0xe4,0x1d,0xd0,0xf8,0x04,0xf1,0x1b,0x9d,0xed,0x0f,0x35,0x2a,0x59,0x78,0x35,0xd0,0x63,0x07,0xa8,0xe0,0xc6,0xef,0x4d,0x21,0x90,0x43,0x39,0xe1,0xcf,0x45,0x89,0x23,0xa3,0xe8,0x9e,0x02,0x5d,0x94,0x53,0x47,0x36,0x6c,0x02,0xf3,0xdd,0x63,0x68,0xd4,0xe4,0x7e,0x85,0xd3,0xd2,0xa9,0x70,0x5b,0xd5,0x79,0x61,0x85,0x2e,0x5a,0x57,0x9f,0x93,0xb1,0xc5,0x14,0xc5,0x39,0xf4,0x9e,0xa1,0x16,0x3a,0x2a,0x49,0x3b,0x0e,0xfc,0xb4,0x7f,0x47,0x48,0xf6,0xa9,0x9e,0x10,0xbf,0x70,0x78,0x28,0x2e,0x4a,0xce,0x18,0x13,0x6e,0x2a,0x8b,0x3e,0xe0,0xa3,0x80,0xdc,0xd3,0xb3,0xef,0x3e,0x65,0xe1,0xb8,0x15,0x72,0x89,0xd6,0x24,0x67,0xad,0x48,0x8b,0xa0,0x39,0x2b,0x2e,0x90,0xa1,0xed,0xed,0xcb,0xdc,0x93,0x1d,0xc1,0x72,0x98,0xcc,0xef,0x76,0x64,0x5c,0x7d,0x33,0x0a,0x05,0xc2,0xce,0x40,0xf8,0x9b,0x85,0x46,0x8f,0x35,0x7a,0x21,0x77,0x51,0xe1,0x54,0x63,0x13,0x04,0xec,0x4e,0x04,0xbb,0x45,0xb3,0x67,0x89,0x09,0xc7,0x4a,0xf5,0x1c,0xe3,0x70,0x36,0x4d,0x8f,0x4f,0x7e,0xb1,0xe6,0x1e,0x00,0x28,0x74,0x29,0xc9,0x96,0x1d,0xe8,0x32,0x2c,0xa9,0xa2,0x62,0x9b,0x13,0x09,0xd8,0x00,0xe9,0x2b,0xc1,0xdc,0x50,0x55,0xdc,0xc7,0x97,0xf3,0x38,0x66,0xeb,0x0c,0xfd,0x8d,0x49,0x02,0x50,0xd4,0x8f,0xfc,0xa8,0x02,0x2f,0x49,0x29,0x0e,0x2d,0x53,0x76,0x16,0x2f,0xba,0xa9,0x82,0xd1,0x64,0x53,0xc8,0x25,0xb3,0x5f,0x65,0x15,0x63,0x5e,0xa9,0x2b,0xea,0x72,0x36,0x7b,0xaa,0x54,0xde,0x3f,0x9e,0xae,0xa6,0x95,0x42,0xa8,0x1a,0x41,0x27,0xf7,0x1c,0xba,0xa2,0x57,0xf3,0x24,0xfe,0xfe,0xf1,0x4f,0x08,0xfb,0xd6,0x5a,0x04,0x9c,0xd2,0xfb,0x36,0x25,0x94,0xa8,0xe2,0x3f,0xf1,0xa2,0x61,0x7d,0xb5,0xb1,0x58,0xf6,0xf0,0x1c,0xf5,0x0a,0xb0,0xed,0x95,0xc6,0xe7,0x09,0x84,0x11,0x64,0x10,0x8b,0x06,0xe1,0xb4,0x0a,0xb0,0xab,0x11,0xc4,0x08,0x30,0x1d,0x3d,0x9d,0x8e,0xa6,0x9e,0x96,0x8a,0x96,0x00,0xb3,0xd1,0x7f,0x38,0x01,0x1c,0xe2,0x80,0x74,0xe2,0xc2,0xe1,0x0b,0xf6,0x19,0x7c,0x60,0x2d,0x8d,0x0c,0xe7,0xd3,0xa3,0xef,0x2d,0x89,0x62,0x3b,0xc9,0xf1,0x2e,0xa3,0x38,0x79,0x1e,0x92,0x66,0xbb,0x8c,0xe0,0x2b,0x12,0x4c,0x6c,0x79,0x29,0xba,0xea,0x69,0x32,0x44,0x09,0x84,0x54,0xa0,0x80,0xeb,0x75,0x23,0xe1,0x3b,0xb1,0xb7,0xc5,0xb6,0x77,0x5f,0xab,0xab,0xab,0xbe,0x90,0x75,0xfe,0x56,0x87,0xaa,0x45,0x13,0x97,0xbb,0x9c,0xfc,0xcd,0x05,0x12,0x43,0xe9,0xbf,0x5a,0xef,0x24,0x06,0x2d,0x33,0x5d,0xe5,0xfc,0xe2,0x4e,0x9d,0xdb,0xde,0x11,0x91,0x05,0x2d,0x80,0xc3,0x6d,0xf9,0xf8,0x43,0x48,0x72,0xf2,0x77,0xed,0x4f,0x5a,0x1c,0xe8,0xeb,0xd3,0xb9,0x60,0x82,0x4a,0x4e,0x4f,0x10,0x01,0xb0,0x4c,0xb6,0x85,0xf9,0xbe,0xe4,0xd0,0xdd,0xb0,0xc5,0x71,0x59,0x8a,0xc2,0x02,0x1a,0x66,0x06,0xfd,0x23,0x34,0x5c,0x6f,0xbb,0x84,0xf0,0xce,0x05,0xfe,0x52,0x73,0x45,0x21,0xb7,0xb0,0x7c,0x63,0x88,0xd3,0xa3,0xb9,0x93,0x18,0xbf,0x01,0x31,0x50,0x4a,0xa9,0xdf,0xba,0xf5,0x48,0xf9,0xd3,0x2a,0x9c,0xd4,0xc6,0x89,0x35,0x24,0xb1,0x13,0x30,0xa2,0xd3,0xaa,0xd3,0xed,0x2a,0x58,0x96,0x6e,0xbb,0x01,0x34,0x46,0x5d,0x54,0x3f,0xd7,0x79,0x7a,0xf5,0x49,0xf5,0x68,0xea,0xeb,0xe9,0x57,0xf6,0x4f,0xec,0x85,0x46,0x74,0x90,0x2b,0x97,0x55,0x87,0x56,0x98,0x69,0x46,0xea,0x3a,0xb7,0xa2,0x51,0xcb,0xbe,0xa1,0x1a,0x68,0x7b,0xd4,0x3f,0x5d,0x0b,0xd8,0x9c,0xd2,0xca,0xba,0x61,0xd5,0x21,0x83,0x74,0x99,0x0e,0xe8,0xb9,0x22,0x19,0xed,0x25,0xdc,0xa0,0x11,0xc6,0x8a,0x97,0x57,0xc0,0x13,0xbd,0x83,0x7b,0x2d,0xd7,0x34,0xe3,0x75,0x1f,0x64,0xfc,0xb4,0xb2,0x3d,0xcd,0x6b,0xc5,0x7e,0xa5,0x67,0xf5,0x71,0x6e,0x17,0x36,0x72,0x44,0x75,0x1e,0x23,0x03,0xb2,0x2a,0x95,0x3e,0x77,0x27,0x56,0x95,0x6c,0xdc,0xc0,0x13,0xff,0xd2,0xc3,0x24,0x90,0x75,0x44,0x22,0xa5,0x72,0x52,0x9d,0x4c,0x92,0xf1,0xeb,0xb1,0x9f,0x1d,0xad,0x4d,0x03,0x6f,0x2f,0xdf,0x31,0xca,0x91,0x01,0xbd,0xf8,0x1a,0xea,0x94,0x8a,0xed,0xcf,0x21,0x7a,0xa8,0xfc,0xcd,0x7a,0x07,0x71,0xaa,0x27,0x53,0xe1,0xa8,0x23,0xbf,0x41,0xc9,0x53,0x77,0xa2,0xff,0xa6,0x1b,0x22,0x65,0x13,0x81,0x53,0xce,0x86,0xd2,0xc8,0x7d,0xd0,0x7a,0x4b,0x32,0xd2,0x7f,0x5f,0x28,0x72,0x64,0x14,0x31,0xce,0x9a,0x18,0xa5,0x02,0xaa,0xef,0xd9,0xaf,0xc5,0xb0,0xd1,0x3c,0xd4,0x6c,0x35,0x7e,0x38,0xe6,0x9e,0x1e,0xe9,0x45,0xad,0xd1,0x99,0x29,0x32,0xa5,0xb1,0xe5,0xc5,0x62,0x9c,0x9f,0x48,0xf7,0x66,0x18,0x53,0xda,0x00,0x78,0x7c,0x9d,0x78,0xfb,0x92,0x55,0x53,0xbf,0x07,0xa5,0x0d,0xd5,0xb9,0xd9,0x35,0x85,0x34,0x20,0xe4,0xd1,0xa7,0x1a,0xe6,0x2f,0xf9,0x0c,0xa1,0x93,0xcd,0xd6,0xc2,0xf4,0xbe,0xd2,0x63,0x41,0x5a,0xaf,0x9a,0x35,0x09,0x4b,0xc2,0xa2,0x2e,0x2a,0x66,0x3c,0x76,0x45,0x00,0x1c,0xd1,0x90,0xb7,0xbc,0x17,0xc7,0x5f,0xea,0xdf,0x8e,0x87,0xce,0x5c,0x24,0xb7,0x63,0xb6,0x58,0x4e,0xd3,0x2e,0x71,0xb0,0x26,0x81,0x42,0xea,0x3e,0xd6,0x89,0x81,0x57,0xbf,0x92,0x3b,0xeb,0xf0,0x19,0x2d,0x1b,0xf5,0xee,0x30,0xa7,0xd3,0x51,0x63,0x4a,0x60,0xb5,0x04,0xdd,0xe3,0x8a,0x2e,0x11,0x4f,0x7a,0xe9,0xbf,0x17,0x6d,0x4a,0x18,0xba,0x28,0x95,0xa7,0xbb,0x4b,0x47,0x44,0x4a,0x9b,0xa8,0xdb,0xb4,0xc1,0x24,0xcd,0x41,0xbb,0xb3,0x2f,0x4b,0xcb,0x1d,0xe4,0x8c,0x4a,0xbb,0x51,0x06,0x07,0xa0,0x01,0xb5,0xa0,0x00,0xbb,0xa4,0x36,0x18,0xb6,0xc1,0x9e,0x43,0x51,0x7b,0x45,0xb4,0x24,0x05,0x92,0x8b,0x67,0xc7,0x13,0x88,0x18,0x58,0xba,0xd3,0xa4,0x25,0x11,0xc2,0x71,0x6f,0xf9,0xcd,0x33,0x20,0x34,0xb6,0x72,0xb5,0x2f,0xf1,0x66,0x10,0x80,0x5c,0xdb,0xe7,0x54,0x4a,0x8a,0x84,0xb6,0x6e,0x1c,0x74,0x5a,0x73,0xc1,0xb6,0xbc,0xda,0x5b,0x77,0xb9,0x51,0xf3,0x6c,0x0f,0x7a,0x53,0x72,0xde,0x9e,0x5d,0x1f,0x9b,0xbc,0xde,0x88,0x43,0xc6,0x90,0x90,0x02,0xdd,0xa4,0x87,0x5e,0x67,0x57,0x1a,0xf0,0xbe,0xc5,0x81,0x85,0x6c,0x32,0xc0,0x9c,0x24,0x0e,0x66,0x4e,0x76,0x1e,0x57,0xcd,0x0d,0x8d,0xc8,0xa7,0x1c,0xb9,0x18,0xa5,0x76,0x2d,0x11,0x12,0x85,0xcd,0x8b,0x56,0x13,0xdd,0xbd,0x0c,0xa0,0x8a,0xc0,0x34,0x2b,0x2b,0xde,0xe3,0x8f,0x96,0xfa,0x75,0x4b,0xb2,0xb0,0x87,0x17,0x9c,0x11,0x3c,0x93,0x98,0x6a,0x81,0x03,0x56,0xeb,0x94,0x54,0x0b,0x93,0xcb,0x9d,0xec,0x4a,0xa9,0x29,0x0f,0xf1,0x2e,0xc1,0xaa,0x2e,0x65,0x6c,0x9b,0xe3,0xd5,0x90,0x75,0x3c,0x36,0x6c,0x60,0x14,0x06,0xc0,0x61,0xbc,0x22,0x03,0x3a,0x1f,0xd1,0xf4,0xe1,0x11,0x1d,0x03,0x9b,0x88,0x13,0xb9,0x83,0xcb,0x50,0x6c,0x3e,0xa7,0xff,0x30,0x57,0x98,0x3e,0x8b,0xf0,0x16,0x82,0xfb,0xb0,0x0f,0x43,0x00,0x53,0x13,0xc8,0x2c,0x13,0x92,0x91,0x8a,0x61,0x65,0xa1,0x33,0x38,0xff,0xe1,0x1a,0x99,0x2c,0x1f,0xb3,0xd1,0x03,0x2a,0xa6,0x79,0xa4,0x18,0xc8,0xba,0x4f,0x8a,0x0b,0xc1,0x99,0xe1,0x0c,0xf6,0xbd,0x77,0xa1,0x4f,0xdd,0x6a,0x06,0x09,0x35,0x14,0x34,0x8e,0x3a,0x89,0x74,0x43,0x4a,0xe8,0xa3,0x67,0x63,0x69,0xc6,0xbe,0x2c,0xf9,0x0e,0x67,0x2b,0x34,0x3f,0xce,0x04,0xac,0x6b,0x22,0xe0,0xcf,0x47,0x56,0x8b,0xc4,0x5d,0x70,0xa6,0x8e,0x68,0xc6,0x49,0xa4,0x83,0x0a,0xe2,0x18,0x59,0x0c,0x1a,0x43,0x7e,0x7a,0x23,0xa5,0x4e,0xfe,0x44,0xf6,0x70,0x86,0xeb,0x69,0x7b,0x9f,0xa5,0x78,0x35,0xf0,0xb8,0xf7,0x0f,0x0a,0x92,0x92,0x26,0xef,0xb3,0x36,0xc0,0xe2,0x18,0x33,0xa0,0x28,0x21,0x8c,0xd6,0x37,0x32,0xc8,0x0a,0xa4,0x77,0xe6,0x2d,0x14,0x1d,0xba,0x81,0x85,0x4f,0x70,0xda,0x68,0xda,0xff,0x4a,0x84,0xcb,0x6d,0xe7,0x79,0x25,0x4e,0x8a,0x97,0xe7,0x35,0x65,0x37,0x4a,0xf4,0x09,0x2a,0xf0,0x5c,0xbd,0x66,0x54,0xaf,0xc3,0xfd,0x72,0xf0,0xae,0x23,0x26,0x95,0xcb,0x66,0x68,0xea,0xfe,0xcc,0x40,0x69,0xbd,0x90,0xbb,0x52,0x8b,0x83,0xef,0xa2,0xfb,0xcd,0xbd,0x93,0xb2,0x89,0x92,0x96,0x21,0xed,0x74,0xd8,0x08,0x73,0x8f,0xc1,0x03,0xee,0xb1,0x05,0x51,0x08,0x51,0xfc,0x93,0x19,0xf1,0x71,0xea,0x0c,0xed,0x0b,0x97,0xb5,0xb9,0xfb,0x5e,0xf9,0x85,0x18,0x6b,0xc5,0x20,0x98,0xf9,0xeb,0x47,0x6f,0x67,0xb7,0xcc,0x76,0x65,0xd4,0x75,0x87,0x97,0x5c,0xb4,0x5a,0x50,0xfc,0x64,0x10,0x07,0x19,0xbf,0x76,0x34,0x5f,0x0f,0xdf,0x1e,0x09,0xef,0xe9,0xfb,0x80,0x0d,0xc1,0x14,0xe4,0x6b,0xe0,0x87,0x9a,0x19,0x5c,0xc0,0x68,0x70,0xe2,0x3d,0x26,0x31,0xda,0xe7,0x1c,0x39,0x94,0x48,0x1c,0x87,0x61,0xc4,0x0d,0x07,0xc5,0xbf,0xca,0x95,0xe7,0x18,0xb7,0xb2,0x25,0x85,0xaf,0x03,0xed,0x34,0x17,0x5a,0x46,0xd5,0x7a,0xf3,0x51,0x8e,0x32,0xa7,0xfc,0x1a,0xa4,0x48,0x27,0x32,0xa8,0x1a,0x87,0xf7,0x24,0xf8,0xd2,0xe7,0x80,0xb3,0xa3,0x9d,0x45,0x1a,0x38,0x0f,0x75,0xc2,0xd6,0x80,0xcc,0x72,0x13,0xea,0xb1,0xd4,0xa5,0x9d,0x39,0x4a,0xe3,0x81,0x0a,0x1c,0x90,0x81,0x8d,0x52,0xf9,0x3f,0xb2,0x03,0xe2,0xd8,0xb1,0xb5,0xfa,0x8f,0x60,0xb2,0xd5,0x85,0xd9,0x13,0x5d,0x64,0x88,0x46,0xf1,0x38,0xb8,0x69,0x53,0x24,0x2d,0x2b,0xb1,0xf2,0xec,0xdf,0x38,0x9b,0x4d,0xe7,0x65,0x18,0x17,0xb8,0xe4,0xe6,0x4b,0x33,0x3f,0x1a,0xac,0x52,0x3a,0x93,0xf2,0x74,0x8a,0x9c,0x38,0xff,0xbc,0x29,0xce,0xd4,0x57,0xb6,0xf9,0x78,0x1b,0x08,0xa6,0x7a,0x19,0x75,0xd0,0x31,0xcc,0xd7,0x15,0x45,0xc0,0x03,0x74,0x34,0x05,0x6c,0x24,0x34,0xd1,0x3e,0x6c,0x4b,0xee,0xbf,0x46,0xfc,0x12,0x22,0x2c,0x0b,0x2e,0xcc,0xd6,0x15,0x9d,0x5a,0xea,0x8e,0x55,0x4d,0x7a,0x09,0x65,0x2b,0x06,0xbf,0x7c,0xa6,0x99,0xa7,0x19,0x9e,0x71,0x6d,0x05,0xdd,0x55,0x30,0x41,0xa8,0xf2,0xb3,0x03,0xd2,0x36,0xa9,0xba,0xba,0xaf,0xb9,0xfa,0x52,0x8f,0x28,0xa2,0xca,0x2a,0xa7,0x80,0xb9,0x40,0x38,0x3c,0x09,0x9a,0xa6,0x5a,0x00,0x74,0xb8,0x3f,0xd1,0xf0,0xbc,0x5b,0x7b,0x5e,0x46,0xc2,0x5e,0x54,0x83,0x8b,0x3c,0xbc,0xfc,0x95,0xf8,0x7f,0x1d,0x47,0x1b,0x3b,0xa8,0x94,0x43,0x4f,0xa5,0x89,0x52,0xfd,0xcb,0x77,0xf1,0x61,0x37,0x26,0x93,0x30,0x6d,0xba,0x4e,0x8f,0x21,0x6d,0x1c,0x8e,0x5c,0xaf,0xf0,0xfe,0x83,0x60,0xa5,0x1c,0x60,0x76,0x36,0x44,0x16,0x9f,0xdc,0x6a,0x82,0x67,0xf2,0xe3,0xf9,0x09,0xa6,0x1b,0x2a,0x67,0x8b,0xce,0x6a,0xe9,0x04,0x03,0xa8,0x36,0xb1,0xa7,0xb7,0xe8,0xcd,0x8b,0x54,0xc3,0x70,0x87,0xa9,0xe1,0x44,0x46,0xd9,0x5e,0x69,0x08,0xd2,0xee,0xdb,0xfc,0xc6,0x53,0xe0,0x2f,0xdf,0x77,0x1f,0x70,0x1a,0x79,0xb9,0xe5,0xa2,0x6e,0xd0,0xa9,0x47,0x84,0x20,0x70,0xf3,0xb5,0x70,0x17,0x42,0x21,0x12,0x19,0xe7,0x61,0x76,0x2c,0x37,0xf0,0xd0,0xa1,0xd1,0xb9,0x75,0x0f,0xee,0x57,0x7e,0x12,0x08,0x11,0x5c,0x66,0xac,0x07,0xec,0x09,0x1e,0x6a,0x3f,0xc4,0xaa,0x6a,0x25,0x3b,0xcb,0xa8,0x68,0xed,0xd3,0x15,0x4d,0xca,0xf5,0x16,0x2f,0x61,0x5e,0x85,0x49,0x0a,0x6c,0xa3,0x42,0xf3,0x4c,0x43,0xac,0x61,0xa3,0xea,0x6b,0xfe,0xef,0xd8,0x50,0xe1,0x90,0xeb,0x1d,0x8d,0xa4,0xd2,0x8b,0x5e,0xce,0xeb,0x16,0x78,0xc0,0x24,0x33,0xec,0xd5,0xd4,0x8b,0x25,0x36,0x40,0x42,0x57,0xe8,0xca,0x7b,0xef,0x58,0x55,0xf2,0xb8,0x13,0xed,0x2f,0x4c,0x40,0x94,0x45,0xa3,0x31,0x7c,0x9b,0xe1,0xa3,0x5a,0xe2,0xfb,0x4d,0x2b,0x87,0x92,0x1b,0x90,0x4b,0xf2,0xc1,0x4d,0xb5,0x14,0xce,0xe0,0x45,0x25,0x1c,0xfc,0x27,0x63,0x74,0xdb,0x15,0xc9,0x9d,0xea,0x15,0xac,0xde,0x19,0x7c,0x6e,0xb5,0x24,0x98,0x8e,0x39,0xb6,0x32,0x87,0xbe,0xb8,0x67,0x68,0x65,0xaa,0xa3,0xba,0xd1,0xb4,0x3b,0x8c,0xab,0x15,0xcb,0xf2,0x7a,0x49,0x87,0x59,0xe3,0x20,0x3a,0xbf,0x36,0x9e,0x97,0x24,0x2f,0x0b,0x01,0x54,0x14,0x9f,0x14,0xac,0x23,0x3c,0xdb,0x73,0xa2,0x2b,0x7f,0xb8,0xf0,0x93,0x25,0xbf,0x2a,0xce,0x83,0xbb,0x6b,0x5d,0xb8,0xa1,0x21,0xa2,0xb6,0x82,0x14,0x9a,0x69,0x13,0x1c,0xcc,0xe5,0x22,0x29,0x84,0x0b,0x11,0x3f,0xc7,0xb0,0xbc,0xc5,0x84,0x05,0xbf,0xe8,0x7f,0x1f,0x95,0xff,0xc2,0xe9,0x6f,0xc5,0x59,0x65,0x67,0xe9,0x43,0x64,0xdf,0xaa,0x6d,0x9d,0x5a,0x6e,0xb9,0x9a,0xe4,0xdd,0xf4,0x24]).unwrap(); + let sk = MLDSA87PrivateKey::from_bytes(&[ + 0x97, 0x92, 0xBC, 0xEC, 0x2F, 0x24, 0x30, 0x68, 0x6A, 0x82, 0xFC, 0xCF, 0x3C, 0x2F, 0x5F, + 0xF6, 0x65, 0xE7, 0x71, 0xD7, 0xAB, 0x41, 0xB9, 0x02, 0x58, 0xCF, 0xA7, 0xE9, 0x0E, 0xC9, + 0x71, 0x24, 0xD8, 0xE9, 0xEE, 0x4E, 0x90, 0xA1, 0x6C, 0x60, 0x2F, 0x5E, 0xC9, 0xBC, 0x38, + 0x51, 0x7D, 0xC3, 0x0E, 0x32, 0x9D, 0x5A, 0xB2, 0x76, 0x73, 0xBD, 0x85, 0xF4, 0xC9, 0xB0, + 0x30, 0x0F, 0x77, 0x63, 0x89, 0x88, 0x67, 0x50, 0xB5, 0x7C, 0x24, 0xDB, 0x3F, 0xC0, 0x12, + 0xE6, 0x1E, 0xDE, 0x59, 0x75, 0x33, 0x37, 0x37, 0x4F, 0xA7, 0x12, 0x49, 0x91, 0x54, 0x9A, + 0xF2, 0x43, 0x49, 0x6D, 0x06, 0x37, 0xCB, 0x3B, 0xE0, 0x5A, 0x59, 0x48, 0x23, 0x5B, 0xF7, + 0x98, 0x75, 0xF8, 0x96, 0xD8, 0xFE, 0x0C, 0xAB, 0x30, 0xC8, 0x49, 0x48, 0xDB, 0x4D, 0x63, + 0x15, 0xAA, 0xAF, 0x16, 0x0A, 0xC6, 0x24, 0x36, 0x64, 0x22, 0x01, 0x48, 0x16, 0x11, 0x09, + 0x11, 0x2C, 0x94, 0x02, 0x89, 0x22, 0x45, 0x2C, 0x62, 0xB8, 0x45, 0x00, 0x45, 0x2A, 0x08, + 0x96, 0x70, 0x90, 0x12, 0x6E, 0x14, 0x93, 0x70, 0xD4, 0x46, 0x10, 0x84, 0x44, 0x51, 0x58, + 0x96, 0x91, 0x0C, 0xA9, 0x29, 0x82, 0xB2, 0x41, 0xC9, 0x08, 0x71, 0xC4, 0x28, 0x68, 0x04, + 0x96, 0x89, 0x48, 0x40, 0x85, 0x9B, 0x22, 0x6D, 0x1C, 0x28, 0x64, 0x59, 0x12, 0x41, 0x9C, + 0xB8, 0x91, 0x84, 0x04, 0x89, 0x44, 0x90, 0x05, 0xCB, 0x34, 0x62, 0xA0, 0x86, 0x90, 0x40, + 0x26, 0x92, 0x20, 0x99, 0x29, 0x13, 0x05, 0x69, 0x5C, 0x34, 0x68, 0xA4, 0x32, 0x8E, 0x19, + 0x26, 0x92, 0x59, 0x46, 0x10, 0x09, 0xA4, 0x49, 0x23, 0x42, 0x4D, 0x12, 0x36, 0x61, 0x58, + 0x10, 0x65, 0x01, 0x28, 0x90, 0x1A, 0x33, 0x4C, 0x99, 0x86, 0x31, 0xD3, 0xA2, 0x49, 0x09, + 0x82, 0x25, 0x43, 0x14, 0x28, 0xC0, 0x38, 0x81, 0x03, 0x15, 0x4D, 0x5B, 0x28, 0x86, 0x08, + 0x87, 0x48, 0x23, 0x31, 0x52, 0x94, 0x22, 0x25, 0xC3, 0xC0, 0x4D, 0xA4, 0x98, 0x21, 0x98, + 0x40, 0x20, 0xD1, 0x42, 0x86, 0xCB, 0x40, 0x70, 0x5B, 0xB0, 0x71, 0x9C, 0x96, 0x2C, 0xC1, + 0x12, 0x06, 0x53, 0x46, 0x09, 0x0C, 0x45, 0x02, 0x14, 0x46, 0x6E, 0x91, 0xB4, 0x21, 0x54, + 0xB0, 0x8C, 0xE4, 0x46, 0x42, 0x9A, 0x20, 0x8C, 0x01, 0x21, 0x25, 0x13, 0x41, 0x05, 0x5A, + 0x40, 0x22, 0x13, 0xC9, 0x0C, 0xA0, 0x18, 0x40, 0x52, 0xC2, 0x30, 0xCB, 0x34, 0x2C, 0x4B, + 0xC8, 0x68, 0x1B, 0xA4, 0x60, 0x49, 0x84, 0x84, 0x63, 0x30, 0x29, 0x4A, 0xA0, 0x69, 0x5B, + 0x80, 0x04, 0xD2, 0x38, 0x0A, 0x14, 0x26, 0x4C, 0xE2, 0xB2, 0x44, 0x8B, 0xA2, 0x11, 0x24, + 0x46, 0x49, 0xC4, 0x14, 0x52, 0x0B, 0x42, 0x71, 0x03, 0xB2, 0x10, 0x92, 0x28, 0x80, 0x01, + 0x24, 0x88, 0xE3, 0x08, 0x11, 0x0A, 0x05, 0x28, 0x19, 0xC4, 0x81, 0x00, 0x20, 0x22, 0xDC, + 0x44, 0x68, 0x42, 0x12, 0x22, 0x44, 0x00, 0x2A, 0xC9, 0x26, 0x6A, 0x0C, 0x87, 0x31, 0xE0, + 0xC0, 0x44, 0x99, 0x14, 0x84, 0x18, 0x36, 0x0D, 0x11, 0x37, 0x42, 0x22, 0x18, 0x8C, 0x63, + 0xB2, 0x91, 0x0C, 0x98, 0x08, 0xA1, 0xA0, 0x01, 0x08, 0x92, 0x44, 0x04, 0x13, 0x24, 0x5C, + 0x98, 0x71, 0x82, 0x84, 0x71, 0x84, 0x32, 0x52, 0x51, 0xB0, 0x31, 0x9C, 0x42, 0x2E, 0x1A, + 0xA8, 0x28, 0x02, 0x08, 0x91, 0x01, 0xC0, 0x89, 0x0B, 0xC7, 0x05, 0x8A, 0x24, 0x65, 0x22, + 0xC2, 0x64, 0x4C, 0x88, 0x91, 0x5C, 0x82, 0x68, 0x13, 0xA5, 0x64, 0x50, 0x20, 0x86, 0x21, + 0x04, 0x02, 0x91, 0x94, 0x44, 0x48, 0x22, 0x30, 0x22, 0x39, 0x4A, 0x02, 0x98, 0x84, 0x09, + 0xA2, 0x88, 0x19, 0x19, 0x29, 0x44, 0x48, 0x8C, 0x22, 0x95, 0x0C, 0xA1, 0x04, 0x72, 0x04, + 0x87, 0x70, 0x12, 0x21, 0x10, 0x42, 0x06, 0x84, 0x1B, 0x49, 0x85, 0x89, 0x06, 0x4A, 0xD3, + 0x36, 0x08, 0xDB, 0xC0, 0x40, 0x58, 0xB6, 0x51, 0x0C, 0xA7, 0x09, 0x8C, 0x24, 0x61, 0x99, + 0x90, 0x64, 0x8C, 0xC2, 0x90, 0x5B, 0x24, 0x90, 0x10, 0xA3, 0x49, 0x03, 0x25, 0x61, 0x43, + 0x32, 0x8A, 0x11, 0x98, 0x44, 0xC8, 0xB2, 0x20, 0x04, 0x38, 0x41, 0x10, 0x47, 0x2C, 0x19, + 0xC6, 0x44, 0x1C, 0x25, 0x2C, 0x04, 0x88, 0x30, 0xD9, 0x46, 0x69, 0x9B, 0x20, 0x00, 0x1B, + 0x46, 0x82, 0x5A, 0xA4, 0x80, 0x5B, 0xA0, 0x49, 0x18, 0x90, 0x25, 0x00, 0x26, 0x80, 0x0B, + 0xC2, 0x31, 0x5A, 0x40, 0x72, 0x54, 0xC6, 0x20, 0xC1, 0xB0, 0x31, 0x24, 0xB1, 0x4D, 0x10, + 0x95, 0x28, 0x14, 0x00, 0x0A, 0xA0, 0xC8, 0x4D, 0x54, 0xA2, 0x88, 0x23, 0x98, 0x81, 0x60, + 0x90, 0x04, 0x02, 0x16, 0x2C, 0x13, 0x21, 0x40, 0x91, 0x86, 0x8D, 0x08, 0xC2, 0x91, 0x91, + 0x14, 0x26, 0xD0, 0xB4, 0x0C, 0x09, 0xC6, 0x60, 0x51, 0x44, 0x2E, 0x04, 0x11, 0x26, 0x00, + 0x29, 0x11, 0x93, 0xC2, 0x08, 0x63, 0xA4, 0x31, 0x22, 0x00, 0x28, 0xC1, 0x14, 0x08, 0x0C, + 0x40, 0x2C, 0x41, 0x14, 0x06, 0x9C, 0x20, 0x72, 0x54, 0x22, 0x06, 0x8B, 0x08, 0x4D, 0xA1, + 0x48, 0x69, 0x10, 0x30, 0x41, 0x1C, 0x28, 0x49, 0x44, 0x18, 0x8E, 0xCC, 0x96, 0x48, 0xD9, + 0x42, 0x50, 0x1B, 0x06, 0x49, 0x0C, 0x45, 0x88, 0x23, 0x04, 0x0D, 0x20, 0x30, 0x2E, 0x23, + 0x85, 0x2C, 0x14, 0x07, 0x30, 0x40, 0xB6, 0x85, 0x4C, 0xC0, 0x20, 0x44, 0x86, 0x20, 0x19, + 0x00, 0x6C, 0x54, 0x02, 0x48, 0xCC, 0x88, 0x6C, 0x59, 0x06, 0x32, 0x49, 0x14, 0x84, 0x04, + 0xC7, 0x50, 0x13, 0x49, 0x28, 0xE4, 0x06, 0x09, 0xD3, 0xC6, 0x10, 0xC8, 0x28, 0x4C, 0x23, + 0x39, 0x44, 0x52, 0xA4, 0x64, 0xCC, 0xA8, 0x49, 0x44, 0x38, 0x32, 0x0A, 0x89, 0x84, 0x00, + 0x34, 0x2C, 0x22, 0x85, 0x8D, 0x10, 0x31, 0x09, 0x09, 0x32, 0x65, 0x1C, 0x89, 0x8C, 0x40, + 0x40, 0x29, 0x21, 0x85, 0x00, 0x09, 0xA1, 0x6D, 0x84, 0xC0, 0x64, 0xE2, 0x02, 0x2D, 0x48, + 0x04, 0x40, 0x12, 0x09, 0x8E, 0xE0, 0x42, 0x2E, 0x93, 0x44, 0x08, 0x12, 0x10, 0x6A, 0x01, + 0x84, 0x05, 0x92, 0x30, 0x8A, 0xCB, 0x34, 0x8E, 0xA2, 0x26, 0x2E, 0x5C, 0x86, 0x11, 0x0B, + 0x35, 0x08, 0x18, 0x10, 0x00, 0x02, 0x34, 0x26, 0x24, 0x23, 0x89, 0xD1, 0x84, 0x00, 0x24, + 0x46, 0x60, 0x13, 0xB2, 0x49, 0x24, 0x28, 0x46, 0x18, 0x02, 0x71, 0xA0, 0x38, 0x90, 0x89, + 0x14, 0x44, 0xD3, 0x96, 0x2D, 0xA3, 0x18, 0x40, 0x23, 0x27, 0x21, 0xC0, 0x18, 0x50, 0x43, + 0xC8, 0x04, 0x41, 0x42, 0x8D, 0x5C, 0x26, 0x41, 0x44, 0xA2, 0x6D, 0x48, 0x12, 0x0E, 0x40, + 0x32, 0x25, 0x0B, 0x14, 0x82, 0x0A, 0x48, 0x2E, 0xCB, 0x82, 0x88, 0x03, 0xA3, 0x60, 0x1B, + 0x25, 0x26, 0x8C, 0xB8, 0x20, 0x24, 0xB0, 0x85, 0x98, 0x04, 0x21, 0x08, 0xA7, 0x2C, 0x83, + 0x38, 0x64, 0x54, 0x32, 0x89, 0x01, 0x04, 0x01, 0x23, 0x49, 0x84, 0x02, 0x95, 0x69, 0xD1, + 0xA4, 0x4D, 0x13, 0xA4, 0x0C, 0x91, 0x46, 0x0D, 0x61, 0x94, 0x80, 0x90, 0x38, 0x45, 0x5C, + 0xC6, 0x50, 0x01, 0x17, 0x20, 0x53, 0xC6, 0x28, 0x9B, 0x18, 0x10, 0x41, 0x12, 0x68, 0x90, + 0x12, 0x21, 0xC0, 0x10, 0x84, 0x42, 0x16, 0x92, 0x53, 0x82, 0x29, 0x81, 0x26, 0x49, 0xD8, + 0xA4, 0x05, 0x9A, 0x26, 0x24, 0x24, 0x03, 0x29, 0xE0, 0x40, 0x26, 0xD2, 0x02, 0x48, 0x11, + 0x24, 0x68, 0x11, 0x99, 0x89, 0x98, 0x14, 0x85, 0xC9, 0x20, 0x0D, 0x50, 0x12, 0x8C, 0x1C, + 0x08, 0x10, 0x02, 0x10, 0x00, 0x00, 0x95, 0x28, 0xC1, 0x28, 0x90, 0x59, 0xB4, 0x85, 0xD3, + 0x14, 0x65, 0x0A, 0x40, 0x6E, 0x11, 0x29, 0x65, 0x18, 0xC2, 0x4C, 0x21, 0x34, 0x65, 0xD8, + 0x30, 0x69, 0x10, 0x30, 0x52, 0x19, 0x31, 0x66, 0x8C, 0x18, 0x8A, 0xC8, 0xC0, 0x08, 0x4C, + 0x98, 0x30, 0xA3, 0xA6, 0x20, 0x41, 0x16, 0x22, 0x18, 0x05, 0x2E, 0x22, 0x25, 0x2A, 0x64, + 0xB8, 0x25, 0x0A, 0xB3, 0x01, 0x63, 0x20, 0x84, 0x09, 0x80, 0x09, 0x19, 0x28, 0x0E, 0x02, + 0x11, 0x01, 0xA3, 0x94, 0x11, 0xE3, 0x98, 0x6C, 0x58, 0x20, 0x21, 0x09, 0x41, 0x10, 0x60, + 0x36, 0x22, 0x08, 0x06, 0x71, 0x12, 0xC2, 0x85, 0x5A, 0xA0, 0x85, 0xC0, 0xC6, 0x84, 0x5C, + 0x38, 0x06, 0xCB, 0xB6, 0x69, 0x14, 0x84, 0x84, 0x53, 0x22, 0x82, 0xA1, 0xA6, 0x40, 0xCC, + 0x86, 0x00, 0xC4, 0x26, 0x22, 0xA0, 0xA8, 0x08, 0x98, 0x34, 0x72, 0xD4, 0x20, 0x41, 0x43, + 0xC4, 0x90, 0x48, 0x16, 0x68, 0x1B, 0x16, 0x52, 0x1A, 0x37, 0x02, 0x50, 0x20, 0x42, 0x48, + 0x48, 0x8A, 0x20, 0x11, 0x41, 0xE2, 0x00, 0x6C, 0xC0, 0xC2, 0x0C, 0x14, 0x06, 0x49, 0x11, + 0x31, 0x4D, 0x19, 0x06, 0x0A, 0x89, 0x46, 0x09, 0x1B, 0x81, 0x65, 0x44, 0xC8, 0x00, 0x82, + 0x06, 0x70, 0x00, 0x16, 0x72, 0xCC, 0x24, 0x50, 0x8A, 0x42, 0x89, 0x9C, 0x96, 0x90, 0x64, + 0x28, 0x70, 0x92, 0xB2, 0x68, 0x98, 0x26, 0x62, 0x61, 0x94, 0x40, 0xC1, 0x16, 0x89, 0xD8, + 0x42, 0x64, 0x1A, 0x21, 0x4E, 0x62, 0x90, 0x64, 0x21, 0xC8, 0x24, 0x8B, 0x28, 0x6D, 0x5C, + 0x42, 0x92, 0xA0, 0xC6, 0x4D, 0x0C, 0x85, 0x80, 0xCC, 0x88, 0x4D, 0xD4, 0x42, 0x8D, 0x42, + 0x34, 0x8A, 0x0B, 0x04, 0x51, 0xC3, 0x26, 0x86, 0x24, 0x25, 0x81, 0x12, 0x35, 0x06, 0xA0, + 0x44, 0x04, 0xC8, 0x94, 0x81, 0x5B, 0xB4, 0x31, 0x1C, 0x08, 0x06, 0x5C, 0x24, 0x08, 0x03, + 0x27, 0x6A, 0x20, 0xC2, 0x25, 0xE1, 0x80, 0x90, 0x19, 0xB4, 0x6D, 0xA3, 0x46, 0x0C, 0x4B, + 0x18, 0x60, 0x50, 0xC6, 0x2C, 0x1B, 0x92, 0x2D, 0x11, 0x15, 0x04, 0xA2, 0x00, 0x04, 0x21, + 0x48, 0x2E, 0xD8, 0x16, 0x06, 0xD2, 0x10, 0x8A, 0x83, 0xA2, 0x25, 0x08, 0x31, 0x0D, 0x09, + 0x38, 0x51, 0xD9, 0x48, 0x49, 0x0B, 0x16, 0x4C, 0x23, 0x32, 0x25, 0x19, 0x19, 0x02, 0x4A, + 0x44, 0x09, 0xD1, 0xB2, 0x21, 0x0B, 0x83, 0x2C, 0x23, 0x25, 0x85, 0x93, 0x16, 0x85, 0x44, + 0xA0, 0x44, 0x1B, 0x83, 0x50, 0x02, 0x22, 0x72, 0x4B, 0x04, 0x80, 0x9B, 0x14, 0x65, 0x21, + 0x93, 0x60, 0x18, 0x13, 0x0A, 0xD9, 0x46, 0x0D, 0x22, 0x45, 0x61, 0xC8, 0xB4, 0x40, 0xA1, + 0x42, 0x2D, 0x02, 0xB8, 0x09, 0x00, 0x14, 0x44, 0x9B, 0xB6, 0x11, 0x0B, 0x97, 0x8C, 0x40, + 0x10, 0x4A, 0x82, 0x14, 0x6A, 0xDA, 0x90, 0x05, 0x1C, 0x02, 0x8E, 0x0C, 0x19, 0x72, 0xA3, + 0xB4, 0x8D, 0x24, 0x30, 0x50, 0x11, 0x87, 0x09, 0x64, 0xC6, 0x28, 0xE4, 0x18, 0x92, 0x98, + 0xB4, 0x6C, 0x61, 0x16, 0x51, 0x40, 0x46, 0x0E, 0x1C, 0x32, 0x48, 0xDA, 0x20, 0x51, 0x88, + 0x36, 0x8A, 0x23, 0xB1, 0x21, 0x82, 0x90, 0x28, 0x1A, 0x15, 0x32, 0xE2, 0x18, 0x61, 0x92, + 0x04, 0x8E, 0x13, 0xB6, 0x90, 0x13, 0x13, 0x68, 0xC9, 0x84, 0x68, 0x4C, 0x40, 0x6D, 0x0B, + 0x33, 0x00, 0x81, 0x46, 0x4D, 0xD2, 0x38, 0x0C, 0x04, 0x96, 0x81, 0xA4, 0x88, 0x50, 0x02, + 0x90, 0x85, 0x22, 0xB0, 0x04, 0xD3, 0xA4, 0x71, 0xD2, 0x80, 0x10, 0xCA, 0x96, 0x40, 0x51, + 0xA6, 0x41, 0xA4, 0x84, 0x28, 0xE0, 0x08, 0x52, 0x0B, 0x30, 0x8C, 0xD2, 0x38, 0x0A, 0x0C, + 0x29, 0x51, 0xC3, 0x82, 0x09, 0xCA, 0x20, 0x91, 0xD8, 0x36, 0x92, 0xA3, 0xA6, 0x28, 0x92, + 0x42, 0x22, 0xA2, 0x16, 0x01, 0x1A, 0x34, 0x86, 0x37, 0xD9, 0xA6, 0x59, 0x16, 0x98, 0x81, + 0xEC, 0x21, 0xCF, 0x48, 0x11, 0x86, 0x9D, 0x1D, 0x7F, 0x13, 0x9F, 0x05, 0x37, 0xE9, 0x6F, + 0x11, 0x84, 0x58, 0x54, 0x05, 0xFD, 0x17, 0x80, 0x8A, 0xF1, 0xE0, 0x62, 0x39, 0xD3, 0xB3, + 0x4E, 0x5A, 0xCA, 0x8B, 0xF1, 0x36, 0x96, 0x77, 0xB4, 0x47, 0xAC, 0x71, 0x8A, 0xC4, 0x7D, + 0x85, 0x0C, 0x4D, 0x77, 0xB0, 0xBE, 0x31, 0xDC, 0x9F, 0x50, 0x8E, 0x39, 0x78, 0xF2, 0x42, + 0x74, 0xAB, 0x01, 0x85, 0xF7, 0x27, 0xAB, 0xDF, 0xF5, 0x9F, 0x44, 0x90, 0x37, 0x1B, 0xF0, + 0x46, 0x10, 0xE3, 0x64, 0xE6, 0x4E, 0xC8, 0x75, 0xEF, 0x9D, 0x20, 0xDC, 0x94, 0x07, 0x7E, + 0x1E, 0x16, 0x63, 0x27, 0xA8, 0x79, 0xB8, 0xAB, 0x51, 0x61, 0x60, 0xB2, 0xA3, 0xF7, 0x74, + 0x37, 0xB9, 0xB3, 0xCC, 0x7D, 0x17, 0xAE, 0xAD, 0xDC, 0x84, 0xDB, 0x62, 0x74, 0x6A, 0x35, + 0xAC, 0x09, 0x6F, 0x78, 0x2F, 0x62, 0xA7, 0xF0, 0x1A, 0xA6, 0xD6, 0x69, 0x3D, 0xEE, 0xC9, + 0x0B, 0x23, 0xC6, 0x69, 0x85, 0xA0, 0x23, 0x07, 0xE0, 0xA1, 0xCA, 0xE5, 0x98, 0xA6, 0x73, + 0x24, 0xDB, 0xA0, 0xF5, 0x2F, 0x22, 0x43, 0x22, 0x75, 0xE9, 0x32, 0x57, 0x06, 0x5C, 0x3B, + 0x7E, 0x5E, 0x1C, 0xFE, 0x1D, 0xFD, 0x4D, 0x0D, 0xF0, 0x86, 0xDF, 0x21, 0x24, 0x34, 0x14, + 0xA2, 0xD2, 0x7E, 0x20, 0x23, 0x0A, 0x82, 0x9B, 0xE4, 0xEB, 0x4C, 0x82, 0xC1, 0x6D, 0x35, + 0xF7, 0x8B, 0x0E, 0x5E, 0x19, 0x83, 0x32, 0xE0, 0x00, 0x74, 0xBB, 0x64, 0x61, 0x2F, 0xAB, + 0x17, 0xD4, 0xC8, 0x97, 0x1C, 0xB6, 0x8E, 0x5E, 0xDA, 0xB0, 0x36, 0x9F, 0x11, 0x57, 0xB3, + 0x46, 0x9A, 0xBD, 0x83, 0x84, 0xE2, 0xD9, 0x55, 0x3F, 0x1B, 0x78, 0xE7, 0x86, 0xE1, 0xEE, + 0x9D, 0x0B, 0x98, 0xD3, 0x9F, 0x83, 0xCC, 0xEC, 0xF3, 0x7D, 0x1E, 0xBD, 0x3A, 0x9D, 0x63, + 0xAE, 0xC7, 0x66, 0x16, 0x4A, 0x10, 0x17, 0x1A, 0x4F, 0xD8, 0xC6, 0x3D, 0xAF, 0x18, 0x2C, + 0x42, 0x12, 0x58, 0xC5, 0xF5, 0x29, 0xAA, 0x55, 0xCB, 0x7E, 0xBA, 0xE2, 0xE1, 0x65, 0x23, + 0x15, 0xE1, 0xF7, 0x1E, 0x8A, 0x74, 0x13, 0x14, 0x10, 0xD0, 0x32, 0x47, 0xED, 0xE1, 0x1D, + 0x34, 0xDB, 0x91, 0xF6, 0xF0, 0x8A, 0xA2, 0x47, 0x8F, 0xD7, 0x89, 0x67, 0x9C, 0x04, 0x94, + 0x9F, 0x71, 0xBC, 0x01, 0x71, 0xE0, 0x7E, 0x3A, 0x8B, 0xB5, 0x75, 0x3D, 0xBB, 0xDA, 0xA4, + 0x11, 0xA6, 0x35, 0x0A, 0xB4, 0x6E, 0xEF, 0xBF, 0x86, 0xFC, 0x55, 0x1C, 0x29, 0xEF, 0xE4, + 0xCD, 0xD7, 0x66, 0x1D, 0x5C, 0xF6, 0xC3, 0xDB, 0x22, 0xD0, 0xCE, 0xDD, 0xE5, 0x99, 0x85, + 0x44, 0x59, 0xD9, 0x7F, 0x20, 0xDF, 0x74, 0x55, 0xBD, 0xF3, 0x56, 0xA1, 0x98, 0xD0, 0xF7, + 0xEB, 0x6D, 0x34, 0x11, 0x1F, 0xC9, 0x40, 0xB2, 0x5C, 0x05, 0x43, 0xB7, 0x88, 0xED, 0xDA, + 0x9D, 0x26, 0x81, 0x0E, 0xAC, 0x3D, 0x6C, 0xC9, 0xC5, 0x13, 0x27, 0xC2, 0xCF, 0x83, 0xE8, + 0x87, 0xD4, 0x08, 0x9E, 0x19, 0x69, 0x5E, 0x11, 0xAD, 0xD8, 0x37, 0xF6, 0xF4, 0x40, 0xCC, + 0x36, 0x0F, 0x93, 0xF3, 0x2F, 0xEE, 0x8A, 0x96, 0x63, 0x71, 0x2C, 0x6B, 0xBD, 0x38, 0xC8, + 0x4A, 0xB7, 0xB5, 0x48, 0x23, 0xEC, 0x36, 0x3E, 0xB7, 0xE4, 0x2E, 0xB5, 0x9F, 0xC1, 0xFC, + 0xE6, 0x0F, 0xBD, 0x55, 0x30, 0x7B, 0x3E, 0xC8, 0x5F, 0xD9, 0xDA, 0xF3, 0x20, 0x6D, 0x7B, + 0x4B, 0x39, 0x17, 0xF1, 0xC8, 0xB7, 0xA9, 0x2E, 0x3C, 0x67, 0xD8, 0x98, 0x80, 0xFD, 0xF2, + 0xE4, 0x7F, 0x5A, 0x0C, 0x99, 0x45, 0x95, 0xDB, 0x17, 0x0A, 0xF4, 0x1B, 0xAB, 0xF5, 0xA2, + 0x5B, 0x4D, 0xC1, 0xC4, 0x2D, 0xD6, 0xA9, 0xDB, 0x27, 0x1E, 0x76, 0x4D, 0xE2, 0xFB, 0x01, + 0x5A, 0x49, 0xA8, 0x50, 0xC7, 0x91, 0x9B, 0xE4, 0x70, 0x06, 0xA3, 0x36, 0xE2, 0xE3, 0x25, + 0xFD, 0xE5, 0x3A, 0xC5, 0x99, 0x55, 0x4D, 0x0A, 0x7D, 0xE4, 0xEF, 0x45, 0xEC, 0x40, 0xC3, + 0x9D, 0x6B, 0xAF, 0xF3, 0x11, 0xBE, 0xEE, 0x75, 0xD8, 0x9E, 0x02, 0xAD, 0x31, 0xF4, 0xBE, + 0x4B, 0xD2, 0x0A, 0xE9, 0x19, 0x4F, 0x5E, 0xDD, 0xDA, 0xA6, 0x65, 0x07, 0x76, 0x11, 0x6E, + 0x9F, 0x27, 0x0F, 0x77, 0x71, 0x4A, 0xD7, 0xA8, 0xE8, 0x9A, 0xCE, 0xF7, 0x4B, 0x7F, 0xF7, + 0xD8, 0xDB, 0xEC, 0x27, 0xF8, 0x02, 0x0A, 0x98, 0x52, 0x47, 0xE2, 0xCD, 0xAC, 0xEF, 0x48, + 0x94, 0xA4, 0xD6, 0x8B, 0xA3, 0x7C, 0xA9, 0x12, 0xD6, 0xBE, 0x73, 0x50, 0x1C, 0x99, 0x51, + 0x81, 0xE5, 0xB7, 0x77, 0x23, 0x35, 0x0B, 0x36, 0x31, 0xDA, 0x37, 0x00, 0xE1, 0x3F, 0xD3, + 0x66, 0xE1, 0x31, 0xBF, 0x06, 0xB3, 0x6E, 0xB6, 0xB0, 0x34, 0x50, 0x93, 0x20, 0x9F, 0x0A, + 0x7B, 0xEF, 0xFA, 0xE1, 0xFD, 0xD8, 0x75, 0xB0, 0x06, 0x87, 0xC1, 0x16, 0x3C, 0x35, 0x3D, + 0x7D, 0x2A, 0xC9, 0x09, 0x37, 0xB3, 0x4E, 0x97, 0x8E, 0x92, 0xF8, 0x21, 0xAD, 0xC9, 0x66, + 0x22, 0x02, 0xEC, 0xE8, 0x9A, 0x17, 0xE7, 0xBB, 0x65, 0xAE, 0x17, 0xD8, 0x3B, 0x90, 0xDB, + 0xBE, 0x6A, 0x50, 0x1A, 0x4E, 0x13, 0x45, 0xBE, 0xE4, 0xE5, 0xA5, 0xB5, 0x3A, 0xF2, 0xE5, + 0xBA, 0x3D, 0x1E, 0xF3, 0xF4, 0xE0, 0x5A, 0xDF, 0x0B, 0x3A, 0x4C, 0xF2, 0xE5, 0x30, 0x36, + 0x0F, 0xEE, 0x64, 0x92, 0x99, 0x02, 0xB5, 0x71, 0xF6, 0xFD, 0x2E, 0x30, 0x56, 0x52, 0xA4, + 0xCB, 0x01, 0x0F, 0x79, 0xF8, 0x15, 0xE1, 0x8F, 0x2B, 0xBB, 0x8C, 0xC8, 0x9F, 0xA6, 0xFC, + 0x76, 0xF7, 0x7C, 0x89, 0xE2, 0x93, 0xCF, 0x17, 0x5A, 0x0B, 0x19, 0x58, 0x00, 0xFE, 0x72, + 0xD2, 0xCC, 0xDD, 0x7D, 0x75, 0xE5, 0xBD, 0x90, 0xBC, 0x6A, 0xC4, 0x35, 0xD6, 0xA4, 0x40, + 0xEF, 0x85, 0x2E, 0x9A, 0x1C, 0x8C, 0x53, 0xDE, 0x03, 0xBF, 0x19, 0x33, 0x65, 0xD7, 0x35, + 0xAA, 0xF2, 0x9C, 0x51, 0x62, 0xA6, 0x17, 0xE3, 0x64, 0xE7, 0xF9, 0x44, 0x16, 0x8D, 0x0F, + 0xB4, 0x8F, 0xEF, 0x40, 0x55, 0x8F, 0x45, 0x42, 0x97, 0xCC, 0x3D, 0xD5, 0x08, 0x66, 0x2C, + 0xF2, 0x3F, 0xB8, 0x8E, 0x19, 0x54, 0xAA, 0x45, 0xD1, 0xC5, 0xE1, 0x15, 0xBC, 0xC3, 0x6F, + 0x05, 0xB3, 0xE0, 0x98, 0xD5, 0x55, 0x22, 0x0F, 0x40, 0xBE, 0x26, 0x29, 0xB3, 0x45, 0x07, + 0xB8, 0x46, 0x4C, 0x54, 0xC2, 0x7B, 0x5D, 0xEC, 0x78, 0xDA, 0x8F, 0x22, 0x65, 0x05, 0x14, + 0x79, 0x7A, 0xF8, 0x6A, 0x25, 0x12, 0xBC, 0xB7, 0xE2, 0x92, 0x33, 0x79, 0xEF, 0x6D, 0x73, + 0xC1, 0x37, 0x00, 0x6C, 0x1B, 0x38, 0xF5, 0x1E, 0x37, 0xF9, 0x35, 0x85, 0xE2, 0x90, 0x41, + 0xA3, 0xE4, 0xE3, 0xAF, 0x46, 0x00, 0x7C, 0xE1, 0x3B, 0x8B, 0x5F, 0x7B, 0x17, 0xD5, 0xD6, + 0x5D, 0x7D, 0x56, 0x68, 0xE4, 0x27, 0xBC, 0xBE, 0x7E, 0xC1, 0xD7, 0xC4, 0x08, 0xC0, 0x54, + 0xA4, 0x8C, 0x1A, 0xE7, 0x97, 0xBF, 0x99, 0xAC, 0xBC, 0x8D, 0x26, 0x07, 0x52, 0x29, 0x35, + 0xFD, 0x66, 0x5E, 0xA7, 0x82, 0x2D, 0x93, 0x0F, 0x23, 0xEA, 0xBF, 0xF7, 0x83, 0xBB, 0x23, + 0x69, 0x75, 0x69, 0xE2, 0x04, 0xB9, 0x43, 0x14, 0x1E, 0x00, 0xC0, 0x88, 0x10, 0x95, 0x6B, + 0xE0, 0x52, 0x53, 0x65, 0xDB, 0xAB, 0x54, 0xED, 0x48, 0xCB, 0x76, 0x96, 0x4C, 0xCD, 0xF5, + 0xCB, 0xD3, 0xAE, 0xE7, 0x28, 0x2D, 0x4A, 0x00, 0x00, 0xD2, 0x78, 0x4D, 0x7B, 0x8F, 0xAB, + 0x16, 0xB2, 0xF7, 0xF0, 0xD5, 0x22, 0x57, 0x32, 0xB1, 0xEF, 0xBC, 0x4E, 0xB1, 0xCF, 0xED, + 0xEB, 0x43, 0xFD, 0xE7, 0x9B, 0x69, 0xEC, 0xC0, 0xFB, 0xEA, 0xA1, 0xE6, 0xB4, 0x07, 0x28, + 0x67, 0x3B, 0xD4, 0xB2, 0xE9, 0x8A, 0x0D, 0x4A, 0x8F, 0x02, 0xF8, 0x53, 0x95, 0x07, 0x30, + 0xF2, 0x8D, 0x35, 0xEB, 0x12, 0xFC, 0xC7, 0x97, 0x68, 0xB8, 0xE1, 0x8E, 0x4B, 0xDA, 0x0E, + 0x58, 0xA3, 0x31, 0xA2, 0xF7, 0x1D, 0x7C, 0xCC, 0x2D, 0x45, 0x1B, 0x32, 0xB1, 0xC6, 0x5C, + 0x31, 0x2A, 0xCF, 0x47, 0xEE, 0x51, 0x3B, 0x21, 0x95, 0x4C, 0x41, 0xC0, 0x0C, 0x87, 0x38, + 0x72, 0xEE, 0x94, 0xCF, 0x14, 0xF4, 0x60, 0x37, 0x42, 0x53, 0x61, 0xF4, 0xBD, 0xB5, 0x48, + 0x21, 0xF7, 0x11, 0x46, 0x0C, 0xEB, 0xAE, 0x8C, 0x07, 0x50, 0x8A, 0x92, 0x19, 0xF8, 0x8F, + 0xA6, 0xBE, 0xDA, 0xA6, 0x78, 0xEE, 0xD5, 0x01, 0x94, 0x4A, 0x16, 0xAE, 0x6F, 0x7B, 0x5B, + 0xB7, 0xA2, 0xE1, 0xE3, 0x57, 0xE7, 0x0D, 0x7B, 0x98, 0x46, 0x1A, 0x2C, 0x71, 0xCB, 0x0F, + 0xA7, 0x62, 0xD6, 0xAD, 0x98, 0x24, 0x08, 0x1D, 0x37, 0xF2, 0x92, 0xFD, 0x4B, 0xE8, 0xB8, + 0x4C, 0x36, 0x11, 0x0D, 0xC7, 0x44, 0x36, 0x02, 0x01, 0xBE, 0xEB, 0xE0, 0xBD, 0x6C, 0x9D, + 0x05, 0xE8, 0x69, 0x25, 0x6D, 0x2F, 0xF3, 0xF9, 0x95, 0x17, 0xB7, 0xEF, 0xD2, 0xA3, 0x37, + 0x74, 0x05, 0x6C, 0xB5, 0x67, 0x16, 0x75, 0xA8, 0xB4, 0x92, 0xE9, 0xF5, 0xF2, 0x62, 0x0E, + 0xB8, 0xEF, 0x93, 0x81, 0xD3, 0xD1, 0xDF, 0x19, 0x93, 0x8B, 0x7B, 0x5F, 0xFA, 0xAC, 0x59, + 0xBC, 0x81, 0x10, 0xFA, 0x87, 0xBA, 0x8D, 0x7A, 0x3D, 0x01, 0x65, 0xF8, 0xE4, 0x1D, 0xD0, + 0xF8, 0x04, 0xF1, 0x1B, 0x9D, 0xED, 0x0F, 0x35, 0x2A, 0x59, 0x78, 0x35, 0xD0, 0x63, 0x07, + 0xA8, 0xE0, 0xC6, 0xEF, 0x4D, 0x21, 0x90, 0x43, 0x39, 0xE1, 0xCF, 0x45, 0x89, 0x23, 0xA3, + 0xE8, 0x9E, 0x02, 0x5D, 0x94, 0x53, 0x47, 0x36, 0x6C, 0x02, 0xF3, 0xDD, 0x63, 0x68, 0xD4, + 0xE4, 0x7E, 0x85, 0xD3, 0xD2, 0xA9, 0x70, 0x5B, 0xD5, 0x79, 0x61, 0x85, 0x2E, 0x5A, 0x57, + 0x9F, 0x93, 0xB1, 0xC5, 0x14, 0xC5, 0x39, 0xF4, 0x9E, 0xA1, 0x16, 0x3A, 0x2A, 0x49, 0x3B, + 0x0E, 0xFC, 0xB4, 0x7F, 0x47, 0x48, 0xF6, 0xA9, 0x9E, 0x10, 0xBF, 0x70, 0x78, 0x28, 0x2E, + 0x4A, 0xCE, 0x18, 0x13, 0x6E, 0x2A, 0x8B, 0x3E, 0xE0, 0xA3, 0x80, 0xDC, 0xD3, 0xB3, 0xEF, + 0x3E, 0x65, 0xE1, 0xB8, 0x15, 0x72, 0x89, 0xD6, 0x24, 0x67, 0xAD, 0x48, 0x8B, 0xA0, 0x39, + 0x2B, 0x2E, 0x90, 0xA1, 0xED, 0xED, 0xCB, 0xDC, 0x93, 0x1D, 0xC1, 0x72, 0x98, 0xCC, 0xEF, + 0x76, 0x64, 0x5C, 0x7D, 0x33, 0x0A, 0x05, 0xC2, 0xCE, 0x40, 0xF8, 0x9B, 0x85, 0x46, 0x8F, + 0x35, 0x7A, 0x21, 0x77, 0x51, 0xE1, 0x54, 0x63, 0x13, 0x04, 0xEC, 0x4E, 0x04, 0xBB, 0x45, + 0xB3, 0x67, 0x89, 0x09, 0xC7, 0x4A, 0xF5, 0x1C, 0xE3, 0x70, 0x36, 0x4D, 0x8F, 0x4F, 0x7E, + 0xB1, 0xE6, 0x1E, 0x00, 0x28, 0x74, 0x29, 0xC9, 0x96, 0x1D, 0xE8, 0x32, 0x2C, 0xA9, 0xA2, + 0x62, 0x9B, 0x13, 0x09, 0xD8, 0x00, 0xE9, 0x2B, 0xC1, 0xDC, 0x50, 0x55, 0xDC, 0xC7, 0x97, + 0xF3, 0x38, 0x66, 0xEB, 0x0C, 0xFD, 0x8D, 0x49, 0x02, 0x50, 0xD4, 0x8F, 0xFC, 0xA8, 0x02, + 0x2F, 0x49, 0x29, 0x0E, 0x2D, 0x53, 0x76, 0x16, 0x2F, 0xBA, 0xA9, 0x82, 0xD1, 0x64, 0x53, + 0xC8, 0x25, 0xB3, 0x5F, 0x65, 0x15, 0x63, 0x5E, 0xA9, 0x2B, 0xEA, 0x72, 0x36, 0x7B, 0xAA, + 0x54, 0xDE, 0x3F, 0x9E, 0xAE, 0xA6, 0x95, 0x42, 0xA8, 0x1A, 0x41, 0x27, 0xF7, 0x1C, 0xBA, + 0xA2, 0x57, 0xF3, 0x24, 0xFE, 0xFE, 0xF1, 0x4F, 0x08, 0xFB, 0xD6, 0x5A, 0x04, 0x9C, 0xD2, + 0xFB, 0x36, 0x25, 0x94, 0xA8, 0xE2, 0x3F, 0xF1, 0xA2, 0x61, 0x7D, 0xB5, 0xB1, 0x58, 0xF6, + 0xF0, 0x1C, 0xF5, 0x0A, 0xB0, 0xED, 0x95, 0xC6, 0xE7, 0x09, 0x84, 0x11, 0x64, 0x10, 0x8B, + 0x06, 0xE1, 0xB4, 0x0A, 0xB0, 0xAB, 0x11, 0xC4, 0x08, 0x30, 0x1D, 0x3D, 0x9D, 0x8E, 0xA6, + 0x9E, 0x96, 0x8A, 0x96, 0x00, 0xB3, 0xD1, 0x7F, 0x38, 0x01, 0x1C, 0xE2, 0x80, 0x74, 0xE2, + 0xC2, 0xE1, 0x0B, 0xF6, 0x19, 0x7C, 0x60, 0x2D, 0x8D, 0x0C, 0xE7, 0xD3, 0xA3, 0xEF, 0x2D, + 0x89, 0x62, 0x3B, 0xC9, 0xF1, 0x2E, 0xA3, 0x38, 0x79, 0x1E, 0x92, 0x66, 0xBB, 0x8C, 0xE0, + 0x2B, 0x12, 0x4C, 0x6C, 0x79, 0x29, 0xBA, 0xEA, 0x69, 0x32, 0x44, 0x09, 0x84, 0x54, 0xA0, + 0x80, 0xEB, 0x75, 0x23, 0xE1, 0x3B, 0xB1, 0xB7, 0xC5, 0xB6, 0x77, 0x5F, 0xAB, 0xAB, 0xAB, + 0xBE, 0x90, 0x75, 0xFE, 0x56, 0x87, 0xAA, 0x45, 0x13, 0x97, 0xBB, 0x9C, 0xFC, 0xCD, 0x05, + 0x12, 0x43, 0xE9, 0xBF, 0x5A, 0xEF, 0x24, 0x06, 0x2D, 0x33, 0x5D, 0xE5, 0xFC, 0xE2, 0x4E, + 0x9D, 0xDB, 0xDE, 0x11, 0x91, 0x05, 0x2D, 0x80, 0xC3, 0x6D, 0xF9, 0xF8, 0x43, 0x48, 0x72, + 0xF2, 0x77, 0xED, 0x4F, 0x5A, 0x1C, 0xE8, 0xEB, 0xD3, 0xB9, 0x60, 0x82, 0x4A, 0x4E, 0x4F, + 0x10, 0x01, 0xB0, 0x4C, 0xB6, 0x85, 0xF9, 0xBE, 0xE4, 0xD0, 0xDD, 0xB0, 0xC5, 0x71, 0x59, + 0x8A, 0xC2, 0x02, 0x1A, 0x66, 0x06, 0xFD, 0x23, 0x34, 0x5C, 0x6F, 0xBB, 0x84, 0xF0, 0xCE, + 0x05, 0xFE, 0x52, 0x73, 0x45, 0x21, 0xB7, 0xB0, 0x7C, 0x63, 0x88, 0xD3, 0xA3, 0xB9, 0x93, + 0x18, 0xBF, 0x01, 0x31, 0x50, 0x4A, 0xA9, 0xDF, 0xBA, 0xF5, 0x48, 0xF9, 0xD3, 0x2A, 0x9C, + 0xD4, 0xC6, 0x89, 0x35, 0x24, 0xB1, 0x13, 0x30, 0xA2, 0xD3, 0xAA, 0xD3, 0xED, 0x2A, 0x58, + 0x96, 0x6E, 0xBB, 0x01, 0x34, 0x46, 0x5D, 0x54, 0x3F, 0xD7, 0x79, 0x7A, 0xF5, 0x49, 0xF5, + 0x68, 0xEA, 0xEB, 0xE9, 0x57, 0xF6, 0x4F, 0xEC, 0x85, 0x46, 0x74, 0x90, 0x2B, 0x97, 0x55, + 0x87, 0x56, 0x98, 0x69, 0x46, 0xEA, 0x3A, 0xB7, 0xA2, 0x51, 0xCB, 0xBE, 0xA1, 0x1A, 0x68, + 0x7B, 0xD4, 0x3F, 0x5D, 0x0B, 0xD8, 0x9C, 0xD2, 0xCA, 0xBA, 0x61, 0xD5, 0x21, 0x83, 0x74, + 0x99, 0x0E, 0xE8, 0xB9, 0x22, 0x19, 0xED, 0x25, 0xDC, 0xA0, 0x11, 0xC6, 0x8A, 0x97, 0x57, + 0xC0, 0x13, 0xBD, 0x83, 0x7B, 0x2D, 0xD7, 0x34, 0xE3, 0x75, 0x1F, 0x64, 0xFC, 0xB4, 0xB2, + 0x3D, 0xCD, 0x6B, 0xC5, 0x7E, 0xA5, 0x67, 0xF5, 0x71, 0x6E, 0x17, 0x36, 0x72, 0x44, 0x75, + 0x1E, 0x23, 0x03, 0xB2, 0x2A, 0x95, 0x3E, 0x77, 0x27, 0x56, 0x95, 0x6C, 0xDC, 0xC0, 0x13, + 0xFF, 0xD2, 0xC3, 0x24, 0x90, 0x75, 0x44, 0x22, 0xA5, 0x72, 0x52, 0x9D, 0x4C, 0x92, 0xF1, + 0xEB, 0xB1, 0x9F, 0x1D, 0xAD, 0x4D, 0x03, 0x6F, 0x2F, 0xDF, 0x31, 0xCA, 0x91, 0x01, 0xBD, + 0xF8, 0x1A, 0xEA, 0x94, 0x8A, 0xED, 0xCF, 0x21, 0x7A, 0xA8, 0xFC, 0xCD, 0x7A, 0x07, 0x71, + 0xAA, 0x27, 0x53, 0xE1, 0xA8, 0x23, 0xBF, 0x41, 0xC9, 0x53, 0x77, 0xA2, 0xFF, 0xA6, 0x1B, + 0x22, 0x65, 0x13, 0x81, 0x53, 0xCE, 0x86, 0xD2, 0xC8, 0x7D, 0xD0, 0x7A, 0x4B, 0x32, 0xD2, + 0x7F, 0x5F, 0x28, 0x72, 0x64, 0x14, 0x31, 0xCE, 0x9A, 0x18, 0xA5, 0x02, 0xAA, 0xEF, 0xD9, + 0xAF, 0xC5, 0xB0, 0xD1, 0x3C, 0xD4, 0x6C, 0x35, 0x7E, 0x38, 0xE6, 0x9E, 0x1E, 0xE9, 0x45, + 0xAD, 0xD1, 0x99, 0x29, 0x32, 0xA5, 0xB1, 0xE5, 0xC5, 0x62, 0x9C, 0x9F, 0x48, 0xF7, 0x66, + 0x18, 0x53, 0xDA, 0x00, 0x78, 0x7C, 0x9D, 0x78, 0xFB, 0x92, 0x55, 0x53, 0xBF, 0x07, 0xA5, + 0x0D, 0xD5, 0xB9, 0xD9, 0x35, 0x85, 0x34, 0x20, 0xE4, 0xD1, 0xA7, 0x1A, 0xE6, 0x2F, 0xF9, + 0x0C, 0xA1, 0x93, 0xCD, 0xD6, 0xC2, 0xF4, 0xBE, 0xD2, 0x63, 0x41, 0x5A, 0xAF, 0x9A, 0x35, + 0x09, 0x4B, 0xC2, 0xA2, 0x2E, 0x2A, 0x66, 0x3C, 0x76, 0x45, 0x00, 0x1C, 0xD1, 0x90, 0xB7, + 0xBC, 0x17, 0xC7, 0x5F, 0xEA, 0xDF, 0x8E, 0x87, 0xCE, 0x5C, 0x24, 0xB7, 0x63, 0xB6, 0x58, + 0x4E, 0xD3, 0x2E, 0x71, 0xB0, 0x26, 0x81, 0x42, 0xEA, 0x3E, 0xD6, 0x89, 0x81, 0x57, 0xBF, + 0x92, 0x3B, 0xEB, 0xF0, 0x19, 0x2D, 0x1B, 0xF5, 0xEE, 0x30, 0xA7, 0xD3, 0x51, 0x63, 0x4A, + 0x60, 0xB5, 0x04, 0xDD, 0xE3, 0x8A, 0x2E, 0x11, 0x4F, 0x7A, 0xE9, 0xBF, 0x17, 0x6D, 0x4A, + 0x18, 0xBA, 0x28, 0x95, 0xA7, 0xBB, 0x4B, 0x47, 0x44, 0x4A, 0x9B, 0xA8, 0xDB, 0xB4, 0xC1, + 0x24, 0xCD, 0x41, 0xBB, 0xB3, 0x2F, 0x4B, 0xCB, 0x1D, 0xE4, 0x8C, 0x4A, 0xBB, 0x51, 0x06, + 0x07, 0xA0, 0x01, 0xB5, 0xA0, 0x00, 0xBB, 0xA4, 0x36, 0x18, 0xB6, 0xC1, 0x9E, 0x43, 0x51, + 0x7B, 0x45, 0xB4, 0x24, 0x05, 0x92, 0x8B, 0x67, 0xC7, 0x13, 0x88, 0x18, 0x58, 0xBA, 0xD3, + 0xA4, 0x25, 0x11, 0xC2, 0x71, 0x6F, 0xF9, 0xCD, 0x33, 0x20, 0x34, 0xB6, 0x72, 0xB5, 0x2F, + 0xF1, 0x66, 0x10, 0x80, 0x5C, 0xDB, 0xE7, 0x54, 0x4A, 0x8A, 0x84, 0xB6, 0x6E, 0x1C, 0x74, + 0x5A, 0x73, 0xC1, 0xB6, 0xBC, 0xDA, 0x5B, 0x77, 0xB9, 0x51, 0xF3, 0x6C, 0x0F, 0x7A, 0x53, + 0x72, 0xDE, 0x9E, 0x5D, 0x1F, 0x9B, 0xBC, 0xDE, 0x88, 0x43, 0xC6, 0x90, 0x90, 0x02, 0xDD, + 0xA4, 0x87, 0x5E, 0x67, 0x57, 0x1A, 0xF0, 0xBE, 0xC5, 0x81, 0x85, 0x6C, 0x32, 0xC0, 0x9C, + 0x24, 0x0E, 0x66, 0x4E, 0x76, 0x1E, 0x57, 0xCD, 0x0D, 0x8D, 0xC8, 0xA7, 0x1C, 0xB9, 0x18, + 0xA5, 0x76, 0x2D, 0x11, 0x12, 0x85, 0xCD, 0x8B, 0x56, 0x13, 0xDD, 0xBD, 0x0C, 0xA0, 0x8A, + 0xC0, 0x34, 0x2B, 0x2B, 0xDE, 0xE3, 0x8F, 0x96, 0xFA, 0x75, 0x4B, 0xB2, 0xB0, 0x87, 0x17, + 0x9C, 0x11, 0x3C, 0x93, 0x98, 0x6A, 0x81, 0x03, 0x56, 0xEB, 0x94, 0x54, 0x0B, 0x93, 0xCB, + 0x9D, 0xEC, 0x4A, 0xA9, 0x29, 0x0F, 0xF1, 0x2E, 0xC1, 0xAA, 0x2E, 0x65, 0x6C, 0x9B, 0xE3, + 0xD5, 0x90, 0x75, 0x3C, 0x36, 0x6C, 0x60, 0x14, 0x06, 0xC0, 0x61, 0xBC, 0x22, 0x03, 0x3A, + 0x1F, 0xD1, 0xF4, 0xE1, 0x11, 0x1D, 0x03, 0x9B, 0x88, 0x13, 0xB9, 0x83, 0xCB, 0x50, 0x6C, + 0x3E, 0xA7, 0xFF, 0x30, 0x57, 0x98, 0x3E, 0x8B, 0xF0, 0x16, 0x82, 0xFB, 0xB0, 0x0F, 0x43, + 0x00, 0x53, 0x13, 0xC8, 0x2C, 0x13, 0x92, 0x91, 0x8A, 0x61, 0x65, 0xA1, 0x33, 0x38, 0xFF, + 0xE1, 0x1A, 0x99, 0x2C, 0x1F, 0xB3, 0xD1, 0x03, 0x2A, 0xA6, 0x79, 0xA4, 0x18, 0xC8, 0xBA, + 0x4F, 0x8A, 0x0B, 0xC1, 0x99, 0xE1, 0x0C, 0xF6, 0xBD, 0x77, 0xA1, 0x4F, 0xDD, 0x6A, 0x06, + 0x09, 0x35, 0x14, 0x34, 0x8E, 0x3A, 0x89, 0x74, 0x43, 0x4A, 0xE8, 0xA3, 0x67, 0x63, 0x69, + 0xC6, 0xBE, 0x2C, 0xF9, 0x0E, 0x67, 0x2B, 0x34, 0x3F, 0xCE, 0x04, 0xAC, 0x6B, 0x22, 0xE0, + 0xCF, 0x47, 0x56, 0x8B, 0xC4, 0x5D, 0x70, 0xA6, 0x8E, 0x68, 0xC6, 0x49, 0xA4, 0x83, 0x0A, + 0xE2, 0x18, 0x59, 0x0C, 0x1A, 0x43, 0x7E, 0x7A, 0x23, 0xA5, 0x4E, 0xFE, 0x44, 0xF6, 0x70, + 0x86, 0xEB, 0x69, 0x7B, 0x9F, 0xA5, 0x78, 0x35, 0xF0, 0xB8, 0xF7, 0x0F, 0x0A, 0x92, 0x92, + 0x26, 0xEF, 0xB3, 0x36, 0xC0, 0xE2, 0x18, 0x33, 0xA0, 0x28, 0x21, 0x8C, 0xD6, 0x37, 0x32, + 0xC8, 0x0A, 0xA4, 0x77, 0xE6, 0x2D, 0x14, 0x1D, 0xBA, 0x81, 0x85, 0x4F, 0x70, 0xDA, 0x68, + 0xDA, 0xFF, 0x4A, 0x84, 0xCB, 0x6D, 0xE7, 0x79, 0x25, 0x4E, 0x8A, 0x97, 0xE7, 0x35, 0x65, + 0x37, 0x4A, 0xF4, 0x09, 0x2A, 0xF0, 0x5C, 0xBD, 0x66, 0x54, 0xAF, 0xC3, 0xFD, 0x72, 0xF0, + 0xAE, 0x23, 0x26, 0x95, 0xCB, 0x66, 0x68, 0xEA, 0xFE, 0xCC, 0x40, 0x69, 0xBD, 0x90, 0xBB, + 0x52, 0x8B, 0x83, 0xEF, 0xA2, 0xFB, 0xCD, 0xBD, 0x93, 0xB2, 0x89, 0x92, 0x96, 0x21, 0xED, + 0x74, 0xD8, 0x08, 0x73, 0x8F, 0xC1, 0x03, 0xEE, 0xB1, 0x05, 0x51, 0x08, 0x51, 0xFC, 0x93, + 0x19, 0xF1, 0x71, 0xEA, 0x0C, 0xED, 0x0B, 0x97, 0xB5, 0xB9, 0xFB, 0x5E, 0xF9, 0x85, 0x18, + 0x6B, 0xC5, 0x20, 0x98, 0xF9, 0xEB, 0x47, 0x6F, 0x67, 0xB7, 0xCC, 0x76, 0x65, 0xD4, 0x75, + 0x87, 0x97, 0x5C, 0xB4, 0x5A, 0x50, 0xFC, 0x64, 0x10, 0x07, 0x19, 0xBF, 0x76, 0x34, 0x5F, + 0x0F, 0xDF, 0x1E, 0x09, 0xEF, 0xE9, 0xFB, 0x80, 0x0D, 0xC1, 0x14, 0xE4, 0x6B, 0xE0, 0x87, + 0x9A, 0x19, 0x5C, 0xC0, 0x68, 0x70, 0xE2, 0x3D, 0x26, 0x31, 0xDA, 0xE7, 0x1C, 0x39, 0x94, + 0x48, 0x1C, 0x87, 0x61, 0xC4, 0x0D, 0x07, 0xC5, 0xBF, 0xCA, 0x95, 0xE7, 0x18, 0xB7, 0xB2, + 0x25, 0x85, 0xAF, 0x03, 0xED, 0x34, 0x17, 0x5A, 0x46, 0xD5, 0x7A, 0xF3, 0x51, 0x8E, 0x32, + 0xA7, 0xFC, 0x1A, 0xA4, 0x48, 0x27, 0x32, 0xA8, 0x1A, 0x87, 0xF7, 0x24, 0xF8, 0xD2, 0xE7, + 0x80, 0xB3, 0xA3, 0x9D, 0x45, 0x1A, 0x38, 0x0F, 0x75, 0xC2, 0xD6, 0x80, 0xCC, 0x72, 0x13, + 0xEA, 0xB1, 0xD4, 0xA5, 0x9D, 0x39, 0x4A, 0xE3, 0x81, 0x0A, 0x1C, 0x90, 0x81, 0x8D, 0x52, + 0xF9, 0x3F, 0xB2, 0x03, 0xE2, 0xD8, 0xB1, 0xB5, 0xFA, 0x8F, 0x60, 0xB2, 0xD5, 0x85, 0xD9, + 0x13, 0x5D, 0x64, 0x88, 0x46, 0xF1, 0x38, 0xB8, 0x69, 0x53, 0x24, 0x2D, 0x2B, 0xB1, 0xF2, + 0xEC, 0xDF, 0x38, 0x9B, 0x4D, 0xE7, 0x65, 0x18, 0x17, 0xB8, 0xE4, 0xE6, 0x4B, 0x33, 0x3F, + 0x1A, 0xAC, 0x52, 0x3A, 0x93, 0xF2, 0x74, 0x8A, 0x9C, 0x38, 0xFF, 0xBC, 0x29, 0xCE, 0xD4, + 0x57, 0xB6, 0xF9, 0x78, 0x1B, 0x08, 0xA6, 0x7A, 0x19, 0x75, 0xD0, 0x31, 0xCC, 0xD7, 0x15, + 0x45, 0xC0, 0x03, 0x74, 0x34, 0x05, 0x6C, 0x24, 0x34, 0xD1, 0x3E, 0x6C, 0x4B, 0xEE, 0xBF, + 0x46, 0xFC, 0x12, 0x22, 0x2C, 0x0B, 0x2E, 0xCC, 0xD6, 0x15, 0x9D, 0x5A, 0xEA, 0x8E, 0x55, + 0x4D, 0x7A, 0x09, 0x65, 0x2B, 0x06, 0xBF, 0x7C, 0xA6, 0x99, 0xA7, 0x19, 0x9E, 0x71, 0x6D, + 0x05, 0xDD, 0x55, 0x30, 0x41, 0xA8, 0xF2, 0xB3, 0x03, 0xD2, 0x36, 0xA9, 0xBA, 0xBA, 0xAF, + 0xB9, 0xFA, 0x52, 0x8F, 0x28, 0xA2, 0xCA, 0x2A, 0xA7, 0x80, 0xB9, 0x40, 0x38, 0x3C, 0x09, + 0x9A, 0xA6, 0x5A, 0x00, 0x74, 0xB8, 0x3F, 0xD1, 0xF0, 0xBC, 0x5B, 0x7B, 0x5E, 0x46, 0xC2, + 0x5E, 0x54, 0x83, 0x8B, 0x3C, 0xBC, 0xFC, 0x95, 0xF8, 0x7F, 0x1D, 0x47, 0x1B, 0x3B, 0xA8, + 0x94, 0x43, 0x4F, 0xA5, 0x89, 0x52, 0xFD, 0xCB, 0x77, 0xF1, 0x61, 0x37, 0x26, 0x93, 0x30, + 0x6D, 0xBA, 0x4E, 0x8F, 0x21, 0x6D, 0x1C, 0x8E, 0x5C, 0xAF, 0xF0, 0xFE, 0x83, 0x60, 0xA5, + 0x1C, 0x60, 0x76, 0x36, 0x44, 0x16, 0x9F, 0xDC, 0x6A, 0x82, 0x67, 0xF2, 0xE3, 0xF9, 0x09, + 0xA6, 0x1B, 0x2A, 0x67, 0x8B, 0xCE, 0x6A, 0xE9, 0x04, 0x03, 0xA8, 0x36, 0xB1, 0xA7, 0xB7, + 0xE8, 0xCD, 0x8B, 0x54, 0xC3, 0x70, 0x87, 0xA9, 0xE1, 0x44, 0x46, 0xD9, 0x5E, 0x69, 0x08, + 0xD2, 0xEE, 0xDB, 0xFC, 0xC6, 0x53, 0xE0, 0x2F, 0xDF, 0x77, 0x1F, 0x70, 0x1A, 0x79, 0xB9, + 0xE5, 0xA2, 0x6E, 0xD0, 0xA9, 0x47, 0x84, 0x20, 0x70, 0xF3, 0xB5, 0x70, 0x17, 0x42, 0x21, + 0x12, 0x19, 0xE7, 0x61, 0x76, 0x2C, 0x37, 0xF0, 0xD0, 0xA1, 0xD1, 0xB9, 0x75, 0x0F, 0xEE, + 0x57, 0x7E, 0x12, 0x08, 0x11, 0x5C, 0x66, 0xAC, 0x07, 0xEC, 0x09, 0x1E, 0x6A, 0x3F, 0xC4, + 0xAA, 0x6A, 0x25, 0x3B, 0xCB, 0xA8, 0x68, 0xED, 0xD3, 0x15, 0x4D, 0xCA, 0xF5, 0x16, 0x2F, + 0x61, 0x5E, 0x85, 0x49, 0x0A, 0x6C, 0xA3, 0x42, 0xF3, 0x4C, 0x43, 0xAC, 0x61, 0xA3, 0xEA, + 0x6B, 0xFE, 0xEF, 0xD8, 0x50, 0xE1, 0x90, 0xEB, 0x1D, 0x8D, 0xA4, 0xD2, 0x8B, 0x5E, 0xCE, + 0xEB, 0x16, 0x78, 0xC0, 0x24, 0x33, 0xEC, 0xD5, 0xD4, 0x8B, 0x25, 0x36, 0x40, 0x42, 0x57, + 0xE8, 0xCA, 0x7B, 0xEF, 0x58, 0x55, 0xF2, 0xB8, 0x13, 0xED, 0x2F, 0x4C, 0x40, 0x94, 0x45, + 0xA3, 0x31, 0x7C, 0x9B, 0xE1, 0xA3, 0x5A, 0xE2, 0xFB, 0x4D, 0x2B, 0x87, 0x92, 0x1B, 0x90, + 0x4B, 0xF2, 0xC1, 0x4D, 0xB5, 0x14, 0xCE, 0xE0, 0x45, 0x25, 0x1C, 0xFC, 0x27, 0x63, 0x74, + 0xDB, 0x15, 0xC9, 0x9D, 0xEA, 0x15, 0xAC, 0xDE, 0x19, 0x7C, 0x6E, 0xB5, 0x24, 0x98, 0x8E, + 0x39, 0xB6, 0x32, 0x87, 0xBE, 0xB8, 0x67, 0x68, 0x65, 0xAA, 0xA3, 0xBA, 0xD1, 0xB4, 0x3B, + 0x8C, 0xAB, 0x15, 0xCB, 0xF2, 0x7A, 0x49, 0x87, 0x59, 0xE3, 0x20, 0x3A, 0xBF, 0x36, 0x9E, + 0x97, 0x24, 0x2F, 0x0B, 0x01, 0x54, 0x14, 0x9F, 0x14, 0xAC, 0x23, 0x3C, 0xDB, 0x73, 0xA2, + 0x2B, 0x7F, 0xB8, 0xF0, 0x93, 0x25, 0xBF, 0x2A, 0xCE, 0x83, 0xBB, 0x6B, 0x5D, 0xB8, 0xA1, + 0x21, 0xA2, 0xB6, 0x82, 0x14, 0x9A, 0x69, 0x13, 0x1C, 0xCC, 0xE5, 0x22, 0x29, 0x84, 0x0B, + 0x11, 0x3F, 0xC7, 0xB0, 0xBC, 0xC5, 0x84, 0x05, 0xBF, 0xE8, 0x7F, 0x1F, 0x95, 0xFF, 0xC2, + 0xE9, 0x6F, 0xC5, 0x59, 0x65, 0x67, 0xE9, 0x43, 0x64, 0xDF, 0xAA, 0x6D, 0x9D, 0x5A, 0x6E, + 0xB9, 0x9A, 0xE4, 0xDD, 0xF4, 0x24, + ]) + .unwrap(); let mu = MLDSA87::compute_mu_from_sk(&sk, msg, None).unwrap(); let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); @@ -276,7 +1122,7 @@ fn bench_mldsa87_sign() { } fn bench_mldsa87_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA87, MLDSA87PrivateKey, MLDSA87_SK_LEN}; + use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SK_LEN, MLDSA87PrivateKey, MLDSATrait}; eprintln!("MLDSA87_lowmemory/Sign"); @@ -289,7 +1135,12 @@ fn bench_mldsa87_lowmemory_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA87PrivateKey::from_bytes(&[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f]).unwrap(); + let sk = MLDSA87PrivateKey::from_bytes(&[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, + 0x1E, 0x1F, + ]) + .unwrap(); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -299,8 +1150,8 @@ fn bench_mldsa87_lowmemory_sign() { } fn bench_mldsa44_verify() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey}; use bouncycastle::hex; + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey, MLDSATrait}; eprintln!("MLDSA44/Verify"); @@ -319,8 +1170,261 @@ fn bench_mldsa44_verify() { // let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); // eprintln!("sig:\n{}", &*hex::encode(sig)); - let mldsa44_pk = MLDSA44PublicKey::from_bytes(&[0xd7,0xb2,0xb4,0x72,0x54,0xaa,0xe0,0xdb,0x45,0xe7,0x93,0x0d,0x4a,0x98,0xd2,0xc9,0x7d,0x8f,0x13,0x97,0xd1,0x78,0x9d,0xaf,0xa1,0x70,0x24,0xb3,0x16,0xe9,0xbe,0xc9,0x4f,0xc9,0x94,0x6d,0x42,0xf1,0x9b,0x79,0xa7,0x41,0x3b,0xba,0xa3,0x3e,0x71,0x49,0xcb,0x42,0xed,0x51,0x15,0x69,0x3a,0xc0,0x41,0xfa,0xcb,0x98,0x8a,0xde,0xb5,0xfe,0x0e,0x1d,0x86,0x31,0x18,0x49,0x95,0xb5,0x92,0xc3,0x97,0xd2,0x29,0x4e,0x2e,0x14,0xf9,0x0a,0xa4,0x14,0xba,0x38,0x26,0x89,0x9a,0xc4,0x3f,0x4c,0xcc,0xac,0xbc,0x26,0xe9,0xa8,0x32,0xb9,0x51,0x18,0xd5,0xcb,0x43,0x3c,0xbe,0xf9,0x66,0x0b,0x00,0x13,0x8e,0x08,0x17,0xf6,0x1e,0x76,0x2c,0xa2,0x74,0xc3,0x6a,0xd5,0x54,0xeb,0x22,0xaa,0xc1,0x16,0x2e,0x4a,0xb0,0x1a,0xcb,0xa1,0xe3,0x8c,0x4e,0xfd,0x8f,0x80,0xb6,0x5b,0x33,0x3d,0x0f,0x72,0xe5,0x5d,0xfe,0x71,0xce,0x9c,0x1e,0xbb,0x98,0x89,0xe7,0xc5,0x61,0x06,0xc0,0xfd,0x73,0x80,0x3a,0x2a,0xec,0xfe,0xaf,0xde,0xd7,0xaa,0x3c,0xb2,0xce,0xda,0x54,0xd1,0x2b,0xd8,0xcd,0x36,0xa7,0x8c,0xf9,0x75,0x94,0x3b,0x47,0xab,0xd2,0x5e,0x88,0x0a,0xc4,0x52,0xe5,0x74,0x2e,0xd1,0xe8,0xd1,0xa8,0x2a,0xfa,0x86,0xe5,0x90,0xc7,0x58,0xc1,0x5a,0xe4,0xd2,0x84,0x0d,0x92,0xbc,0xa1,0xa5,0x09,0x0f,0x40,0x49,0x65,0x97,0xfc,0xa7,0xd8,0xb9,0x51,0x3f,0x1a,0x1b,0xda,0x6e,0x95,0x0a,0xaa,0x98,0xde,0x46,0x75,0x07,0xd4,0xa4,0xf5,0xa4,0xf0,0x59,0x92,0x16,0x58,0x2c,0x35,0x72,0xf6,0x2e,0xda,0x89,0x05,0xab,0x35,0x81,0x67,0x0c,0x4a,0x02,0x77,0x7a,0x33,0xe0,0xca,0x72,0x95,0xfd,0x8f,0x4f,0xf6,0xd1,0xa0,0xa3,0xa7,0x68,0x3d,0x65,0xf5,0xf5,0xf7,0xfc,0x60,0xda,0x02,0x3e,0x82,0x6c,0x5f,0x92,0x14,0x4c,0x02,0xf7,0xd1,0xba,0x10,0x75,0x98,0x75,0x53,0xea,0x93,0x67,0xfc,0xd7,0x6d,0x99,0x0b,0x7f,0xa9,0x9c,0xd4,0x5a,0xfd,0xb8,0x83,0x6d,0x43,0xe4,0x59,0xf5,0x18,0x7d,0xf0,0x58,0x47,0x97,0x09,0xa0,0x1e,0xa6,0x83,0x59,0x35,0xfa,0x70,0x46,0x09,0x90,0xcd,0x3d,0xc1,0xba,0x40,0x1b,0xa9,0x4b,0xab,0x1d,0xde,0x41,0xac,0x67,0xab,0x33,0x19,0xdc,0xac,0xa0,0x60,0x48,0xd4,0xc4,0xee,0xf2,0x7e,0xe1,0x3a,0x9c,0x17,0xd0,0x53,0x8f,0x43,0x0f,0x2d,0x64,0x2d,0xc2,0x41,0x56,0x60,0xde,0x78,0x87,0x7d,0x8d,0x8a,0xbc,0x72,0x52,0x39,0x78,0xc0,0x42,0xe4,0x28,0x5f,0x43,0x19,0x84,0x6c,0x44,0x12,0x62,0x42,0x97,0x68,0x44,0xc1,0x0e,0x55,0x6b,0xa2,0x15,0xb5,0xa7,0x19,0xe5,0x9d,0x0c,0x6b,0x2a,0x96,0xd3,0x98,0x59,0x07,0x1f,0xdc,0xc2,0xcd,0xe7,0x52,0x4a,0x7b,0xed,0xae,0x54,0xe8,0x5b,0x31,0x8e,0x85,0x4e,0x8f,0xe2,0xb2,0xf3,0xed,0xfa,0xc9,0x71,0x91,0x28,0x27,0x0a,0xaf,0xd1,0xe5,0x04,0x4c,0x3a,0x4f,0xda,0xfd,0x9f,0xf3,0x1f,0x90,0x78,0x4b,0x8e,0x8e,0x45,0x96,0x14,0x4a,0x0d,0xaf,0x58,0x65,0x11,0xd3,0xd9,0x96,0x2b,0x9e,0xa9,0x5a,0xf1,0x97,0xb4,0xe5,0xfc,0x60,0xf2,0xb1,0xed,0x15,0xde,0x3a,0x5b,0xef,0x5f,0x89,0xbd,0xc7,0x9d,0x91,0x05,0x1d,0x9b,0x28,0x16,0xe7,0x4f,0xa5,0x45,0x31,0xef,0xdc,0x1c,0xbe,0x74,0xd4,0x48,0x85,0x7f,0x47,0x6b,0xcd,0x58,0xf2,0x1c,0x0b,0x65,0x3b,0x3b,0x76,0xa4,0xe0,0x76,0xa6,0x55,0x9a,0x30,0x27,0x18,0x55,0x5c,0xc6,0x3f,0x74,0x85,0x9a,0xab,0xab,0x92,0x5f,0x02,0x38,0x61,0xca,0x8c,0xd0,0xf7,0xba,0xdb,0x28,0x71,0xf6,0x7d,0x55,0x32,0x6d,0x74,0x51,0x13,0x5a,0xd4,0x5f,0x4a,0x1b,0xa6,0x91,0x18,0xfb,0xb2,0xc8,0xa3,0x0e,0xec,0x93,0x92,0xef,0x3f,0x97,0x70,0x66,0xc9,0xad,0xd5,0xc7,0x10,0xcc,0x64,0x7b,0x15,0x14,0xd2,0x17,0xd9,0x58,0xc7,0x01,0x7c,0x3e,0x90,0xfd,0x20,0xc0,0x4e,0x67,0x4b,0x90,0x48,0x6e,0x93,0x70,0xa3,0x1a,0x00,0x1d,0x32,0xf4,0x73,0x97,0x9e,0x49,0x06,0x74,0x9e,0x7e,0x47,0x7f,0xa0,0xb7,0x45,0x08,0xf8,0xa5,0xf2,0x37,0x83,0x12,0xb8,0x3c,0x25,0xbd,0x38,0x8c,0xa0,0xb0,0xff,0xf7,0x47,0x8b,0xaf,0x42,0xb7,0x16,0x67,0xed,0xaa,0xc9,0x7c,0x46,0xb1,0x29,0x64,0x3e,0x58,0x6e,0x5b,0x05,0x5a,0x0c,0x21,0x19,0x46,0xd4,0xf3,0x6e,0x67,0x5b,0xed,0x58,0x60,0xfa,0x04,0x2a,0x31,0x5d,0x98,0x26,0x16,0x4d,0x6a,0x92,0x37,0xc3,0x5a,0x5f,0xbf,0x49,0x54,0x90,0xa5,0xbd,0x4d,0xf2,0x48,0xb9,0x5c,0x4a,0xae,0x77,0x84,0xb6,0x05,0x67,0x31,0x66,0xac,0x42,0x45,0xb5,0xb4,0xb0,0x82,0xa0,0x9e,0x93,0x23,0xe6,0x2f,0x20,0x78,0xc5,0xb7,0x67,0x83,0x44,0x6d,0xef,0xd7,0x36,0xad,0x3a,0x37,0x02,0xd4,0x9b,0x08,0x98,0x44,0x90,0x0a,0x61,0x83,0x33,0x97,0xbc,0x44,0x19,0xb3,0x0d,0x7a,0x97,0xa0,0xb3,0x87,0xc1,0x91,0x14,0x74,0xc4,0xd4,0x1b,0x53,0xe3,0x2a,0x97,0x7a,0xcb,0x6f,0x0e,0xa7,0x5d,0xb6,0x5b,0xb3,0x9e,0x59,0xe7,0x01,0xe7,0x69,0x57,0xde,0xf6,0xf2,0xd4,0x45,0x59,0xc3,0x1a,0x77,0x12,0x2b,0x52,0x04,0xe3,0xb5,0xc2,0x19,0xf1,0x68,0x8b,0x14,0xed,0x0b,0xc0,0xb8,0x01,0xb3,0xe6,0xe8,0x2d,0xcd,0x43,0xe9,0xc0,0xe9,0xf4,0x17,0x44,0xcd,0x98,0x15,0xbd,0x1b,0xc8,0x82,0x0d,0x8b,0xb1,0x23,0xf0,0x4f,0xac,0xd1,0xb1,0xb6,0x85,0xdd,0x5a,0x2b,0x1b,0x8d,0xbb,0xf3,0xed,0x93,0x36,0x70,0xf0,0x95,0xa1,0x80,0xb4,0xf1,0x92,0xd0,0x8b,0x10,0xb8,0xfa,0xbb,0xdf,0xcc,0x2b,0x24,0x51,0x8e,0x32,0xee,0xa0,0xa5,0xe0,0xc9,0x04,0xca,0x84,0x47,0x80,0x08,0x3f,0x3b,0x0c,0xd2,0xd0,0xb8,0xb6,0xaf,0x67,0xbc,0x35,0x5b,0x94,0x94,0x02,0x5d,0xc7,0xb0,0xa7,0x8f,0xa8,0x0e,0x3a,0x2d,0xbf,0xeb,0x51,0x32,0x88,0x51,0xd6,0x07,0x81,0x98,0xe9,0x49,0x36,0x51,0xae,0x78,0x7e,0xc0,0x25,0x1f,0x92,0x2b,0xa3,0x0e,0x9f,0x51,0xdf,0x62,0xa6,0xd7,0x27,0x84,0xcf,0x3d,0xd2,0x05,0x39,0x31,0x76,0xdf,0xa3,0x24,0xa5,0x12,0xbd,0x94,0x97,0x0a,0x36,0xdd,0x34,0xa5,0x14,0xa8,0x67,0x91,0xf0,0xeb,0x36,0xf0,0x14,0x5b,0x09,0xab,0x64,0x65,0x1b,0x4a,0x03,0x13,0xb2,0x99,0x61,0x1a,0x2a,0x1c,0x48,0x89,0x16,0x27,0x59,0x87,0x68,0xa3,0x11,0x40,0x60,0xba,0x44,0x43,0x48,0x6d,0xf5,0x15,0x22,0xa1,0xce,0x88,0xb3,0x09,0x85,0xc2,0x16,0xf8,0xe6,0xed,0x17,0x8d,0xd5,0x67,0xb3,0x04,0xa0,0xd4,0xca,0xfb,0xa8,0x82,0xa2,0x83,0x42,0xf1,0x7a,0x9a,0xa2,0x6a,0xe5,0x8d,0xb6,0x30,0x08,0x3d,0x2c,0x35,0x8f,0xdf,0x56,0x6c,0x3f,0x5d,0x62,0xa4,0x28,0x56,0x7b,0xc9,0xea,0x8c,0xe9,0x5c,0xaa,0x0f,0x35,0x47,0x4b,0x0b,0xfa,0x8f,0x33,0x9a,0x25,0x0a,0xb4,0xdf,0xcf,0x20,0x83,0xbe,0x8e,0xef,0xbc,0x10,0x55,0xe1,0x8f,0xe1,0x53,0x70,0xee,0xcb,0x26,0x05,0x66,0xd8,0x3f,0xf0,0x6b,0x21,0x1a,0xae,0xc4,0x3c,0xa2,0x9b,0x54,0xcc,0xd0,0x0f,0x88,0x15,0xa2,0x46,0x5e,0xf0,0xb4,0x65,0x15,0xcc,0x7e,0x41,0xf3,0x12,0x4f,0x09,0xef,0xff,0x73,0x93,0x09,0xab,0x58,0xb2,0x9a,0x14,0x59,0xa0,0x0b,0xce,0x50,0x38,0xe9,0x38,0xc9,0x67,0x8f,0x72,0xeb,0x0e,0x4e,0xe5,0xfd,0xaa,0xe6,0x6d,0x9f,0x85,0x73,0xfc,0x97,0xfc,0x42,0xb4,0x95,0x9f,0x4b,0xf8,0xb6,0x1d,0x78,0x43,0x3e,0x86,0xb0,0x33,0x5d,0x6e,0x91,0x91,0xc4,0xd8,0xbf,0x48,0x7b,0x39,0x05,0xc1,0x08,0xcf,0xd6,0xac,0x24,0xb0,0xce,0xb7,0xdc,0xb7,0xcf,0x51,0xf8,0x4d,0x0e,0xd6,0x87,0xb9,0x5e,0xae,0xb1,0xc5,0x33,0xc0,0x6f,0x0d,0x97,0x02,0x3d,0x92,0xa7,0x08,0x25,0x83,0x7b,0x59,0xba,0x6c,0xb7,0xd4,0xe5,0x6b,0x0a,0x87,0xc2,0x03,0x86,0x2a,0xe8,0xf3,0x15,0xba,0x59,0x25,0xe8,0xed,0xef,0xa6,0x79,0x36,0x9a,0x22,0x02,0x76,0x61,0x51,0xf1,0x6a,0x96,0x5f,0x9f,0x81,0xec,0xe7,0x6c,0xc0,0x70,0xb5,0x58,0x69,0xe4,0xdb,0x97,0x84,0xcf,0x05,0xc8,0x30,0xb3,0x24,0x2c,0x83,0x12]).unwrap(); - let sig: [u8; MLDSA44_SIG_LEN] = [0x5e,0x93,0xb7,0x85,0xc5,0x11,0x9c,0x39,0x83,0xa2,0x91,0xb1,0x84,0x20,0xfd,0xbe,0x4b,0xca,0x53,0xd5,0xa3,0x73,0x29,0x22,0xfa,0xaa,0xcd,0x5a,0x5d,0x32,0xa7,0x45,0xc7,0x8d,0x10,0x5b,0xa1,0x0b,0xee,0x1e,0xd8,0x06,0x9f,0x19,0xe6,0xc5,0x37,0xbd,0xa1,0x6e,0x89,0xd3,0x90,0x04,0xc3,0x59,0xd1,0xfd,0x38,0x1a,0x02,0x91,0xf1,0xc5,0x1f,0x1c,0x38,0xed,0xcd,0xb3,0x15,0xc8,0xc6,0x95,0x70,0xd8,0xf2,0x5f,0x16,0x55,0xba,0x8e,0xa8,0x3a,0xff,0x24,0xb8,0xb6,0xbe,0x8d,0xe7,0x62,0x34,0x2e,0x34,0x7e,0xab,0x2c,0xaa,0x68,0x03,0xed,0x70,0x59,0x52,0xdd,0x64,0x50,0xc5,0x18,0x5e,0x9d,0x60,0xce,0x96,0xe8,0xdc,0xa4,0x23,0xa0,0x2f,0x64,0x6c,0xea,0x69,0x01,0x64,0xa2,0x26,0xe4,0xc3,0xd6,0xa5,0x15,0xce,0x16,0x29,0x0f,0x19,0xb2,0xc6,0x26,0xda,0x9b,0x45,0x0e,0xcf,0x66,0x50,0x13,0xc5,0xe2,0x26,0xb6,0xc0,0xac,0x5c,0x07,0xce,0x90,0xe2,0x78,0xf1,0xb0,0x13,0x4e,0x38,0x5d,0x13,0xe7,0x42,0x08,0xa0,0xb3,0xff,0x05,0x2a,0x36,0x25,0x79,0xf9,0x20,0x7e,0xa0,0x1f,0x18,0xa0,0x39,0xaa,0x1b,0x97,0xae,0x34,0x52,0x67,0x5b,0x62,0x07,0x71,0xf8,0x01,0x2e,0xe7,0xa4,0xe5,0x5c,0x98,0xbf,0xd2,0x01,0x9e,0xd8,0xa3,0xb0,0x0a,0xce,0xa8,0xe8,0xab,0x28,0x17,0x2f,0xaa,0x42,0xca,0x1f,0xda,0x83,0xc5,0xff,0xe8,0x1a,0x45,0xbe,0x73,0x6b,0xde,0xdd,0x5f,0xb3,0x00,0xce,0x17,0x07,0x8b,0x38,0x0f,0x62,0x0b,0xde,0xeb,0xad,0x69,0x36,0x01,0x37,0x2c,0x85,0xea,0xcf,0x79,0xbc,0x98,0xe1,0xb4,0x8f,0x2a,0xd7,0xe5,0xdc,0xe4,0x27,0x9a,0x12,0x95,0xbb,0x2b,0xa6,0x0a,0x0c,0x5e,0x37,0x26,0x64,0x2d,0x23,0x36,0xc5,0xeb,0x1d,0x37,0xc8,0x62,0x3c,0x75,0x58,0x24,0x13,0x18,0xd8,0x9b,0xc7,0x83,0xc4,0xf0,0x00,0x98,0x07,0x74,0x84,0x62,0x3c,0x21,0x75,0x60,0xa0,0xc7,0xaa,0xf7,0x5d,0xca,0xcc,0xb7,0x8e,0xe6,0x9c,0x20,0x7c,0x27,0xc8,0xbf,0x39,0x65,0xcc,0xf5,0x8a,0x80,0xc8,0x8e,0xfc,0xc7,0xe5,0xde,0xb3,0x61,0x5d,0x50,0x45,0xa7,0x41,0xc4,0xda,0xc0,0xa0,0x21,0xdd,0x06,0x0d,0x31,0x5d,0x4e,0xc2,0x85,0x7e,0xb6,0x64,0xd7,0x28,0xd0,0xaf,0x97,0x3b,0xea,0x07,0xe1,0xca,0x56,0x3f,0xaa,0x0e,0x19,0x99,0x6c,0xea,0x37,0x70,0x31,0x6c,0x11,0xa5,0x06,0x66,0x65,0x66,0x20,0x05,0xac,0xe9,0x8f,0x61,0x10,0xe8,0x83,0xba,0xe0,0x60,0xda,0xa7,0xb6,0xd8,0x33,0x79,0xe0,0x87,0x87,0x96,0x69,0x17,0x08,0xa3,0x2b,0x85,0x73,0x0d,0xe8,0xb9,0x2d,0x89,0xf9,0x0a,0x36,0x60,0xc9,0x49,0x16,0x5b,0x14,0x61,0x25,0x67,0x66,0x2e,0x16,0x22,0x32,0x29,0x6c,0xbd,0x14,0x35,0x17,0xa2,0x82,0xe2,0x2c,0x46,0xb6,0x36,0x06,0xd3,0xc1,0x4e,0xd4,0x55,0x9a,0x5a,0x1c,0x45,0x9b,0xab,0x7f,0x35,0x50,0x07,0xad,0x6f,0x7e,0x3b,0x1e,0x07,0x44,0x5d,0xfc,0x96,0xbd,0x9b,0x75,0x08,0x0b,0x3d,0x4f,0x68,0x99,0x84,0x90,0xa2,0x6b,0x5e,0x09,0x0b,0xe2,0x67,0x40,0x71,0xab,0x92,0x5b,0xb6,0x50,0x59,0x08,0x56,0xc5,0x9f,0x8b,0xa7,0x48,0x8d,0x2b,0x72,0xf8,0x40,0xac,0x3e,0xaf,0xe4,0xdd,0x91,0xf0,0xf5,0x1c,0x43,0x64,0x11,0x2c,0x1a,0x13,0x9e,0x3e,0x94,0x2a,0x59,0x7b,0x93,0xa1,0xe3,0xf4,0xfa,0xde,0xd1,0x29,0xc1,0x4b,0x59,0x78,0xb3,0x15,0xe2,0x24,0x6a,0x93,0x14,0x6a,0x79,0x36,0x5f,0x0f,0x59,0x7a,0x18,0x34,0x0c,0xca,0x86,0xbb,0x15,0xce,0xed,0x39,0xf1,0x75,0xea,0xb1,0xe5,0x46,0x53,0x5a,0xfb,0x96,0x6f,0x0a,0x65,0xa8,0xf6,0x6f,0x73,0x7a,0xb0,0x28,0x97,0xed,0xdf,0xe9,0x2c,0xf7,0x78,0x68,0x94,0x84,0x3c,0x26,0x91,0x46,0x47,0x76,0xc9,0x4b,0xd4,0x50,0xa1,0x06,0x91,0x38,0xb2,0x6d,0xf8,0x3b,0x2d,0x1d,0xd8,0x01,0x14,0x3a,0x8f,0xdf,0xdc,0x25,0x14,0xcc,0x5b,0x58,0x31,0xab,0x53,0xa7,0x5c,0x55,0xef,0x29,0xf4,0x0e,0x7c,0x63,0xd2,0xc7,0x2a,0xbe,0x97,0xe2,0xaf,0x14,0x85,0x3b,0xe4,0x9b,0xe1,0x6f,0x47,0x30,0xa1,0x59,0x97,0x49,0x70,0x95,0x14,0x39,0xe5,0x5c,0x15,0x89,0xd0,0xf4,0xa1,0x62,0xe3,0x51,0x7d,0xf9,0xd7,0xab,0xc9,0x8d,0x8a,0x30,0x72,0x16,0xe7,0xf1,0xcb,0x46,0x27,0xc9,0x17,0x5c,0x0e,0xef,0x23,0x33,0x7e,0x56,0xd5,0x28,0x1b,0x83,0x72,0x6f,0xff,0x40,0xa1,0x48,0xb0,0xc4,0x8e,0x8d,0xf3,0x49,0x6a,0x21,0x18,0xd8,0x02,0x19,0xae,0xf8,0xf4,0x0b,0x29,0xfb,0xa1,0xf2,0xf7,0x87,0x86,0xb6,0x7f,0xfb,0x7b,0x7d,0x47,0xd4,0x06,0xb7,0x65,0xbd,0x13,0x66,0x10,0xbe,0xde,0xb9,0x5c,0xd7,0x32,0x1f,0x58,0xf3,0xb8,0x36,0xc9,0x25,0x8b,0xe3,0x5d,0x78,0xb4,0x98,0xf3,0xef,0xe1,0xdb,0x2b,0x24,0x3d,0x73,0x4f,0xab,0x15,0x9b,0xae,0xd8,0x80,0x7c,0x3c,0xcc,0xf8,0x3e,0xb2,0xea,0xf8,0xa9,0xaf,0x01,0xa5,0x18,0xd4,0x8c,0x60,0xe9,0x1a,0x96,0x81,0x2a,0xd6,0x89,0xc2,0xd8,0x3c,0xc4,0xe8,0xe9,0xb3,0x65,0x04,0x22,0xbe,0xd6,0xf1,0x3c,0x24,0xad,0xaa,0xd9,0x1c,0x95,0xb3,0xe3,0xcf,0x35,0x4f,0x0f,0x6b,0xc9,0xee,0x89,0x41,0xa6,0xb1,0x5b,0x69,0x75,0x13,0x1d,0x95,0x23,0x3d,0x89,0x35,0xde,0x36,0x7e,0xfc,0x6d,0x86,0xa4,0x5d,0xac,0x7d,0x0f,0x1d,0xdd,0x9a,0xeb,0xd2,0xc5,0x9c,0x02,0x7f,0xcd,0xa4,0x48,0x80,0x1e,0x93,0xe7,0x33,0xac,0xa5,0x18,0x74,0xbe,0x9a,0xb9,0x27,0xa9,0x04,0xf9,0x6d,0xdb,0x7a,0x46,0xb2,0xda,0x13,0x26,0x1d,0x52,0x2b,0x23,0xc9,0x50,0xc0,0x1d,0x5f,0x5e,0x11,0x2b,0x76,0xf8,0x51,0xff,0x23,0x4f,0x06,0xf8,0xd5,0xe6,0x5b,0x13,0x19,0xab,0xcd,0x79,0xa1,0x80,0xae,0x06,0x3d,0x65,0xb2,0x8c,0x74,0x58,0x78,0xc0,0x6d,0xbb,0x69,0xba,0x73,0x29,0x3e,0xab,0x34,0x43,0x4b,0xf1,0xa9,0x2f,0xba,0x69,0x19,0x93,0xbd,0x0f,0xf3,0xed,0xac,0x76,0xa1,0x2f,0x80,0xc0,0xad,0xa4,0xb1,0x96,0x9c,0x76,0x65,0x58,0x9d,0x53,0x0a,0x67,0x01,0x6a,0x62,0x54,0x03,0xc5,0x37,0x03,0x29,0x04,0xf2,0xe1,0x04,0x54,0x7c,0xd3,0xea,0x40,0x62,0x60,0xdd,0x35,0x7f,0xa0,0x6e,0xa0,0x12,0xa7,0x85,0x82,0x6c,0x16,0x0e,0x99,0xff,0xd0,0x65,0xb0,0xe3,0xf3,0x3c,0x76,0x89,0xd3,0x55,0x2a,0xb9,0xe2,0xe0,0x9f,0xa7,0xe5,0x5b,0xbc,0xef,0x04,0x22,0x42,0xbc,0xac,0xad,0x8a,0x3d,0xa4,0x7b,0xcc,0x54,0xa1,0x21,0xf1,0x52,0x6c,0x8c,0xd4,0xcc,0x5a,0x89,0x2a,0x81,0x31,0xcf,0x4e,0xef,0xaf,0x42,0x48,0xdd,0xd6,0xa1,0x1e,0xc4,0x27,0xba,0x37,0x8a,0xae,0x89,0xaa,0xf5,0x82,0xce,0x1f,0x4e,0x32,0x69,0x0a,0x55,0x5e,0x74,0x07,0x61,0xd3,0x58,0xad,0x4e,0x92,0xbc,0x38,0x41,0x8a,0xa7,0x82,0xda,0x91,0x65,0x24,0xfb,0x09,0xab,0x2c,0xa6,0xb3,0xd3,0x11,0x3d,0x6f,0x2c,0x2a,0x6a,0x9b,0x9d,0x29,0xd4,0xe7,0x48,0x92,0x55,0x25,0x2a,0xf0,0x75,0xcb,0xf9,0xfe,0xac,0xed,0xae,0x6f,0x3e,0xc0,0xb0,0x70,0x82,0x46,0x89,0xdd,0x3c,0x78,0xac,0x14,0x3e,0xd6,0x77,0x6d,0x95,0xdd,0x8f,0x13,0xd4,0x35,0xa2,0x90,0xbd,0xca,0x4c,0x11,0x31,0x8e,0x5a,0xcc,0xe0,0x44,0x69,0x64,0x4e,0x13,0x74,0xa9,0x45,0x1b,0x62,0x04,0xf3,0xb3,0x96,0x1b,0x7d,0xd2,0x39,0xe3,0x06,0xfe,0xf5,0xf4,0xf4,0xe5,0x1b,0x78,0xb0,0xfb,0x9d,0xce,0xe6,0x9c,0x3e,0x79,0x0b,0x23,0x1f,0x2e,0x65,0xfd,0x1a,0xb1,0xc2,0xa7,0x5b,0x07,0x06,0x7d,0x5c,0x16,0xdd,0xe0,0x09,0x83,0xa5,0x8f,0xfc,0xda,0xaa,0xee,0x16,0xd2,0x74,0x2e,0x13,0x3e,0xd7,0x37,0xb4,0x80,0x64,0xc8,0xa3,0x8e,0xca,0x35,0xab,0x3f,0xa1,0x8f,0x6d,0x62,0xf6,0x42,0xb1,0x2c,0xfd,0xc7,0x98,0x0f,0x2a,0xb7,0xdb,0x32,0x1f,0xec,0x9d,0xcf,0xe4,0x99,0xb4,0xfc,0x1e,0xe7,0xeb,0x29,0x79,0x54,0x05,0x66,0x17,0xc6,0x0a,0x66,0x40,0xb9,0x28,0x35,0xd1,0x65,0xc3,0xc0,0x0a,0x95,0x19,0x52,0x61,0x44,0x88,0xd5,0x65,0x7b,0xa0,0xb5,0xe9,0x0a,0xe9,0xe0,0xef,0x7b,0x3b,0x9e,0xca,0xeb,0xd8,0x1b,0x85,0x51,0xb6,0xd7,0x0e,0x83,0x5b,0x27,0x34,0x76,0x16,0x39,0xd4,0x2e,0x76,0xff,0xc5,0xb3,0x27,0x2b,0x61,0xc8,0x96,0xb4,0x5b,0x4b,0xd1,0x8f,0x30,0xe5,0x8c,0x44,0x06,0x43,0xba,0x15,0x92,0x21,0xcc,0x67,0x39,0xa1,0x9a,0x65,0xf2,0x91,0x1f,0xae,0x47,0xb0,0xd4,0xca,0xc4,0x20,0x0a,0x6f,0x04,0x3b,0x17,0xa0,0x3a,0xd3,0x93,0xec,0xb8,0x23,0xed,0x03,0xc8,0xb6,0xcd,0x68,0x16,0x7e,0x6c,0x82,0x34,0xf7,0x43,0x25,0x57,0xdb,0x27,0x20,0x79,0xee,0x89,0x9a,0xed,0xe7,0x3b,0x6b,0x98,0xd6,0x00,0x3f,0x45,0x78,0x9a,0x14,0x1b,0x60,0xd6,0xdb,0x40,0xcd,0x2a,0x59,0x74,0x57,0x1a,0x4a,0xd3,0x66,0x7b,0x88,0x93,0x18,0xba,0x60,0x28,0x5d,0x90,0x3a,0x2e,0xac,0x01,0xc2,0x16,0x08,0x83,0x8c,0x40,0x90,0x7d,0xe6,0xbb,0xab,0xe0,0x42,0xcf,0x2e,0xcd,0xd9,0x7f,0x54,0x9f,0x95,0xec,0x69,0x8d,0x79,0x22,0x2c,0x65,0xba,0x27,0xc3,0x0d,0x33,0x2a,0x68,0xd0,0x57,0xae,0xcd,0xc9,0x38,0x8a,0xa3,0x43,0x20,0xe0,0xaa,0x74,0xfd,0xbd,0x4d,0x1b,0x64,0x3c,0xac,0xe2,0x16,0xb6,0xd8,0xad,0x8f,0x07,0xa9,0x99,0x55,0xbf,0xdb,0x74,0x3a,0x86,0xb4,0x0f,0xc6,0x15,0x27,0xba,0xca,0x43,0x4a,0xc2,0xa7,0xfb,0xea,0xa7,0x71,0x11,0xdc,0x80,0x98,0xb1,0x7e,0x80,0x0f,0x59,0xdd,0x77,0xcc,0xb0,0xe6,0x77,0x07,0xe6,0x01,0x23,0xd3,0x34,0xe0,0x73,0xa2,0xf5,0xa1,0x6f,0xfb,0xcd,0x70,0x13,0x89,0xad,0xd5,0x7c,0x3c,0xec,0xcb,0x88,0xb2,0x86,0xac,0x1e,0x6e,0x3e,0x64,0x85,0xaf,0x1a,0x12,0xea,0x24,0x1d,0x14,0xa1,0xb5,0x00,0x3d,0x7f,0x3b,0xc9,0xe9,0x57,0xd4,0x48,0x3c,0x0f,0x9f,0x70,0x3b,0x3a,0x18,0x7d,0x55,0xe5,0x05,0x81,0x76,0x15,0xfb,0xc4,0xae,0x08,0x37,0x61,0x61,0x84,0x24,0x5c,0xfb,0xa6,0x1c,0xe3,0xb9,0x29,0xe3,0x3f,0x52,0xb7,0x1c,0xdd,0x7b,0x6a,0x0d,0xa5,0x5c,0x1f,0x99,0x75,0x10,0xb1,0xa9,0x00,0x2c,0xa4,0xe0,0x67,0x83,0x73,0xa3,0xb1,0xab,0x28,0x97,0xe6,0xb4,0x23,0xf1,0x5a,0x44,0x0a,0x63,0x6c,0xc8,0x61,0x49,0x1e,0xf4,0x1a,0xd0,0xaa,0x62,0x7d,0x8e,0x19,0x8a,0x5e,0xe7,0xbd,0x7b,0x6c,0xb2,0xc9,0xce,0x2a,0x8c,0xc0,0x15,0xf0,0xd2,0x06,0xde,0x4c,0x49,0xe2,0xf8,0x7f,0x31,0x09,0x54,0xa1,0x0d,0x86,0xe2,0x94,0xf7,0x42,0xee,0x18,0x6f,0x4a,0xe9,0x81,0x5f,0x69,0x96,0x22,0x79,0x22,0x06,0xca,0xfb,0xa8,0xf5,0x62,0x17,0x38,0x16,0x0e,0x6c,0x5d,0x61,0x1a,0x82,0x52,0xc6,0xf3,0x50,0x85,0xb6,0x04,0xef,0x89,0x51,0x64,0xd4,0xea,0x6d,0xdd,0x31,0x0c,0x7d,0x8f,0x0c,0x87,0x9f,0xb1,0xf8,0x84,0xc5,0x74,0x1d,0x09,0x6b,0x3d,0x2d,0xa0,0xce,0x11,0x51,0x79,0x0d,0xda,0x88,0x1d,0x18,0xcb,0x6b,0x19,0xa9,0xfe,0xd6,0xf5,0x25,0x4b,0x7d,0x52,0xd5,0xd9,0x2b,0xbb,0xe2,0x4c,0x9d,0x6a,0x65,0x60,0x4a,0x0b,0x8e,0xd2,0x4a,0xd5,0xc1,0x97,0xd6,0x83,0xf5,0x98,0x74,0x3c,0x96,0xb5,0x96,0x0e,0x87,0x23,0x73,0x2b,0x5b,0xd6,0x47,0xe9,0xdb,0xea,0xa8,0x51,0xd0,0xe1,0xcf,0x6d,0x2c,0x07,0x0d,0x44,0x42,0x76,0x2c,0x28,0x09,0x8c,0x5c,0xf5,0xa5,0x4b,0x2b,0x5e,0x69,0xa9,0x9b,0x10,0x81,0x5b,0xf0,0xf4,0x77,0xbb,0x71,0xf0,0xd5,0xd3,0xa6,0x2b,0xa2,0xb3,0xe2,0x9b,0xf8,0x4d,0x4b,0x4e,0x57,0x47,0x07,0xf5,0xf7,0x4a,0xf7,0x04,0xd2,0x77,0xbd,0x6c,0xa3,0x8d,0xa2,0x1e,0x2c,0xda,0xc5,0x49,0xe5,0xea,0xe1,0xde,0x7a,0x18,0xee,0x53,0x4c,0x8c,0x22,0x91,0xc9,0x08,0xca,0xab,0xf1,0x59,0xe9,0x0e,0x65,0x49,0xdb,0x94,0xba,0x7a,0x3f,0x3d,0x97,0xdd,0x39,0x8a,0x75,0xdf,0x5b,0x1a,0x7c,0xdf,0xb2,0x54,0x10,0xb7,0xef,0xc4,0xed,0x00,0xd9,0x99,0x5b,0x37,0xb5,0x8b,0xf9,0x1e,0xd7,0xa3,0x51,0x0c,0xff,0xea,0x82,0xf9,0xe1,0xc2,0xa3,0x29,0x04,0x06,0x00,0x4d,0x09,0x05,0x7d,0x63,0xb7,0x70,0xfa,0x0e,0x53,0x10,0x31,0x99,0x54,0x4e,0xba,0x66,0x2a,0x2c,0x30,0x2c,0xf3,0x90,0x08,0xf1,0x42,0xd2,0xb1,0x69,0x63,0xe9,0x5a,0xb1,0x0b,0xe7,0xc2,0x61,0x01,0x68,0x60,0x8f,0x35,0x3a,0x2f,0x2c,0x41,0xc7,0x05,0x6d,0xec,0x1a,0x8c,0x7a,0x6b,0xfa,0x00,0x27,0xf9,0xde,0xda,0xcb,0x77,0x86,0xb6,0x7e,0xa2,0xc4,0x94,0xd4,0x3b,0xa8,0x51,0xcf,0x94,0x15,0xc1,0xbc,0xc5,0x2f,0x02,0x7e,0xc0,0x2c,0x65,0x53,0x4f,0x60,0x8e,0x9d,0x16,0x6d,0x51,0xdd,0x43,0x1c,0xdf,0x58,0x71,0xf5,0xcd,0xd1,0x57,0x9c,0xc0,0x60,0x79,0xdf,0x07,0x5a,0x25,0x06,0x2b,0xa7,0xe7,0x0d,0x96,0x66,0xc4,0xe7,0xfe,0xd3,0x4c,0xea,0x0e,0xa0,0xf1,0x1a,0xde,0x1e,0xb2,0xa9,0xb3,0x97,0xbc,0xaa,0xad,0x10,0x61,0x27,0x0e,0xcf,0x49,0x78,0x03,0xa5,0xfc,0xe7,0xf4,0x1e,0x65,0x04,0xfb,0xec,0x71,0xa7,0xde,0x7d,0x06,0x6b,0x82,0x61,0x86,0x8a,0xfc,0x49,0xb9,0xe6,0x85,0xf0,0xdc,0xce,0x75,0xe2,0xfc,0xb3,0xba,0x8c,0xf1,0x90,0x57,0xe3,0x94,0x15,0x76,0xba,0xf5,0x8f,0xb8,0x21,0xbd,0x42,0x68,0xf7,0xfa,0xe3,0x02,0x86,0x01,0xda,0x02,0x2e,0x9b,0x46,0x86,0x46,0xab,0xdb,0x4f,0xa6,0x09,0x8a,0x44,0x9b,0x42,0x67,0xd5,0x09,0xd9,0xa3,0x3f,0x4c,0x3e,0xbc,0xc3,0x2d,0xac,0x09,0x4d,0x48,0xed,0x60,0x0e,0x76,0x57,0x87,0xfb,0x92,0xb1,0x97,0x4f,0x74,0xf7,0xbb,0x4c,0x66,0xeb,0x2b,0xbd,0x02,0x89,0x5e,0x6a,0x38,0x1c,0x1c,0x45,0x2e,0xaa,0xb1,0xae,0x47,0x31,0xcf,0x63,0x2f,0x61,0xae,0x2c,0x90,0x59,0x21,0x17,0x4a,0x3b,0xc9,0xbb,0x4c,0xdc,0x89,0xd6,0x30,0x26,0x4b,0x61,0x49,0x88,0xf3,0xab,0xbe,0xa1,0xbd,0x61,0x7f,0xfa,0x53,0xd7,0x1b,0x7d,0x8a,0x37,0x14,0x62,0xb7,0x73,0x35,0x1a,0x2d,0xcc,0xae,0xdd,0x7f,0x59,0xcd,0x72,0x8f,0xad,0xee,0x05,0x90,0x67,0xbd,0x80,0xc9,0x4c,0x8c,0x9a,0x1f,0xfc,0xa2,0xdc,0x4f,0x84,0x8b,0x82,0x9c,0x05,0x61,0x38,0x5a,0xa8,0x2c,0xc9,0x85,0x03,0xd0,0xbb,0x66,0xa6,0xaa,0x4f,0xae,0x07,0x03,0xd1,0x2e,0x60,0xe1,0x46,0x0e,0xfb,0xbc,0xdf,0x24,0x12,0xc1,0x3e,0x7c,0x68,0x4d,0x1b,0x01,0x10,0x20,0x26,0x34,0x3a,0x41,0x43,0x44,0x58,0x5f,0x6e,0x70,0x72,0x74,0x8b,0xae,0xb5,0xbb,0xc6,0xd1,0xe2,0xef,0xfb,0xfe,0x06,0x0e,0x2e,0x3e,0x51,0x60,0x79,0x7c,0x9e,0xa6,0xba,0xc7,0xf1,0x10,0x24,0x40,0x4a,0x52,0x57,0x5f,0x6c,0x89,0x8c,0x97,0xaa,0xb2,0xc3,0xcc,0xea,0xf2,0x2f,0x3f,0x53,0x5f,0x7b,0x81,0x83,0x96,0xa1,0xb1,0xbc,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x25,0x36,0x42]; + let mldsa44_pk = MLDSA44PublicKey::from_bytes(&[ + 0xD7, 0xB2, 0xB4, 0x72, 0x54, 0xAA, 0xE0, 0xDB, 0x45, 0xE7, 0x93, 0x0D, 0x4A, 0x98, 0xD2, + 0xC9, 0x7D, 0x8F, 0x13, 0x97, 0xD1, 0x78, 0x9D, 0xAF, 0xA1, 0x70, 0x24, 0xB3, 0x16, 0xE9, + 0xBE, 0xC9, 0x4F, 0xC9, 0x94, 0x6D, 0x42, 0xF1, 0x9B, 0x79, 0xA7, 0x41, 0x3B, 0xBA, 0xA3, + 0x3E, 0x71, 0x49, 0xCB, 0x42, 0xED, 0x51, 0x15, 0x69, 0x3A, 0xC0, 0x41, 0xFA, 0xCB, 0x98, + 0x8A, 0xDE, 0xB5, 0xFE, 0x0E, 0x1D, 0x86, 0x31, 0x18, 0x49, 0x95, 0xB5, 0x92, 0xC3, 0x97, + 0xD2, 0x29, 0x4E, 0x2E, 0x14, 0xF9, 0x0A, 0xA4, 0x14, 0xBA, 0x38, 0x26, 0x89, 0x9A, 0xC4, + 0x3F, 0x4C, 0xCC, 0xAC, 0xBC, 0x26, 0xE9, 0xA8, 0x32, 0xB9, 0x51, 0x18, 0xD5, 0xCB, 0x43, + 0x3C, 0xBE, 0xF9, 0x66, 0x0B, 0x00, 0x13, 0x8E, 0x08, 0x17, 0xF6, 0x1E, 0x76, 0x2C, 0xA2, + 0x74, 0xC3, 0x6A, 0xD5, 0x54, 0xEB, 0x22, 0xAA, 0xC1, 0x16, 0x2E, 0x4A, 0xB0, 0x1A, 0xCB, + 0xA1, 0xE3, 0x8C, 0x4E, 0xFD, 0x8F, 0x80, 0xB6, 0x5B, 0x33, 0x3D, 0x0F, 0x72, 0xE5, 0x5D, + 0xFE, 0x71, 0xCE, 0x9C, 0x1E, 0xBB, 0x98, 0x89, 0xE7, 0xC5, 0x61, 0x06, 0xC0, 0xFD, 0x73, + 0x80, 0x3A, 0x2A, 0xEC, 0xFE, 0xAF, 0xDE, 0xD7, 0xAA, 0x3C, 0xB2, 0xCE, 0xDA, 0x54, 0xD1, + 0x2B, 0xD8, 0xCD, 0x36, 0xA7, 0x8C, 0xF9, 0x75, 0x94, 0x3B, 0x47, 0xAB, 0xD2, 0x5E, 0x88, + 0x0A, 0xC4, 0x52, 0xE5, 0x74, 0x2E, 0xD1, 0xE8, 0xD1, 0xA8, 0x2A, 0xFA, 0x86, 0xE5, 0x90, + 0xC7, 0x58, 0xC1, 0x5A, 0xE4, 0xD2, 0x84, 0x0D, 0x92, 0xBC, 0xA1, 0xA5, 0x09, 0x0F, 0x40, + 0x49, 0x65, 0x97, 0xFC, 0xA7, 0xD8, 0xB9, 0x51, 0x3F, 0x1A, 0x1B, 0xDA, 0x6E, 0x95, 0x0A, + 0xAA, 0x98, 0xDE, 0x46, 0x75, 0x07, 0xD4, 0xA4, 0xF5, 0xA4, 0xF0, 0x59, 0x92, 0x16, 0x58, + 0x2C, 0x35, 0x72, 0xF6, 0x2E, 0xDA, 0x89, 0x05, 0xAB, 0x35, 0x81, 0x67, 0x0C, 0x4A, 0x02, + 0x77, 0x7A, 0x33, 0xE0, 0xCA, 0x72, 0x95, 0xFD, 0x8F, 0x4F, 0xF6, 0xD1, 0xA0, 0xA3, 0xA7, + 0x68, 0x3D, 0x65, 0xF5, 0xF5, 0xF7, 0xFC, 0x60, 0xDA, 0x02, 0x3E, 0x82, 0x6C, 0x5F, 0x92, + 0x14, 0x4C, 0x02, 0xF7, 0xD1, 0xBA, 0x10, 0x75, 0x98, 0x75, 0x53, 0xEA, 0x93, 0x67, 0xFC, + 0xD7, 0x6D, 0x99, 0x0B, 0x7F, 0xA9, 0x9C, 0xD4, 0x5A, 0xFD, 0xB8, 0x83, 0x6D, 0x43, 0xE4, + 0x59, 0xF5, 0x18, 0x7D, 0xF0, 0x58, 0x47, 0x97, 0x09, 0xA0, 0x1E, 0xA6, 0x83, 0x59, 0x35, + 0xFA, 0x70, 0x46, 0x09, 0x90, 0xCD, 0x3D, 0xC1, 0xBA, 0x40, 0x1B, 0xA9, 0x4B, 0xAB, 0x1D, + 0xDE, 0x41, 0xAC, 0x67, 0xAB, 0x33, 0x19, 0xDC, 0xAC, 0xA0, 0x60, 0x48, 0xD4, 0xC4, 0xEE, + 0xF2, 0x7E, 0xE1, 0x3A, 0x9C, 0x17, 0xD0, 0x53, 0x8F, 0x43, 0x0F, 0x2D, 0x64, 0x2D, 0xC2, + 0x41, 0x56, 0x60, 0xDE, 0x78, 0x87, 0x7D, 0x8D, 0x8A, 0xBC, 0x72, 0x52, 0x39, 0x78, 0xC0, + 0x42, 0xE4, 0x28, 0x5F, 0x43, 0x19, 0x84, 0x6C, 0x44, 0x12, 0x62, 0x42, 0x97, 0x68, 0x44, + 0xC1, 0x0E, 0x55, 0x6B, 0xA2, 0x15, 0xB5, 0xA7, 0x19, 0xE5, 0x9D, 0x0C, 0x6B, 0x2A, 0x96, + 0xD3, 0x98, 0x59, 0x07, 0x1F, 0xDC, 0xC2, 0xCD, 0xE7, 0x52, 0x4A, 0x7B, 0xED, 0xAE, 0x54, + 0xE8, 0x5B, 0x31, 0x8E, 0x85, 0x4E, 0x8F, 0xE2, 0xB2, 0xF3, 0xED, 0xFA, 0xC9, 0x71, 0x91, + 0x28, 0x27, 0x0A, 0xAF, 0xD1, 0xE5, 0x04, 0x4C, 0x3A, 0x4F, 0xDA, 0xFD, 0x9F, 0xF3, 0x1F, + 0x90, 0x78, 0x4B, 0x8E, 0x8E, 0x45, 0x96, 0x14, 0x4A, 0x0D, 0xAF, 0x58, 0x65, 0x11, 0xD3, + 0xD9, 0x96, 0x2B, 0x9E, 0xA9, 0x5A, 0xF1, 0x97, 0xB4, 0xE5, 0xFC, 0x60, 0xF2, 0xB1, 0xED, + 0x15, 0xDE, 0x3A, 0x5B, 0xEF, 0x5F, 0x89, 0xBD, 0xC7, 0x9D, 0x91, 0x05, 0x1D, 0x9B, 0x28, + 0x16, 0xE7, 0x4F, 0xA5, 0x45, 0x31, 0xEF, 0xDC, 0x1C, 0xBE, 0x74, 0xD4, 0x48, 0x85, 0x7F, + 0x47, 0x6B, 0xCD, 0x58, 0xF2, 0x1C, 0x0B, 0x65, 0x3B, 0x3B, 0x76, 0xA4, 0xE0, 0x76, 0xA6, + 0x55, 0x9A, 0x30, 0x27, 0x18, 0x55, 0x5C, 0xC6, 0x3F, 0x74, 0x85, 0x9A, 0xAB, 0xAB, 0x92, + 0x5F, 0x02, 0x38, 0x61, 0xCA, 0x8C, 0xD0, 0xF7, 0xBA, 0xDB, 0x28, 0x71, 0xF6, 0x7D, 0x55, + 0x32, 0x6D, 0x74, 0x51, 0x13, 0x5A, 0xD4, 0x5F, 0x4A, 0x1B, 0xA6, 0x91, 0x18, 0xFB, 0xB2, + 0xC8, 0xA3, 0x0E, 0xEC, 0x93, 0x92, 0xEF, 0x3F, 0x97, 0x70, 0x66, 0xC9, 0xAD, 0xD5, 0xC7, + 0x10, 0xCC, 0x64, 0x7B, 0x15, 0x14, 0xD2, 0x17, 0xD9, 0x58, 0xC7, 0x01, 0x7C, 0x3E, 0x90, + 0xFD, 0x20, 0xC0, 0x4E, 0x67, 0x4B, 0x90, 0x48, 0x6E, 0x93, 0x70, 0xA3, 0x1A, 0x00, 0x1D, + 0x32, 0xF4, 0x73, 0x97, 0x9E, 0x49, 0x06, 0x74, 0x9E, 0x7E, 0x47, 0x7F, 0xA0, 0xB7, 0x45, + 0x08, 0xF8, 0xA5, 0xF2, 0x37, 0x83, 0x12, 0xB8, 0x3C, 0x25, 0xBD, 0x38, 0x8C, 0xA0, 0xB0, + 0xFF, 0xF7, 0x47, 0x8B, 0xAF, 0x42, 0xB7, 0x16, 0x67, 0xED, 0xAA, 0xC9, 0x7C, 0x46, 0xB1, + 0x29, 0x64, 0x3E, 0x58, 0x6E, 0x5B, 0x05, 0x5A, 0x0C, 0x21, 0x19, 0x46, 0xD4, 0xF3, 0x6E, + 0x67, 0x5B, 0xED, 0x58, 0x60, 0xFA, 0x04, 0x2A, 0x31, 0x5D, 0x98, 0x26, 0x16, 0x4D, 0x6A, + 0x92, 0x37, 0xC3, 0x5A, 0x5F, 0xBF, 0x49, 0x54, 0x90, 0xA5, 0xBD, 0x4D, 0xF2, 0x48, 0xB9, + 0x5C, 0x4A, 0xAE, 0x77, 0x84, 0xB6, 0x05, 0x67, 0x31, 0x66, 0xAC, 0x42, 0x45, 0xB5, 0xB4, + 0xB0, 0x82, 0xA0, 0x9E, 0x93, 0x23, 0xE6, 0x2F, 0x20, 0x78, 0xC5, 0xB7, 0x67, 0x83, 0x44, + 0x6D, 0xEF, 0xD7, 0x36, 0xAD, 0x3A, 0x37, 0x02, 0xD4, 0x9B, 0x08, 0x98, 0x44, 0x90, 0x0A, + 0x61, 0x83, 0x33, 0x97, 0xBC, 0x44, 0x19, 0xB3, 0x0D, 0x7A, 0x97, 0xA0, 0xB3, 0x87, 0xC1, + 0x91, 0x14, 0x74, 0xC4, 0xD4, 0x1B, 0x53, 0xE3, 0x2A, 0x97, 0x7A, 0xCB, 0x6F, 0x0E, 0xA7, + 0x5D, 0xB6, 0x5B, 0xB3, 0x9E, 0x59, 0xE7, 0x01, 0xE7, 0x69, 0x57, 0xDE, 0xF6, 0xF2, 0xD4, + 0x45, 0x59, 0xC3, 0x1A, 0x77, 0x12, 0x2B, 0x52, 0x04, 0xE3, 0xB5, 0xC2, 0x19, 0xF1, 0x68, + 0x8B, 0x14, 0xED, 0x0B, 0xC0, 0xB8, 0x01, 0xB3, 0xE6, 0xE8, 0x2D, 0xCD, 0x43, 0xE9, 0xC0, + 0xE9, 0xF4, 0x17, 0x44, 0xCD, 0x98, 0x15, 0xBD, 0x1B, 0xC8, 0x82, 0x0D, 0x8B, 0xB1, 0x23, + 0xF0, 0x4F, 0xAC, 0xD1, 0xB1, 0xB6, 0x85, 0xDD, 0x5A, 0x2B, 0x1B, 0x8D, 0xBB, 0xF3, 0xED, + 0x93, 0x36, 0x70, 0xF0, 0x95, 0xA1, 0x80, 0xB4, 0xF1, 0x92, 0xD0, 0x8B, 0x10, 0xB8, 0xFA, + 0xBB, 0xDF, 0xCC, 0x2B, 0x24, 0x51, 0x8E, 0x32, 0xEE, 0xA0, 0xA5, 0xE0, 0xC9, 0x04, 0xCA, + 0x84, 0x47, 0x80, 0x08, 0x3F, 0x3B, 0x0C, 0xD2, 0xD0, 0xB8, 0xB6, 0xAF, 0x67, 0xBC, 0x35, + 0x5B, 0x94, 0x94, 0x02, 0x5D, 0xC7, 0xB0, 0xA7, 0x8F, 0xA8, 0x0E, 0x3A, 0x2D, 0xBF, 0xEB, + 0x51, 0x32, 0x88, 0x51, 0xD6, 0x07, 0x81, 0x98, 0xE9, 0x49, 0x36, 0x51, 0xAE, 0x78, 0x7E, + 0xC0, 0x25, 0x1F, 0x92, 0x2B, 0xA3, 0x0E, 0x9F, 0x51, 0xDF, 0x62, 0xA6, 0xD7, 0x27, 0x84, + 0xCF, 0x3D, 0xD2, 0x05, 0x39, 0x31, 0x76, 0xDF, 0xA3, 0x24, 0xA5, 0x12, 0xBD, 0x94, 0x97, + 0x0A, 0x36, 0xDD, 0x34, 0xA5, 0x14, 0xA8, 0x67, 0x91, 0xF0, 0xEB, 0x36, 0xF0, 0x14, 0x5B, + 0x09, 0xAB, 0x64, 0x65, 0x1B, 0x4A, 0x03, 0x13, 0xB2, 0x99, 0x61, 0x1A, 0x2A, 0x1C, 0x48, + 0x89, 0x16, 0x27, 0x59, 0x87, 0x68, 0xA3, 0x11, 0x40, 0x60, 0xBA, 0x44, 0x43, 0x48, 0x6D, + 0xF5, 0x15, 0x22, 0xA1, 0xCE, 0x88, 0xB3, 0x09, 0x85, 0xC2, 0x16, 0xF8, 0xE6, 0xED, 0x17, + 0x8D, 0xD5, 0x67, 0xB3, 0x04, 0xA0, 0xD4, 0xCA, 0xFB, 0xA8, 0x82, 0xA2, 0x83, 0x42, 0xF1, + 0x7A, 0x9A, 0xA2, 0x6A, 0xE5, 0x8D, 0xB6, 0x30, 0x08, 0x3D, 0x2C, 0x35, 0x8F, 0xDF, 0x56, + 0x6C, 0x3F, 0x5D, 0x62, 0xA4, 0x28, 0x56, 0x7B, 0xC9, 0xEA, 0x8C, 0xE9, 0x5C, 0xAA, 0x0F, + 0x35, 0x47, 0x4B, 0x0B, 0xFA, 0x8F, 0x33, 0x9A, 0x25, 0x0A, 0xB4, 0xDF, 0xCF, 0x20, 0x83, + 0xBE, 0x8E, 0xEF, 0xBC, 0x10, 0x55, 0xE1, 0x8F, 0xE1, 0x53, 0x70, 0xEE, 0xCB, 0x26, 0x05, + 0x66, 0xD8, 0x3F, 0xF0, 0x6B, 0x21, 0x1A, 0xAE, 0xC4, 0x3C, 0xA2, 0x9B, 0x54, 0xCC, 0xD0, + 0x0F, 0x88, 0x15, 0xA2, 0x46, 0x5E, 0xF0, 0xB4, 0x65, 0x15, 0xCC, 0x7E, 0x41, 0xF3, 0x12, + 0x4F, 0x09, 0xEF, 0xFF, 0x73, 0x93, 0x09, 0xAB, 0x58, 0xB2, 0x9A, 0x14, 0x59, 0xA0, 0x0B, + 0xCE, 0x50, 0x38, 0xE9, 0x38, 0xC9, 0x67, 0x8F, 0x72, 0xEB, 0x0E, 0x4E, 0xE5, 0xFD, 0xAA, + 0xE6, 0x6D, 0x9F, 0x85, 0x73, 0xFC, 0x97, 0xFC, 0x42, 0xB4, 0x95, 0x9F, 0x4B, 0xF8, 0xB6, + 0x1D, 0x78, 0x43, 0x3E, 0x86, 0xB0, 0x33, 0x5D, 0x6E, 0x91, 0x91, 0xC4, 0xD8, 0xBF, 0x48, + 0x7B, 0x39, 0x05, 0xC1, 0x08, 0xCF, 0xD6, 0xAC, 0x24, 0xB0, 0xCE, 0xB7, 0xDC, 0xB7, 0xCF, + 0x51, 0xF8, 0x4D, 0x0E, 0xD6, 0x87, 0xB9, 0x5E, 0xAE, 0xB1, 0xC5, 0x33, 0xC0, 0x6F, 0x0D, + 0x97, 0x02, 0x3D, 0x92, 0xA7, 0x08, 0x25, 0x83, 0x7B, 0x59, 0xBA, 0x6C, 0xB7, 0xD4, 0xE5, + 0x6B, 0x0A, 0x87, 0xC2, 0x03, 0x86, 0x2A, 0xE8, 0xF3, 0x15, 0xBA, 0x59, 0x25, 0xE8, 0xED, + 0xEF, 0xA6, 0x79, 0x36, 0x9A, 0x22, 0x02, 0x76, 0x61, 0x51, 0xF1, 0x6A, 0x96, 0x5F, 0x9F, + 0x81, 0xEC, 0xE7, 0x6C, 0xC0, 0x70, 0xB5, 0x58, 0x69, 0xE4, 0xDB, 0x97, 0x84, 0xCF, 0x05, + 0xC8, 0x30, 0xB3, 0x24, 0x2C, 0x83, 0x12, + ]) + .unwrap(); + let sig: [u8; MLDSA44_SIG_LEN] = [ + 0x5E, 0x93, 0xB7, 0x85, 0xC5, 0x11, 0x9C, 0x39, 0x83, 0xA2, 0x91, 0xB1, 0x84, 0x20, 0xFD, + 0xBE, 0x4B, 0xCA, 0x53, 0xD5, 0xA3, 0x73, 0x29, 0x22, 0xFA, 0xAA, 0xCD, 0x5A, 0x5D, 0x32, + 0xA7, 0x45, 0xC7, 0x8D, 0x10, 0x5B, 0xA1, 0x0B, 0xEE, 0x1E, 0xD8, 0x06, 0x9F, 0x19, 0xE6, + 0xC5, 0x37, 0xBD, 0xA1, 0x6E, 0x89, 0xD3, 0x90, 0x04, 0xC3, 0x59, 0xD1, 0xFD, 0x38, 0x1A, + 0x02, 0x91, 0xF1, 0xC5, 0x1F, 0x1C, 0x38, 0xED, 0xCD, 0xB3, 0x15, 0xC8, 0xC6, 0x95, 0x70, + 0xD8, 0xF2, 0x5F, 0x16, 0x55, 0xBA, 0x8E, 0xA8, 0x3A, 0xFF, 0x24, 0xB8, 0xB6, 0xBE, 0x8D, + 0xE7, 0x62, 0x34, 0x2E, 0x34, 0x7E, 0xAB, 0x2C, 0xAA, 0x68, 0x03, 0xED, 0x70, 0x59, 0x52, + 0xDD, 0x64, 0x50, 0xC5, 0x18, 0x5E, 0x9D, 0x60, 0xCE, 0x96, 0xE8, 0xDC, 0xA4, 0x23, 0xA0, + 0x2F, 0x64, 0x6C, 0xEA, 0x69, 0x01, 0x64, 0xA2, 0x26, 0xE4, 0xC3, 0xD6, 0xA5, 0x15, 0xCE, + 0x16, 0x29, 0x0F, 0x19, 0xB2, 0xC6, 0x26, 0xDA, 0x9B, 0x45, 0x0E, 0xCF, 0x66, 0x50, 0x13, + 0xC5, 0xE2, 0x26, 0xB6, 0xC0, 0xAC, 0x5C, 0x07, 0xCE, 0x90, 0xE2, 0x78, 0xF1, 0xB0, 0x13, + 0x4E, 0x38, 0x5D, 0x13, 0xE7, 0x42, 0x08, 0xA0, 0xB3, 0xFF, 0x05, 0x2A, 0x36, 0x25, 0x79, + 0xF9, 0x20, 0x7E, 0xA0, 0x1F, 0x18, 0xA0, 0x39, 0xAA, 0x1B, 0x97, 0xAE, 0x34, 0x52, 0x67, + 0x5B, 0x62, 0x07, 0x71, 0xF8, 0x01, 0x2E, 0xE7, 0xA4, 0xE5, 0x5C, 0x98, 0xBF, 0xD2, 0x01, + 0x9E, 0xD8, 0xA3, 0xB0, 0x0A, 0xCE, 0xA8, 0xE8, 0xAB, 0x28, 0x17, 0x2F, 0xAA, 0x42, 0xCA, + 0x1F, 0xDA, 0x83, 0xC5, 0xFF, 0xE8, 0x1A, 0x45, 0xBE, 0x73, 0x6B, 0xDE, 0xDD, 0x5F, 0xB3, + 0x00, 0xCE, 0x17, 0x07, 0x8B, 0x38, 0x0F, 0x62, 0x0B, 0xDE, 0xEB, 0xAD, 0x69, 0x36, 0x01, + 0x37, 0x2C, 0x85, 0xEA, 0xCF, 0x79, 0xBC, 0x98, 0xE1, 0xB4, 0x8F, 0x2A, 0xD7, 0xE5, 0xDC, + 0xE4, 0x27, 0x9A, 0x12, 0x95, 0xBB, 0x2B, 0xA6, 0x0A, 0x0C, 0x5E, 0x37, 0x26, 0x64, 0x2D, + 0x23, 0x36, 0xC5, 0xEB, 0x1D, 0x37, 0xC8, 0x62, 0x3C, 0x75, 0x58, 0x24, 0x13, 0x18, 0xD8, + 0x9B, 0xC7, 0x83, 0xC4, 0xF0, 0x00, 0x98, 0x07, 0x74, 0x84, 0x62, 0x3C, 0x21, 0x75, 0x60, + 0xA0, 0xC7, 0xAA, 0xF7, 0x5D, 0xCA, 0xCC, 0xB7, 0x8E, 0xE6, 0x9C, 0x20, 0x7C, 0x27, 0xC8, + 0xBF, 0x39, 0x65, 0xCC, 0xF5, 0x8A, 0x80, 0xC8, 0x8E, 0xFC, 0xC7, 0xE5, 0xDE, 0xB3, 0x61, + 0x5D, 0x50, 0x45, 0xA7, 0x41, 0xC4, 0xDA, 0xC0, 0xA0, 0x21, 0xDD, 0x06, 0x0D, 0x31, 0x5D, + 0x4E, 0xC2, 0x85, 0x7E, 0xB6, 0x64, 0xD7, 0x28, 0xD0, 0xAF, 0x97, 0x3B, 0xEA, 0x07, 0xE1, + 0xCA, 0x56, 0x3F, 0xAA, 0x0E, 0x19, 0x99, 0x6C, 0xEA, 0x37, 0x70, 0x31, 0x6C, 0x11, 0xA5, + 0x06, 0x66, 0x65, 0x66, 0x20, 0x05, 0xAC, 0xE9, 0x8F, 0x61, 0x10, 0xE8, 0x83, 0xBA, 0xE0, + 0x60, 0xDA, 0xA7, 0xB6, 0xD8, 0x33, 0x79, 0xE0, 0x87, 0x87, 0x96, 0x69, 0x17, 0x08, 0xA3, + 0x2B, 0x85, 0x73, 0x0D, 0xE8, 0xB9, 0x2D, 0x89, 0xF9, 0x0A, 0x36, 0x60, 0xC9, 0x49, 0x16, + 0x5B, 0x14, 0x61, 0x25, 0x67, 0x66, 0x2E, 0x16, 0x22, 0x32, 0x29, 0x6C, 0xBD, 0x14, 0x35, + 0x17, 0xA2, 0x82, 0xE2, 0x2C, 0x46, 0xB6, 0x36, 0x06, 0xD3, 0xC1, 0x4E, 0xD4, 0x55, 0x9A, + 0x5A, 0x1C, 0x45, 0x9B, 0xAB, 0x7F, 0x35, 0x50, 0x07, 0xAD, 0x6F, 0x7E, 0x3B, 0x1E, 0x07, + 0x44, 0x5D, 0xFC, 0x96, 0xBD, 0x9B, 0x75, 0x08, 0x0B, 0x3D, 0x4F, 0x68, 0x99, 0x84, 0x90, + 0xA2, 0x6B, 0x5E, 0x09, 0x0B, 0xE2, 0x67, 0x40, 0x71, 0xAB, 0x92, 0x5B, 0xB6, 0x50, 0x59, + 0x08, 0x56, 0xC5, 0x9F, 0x8B, 0xA7, 0x48, 0x8D, 0x2B, 0x72, 0xF8, 0x40, 0xAC, 0x3E, 0xAF, + 0xE4, 0xDD, 0x91, 0xF0, 0xF5, 0x1C, 0x43, 0x64, 0x11, 0x2C, 0x1A, 0x13, 0x9E, 0x3E, 0x94, + 0x2A, 0x59, 0x7B, 0x93, 0xA1, 0xE3, 0xF4, 0xFA, 0xDE, 0xD1, 0x29, 0xC1, 0x4B, 0x59, 0x78, + 0xB3, 0x15, 0xE2, 0x24, 0x6A, 0x93, 0x14, 0x6A, 0x79, 0x36, 0x5F, 0x0F, 0x59, 0x7A, 0x18, + 0x34, 0x0C, 0xCA, 0x86, 0xBB, 0x15, 0xCE, 0xED, 0x39, 0xF1, 0x75, 0xEA, 0xB1, 0xE5, 0x46, + 0x53, 0x5A, 0xFB, 0x96, 0x6F, 0x0A, 0x65, 0xA8, 0xF6, 0x6F, 0x73, 0x7A, 0xB0, 0x28, 0x97, + 0xED, 0xDF, 0xE9, 0x2C, 0xF7, 0x78, 0x68, 0x94, 0x84, 0x3C, 0x26, 0x91, 0x46, 0x47, 0x76, + 0xC9, 0x4B, 0xD4, 0x50, 0xA1, 0x06, 0x91, 0x38, 0xB2, 0x6D, 0xF8, 0x3B, 0x2D, 0x1D, 0xD8, + 0x01, 0x14, 0x3A, 0x8F, 0xDF, 0xDC, 0x25, 0x14, 0xCC, 0x5B, 0x58, 0x31, 0xAB, 0x53, 0xA7, + 0x5C, 0x55, 0xEF, 0x29, 0xF4, 0x0E, 0x7C, 0x63, 0xD2, 0xC7, 0x2A, 0xBE, 0x97, 0xE2, 0xAF, + 0x14, 0x85, 0x3B, 0xE4, 0x9B, 0xE1, 0x6F, 0x47, 0x30, 0xA1, 0x59, 0x97, 0x49, 0x70, 0x95, + 0x14, 0x39, 0xE5, 0x5C, 0x15, 0x89, 0xD0, 0xF4, 0xA1, 0x62, 0xE3, 0x51, 0x7D, 0xF9, 0xD7, + 0xAB, 0xC9, 0x8D, 0x8A, 0x30, 0x72, 0x16, 0xE7, 0xF1, 0xCB, 0x46, 0x27, 0xC9, 0x17, 0x5C, + 0x0E, 0xEF, 0x23, 0x33, 0x7E, 0x56, 0xD5, 0x28, 0x1B, 0x83, 0x72, 0x6F, 0xFF, 0x40, 0xA1, + 0x48, 0xB0, 0xC4, 0x8E, 0x8D, 0xF3, 0x49, 0x6A, 0x21, 0x18, 0xD8, 0x02, 0x19, 0xAE, 0xF8, + 0xF4, 0x0B, 0x29, 0xFB, 0xA1, 0xF2, 0xF7, 0x87, 0x86, 0xB6, 0x7F, 0xFB, 0x7B, 0x7D, 0x47, + 0xD4, 0x06, 0xB7, 0x65, 0xBD, 0x13, 0x66, 0x10, 0xBE, 0xDE, 0xB9, 0x5C, 0xD7, 0x32, 0x1F, + 0x58, 0xF3, 0xB8, 0x36, 0xC9, 0x25, 0x8B, 0xE3, 0x5D, 0x78, 0xB4, 0x98, 0xF3, 0xEF, 0xE1, + 0xDB, 0x2B, 0x24, 0x3D, 0x73, 0x4F, 0xAB, 0x15, 0x9B, 0xAE, 0xD8, 0x80, 0x7C, 0x3C, 0xCC, + 0xF8, 0x3E, 0xB2, 0xEA, 0xF8, 0xA9, 0xAF, 0x01, 0xA5, 0x18, 0xD4, 0x8C, 0x60, 0xE9, 0x1A, + 0x96, 0x81, 0x2A, 0xD6, 0x89, 0xC2, 0xD8, 0x3C, 0xC4, 0xE8, 0xE9, 0xB3, 0x65, 0x04, 0x22, + 0xBE, 0xD6, 0xF1, 0x3C, 0x24, 0xAD, 0xAA, 0xD9, 0x1C, 0x95, 0xB3, 0xE3, 0xCF, 0x35, 0x4F, + 0x0F, 0x6B, 0xC9, 0xEE, 0x89, 0x41, 0xA6, 0xB1, 0x5B, 0x69, 0x75, 0x13, 0x1D, 0x95, 0x23, + 0x3D, 0x89, 0x35, 0xDE, 0x36, 0x7E, 0xFC, 0x6D, 0x86, 0xA4, 0x5D, 0xAC, 0x7D, 0x0F, 0x1D, + 0xDD, 0x9A, 0xEB, 0xD2, 0xC5, 0x9C, 0x02, 0x7F, 0xCD, 0xA4, 0x48, 0x80, 0x1E, 0x93, 0xE7, + 0x33, 0xAC, 0xA5, 0x18, 0x74, 0xBE, 0x9A, 0xB9, 0x27, 0xA9, 0x04, 0xF9, 0x6D, 0xDB, 0x7A, + 0x46, 0xB2, 0xDA, 0x13, 0x26, 0x1D, 0x52, 0x2B, 0x23, 0xC9, 0x50, 0xC0, 0x1D, 0x5F, 0x5E, + 0x11, 0x2B, 0x76, 0xF8, 0x51, 0xFF, 0x23, 0x4F, 0x06, 0xF8, 0xD5, 0xE6, 0x5B, 0x13, 0x19, + 0xAB, 0xCD, 0x79, 0xA1, 0x80, 0xAE, 0x06, 0x3D, 0x65, 0xB2, 0x8C, 0x74, 0x58, 0x78, 0xC0, + 0x6D, 0xBB, 0x69, 0xBA, 0x73, 0x29, 0x3E, 0xAB, 0x34, 0x43, 0x4B, 0xF1, 0xA9, 0x2F, 0xBA, + 0x69, 0x19, 0x93, 0xBD, 0x0F, 0xF3, 0xED, 0xAC, 0x76, 0xA1, 0x2F, 0x80, 0xC0, 0xAD, 0xA4, + 0xB1, 0x96, 0x9C, 0x76, 0x65, 0x58, 0x9D, 0x53, 0x0A, 0x67, 0x01, 0x6A, 0x62, 0x54, 0x03, + 0xC5, 0x37, 0x03, 0x29, 0x04, 0xF2, 0xE1, 0x04, 0x54, 0x7C, 0xD3, 0xEA, 0x40, 0x62, 0x60, + 0xDD, 0x35, 0x7F, 0xA0, 0x6E, 0xA0, 0x12, 0xA7, 0x85, 0x82, 0x6C, 0x16, 0x0E, 0x99, 0xFF, + 0xD0, 0x65, 0xB0, 0xE3, 0xF3, 0x3C, 0x76, 0x89, 0xD3, 0x55, 0x2A, 0xB9, 0xE2, 0xE0, 0x9F, + 0xA7, 0xE5, 0x5B, 0xBC, 0xEF, 0x04, 0x22, 0x42, 0xBC, 0xAC, 0xAD, 0x8A, 0x3D, 0xA4, 0x7B, + 0xCC, 0x54, 0xA1, 0x21, 0xF1, 0x52, 0x6C, 0x8C, 0xD4, 0xCC, 0x5A, 0x89, 0x2A, 0x81, 0x31, + 0xCF, 0x4E, 0xEF, 0xAF, 0x42, 0x48, 0xDD, 0xD6, 0xA1, 0x1E, 0xC4, 0x27, 0xBA, 0x37, 0x8A, + 0xAE, 0x89, 0xAA, 0xF5, 0x82, 0xCE, 0x1F, 0x4E, 0x32, 0x69, 0x0A, 0x55, 0x5E, 0x74, 0x07, + 0x61, 0xD3, 0x58, 0xAD, 0x4E, 0x92, 0xBC, 0x38, 0x41, 0x8A, 0xA7, 0x82, 0xDA, 0x91, 0x65, + 0x24, 0xFB, 0x09, 0xAB, 0x2C, 0xA6, 0xB3, 0xD3, 0x11, 0x3D, 0x6F, 0x2C, 0x2A, 0x6A, 0x9B, + 0x9D, 0x29, 0xD4, 0xE7, 0x48, 0x92, 0x55, 0x25, 0x2A, 0xF0, 0x75, 0xCB, 0xF9, 0xFE, 0xAC, + 0xED, 0xAE, 0x6F, 0x3E, 0xC0, 0xB0, 0x70, 0x82, 0x46, 0x89, 0xDD, 0x3C, 0x78, 0xAC, 0x14, + 0x3E, 0xD6, 0x77, 0x6D, 0x95, 0xDD, 0x8F, 0x13, 0xD4, 0x35, 0xA2, 0x90, 0xBD, 0xCA, 0x4C, + 0x11, 0x31, 0x8E, 0x5A, 0xCC, 0xE0, 0x44, 0x69, 0x64, 0x4E, 0x13, 0x74, 0xA9, 0x45, 0x1B, + 0x62, 0x04, 0xF3, 0xB3, 0x96, 0x1B, 0x7D, 0xD2, 0x39, 0xE3, 0x06, 0xFE, 0xF5, 0xF4, 0xF4, + 0xE5, 0x1B, 0x78, 0xB0, 0xFB, 0x9D, 0xCE, 0xE6, 0x9C, 0x3E, 0x79, 0x0B, 0x23, 0x1F, 0x2E, + 0x65, 0xFD, 0x1A, 0xB1, 0xC2, 0xA7, 0x5B, 0x07, 0x06, 0x7D, 0x5C, 0x16, 0xDD, 0xE0, 0x09, + 0x83, 0xA5, 0x8F, 0xFC, 0xDA, 0xAA, 0xEE, 0x16, 0xD2, 0x74, 0x2E, 0x13, 0x3E, 0xD7, 0x37, + 0xB4, 0x80, 0x64, 0xC8, 0xA3, 0x8E, 0xCA, 0x35, 0xAB, 0x3F, 0xA1, 0x8F, 0x6D, 0x62, 0xF6, + 0x42, 0xB1, 0x2C, 0xFD, 0xC7, 0x98, 0x0F, 0x2A, 0xB7, 0xDB, 0x32, 0x1F, 0xEC, 0x9D, 0xCF, + 0xE4, 0x99, 0xB4, 0xFC, 0x1E, 0xE7, 0xEB, 0x29, 0x79, 0x54, 0x05, 0x66, 0x17, 0xC6, 0x0A, + 0x66, 0x40, 0xB9, 0x28, 0x35, 0xD1, 0x65, 0xC3, 0xC0, 0x0A, 0x95, 0x19, 0x52, 0x61, 0x44, + 0x88, 0xD5, 0x65, 0x7B, 0xA0, 0xB5, 0xE9, 0x0A, 0xE9, 0xE0, 0xEF, 0x7B, 0x3B, 0x9E, 0xCA, + 0xEB, 0xD8, 0x1B, 0x85, 0x51, 0xB6, 0xD7, 0x0E, 0x83, 0x5B, 0x27, 0x34, 0x76, 0x16, 0x39, + 0xD4, 0x2E, 0x76, 0xFF, 0xC5, 0xB3, 0x27, 0x2B, 0x61, 0xC8, 0x96, 0xB4, 0x5B, 0x4B, 0xD1, + 0x8F, 0x30, 0xE5, 0x8C, 0x44, 0x06, 0x43, 0xBA, 0x15, 0x92, 0x21, 0xCC, 0x67, 0x39, 0xA1, + 0x9A, 0x65, 0xF2, 0x91, 0x1F, 0xAE, 0x47, 0xB0, 0xD4, 0xCA, 0xC4, 0x20, 0x0A, 0x6F, 0x04, + 0x3B, 0x17, 0xA0, 0x3A, 0xD3, 0x93, 0xEC, 0xB8, 0x23, 0xED, 0x03, 0xC8, 0xB6, 0xCD, 0x68, + 0x16, 0x7E, 0x6C, 0x82, 0x34, 0xF7, 0x43, 0x25, 0x57, 0xDB, 0x27, 0x20, 0x79, 0xEE, 0x89, + 0x9A, 0xED, 0xE7, 0x3B, 0x6B, 0x98, 0xD6, 0x00, 0x3F, 0x45, 0x78, 0x9A, 0x14, 0x1B, 0x60, + 0xD6, 0xDB, 0x40, 0xCD, 0x2A, 0x59, 0x74, 0x57, 0x1A, 0x4A, 0xD3, 0x66, 0x7B, 0x88, 0x93, + 0x18, 0xBA, 0x60, 0x28, 0x5D, 0x90, 0x3A, 0x2E, 0xAC, 0x01, 0xC2, 0x16, 0x08, 0x83, 0x8C, + 0x40, 0x90, 0x7D, 0xE6, 0xBB, 0xAB, 0xE0, 0x42, 0xCF, 0x2E, 0xCD, 0xD9, 0x7F, 0x54, 0x9F, + 0x95, 0xEC, 0x69, 0x8D, 0x79, 0x22, 0x2C, 0x65, 0xBA, 0x27, 0xC3, 0x0D, 0x33, 0x2A, 0x68, + 0xD0, 0x57, 0xAE, 0xCD, 0xC9, 0x38, 0x8A, 0xA3, 0x43, 0x20, 0xE0, 0xAA, 0x74, 0xFD, 0xBD, + 0x4D, 0x1B, 0x64, 0x3C, 0xAC, 0xE2, 0x16, 0xB6, 0xD8, 0xAD, 0x8F, 0x07, 0xA9, 0x99, 0x55, + 0xBF, 0xDB, 0x74, 0x3A, 0x86, 0xB4, 0x0F, 0xC6, 0x15, 0x27, 0xBA, 0xCA, 0x43, 0x4A, 0xC2, + 0xA7, 0xFB, 0xEA, 0xA7, 0x71, 0x11, 0xDC, 0x80, 0x98, 0xB1, 0x7E, 0x80, 0x0F, 0x59, 0xDD, + 0x77, 0xCC, 0xB0, 0xE6, 0x77, 0x07, 0xE6, 0x01, 0x23, 0xD3, 0x34, 0xE0, 0x73, 0xA2, 0xF5, + 0xA1, 0x6F, 0xFB, 0xCD, 0x70, 0x13, 0x89, 0xAD, 0xD5, 0x7C, 0x3C, 0xEC, 0xCB, 0x88, 0xB2, + 0x86, 0xAC, 0x1E, 0x6E, 0x3E, 0x64, 0x85, 0xAF, 0x1A, 0x12, 0xEA, 0x24, 0x1D, 0x14, 0xA1, + 0xB5, 0x00, 0x3D, 0x7F, 0x3B, 0xC9, 0xE9, 0x57, 0xD4, 0x48, 0x3C, 0x0F, 0x9F, 0x70, 0x3B, + 0x3A, 0x18, 0x7D, 0x55, 0xE5, 0x05, 0x81, 0x76, 0x15, 0xFB, 0xC4, 0xAE, 0x08, 0x37, 0x61, + 0x61, 0x84, 0x24, 0x5C, 0xFB, 0xA6, 0x1C, 0xE3, 0xB9, 0x29, 0xE3, 0x3F, 0x52, 0xB7, 0x1C, + 0xDD, 0x7B, 0x6A, 0x0D, 0xA5, 0x5C, 0x1F, 0x99, 0x75, 0x10, 0xB1, 0xA9, 0x00, 0x2C, 0xA4, + 0xE0, 0x67, 0x83, 0x73, 0xA3, 0xB1, 0xAB, 0x28, 0x97, 0xE6, 0xB4, 0x23, 0xF1, 0x5A, 0x44, + 0x0A, 0x63, 0x6C, 0xC8, 0x61, 0x49, 0x1E, 0xF4, 0x1A, 0xD0, 0xAA, 0x62, 0x7D, 0x8E, 0x19, + 0x8A, 0x5E, 0xE7, 0xBD, 0x7B, 0x6C, 0xB2, 0xC9, 0xCE, 0x2A, 0x8C, 0xC0, 0x15, 0xF0, 0xD2, + 0x06, 0xDE, 0x4C, 0x49, 0xE2, 0xF8, 0x7F, 0x31, 0x09, 0x54, 0xA1, 0x0D, 0x86, 0xE2, 0x94, + 0xF7, 0x42, 0xEE, 0x18, 0x6F, 0x4A, 0xE9, 0x81, 0x5F, 0x69, 0x96, 0x22, 0x79, 0x22, 0x06, + 0xCA, 0xFB, 0xA8, 0xF5, 0x62, 0x17, 0x38, 0x16, 0x0E, 0x6C, 0x5D, 0x61, 0x1A, 0x82, 0x52, + 0xC6, 0xF3, 0x50, 0x85, 0xB6, 0x04, 0xEF, 0x89, 0x51, 0x64, 0xD4, 0xEA, 0x6D, 0xDD, 0x31, + 0x0C, 0x7D, 0x8F, 0x0C, 0x87, 0x9F, 0xB1, 0xF8, 0x84, 0xC5, 0x74, 0x1D, 0x09, 0x6B, 0x3D, + 0x2D, 0xA0, 0xCE, 0x11, 0x51, 0x79, 0x0D, 0xDA, 0x88, 0x1D, 0x18, 0xCB, 0x6B, 0x19, 0xA9, + 0xFE, 0xD6, 0xF5, 0x25, 0x4B, 0x7D, 0x52, 0xD5, 0xD9, 0x2B, 0xBB, 0xE2, 0x4C, 0x9D, 0x6A, + 0x65, 0x60, 0x4A, 0x0B, 0x8E, 0xD2, 0x4A, 0xD5, 0xC1, 0x97, 0xD6, 0x83, 0xF5, 0x98, 0x74, + 0x3C, 0x96, 0xB5, 0x96, 0x0E, 0x87, 0x23, 0x73, 0x2B, 0x5B, 0xD6, 0x47, 0xE9, 0xDB, 0xEA, + 0xA8, 0x51, 0xD0, 0xE1, 0xCF, 0x6D, 0x2C, 0x07, 0x0D, 0x44, 0x42, 0x76, 0x2C, 0x28, 0x09, + 0x8C, 0x5C, 0xF5, 0xA5, 0x4B, 0x2B, 0x5E, 0x69, 0xA9, 0x9B, 0x10, 0x81, 0x5B, 0xF0, 0xF4, + 0x77, 0xBB, 0x71, 0xF0, 0xD5, 0xD3, 0xA6, 0x2B, 0xA2, 0xB3, 0xE2, 0x9B, 0xF8, 0x4D, 0x4B, + 0x4E, 0x57, 0x47, 0x07, 0xF5, 0xF7, 0x4A, 0xF7, 0x04, 0xD2, 0x77, 0xBD, 0x6C, 0xA3, 0x8D, + 0xA2, 0x1E, 0x2C, 0xDA, 0xC5, 0x49, 0xE5, 0xEA, 0xE1, 0xDE, 0x7A, 0x18, 0xEE, 0x53, 0x4C, + 0x8C, 0x22, 0x91, 0xC9, 0x08, 0xCA, 0xAB, 0xF1, 0x59, 0xE9, 0x0E, 0x65, 0x49, 0xDB, 0x94, + 0xBA, 0x7A, 0x3F, 0x3D, 0x97, 0xDD, 0x39, 0x8A, 0x75, 0xDF, 0x5B, 0x1A, 0x7C, 0xDF, 0xB2, + 0x54, 0x10, 0xB7, 0xEF, 0xC4, 0xED, 0x00, 0xD9, 0x99, 0x5B, 0x37, 0xB5, 0x8B, 0xF9, 0x1E, + 0xD7, 0xA3, 0x51, 0x0C, 0xFF, 0xEA, 0x82, 0xF9, 0xE1, 0xC2, 0xA3, 0x29, 0x04, 0x06, 0x00, + 0x4D, 0x09, 0x05, 0x7D, 0x63, 0xB7, 0x70, 0xFA, 0x0E, 0x53, 0x10, 0x31, 0x99, 0x54, 0x4E, + 0xBA, 0x66, 0x2A, 0x2C, 0x30, 0x2C, 0xF3, 0x90, 0x08, 0xF1, 0x42, 0xD2, 0xB1, 0x69, 0x63, + 0xE9, 0x5A, 0xB1, 0x0B, 0xE7, 0xC2, 0x61, 0x01, 0x68, 0x60, 0x8F, 0x35, 0x3A, 0x2F, 0x2C, + 0x41, 0xC7, 0x05, 0x6D, 0xEC, 0x1A, 0x8C, 0x7A, 0x6B, 0xFA, 0x00, 0x27, 0xF9, 0xDE, 0xDA, + 0xCB, 0x77, 0x86, 0xB6, 0x7E, 0xA2, 0xC4, 0x94, 0xD4, 0x3B, 0xA8, 0x51, 0xCF, 0x94, 0x15, + 0xC1, 0xBC, 0xC5, 0x2F, 0x02, 0x7E, 0xC0, 0x2C, 0x65, 0x53, 0x4F, 0x60, 0x8E, 0x9D, 0x16, + 0x6D, 0x51, 0xDD, 0x43, 0x1C, 0xDF, 0x58, 0x71, 0xF5, 0xCD, 0xD1, 0x57, 0x9C, 0xC0, 0x60, + 0x79, 0xDF, 0x07, 0x5A, 0x25, 0x06, 0x2B, 0xA7, 0xE7, 0x0D, 0x96, 0x66, 0xC4, 0xE7, 0xFE, + 0xD3, 0x4C, 0xEA, 0x0E, 0xA0, 0xF1, 0x1A, 0xDE, 0x1E, 0xB2, 0xA9, 0xB3, 0x97, 0xBC, 0xAA, + 0xAD, 0x10, 0x61, 0x27, 0x0E, 0xCF, 0x49, 0x78, 0x03, 0xA5, 0xFC, 0xE7, 0xF4, 0x1E, 0x65, + 0x04, 0xFB, 0xEC, 0x71, 0xA7, 0xDE, 0x7D, 0x06, 0x6B, 0x82, 0x61, 0x86, 0x8A, 0xFC, 0x49, + 0xB9, 0xE6, 0x85, 0xF0, 0xDC, 0xCE, 0x75, 0xE2, 0xFC, 0xB3, 0xBA, 0x8C, 0xF1, 0x90, 0x57, + 0xE3, 0x94, 0x15, 0x76, 0xBA, 0xF5, 0x8F, 0xB8, 0x21, 0xBD, 0x42, 0x68, 0xF7, 0xFA, 0xE3, + 0x02, 0x86, 0x01, 0xDA, 0x02, 0x2E, 0x9B, 0x46, 0x86, 0x46, 0xAB, 0xDB, 0x4F, 0xA6, 0x09, + 0x8A, 0x44, 0x9B, 0x42, 0x67, 0xD5, 0x09, 0xD9, 0xA3, 0x3F, 0x4C, 0x3E, 0xBC, 0xC3, 0x2D, + 0xAC, 0x09, 0x4D, 0x48, 0xED, 0x60, 0x0E, 0x76, 0x57, 0x87, 0xFB, 0x92, 0xB1, 0x97, 0x4F, + 0x74, 0xF7, 0xBB, 0x4C, 0x66, 0xEB, 0x2B, 0xBD, 0x02, 0x89, 0x5E, 0x6A, 0x38, 0x1C, 0x1C, + 0x45, 0x2E, 0xAA, 0xB1, 0xAE, 0x47, 0x31, 0xCF, 0x63, 0x2F, 0x61, 0xAE, 0x2C, 0x90, 0x59, + 0x21, 0x17, 0x4A, 0x3B, 0xC9, 0xBB, 0x4C, 0xDC, 0x89, 0xD6, 0x30, 0x26, 0x4B, 0x61, 0x49, + 0x88, 0xF3, 0xAB, 0xBE, 0xA1, 0xBD, 0x61, 0x7F, 0xFA, 0x53, 0xD7, 0x1B, 0x7D, 0x8A, 0x37, + 0x14, 0x62, 0xB7, 0x73, 0x35, 0x1A, 0x2D, 0xCC, 0xAE, 0xDD, 0x7F, 0x59, 0xCD, 0x72, 0x8F, + 0xAD, 0xEE, 0x05, 0x90, 0x67, 0xBD, 0x80, 0xC9, 0x4C, 0x8C, 0x9A, 0x1F, 0xFC, 0xA2, 0xDC, + 0x4F, 0x84, 0x8B, 0x82, 0x9C, 0x05, 0x61, 0x38, 0x5A, 0xA8, 0x2C, 0xC9, 0x85, 0x03, 0xD0, + 0xBB, 0x66, 0xA6, 0xAA, 0x4F, 0xAE, 0x07, 0x03, 0xD1, 0x2E, 0x60, 0xE1, 0x46, 0x0E, 0xFB, + 0xBC, 0xDF, 0x24, 0x12, 0xC1, 0x3E, 0x7C, 0x68, 0x4D, 0x1B, 0x01, 0x10, 0x20, 0x26, 0x34, + 0x3A, 0x41, 0x43, 0x44, 0x58, 0x5F, 0x6E, 0x70, 0x72, 0x74, 0x8B, 0xAE, 0xB5, 0xBB, 0xC6, + 0xD1, 0xE2, 0xEF, 0xFB, 0xFE, 0x06, 0x0E, 0x2E, 0x3E, 0x51, 0x60, 0x79, 0x7C, 0x9E, 0xA6, + 0xBA, 0xC7, 0xF1, 0x10, 0x24, 0x40, 0x4A, 0x52, 0x57, 0x5F, 0x6C, 0x89, 0x8C, 0x97, 0xAA, + 0xB2, 0xC3, 0xCC, 0xEA, 0xF2, 0x2F, 0x3F, 0x53, 0x5F, 0x7B, 0x81, 0x83, 0x96, 0xA1, 0xB1, + 0xBC, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x25, 0x36, 0x42, + ]; if MLDSA44::verify(&mldsa44_pk, msg, None, &sig).is_ok() { println!("Verification succeeded!"); @@ -330,7 +1434,7 @@ fn bench_mldsa44_verify() { } fn bench_mldsa44_lowmemory_verify() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey}; + use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey, MLDSATrait}; eprintln!("MLDSA44_lowmemory/Verify"); @@ -349,8 +1453,261 @@ fn bench_mldsa44_lowmemory_verify() { // let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); // eprintln!("sig:\n{}", &*hex::encode(sig)); - let pk = MLDSA44PublicKey::from_bytes(&[0xd7,0xb2,0xb4,0x72,0x54,0xaa,0xe0,0xdb,0x45,0xe7,0x93,0x0d,0x4a,0x98,0xd2,0xc9,0x7d,0x8f,0x13,0x97,0xd1,0x78,0x9d,0xaf,0xa1,0x70,0x24,0xb3,0x16,0xe9,0xbe,0xc9,0x4f,0xc9,0x94,0x6d,0x42,0xf1,0x9b,0x79,0xa7,0x41,0x3b,0xba,0xa3,0x3e,0x71,0x49,0xcb,0x42,0xed,0x51,0x15,0x69,0x3a,0xc0,0x41,0xfa,0xcb,0x98,0x8a,0xde,0xb5,0xfe,0x0e,0x1d,0x86,0x31,0x18,0x49,0x95,0xb5,0x92,0xc3,0x97,0xd2,0x29,0x4e,0x2e,0x14,0xf9,0x0a,0xa4,0x14,0xba,0x38,0x26,0x89,0x9a,0xc4,0x3f,0x4c,0xcc,0xac,0xbc,0x26,0xe9,0xa8,0x32,0xb9,0x51,0x18,0xd5,0xcb,0x43,0x3c,0xbe,0xf9,0x66,0x0b,0x00,0x13,0x8e,0x08,0x17,0xf6,0x1e,0x76,0x2c,0xa2,0x74,0xc3,0x6a,0xd5,0x54,0xeb,0x22,0xaa,0xc1,0x16,0x2e,0x4a,0xb0,0x1a,0xcb,0xa1,0xe3,0x8c,0x4e,0xfd,0x8f,0x80,0xb6,0x5b,0x33,0x3d,0x0f,0x72,0xe5,0x5d,0xfe,0x71,0xce,0x9c,0x1e,0xbb,0x98,0x89,0xe7,0xc5,0x61,0x06,0xc0,0xfd,0x73,0x80,0x3a,0x2a,0xec,0xfe,0xaf,0xde,0xd7,0xaa,0x3c,0xb2,0xce,0xda,0x54,0xd1,0x2b,0xd8,0xcd,0x36,0xa7,0x8c,0xf9,0x75,0x94,0x3b,0x47,0xab,0xd2,0x5e,0x88,0x0a,0xc4,0x52,0xe5,0x74,0x2e,0xd1,0xe8,0xd1,0xa8,0x2a,0xfa,0x86,0xe5,0x90,0xc7,0x58,0xc1,0x5a,0xe4,0xd2,0x84,0x0d,0x92,0xbc,0xa1,0xa5,0x09,0x0f,0x40,0x49,0x65,0x97,0xfc,0xa7,0xd8,0xb9,0x51,0x3f,0x1a,0x1b,0xda,0x6e,0x95,0x0a,0xaa,0x98,0xde,0x46,0x75,0x07,0xd4,0xa4,0xf5,0xa4,0xf0,0x59,0x92,0x16,0x58,0x2c,0x35,0x72,0xf6,0x2e,0xda,0x89,0x05,0xab,0x35,0x81,0x67,0x0c,0x4a,0x02,0x77,0x7a,0x33,0xe0,0xca,0x72,0x95,0xfd,0x8f,0x4f,0xf6,0xd1,0xa0,0xa3,0xa7,0x68,0x3d,0x65,0xf5,0xf5,0xf7,0xfc,0x60,0xda,0x02,0x3e,0x82,0x6c,0x5f,0x92,0x14,0x4c,0x02,0xf7,0xd1,0xba,0x10,0x75,0x98,0x75,0x53,0xea,0x93,0x67,0xfc,0xd7,0x6d,0x99,0x0b,0x7f,0xa9,0x9c,0xd4,0x5a,0xfd,0xb8,0x83,0x6d,0x43,0xe4,0x59,0xf5,0x18,0x7d,0xf0,0x58,0x47,0x97,0x09,0xa0,0x1e,0xa6,0x83,0x59,0x35,0xfa,0x70,0x46,0x09,0x90,0xcd,0x3d,0xc1,0xba,0x40,0x1b,0xa9,0x4b,0xab,0x1d,0xde,0x41,0xac,0x67,0xab,0x33,0x19,0xdc,0xac,0xa0,0x60,0x48,0xd4,0xc4,0xee,0xf2,0x7e,0xe1,0x3a,0x9c,0x17,0xd0,0x53,0x8f,0x43,0x0f,0x2d,0x64,0x2d,0xc2,0x41,0x56,0x60,0xde,0x78,0x87,0x7d,0x8d,0x8a,0xbc,0x72,0x52,0x39,0x78,0xc0,0x42,0xe4,0x28,0x5f,0x43,0x19,0x84,0x6c,0x44,0x12,0x62,0x42,0x97,0x68,0x44,0xc1,0x0e,0x55,0x6b,0xa2,0x15,0xb5,0xa7,0x19,0xe5,0x9d,0x0c,0x6b,0x2a,0x96,0xd3,0x98,0x59,0x07,0x1f,0xdc,0xc2,0xcd,0xe7,0x52,0x4a,0x7b,0xed,0xae,0x54,0xe8,0x5b,0x31,0x8e,0x85,0x4e,0x8f,0xe2,0xb2,0xf3,0xed,0xfa,0xc9,0x71,0x91,0x28,0x27,0x0a,0xaf,0xd1,0xe5,0x04,0x4c,0x3a,0x4f,0xda,0xfd,0x9f,0xf3,0x1f,0x90,0x78,0x4b,0x8e,0x8e,0x45,0x96,0x14,0x4a,0x0d,0xaf,0x58,0x65,0x11,0xd3,0xd9,0x96,0x2b,0x9e,0xa9,0x5a,0xf1,0x97,0xb4,0xe5,0xfc,0x60,0xf2,0xb1,0xed,0x15,0xde,0x3a,0x5b,0xef,0x5f,0x89,0xbd,0xc7,0x9d,0x91,0x05,0x1d,0x9b,0x28,0x16,0xe7,0x4f,0xa5,0x45,0x31,0xef,0xdc,0x1c,0xbe,0x74,0xd4,0x48,0x85,0x7f,0x47,0x6b,0xcd,0x58,0xf2,0x1c,0x0b,0x65,0x3b,0x3b,0x76,0xa4,0xe0,0x76,0xa6,0x55,0x9a,0x30,0x27,0x18,0x55,0x5c,0xc6,0x3f,0x74,0x85,0x9a,0xab,0xab,0x92,0x5f,0x02,0x38,0x61,0xca,0x8c,0xd0,0xf7,0xba,0xdb,0x28,0x71,0xf6,0x7d,0x55,0x32,0x6d,0x74,0x51,0x13,0x5a,0xd4,0x5f,0x4a,0x1b,0xa6,0x91,0x18,0xfb,0xb2,0xc8,0xa3,0x0e,0xec,0x93,0x92,0xef,0x3f,0x97,0x70,0x66,0xc9,0xad,0xd5,0xc7,0x10,0xcc,0x64,0x7b,0x15,0x14,0xd2,0x17,0xd9,0x58,0xc7,0x01,0x7c,0x3e,0x90,0xfd,0x20,0xc0,0x4e,0x67,0x4b,0x90,0x48,0x6e,0x93,0x70,0xa3,0x1a,0x00,0x1d,0x32,0xf4,0x73,0x97,0x9e,0x49,0x06,0x74,0x9e,0x7e,0x47,0x7f,0xa0,0xb7,0x45,0x08,0xf8,0xa5,0xf2,0x37,0x83,0x12,0xb8,0x3c,0x25,0xbd,0x38,0x8c,0xa0,0xb0,0xff,0xf7,0x47,0x8b,0xaf,0x42,0xb7,0x16,0x67,0xed,0xaa,0xc9,0x7c,0x46,0xb1,0x29,0x64,0x3e,0x58,0x6e,0x5b,0x05,0x5a,0x0c,0x21,0x19,0x46,0xd4,0xf3,0x6e,0x67,0x5b,0xed,0x58,0x60,0xfa,0x04,0x2a,0x31,0x5d,0x98,0x26,0x16,0x4d,0x6a,0x92,0x37,0xc3,0x5a,0x5f,0xbf,0x49,0x54,0x90,0xa5,0xbd,0x4d,0xf2,0x48,0xb9,0x5c,0x4a,0xae,0x77,0x84,0xb6,0x05,0x67,0x31,0x66,0xac,0x42,0x45,0xb5,0xb4,0xb0,0x82,0xa0,0x9e,0x93,0x23,0xe6,0x2f,0x20,0x78,0xc5,0xb7,0x67,0x83,0x44,0x6d,0xef,0xd7,0x36,0xad,0x3a,0x37,0x02,0xd4,0x9b,0x08,0x98,0x44,0x90,0x0a,0x61,0x83,0x33,0x97,0xbc,0x44,0x19,0xb3,0x0d,0x7a,0x97,0xa0,0xb3,0x87,0xc1,0x91,0x14,0x74,0xc4,0xd4,0x1b,0x53,0xe3,0x2a,0x97,0x7a,0xcb,0x6f,0x0e,0xa7,0x5d,0xb6,0x5b,0xb3,0x9e,0x59,0xe7,0x01,0xe7,0x69,0x57,0xde,0xf6,0xf2,0xd4,0x45,0x59,0xc3,0x1a,0x77,0x12,0x2b,0x52,0x04,0xe3,0xb5,0xc2,0x19,0xf1,0x68,0x8b,0x14,0xed,0x0b,0xc0,0xb8,0x01,0xb3,0xe6,0xe8,0x2d,0xcd,0x43,0xe9,0xc0,0xe9,0xf4,0x17,0x44,0xcd,0x98,0x15,0xbd,0x1b,0xc8,0x82,0x0d,0x8b,0xb1,0x23,0xf0,0x4f,0xac,0xd1,0xb1,0xb6,0x85,0xdd,0x5a,0x2b,0x1b,0x8d,0xbb,0xf3,0xed,0x93,0x36,0x70,0xf0,0x95,0xa1,0x80,0xb4,0xf1,0x92,0xd0,0x8b,0x10,0xb8,0xfa,0xbb,0xdf,0xcc,0x2b,0x24,0x51,0x8e,0x32,0xee,0xa0,0xa5,0xe0,0xc9,0x04,0xca,0x84,0x47,0x80,0x08,0x3f,0x3b,0x0c,0xd2,0xd0,0xb8,0xb6,0xaf,0x67,0xbc,0x35,0x5b,0x94,0x94,0x02,0x5d,0xc7,0xb0,0xa7,0x8f,0xa8,0x0e,0x3a,0x2d,0xbf,0xeb,0x51,0x32,0x88,0x51,0xd6,0x07,0x81,0x98,0xe9,0x49,0x36,0x51,0xae,0x78,0x7e,0xc0,0x25,0x1f,0x92,0x2b,0xa3,0x0e,0x9f,0x51,0xdf,0x62,0xa6,0xd7,0x27,0x84,0xcf,0x3d,0xd2,0x05,0x39,0x31,0x76,0xdf,0xa3,0x24,0xa5,0x12,0xbd,0x94,0x97,0x0a,0x36,0xdd,0x34,0xa5,0x14,0xa8,0x67,0x91,0xf0,0xeb,0x36,0xf0,0x14,0x5b,0x09,0xab,0x64,0x65,0x1b,0x4a,0x03,0x13,0xb2,0x99,0x61,0x1a,0x2a,0x1c,0x48,0x89,0x16,0x27,0x59,0x87,0x68,0xa3,0x11,0x40,0x60,0xba,0x44,0x43,0x48,0x6d,0xf5,0x15,0x22,0xa1,0xce,0x88,0xb3,0x09,0x85,0xc2,0x16,0xf8,0xe6,0xed,0x17,0x8d,0xd5,0x67,0xb3,0x04,0xa0,0xd4,0xca,0xfb,0xa8,0x82,0xa2,0x83,0x42,0xf1,0x7a,0x9a,0xa2,0x6a,0xe5,0x8d,0xb6,0x30,0x08,0x3d,0x2c,0x35,0x8f,0xdf,0x56,0x6c,0x3f,0x5d,0x62,0xa4,0x28,0x56,0x7b,0xc9,0xea,0x8c,0xe9,0x5c,0xaa,0x0f,0x35,0x47,0x4b,0x0b,0xfa,0x8f,0x33,0x9a,0x25,0x0a,0xb4,0xdf,0xcf,0x20,0x83,0xbe,0x8e,0xef,0xbc,0x10,0x55,0xe1,0x8f,0xe1,0x53,0x70,0xee,0xcb,0x26,0x05,0x66,0xd8,0x3f,0xf0,0x6b,0x21,0x1a,0xae,0xc4,0x3c,0xa2,0x9b,0x54,0xcc,0xd0,0x0f,0x88,0x15,0xa2,0x46,0x5e,0xf0,0xb4,0x65,0x15,0xcc,0x7e,0x41,0xf3,0x12,0x4f,0x09,0xef,0xff,0x73,0x93,0x09,0xab,0x58,0xb2,0x9a,0x14,0x59,0xa0,0x0b,0xce,0x50,0x38,0xe9,0x38,0xc9,0x67,0x8f,0x72,0xeb,0x0e,0x4e,0xe5,0xfd,0xaa,0xe6,0x6d,0x9f,0x85,0x73,0xfc,0x97,0xfc,0x42,0xb4,0x95,0x9f,0x4b,0xf8,0xb6,0x1d,0x78,0x43,0x3e,0x86,0xb0,0x33,0x5d,0x6e,0x91,0x91,0xc4,0xd8,0xbf,0x48,0x7b,0x39,0x05,0xc1,0x08,0xcf,0xd6,0xac,0x24,0xb0,0xce,0xb7,0xdc,0xb7,0xcf,0x51,0xf8,0x4d,0x0e,0xd6,0x87,0xb9,0x5e,0xae,0xb1,0xc5,0x33,0xc0,0x6f,0x0d,0x97,0x02,0x3d,0x92,0xa7,0x08,0x25,0x83,0x7b,0x59,0xba,0x6c,0xb7,0xd4,0xe5,0x6b,0x0a,0x87,0xc2,0x03,0x86,0x2a,0xe8,0xf3,0x15,0xba,0x59,0x25,0xe8,0xed,0xef,0xa6,0x79,0x36,0x9a,0x22,0x02,0x76,0x61,0x51,0xf1,0x6a,0x96,0x5f,0x9f,0x81,0xec,0xe7,0x6c,0xc0,0x70,0xb5,0x58,0x69,0xe4,0xdb,0x97,0x84,0xcf,0x05,0xc8,0x30,0xb3,0x24,0x2c,0x83,0x12]).unwrap(); - let sig: [u8; MLDSA44_SIG_LEN] = [0x5e,0x93,0xb7,0x85,0xc5,0x11,0x9c,0x39,0x83,0xa2,0x91,0xb1,0x84,0x20,0xfd,0xbe,0x4b,0xca,0x53,0xd5,0xa3,0x73,0x29,0x22,0xfa,0xaa,0xcd,0x5a,0x5d,0x32,0xa7,0x45,0xc7,0x8d,0x10,0x5b,0xa1,0x0b,0xee,0x1e,0xd8,0x06,0x9f,0x19,0xe6,0xc5,0x37,0xbd,0xa1,0x6e,0x89,0xd3,0x90,0x04,0xc3,0x59,0xd1,0xfd,0x38,0x1a,0x02,0x91,0xf1,0xc5,0x1f,0x1c,0x38,0xed,0xcd,0xb3,0x15,0xc8,0xc6,0x95,0x70,0xd8,0xf2,0x5f,0x16,0x55,0xba,0x8e,0xa8,0x3a,0xff,0x24,0xb8,0xb6,0xbe,0x8d,0xe7,0x62,0x34,0x2e,0x34,0x7e,0xab,0x2c,0xaa,0x68,0x03,0xed,0x70,0x59,0x52,0xdd,0x64,0x50,0xc5,0x18,0x5e,0x9d,0x60,0xce,0x96,0xe8,0xdc,0xa4,0x23,0xa0,0x2f,0x64,0x6c,0xea,0x69,0x01,0x64,0xa2,0x26,0xe4,0xc3,0xd6,0xa5,0x15,0xce,0x16,0x29,0x0f,0x19,0xb2,0xc6,0x26,0xda,0x9b,0x45,0x0e,0xcf,0x66,0x50,0x13,0xc5,0xe2,0x26,0xb6,0xc0,0xac,0x5c,0x07,0xce,0x90,0xe2,0x78,0xf1,0xb0,0x13,0x4e,0x38,0x5d,0x13,0xe7,0x42,0x08,0xa0,0xb3,0xff,0x05,0x2a,0x36,0x25,0x79,0xf9,0x20,0x7e,0xa0,0x1f,0x18,0xa0,0x39,0xaa,0x1b,0x97,0xae,0x34,0x52,0x67,0x5b,0x62,0x07,0x71,0xf8,0x01,0x2e,0xe7,0xa4,0xe5,0x5c,0x98,0xbf,0xd2,0x01,0x9e,0xd8,0xa3,0xb0,0x0a,0xce,0xa8,0xe8,0xab,0x28,0x17,0x2f,0xaa,0x42,0xca,0x1f,0xda,0x83,0xc5,0xff,0xe8,0x1a,0x45,0xbe,0x73,0x6b,0xde,0xdd,0x5f,0xb3,0x00,0xce,0x17,0x07,0x8b,0x38,0x0f,0x62,0x0b,0xde,0xeb,0xad,0x69,0x36,0x01,0x37,0x2c,0x85,0xea,0xcf,0x79,0xbc,0x98,0xe1,0xb4,0x8f,0x2a,0xd7,0xe5,0xdc,0xe4,0x27,0x9a,0x12,0x95,0xbb,0x2b,0xa6,0x0a,0x0c,0x5e,0x37,0x26,0x64,0x2d,0x23,0x36,0xc5,0xeb,0x1d,0x37,0xc8,0x62,0x3c,0x75,0x58,0x24,0x13,0x18,0xd8,0x9b,0xc7,0x83,0xc4,0xf0,0x00,0x98,0x07,0x74,0x84,0x62,0x3c,0x21,0x75,0x60,0xa0,0xc7,0xaa,0xf7,0x5d,0xca,0xcc,0xb7,0x8e,0xe6,0x9c,0x20,0x7c,0x27,0xc8,0xbf,0x39,0x65,0xcc,0xf5,0x8a,0x80,0xc8,0x8e,0xfc,0xc7,0xe5,0xde,0xb3,0x61,0x5d,0x50,0x45,0xa7,0x41,0xc4,0xda,0xc0,0xa0,0x21,0xdd,0x06,0x0d,0x31,0x5d,0x4e,0xc2,0x85,0x7e,0xb6,0x64,0xd7,0x28,0xd0,0xaf,0x97,0x3b,0xea,0x07,0xe1,0xca,0x56,0x3f,0xaa,0x0e,0x19,0x99,0x6c,0xea,0x37,0x70,0x31,0x6c,0x11,0xa5,0x06,0x66,0x65,0x66,0x20,0x05,0xac,0xe9,0x8f,0x61,0x10,0xe8,0x83,0xba,0xe0,0x60,0xda,0xa7,0xb6,0xd8,0x33,0x79,0xe0,0x87,0x87,0x96,0x69,0x17,0x08,0xa3,0x2b,0x85,0x73,0x0d,0xe8,0xb9,0x2d,0x89,0xf9,0x0a,0x36,0x60,0xc9,0x49,0x16,0x5b,0x14,0x61,0x25,0x67,0x66,0x2e,0x16,0x22,0x32,0x29,0x6c,0xbd,0x14,0x35,0x17,0xa2,0x82,0xe2,0x2c,0x46,0xb6,0x36,0x06,0xd3,0xc1,0x4e,0xd4,0x55,0x9a,0x5a,0x1c,0x45,0x9b,0xab,0x7f,0x35,0x50,0x07,0xad,0x6f,0x7e,0x3b,0x1e,0x07,0x44,0x5d,0xfc,0x96,0xbd,0x9b,0x75,0x08,0x0b,0x3d,0x4f,0x68,0x99,0x84,0x90,0xa2,0x6b,0x5e,0x09,0x0b,0xe2,0x67,0x40,0x71,0xab,0x92,0x5b,0xb6,0x50,0x59,0x08,0x56,0xc5,0x9f,0x8b,0xa7,0x48,0x8d,0x2b,0x72,0xf8,0x40,0xac,0x3e,0xaf,0xe4,0xdd,0x91,0xf0,0xf5,0x1c,0x43,0x64,0x11,0x2c,0x1a,0x13,0x9e,0x3e,0x94,0x2a,0x59,0x7b,0x93,0xa1,0xe3,0xf4,0xfa,0xde,0xd1,0x29,0xc1,0x4b,0x59,0x78,0xb3,0x15,0xe2,0x24,0x6a,0x93,0x14,0x6a,0x79,0x36,0x5f,0x0f,0x59,0x7a,0x18,0x34,0x0c,0xca,0x86,0xbb,0x15,0xce,0xed,0x39,0xf1,0x75,0xea,0xb1,0xe5,0x46,0x53,0x5a,0xfb,0x96,0x6f,0x0a,0x65,0xa8,0xf6,0x6f,0x73,0x7a,0xb0,0x28,0x97,0xed,0xdf,0xe9,0x2c,0xf7,0x78,0x68,0x94,0x84,0x3c,0x26,0x91,0x46,0x47,0x76,0xc9,0x4b,0xd4,0x50,0xa1,0x06,0x91,0x38,0xb2,0x6d,0xf8,0x3b,0x2d,0x1d,0xd8,0x01,0x14,0x3a,0x8f,0xdf,0xdc,0x25,0x14,0xcc,0x5b,0x58,0x31,0xab,0x53,0xa7,0x5c,0x55,0xef,0x29,0xf4,0x0e,0x7c,0x63,0xd2,0xc7,0x2a,0xbe,0x97,0xe2,0xaf,0x14,0x85,0x3b,0xe4,0x9b,0xe1,0x6f,0x47,0x30,0xa1,0x59,0x97,0x49,0x70,0x95,0x14,0x39,0xe5,0x5c,0x15,0x89,0xd0,0xf4,0xa1,0x62,0xe3,0x51,0x7d,0xf9,0xd7,0xab,0xc9,0x8d,0x8a,0x30,0x72,0x16,0xe7,0xf1,0xcb,0x46,0x27,0xc9,0x17,0x5c,0x0e,0xef,0x23,0x33,0x7e,0x56,0xd5,0x28,0x1b,0x83,0x72,0x6f,0xff,0x40,0xa1,0x48,0xb0,0xc4,0x8e,0x8d,0xf3,0x49,0x6a,0x21,0x18,0xd8,0x02,0x19,0xae,0xf8,0xf4,0x0b,0x29,0xfb,0xa1,0xf2,0xf7,0x87,0x86,0xb6,0x7f,0xfb,0x7b,0x7d,0x47,0xd4,0x06,0xb7,0x65,0xbd,0x13,0x66,0x10,0xbe,0xde,0xb9,0x5c,0xd7,0x32,0x1f,0x58,0xf3,0xb8,0x36,0xc9,0x25,0x8b,0xe3,0x5d,0x78,0xb4,0x98,0xf3,0xef,0xe1,0xdb,0x2b,0x24,0x3d,0x73,0x4f,0xab,0x15,0x9b,0xae,0xd8,0x80,0x7c,0x3c,0xcc,0xf8,0x3e,0xb2,0xea,0xf8,0xa9,0xaf,0x01,0xa5,0x18,0xd4,0x8c,0x60,0xe9,0x1a,0x96,0x81,0x2a,0xd6,0x89,0xc2,0xd8,0x3c,0xc4,0xe8,0xe9,0xb3,0x65,0x04,0x22,0xbe,0xd6,0xf1,0x3c,0x24,0xad,0xaa,0xd9,0x1c,0x95,0xb3,0xe3,0xcf,0x35,0x4f,0x0f,0x6b,0xc9,0xee,0x89,0x41,0xa6,0xb1,0x5b,0x69,0x75,0x13,0x1d,0x95,0x23,0x3d,0x89,0x35,0xde,0x36,0x7e,0xfc,0x6d,0x86,0xa4,0x5d,0xac,0x7d,0x0f,0x1d,0xdd,0x9a,0xeb,0xd2,0xc5,0x9c,0x02,0x7f,0xcd,0xa4,0x48,0x80,0x1e,0x93,0xe7,0x33,0xac,0xa5,0x18,0x74,0xbe,0x9a,0xb9,0x27,0xa9,0x04,0xf9,0x6d,0xdb,0x7a,0x46,0xb2,0xda,0x13,0x26,0x1d,0x52,0x2b,0x23,0xc9,0x50,0xc0,0x1d,0x5f,0x5e,0x11,0x2b,0x76,0xf8,0x51,0xff,0x23,0x4f,0x06,0xf8,0xd5,0xe6,0x5b,0x13,0x19,0xab,0xcd,0x79,0xa1,0x80,0xae,0x06,0x3d,0x65,0xb2,0x8c,0x74,0x58,0x78,0xc0,0x6d,0xbb,0x69,0xba,0x73,0x29,0x3e,0xab,0x34,0x43,0x4b,0xf1,0xa9,0x2f,0xba,0x69,0x19,0x93,0xbd,0x0f,0xf3,0xed,0xac,0x76,0xa1,0x2f,0x80,0xc0,0xad,0xa4,0xb1,0x96,0x9c,0x76,0x65,0x58,0x9d,0x53,0x0a,0x67,0x01,0x6a,0x62,0x54,0x03,0xc5,0x37,0x03,0x29,0x04,0xf2,0xe1,0x04,0x54,0x7c,0xd3,0xea,0x40,0x62,0x60,0xdd,0x35,0x7f,0xa0,0x6e,0xa0,0x12,0xa7,0x85,0x82,0x6c,0x16,0x0e,0x99,0xff,0xd0,0x65,0xb0,0xe3,0xf3,0x3c,0x76,0x89,0xd3,0x55,0x2a,0xb9,0xe2,0xe0,0x9f,0xa7,0xe5,0x5b,0xbc,0xef,0x04,0x22,0x42,0xbc,0xac,0xad,0x8a,0x3d,0xa4,0x7b,0xcc,0x54,0xa1,0x21,0xf1,0x52,0x6c,0x8c,0xd4,0xcc,0x5a,0x89,0x2a,0x81,0x31,0xcf,0x4e,0xef,0xaf,0x42,0x48,0xdd,0xd6,0xa1,0x1e,0xc4,0x27,0xba,0x37,0x8a,0xae,0x89,0xaa,0xf5,0x82,0xce,0x1f,0x4e,0x32,0x69,0x0a,0x55,0x5e,0x74,0x07,0x61,0xd3,0x58,0xad,0x4e,0x92,0xbc,0x38,0x41,0x8a,0xa7,0x82,0xda,0x91,0x65,0x24,0xfb,0x09,0xab,0x2c,0xa6,0xb3,0xd3,0x11,0x3d,0x6f,0x2c,0x2a,0x6a,0x9b,0x9d,0x29,0xd4,0xe7,0x48,0x92,0x55,0x25,0x2a,0xf0,0x75,0xcb,0xf9,0xfe,0xac,0xed,0xae,0x6f,0x3e,0xc0,0xb0,0x70,0x82,0x46,0x89,0xdd,0x3c,0x78,0xac,0x14,0x3e,0xd6,0x77,0x6d,0x95,0xdd,0x8f,0x13,0xd4,0x35,0xa2,0x90,0xbd,0xca,0x4c,0x11,0x31,0x8e,0x5a,0xcc,0xe0,0x44,0x69,0x64,0x4e,0x13,0x74,0xa9,0x45,0x1b,0x62,0x04,0xf3,0xb3,0x96,0x1b,0x7d,0xd2,0x39,0xe3,0x06,0xfe,0xf5,0xf4,0xf4,0xe5,0x1b,0x78,0xb0,0xfb,0x9d,0xce,0xe6,0x9c,0x3e,0x79,0x0b,0x23,0x1f,0x2e,0x65,0xfd,0x1a,0xb1,0xc2,0xa7,0x5b,0x07,0x06,0x7d,0x5c,0x16,0xdd,0xe0,0x09,0x83,0xa5,0x8f,0xfc,0xda,0xaa,0xee,0x16,0xd2,0x74,0x2e,0x13,0x3e,0xd7,0x37,0xb4,0x80,0x64,0xc8,0xa3,0x8e,0xca,0x35,0xab,0x3f,0xa1,0x8f,0x6d,0x62,0xf6,0x42,0xb1,0x2c,0xfd,0xc7,0x98,0x0f,0x2a,0xb7,0xdb,0x32,0x1f,0xec,0x9d,0xcf,0xe4,0x99,0xb4,0xfc,0x1e,0xe7,0xeb,0x29,0x79,0x54,0x05,0x66,0x17,0xc6,0x0a,0x66,0x40,0xb9,0x28,0x35,0xd1,0x65,0xc3,0xc0,0x0a,0x95,0x19,0x52,0x61,0x44,0x88,0xd5,0x65,0x7b,0xa0,0xb5,0xe9,0x0a,0xe9,0xe0,0xef,0x7b,0x3b,0x9e,0xca,0xeb,0xd8,0x1b,0x85,0x51,0xb6,0xd7,0x0e,0x83,0x5b,0x27,0x34,0x76,0x16,0x39,0xd4,0x2e,0x76,0xff,0xc5,0xb3,0x27,0x2b,0x61,0xc8,0x96,0xb4,0x5b,0x4b,0xd1,0x8f,0x30,0xe5,0x8c,0x44,0x06,0x43,0xba,0x15,0x92,0x21,0xcc,0x67,0x39,0xa1,0x9a,0x65,0xf2,0x91,0x1f,0xae,0x47,0xb0,0xd4,0xca,0xc4,0x20,0x0a,0x6f,0x04,0x3b,0x17,0xa0,0x3a,0xd3,0x93,0xec,0xb8,0x23,0xed,0x03,0xc8,0xb6,0xcd,0x68,0x16,0x7e,0x6c,0x82,0x34,0xf7,0x43,0x25,0x57,0xdb,0x27,0x20,0x79,0xee,0x89,0x9a,0xed,0xe7,0x3b,0x6b,0x98,0xd6,0x00,0x3f,0x45,0x78,0x9a,0x14,0x1b,0x60,0xd6,0xdb,0x40,0xcd,0x2a,0x59,0x74,0x57,0x1a,0x4a,0xd3,0x66,0x7b,0x88,0x93,0x18,0xba,0x60,0x28,0x5d,0x90,0x3a,0x2e,0xac,0x01,0xc2,0x16,0x08,0x83,0x8c,0x40,0x90,0x7d,0xe6,0xbb,0xab,0xe0,0x42,0xcf,0x2e,0xcd,0xd9,0x7f,0x54,0x9f,0x95,0xec,0x69,0x8d,0x79,0x22,0x2c,0x65,0xba,0x27,0xc3,0x0d,0x33,0x2a,0x68,0xd0,0x57,0xae,0xcd,0xc9,0x38,0x8a,0xa3,0x43,0x20,0xe0,0xaa,0x74,0xfd,0xbd,0x4d,0x1b,0x64,0x3c,0xac,0xe2,0x16,0xb6,0xd8,0xad,0x8f,0x07,0xa9,0x99,0x55,0xbf,0xdb,0x74,0x3a,0x86,0xb4,0x0f,0xc6,0x15,0x27,0xba,0xca,0x43,0x4a,0xc2,0xa7,0xfb,0xea,0xa7,0x71,0x11,0xdc,0x80,0x98,0xb1,0x7e,0x80,0x0f,0x59,0xdd,0x77,0xcc,0xb0,0xe6,0x77,0x07,0xe6,0x01,0x23,0xd3,0x34,0xe0,0x73,0xa2,0xf5,0xa1,0x6f,0xfb,0xcd,0x70,0x13,0x89,0xad,0xd5,0x7c,0x3c,0xec,0xcb,0x88,0xb2,0x86,0xac,0x1e,0x6e,0x3e,0x64,0x85,0xaf,0x1a,0x12,0xea,0x24,0x1d,0x14,0xa1,0xb5,0x00,0x3d,0x7f,0x3b,0xc9,0xe9,0x57,0xd4,0x48,0x3c,0x0f,0x9f,0x70,0x3b,0x3a,0x18,0x7d,0x55,0xe5,0x05,0x81,0x76,0x15,0xfb,0xc4,0xae,0x08,0x37,0x61,0x61,0x84,0x24,0x5c,0xfb,0xa6,0x1c,0xe3,0xb9,0x29,0xe3,0x3f,0x52,0xb7,0x1c,0xdd,0x7b,0x6a,0x0d,0xa5,0x5c,0x1f,0x99,0x75,0x10,0xb1,0xa9,0x00,0x2c,0xa4,0xe0,0x67,0x83,0x73,0xa3,0xb1,0xab,0x28,0x97,0xe6,0xb4,0x23,0xf1,0x5a,0x44,0x0a,0x63,0x6c,0xc8,0x61,0x49,0x1e,0xf4,0x1a,0xd0,0xaa,0x62,0x7d,0x8e,0x19,0x8a,0x5e,0xe7,0xbd,0x7b,0x6c,0xb2,0xc9,0xce,0x2a,0x8c,0xc0,0x15,0xf0,0xd2,0x06,0xde,0x4c,0x49,0xe2,0xf8,0x7f,0x31,0x09,0x54,0xa1,0x0d,0x86,0xe2,0x94,0xf7,0x42,0xee,0x18,0x6f,0x4a,0xe9,0x81,0x5f,0x69,0x96,0x22,0x79,0x22,0x06,0xca,0xfb,0xa8,0xf5,0x62,0x17,0x38,0x16,0x0e,0x6c,0x5d,0x61,0x1a,0x82,0x52,0xc6,0xf3,0x50,0x85,0xb6,0x04,0xef,0x89,0x51,0x64,0xd4,0xea,0x6d,0xdd,0x31,0x0c,0x7d,0x8f,0x0c,0x87,0x9f,0xb1,0xf8,0x84,0xc5,0x74,0x1d,0x09,0x6b,0x3d,0x2d,0xa0,0xce,0x11,0x51,0x79,0x0d,0xda,0x88,0x1d,0x18,0xcb,0x6b,0x19,0xa9,0xfe,0xd6,0xf5,0x25,0x4b,0x7d,0x52,0xd5,0xd9,0x2b,0xbb,0xe2,0x4c,0x9d,0x6a,0x65,0x60,0x4a,0x0b,0x8e,0xd2,0x4a,0xd5,0xc1,0x97,0xd6,0x83,0xf5,0x98,0x74,0x3c,0x96,0xb5,0x96,0x0e,0x87,0x23,0x73,0x2b,0x5b,0xd6,0x47,0xe9,0xdb,0xea,0xa8,0x51,0xd0,0xe1,0xcf,0x6d,0x2c,0x07,0x0d,0x44,0x42,0x76,0x2c,0x28,0x09,0x8c,0x5c,0xf5,0xa5,0x4b,0x2b,0x5e,0x69,0xa9,0x9b,0x10,0x81,0x5b,0xf0,0xf4,0x77,0xbb,0x71,0xf0,0xd5,0xd3,0xa6,0x2b,0xa2,0xb3,0xe2,0x9b,0xf8,0x4d,0x4b,0x4e,0x57,0x47,0x07,0xf5,0xf7,0x4a,0xf7,0x04,0xd2,0x77,0xbd,0x6c,0xa3,0x8d,0xa2,0x1e,0x2c,0xda,0xc5,0x49,0xe5,0xea,0xe1,0xde,0x7a,0x18,0xee,0x53,0x4c,0x8c,0x22,0x91,0xc9,0x08,0xca,0xab,0xf1,0x59,0xe9,0x0e,0x65,0x49,0xdb,0x94,0xba,0x7a,0x3f,0x3d,0x97,0xdd,0x39,0x8a,0x75,0xdf,0x5b,0x1a,0x7c,0xdf,0xb2,0x54,0x10,0xb7,0xef,0xc4,0xed,0x00,0xd9,0x99,0x5b,0x37,0xb5,0x8b,0xf9,0x1e,0xd7,0xa3,0x51,0x0c,0xff,0xea,0x82,0xf9,0xe1,0xc2,0xa3,0x29,0x04,0x06,0x00,0x4d,0x09,0x05,0x7d,0x63,0xb7,0x70,0xfa,0x0e,0x53,0x10,0x31,0x99,0x54,0x4e,0xba,0x66,0x2a,0x2c,0x30,0x2c,0xf3,0x90,0x08,0xf1,0x42,0xd2,0xb1,0x69,0x63,0xe9,0x5a,0xb1,0x0b,0xe7,0xc2,0x61,0x01,0x68,0x60,0x8f,0x35,0x3a,0x2f,0x2c,0x41,0xc7,0x05,0x6d,0xec,0x1a,0x8c,0x7a,0x6b,0xfa,0x00,0x27,0xf9,0xde,0xda,0xcb,0x77,0x86,0xb6,0x7e,0xa2,0xc4,0x94,0xd4,0x3b,0xa8,0x51,0xcf,0x94,0x15,0xc1,0xbc,0xc5,0x2f,0x02,0x7e,0xc0,0x2c,0x65,0x53,0x4f,0x60,0x8e,0x9d,0x16,0x6d,0x51,0xdd,0x43,0x1c,0xdf,0x58,0x71,0xf5,0xcd,0xd1,0x57,0x9c,0xc0,0x60,0x79,0xdf,0x07,0x5a,0x25,0x06,0x2b,0xa7,0xe7,0x0d,0x96,0x66,0xc4,0xe7,0xfe,0xd3,0x4c,0xea,0x0e,0xa0,0xf1,0x1a,0xde,0x1e,0xb2,0xa9,0xb3,0x97,0xbc,0xaa,0xad,0x10,0x61,0x27,0x0e,0xcf,0x49,0x78,0x03,0xa5,0xfc,0xe7,0xf4,0x1e,0x65,0x04,0xfb,0xec,0x71,0xa7,0xde,0x7d,0x06,0x6b,0x82,0x61,0x86,0x8a,0xfc,0x49,0xb9,0xe6,0x85,0xf0,0xdc,0xce,0x75,0xe2,0xfc,0xb3,0xba,0x8c,0xf1,0x90,0x57,0xe3,0x94,0x15,0x76,0xba,0xf5,0x8f,0xb8,0x21,0xbd,0x42,0x68,0xf7,0xfa,0xe3,0x02,0x86,0x01,0xda,0x02,0x2e,0x9b,0x46,0x86,0x46,0xab,0xdb,0x4f,0xa6,0x09,0x8a,0x44,0x9b,0x42,0x67,0xd5,0x09,0xd9,0xa3,0x3f,0x4c,0x3e,0xbc,0xc3,0x2d,0xac,0x09,0x4d,0x48,0xed,0x60,0x0e,0x76,0x57,0x87,0xfb,0x92,0xb1,0x97,0x4f,0x74,0xf7,0xbb,0x4c,0x66,0xeb,0x2b,0xbd,0x02,0x89,0x5e,0x6a,0x38,0x1c,0x1c,0x45,0x2e,0xaa,0xb1,0xae,0x47,0x31,0xcf,0x63,0x2f,0x61,0xae,0x2c,0x90,0x59,0x21,0x17,0x4a,0x3b,0xc9,0xbb,0x4c,0xdc,0x89,0xd6,0x30,0x26,0x4b,0x61,0x49,0x88,0xf3,0xab,0xbe,0xa1,0xbd,0x61,0x7f,0xfa,0x53,0xd7,0x1b,0x7d,0x8a,0x37,0x14,0x62,0xb7,0x73,0x35,0x1a,0x2d,0xcc,0xae,0xdd,0x7f,0x59,0xcd,0x72,0x8f,0xad,0xee,0x05,0x90,0x67,0xbd,0x80,0xc9,0x4c,0x8c,0x9a,0x1f,0xfc,0xa2,0xdc,0x4f,0x84,0x8b,0x82,0x9c,0x05,0x61,0x38,0x5a,0xa8,0x2c,0xc9,0x85,0x03,0xd0,0xbb,0x66,0xa6,0xaa,0x4f,0xae,0x07,0x03,0xd1,0x2e,0x60,0xe1,0x46,0x0e,0xfb,0xbc,0xdf,0x24,0x12,0xc1,0x3e,0x7c,0x68,0x4d,0x1b,0x01,0x10,0x20,0x26,0x34,0x3a,0x41,0x43,0x44,0x58,0x5f,0x6e,0x70,0x72,0x74,0x8b,0xae,0xb5,0xbb,0xc6,0xd1,0xe2,0xef,0xfb,0xfe,0x06,0x0e,0x2e,0x3e,0x51,0x60,0x79,0x7c,0x9e,0xa6,0xba,0xc7,0xf1,0x10,0x24,0x40,0x4a,0x52,0x57,0x5f,0x6c,0x89,0x8c,0x97,0xaa,0xb2,0xc3,0xcc,0xea,0xf2,0x2f,0x3f,0x53,0x5f,0x7b,0x81,0x83,0x96,0xa1,0xb1,0xbc,0xe6,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x18,0x25,0x36,0x42]; + let pk = MLDSA44PublicKey::from_bytes(&[ + 0xD7, 0xB2, 0xB4, 0x72, 0x54, 0xAA, 0xE0, 0xDB, 0x45, 0xE7, 0x93, 0x0D, 0x4A, 0x98, 0xD2, + 0xC9, 0x7D, 0x8F, 0x13, 0x97, 0xD1, 0x78, 0x9D, 0xAF, 0xA1, 0x70, 0x24, 0xB3, 0x16, 0xE9, + 0xBE, 0xC9, 0x4F, 0xC9, 0x94, 0x6D, 0x42, 0xF1, 0x9B, 0x79, 0xA7, 0x41, 0x3B, 0xBA, 0xA3, + 0x3E, 0x71, 0x49, 0xCB, 0x42, 0xED, 0x51, 0x15, 0x69, 0x3A, 0xC0, 0x41, 0xFA, 0xCB, 0x98, + 0x8A, 0xDE, 0xB5, 0xFE, 0x0E, 0x1D, 0x86, 0x31, 0x18, 0x49, 0x95, 0xB5, 0x92, 0xC3, 0x97, + 0xD2, 0x29, 0x4E, 0x2E, 0x14, 0xF9, 0x0A, 0xA4, 0x14, 0xBA, 0x38, 0x26, 0x89, 0x9A, 0xC4, + 0x3F, 0x4C, 0xCC, 0xAC, 0xBC, 0x26, 0xE9, 0xA8, 0x32, 0xB9, 0x51, 0x18, 0xD5, 0xCB, 0x43, + 0x3C, 0xBE, 0xF9, 0x66, 0x0B, 0x00, 0x13, 0x8E, 0x08, 0x17, 0xF6, 0x1E, 0x76, 0x2C, 0xA2, + 0x74, 0xC3, 0x6A, 0xD5, 0x54, 0xEB, 0x22, 0xAA, 0xC1, 0x16, 0x2E, 0x4A, 0xB0, 0x1A, 0xCB, + 0xA1, 0xE3, 0x8C, 0x4E, 0xFD, 0x8F, 0x80, 0xB6, 0x5B, 0x33, 0x3D, 0x0F, 0x72, 0xE5, 0x5D, + 0xFE, 0x71, 0xCE, 0x9C, 0x1E, 0xBB, 0x98, 0x89, 0xE7, 0xC5, 0x61, 0x06, 0xC0, 0xFD, 0x73, + 0x80, 0x3A, 0x2A, 0xEC, 0xFE, 0xAF, 0xDE, 0xD7, 0xAA, 0x3C, 0xB2, 0xCE, 0xDA, 0x54, 0xD1, + 0x2B, 0xD8, 0xCD, 0x36, 0xA7, 0x8C, 0xF9, 0x75, 0x94, 0x3B, 0x47, 0xAB, 0xD2, 0x5E, 0x88, + 0x0A, 0xC4, 0x52, 0xE5, 0x74, 0x2E, 0xD1, 0xE8, 0xD1, 0xA8, 0x2A, 0xFA, 0x86, 0xE5, 0x90, + 0xC7, 0x58, 0xC1, 0x5A, 0xE4, 0xD2, 0x84, 0x0D, 0x92, 0xBC, 0xA1, 0xA5, 0x09, 0x0F, 0x40, + 0x49, 0x65, 0x97, 0xFC, 0xA7, 0xD8, 0xB9, 0x51, 0x3F, 0x1A, 0x1B, 0xDA, 0x6E, 0x95, 0x0A, + 0xAA, 0x98, 0xDE, 0x46, 0x75, 0x07, 0xD4, 0xA4, 0xF5, 0xA4, 0xF0, 0x59, 0x92, 0x16, 0x58, + 0x2C, 0x35, 0x72, 0xF6, 0x2E, 0xDA, 0x89, 0x05, 0xAB, 0x35, 0x81, 0x67, 0x0C, 0x4A, 0x02, + 0x77, 0x7A, 0x33, 0xE0, 0xCA, 0x72, 0x95, 0xFD, 0x8F, 0x4F, 0xF6, 0xD1, 0xA0, 0xA3, 0xA7, + 0x68, 0x3D, 0x65, 0xF5, 0xF5, 0xF7, 0xFC, 0x60, 0xDA, 0x02, 0x3E, 0x82, 0x6C, 0x5F, 0x92, + 0x14, 0x4C, 0x02, 0xF7, 0xD1, 0xBA, 0x10, 0x75, 0x98, 0x75, 0x53, 0xEA, 0x93, 0x67, 0xFC, + 0xD7, 0x6D, 0x99, 0x0B, 0x7F, 0xA9, 0x9C, 0xD4, 0x5A, 0xFD, 0xB8, 0x83, 0x6D, 0x43, 0xE4, + 0x59, 0xF5, 0x18, 0x7D, 0xF0, 0x58, 0x47, 0x97, 0x09, 0xA0, 0x1E, 0xA6, 0x83, 0x59, 0x35, + 0xFA, 0x70, 0x46, 0x09, 0x90, 0xCD, 0x3D, 0xC1, 0xBA, 0x40, 0x1B, 0xA9, 0x4B, 0xAB, 0x1D, + 0xDE, 0x41, 0xAC, 0x67, 0xAB, 0x33, 0x19, 0xDC, 0xAC, 0xA0, 0x60, 0x48, 0xD4, 0xC4, 0xEE, + 0xF2, 0x7E, 0xE1, 0x3A, 0x9C, 0x17, 0xD0, 0x53, 0x8F, 0x43, 0x0F, 0x2D, 0x64, 0x2D, 0xC2, + 0x41, 0x56, 0x60, 0xDE, 0x78, 0x87, 0x7D, 0x8D, 0x8A, 0xBC, 0x72, 0x52, 0x39, 0x78, 0xC0, + 0x42, 0xE4, 0x28, 0x5F, 0x43, 0x19, 0x84, 0x6C, 0x44, 0x12, 0x62, 0x42, 0x97, 0x68, 0x44, + 0xC1, 0x0E, 0x55, 0x6B, 0xA2, 0x15, 0xB5, 0xA7, 0x19, 0xE5, 0x9D, 0x0C, 0x6B, 0x2A, 0x96, + 0xD3, 0x98, 0x59, 0x07, 0x1F, 0xDC, 0xC2, 0xCD, 0xE7, 0x52, 0x4A, 0x7B, 0xED, 0xAE, 0x54, + 0xE8, 0x5B, 0x31, 0x8E, 0x85, 0x4E, 0x8F, 0xE2, 0xB2, 0xF3, 0xED, 0xFA, 0xC9, 0x71, 0x91, + 0x28, 0x27, 0x0A, 0xAF, 0xD1, 0xE5, 0x04, 0x4C, 0x3A, 0x4F, 0xDA, 0xFD, 0x9F, 0xF3, 0x1F, + 0x90, 0x78, 0x4B, 0x8E, 0x8E, 0x45, 0x96, 0x14, 0x4A, 0x0D, 0xAF, 0x58, 0x65, 0x11, 0xD3, + 0xD9, 0x96, 0x2B, 0x9E, 0xA9, 0x5A, 0xF1, 0x97, 0xB4, 0xE5, 0xFC, 0x60, 0xF2, 0xB1, 0xED, + 0x15, 0xDE, 0x3A, 0x5B, 0xEF, 0x5F, 0x89, 0xBD, 0xC7, 0x9D, 0x91, 0x05, 0x1D, 0x9B, 0x28, + 0x16, 0xE7, 0x4F, 0xA5, 0x45, 0x31, 0xEF, 0xDC, 0x1C, 0xBE, 0x74, 0xD4, 0x48, 0x85, 0x7F, + 0x47, 0x6B, 0xCD, 0x58, 0xF2, 0x1C, 0x0B, 0x65, 0x3B, 0x3B, 0x76, 0xA4, 0xE0, 0x76, 0xA6, + 0x55, 0x9A, 0x30, 0x27, 0x18, 0x55, 0x5C, 0xC6, 0x3F, 0x74, 0x85, 0x9A, 0xAB, 0xAB, 0x92, + 0x5F, 0x02, 0x38, 0x61, 0xCA, 0x8C, 0xD0, 0xF7, 0xBA, 0xDB, 0x28, 0x71, 0xF6, 0x7D, 0x55, + 0x32, 0x6D, 0x74, 0x51, 0x13, 0x5A, 0xD4, 0x5F, 0x4A, 0x1B, 0xA6, 0x91, 0x18, 0xFB, 0xB2, + 0xC8, 0xA3, 0x0E, 0xEC, 0x93, 0x92, 0xEF, 0x3F, 0x97, 0x70, 0x66, 0xC9, 0xAD, 0xD5, 0xC7, + 0x10, 0xCC, 0x64, 0x7B, 0x15, 0x14, 0xD2, 0x17, 0xD9, 0x58, 0xC7, 0x01, 0x7C, 0x3E, 0x90, + 0xFD, 0x20, 0xC0, 0x4E, 0x67, 0x4B, 0x90, 0x48, 0x6E, 0x93, 0x70, 0xA3, 0x1A, 0x00, 0x1D, + 0x32, 0xF4, 0x73, 0x97, 0x9E, 0x49, 0x06, 0x74, 0x9E, 0x7E, 0x47, 0x7F, 0xA0, 0xB7, 0x45, + 0x08, 0xF8, 0xA5, 0xF2, 0x37, 0x83, 0x12, 0xB8, 0x3C, 0x25, 0xBD, 0x38, 0x8C, 0xA0, 0xB0, + 0xFF, 0xF7, 0x47, 0x8B, 0xAF, 0x42, 0xB7, 0x16, 0x67, 0xED, 0xAA, 0xC9, 0x7C, 0x46, 0xB1, + 0x29, 0x64, 0x3E, 0x58, 0x6E, 0x5B, 0x05, 0x5A, 0x0C, 0x21, 0x19, 0x46, 0xD4, 0xF3, 0x6E, + 0x67, 0x5B, 0xED, 0x58, 0x60, 0xFA, 0x04, 0x2A, 0x31, 0x5D, 0x98, 0x26, 0x16, 0x4D, 0x6A, + 0x92, 0x37, 0xC3, 0x5A, 0x5F, 0xBF, 0x49, 0x54, 0x90, 0xA5, 0xBD, 0x4D, 0xF2, 0x48, 0xB9, + 0x5C, 0x4A, 0xAE, 0x77, 0x84, 0xB6, 0x05, 0x67, 0x31, 0x66, 0xAC, 0x42, 0x45, 0xB5, 0xB4, + 0xB0, 0x82, 0xA0, 0x9E, 0x93, 0x23, 0xE6, 0x2F, 0x20, 0x78, 0xC5, 0xB7, 0x67, 0x83, 0x44, + 0x6D, 0xEF, 0xD7, 0x36, 0xAD, 0x3A, 0x37, 0x02, 0xD4, 0x9B, 0x08, 0x98, 0x44, 0x90, 0x0A, + 0x61, 0x83, 0x33, 0x97, 0xBC, 0x44, 0x19, 0xB3, 0x0D, 0x7A, 0x97, 0xA0, 0xB3, 0x87, 0xC1, + 0x91, 0x14, 0x74, 0xC4, 0xD4, 0x1B, 0x53, 0xE3, 0x2A, 0x97, 0x7A, 0xCB, 0x6F, 0x0E, 0xA7, + 0x5D, 0xB6, 0x5B, 0xB3, 0x9E, 0x59, 0xE7, 0x01, 0xE7, 0x69, 0x57, 0xDE, 0xF6, 0xF2, 0xD4, + 0x45, 0x59, 0xC3, 0x1A, 0x77, 0x12, 0x2B, 0x52, 0x04, 0xE3, 0xB5, 0xC2, 0x19, 0xF1, 0x68, + 0x8B, 0x14, 0xED, 0x0B, 0xC0, 0xB8, 0x01, 0xB3, 0xE6, 0xE8, 0x2D, 0xCD, 0x43, 0xE9, 0xC0, + 0xE9, 0xF4, 0x17, 0x44, 0xCD, 0x98, 0x15, 0xBD, 0x1B, 0xC8, 0x82, 0x0D, 0x8B, 0xB1, 0x23, + 0xF0, 0x4F, 0xAC, 0xD1, 0xB1, 0xB6, 0x85, 0xDD, 0x5A, 0x2B, 0x1B, 0x8D, 0xBB, 0xF3, 0xED, + 0x93, 0x36, 0x70, 0xF0, 0x95, 0xA1, 0x80, 0xB4, 0xF1, 0x92, 0xD0, 0x8B, 0x10, 0xB8, 0xFA, + 0xBB, 0xDF, 0xCC, 0x2B, 0x24, 0x51, 0x8E, 0x32, 0xEE, 0xA0, 0xA5, 0xE0, 0xC9, 0x04, 0xCA, + 0x84, 0x47, 0x80, 0x08, 0x3F, 0x3B, 0x0C, 0xD2, 0xD0, 0xB8, 0xB6, 0xAF, 0x67, 0xBC, 0x35, + 0x5B, 0x94, 0x94, 0x02, 0x5D, 0xC7, 0xB0, 0xA7, 0x8F, 0xA8, 0x0E, 0x3A, 0x2D, 0xBF, 0xEB, + 0x51, 0x32, 0x88, 0x51, 0xD6, 0x07, 0x81, 0x98, 0xE9, 0x49, 0x36, 0x51, 0xAE, 0x78, 0x7E, + 0xC0, 0x25, 0x1F, 0x92, 0x2B, 0xA3, 0x0E, 0x9F, 0x51, 0xDF, 0x62, 0xA6, 0xD7, 0x27, 0x84, + 0xCF, 0x3D, 0xD2, 0x05, 0x39, 0x31, 0x76, 0xDF, 0xA3, 0x24, 0xA5, 0x12, 0xBD, 0x94, 0x97, + 0x0A, 0x36, 0xDD, 0x34, 0xA5, 0x14, 0xA8, 0x67, 0x91, 0xF0, 0xEB, 0x36, 0xF0, 0x14, 0x5B, + 0x09, 0xAB, 0x64, 0x65, 0x1B, 0x4A, 0x03, 0x13, 0xB2, 0x99, 0x61, 0x1A, 0x2A, 0x1C, 0x48, + 0x89, 0x16, 0x27, 0x59, 0x87, 0x68, 0xA3, 0x11, 0x40, 0x60, 0xBA, 0x44, 0x43, 0x48, 0x6D, + 0xF5, 0x15, 0x22, 0xA1, 0xCE, 0x88, 0xB3, 0x09, 0x85, 0xC2, 0x16, 0xF8, 0xE6, 0xED, 0x17, + 0x8D, 0xD5, 0x67, 0xB3, 0x04, 0xA0, 0xD4, 0xCA, 0xFB, 0xA8, 0x82, 0xA2, 0x83, 0x42, 0xF1, + 0x7A, 0x9A, 0xA2, 0x6A, 0xE5, 0x8D, 0xB6, 0x30, 0x08, 0x3D, 0x2C, 0x35, 0x8F, 0xDF, 0x56, + 0x6C, 0x3F, 0x5D, 0x62, 0xA4, 0x28, 0x56, 0x7B, 0xC9, 0xEA, 0x8C, 0xE9, 0x5C, 0xAA, 0x0F, + 0x35, 0x47, 0x4B, 0x0B, 0xFA, 0x8F, 0x33, 0x9A, 0x25, 0x0A, 0xB4, 0xDF, 0xCF, 0x20, 0x83, + 0xBE, 0x8E, 0xEF, 0xBC, 0x10, 0x55, 0xE1, 0x8F, 0xE1, 0x53, 0x70, 0xEE, 0xCB, 0x26, 0x05, + 0x66, 0xD8, 0x3F, 0xF0, 0x6B, 0x21, 0x1A, 0xAE, 0xC4, 0x3C, 0xA2, 0x9B, 0x54, 0xCC, 0xD0, + 0x0F, 0x88, 0x15, 0xA2, 0x46, 0x5E, 0xF0, 0xB4, 0x65, 0x15, 0xCC, 0x7E, 0x41, 0xF3, 0x12, + 0x4F, 0x09, 0xEF, 0xFF, 0x73, 0x93, 0x09, 0xAB, 0x58, 0xB2, 0x9A, 0x14, 0x59, 0xA0, 0x0B, + 0xCE, 0x50, 0x38, 0xE9, 0x38, 0xC9, 0x67, 0x8F, 0x72, 0xEB, 0x0E, 0x4E, 0xE5, 0xFD, 0xAA, + 0xE6, 0x6D, 0x9F, 0x85, 0x73, 0xFC, 0x97, 0xFC, 0x42, 0xB4, 0x95, 0x9F, 0x4B, 0xF8, 0xB6, + 0x1D, 0x78, 0x43, 0x3E, 0x86, 0xB0, 0x33, 0x5D, 0x6E, 0x91, 0x91, 0xC4, 0xD8, 0xBF, 0x48, + 0x7B, 0x39, 0x05, 0xC1, 0x08, 0xCF, 0xD6, 0xAC, 0x24, 0xB0, 0xCE, 0xB7, 0xDC, 0xB7, 0xCF, + 0x51, 0xF8, 0x4D, 0x0E, 0xD6, 0x87, 0xB9, 0x5E, 0xAE, 0xB1, 0xC5, 0x33, 0xC0, 0x6F, 0x0D, + 0x97, 0x02, 0x3D, 0x92, 0xA7, 0x08, 0x25, 0x83, 0x7B, 0x59, 0xBA, 0x6C, 0xB7, 0xD4, 0xE5, + 0x6B, 0x0A, 0x87, 0xC2, 0x03, 0x86, 0x2A, 0xE8, 0xF3, 0x15, 0xBA, 0x59, 0x25, 0xE8, 0xED, + 0xEF, 0xA6, 0x79, 0x36, 0x9A, 0x22, 0x02, 0x76, 0x61, 0x51, 0xF1, 0x6A, 0x96, 0x5F, 0x9F, + 0x81, 0xEC, 0xE7, 0x6C, 0xC0, 0x70, 0xB5, 0x58, 0x69, 0xE4, 0xDB, 0x97, 0x84, 0xCF, 0x05, + 0xC8, 0x30, 0xB3, 0x24, 0x2C, 0x83, 0x12, + ]) + .unwrap(); + let sig: [u8; MLDSA44_SIG_LEN] = [ + 0x5E, 0x93, 0xB7, 0x85, 0xC5, 0x11, 0x9C, 0x39, 0x83, 0xA2, 0x91, 0xB1, 0x84, 0x20, 0xFD, + 0xBE, 0x4B, 0xCA, 0x53, 0xD5, 0xA3, 0x73, 0x29, 0x22, 0xFA, 0xAA, 0xCD, 0x5A, 0x5D, 0x32, + 0xA7, 0x45, 0xC7, 0x8D, 0x10, 0x5B, 0xA1, 0x0B, 0xEE, 0x1E, 0xD8, 0x06, 0x9F, 0x19, 0xE6, + 0xC5, 0x37, 0xBD, 0xA1, 0x6E, 0x89, 0xD3, 0x90, 0x04, 0xC3, 0x59, 0xD1, 0xFD, 0x38, 0x1A, + 0x02, 0x91, 0xF1, 0xC5, 0x1F, 0x1C, 0x38, 0xED, 0xCD, 0xB3, 0x15, 0xC8, 0xC6, 0x95, 0x70, + 0xD8, 0xF2, 0x5F, 0x16, 0x55, 0xBA, 0x8E, 0xA8, 0x3A, 0xFF, 0x24, 0xB8, 0xB6, 0xBE, 0x8D, + 0xE7, 0x62, 0x34, 0x2E, 0x34, 0x7E, 0xAB, 0x2C, 0xAA, 0x68, 0x03, 0xED, 0x70, 0x59, 0x52, + 0xDD, 0x64, 0x50, 0xC5, 0x18, 0x5E, 0x9D, 0x60, 0xCE, 0x96, 0xE8, 0xDC, 0xA4, 0x23, 0xA0, + 0x2F, 0x64, 0x6C, 0xEA, 0x69, 0x01, 0x64, 0xA2, 0x26, 0xE4, 0xC3, 0xD6, 0xA5, 0x15, 0xCE, + 0x16, 0x29, 0x0F, 0x19, 0xB2, 0xC6, 0x26, 0xDA, 0x9B, 0x45, 0x0E, 0xCF, 0x66, 0x50, 0x13, + 0xC5, 0xE2, 0x26, 0xB6, 0xC0, 0xAC, 0x5C, 0x07, 0xCE, 0x90, 0xE2, 0x78, 0xF1, 0xB0, 0x13, + 0x4E, 0x38, 0x5D, 0x13, 0xE7, 0x42, 0x08, 0xA0, 0xB3, 0xFF, 0x05, 0x2A, 0x36, 0x25, 0x79, + 0xF9, 0x20, 0x7E, 0xA0, 0x1F, 0x18, 0xA0, 0x39, 0xAA, 0x1B, 0x97, 0xAE, 0x34, 0x52, 0x67, + 0x5B, 0x62, 0x07, 0x71, 0xF8, 0x01, 0x2E, 0xE7, 0xA4, 0xE5, 0x5C, 0x98, 0xBF, 0xD2, 0x01, + 0x9E, 0xD8, 0xA3, 0xB0, 0x0A, 0xCE, 0xA8, 0xE8, 0xAB, 0x28, 0x17, 0x2F, 0xAA, 0x42, 0xCA, + 0x1F, 0xDA, 0x83, 0xC5, 0xFF, 0xE8, 0x1A, 0x45, 0xBE, 0x73, 0x6B, 0xDE, 0xDD, 0x5F, 0xB3, + 0x00, 0xCE, 0x17, 0x07, 0x8B, 0x38, 0x0F, 0x62, 0x0B, 0xDE, 0xEB, 0xAD, 0x69, 0x36, 0x01, + 0x37, 0x2C, 0x85, 0xEA, 0xCF, 0x79, 0xBC, 0x98, 0xE1, 0xB4, 0x8F, 0x2A, 0xD7, 0xE5, 0xDC, + 0xE4, 0x27, 0x9A, 0x12, 0x95, 0xBB, 0x2B, 0xA6, 0x0A, 0x0C, 0x5E, 0x37, 0x26, 0x64, 0x2D, + 0x23, 0x36, 0xC5, 0xEB, 0x1D, 0x37, 0xC8, 0x62, 0x3C, 0x75, 0x58, 0x24, 0x13, 0x18, 0xD8, + 0x9B, 0xC7, 0x83, 0xC4, 0xF0, 0x00, 0x98, 0x07, 0x74, 0x84, 0x62, 0x3C, 0x21, 0x75, 0x60, + 0xA0, 0xC7, 0xAA, 0xF7, 0x5D, 0xCA, 0xCC, 0xB7, 0x8E, 0xE6, 0x9C, 0x20, 0x7C, 0x27, 0xC8, + 0xBF, 0x39, 0x65, 0xCC, 0xF5, 0x8A, 0x80, 0xC8, 0x8E, 0xFC, 0xC7, 0xE5, 0xDE, 0xB3, 0x61, + 0x5D, 0x50, 0x45, 0xA7, 0x41, 0xC4, 0xDA, 0xC0, 0xA0, 0x21, 0xDD, 0x06, 0x0D, 0x31, 0x5D, + 0x4E, 0xC2, 0x85, 0x7E, 0xB6, 0x64, 0xD7, 0x28, 0xD0, 0xAF, 0x97, 0x3B, 0xEA, 0x07, 0xE1, + 0xCA, 0x56, 0x3F, 0xAA, 0x0E, 0x19, 0x99, 0x6C, 0xEA, 0x37, 0x70, 0x31, 0x6C, 0x11, 0xA5, + 0x06, 0x66, 0x65, 0x66, 0x20, 0x05, 0xAC, 0xE9, 0x8F, 0x61, 0x10, 0xE8, 0x83, 0xBA, 0xE0, + 0x60, 0xDA, 0xA7, 0xB6, 0xD8, 0x33, 0x79, 0xE0, 0x87, 0x87, 0x96, 0x69, 0x17, 0x08, 0xA3, + 0x2B, 0x85, 0x73, 0x0D, 0xE8, 0xB9, 0x2D, 0x89, 0xF9, 0x0A, 0x36, 0x60, 0xC9, 0x49, 0x16, + 0x5B, 0x14, 0x61, 0x25, 0x67, 0x66, 0x2E, 0x16, 0x22, 0x32, 0x29, 0x6C, 0xBD, 0x14, 0x35, + 0x17, 0xA2, 0x82, 0xE2, 0x2C, 0x46, 0xB6, 0x36, 0x06, 0xD3, 0xC1, 0x4E, 0xD4, 0x55, 0x9A, + 0x5A, 0x1C, 0x45, 0x9B, 0xAB, 0x7F, 0x35, 0x50, 0x07, 0xAD, 0x6F, 0x7E, 0x3B, 0x1E, 0x07, + 0x44, 0x5D, 0xFC, 0x96, 0xBD, 0x9B, 0x75, 0x08, 0x0B, 0x3D, 0x4F, 0x68, 0x99, 0x84, 0x90, + 0xA2, 0x6B, 0x5E, 0x09, 0x0B, 0xE2, 0x67, 0x40, 0x71, 0xAB, 0x92, 0x5B, 0xB6, 0x50, 0x59, + 0x08, 0x56, 0xC5, 0x9F, 0x8B, 0xA7, 0x48, 0x8D, 0x2B, 0x72, 0xF8, 0x40, 0xAC, 0x3E, 0xAF, + 0xE4, 0xDD, 0x91, 0xF0, 0xF5, 0x1C, 0x43, 0x64, 0x11, 0x2C, 0x1A, 0x13, 0x9E, 0x3E, 0x94, + 0x2A, 0x59, 0x7B, 0x93, 0xA1, 0xE3, 0xF4, 0xFA, 0xDE, 0xD1, 0x29, 0xC1, 0x4B, 0x59, 0x78, + 0xB3, 0x15, 0xE2, 0x24, 0x6A, 0x93, 0x14, 0x6A, 0x79, 0x36, 0x5F, 0x0F, 0x59, 0x7A, 0x18, + 0x34, 0x0C, 0xCA, 0x86, 0xBB, 0x15, 0xCE, 0xED, 0x39, 0xF1, 0x75, 0xEA, 0xB1, 0xE5, 0x46, + 0x53, 0x5A, 0xFB, 0x96, 0x6F, 0x0A, 0x65, 0xA8, 0xF6, 0x6F, 0x73, 0x7A, 0xB0, 0x28, 0x97, + 0xED, 0xDF, 0xE9, 0x2C, 0xF7, 0x78, 0x68, 0x94, 0x84, 0x3C, 0x26, 0x91, 0x46, 0x47, 0x76, + 0xC9, 0x4B, 0xD4, 0x50, 0xA1, 0x06, 0x91, 0x38, 0xB2, 0x6D, 0xF8, 0x3B, 0x2D, 0x1D, 0xD8, + 0x01, 0x14, 0x3A, 0x8F, 0xDF, 0xDC, 0x25, 0x14, 0xCC, 0x5B, 0x58, 0x31, 0xAB, 0x53, 0xA7, + 0x5C, 0x55, 0xEF, 0x29, 0xF4, 0x0E, 0x7C, 0x63, 0xD2, 0xC7, 0x2A, 0xBE, 0x97, 0xE2, 0xAF, + 0x14, 0x85, 0x3B, 0xE4, 0x9B, 0xE1, 0x6F, 0x47, 0x30, 0xA1, 0x59, 0x97, 0x49, 0x70, 0x95, + 0x14, 0x39, 0xE5, 0x5C, 0x15, 0x89, 0xD0, 0xF4, 0xA1, 0x62, 0xE3, 0x51, 0x7D, 0xF9, 0xD7, + 0xAB, 0xC9, 0x8D, 0x8A, 0x30, 0x72, 0x16, 0xE7, 0xF1, 0xCB, 0x46, 0x27, 0xC9, 0x17, 0x5C, + 0x0E, 0xEF, 0x23, 0x33, 0x7E, 0x56, 0xD5, 0x28, 0x1B, 0x83, 0x72, 0x6F, 0xFF, 0x40, 0xA1, + 0x48, 0xB0, 0xC4, 0x8E, 0x8D, 0xF3, 0x49, 0x6A, 0x21, 0x18, 0xD8, 0x02, 0x19, 0xAE, 0xF8, + 0xF4, 0x0B, 0x29, 0xFB, 0xA1, 0xF2, 0xF7, 0x87, 0x86, 0xB6, 0x7F, 0xFB, 0x7B, 0x7D, 0x47, + 0xD4, 0x06, 0xB7, 0x65, 0xBD, 0x13, 0x66, 0x10, 0xBE, 0xDE, 0xB9, 0x5C, 0xD7, 0x32, 0x1F, + 0x58, 0xF3, 0xB8, 0x36, 0xC9, 0x25, 0x8B, 0xE3, 0x5D, 0x78, 0xB4, 0x98, 0xF3, 0xEF, 0xE1, + 0xDB, 0x2B, 0x24, 0x3D, 0x73, 0x4F, 0xAB, 0x15, 0x9B, 0xAE, 0xD8, 0x80, 0x7C, 0x3C, 0xCC, + 0xF8, 0x3E, 0xB2, 0xEA, 0xF8, 0xA9, 0xAF, 0x01, 0xA5, 0x18, 0xD4, 0x8C, 0x60, 0xE9, 0x1A, + 0x96, 0x81, 0x2A, 0xD6, 0x89, 0xC2, 0xD8, 0x3C, 0xC4, 0xE8, 0xE9, 0xB3, 0x65, 0x04, 0x22, + 0xBE, 0xD6, 0xF1, 0x3C, 0x24, 0xAD, 0xAA, 0xD9, 0x1C, 0x95, 0xB3, 0xE3, 0xCF, 0x35, 0x4F, + 0x0F, 0x6B, 0xC9, 0xEE, 0x89, 0x41, 0xA6, 0xB1, 0x5B, 0x69, 0x75, 0x13, 0x1D, 0x95, 0x23, + 0x3D, 0x89, 0x35, 0xDE, 0x36, 0x7E, 0xFC, 0x6D, 0x86, 0xA4, 0x5D, 0xAC, 0x7D, 0x0F, 0x1D, + 0xDD, 0x9A, 0xEB, 0xD2, 0xC5, 0x9C, 0x02, 0x7F, 0xCD, 0xA4, 0x48, 0x80, 0x1E, 0x93, 0xE7, + 0x33, 0xAC, 0xA5, 0x18, 0x74, 0xBE, 0x9A, 0xB9, 0x27, 0xA9, 0x04, 0xF9, 0x6D, 0xDB, 0x7A, + 0x46, 0xB2, 0xDA, 0x13, 0x26, 0x1D, 0x52, 0x2B, 0x23, 0xC9, 0x50, 0xC0, 0x1D, 0x5F, 0x5E, + 0x11, 0x2B, 0x76, 0xF8, 0x51, 0xFF, 0x23, 0x4F, 0x06, 0xF8, 0xD5, 0xE6, 0x5B, 0x13, 0x19, + 0xAB, 0xCD, 0x79, 0xA1, 0x80, 0xAE, 0x06, 0x3D, 0x65, 0xB2, 0x8C, 0x74, 0x58, 0x78, 0xC0, + 0x6D, 0xBB, 0x69, 0xBA, 0x73, 0x29, 0x3E, 0xAB, 0x34, 0x43, 0x4B, 0xF1, 0xA9, 0x2F, 0xBA, + 0x69, 0x19, 0x93, 0xBD, 0x0F, 0xF3, 0xED, 0xAC, 0x76, 0xA1, 0x2F, 0x80, 0xC0, 0xAD, 0xA4, + 0xB1, 0x96, 0x9C, 0x76, 0x65, 0x58, 0x9D, 0x53, 0x0A, 0x67, 0x01, 0x6A, 0x62, 0x54, 0x03, + 0xC5, 0x37, 0x03, 0x29, 0x04, 0xF2, 0xE1, 0x04, 0x54, 0x7C, 0xD3, 0xEA, 0x40, 0x62, 0x60, + 0xDD, 0x35, 0x7F, 0xA0, 0x6E, 0xA0, 0x12, 0xA7, 0x85, 0x82, 0x6C, 0x16, 0x0E, 0x99, 0xFF, + 0xD0, 0x65, 0xB0, 0xE3, 0xF3, 0x3C, 0x76, 0x89, 0xD3, 0x55, 0x2A, 0xB9, 0xE2, 0xE0, 0x9F, + 0xA7, 0xE5, 0x5B, 0xBC, 0xEF, 0x04, 0x22, 0x42, 0xBC, 0xAC, 0xAD, 0x8A, 0x3D, 0xA4, 0x7B, + 0xCC, 0x54, 0xA1, 0x21, 0xF1, 0x52, 0x6C, 0x8C, 0xD4, 0xCC, 0x5A, 0x89, 0x2A, 0x81, 0x31, + 0xCF, 0x4E, 0xEF, 0xAF, 0x42, 0x48, 0xDD, 0xD6, 0xA1, 0x1E, 0xC4, 0x27, 0xBA, 0x37, 0x8A, + 0xAE, 0x89, 0xAA, 0xF5, 0x82, 0xCE, 0x1F, 0x4E, 0x32, 0x69, 0x0A, 0x55, 0x5E, 0x74, 0x07, + 0x61, 0xD3, 0x58, 0xAD, 0x4E, 0x92, 0xBC, 0x38, 0x41, 0x8A, 0xA7, 0x82, 0xDA, 0x91, 0x65, + 0x24, 0xFB, 0x09, 0xAB, 0x2C, 0xA6, 0xB3, 0xD3, 0x11, 0x3D, 0x6F, 0x2C, 0x2A, 0x6A, 0x9B, + 0x9D, 0x29, 0xD4, 0xE7, 0x48, 0x92, 0x55, 0x25, 0x2A, 0xF0, 0x75, 0xCB, 0xF9, 0xFE, 0xAC, + 0xED, 0xAE, 0x6F, 0x3E, 0xC0, 0xB0, 0x70, 0x82, 0x46, 0x89, 0xDD, 0x3C, 0x78, 0xAC, 0x14, + 0x3E, 0xD6, 0x77, 0x6D, 0x95, 0xDD, 0x8F, 0x13, 0xD4, 0x35, 0xA2, 0x90, 0xBD, 0xCA, 0x4C, + 0x11, 0x31, 0x8E, 0x5A, 0xCC, 0xE0, 0x44, 0x69, 0x64, 0x4E, 0x13, 0x74, 0xA9, 0x45, 0x1B, + 0x62, 0x04, 0xF3, 0xB3, 0x96, 0x1B, 0x7D, 0xD2, 0x39, 0xE3, 0x06, 0xFE, 0xF5, 0xF4, 0xF4, + 0xE5, 0x1B, 0x78, 0xB0, 0xFB, 0x9D, 0xCE, 0xE6, 0x9C, 0x3E, 0x79, 0x0B, 0x23, 0x1F, 0x2E, + 0x65, 0xFD, 0x1A, 0xB1, 0xC2, 0xA7, 0x5B, 0x07, 0x06, 0x7D, 0x5C, 0x16, 0xDD, 0xE0, 0x09, + 0x83, 0xA5, 0x8F, 0xFC, 0xDA, 0xAA, 0xEE, 0x16, 0xD2, 0x74, 0x2E, 0x13, 0x3E, 0xD7, 0x37, + 0xB4, 0x80, 0x64, 0xC8, 0xA3, 0x8E, 0xCA, 0x35, 0xAB, 0x3F, 0xA1, 0x8F, 0x6D, 0x62, 0xF6, + 0x42, 0xB1, 0x2C, 0xFD, 0xC7, 0x98, 0x0F, 0x2A, 0xB7, 0xDB, 0x32, 0x1F, 0xEC, 0x9D, 0xCF, + 0xE4, 0x99, 0xB4, 0xFC, 0x1E, 0xE7, 0xEB, 0x29, 0x79, 0x54, 0x05, 0x66, 0x17, 0xC6, 0x0A, + 0x66, 0x40, 0xB9, 0x28, 0x35, 0xD1, 0x65, 0xC3, 0xC0, 0x0A, 0x95, 0x19, 0x52, 0x61, 0x44, + 0x88, 0xD5, 0x65, 0x7B, 0xA0, 0xB5, 0xE9, 0x0A, 0xE9, 0xE0, 0xEF, 0x7B, 0x3B, 0x9E, 0xCA, + 0xEB, 0xD8, 0x1B, 0x85, 0x51, 0xB6, 0xD7, 0x0E, 0x83, 0x5B, 0x27, 0x34, 0x76, 0x16, 0x39, + 0xD4, 0x2E, 0x76, 0xFF, 0xC5, 0xB3, 0x27, 0x2B, 0x61, 0xC8, 0x96, 0xB4, 0x5B, 0x4B, 0xD1, + 0x8F, 0x30, 0xE5, 0x8C, 0x44, 0x06, 0x43, 0xBA, 0x15, 0x92, 0x21, 0xCC, 0x67, 0x39, 0xA1, + 0x9A, 0x65, 0xF2, 0x91, 0x1F, 0xAE, 0x47, 0xB0, 0xD4, 0xCA, 0xC4, 0x20, 0x0A, 0x6F, 0x04, + 0x3B, 0x17, 0xA0, 0x3A, 0xD3, 0x93, 0xEC, 0xB8, 0x23, 0xED, 0x03, 0xC8, 0xB6, 0xCD, 0x68, + 0x16, 0x7E, 0x6C, 0x82, 0x34, 0xF7, 0x43, 0x25, 0x57, 0xDB, 0x27, 0x20, 0x79, 0xEE, 0x89, + 0x9A, 0xED, 0xE7, 0x3B, 0x6B, 0x98, 0xD6, 0x00, 0x3F, 0x45, 0x78, 0x9A, 0x14, 0x1B, 0x60, + 0xD6, 0xDB, 0x40, 0xCD, 0x2A, 0x59, 0x74, 0x57, 0x1A, 0x4A, 0xD3, 0x66, 0x7B, 0x88, 0x93, + 0x18, 0xBA, 0x60, 0x28, 0x5D, 0x90, 0x3A, 0x2E, 0xAC, 0x01, 0xC2, 0x16, 0x08, 0x83, 0x8C, + 0x40, 0x90, 0x7D, 0xE6, 0xBB, 0xAB, 0xE0, 0x42, 0xCF, 0x2E, 0xCD, 0xD9, 0x7F, 0x54, 0x9F, + 0x95, 0xEC, 0x69, 0x8D, 0x79, 0x22, 0x2C, 0x65, 0xBA, 0x27, 0xC3, 0x0D, 0x33, 0x2A, 0x68, + 0xD0, 0x57, 0xAE, 0xCD, 0xC9, 0x38, 0x8A, 0xA3, 0x43, 0x20, 0xE0, 0xAA, 0x74, 0xFD, 0xBD, + 0x4D, 0x1B, 0x64, 0x3C, 0xAC, 0xE2, 0x16, 0xB6, 0xD8, 0xAD, 0x8F, 0x07, 0xA9, 0x99, 0x55, + 0xBF, 0xDB, 0x74, 0x3A, 0x86, 0xB4, 0x0F, 0xC6, 0x15, 0x27, 0xBA, 0xCA, 0x43, 0x4A, 0xC2, + 0xA7, 0xFB, 0xEA, 0xA7, 0x71, 0x11, 0xDC, 0x80, 0x98, 0xB1, 0x7E, 0x80, 0x0F, 0x59, 0xDD, + 0x77, 0xCC, 0xB0, 0xE6, 0x77, 0x07, 0xE6, 0x01, 0x23, 0xD3, 0x34, 0xE0, 0x73, 0xA2, 0xF5, + 0xA1, 0x6F, 0xFB, 0xCD, 0x70, 0x13, 0x89, 0xAD, 0xD5, 0x7C, 0x3C, 0xEC, 0xCB, 0x88, 0xB2, + 0x86, 0xAC, 0x1E, 0x6E, 0x3E, 0x64, 0x85, 0xAF, 0x1A, 0x12, 0xEA, 0x24, 0x1D, 0x14, 0xA1, + 0xB5, 0x00, 0x3D, 0x7F, 0x3B, 0xC9, 0xE9, 0x57, 0xD4, 0x48, 0x3C, 0x0F, 0x9F, 0x70, 0x3B, + 0x3A, 0x18, 0x7D, 0x55, 0xE5, 0x05, 0x81, 0x76, 0x15, 0xFB, 0xC4, 0xAE, 0x08, 0x37, 0x61, + 0x61, 0x84, 0x24, 0x5C, 0xFB, 0xA6, 0x1C, 0xE3, 0xB9, 0x29, 0xE3, 0x3F, 0x52, 0xB7, 0x1C, + 0xDD, 0x7B, 0x6A, 0x0D, 0xA5, 0x5C, 0x1F, 0x99, 0x75, 0x10, 0xB1, 0xA9, 0x00, 0x2C, 0xA4, + 0xE0, 0x67, 0x83, 0x73, 0xA3, 0xB1, 0xAB, 0x28, 0x97, 0xE6, 0xB4, 0x23, 0xF1, 0x5A, 0x44, + 0x0A, 0x63, 0x6C, 0xC8, 0x61, 0x49, 0x1E, 0xF4, 0x1A, 0xD0, 0xAA, 0x62, 0x7D, 0x8E, 0x19, + 0x8A, 0x5E, 0xE7, 0xBD, 0x7B, 0x6C, 0xB2, 0xC9, 0xCE, 0x2A, 0x8C, 0xC0, 0x15, 0xF0, 0xD2, + 0x06, 0xDE, 0x4C, 0x49, 0xE2, 0xF8, 0x7F, 0x31, 0x09, 0x54, 0xA1, 0x0D, 0x86, 0xE2, 0x94, + 0xF7, 0x42, 0xEE, 0x18, 0x6F, 0x4A, 0xE9, 0x81, 0x5F, 0x69, 0x96, 0x22, 0x79, 0x22, 0x06, + 0xCA, 0xFB, 0xA8, 0xF5, 0x62, 0x17, 0x38, 0x16, 0x0E, 0x6C, 0x5D, 0x61, 0x1A, 0x82, 0x52, + 0xC6, 0xF3, 0x50, 0x85, 0xB6, 0x04, 0xEF, 0x89, 0x51, 0x64, 0xD4, 0xEA, 0x6D, 0xDD, 0x31, + 0x0C, 0x7D, 0x8F, 0x0C, 0x87, 0x9F, 0xB1, 0xF8, 0x84, 0xC5, 0x74, 0x1D, 0x09, 0x6B, 0x3D, + 0x2D, 0xA0, 0xCE, 0x11, 0x51, 0x79, 0x0D, 0xDA, 0x88, 0x1D, 0x18, 0xCB, 0x6B, 0x19, 0xA9, + 0xFE, 0xD6, 0xF5, 0x25, 0x4B, 0x7D, 0x52, 0xD5, 0xD9, 0x2B, 0xBB, 0xE2, 0x4C, 0x9D, 0x6A, + 0x65, 0x60, 0x4A, 0x0B, 0x8E, 0xD2, 0x4A, 0xD5, 0xC1, 0x97, 0xD6, 0x83, 0xF5, 0x98, 0x74, + 0x3C, 0x96, 0xB5, 0x96, 0x0E, 0x87, 0x23, 0x73, 0x2B, 0x5B, 0xD6, 0x47, 0xE9, 0xDB, 0xEA, + 0xA8, 0x51, 0xD0, 0xE1, 0xCF, 0x6D, 0x2C, 0x07, 0x0D, 0x44, 0x42, 0x76, 0x2C, 0x28, 0x09, + 0x8C, 0x5C, 0xF5, 0xA5, 0x4B, 0x2B, 0x5E, 0x69, 0xA9, 0x9B, 0x10, 0x81, 0x5B, 0xF0, 0xF4, + 0x77, 0xBB, 0x71, 0xF0, 0xD5, 0xD3, 0xA6, 0x2B, 0xA2, 0xB3, 0xE2, 0x9B, 0xF8, 0x4D, 0x4B, + 0x4E, 0x57, 0x47, 0x07, 0xF5, 0xF7, 0x4A, 0xF7, 0x04, 0xD2, 0x77, 0xBD, 0x6C, 0xA3, 0x8D, + 0xA2, 0x1E, 0x2C, 0xDA, 0xC5, 0x49, 0xE5, 0xEA, 0xE1, 0xDE, 0x7A, 0x18, 0xEE, 0x53, 0x4C, + 0x8C, 0x22, 0x91, 0xC9, 0x08, 0xCA, 0xAB, 0xF1, 0x59, 0xE9, 0x0E, 0x65, 0x49, 0xDB, 0x94, + 0xBA, 0x7A, 0x3F, 0x3D, 0x97, 0xDD, 0x39, 0x8A, 0x75, 0xDF, 0x5B, 0x1A, 0x7C, 0xDF, 0xB2, + 0x54, 0x10, 0xB7, 0xEF, 0xC4, 0xED, 0x00, 0xD9, 0x99, 0x5B, 0x37, 0xB5, 0x8B, 0xF9, 0x1E, + 0xD7, 0xA3, 0x51, 0x0C, 0xFF, 0xEA, 0x82, 0xF9, 0xE1, 0xC2, 0xA3, 0x29, 0x04, 0x06, 0x00, + 0x4D, 0x09, 0x05, 0x7D, 0x63, 0xB7, 0x70, 0xFA, 0x0E, 0x53, 0x10, 0x31, 0x99, 0x54, 0x4E, + 0xBA, 0x66, 0x2A, 0x2C, 0x30, 0x2C, 0xF3, 0x90, 0x08, 0xF1, 0x42, 0xD2, 0xB1, 0x69, 0x63, + 0xE9, 0x5A, 0xB1, 0x0B, 0xE7, 0xC2, 0x61, 0x01, 0x68, 0x60, 0x8F, 0x35, 0x3A, 0x2F, 0x2C, + 0x41, 0xC7, 0x05, 0x6D, 0xEC, 0x1A, 0x8C, 0x7A, 0x6B, 0xFA, 0x00, 0x27, 0xF9, 0xDE, 0xDA, + 0xCB, 0x77, 0x86, 0xB6, 0x7E, 0xA2, 0xC4, 0x94, 0xD4, 0x3B, 0xA8, 0x51, 0xCF, 0x94, 0x15, + 0xC1, 0xBC, 0xC5, 0x2F, 0x02, 0x7E, 0xC0, 0x2C, 0x65, 0x53, 0x4F, 0x60, 0x8E, 0x9D, 0x16, + 0x6D, 0x51, 0xDD, 0x43, 0x1C, 0xDF, 0x58, 0x71, 0xF5, 0xCD, 0xD1, 0x57, 0x9C, 0xC0, 0x60, + 0x79, 0xDF, 0x07, 0x5A, 0x25, 0x06, 0x2B, 0xA7, 0xE7, 0x0D, 0x96, 0x66, 0xC4, 0xE7, 0xFE, + 0xD3, 0x4C, 0xEA, 0x0E, 0xA0, 0xF1, 0x1A, 0xDE, 0x1E, 0xB2, 0xA9, 0xB3, 0x97, 0xBC, 0xAA, + 0xAD, 0x10, 0x61, 0x27, 0x0E, 0xCF, 0x49, 0x78, 0x03, 0xA5, 0xFC, 0xE7, 0xF4, 0x1E, 0x65, + 0x04, 0xFB, 0xEC, 0x71, 0xA7, 0xDE, 0x7D, 0x06, 0x6B, 0x82, 0x61, 0x86, 0x8A, 0xFC, 0x49, + 0xB9, 0xE6, 0x85, 0xF0, 0xDC, 0xCE, 0x75, 0xE2, 0xFC, 0xB3, 0xBA, 0x8C, 0xF1, 0x90, 0x57, + 0xE3, 0x94, 0x15, 0x76, 0xBA, 0xF5, 0x8F, 0xB8, 0x21, 0xBD, 0x42, 0x68, 0xF7, 0xFA, 0xE3, + 0x02, 0x86, 0x01, 0xDA, 0x02, 0x2E, 0x9B, 0x46, 0x86, 0x46, 0xAB, 0xDB, 0x4F, 0xA6, 0x09, + 0x8A, 0x44, 0x9B, 0x42, 0x67, 0xD5, 0x09, 0xD9, 0xA3, 0x3F, 0x4C, 0x3E, 0xBC, 0xC3, 0x2D, + 0xAC, 0x09, 0x4D, 0x48, 0xED, 0x60, 0x0E, 0x76, 0x57, 0x87, 0xFB, 0x92, 0xB1, 0x97, 0x4F, + 0x74, 0xF7, 0xBB, 0x4C, 0x66, 0xEB, 0x2B, 0xBD, 0x02, 0x89, 0x5E, 0x6A, 0x38, 0x1C, 0x1C, + 0x45, 0x2E, 0xAA, 0xB1, 0xAE, 0x47, 0x31, 0xCF, 0x63, 0x2F, 0x61, 0xAE, 0x2C, 0x90, 0x59, + 0x21, 0x17, 0x4A, 0x3B, 0xC9, 0xBB, 0x4C, 0xDC, 0x89, 0xD6, 0x30, 0x26, 0x4B, 0x61, 0x49, + 0x88, 0xF3, 0xAB, 0xBE, 0xA1, 0xBD, 0x61, 0x7F, 0xFA, 0x53, 0xD7, 0x1B, 0x7D, 0x8A, 0x37, + 0x14, 0x62, 0xB7, 0x73, 0x35, 0x1A, 0x2D, 0xCC, 0xAE, 0xDD, 0x7F, 0x59, 0xCD, 0x72, 0x8F, + 0xAD, 0xEE, 0x05, 0x90, 0x67, 0xBD, 0x80, 0xC9, 0x4C, 0x8C, 0x9A, 0x1F, 0xFC, 0xA2, 0xDC, + 0x4F, 0x84, 0x8B, 0x82, 0x9C, 0x05, 0x61, 0x38, 0x5A, 0xA8, 0x2C, 0xC9, 0x85, 0x03, 0xD0, + 0xBB, 0x66, 0xA6, 0xAA, 0x4F, 0xAE, 0x07, 0x03, 0xD1, 0x2E, 0x60, 0xE1, 0x46, 0x0E, 0xFB, + 0xBC, 0xDF, 0x24, 0x12, 0xC1, 0x3E, 0x7C, 0x68, 0x4D, 0x1B, 0x01, 0x10, 0x20, 0x26, 0x34, + 0x3A, 0x41, 0x43, 0x44, 0x58, 0x5F, 0x6E, 0x70, 0x72, 0x74, 0x8B, 0xAE, 0xB5, 0xBB, 0xC6, + 0xD1, 0xE2, 0xEF, 0xFB, 0xFE, 0x06, 0x0E, 0x2E, 0x3E, 0x51, 0x60, 0x79, 0x7C, 0x9E, 0xA6, + 0xBA, 0xC7, 0xF1, 0x10, 0x24, 0x40, 0x4A, 0x52, 0x57, 0x5F, 0x6C, 0x89, 0x8C, 0x97, 0xAA, + 0xB2, 0xC3, 0xCC, 0xEA, 0xF2, 0x2F, 0x3F, 0x53, 0x5F, 0x7B, 0x81, 0x83, 0x96, 0xA1, 0xB1, + 0xBC, 0xE6, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x25, 0x36, 0x42, + ]; if MLDSA44::verify(&pk, msg, None, &sig).is_ok() { println!("Verification succeeded!"); @@ -360,8 +1717,8 @@ fn bench_mldsa44_lowmemory_verify() { } fn bench_mldsa65_verify() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey}; use bouncycastle::hex; + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey, MLDSATrait}; eprintln!("MLDSA65/Verify"); @@ -381,7 +1738,140 @@ fn bench_mldsa65_verify() { // let sig = MLDSA65::sign_mu_deterministic(&mldsa65_sk, &mu, [0u8; 32]).unwrap(); // eprintln!("sig:\n{}", &*hex::encode(sig)); - let mldsa65_pk = MLDSA65PublicKey::from_bytes(&[0x48,0x68,0x3d,0x91,0x97,0x8e,0x31,0xeb,0x3d,0xdd,0xb8,0xb0,0x47,0x34,0x82,0xd2,0xb8,0x8a,0x5f,0x62,0x59,0x49,0xfd,0x8f,0x58,0xa5,0x61,0xe6,0x96,0xbd,0x4c,0x27,0xd0,0x5b,0x38,0xdb,0xb2,0xed,0xf0,0x1e,0x66,0x4e,0xfd,0x81,0xbe,0x1e,0xa8,0x93,0x68,0x8c,0xe6,0x8a,0xa2,0xd5,0x1c,0x59,0x58,0xf8,0xbb,0xc6,0xeb,0x4e,0x89,0xee,0x67,0xd2,0xc0,0x32,0x09,0x54,0xd5,0x72,0x12,0xca,0xc7,0x22,0x9f,0xf1,0xd6,0xea,0xf0,0x39,0x28,0xbd,0x51,0x51,0x1f,0x8d,0x88,0xd8,0x47,0x73,0x6c,0x7d,0xe2,0x73,0x0d,0x59,0x78,0xe5,0x41,0x07,0x13,0x16,0x09,0x78,0x86,0x77,0x11,0xbf,0x55,0x39,0xa0,0xbf,0xc4,0xc3,0x50,0xc2,0xbe,0x57,0x2b,0xaf,0x0e,0xe2,0xe2,0xfb,0x16,0xcc,0xfe,0xa0,0x80,0x28,0xd9,0x9a,0xc4,0x9a,0xeb,0xb7,0x59,0x37,0xdd,0xce,0x11,0x1c,0xda,0xb6,0x2f,0xff,0x3c,0xea,0x8b,0xa2,0x23,0x3d,0x1e,0x56,0xfb,0xc5,0xc5,0xa1,0xe7,0x26,0xde,0x63,0xfa,0xdd,0x2a,0xf0,0x16,0xb1,0x19,0x17,0x7f,0xa3,0xd9,0x71,0xa2,0xd9,0x27,0x71,0x73,0xfc,0xe5,0x5b,0x67,0x74,0x5a,0xf0,0xb7,0xc2,0x1d,0x59,0x7d,0xbe,0xb9,0x3e,0x6a,0x32,0xf3,0x41,0xc4,0x9a,0x5a,0x8b,0xe9,0xe8,0x25,0x08,0x8d,0x1f,0x2a,0xa4,0x51,0x55,0xd6,0xc8,0xae,0x15,0x36,0x7e,0x4e,0xb0,0x03,0xb8,0xfd,0xf7,0x85,0x10,0x71,0x94,0x97,0x39,0xf9,0xff,0xf0,0x90,0x23,0xea,0xf4,0x51,0x04,0xd2,0xa8,0x4a,0x45,0x90,0x6e,0xed,0x46,0x71,0xa4,0x4d,0xc2,0x8d,0x27,0x98,0x7b,0xb5,0x5d,0xf6,0x9e,0x9e,0x85,0x61,0xf6,0x1a,0x80,0xa7,0x26,0x99,0x50,0x38,0x65,0xfe,0xd9,0xb7,0xee,0x72,0xa8,0xe1,0x7a,0x19,0xc4,0x08,0x14,0x4f,0x4b,0x29,0xaf,0xef,0x70,0x31,0xc3,0xa6,0xd8,0x57,0x16,0x10,0xb4,0x2c,0x9f,0x42,0x12,0x45,0xa8,0x8f,0x19,0x7e,0x16,0x81,0x2b,0x03,0x11,0x59,0xb6,0x5b,0x96,0x87,0xe5,0xb3,0xe9,0x34,0xc5,0x22,0x5a,0xe9,0x8a,0x79,0xba,0x73,0xd2,0xb3,0x99,0xd7,0x35,0x10,0xef,0xfa,0xd1,0x9e,0x53,0xb8,0x45,0x0f,0x0b,0xa8,0xfc,0xe1,0x01,0x2f,0xd9,0x8d,0x26,0x0a,0x74,0xaa,0xaa,0x13,0xfa,0xe2,0x49,0xa0,0x06,0xb1,0xc3,0x4f,0x5b,0xa0,0xb8,0x82,0xf2,0x63,0x78,0x22,0x2f,0xb3,0x6f,0x22,0x83,0xc2,0x43,0xf0,0xff,0xeb,0x5f,0x1b,0xb4,0x14,0xa0,0xa7,0x0d,0x55,0xe3,0xd4,0x0a,0x56,0xb6,0xcb,0xc8,0x8a,0xe1,0xf0,0x3b,0x7b,0x28,0x82,0xd9,0x8d,0xee,0xa2,0x8e,0x14,0x5c,0x9d,0xed,0xfd,0x8e,0xaf,0x1c,0xef,0x2e,0xd9,0x4a,0x8b,0x05,0x0f,0x89,0x64,0xf4,0x6d,0x1e,0xa0,0xd0,0xc2,0xa4,0x3e,0x0d,0xda,0x61,0x82,0xad,0xbf,0x4f,0x6e,0xd1,0x75,0xb6,0x74,0x22,0x57,0x85,0x9b,0xf2,0x2f,0x3a,0x41,0x7e,0xcf,0x1f,0x9d,0x89,0x31,0x7b,0x5e,0x53,0x9d,0x58,0x7a,0xf1,0x6b,0x9e,0x13,0x13,0xe0,0x45,0x14,0xff,0xa6,0x4b,0xa8,0xb3,0xff,0x2b,0x83,0x21,0xf8,0x81,0x1c,0xb3,0xfb,0x02,0x2c,0x8f,0x64,0x4e,0x70,0xa4,0xb8,0x0a,0x2f,0xbf,0xee,0x60,0x4a,0xbb,0x73,0x79,0x09,0x1e,0xa8,0xe6,0xc5,0xc7,0x4d,0xfc,0x02,0x83,0x66,0x6b,0x40,0xc0,0x79,0x38,0x70,0x02,0x82,0x04,0xa1,0x36,0xbf,0x5d,0xa9,0x56,0x8e,0xb7,0x98,0xd3,0x49,0x03,0x8b,0xdb,0x0c,0x11,0xe0,0x34,0x45,0xe7,0x84,0x7c,0xb5,0x06,0x9c,0x75,0xcf,0x28,0xac,0x60,0x1c,0x77,0x99,0xd9,0x58,0x21,0x0d,0xdb,0xcb,0x22,0x6e,0x51,0xaf,0xef,0x9f,0x1d,0xe4,0x7b,0x07,0x38,0x73,0xd6,0xd3,0xf9,0x74,0x56,0xbe,0xde,0x08,0x50,0x82,0xe7,0x4a,0x29,0x8b,0x2c,0xd4,0x8f,0x4b,0x30,0x93,0x15,0x5f,0x36,0x6c,0x8f,0xa6,0x01,0xc6,0xaf,0x85,0x8d,0xfa,0x32,0xc0,0x84,0x91,0xb2,0xa2,0x98,0x87,0xf9,0x03,0x35,0x94,0x9a,0x5d,0x6e,0xda,0xa6,0x79,0x88,0x2a,0x3a,0x95,0xd6,0xbf,0x6d,0x97,0x0a,0x22,0x1f,0x4b,0x9d,0x3d,0x8c,0xbf,0x38,0x4a,0xf8,0x1a,0xac,0x95,0xe2,0xb3,0x29,0x4e,0x04,0x78,0x9a,0xc8,0x37,0x27,0xa5,0xdc,0x04,0x55,0x9f,0x96,0xaf,0x41,0xd8,0xa0,0x53,0x51,0x6f,0xee,0xee,0xbc,0x52,0x74,0x6e,0xb6,0xab,0x28,0x19,0xe0,0x91,0x08,0x71,0x0d,0x83,0x5f,0x01,0x1f,0xa6,0x30,0x65,0x87,0x2a,0xd3,0x34,0xd5,0xcd,0xff,0xb2,0xb2,0x31,0x05,0x07,0xe9,0x2f,0xc9,0x93,0xae,0x31,0x7d,0xa9,0x7f,0x4f,0x30,0x9c,0xda,0xf0,0xf6,0x7e,0xd9,0x9d,0x90,0x21,0x55,0x76,0x08,0x38,0x49,0xf9,0x53,0xb2,0x46,0xd7,0xfe,0xdb,0x3f,0xdb,0x67,0x67,0x98,0x50,0xa5,0xad,0x40,0x4e,0x64,0x14,0x7f,0xb7,0xcf,0x4f,0x6a,0xed,0xdd,0x05,0xaf,0xb4,0xb8,0x34,0x96,0x8d,0x1f,0xe8,0x80,0x14,0x96,0x0d,0xce,0x5d,0x94,0x22,0x36,0x52,0x6e,0x12,0xa4,0x78,0xd6,0x9e,0x5f,0xbe,0x69,0x70,0x31,0x0b,0x30,0x8c,0x06,0x84,0x50,0x18,0xcf,0xc7,0xb2,0xab,0x43,0x0a,0x13,0xa6,0xb1,0xac,0x7b,0xb0,0x2c,0xcc,0xbb,0x3d,0x91,0x1a,0xc2,0xf1,0x10,0x68,0x61,0x3f,0xbe,0x02,0x9b,0xfd,0xce,0x02,0xcf,0x5c,0xd3,0x89,0x50,0xed,0x72,0xc8,0x39,0x44,0xed,0xfb,0xc7,0x56,0x15,0xaf,0x87,0xf8,0x64,0xc0,0x51,0xf3,0xc5,0x54,0x56,0xc5,0x41,0x28,0x63,0xa4,0x0c,0x06,0xd1,0xda,0xb5,0x62,0xbd,0xff,0x05,0x71,0xb8,0xd3,0xc3,0x91,0x7b,0xbd,0x30,0x08,0x80,0xbb,0xa5,0xe9,0x98,0x23,0x9b,0x95,0xfa,0x91,0xb7,0xd6,0x41,0x6d,0x4f,0x39,0x8b,0x3a,0xdb,0xcd,0x30,0x98,0x3e,0xd3,0x59,0x2b,0x4d,0x9e,0xf7,0xd4,0x23,0x6f,0xd0,0x0f,0x50,0xd9,0x8a,0xa5,0x3a,0x23,0x5a,0xc4,0x17,0x27,0x20,0xf7,0x7d,0x96,0x17,0x26,0x72,0x98,0x0c,0xfe,0x8f,0xf7,0xa5,0xa7,0x02,0x78,0x3e,0xdc,0x2b,0xa3,0x1b,0x22,0x59,0x01,0x5a,0x11,0x2f,0xc7,0xf4,0x68,0xa9,0xc2,0xf9,0x46,0x40,0x39,0x00,0x2d,0x30,0xef,0x67,0x8b,0x4c,0xb7,0x98,0xbc,0x11,0x62,0x16,0xbf,0x7a,0x9a,0x7c,0x18,0xba,0x03,0xb7,0xb5,0x8f,0xd0,0x75,0x15,0xd3,0x11,0x50,0x49,0xd3,0x61,0x4b,0xe7,0xa0,0x7e,0x74,0x43,0x00,0x75,0x0d,0xf1,0xd2,0xc5,0x87,0x53,0x38,0x90,0x59,0xea,0xfc,0x3d,0x78,0x5c,0xcd,0xd3,0x1c,0x07,0x64,0x8b,0xed,0xc0,0x3a,0x5c,0x3b,0x8a,0xd4,0x6d,0x06,0x4d,0x59,0xc1,0x3d,0x57,0x37,0x47,0x29,0xfc,0x4e,0x29,0x53,0x62,0xe2,0xa5,0x19,0x12,0x04,0x53,0x04,0x28,0xbc,0x15,0x22,0xaf,0xa2,0x8f,0xf5,0xfe,0x16,0x55,0xe3,0x04,0xca,0x5b,0xc8,0xc2,0x7a,0xd0,0xe0,0xc6,0xa3,0x9d,0xd4,0xdf,0x28,0x95,0x6c,0x14,0xb3,0x8c,0xc9,0x36,0x82,0xce,0xfe,0x40,0x2b,0xbd,0x5e,0x82,0xd2,0x9c,0x46,0x4e,0x44,0xeb,0x5d,0x37,0xb4,0x8f,0xc5,0x68,0xdf,0xe0,0xcc,0x6e,0x8e,0x16,0xba,0xea,0x05,0xe5,0x13,0x55,0x90,0xf1,0x92,0x94,0xe7,0x3e,0x83,0x67,0xb0,0x21,0x6d,0xbb,0x81,0x50,0x30,0xb9,0xde,0x55,0x91,0x3f,0x08,0x03,0x9c,0x42,0x35,0x1c,0x59,0xe5,0x51,0x5d,0xd5,0xaf,0x8e,0x08,0x9a,0x15,0xe6,0x25,0xe8,0xf6,0xde,0xe6,0x39,0x38,0x6c,0x46,0x49,0x7d,0x7a,0x26,0x32,0x88,0x77,0x4d,0xe5,0x81,0xa7,0xde,0x96,0x29,0xb4,0x1b,0x44,0x24,0x14,0x1f,0x97,0x8f,0xb8,0x33,0x12,0x08,0xef,0xde,0xc3,0xc6,0xe0,0xde,0x39,0xbc,0x57,0x06,0x3f,0x3d,0xcd,0x6c,0x47,0x03,0x73,0xc0,0x88,0x91,0xea,0x29,0xcb,0xc7,0xcc,0x6d,0x64,0x83,0xb8,0x88,0x90,0x83,0xac,0xe8,0x6a,0xa7,0xb5,0x1b,0x1c,0x2c,0xfe,0x6e,0x2a,0xd1,0x8d,0x97,0xce,0x36,0xfb,0xc5,0x6e,0xa4,0x2f,0xae,0x97,0xe6,0xa7,0xac,0x11,0x48,0x64,0x47,0x8c,0x36,0x6d,0xf1,0xeb,0xb1,0xe7,0xb1,0x1a,0x90,0x98,0x50,0x4f,0xd5,0x97,0x5b,0xdf,0x1f,0x49,0xdc,0x70,0x00,0x2b,0x63,0xc1,0x73,0x9a,0x9d,0x26,0x3f,0xba,0xd4,0x07,0x3f,0x6a,0x9f,0x6c,0x2b,0x8a,0xf4,0xb4,0xc3,0x32,0xa1,0x03,0xa0,0xcf,0xfa,0x5d,0xee,0xb2,0xd0,0x62,0xca,0x3c,0x21,0x5f,0xd3,0x60,0x02,0x6b,0xe7,0xc5,0x16,0x4f,0x4a,0x44,0x24,0xef,0x74,0x94,0x88,0x04,0xd6,0x6f,0x46,0x48,0x77,0x32,0xc8,0x20,0x2c,0x79,0x54,0x78,0x64,0x7b,0x4e,0xa7,0x1d,0x62,0x7c,0x08,0x60,0x24,0xcc,0xa3,0x54,0xa4,0x1f,0x08,0x77,0xb3,0x8f,0x19,0xb3,0x77,0x4a,0xd2,0x09,0x5c,0x8d,0xa5,0x3b,0x06,0x9e,0x21,0xc7,0x6a,0xe2,0xd2,0x00,0x7e,0x16,0x71,0x9e,0xd4,0x00,0x80,0xd3,0x34,0xf7,0xda,0x52,0xe9,0xf5,0xa5,0x99,0x04,0x39,0xca,0xf0,0x83,0xa9,0x5b,0x83,0x3f,0x02,0xad,0x10,0xa0,0x8c,0x1a,0x6d,0x0f,0x26,0x0c,0x00,0x72,0x85,0xbd,0x4a,0x2f,0x47,0x70,0x3a,0x5a,0xef,0x46,0x52,0x87,0xd2,0x53,0xb1,0x8a,0xc2,0x25,0x14,0x31,0x62,0x10,0xff,0x56,0x68,0x14,0xb1,0x0f,0x87,0xa2,0x93,0xd6,0xf1,0x99,0xd3,0xc3,0x95,0x99,0x90,0xd0,0xc1,0x26,0x8b,0x4f,0x50,0xd5,0xf9,0xfc,0xef,0xbb,0xf2,0x37,0xbd,0x0c,0x28,0xb8,0x01,0x82,0xd6,0x65,0x97,0x41,0xf1,0x4f,0x10,0xbf,0xbb,0x21,0xbb,0xa1,0x2a,0xb6,0x20,0xaa,0x23,0x96,0xf5,0x6c,0x06,0x86,0xb4,0xea,0x90,0x17,0x99,0x02,0x24,0x21,0x6b,0x2f,0xe8,0xad,0x76,0xc4,0xa9,0x14,0x8e,0xef,0x9a,0x86,0xa3,0x63,0x5a,0x6a,0xa7,0x7b,0xc1,0xdc,0xfb,0x6f,0xba,0x59,0xa7,0x7d,0xfd,0xa9,0xb7,0x53,0x0d,0xc0,0xca,0x86,0x48,0xc8,0xd9,0x73,0x73,0x8e,0x01,0xba,0xb8,0xf0,0x8b,0x49,0x05,0xe8,0x4a,0xa4,0x64,0x1b,0xd6,0x02,0x41,0x0c,0xd9,0x75,0x20,0x26,0x5f,0x2f,0x23,0x1f,0x2b,0x35,0xe1,0x5e,0xb2,0xfa,0x04,0xd2,0xbd,0x94,0xd5,0xa7,0x7a,0xba,0xf1,0xe0,0xe1,0x61,0x01,0x0a,0x99,0x00,0x87,0xf5,0xb4,0x6e,0xa9,0x88,0xb2,0xbc,0x05,0x12,0xfd,0xa0,0xfa,0x92,0x3d,0xad,0xd6,0xc4,0x5c,0x53,0x01,0xd0,0x94,0x83,0x67,0x32,0x65,0xb5,0xab,0x2e,0x10,0xf4,0xba,0x52,0x0f,0x6b,0xba,0xd5,0x64,0xa5,0xc3,0xd5,0xe2,0x7b,0xdb,0x08,0x0f,0x7d,0x20,0xe1,0x32,0x96,0xa3,0x18,0x19,0x54,0xc3,0x9c,0x64,0x9c,0x94,0x3e,0xbe,0x17,0xdf,0x5c,0x1f,0x7a,0xae,0x0a,0x8f,0xe1,0x26,0xc4,0x77,0x58,0x5a,0x5d,0x4d,0x64,0x8a,0x0d,0x00,0x8b,0x6a,0xf5,0xe8,0xcd,0x31,0xbe,0x69,0xa9,0x29,0x6d,0x4f,0x3f,0xd2,0x5e,0xd8,0x6f,0x22,0x1e,0x4b,0x93,0xf6,0x5f,0x59,0x29,0x96,0x75,0x33,0x62,0x4b,0x92,0x35,0x75,0x0c,0x30,0x70,0x75,0x50,0xb5,0x85,0x36,0xd1,0x09,0xa7,0x13,0x1c,0x5a,0x5b,0xbe,0x4a,0x57,0x15,0x56,0x7c,0x12,0x53,0x4a,0xec,0x76,0x60,0x76,0x1e,0xeb,0xb9,0xfa,0xe2,0x89,0x1c,0x77,0x45,0x89,0xb8,0x0e,0x56,0x6a,0xd5,0x57,0xdd,0xef,0x73,0x67,0x19,0x6b,0x72,0x27,0xea,0x98,0x70,0xef,0x09,0xdd,0xfe,0xc7,0x9d,0x6b,0x93,0x19,0xa6,0x87,0x9b,0x52,0x05,0xd7,0x6b,0xf7,0xab,0xa5,0xac,0xf3,0x3a,0xfb,0x59,0xd1,0x7f,0xc5,0x4e,0x68,0x38,0x3d,0x6b,0xe5,0xa0,0x8e,0x9b,0x66,0xda,0x53,0xdc,0xde,0x00,0x8b,0xb2,0x94,0xb8,0x58,0x2b,0xd1,0x32,0xcd,0xcc,0x49,0x95,0x9f,0xdb,0xc2,0x1e,0x52,0x72,0x18,0x80,0xc8,0xad,0x03,0x52,0xc7,0x9f,0x03,0xa4,0x3b,0xbd,0x84,0xc4,0xcd,0xfd,0xc6,0xc5,0x29,0x00,0x5e,0x1e,0x7c,0xd9,0xa3,0x49,0xa7,0x16,0x8a,0x35,0x56,0x9b,0xa5,0xde,0xa8,0x18,0x96,0x8d,0x5a,0x91,0x46,0x6b,0xd6,0xe6,0x4e,0x20,0xbf,0x62,0x41,0x71,0x98,0xaf,0xc4,0xe8,0x1c,0x28,0xdd,0x77,0xed,0x40,0x28,0x23,0x23,0x98,0xb5,0x2f,0xbd,0xe8,0x6b,0xc8,0x4f,0x47,0x5b,0x90,0x16,0x71,0x0c,0xe2,0xaa,0xbc,0x11,0xa0,0x6b,0x4d,0xba,0xc9,0x01,0xec,0x16,0xcf,0x36,0x5c,0xa3,0xf2,0xd5,0x38,0x13,0x94,0x8a,0x69,0x3a,0x0f,0x93,0xe7,0x9c,0x46,0xca,0x5d,0x5a,0x6d,0xca,0x3d,0x28,0xca,0x50,0xad,0x18,0xbd,0x13,0xfc,0xa5,0x50,0x59,0xdd,0x9b,0x18,0x5f,0x79,0xf9,0xc4,0x71,0x96,0xa4,0xe8,0x1b,0x21,0x04,0xbc,0x46,0x0a,0x05,0x1e,0x02,0xf2,0xe8,0x44,0x4f]).unwrap(); + let mldsa65_pk = MLDSA65PublicKey::from_bytes(&[ + 0x48, 0x68, 0x3D, 0x91, 0x97, 0x8E, 0x31, 0xEB, 0x3D, 0xDD, 0xB8, 0xB0, 0x47, 0x34, 0x82, + 0xD2, 0xB8, 0x8A, 0x5F, 0x62, 0x59, 0x49, 0xFD, 0x8F, 0x58, 0xA5, 0x61, 0xE6, 0x96, 0xBD, + 0x4C, 0x27, 0xD0, 0x5B, 0x38, 0xDB, 0xB2, 0xED, 0xF0, 0x1E, 0x66, 0x4E, 0xFD, 0x81, 0xBE, + 0x1E, 0xA8, 0x93, 0x68, 0x8C, 0xE6, 0x8A, 0xA2, 0xD5, 0x1C, 0x59, 0x58, 0xF8, 0xBB, 0xC6, + 0xEB, 0x4E, 0x89, 0xEE, 0x67, 0xD2, 0xC0, 0x32, 0x09, 0x54, 0xD5, 0x72, 0x12, 0xCA, 0xC7, + 0x22, 0x9F, 0xF1, 0xD6, 0xEA, 0xF0, 0x39, 0x28, 0xBD, 0x51, 0x51, 0x1F, 0x8D, 0x88, 0xD8, + 0x47, 0x73, 0x6C, 0x7D, 0xE2, 0x73, 0x0D, 0x59, 0x78, 0xE5, 0x41, 0x07, 0x13, 0x16, 0x09, + 0x78, 0x86, 0x77, 0x11, 0xBF, 0x55, 0x39, 0xA0, 0xBF, 0xC4, 0xC3, 0x50, 0xC2, 0xBE, 0x57, + 0x2B, 0xAF, 0x0E, 0xE2, 0xE2, 0xFB, 0x16, 0xCC, 0xFE, 0xA0, 0x80, 0x28, 0xD9, 0x9A, 0xC4, + 0x9A, 0xEB, 0xB7, 0x59, 0x37, 0xDD, 0xCE, 0x11, 0x1C, 0xDA, 0xB6, 0x2F, 0xFF, 0x3C, 0xEA, + 0x8B, 0xA2, 0x23, 0x3D, 0x1E, 0x56, 0xFB, 0xC5, 0xC5, 0xA1, 0xE7, 0x26, 0xDE, 0x63, 0xFA, + 0xDD, 0x2A, 0xF0, 0x16, 0xB1, 0x19, 0x17, 0x7F, 0xA3, 0xD9, 0x71, 0xA2, 0xD9, 0x27, 0x71, + 0x73, 0xFC, 0xE5, 0x5B, 0x67, 0x74, 0x5A, 0xF0, 0xB7, 0xC2, 0x1D, 0x59, 0x7D, 0xBE, 0xB9, + 0x3E, 0x6A, 0x32, 0xF3, 0x41, 0xC4, 0x9A, 0x5A, 0x8B, 0xE9, 0xE8, 0x25, 0x08, 0x8D, 0x1F, + 0x2A, 0xA4, 0x51, 0x55, 0xD6, 0xC8, 0xAE, 0x15, 0x36, 0x7E, 0x4E, 0xB0, 0x03, 0xB8, 0xFD, + 0xF7, 0x85, 0x10, 0x71, 0x94, 0x97, 0x39, 0xF9, 0xFF, 0xF0, 0x90, 0x23, 0xEA, 0xF4, 0x51, + 0x04, 0xD2, 0xA8, 0x4A, 0x45, 0x90, 0x6E, 0xED, 0x46, 0x71, 0xA4, 0x4D, 0xC2, 0x8D, 0x27, + 0x98, 0x7B, 0xB5, 0x5D, 0xF6, 0x9E, 0x9E, 0x85, 0x61, 0xF6, 0x1A, 0x80, 0xA7, 0x26, 0x99, + 0x50, 0x38, 0x65, 0xFE, 0xD9, 0xB7, 0xEE, 0x72, 0xA8, 0xE1, 0x7A, 0x19, 0xC4, 0x08, 0x14, + 0x4F, 0x4B, 0x29, 0xAF, 0xEF, 0x70, 0x31, 0xC3, 0xA6, 0xD8, 0x57, 0x16, 0x10, 0xB4, 0x2C, + 0x9F, 0x42, 0x12, 0x45, 0xA8, 0x8F, 0x19, 0x7E, 0x16, 0x81, 0x2B, 0x03, 0x11, 0x59, 0xB6, + 0x5B, 0x96, 0x87, 0xE5, 0xB3, 0xE9, 0x34, 0xC5, 0x22, 0x5A, 0xE9, 0x8A, 0x79, 0xBA, 0x73, + 0xD2, 0xB3, 0x99, 0xD7, 0x35, 0x10, 0xEF, 0xFA, 0xD1, 0x9E, 0x53, 0xB8, 0x45, 0x0F, 0x0B, + 0xA8, 0xFC, 0xE1, 0x01, 0x2F, 0xD9, 0x8D, 0x26, 0x0A, 0x74, 0xAA, 0xAA, 0x13, 0xFA, 0xE2, + 0x49, 0xA0, 0x06, 0xB1, 0xC3, 0x4F, 0x5B, 0xA0, 0xB8, 0x82, 0xF2, 0x63, 0x78, 0x22, 0x2F, + 0xB3, 0x6F, 0x22, 0x83, 0xC2, 0x43, 0xF0, 0xFF, 0xEB, 0x5F, 0x1B, 0xB4, 0x14, 0xA0, 0xA7, + 0x0D, 0x55, 0xE3, 0xD4, 0x0A, 0x56, 0xB6, 0xCB, 0xC8, 0x8A, 0xE1, 0xF0, 0x3B, 0x7B, 0x28, + 0x82, 0xD9, 0x8D, 0xEE, 0xA2, 0x8E, 0x14, 0x5C, 0x9D, 0xED, 0xFD, 0x8E, 0xAF, 0x1C, 0xEF, + 0x2E, 0xD9, 0x4A, 0x8B, 0x05, 0x0F, 0x89, 0x64, 0xF4, 0x6D, 0x1E, 0xA0, 0xD0, 0xC2, 0xA4, + 0x3E, 0x0D, 0xDA, 0x61, 0x82, 0xAD, 0xBF, 0x4F, 0x6E, 0xD1, 0x75, 0xB6, 0x74, 0x22, 0x57, + 0x85, 0x9B, 0xF2, 0x2F, 0x3A, 0x41, 0x7E, 0xCF, 0x1F, 0x9D, 0x89, 0x31, 0x7B, 0x5E, 0x53, + 0x9D, 0x58, 0x7A, 0xF1, 0x6B, 0x9E, 0x13, 0x13, 0xE0, 0x45, 0x14, 0xFF, 0xA6, 0x4B, 0xA8, + 0xB3, 0xFF, 0x2B, 0x83, 0x21, 0xF8, 0x81, 0x1C, 0xB3, 0xFB, 0x02, 0x2C, 0x8F, 0x64, 0x4E, + 0x70, 0xA4, 0xB8, 0x0A, 0x2F, 0xBF, 0xEE, 0x60, 0x4A, 0xBB, 0x73, 0x79, 0x09, 0x1E, 0xA8, + 0xE6, 0xC5, 0xC7, 0x4D, 0xFC, 0x02, 0x83, 0x66, 0x6B, 0x40, 0xC0, 0x79, 0x38, 0x70, 0x02, + 0x82, 0x04, 0xA1, 0x36, 0xBF, 0x5D, 0xA9, 0x56, 0x8E, 0xB7, 0x98, 0xD3, 0x49, 0x03, 0x8B, + 0xDB, 0x0C, 0x11, 0xE0, 0x34, 0x45, 0xE7, 0x84, 0x7C, 0xB5, 0x06, 0x9C, 0x75, 0xCF, 0x28, + 0xAC, 0x60, 0x1C, 0x77, 0x99, 0xD9, 0x58, 0x21, 0x0D, 0xDB, 0xCB, 0x22, 0x6E, 0x51, 0xAF, + 0xEF, 0x9F, 0x1D, 0xE4, 0x7B, 0x07, 0x38, 0x73, 0xD6, 0xD3, 0xF9, 0x74, 0x56, 0xBE, 0xDE, + 0x08, 0x50, 0x82, 0xE7, 0x4A, 0x29, 0x8B, 0x2C, 0xD4, 0x8F, 0x4B, 0x30, 0x93, 0x15, 0x5F, + 0x36, 0x6C, 0x8F, 0xA6, 0x01, 0xC6, 0xAF, 0x85, 0x8D, 0xFA, 0x32, 0xC0, 0x84, 0x91, 0xB2, + 0xA2, 0x98, 0x87, 0xF9, 0x03, 0x35, 0x94, 0x9A, 0x5D, 0x6E, 0xDA, 0xA6, 0x79, 0x88, 0x2A, + 0x3A, 0x95, 0xD6, 0xBF, 0x6D, 0x97, 0x0A, 0x22, 0x1F, 0x4B, 0x9D, 0x3D, 0x8C, 0xBF, 0x38, + 0x4A, 0xF8, 0x1A, 0xAC, 0x95, 0xE2, 0xB3, 0x29, 0x4E, 0x04, 0x78, 0x9A, 0xC8, 0x37, 0x27, + 0xA5, 0xDC, 0x04, 0x55, 0x9F, 0x96, 0xAF, 0x41, 0xD8, 0xA0, 0x53, 0x51, 0x6F, 0xEE, 0xEE, + 0xBC, 0x52, 0x74, 0x6E, 0xB6, 0xAB, 0x28, 0x19, 0xE0, 0x91, 0x08, 0x71, 0x0D, 0x83, 0x5F, + 0x01, 0x1F, 0xA6, 0x30, 0x65, 0x87, 0x2A, 0xD3, 0x34, 0xD5, 0xCD, 0xFF, 0xB2, 0xB2, 0x31, + 0x05, 0x07, 0xE9, 0x2F, 0xC9, 0x93, 0xAE, 0x31, 0x7D, 0xA9, 0x7F, 0x4F, 0x30, 0x9C, 0xDA, + 0xF0, 0xF6, 0x7E, 0xD9, 0x9D, 0x90, 0x21, 0x55, 0x76, 0x08, 0x38, 0x49, 0xF9, 0x53, 0xB2, + 0x46, 0xD7, 0xFE, 0xDB, 0x3F, 0xDB, 0x67, 0x67, 0x98, 0x50, 0xA5, 0xAD, 0x40, 0x4E, 0x64, + 0x14, 0x7F, 0xB7, 0xCF, 0x4F, 0x6A, 0xED, 0xDD, 0x05, 0xAF, 0xB4, 0xB8, 0x34, 0x96, 0x8D, + 0x1F, 0xE8, 0x80, 0x14, 0x96, 0x0D, 0xCE, 0x5D, 0x94, 0x22, 0x36, 0x52, 0x6E, 0x12, 0xA4, + 0x78, 0xD6, 0x9E, 0x5F, 0xBE, 0x69, 0x70, 0x31, 0x0B, 0x30, 0x8C, 0x06, 0x84, 0x50, 0x18, + 0xCF, 0xC7, 0xB2, 0xAB, 0x43, 0x0A, 0x13, 0xA6, 0xB1, 0xAC, 0x7B, 0xB0, 0x2C, 0xCC, 0xBB, + 0x3D, 0x91, 0x1A, 0xC2, 0xF1, 0x10, 0x68, 0x61, 0x3F, 0xBE, 0x02, 0x9B, 0xFD, 0xCE, 0x02, + 0xCF, 0x5C, 0xD3, 0x89, 0x50, 0xED, 0x72, 0xC8, 0x39, 0x44, 0xED, 0xFB, 0xC7, 0x56, 0x15, + 0xAF, 0x87, 0xF8, 0x64, 0xC0, 0x51, 0xF3, 0xC5, 0x54, 0x56, 0xC5, 0x41, 0x28, 0x63, 0xA4, + 0x0C, 0x06, 0xD1, 0xDA, 0xB5, 0x62, 0xBD, 0xFF, 0x05, 0x71, 0xB8, 0xD3, 0xC3, 0x91, 0x7B, + 0xBD, 0x30, 0x08, 0x80, 0xBB, 0xA5, 0xE9, 0x98, 0x23, 0x9B, 0x95, 0xFA, 0x91, 0xB7, 0xD6, + 0x41, 0x6D, 0x4F, 0x39, 0x8B, 0x3A, 0xDB, 0xCD, 0x30, 0x98, 0x3E, 0xD3, 0x59, 0x2B, 0x4D, + 0x9E, 0xF7, 0xD4, 0x23, 0x6F, 0xD0, 0x0F, 0x50, 0xD9, 0x8A, 0xA5, 0x3A, 0x23, 0x5A, 0xC4, + 0x17, 0x27, 0x20, 0xF7, 0x7D, 0x96, 0x17, 0x26, 0x72, 0x98, 0x0C, 0xFE, 0x8F, 0xF7, 0xA5, + 0xA7, 0x02, 0x78, 0x3E, 0xDC, 0x2B, 0xA3, 0x1B, 0x22, 0x59, 0x01, 0x5A, 0x11, 0x2F, 0xC7, + 0xF4, 0x68, 0xA9, 0xC2, 0xF9, 0x46, 0x40, 0x39, 0x00, 0x2D, 0x30, 0xEF, 0x67, 0x8B, 0x4C, + 0xB7, 0x98, 0xBC, 0x11, 0x62, 0x16, 0xBF, 0x7A, 0x9A, 0x7C, 0x18, 0xBA, 0x03, 0xB7, 0xB5, + 0x8F, 0xD0, 0x75, 0x15, 0xD3, 0x11, 0x50, 0x49, 0xD3, 0x61, 0x4B, 0xE7, 0xA0, 0x7E, 0x74, + 0x43, 0x00, 0x75, 0x0D, 0xF1, 0xD2, 0xC5, 0x87, 0x53, 0x38, 0x90, 0x59, 0xEA, 0xFC, 0x3D, + 0x78, 0x5C, 0xCD, 0xD3, 0x1C, 0x07, 0x64, 0x8B, 0xED, 0xC0, 0x3A, 0x5C, 0x3B, 0x8A, 0xD4, + 0x6D, 0x06, 0x4D, 0x59, 0xC1, 0x3D, 0x57, 0x37, 0x47, 0x29, 0xFC, 0x4E, 0x29, 0x53, 0x62, + 0xE2, 0xA5, 0x19, 0x12, 0x04, 0x53, 0x04, 0x28, 0xBC, 0x15, 0x22, 0xAF, 0xA2, 0x8F, 0xF5, + 0xFE, 0x16, 0x55, 0xE3, 0x04, 0xCA, 0x5B, 0xC8, 0xC2, 0x7A, 0xD0, 0xE0, 0xC6, 0xA3, 0x9D, + 0xD4, 0xDF, 0x28, 0x95, 0x6C, 0x14, 0xB3, 0x8C, 0xC9, 0x36, 0x82, 0xCE, 0xFE, 0x40, 0x2B, + 0xBD, 0x5E, 0x82, 0xD2, 0x9C, 0x46, 0x4E, 0x44, 0xEB, 0x5D, 0x37, 0xB4, 0x8F, 0xC5, 0x68, + 0xDF, 0xE0, 0xCC, 0x6E, 0x8E, 0x16, 0xBA, 0xEA, 0x05, 0xE5, 0x13, 0x55, 0x90, 0xF1, 0x92, + 0x94, 0xE7, 0x3E, 0x83, 0x67, 0xB0, 0x21, 0x6D, 0xBB, 0x81, 0x50, 0x30, 0xB9, 0xDE, 0x55, + 0x91, 0x3F, 0x08, 0x03, 0x9C, 0x42, 0x35, 0x1C, 0x59, 0xE5, 0x51, 0x5D, 0xD5, 0xAF, 0x8E, + 0x08, 0x9A, 0x15, 0xE6, 0x25, 0xE8, 0xF6, 0xDE, 0xE6, 0x39, 0x38, 0x6C, 0x46, 0x49, 0x7D, + 0x7A, 0x26, 0x32, 0x88, 0x77, 0x4D, 0xE5, 0x81, 0xA7, 0xDE, 0x96, 0x29, 0xB4, 0x1B, 0x44, + 0x24, 0x14, 0x1F, 0x97, 0x8F, 0xB8, 0x33, 0x12, 0x08, 0xEF, 0xDE, 0xC3, 0xC6, 0xE0, 0xDE, + 0x39, 0xBC, 0x57, 0x06, 0x3F, 0x3D, 0xCD, 0x6C, 0x47, 0x03, 0x73, 0xC0, 0x88, 0x91, 0xEA, + 0x29, 0xCB, 0xC7, 0xCC, 0x6D, 0x64, 0x83, 0xB8, 0x88, 0x90, 0x83, 0xAC, 0xE8, 0x6A, 0xA7, + 0xB5, 0x1B, 0x1C, 0x2C, 0xFE, 0x6E, 0x2A, 0xD1, 0x8D, 0x97, 0xCE, 0x36, 0xFB, 0xC5, 0x6E, + 0xA4, 0x2F, 0xAE, 0x97, 0xE6, 0xA7, 0xAC, 0x11, 0x48, 0x64, 0x47, 0x8C, 0x36, 0x6D, 0xF1, + 0xEB, 0xB1, 0xE7, 0xB1, 0x1A, 0x90, 0x98, 0x50, 0x4F, 0xD5, 0x97, 0x5B, 0xDF, 0x1F, 0x49, + 0xDC, 0x70, 0x00, 0x2B, 0x63, 0xC1, 0x73, 0x9A, 0x9D, 0x26, 0x3F, 0xBA, 0xD4, 0x07, 0x3F, + 0x6A, 0x9F, 0x6C, 0x2B, 0x8A, 0xF4, 0xB4, 0xC3, 0x32, 0xA1, 0x03, 0xA0, 0xCF, 0xFA, 0x5D, + 0xEE, 0xB2, 0xD0, 0x62, 0xCA, 0x3C, 0x21, 0x5F, 0xD3, 0x60, 0x02, 0x6B, 0xE7, 0xC5, 0x16, + 0x4F, 0x4A, 0x44, 0x24, 0xEF, 0x74, 0x94, 0x88, 0x04, 0xD6, 0x6F, 0x46, 0x48, 0x77, 0x32, + 0xC8, 0x20, 0x2C, 0x79, 0x54, 0x78, 0x64, 0x7B, 0x4E, 0xA7, 0x1D, 0x62, 0x7C, 0x08, 0x60, + 0x24, 0xCC, 0xA3, 0x54, 0xA4, 0x1F, 0x08, 0x77, 0xB3, 0x8F, 0x19, 0xB3, 0x77, 0x4A, 0xD2, + 0x09, 0x5C, 0x8D, 0xA5, 0x3B, 0x06, 0x9E, 0x21, 0xC7, 0x6A, 0xE2, 0xD2, 0x00, 0x7E, 0x16, + 0x71, 0x9E, 0xD4, 0x00, 0x80, 0xD3, 0x34, 0xF7, 0xDA, 0x52, 0xE9, 0xF5, 0xA5, 0x99, 0x04, + 0x39, 0xCA, 0xF0, 0x83, 0xA9, 0x5B, 0x83, 0x3F, 0x02, 0xAD, 0x10, 0xA0, 0x8C, 0x1A, 0x6D, + 0x0F, 0x26, 0x0C, 0x00, 0x72, 0x85, 0xBD, 0x4A, 0x2F, 0x47, 0x70, 0x3A, 0x5A, 0xEF, 0x46, + 0x52, 0x87, 0xD2, 0x53, 0xB1, 0x8A, 0xC2, 0x25, 0x14, 0x31, 0x62, 0x10, 0xFF, 0x56, 0x68, + 0x14, 0xB1, 0x0F, 0x87, 0xA2, 0x93, 0xD6, 0xF1, 0x99, 0xD3, 0xC3, 0x95, 0x99, 0x90, 0xD0, + 0xC1, 0x26, 0x8B, 0x4F, 0x50, 0xD5, 0xF9, 0xFC, 0xEF, 0xBB, 0xF2, 0x37, 0xBD, 0x0C, 0x28, + 0xB8, 0x01, 0x82, 0xD6, 0x65, 0x97, 0x41, 0xF1, 0x4F, 0x10, 0xBF, 0xBB, 0x21, 0xBB, 0xA1, + 0x2A, 0xB6, 0x20, 0xAA, 0x23, 0x96, 0xF5, 0x6C, 0x06, 0x86, 0xB4, 0xEA, 0x90, 0x17, 0x99, + 0x02, 0x24, 0x21, 0x6B, 0x2F, 0xE8, 0xAD, 0x76, 0xC4, 0xA9, 0x14, 0x8E, 0xEF, 0x9A, 0x86, + 0xA3, 0x63, 0x5A, 0x6A, 0xA7, 0x7B, 0xC1, 0xDC, 0xFB, 0x6F, 0xBA, 0x59, 0xA7, 0x7D, 0xFD, + 0xA9, 0xB7, 0x53, 0x0D, 0xC0, 0xCA, 0x86, 0x48, 0xC8, 0xD9, 0x73, 0x73, 0x8E, 0x01, 0xBA, + 0xB8, 0xF0, 0x8B, 0x49, 0x05, 0xE8, 0x4A, 0xA4, 0x64, 0x1B, 0xD6, 0x02, 0x41, 0x0C, 0xD9, + 0x75, 0x20, 0x26, 0x5F, 0x2F, 0x23, 0x1F, 0x2B, 0x35, 0xE1, 0x5E, 0xB2, 0xFA, 0x04, 0xD2, + 0xBD, 0x94, 0xD5, 0xA7, 0x7A, 0xBA, 0xF1, 0xE0, 0xE1, 0x61, 0x01, 0x0A, 0x99, 0x00, 0x87, + 0xF5, 0xB4, 0x6E, 0xA9, 0x88, 0xB2, 0xBC, 0x05, 0x12, 0xFD, 0xA0, 0xFA, 0x92, 0x3D, 0xAD, + 0xD6, 0xC4, 0x5C, 0x53, 0x01, 0xD0, 0x94, 0x83, 0x67, 0x32, 0x65, 0xB5, 0xAB, 0x2E, 0x10, + 0xF4, 0xBA, 0x52, 0x0F, 0x6B, 0xBA, 0xD5, 0x64, 0xA5, 0xC3, 0xD5, 0xE2, 0x7B, 0xDB, 0x08, + 0x0F, 0x7D, 0x20, 0xE1, 0x32, 0x96, 0xA3, 0x18, 0x19, 0x54, 0xC3, 0x9C, 0x64, 0x9C, 0x94, + 0x3E, 0xBE, 0x17, 0xDF, 0x5C, 0x1F, 0x7A, 0xAE, 0x0A, 0x8F, 0xE1, 0x26, 0xC4, 0x77, 0x58, + 0x5A, 0x5D, 0x4D, 0x64, 0x8A, 0x0D, 0x00, 0x8B, 0x6A, 0xF5, 0xE8, 0xCD, 0x31, 0xBE, 0x69, + 0xA9, 0x29, 0x6D, 0x4F, 0x3F, 0xD2, 0x5E, 0xD8, 0x6F, 0x22, 0x1E, 0x4B, 0x93, 0xF6, 0x5F, + 0x59, 0x29, 0x96, 0x75, 0x33, 0x62, 0x4B, 0x92, 0x35, 0x75, 0x0C, 0x30, 0x70, 0x75, 0x50, + 0xB5, 0x85, 0x36, 0xD1, 0x09, 0xA7, 0x13, 0x1C, 0x5A, 0x5B, 0xBE, 0x4A, 0x57, 0x15, 0x56, + 0x7C, 0x12, 0x53, 0x4A, 0xEC, 0x76, 0x60, 0x76, 0x1E, 0xEB, 0xB9, 0xFA, 0xE2, 0x89, 0x1C, + 0x77, 0x45, 0x89, 0xB8, 0x0E, 0x56, 0x6A, 0xD5, 0x57, 0xDD, 0xEF, 0x73, 0x67, 0x19, 0x6B, + 0x72, 0x27, 0xEA, 0x98, 0x70, 0xEF, 0x09, 0xDD, 0xFE, 0xC7, 0x9D, 0x6B, 0x93, 0x19, 0xA6, + 0x87, 0x9B, 0x52, 0x05, 0xD7, 0x6B, 0xF7, 0xAB, 0xA5, 0xAC, 0xF3, 0x3A, 0xFB, 0x59, 0xD1, + 0x7F, 0xC5, 0x4E, 0x68, 0x38, 0x3D, 0x6B, 0xE5, 0xA0, 0x8E, 0x9B, 0x66, 0xDA, 0x53, 0xDC, + 0xDE, 0x00, 0x8B, 0xB2, 0x94, 0xB8, 0x58, 0x2B, 0xD1, 0x32, 0xCD, 0xCC, 0x49, 0x95, 0x9F, + 0xDB, 0xC2, 0x1E, 0x52, 0x72, 0x18, 0x80, 0xC8, 0xAD, 0x03, 0x52, 0xC7, 0x9F, 0x03, 0xA4, + 0x3B, 0xBD, 0x84, 0xC4, 0xCD, 0xFD, 0xC6, 0xC5, 0x29, 0x00, 0x5E, 0x1E, 0x7C, 0xD9, 0xA3, + 0x49, 0xA7, 0x16, 0x8A, 0x35, 0x56, 0x9B, 0xA5, 0xDE, 0xA8, 0x18, 0x96, 0x8D, 0x5A, 0x91, + 0x46, 0x6B, 0xD6, 0xE6, 0x4E, 0x20, 0xBF, 0x62, 0x41, 0x71, 0x98, 0xAF, 0xC4, 0xE8, 0x1C, + 0x28, 0xDD, 0x77, 0xED, 0x40, 0x28, 0x23, 0x23, 0x98, 0xB5, 0x2F, 0xBD, 0xE8, 0x6B, 0xC8, + 0x4F, 0x47, 0x5B, 0x90, 0x16, 0x71, 0x0C, 0xE2, 0xAA, 0xBC, 0x11, 0xA0, 0x6B, 0x4D, 0xBA, + 0xC9, 0x01, 0xEC, 0x16, 0xCF, 0x36, 0x5C, 0xA3, 0xF2, 0xD5, 0x38, 0x13, 0x94, 0x8A, 0x69, + 0x3A, 0x0F, 0x93, 0xE7, 0x9C, 0x46, 0xCA, 0x5D, 0x5A, 0x6D, 0xCA, 0x3D, 0x28, 0xCA, 0x50, + 0xAD, 0x18, 0xBD, 0x13, 0xFC, 0xA5, 0x50, 0x59, 0xDD, 0x9B, 0x18, 0x5F, 0x79, 0xF9, 0xC4, + 0x71, 0x96, 0xA4, 0xE8, 0x1B, 0x21, 0x04, 0xBC, 0x46, 0x0A, 0x05, 0x1E, 0x02, 0xF2, 0xE8, + 0x44, 0x4F, + ]) + .unwrap(); // todo -- there's something I don't understand here that removing this hex:: for a straight array causes peak memory usage to go up. // Find the optimal and make all the tests the same. let sig = &*hex::decode("9061f15cbf2092f744fbcd799eb02414053c1b0f7412124bedc41cf9a3db0166469e874037d7f081e5f8d3d2033a0307d1c49ed01fe64578c4a6fabd80880cdf1911848f184d4bcf536ca795a0fb1aa19ab7ee3ba6b58bd64bbeac9f58650fff1ef5a97ab6916df962072e20e7c6be96090e3a781a504bc4442bd8889a0aa628907a74299f39fa836031f1bd68355bebe7ae93c1e361a9efbed1325d96227070461fcd6f151b8669d9229b977d9ee51fd2260c3e4a2e820416f9e074958dc3b3e2217e6312b7e0b582a048981cf6579f4bc7715b78c808e4c57e3b8aa38b05c04fcedf209f52c1e331ae83dbdff60ba450a17cc397568e54bc3f16ddf30b92747ce460d925b9be20a1d35e2aed97f124af2616a5361df28ba30e522dd08fa00fd28d1ac484d756a89e3a442fefe8332c56cd2a9fde691bdbda43f1cc54cef57bead96120b50c7d4695bdbb1303cc5ddda898e4eeb83083176e40e0232cdd1c3150371df05d6fdad7e1164d90393cf308e99edfeb31fed263e2866ee3b7f3937b399c974d87ba7b489efe3c9b80371d2928446adc31991ab0cefaaa080575b9ec81cfa133a9911c035a8058d0d3f2e34de4a9fb009bb4ccdb16de7b908574a7496725ff857556c1b33917e986c80f1014a9e3083add2fb35f345c5d06159e443329d0da099987b996c3731592b460c2ffd2955f7546f4216100ba43188803ff9b36969685f909fa2539323b8c8ec1c095a5085e554dd450e0e67ab670b6a11ebf6c25520fc13e364060f91f9b7f3d5cb48ff28b8fc83d4293f1f35ad6ff6ae4574ad7a1c6005fc0389a7b21386b0850a05d832fe6a14bb2b1db1f8e20bd09174946cd098b81c8f797e95f2143a949770cf1219bfef039db51a80fc247f65f41554c7173dd805ba82fdf47ab6d4bfd37dfe46fc47904421ae00dc005a22f9c4784b0ea9e665392a412245016d5c6d7673a6a180d228d4255a538e451ffd8b414d40304c0c888992e0ab6de1602109527417bc1c7eb782ae77a8c3cdfc1d13a1e874207898264e38080243109c5969649ac8383417e922ba115331142d0ed35440b15d40bee0cf58af37c0f0524ffac1c71ceed3bb82f76ab108a8ad1a0c8b78d9341148c642369be7bef59d46f49d70c83560607f140848ec9a7607d4a08f8b6e4447f5523f416981888a8de9647ffef79389e4983e5c9387698d0cc2d429322365ce7e7b5fd6d6eb921c813fcf06199fe1ca41e9cfe03b539f321671a2acad0963f876f9db7a1c4371b9f101005217995b5b6a40976246d245da603dba8dac812a5480c3476a99d0ffdf0ef943d72d912543148b2fe78e8b0159324fe9bcd4ced33cd212fe4f3dfd6d4c5e1958beb95ac6b533ace3e78015e3880b52bf45299263a4c0096f8ba5fe3a6298cab675cb7f382e7ef49720eb4cf47376e2d2574122ccf91129c858e948904fecefb91226ed42403ba12dd3258909a87dfcbf65cc3adc3d98d277fdcec7664e2292b7d27afbb5aafb405c20a34b2fe2c0849ee280bb891dfdf59f19b89b0246358db54cf3fdc66eaaaa750c8903f1d42678f3edf0b7530410aa881bc617f94346379854af4532e61f65aae7576c35faf55e155bd6787b4634d54191907e155c239e68480cdfa0c87054bfb62855f409a20d5335fb123e681e64ec847cd985b6062059f436aebac623c038b6c3405ac325191a8d1126a5ef8f38cccbf144a5c324c1e093cf99efbe10ca03d439bcfb8ba5e293b7d318837f7bc42a99964392369da76e79d71d1a2c248a11324a87ae1e3cbeab6fb0d0bcae1ef55e43dfb6f1b4cfb82c7a778fb828a3727ef07685fe38a74b3dd25d015322c2d9f245c08d8c2b43865694233782eb734436c4eddef5406208d6c4572c7371262fe02319cfbbcf2e23bed8aa969d1ae6f5f25ff6b8ebcf0925066f761a39bbff49f0c8dbc3be84f0c442b044ea01b669747e3c8293cfe9ccdf2ef063ae3d28d10720c279a2691616abd23b055cfc6c562125df4ad0fa6631304972ddc3674b1aaa7665bf621320d83eac8d5b371d7d719829f58b23458182558710de31d81ef9a47d8839c79640b2025d1965a418bc90e4115f1423311a8b64fcde0f2d2145ee535b0931b84bc8110445f2ff68d136ed709ddb7ea9ff75f3b4e8b4f836230ca9e81069477f634e07270af60ef96f72557a081d664abcf35548f699484653da645483ff2bf5998617ae8bfa62d56e714f3c0136e5035a3f78e06c2f470df7fd3380d14033f81e2aae6b4d90487dab76b9b3b8761fb56c36f5429da3d4346cb22e641ad8d7d2d80fa240d4e0154e6b3d2f1b3ef6cf174c08d062f575c83a4078174f874364df36a6328beeef69ba7f90e1df9fdcec9a2f15ebf04fa7d6756da2e5a59c9cbbcbc397d6fb28d0fc9a60534dff0752716ed079ad1ab19a224d1c8ae8a53242fd164989ff997489b6520eb3c0e97f4bcc1a9c3cbd44f008c03ef52cf7e626881d246925e0336c0ac668867f853da7820f914115a7c77ac31b66f46fbf97f66fa26416fc4581d459a4f2462d52cf0c79b278955aa73e8fa56e3c320f516bcc54c97e587199c15ab953cc37189b81c70cabb2559e445bcc9d8174ad7574e9acb02f43e0c34ff5e6746ee730ad41ff8eef93c2071c2649063dd92f343c06ef6abaf98f28d98d968071c12cc10a90c22d8b3b3480c76f7a51b7ec594b3435d2e3d779c1a15037697f3a058650472e47eecd5f32eb3243a516f0e703f9888c84690750648d6a9a876bf1f353db6891dc6d317d6e87ac088f42b5f6f20d799ece4fa7aaa928d2ac795e8de83d1e1c7fa2f9a4106693e981c21c63b3221c4fa2649f45f0c6e05dbf24011af16ab2e5fe94a640b485988037ebe1e8ad0b2623d95e9947f0726121d7828614e3b2d77a7a1f9a938bea9a1a7a2627b7d2e358c42ccc6c0b80a15a1c2f6e9aaf0495bdb7bb8d4b0e28a1ab5ab93ca0ff3e3f910c490c13486852534d5e12160835ec5916c5c68349c4e2d8fa956c643277edd3b6c81c88c010421705fd317ff9e3c94df0ed5305f530acbccf8dd0e87140cd38152664a572c168cd72595b7fac243c03f3fb33ef74a28c0e4469f94587c13704e9efe8010b2125aca78c22c33c82366e1a7c4028c2ae2e8d26e1a57e4297fac987f84a0a27f42b4c93a4f4d14569824b0880fb67407ed58f267ac403aa0b1f93784b4b4c67036037e60d58072611b0e90ca316976ef4e0b302cdad1b6dcca92efb8e1f6be2397967508be2c02a25ed0380ba1f7955f857c8fb043297780d136b2b064040c8e55143d715ea997e134ed973c98ef82786f0ccf66c17d863542180c66d54d08e116f2e35d995e214489ad0fad7a55fe9ebc1a777fe34141147c080b98d13463a3bbc6fc82f2fc95f4de7b3591d9c8cd4416917a4338095d5620104b7be13f5a131dd3f7aad5b559d11e8171dfb91e2bb1e47ac3810b1cdc1a1e370c867b7b7b50c4688dce545763157e02f47e1cc661d5bf2fbc336cfae080ab15728b1ab9dd199f2779d451e6178977fb658c17344cffb7aa3af5791a28fc8a089c85187753e5e313c8d1f0fe7755e28be444426a189e8bce2d2f79db31d4c3ca911a83455525355f95d159351cd731a88e55403851236ee2128f279d5be644c042453ae65d9e9f3b40d6c82bdeb002acdee061ecca3f2dceabef9a900e6e063d56ab39cb82dbc77a4677572d7616cd72c0f6d5b9b941dfda1fe7c896b8cc24d65a4322d712a84e94adfc8ed0cc56cc1ae97f775bd3cea5b20b524d9a7a916056e19af095d30171e5e14c7c998f78dc44845edf307363eab7913f680a5e5a1540a6f945507ffa67591f8d1a2920ab3b6e754e35379dd67870c242335e2717903ff3c687e5c33dc953416865d5f23bd752e55492b9d5d888d7b37ef33b0a6774d052b0987c066a2e01767207aa7fbfc393ca62874613dde3794f74fadb5d55b877b877a605918c812610fbcafad72ee245e6dd8721138d6bd3f4eedad853aed1ec437ad02ac937c80dae26fa5f70083bd346779b779387f7b3d2aae57770d8177928833281ccb7a38da24834fd9726fd17eb603cba9041e82bfeed0e33942dde1d48c271f5b39aa7230f41afb89d36f7976eee4f51a036743031c534f64685b94c990a93a5737fe628ee9cda8ed9c08b11d3836f833835c445b317a77ead7599d1a0c08873014510d36bb7ff5fb961277589ea48c32a60c87ec40681be067b17785ec44825bd89faa25249e735a628b6eebcc6cce4e0314c627588118c40b2e0d460d8d5ce358c56458f36914ca203f5a5381c6deb5a76bbc08c40a87437da0d0b571788a05e9f96d9bb770de8a0b1b960ff2a44a964c9b7939853742e83ce8deb79191b2d82454655f227079dd8c5b0216c8470b8e1ac70526301bbfa2bc4adca68a766ccb2a6e0ebf2e99905bf5242590b01703868b3faf841c11c383be145a40fea6375e18a01468e459603b5efdf8a4e9abd179280ae8b5947d78d2f0c4d37715eaa42bc37cf8730e41ffbf9826d46424f2922a96033cefaa8b4bbe4c8b89d43501fd5211d5392ca19a98ba127d9025b5c6e86ba024471940549a2b5d8e14961c9dc19696da1a5bffd01030d5e6100000000000000000000000000000000000000000000000005090f131a1f").unwrap(); @@ -395,8 +1885,8 @@ fn bench_mldsa65_verify() { } fn bench_mldsa65_lowmemory_verify() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey}; use bouncycastle::hex; + use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey, MLDSATrait}; eprintln!("MLDSA65_lowmemory/Verify"); @@ -428,8 +1918,10 @@ fn bench_mldsa65_lowmemory_verify() { } fn bench_mldsa87_verify() { - use bouncycastle::mldsa::{MLDSATrait, MLDSA87, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87PublicKey}; use bouncycastle::hex; + use bouncycastle::mldsa::{ + MLDSA87, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87PublicKey, MLDSATrait, + }; eprintln!("MLDSA87/Verify"); @@ -450,7 +1942,182 @@ fn bench_mldsa87_verify() { // eprintln!("sig:\n{}", &*hex::encode(sig)); // let mldsa87_pk = MLDSA87PublicKey::from_bytes(&*hex::decode("9792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124a73b323b9ba21ab64d767c433f5a521effe18f86e46a188952c4467e048b729e7fc4d115e7e48da1896d5fe119b10dcddef62cb307954074b42336e52836de61da941f8d37ea68ac8106fabe19070679af6008537120f70793b8ea9cc0e6e7b7b4c9a5c7421c60f24451ba1e933db1a2ee16c79559f21b3d1b8305850aa42afbb13f1f4d5b9f4835f9d87dfceb162d0ef4a7fdc4cba1743cd1c87bb4967da16cc8764b6569df8ee5bdcbffe9a4e05748e6fdf225af9e4eeb7773b62e8f85f9b56b548945551844fbd89806a4ac369bed2d256100f688a6ad5e0a709826dc4449e91e23c5506e642361ef5a313712f79bc4b3186861ca85a4bab17e7f943d1b8a333aa3ae7ce16b440d6018f9e04daf5725c7f1a93fad1a5a27b67895bd249aa91685de20af32c8b7e268c7f96877d0c85001135a4f0a8f1b8264fa6ebe5a349d8aecad1a16299ccf2fd9c7b85bace2ced3aa1276ba61ee78ed7e5ca5b67cdd458a9354030e6abbbabf56a0a2316fec9dba83b51d42fd3167f1e0f90855d5c66509b210265dc1e54ec44b43ba7cf9aef118b44d80912ce75166a6651e116cebe49229a7062c09931f71abd2293f76f7efc3215ba97800037e58e470bdbbb43c1b0439eaf79c54d93b44aac9efe9fbe151874cfb2a64cbee28cc4c0fe7775e5d870f1c02e5b2e3c5004c995f24c9b779cb753a277d0e71fd425eb6bc2ca56ce129db51f70740f31e63976b50c7312e9797d78c5b1ac24a5fa347cc916e0a83f5c3b675cd30b81e3fa10b93444e07397571cce98b28da51db9056bc728c5b0b1181e2fbd387b4c79ab1a5fefece37167af772ddad14eb4c3982da5a59d0e9eb173ec6315091170027a3ab5ef6aa129cb8585727b9358a28501d713a72f3f1db31714286f9b6408013af06045d75592fc0b7dd47c73ed9c75b11e9d7c69f7cadfc3280a9062c5273c43be1c34f87448864cea7b5c97d6d32f59bd5f25384653bb5c4faa45bea8b89402843e645b6b9269e2bd988ddacb033328ffb060450f7df080053e6969b251e875ecec32cfc592840d69ab69a75e06b379c535d95266b082f4f09c93162b33b0d9f7307a4eaaa52104437fed66f8ee3eabbd45d67b25a8133f496468b52baffdbfad93eef1a9818b5e42ec722788a3d8d3529fc777d2ba570801dfae01ec88302837c1fb9e0355727645ee1046c3f915f6ae82dad4fb6b0356a46518ffc834155c3b4fe6dafa6cc8a5ccf53c73a0849d8d44f7dcf72754e70e1b7dfb447bb4ef49d1a718f6171bbce200950e0ce926106b151a3e871d5ce49731bd6650a9b0ca972da1c5f136d44820ea6383c08f3b384cf2338e789c513f618cc5694a6f0cee104511e1ed7c5f23a1ebfd8a0db8424553240156dbf622831b0c643d1c551b6f3f7a98d29b85c2de05a65fa615eee16495bd90737672115b53e91c5d90028cf3f1a93953a153de53b44084e9ccff6b736693926daefebb2d77aa5ad689b92f31686669df16d1715cc58f7a2cfb72dd1a51e92f825993a74022be7e9eb6054654457094d14928f20215e7b222ac56b51adbec8d8bdb6983979a7e3a21b44b5d1518ca97d0b5195f51ed6a24350c89747e1edea51b448e3e9147054ce927873c90db394d86888e07dff177593d6f79e152302204aeb03be2386af3e24078bd028b1689f5e147c9f452c8ceb02ec59cc9db63a03576ceeafe98239023897da0236630a53c0de7f435a19869792fab36e7b9e635760f09069e6432e700035ac2a02879fff0a1e1bec522047193d94eb5df1efd53eea1144ca78940852f5ec9727904b366ede4f5e2d331fad5fc282ea2c47e923142771c3dd75a87357487def99e5f18e9d9ed623c175d02888c51f82c07a80d54716b3c3c2bdbe2e9f0a9bbaaebeb4d52936876406f5c00e8e4bbd0a5ec05797e6207c5ab6c88f1a688421bd05a114f4d7de2ac241fa0e8bedff47f762ddcbeaa91004f8d31e85095c81054994ad3826e344ba96040810fc0b2ad1de48cfade002c62e5a49a0731ab38344bc1636df16bf607d56855e56d684003c718e4bad9e5a099979fcddeeb1c4a7776cd37a3417cb0e184e29ef9bc0e87475ba663be09e00ab562eb7c0f7165f969a9b42414198ccf1bff2a2c8d689a414ece7662927665689e94db961ebaec5615cbc1a7895c6851ac961432ff1118d4607d32ef9dc732d51333be4b4d0e30ddea784eca8be47e741be9c19631dc470a52ef4dc13a4f3633fd434d787c170977b417df598e1d0dde506bb71d6f0bc17ec70e3b03cdc1965cb36993f633b0472e50d0923ac6c66fdf1d3e6459cc121f0f5f94d09e9dbcf5d690e23233838a0bacb7c638d1b2650a4308cd171b6855126d1da672a6ed85a8d78c286fb56f4ab3d21497528045c63262c8a42af2f9802c53b7bb8be28e78fe0b5ce45fbb7a1af1a3b28a8d94b7890e3c882e39bc98e9f0ad76025bf0dd2f00298e7141a226b3d7cee414f604d1e0ba54d11d5fe58bccea6ad77ad2e8c1caacf32459014b7b91001b1efa8ad172a523fb8e365b577121bf9fd88a2c60c21e821d7b6acb47a5a995e40caced5c223b8fe6de5e18e9d2e5893aefebb7aae7ff1a146260e2f110e939528213a0025a38ec79aabc861b25ebc509a4674c132aaacb7e0146f14efd11cfcaf4caa4f775a716ce325e0a435a4d349d720bcf137450afc45046fc1a1f83a9d329777a7084e4aadae7122ce97005930528eb3c7f7f1129b372887a371155a3ba201a25cbf1dcb64e7cdee092c3141fb5550fe3d0dd82e870e578b2b46500818113b8f6569773c677385b69a42b77dcba7acffd95fd4452e23aaa1d37e1da2151ea658d40a3596b27ac9f8129dc6cf0643772624b59f4f461230df471ca26087c3942d5c6687df6082835935a3f87cb762b0c3b1d0dda4a6533965bef1b7b8292e254c014d090fed857c44c1839c694c0a64e3fad90a11f534722b6ee1574f2e149d55d744de4887024e08511431c062750e16c74ab9f3242f2db3ffb12a8d6107faa229d6f6373b07f36d3932b3bdb04c19dd64eadd7f93c3c564c358a1c81dcf1c9c31e5b06568f97544c17dc15698c5cb38983a9afc42783faa773a52c9d8260690be9e3156aa5bc1509dea3f69587695cd6ff172ba83e6a6d8a7d6bbebbbcda3672731983f89bc5831dc37c3f3c5c56facc697f3cb20bd5dbadbd702e54844ac2f626901fe159db93dfd4773d8fe73562b846c1fc856d1802762840ebc72d7988bde75cbca70d319d32ce0cc0253bb2ad455723ee0c7f4736ce6e6665c5aca32a481c53839bc259167b013d0423395eeb9aaaee3206149a7d550d67fc5fdfe4a8a5c35d2510b664379ab8f72855a2af47abce2a632048eaf89e5cb4a88debc53a595103acce4f1cff18acff07afe1eb5716aa1e40b63134c3a3ae9579fa87f515be093c2d29db6d6b65c93661e00636b592704d093cc6716c2342eb1853d48c85c63ac8a2854462c7b77e7e3bd1eac5bca28ffaa00b5d349f8a547ad875b96a8c2b2910c9301309a3f9138a5693111f55b3c009ca947c39dfc82d98eb1caa4a9cbe885f786fa86e55be062222f8ba90a974073326b31212aece0a34a60").unwrap()).unwrap(); - let pk = MLDSA87PublicKey::from_bytes(&[0x97,0x92,0xbc,0xec,0x2f,0x24,0x30,0x68,0x6a,0x82,0xfc,0xcf,0x3c,0x2f,0x5f,0xf6,0x65,0xe7,0x71,0xd7,0xab,0x41,0xb9,0x02,0x58,0xcf,0xa7,0xe9,0x0e,0xc9,0x71,0x24,0xa7,0x3b,0x32,0x3b,0x9b,0xa2,0x1a,0xb6,0x4d,0x76,0x7c,0x43,0x3f,0x5a,0x52,0x1e,0xff,0xe1,0x8f,0x86,0xe4,0x6a,0x18,0x89,0x52,0xc4,0x46,0x7e,0x04,0x8b,0x72,0x9e,0x7f,0xc4,0xd1,0x15,0xe7,0xe4,0x8d,0xa1,0x89,0x6d,0x5f,0xe1,0x19,0xb1,0x0d,0xcd,0xde,0xf6,0x2c,0xb3,0x07,0x95,0x40,0x74,0xb4,0x23,0x36,0xe5,0x28,0x36,0xde,0x61,0xda,0x94,0x1f,0x8d,0x37,0xea,0x68,0xac,0x81,0x06,0xfa,0xbe,0x19,0x07,0x06,0x79,0xaf,0x60,0x08,0x53,0x71,0x20,0xf7,0x07,0x93,0xb8,0xea,0x9c,0xc0,0xe6,0xe7,0xb7,0xb4,0xc9,0xa5,0xc7,0x42,0x1c,0x60,0xf2,0x44,0x51,0xba,0x1e,0x93,0x3d,0xb1,0xa2,0xee,0x16,0xc7,0x95,0x59,0xf2,0x1b,0x3d,0x1b,0x83,0x05,0x85,0x0a,0xa4,0x2a,0xfb,0xb1,0x3f,0x1f,0x4d,0x5b,0x9f,0x48,0x35,0xf9,0xd8,0x7d,0xfc,0xeb,0x16,0x2d,0x0e,0xf4,0xa7,0xfd,0xc4,0xcb,0xa1,0x74,0x3c,0xd1,0xc8,0x7b,0xb4,0x96,0x7d,0xa1,0x6c,0xc8,0x76,0x4b,0x65,0x69,0xdf,0x8e,0xe5,0xbd,0xcb,0xff,0xe9,0xa4,0xe0,0x57,0x48,0xe6,0xfd,0xf2,0x25,0xaf,0x9e,0x4e,0xeb,0x77,0x73,0xb6,0x2e,0x8f,0x85,0xf9,0xb5,0x6b,0x54,0x89,0x45,0x55,0x18,0x44,0xfb,0xd8,0x98,0x06,0xa4,0xac,0x36,0x9b,0xed,0x2d,0x25,0x61,0x00,0xf6,0x88,0xa6,0xad,0x5e,0x0a,0x70,0x98,0x26,0xdc,0x44,0x49,0xe9,0x1e,0x23,0xc5,0x50,0x6e,0x64,0x23,0x61,0xef,0x5a,0x31,0x37,0x12,0xf7,0x9b,0xc4,0xb3,0x18,0x68,0x61,0xca,0x85,0xa4,0xba,0xb1,0x7e,0x7f,0x94,0x3d,0x1b,0x8a,0x33,0x3a,0xa3,0xae,0x7c,0xe1,0x6b,0x44,0x0d,0x60,0x18,0xf9,0xe0,0x4d,0xaf,0x57,0x25,0xc7,0xf1,0xa9,0x3f,0xad,0x1a,0x5a,0x27,0xb6,0x78,0x95,0xbd,0x24,0x9a,0xa9,0x16,0x85,0xde,0x20,0xaf,0x32,0xc8,0xb7,0xe2,0x68,0xc7,0xf9,0x68,0x77,0xd0,0xc8,0x50,0x01,0x13,0x5a,0x4f,0x0a,0x8f,0x1b,0x82,0x64,0xfa,0x6e,0xbe,0x5a,0x34,0x9d,0x8a,0xec,0xad,0x1a,0x16,0x29,0x9c,0xcf,0x2f,0xd9,0xc7,0xb8,0x5b,0xac,0xe2,0xce,0xd3,0xaa,0x12,0x76,0xba,0x61,0xee,0x78,0xed,0x7e,0x5c,0xa5,0xb6,0x7c,0xdd,0x45,0x8a,0x93,0x54,0x03,0x0e,0x6a,0xbb,0xba,0xbf,0x56,0xa0,0xa2,0x31,0x6f,0xec,0x9d,0xba,0x83,0xb5,0x1d,0x42,0xfd,0x31,0x67,0xf1,0xe0,0xf9,0x08,0x55,0xd5,0xc6,0x65,0x09,0xb2,0x10,0x26,0x5d,0xc1,0xe5,0x4e,0xc4,0x4b,0x43,0xba,0x7c,0xf9,0xae,0xf1,0x18,0xb4,0x4d,0x80,0x91,0x2c,0xe7,0x51,0x66,0xa6,0x65,0x1e,0x11,0x6c,0xeb,0xe4,0x92,0x29,0xa7,0x06,0x2c,0x09,0x93,0x1f,0x71,0xab,0xd2,0x29,0x3f,0x76,0xf7,0xef,0xc3,0x21,0x5b,0xa9,0x78,0x00,0x03,0x7e,0x58,0xe4,0x70,0xbd,0xbb,0xb4,0x3c,0x1b,0x04,0x39,0xea,0xf7,0x9c,0x54,0xd9,0x3b,0x44,0xaa,0xc9,0xef,0xe9,0xfb,0xe1,0x51,0x87,0x4c,0xfb,0x2a,0x64,0xcb,0xee,0x28,0xcc,0x4c,0x0f,0xe7,0x77,0x5e,0x5d,0x87,0x0f,0x1c,0x02,0xe5,0xb2,0xe3,0xc5,0x00,0x4c,0x99,0x5f,0x24,0xc9,0xb7,0x79,0xcb,0x75,0x3a,0x27,0x7d,0x0e,0x71,0xfd,0x42,0x5e,0xb6,0xbc,0x2c,0xa5,0x6c,0xe1,0x29,0xdb,0x51,0xf7,0x07,0x40,0xf3,0x1e,0x63,0x97,0x6b,0x50,0xc7,0x31,0x2e,0x97,0x97,0xd7,0x8c,0x5b,0x1a,0xc2,0x4a,0x5f,0xa3,0x47,0xcc,0x91,0x6e,0x0a,0x83,0xf5,0xc3,0xb6,0x75,0xcd,0x30,0xb8,0x1e,0x3f,0xa1,0x0b,0x93,0x44,0x4e,0x07,0x39,0x75,0x71,0xcc,0xe9,0x8b,0x28,0xda,0x51,0xdb,0x90,0x56,0xbc,0x72,0x8c,0x5b,0x0b,0x11,0x81,0xe2,0xfb,0xd3,0x87,0xb4,0xc7,0x9a,0xb1,0xa5,0xfe,0xfe,0xce,0x37,0x16,0x7a,0xf7,0x72,0xdd,0xad,0x14,0xeb,0x4c,0x39,0x82,0xda,0x5a,0x59,0xd0,0xe9,0xeb,0x17,0x3e,0xc6,0x31,0x50,0x91,0x17,0x00,0x27,0xa3,0xab,0x5e,0xf6,0xaa,0x12,0x9c,0xb8,0x58,0x57,0x27,0xb9,0x35,0x8a,0x28,0x50,0x1d,0x71,0x3a,0x72,0xf3,0xf1,0xdb,0x31,0x71,0x42,0x86,0xf9,0xb6,0x40,0x80,0x13,0xaf,0x06,0x04,0x5d,0x75,0x59,0x2f,0xc0,0xb7,0xdd,0x47,0xc7,0x3e,0xd9,0xc7,0x5b,0x11,0xe9,0xd7,0xc6,0x9f,0x7c,0xad,0xfc,0x32,0x80,0xa9,0x06,0x2c,0x52,0x73,0xc4,0x3b,0xe1,0xc3,0x4f,0x87,0x44,0x88,0x64,0xce,0xa7,0xb5,0xc9,0x7d,0x6d,0x32,0xf5,0x9b,0xd5,0xf2,0x53,0x84,0x65,0x3b,0xb5,0xc4,0xfa,0xa4,0x5b,0xea,0x8b,0x89,0x40,0x28,0x43,0xe6,0x45,0xb6,0xb9,0x26,0x9e,0x2b,0xd9,0x88,0xdd,0xac,0xb0,0x33,0x32,0x8f,0xfb,0x06,0x04,0x50,0xf7,0xdf,0x08,0x00,0x53,0xe6,0x96,0x9b,0x25,0x1e,0x87,0x5e,0xce,0xc3,0x2c,0xfc,0x59,0x28,0x40,0xd6,0x9a,0xb6,0x9a,0x75,0xe0,0x6b,0x37,0x9c,0x53,0x5d,0x95,0x26,0x6b,0x08,0x2f,0x4f,0x09,0xc9,0x31,0x62,0xb3,0x3b,0x0d,0x9f,0x73,0x07,0xa4,0xea,0xaa,0x52,0x10,0x44,0x37,0xfe,0xd6,0x6f,0x8e,0xe3,0xea,0xbb,0xd4,0x5d,0x67,0xb2,0x5a,0x81,0x33,0xf4,0x96,0x46,0x8b,0x52,0xba,0xff,0xdb,0xfa,0xd9,0x3e,0xef,0x1a,0x98,0x18,0xb5,0xe4,0x2e,0xc7,0x22,0x78,0x8a,0x3d,0x8d,0x35,0x29,0xfc,0x77,0x7d,0x2b,0xa5,0x70,0x80,0x1d,0xfa,0xe0,0x1e,0xc8,0x83,0x02,0x83,0x7c,0x1f,0xb9,0xe0,0x35,0x57,0x27,0x64,0x5e,0xe1,0x04,0x6c,0x3f,0x91,0x5f,0x6a,0xe8,0x2d,0xad,0x4f,0xb6,0xb0,0x35,0x6a,0x46,0x51,0x8f,0xfc,0x83,0x41,0x55,0xc3,0xb4,0xfe,0x6d,0xaf,0xa6,0xcc,0x8a,0x5c,0xcf,0x53,0xc7,0x3a,0x08,0x49,0xd8,0xd4,0x4f,0x7d,0xcf,0x72,0x75,0x4e,0x70,0xe1,0xb7,0xdf,0xb4,0x47,0xbb,0x4e,0xf4,0x9d,0x1a,0x71,0x8f,0x61,0x71,0xbb,0xce,0x20,0x09,0x50,0xe0,0xce,0x92,0x61,0x06,0xb1,0x51,0xa3,0xe8,0x71,0xd5,0xce,0x49,0x73,0x1b,0xd6,0x65,0x0a,0x9b,0x0c,0xa9,0x72,0xda,0x1c,0x5f,0x13,0x6d,0x44,0x82,0x0e,0xa6,0x38,0x3c,0x08,0xf3,0xb3,0x84,0xcf,0x23,0x38,0xe7,0x89,0xc5,0x13,0xf6,0x18,0xcc,0x56,0x94,0xa6,0xf0,0xce,0xe1,0x04,0x51,0x1e,0x1e,0xd7,0xc5,0xf2,0x3a,0x1e,0xbf,0xd8,0xa0,0xdb,0x84,0x24,0x55,0x32,0x40,0x15,0x6d,0xbf,0x62,0x28,0x31,0xb0,0xc6,0x43,0xd1,0xc5,0x51,0xb6,0xf3,0xf7,0xa9,0x8d,0x29,0xb8,0x5c,0x2d,0xe0,0x5a,0x65,0xfa,0x61,0x5e,0xee,0x16,0x49,0x5b,0xd9,0x07,0x37,0x67,0x21,0x15,0xb5,0x3e,0x91,0xc5,0xd9,0x00,0x28,0xcf,0x3f,0x1a,0x93,0x95,0x3a,0x15,0x3d,0xe5,0x3b,0x44,0x08,0x4e,0x9c,0xcf,0xf6,0xb7,0x36,0x69,0x39,0x26,0xda,0xef,0xeb,0xb2,0xd7,0x7a,0xa5,0xad,0x68,0x9b,0x92,0xf3,0x16,0x86,0x66,0x9d,0xf1,0x6d,0x17,0x15,0xcc,0x58,0xf7,0xa2,0xcf,0xb7,0x2d,0xd1,0xa5,0x1e,0x92,0xf8,0x25,0x99,0x3a,0x74,0x02,0x2b,0xe7,0xe9,0xeb,0x60,0x54,0x65,0x44,0x57,0x09,0x4d,0x14,0x92,0x8f,0x20,0x21,0x5e,0x7b,0x22,0x2a,0xc5,0x6b,0x51,0xad,0xbe,0xc8,0xd8,0xbd,0xb6,0x98,0x39,0x79,0xa7,0xe3,0xa2,0x1b,0x44,0xb5,0xd1,0x51,0x8c,0xa9,0x7d,0x0b,0x51,0x95,0xf5,0x1e,0xd6,0xa2,0x43,0x50,0xc8,0x97,0x47,0xe1,0xed,0xea,0x51,0xb4,0x48,0xe3,0xe9,0x14,0x70,0x54,0xce,0x92,0x78,0x73,0xc9,0x0d,0xb3,0x94,0xd8,0x68,0x88,0xe0,0x7d,0xff,0x17,0x75,0x93,0xd6,0xf7,0x9e,0x15,0x23,0x02,0x20,0x4a,0xeb,0x03,0xbe,0x23,0x86,0xaf,0x3e,0x24,0x07,0x8b,0xd0,0x28,0xb1,0x68,0x9f,0x5e,0x14,0x7c,0x9f,0x45,0x2c,0x8c,0xeb,0x02,0xec,0x59,0xcc,0x9d,0xb6,0x3a,0x03,0x57,0x6c,0xee,0xaf,0xe9,0x82,0x39,0x02,0x38,0x97,0xda,0x02,0x36,0x63,0x0a,0x53,0xc0,0xde,0x7f,0x43,0x5a,0x19,0x86,0x97,0x92,0xfa,0xb3,0x6e,0x7b,0x9e,0x63,0x57,0x60,0xf0,0x90,0x69,0xe6,0x43,0x2e,0x70,0x00,0x35,0xac,0x2a,0x02,0x87,0x9f,0xff,0x0a,0x1e,0x1b,0xec,0x52,0x20,0x47,0x19,0x3d,0x94,0xeb,0x5d,0xf1,0xef,0xd5,0x3e,0xea,0x11,0x44,0xca,0x78,0x94,0x08,0x52,0xf5,0xec,0x97,0x27,0x90,0x4b,0x36,0x6e,0xde,0x4f,0x5e,0x2d,0x33,0x1f,0xad,0x5f,0xc2,0x82,0xea,0x2c,0x47,0xe9,0x23,0x14,0x27,0x71,0xc3,0xdd,0x75,0xa8,0x73,0x57,0x48,0x7d,0xef,0x99,0xe5,0xf1,0x8e,0x9d,0x9e,0xd6,0x23,0xc1,0x75,0xd0,0x28,0x88,0xc5,0x1f,0x82,0xc0,0x7a,0x80,0xd5,0x47,0x16,0xb3,0xc3,0xc2,0xbd,0xbe,0x2e,0x9f,0x0a,0x9b,0xba,0xae,0xbe,0xb4,0xd5,0x29,0x36,0x87,0x64,0x06,0xf5,0xc0,0x0e,0x8e,0x4b,0xbd,0x0a,0x5e,0xc0,0x57,0x97,0xe6,0x20,0x7c,0x5a,0xb6,0xc8,0x8f,0x1a,0x68,0x84,0x21,0xbd,0x05,0xa1,0x14,0xf4,0xd7,0xde,0x2a,0xc2,0x41,0xfa,0x0e,0x8b,0xed,0xff,0x47,0xf7,0x62,0xdd,0xcb,0xea,0xa9,0x10,0x04,0xf8,0xd3,0x1e,0x85,0x09,0x5c,0x81,0x05,0x49,0x94,0xad,0x38,0x26,0xe3,0x44,0xba,0x96,0x04,0x08,0x10,0xfc,0x0b,0x2a,0xd1,0xde,0x48,0xcf,0xad,0xe0,0x02,0xc6,0x2e,0x5a,0x49,0xa0,0x73,0x1a,0xb3,0x83,0x44,0xbc,0x16,0x36,0xdf,0x16,0xbf,0x60,0x7d,0x56,0x85,0x5e,0x56,0xd6,0x84,0x00,0x3c,0x71,0x8e,0x4b,0xad,0x9e,0x5a,0x09,0x99,0x79,0xfc,0xdd,0xee,0xb1,0xc4,0xa7,0x77,0x6c,0xd3,0x7a,0x34,0x17,0xcb,0x0e,0x18,0x4e,0x29,0xef,0x9b,0xc0,0xe8,0x74,0x75,0xba,0x66,0x3b,0xe0,0x9e,0x00,0xab,0x56,0x2e,0xb7,0xc0,0xf7,0x16,0x5f,0x96,0x9a,0x9b,0x42,0x41,0x41,0x98,0xcc,0xf1,0xbf,0xf2,0xa2,0xc8,0xd6,0x89,0xa4,0x14,0xec,0xe7,0x66,0x29,0x27,0x66,0x56,0x89,0xe9,0x4d,0xb9,0x61,0xeb,0xae,0xc5,0x61,0x5c,0xbc,0x1a,0x78,0x95,0xc6,0x85,0x1a,0xc9,0x61,0x43,0x2f,0xf1,0x11,0x8d,0x46,0x07,0xd3,0x2e,0xf9,0xdc,0x73,0x2d,0x51,0x33,0x3b,0xe4,0xb4,0xd0,0xe3,0x0d,0xde,0xa7,0x84,0xec,0xa8,0xbe,0x47,0xe7,0x41,0xbe,0x9c,0x19,0x63,0x1d,0xc4,0x70,0xa5,0x2e,0xf4,0xdc,0x13,0xa4,0xf3,0x63,0x3f,0xd4,0x34,0xd7,0x87,0xc1,0x70,0x97,0x7b,0x41,0x7d,0xf5,0x98,0xe1,0xd0,0xdd,0xe5,0x06,0xbb,0x71,0xd6,0xf0,0xbc,0x17,0xec,0x70,0xe3,0xb0,0x3c,0xdc,0x19,0x65,0xcb,0x36,0x99,0x3f,0x63,0x3b,0x04,0x72,0xe5,0x0d,0x09,0x23,0xac,0x6c,0x66,0xfd,0xf1,0xd3,0xe6,0x45,0x9c,0xc1,0x21,0xf0,0xf5,0xf9,0x4d,0x09,0xe9,0xdb,0xcf,0x5d,0x69,0x0e,0x23,0x23,0x38,0x38,0xa0,0xba,0xcb,0x7c,0x63,0x8d,0x1b,0x26,0x50,0xa4,0x30,0x8c,0xd1,0x71,0xb6,0x85,0x51,0x26,0xd1,0xda,0x67,0x2a,0x6e,0xd8,0x5a,0x8d,0x78,0xc2,0x86,0xfb,0x56,0xf4,0xab,0x3d,0x21,0x49,0x75,0x28,0x04,0x5c,0x63,0x26,0x2c,0x8a,0x42,0xaf,0x2f,0x98,0x02,0xc5,0x3b,0x7b,0xb8,0xbe,0x28,0xe7,0x8f,0xe0,0xb5,0xce,0x45,0xfb,0xb7,0xa1,0xaf,0x1a,0x3b,0x28,0xa8,0xd9,0x4b,0x78,0x90,0xe3,0xc8,0x82,0xe3,0x9b,0xc9,0x8e,0x9f,0x0a,0xd7,0x60,0x25,0xbf,0x0d,0xd2,0xf0,0x02,0x98,0xe7,0x14,0x1a,0x22,0x6b,0x3d,0x7c,0xee,0x41,0x4f,0x60,0x4d,0x1e,0x0b,0xa5,0x4d,0x11,0xd5,0xfe,0x58,0xbc,0xce,0xa6,0xad,0x77,0xad,0x2e,0x8c,0x1c,0xaa,0xcf,0x32,0x45,0x90,0x14,0xb7,0xb9,0x10,0x01,0xb1,0xef,0xa8,0xad,0x17,0x2a,0x52,0x3f,0xb8,0xe3,0x65,0xb5,0x77,0x12,0x1b,0xf9,0xfd,0x88,0xa2,0xc6,0x0c,0x21,0xe8,0x21,0xd7,0xb6,0xac,0xb4,0x7a,0x5a,0x99,0x5e,0x40,0xca,0xce,0xd5,0xc2,0x23,0xb8,0xfe,0x6d,0xe5,0xe1,0x8e,0x9d,0x2e,0x58,0x93,0xae,0xfe,0xbb,0x7a,0xae,0x7f,0xf1,0xa1,0x46,0x26,0x0e,0x2f,0x11,0x0e,0x93,0x95,0x28,0x21,0x3a,0x00,0x25,0xa3,0x8e,0xc7,0x9a,0xab,0xc8,0x61,0xb2,0x5e,0xbc,0x50,0x9a,0x46,0x74,0xc1,0x32,0xaa,0xac,0xb7,0xe0,0x14,0x6f,0x14,0xef,0xd1,0x1c,0xfc,0xaf,0x4c,0xaa,0x4f,0x77,0x5a,0x71,0x6c,0xe3,0x25,0xe0,0xa4,0x35,0xa4,0xd3,0x49,0xd7,0x20,0xbc,0xf1,0x37,0x45,0x0a,0xfc,0x45,0x04,0x6f,0xc1,0xa1,0xf8,0x3a,0x9d,0x32,0x97,0x77,0xa7,0x08,0x4e,0x4a,0xad,0xae,0x71,0x22,0xce,0x97,0x00,0x59,0x30,0x52,0x8e,0xb3,0xc7,0xf7,0xf1,0x12,0x9b,0x37,0x28,0x87,0xa3,0x71,0x15,0x5a,0x3b,0xa2,0x01,0xa2,0x5c,0xbf,0x1d,0xcb,0x64,0xe7,0xcd,0xee,0x09,0x2c,0x31,0x41,0xfb,0x55,0x50,0xfe,0x3d,0x0d,0xd8,0x2e,0x87,0x0e,0x57,0x8b,0x2b,0x46,0x50,0x08,0x18,0x11,0x3b,0x8f,0x65,0x69,0x77,0x3c,0x67,0x73,0x85,0xb6,0x9a,0x42,0xb7,0x7d,0xcb,0xa7,0xac,0xff,0xd9,0x5f,0xd4,0x45,0x2e,0x23,0xaa,0xa1,0xd3,0x7e,0x1d,0xa2,0x15,0x1e,0xa6,0x58,0xd4,0x0a,0x35,0x96,0xb2,0x7a,0xc9,0xf8,0x12,0x9d,0xc6,0xcf,0x06,0x43,0x77,0x26,0x24,0xb5,0x9f,0x4f,0x46,0x12,0x30,0xdf,0x47,0x1c,0xa2,0x60,0x87,0xc3,0x94,0x2d,0x5c,0x66,0x87,0xdf,0x60,0x82,0x83,0x59,0x35,0xa3,0xf8,0x7c,0xb7,0x62,0xb0,0xc3,0xb1,0xd0,0xdd,0xa4,0xa6,0x53,0x39,0x65,0xbe,0xf1,0xb7,0xb8,0x29,0x2e,0x25,0x4c,0x01,0x4d,0x09,0x0f,0xed,0x85,0x7c,0x44,0xc1,0x83,0x9c,0x69,0x4c,0x0a,0x64,0xe3,0xfa,0xd9,0x0a,0x11,0xf5,0x34,0x72,0x2b,0x6e,0xe1,0x57,0x4f,0x2e,0x14,0x9d,0x55,0xd7,0x44,0xde,0x48,0x87,0x02,0x4e,0x08,0x51,0x14,0x31,0xc0,0x62,0x75,0x0e,0x16,0xc7,0x4a,0xb9,0xf3,0x24,0x2f,0x2d,0xb3,0xff,0xb1,0x2a,0x8d,0x61,0x07,0xfa,0xa2,0x29,0xd6,0xf6,0x37,0x3b,0x07,0xf3,0x6d,0x39,0x32,0xb3,0xbd,0xb0,0x4c,0x19,0xdd,0x64,0xea,0xdd,0x7f,0x93,0xc3,0xc5,0x64,0xc3,0x58,0xa1,0xc8,0x1d,0xcf,0x1c,0x9c,0x31,0xe5,0xb0,0x65,0x68,0xf9,0x75,0x44,0xc1,0x7d,0xc1,0x56,0x98,0xc5,0xcb,0x38,0x98,0x3a,0x9a,0xfc,0x42,0x78,0x3f,0xaa,0x77,0x3a,0x52,0xc9,0xd8,0x26,0x06,0x90,0xbe,0x9e,0x31,0x56,0xaa,0x5b,0xc1,0x50,0x9d,0xea,0x3f,0x69,0x58,0x76,0x95,0xcd,0x6f,0xf1,0x72,0xba,0x83,0xe6,0xa6,0xd8,0xa7,0xd6,0xbb,0xeb,0xbb,0xcd,0xa3,0x67,0x27,0x31,0x98,0x3f,0x89,0xbc,0x58,0x31,0xdc,0x37,0xc3,0xf3,0xc5,0xc5,0x6f,0xac,0xc6,0x97,0xf3,0xcb,0x20,0xbd,0x5d,0xba,0xdb,0xd7,0x02,0xe5,0x48,0x44,0xac,0x2f,0x62,0x69,0x01,0xfe,0x15,0x9d,0xb9,0x3d,0xfd,0x47,0x73,0xd8,0xfe,0x73,0x56,0x2b,0x84,0x6c,0x1f,0xc8,0x56,0xd1,0x80,0x27,0x62,0x84,0x0e,0xbc,0x72,0xd7,0x98,0x8b,0xde,0x75,0xcb,0xca,0x70,0xd3,0x19,0xd3,0x2c,0xe0,0xcc,0x02,0x53,0xbb,0x2a,0xd4,0x55,0x72,0x3e,0xe0,0xc7,0xf4,0x73,0x6c,0xe6,0xe6,0x66,0x5c,0x5a,0xca,0x32,0xa4,0x81,0xc5,0x38,0x39,0xbc,0x25,0x91,0x67,0xb0,0x13,0xd0,0x42,0x33,0x95,0xee,0xb9,0xaa,0xae,0xe3,0x20,0x61,0x49,0xa7,0xd5,0x50,0xd6,0x7f,0xc5,0xfd,0xfe,0x4a,0x8a,0x5c,0x35,0xd2,0x51,0x0b,0x66,0x43,0x79,0xab,0x8f,0x72,0x85,0x5a,0x2a,0xf4,0x7a,0xbc,0xe2,0xa6,0x32,0x04,0x8e,0xaf,0x89,0xe5,0xcb,0x4a,0x88,0xde,0xbc,0x53,0xa5,0x95,0x10,0x3a,0xcc,0xe4,0xf1,0xcf,0xf1,0x8a,0xcf,0xf0,0x7a,0xfe,0x1e,0xb5,0x71,0x6a,0xa1,0xe4,0x0b,0x63,0x13,0x4c,0x3a,0x3a,0xe9,0x57,0x9f,0xa8,0x7f,0x51,0x5b,0xe0,0x93,0xc2,0xd2,0x9d,0xb6,0xd6,0xb6,0x5c,0x93,0x66,0x1e,0x00,0x63,0x6b,0x59,0x27,0x04,0xd0,0x93,0xcc,0x67,0x16,0xc2,0x34,0x2e,0xb1,0x85,0x3d,0x48,0xc8,0x5c,0x63,0xac,0x8a,0x28,0x54,0x46,0x2c,0x7b,0x77,0xe7,0xe3,0xbd,0x1e,0xac,0x5b,0xca,0x28,0xff,0xaa,0x00,0xb5,0xd3,0x49,0xf8,0xa5,0x47,0xad,0x87,0x5b,0x96,0xa8,0xc2,0xb2,0x91,0x0c,0x93,0x01,0x30,0x9a,0x3f,0x91,0x38,0xa5,0x69,0x31,0x11,0xf5,0x5b,0x3c,0x00,0x9c,0xa9,0x47,0xc3,0x9d,0xfc,0x82,0xd9,0x8e,0xb1,0xca,0xa4,0xa9,0xcb,0xe8,0x85,0xf7,0x86,0xfa,0x86,0xe5,0x5b,0xe0,0x62,0x22,0x2f,0x8b,0xa9,0x0a,0x97,0x40,0x73,0x32,0x6b,0x31,0x21,0x2a,0xec,0xe0,0xa3,0x4a,0x60]).unwrap(); + let pk = MLDSA87PublicKey::from_bytes(&[ + 0x97, 0x92, 0xBC, 0xEC, 0x2F, 0x24, 0x30, 0x68, 0x6A, 0x82, 0xFC, 0xCF, 0x3C, 0x2F, 0x5F, + 0xF6, 0x65, 0xE7, 0x71, 0xD7, 0xAB, 0x41, 0xB9, 0x02, 0x58, 0xCF, 0xA7, 0xE9, 0x0E, 0xC9, + 0x71, 0x24, 0xA7, 0x3B, 0x32, 0x3B, 0x9B, 0xA2, 0x1A, 0xB6, 0x4D, 0x76, 0x7C, 0x43, 0x3F, + 0x5A, 0x52, 0x1E, 0xFF, 0xE1, 0x8F, 0x86, 0xE4, 0x6A, 0x18, 0x89, 0x52, 0xC4, 0x46, 0x7E, + 0x04, 0x8B, 0x72, 0x9E, 0x7F, 0xC4, 0xD1, 0x15, 0xE7, 0xE4, 0x8D, 0xA1, 0x89, 0x6D, 0x5F, + 0xE1, 0x19, 0xB1, 0x0D, 0xCD, 0xDE, 0xF6, 0x2C, 0xB3, 0x07, 0x95, 0x40, 0x74, 0xB4, 0x23, + 0x36, 0xE5, 0x28, 0x36, 0xDE, 0x61, 0xDA, 0x94, 0x1F, 0x8D, 0x37, 0xEA, 0x68, 0xAC, 0x81, + 0x06, 0xFA, 0xBE, 0x19, 0x07, 0x06, 0x79, 0xAF, 0x60, 0x08, 0x53, 0x71, 0x20, 0xF7, 0x07, + 0x93, 0xB8, 0xEA, 0x9C, 0xC0, 0xE6, 0xE7, 0xB7, 0xB4, 0xC9, 0xA5, 0xC7, 0x42, 0x1C, 0x60, + 0xF2, 0x44, 0x51, 0xBA, 0x1E, 0x93, 0x3D, 0xB1, 0xA2, 0xEE, 0x16, 0xC7, 0x95, 0x59, 0xF2, + 0x1B, 0x3D, 0x1B, 0x83, 0x05, 0x85, 0x0A, 0xA4, 0x2A, 0xFB, 0xB1, 0x3F, 0x1F, 0x4D, 0x5B, + 0x9F, 0x48, 0x35, 0xF9, 0xD8, 0x7D, 0xFC, 0xEB, 0x16, 0x2D, 0x0E, 0xF4, 0xA7, 0xFD, 0xC4, + 0xCB, 0xA1, 0x74, 0x3C, 0xD1, 0xC8, 0x7B, 0xB4, 0x96, 0x7D, 0xA1, 0x6C, 0xC8, 0x76, 0x4B, + 0x65, 0x69, 0xDF, 0x8E, 0xE5, 0xBD, 0xCB, 0xFF, 0xE9, 0xA4, 0xE0, 0x57, 0x48, 0xE6, 0xFD, + 0xF2, 0x25, 0xAF, 0x9E, 0x4E, 0xEB, 0x77, 0x73, 0xB6, 0x2E, 0x8F, 0x85, 0xF9, 0xB5, 0x6B, + 0x54, 0x89, 0x45, 0x55, 0x18, 0x44, 0xFB, 0xD8, 0x98, 0x06, 0xA4, 0xAC, 0x36, 0x9B, 0xED, + 0x2D, 0x25, 0x61, 0x00, 0xF6, 0x88, 0xA6, 0xAD, 0x5E, 0x0A, 0x70, 0x98, 0x26, 0xDC, 0x44, + 0x49, 0xE9, 0x1E, 0x23, 0xC5, 0x50, 0x6E, 0x64, 0x23, 0x61, 0xEF, 0x5A, 0x31, 0x37, 0x12, + 0xF7, 0x9B, 0xC4, 0xB3, 0x18, 0x68, 0x61, 0xCA, 0x85, 0xA4, 0xBA, 0xB1, 0x7E, 0x7F, 0x94, + 0x3D, 0x1B, 0x8A, 0x33, 0x3A, 0xA3, 0xAE, 0x7C, 0xE1, 0x6B, 0x44, 0x0D, 0x60, 0x18, 0xF9, + 0xE0, 0x4D, 0xAF, 0x57, 0x25, 0xC7, 0xF1, 0xA9, 0x3F, 0xAD, 0x1A, 0x5A, 0x27, 0xB6, 0x78, + 0x95, 0xBD, 0x24, 0x9A, 0xA9, 0x16, 0x85, 0xDE, 0x20, 0xAF, 0x32, 0xC8, 0xB7, 0xE2, 0x68, + 0xC7, 0xF9, 0x68, 0x77, 0xD0, 0xC8, 0x50, 0x01, 0x13, 0x5A, 0x4F, 0x0A, 0x8F, 0x1B, 0x82, + 0x64, 0xFA, 0x6E, 0xBE, 0x5A, 0x34, 0x9D, 0x8A, 0xEC, 0xAD, 0x1A, 0x16, 0x29, 0x9C, 0xCF, + 0x2F, 0xD9, 0xC7, 0xB8, 0x5B, 0xAC, 0xE2, 0xCE, 0xD3, 0xAA, 0x12, 0x76, 0xBA, 0x61, 0xEE, + 0x78, 0xED, 0x7E, 0x5C, 0xA5, 0xB6, 0x7C, 0xDD, 0x45, 0x8A, 0x93, 0x54, 0x03, 0x0E, 0x6A, + 0xBB, 0xBA, 0xBF, 0x56, 0xA0, 0xA2, 0x31, 0x6F, 0xEC, 0x9D, 0xBA, 0x83, 0xB5, 0x1D, 0x42, + 0xFD, 0x31, 0x67, 0xF1, 0xE0, 0xF9, 0x08, 0x55, 0xD5, 0xC6, 0x65, 0x09, 0xB2, 0x10, 0x26, + 0x5D, 0xC1, 0xE5, 0x4E, 0xC4, 0x4B, 0x43, 0xBA, 0x7C, 0xF9, 0xAE, 0xF1, 0x18, 0xB4, 0x4D, + 0x80, 0x91, 0x2C, 0xE7, 0x51, 0x66, 0xA6, 0x65, 0x1E, 0x11, 0x6C, 0xEB, 0xE4, 0x92, 0x29, + 0xA7, 0x06, 0x2C, 0x09, 0x93, 0x1F, 0x71, 0xAB, 0xD2, 0x29, 0x3F, 0x76, 0xF7, 0xEF, 0xC3, + 0x21, 0x5B, 0xA9, 0x78, 0x00, 0x03, 0x7E, 0x58, 0xE4, 0x70, 0xBD, 0xBB, 0xB4, 0x3C, 0x1B, + 0x04, 0x39, 0xEA, 0xF7, 0x9C, 0x54, 0xD9, 0x3B, 0x44, 0xAA, 0xC9, 0xEF, 0xE9, 0xFB, 0xE1, + 0x51, 0x87, 0x4C, 0xFB, 0x2A, 0x64, 0xCB, 0xEE, 0x28, 0xCC, 0x4C, 0x0F, 0xE7, 0x77, 0x5E, + 0x5D, 0x87, 0x0F, 0x1C, 0x02, 0xE5, 0xB2, 0xE3, 0xC5, 0x00, 0x4C, 0x99, 0x5F, 0x24, 0xC9, + 0xB7, 0x79, 0xCB, 0x75, 0x3A, 0x27, 0x7D, 0x0E, 0x71, 0xFD, 0x42, 0x5E, 0xB6, 0xBC, 0x2C, + 0xA5, 0x6C, 0xE1, 0x29, 0xDB, 0x51, 0xF7, 0x07, 0x40, 0xF3, 0x1E, 0x63, 0x97, 0x6B, 0x50, + 0xC7, 0x31, 0x2E, 0x97, 0x97, 0xD7, 0x8C, 0x5B, 0x1A, 0xC2, 0x4A, 0x5F, 0xA3, 0x47, 0xCC, + 0x91, 0x6E, 0x0A, 0x83, 0xF5, 0xC3, 0xB6, 0x75, 0xCD, 0x30, 0xB8, 0x1E, 0x3F, 0xA1, 0x0B, + 0x93, 0x44, 0x4E, 0x07, 0x39, 0x75, 0x71, 0xCC, 0xE9, 0x8B, 0x28, 0xDA, 0x51, 0xDB, 0x90, + 0x56, 0xBC, 0x72, 0x8C, 0x5B, 0x0B, 0x11, 0x81, 0xE2, 0xFB, 0xD3, 0x87, 0xB4, 0xC7, 0x9A, + 0xB1, 0xA5, 0xFE, 0xFE, 0xCE, 0x37, 0x16, 0x7A, 0xF7, 0x72, 0xDD, 0xAD, 0x14, 0xEB, 0x4C, + 0x39, 0x82, 0xDA, 0x5A, 0x59, 0xD0, 0xE9, 0xEB, 0x17, 0x3E, 0xC6, 0x31, 0x50, 0x91, 0x17, + 0x00, 0x27, 0xA3, 0xAB, 0x5E, 0xF6, 0xAA, 0x12, 0x9C, 0xB8, 0x58, 0x57, 0x27, 0xB9, 0x35, + 0x8A, 0x28, 0x50, 0x1D, 0x71, 0x3A, 0x72, 0xF3, 0xF1, 0xDB, 0x31, 0x71, 0x42, 0x86, 0xF9, + 0xB6, 0x40, 0x80, 0x13, 0xAF, 0x06, 0x04, 0x5D, 0x75, 0x59, 0x2F, 0xC0, 0xB7, 0xDD, 0x47, + 0xC7, 0x3E, 0xD9, 0xC7, 0x5B, 0x11, 0xE9, 0xD7, 0xC6, 0x9F, 0x7C, 0xAD, 0xFC, 0x32, 0x80, + 0xA9, 0x06, 0x2C, 0x52, 0x73, 0xC4, 0x3B, 0xE1, 0xC3, 0x4F, 0x87, 0x44, 0x88, 0x64, 0xCE, + 0xA7, 0xB5, 0xC9, 0x7D, 0x6D, 0x32, 0xF5, 0x9B, 0xD5, 0xF2, 0x53, 0x84, 0x65, 0x3B, 0xB5, + 0xC4, 0xFA, 0xA4, 0x5B, 0xEA, 0x8B, 0x89, 0x40, 0x28, 0x43, 0xE6, 0x45, 0xB6, 0xB9, 0x26, + 0x9E, 0x2B, 0xD9, 0x88, 0xDD, 0xAC, 0xB0, 0x33, 0x32, 0x8F, 0xFB, 0x06, 0x04, 0x50, 0xF7, + 0xDF, 0x08, 0x00, 0x53, 0xE6, 0x96, 0x9B, 0x25, 0x1E, 0x87, 0x5E, 0xCE, 0xC3, 0x2C, 0xFC, + 0x59, 0x28, 0x40, 0xD6, 0x9A, 0xB6, 0x9A, 0x75, 0xE0, 0x6B, 0x37, 0x9C, 0x53, 0x5D, 0x95, + 0x26, 0x6B, 0x08, 0x2F, 0x4F, 0x09, 0xC9, 0x31, 0x62, 0xB3, 0x3B, 0x0D, 0x9F, 0x73, 0x07, + 0xA4, 0xEA, 0xAA, 0x52, 0x10, 0x44, 0x37, 0xFE, 0xD6, 0x6F, 0x8E, 0xE3, 0xEA, 0xBB, 0xD4, + 0x5D, 0x67, 0xB2, 0x5A, 0x81, 0x33, 0xF4, 0x96, 0x46, 0x8B, 0x52, 0xBA, 0xFF, 0xDB, 0xFA, + 0xD9, 0x3E, 0xEF, 0x1A, 0x98, 0x18, 0xB5, 0xE4, 0x2E, 0xC7, 0x22, 0x78, 0x8A, 0x3D, 0x8D, + 0x35, 0x29, 0xFC, 0x77, 0x7D, 0x2B, 0xA5, 0x70, 0x80, 0x1D, 0xFA, 0xE0, 0x1E, 0xC8, 0x83, + 0x02, 0x83, 0x7C, 0x1F, 0xB9, 0xE0, 0x35, 0x57, 0x27, 0x64, 0x5E, 0xE1, 0x04, 0x6C, 0x3F, + 0x91, 0x5F, 0x6A, 0xE8, 0x2D, 0xAD, 0x4F, 0xB6, 0xB0, 0x35, 0x6A, 0x46, 0x51, 0x8F, 0xFC, + 0x83, 0x41, 0x55, 0xC3, 0xB4, 0xFE, 0x6D, 0xAF, 0xA6, 0xCC, 0x8A, 0x5C, 0xCF, 0x53, 0xC7, + 0x3A, 0x08, 0x49, 0xD8, 0xD4, 0x4F, 0x7D, 0xCF, 0x72, 0x75, 0x4E, 0x70, 0xE1, 0xB7, 0xDF, + 0xB4, 0x47, 0xBB, 0x4E, 0xF4, 0x9D, 0x1A, 0x71, 0x8F, 0x61, 0x71, 0xBB, 0xCE, 0x20, 0x09, + 0x50, 0xE0, 0xCE, 0x92, 0x61, 0x06, 0xB1, 0x51, 0xA3, 0xE8, 0x71, 0xD5, 0xCE, 0x49, 0x73, + 0x1B, 0xD6, 0x65, 0x0A, 0x9B, 0x0C, 0xA9, 0x72, 0xDA, 0x1C, 0x5F, 0x13, 0x6D, 0x44, 0x82, + 0x0E, 0xA6, 0x38, 0x3C, 0x08, 0xF3, 0xB3, 0x84, 0xCF, 0x23, 0x38, 0xE7, 0x89, 0xC5, 0x13, + 0xF6, 0x18, 0xCC, 0x56, 0x94, 0xA6, 0xF0, 0xCE, 0xE1, 0x04, 0x51, 0x1E, 0x1E, 0xD7, 0xC5, + 0xF2, 0x3A, 0x1E, 0xBF, 0xD8, 0xA0, 0xDB, 0x84, 0x24, 0x55, 0x32, 0x40, 0x15, 0x6D, 0xBF, + 0x62, 0x28, 0x31, 0xB0, 0xC6, 0x43, 0xD1, 0xC5, 0x51, 0xB6, 0xF3, 0xF7, 0xA9, 0x8D, 0x29, + 0xB8, 0x5C, 0x2D, 0xE0, 0x5A, 0x65, 0xFA, 0x61, 0x5E, 0xEE, 0x16, 0x49, 0x5B, 0xD9, 0x07, + 0x37, 0x67, 0x21, 0x15, 0xB5, 0x3E, 0x91, 0xC5, 0xD9, 0x00, 0x28, 0xCF, 0x3F, 0x1A, 0x93, + 0x95, 0x3A, 0x15, 0x3D, 0xE5, 0x3B, 0x44, 0x08, 0x4E, 0x9C, 0xCF, 0xF6, 0xB7, 0x36, 0x69, + 0x39, 0x26, 0xDA, 0xEF, 0xEB, 0xB2, 0xD7, 0x7A, 0xA5, 0xAD, 0x68, 0x9B, 0x92, 0xF3, 0x16, + 0x86, 0x66, 0x9D, 0xF1, 0x6D, 0x17, 0x15, 0xCC, 0x58, 0xF7, 0xA2, 0xCF, 0xB7, 0x2D, 0xD1, + 0xA5, 0x1E, 0x92, 0xF8, 0x25, 0x99, 0x3A, 0x74, 0x02, 0x2B, 0xE7, 0xE9, 0xEB, 0x60, 0x54, + 0x65, 0x44, 0x57, 0x09, 0x4D, 0x14, 0x92, 0x8F, 0x20, 0x21, 0x5E, 0x7B, 0x22, 0x2A, 0xC5, + 0x6B, 0x51, 0xAD, 0xBE, 0xC8, 0xD8, 0xBD, 0xB6, 0x98, 0x39, 0x79, 0xA7, 0xE3, 0xA2, 0x1B, + 0x44, 0xB5, 0xD1, 0x51, 0x8C, 0xA9, 0x7D, 0x0B, 0x51, 0x95, 0xF5, 0x1E, 0xD6, 0xA2, 0x43, + 0x50, 0xC8, 0x97, 0x47, 0xE1, 0xED, 0xEA, 0x51, 0xB4, 0x48, 0xE3, 0xE9, 0x14, 0x70, 0x54, + 0xCE, 0x92, 0x78, 0x73, 0xC9, 0x0D, 0xB3, 0x94, 0xD8, 0x68, 0x88, 0xE0, 0x7D, 0xFF, 0x17, + 0x75, 0x93, 0xD6, 0xF7, 0x9E, 0x15, 0x23, 0x02, 0x20, 0x4A, 0xEB, 0x03, 0xBE, 0x23, 0x86, + 0xAF, 0x3E, 0x24, 0x07, 0x8B, 0xD0, 0x28, 0xB1, 0x68, 0x9F, 0x5E, 0x14, 0x7C, 0x9F, 0x45, + 0x2C, 0x8C, 0xEB, 0x02, 0xEC, 0x59, 0xCC, 0x9D, 0xB6, 0x3A, 0x03, 0x57, 0x6C, 0xEE, 0xAF, + 0xE9, 0x82, 0x39, 0x02, 0x38, 0x97, 0xDA, 0x02, 0x36, 0x63, 0x0A, 0x53, 0xC0, 0xDE, 0x7F, + 0x43, 0x5A, 0x19, 0x86, 0x97, 0x92, 0xFA, 0xB3, 0x6E, 0x7B, 0x9E, 0x63, 0x57, 0x60, 0xF0, + 0x90, 0x69, 0xE6, 0x43, 0x2E, 0x70, 0x00, 0x35, 0xAC, 0x2A, 0x02, 0x87, 0x9F, 0xFF, 0x0A, + 0x1E, 0x1B, 0xEC, 0x52, 0x20, 0x47, 0x19, 0x3D, 0x94, 0xEB, 0x5D, 0xF1, 0xEF, 0xD5, 0x3E, + 0xEA, 0x11, 0x44, 0xCA, 0x78, 0x94, 0x08, 0x52, 0xF5, 0xEC, 0x97, 0x27, 0x90, 0x4B, 0x36, + 0x6E, 0xDE, 0x4F, 0x5E, 0x2D, 0x33, 0x1F, 0xAD, 0x5F, 0xC2, 0x82, 0xEA, 0x2C, 0x47, 0xE9, + 0x23, 0x14, 0x27, 0x71, 0xC3, 0xDD, 0x75, 0xA8, 0x73, 0x57, 0x48, 0x7D, 0xEF, 0x99, 0xE5, + 0xF1, 0x8E, 0x9D, 0x9E, 0xD6, 0x23, 0xC1, 0x75, 0xD0, 0x28, 0x88, 0xC5, 0x1F, 0x82, 0xC0, + 0x7A, 0x80, 0xD5, 0x47, 0x16, 0xB3, 0xC3, 0xC2, 0xBD, 0xBE, 0x2E, 0x9F, 0x0A, 0x9B, 0xBA, + 0xAE, 0xBE, 0xB4, 0xD5, 0x29, 0x36, 0x87, 0x64, 0x06, 0xF5, 0xC0, 0x0E, 0x8E, 0x4B, 0xBD, + 0x0A, 0x5E, 0xC0, 0x57, 0x97, 0xE6, 0x20, 0x7C, 0x5A, 0xB6, 0xC8, 0x8F, 0x1A, 0x68, 0x84, + 0x21, 0xBD, 0x05, 0xA1, 0x14, 0xF4, 0xD7, 0xDE, 0x2A, 0xC2, 0x41, 0xFA, 0x0E, 0x8B, 0xED, + 0xFF, 0x47, 0xF7, 0x62, 0xDD, 0xCB, 0xEA, 0xA9, 0x10, 0x04, 0xF8, 0xD3, 0x1E, 0x85, 0x09, + 0x5C, 0x81, 0x05, 0x49, 0x94, 0xAD, 0x38, 0x26, 0xE3, 0x44, 0xBA, 0x96, 0x04, 0x08, 0x10, + 0xFC, 0x0B, 0x2A, 0xD1, 0xDE, 0x48, 0xCF, 0xAD, 0xE0, 0x02, 0xC6, 0x2E, 0x5A, 0x49, 0xA0, + 0x73, 0x1A, 0xB3, 0x83, 0x44, 0xBC, 0x16, 0x36, 0xDF, 0x16, 0xBF, 0x60, 0x7D, 0x56, 0x85, + 0x5E, 0x56, 0xD6, 0x84, 0x00, 0x3C, 0x71, 0x8E, 0x4B, 0xAD, 0x9E, 0x5A, 0x09, 0x99, 0x79, + 0xFC, 0xDD, 0xEE, 0xB1, 0xC4, 0xA7, 0x77, 0x6C, 0xD3, 0x7A, 0x34, 0x17, 0xCB, 0x0E, 0x18, + 0x4E, 0x29, 0xEF, 0x9B, 0xC0, 0xE8, 0x74, 0x75, 0xBA, 0x66, 0x3B, 0xE0, 0x9E, 0x00, 0xAB, + 0x56, 0x2E, 0xB7, 0xC0, 0xF7, 0x16, 0x5F, 0x96, 0x9A, 0x9B, 0x42, 0x41, 0x41, 0x98, 0xCC, + 0xF1, 0xBF, 0xF2, 0xA2, 0xC8, 0xD6, 0x89, 0xA4, 0x14, 0xEC, 0xE7, 0x66, 0x29, 0x27, 0x66, + 0x56, 0x89, 0xE9, 0x4D, 0xB9, 0x61, 0xEB, 0xAE, 0xC5, 0x61, 0x5C, 0xBC, 0x1A, 0x78, 0x95, + 0xC6, 0x85, 0x1A, 0xC9, 0x61, 0x43, 0x2F, 0xF1, 0x11, 0x8D, 0x46, 0x07, 0xD3, 0x2E, 0xF9, + 0xDC, 0x73, 0x2D, 0x51, 0x33, 0x3B, 0xE4, 0xB4, 0xD0, 0xE3, 0x0D, 0xDE, 0xA7, 0x84, 0xEC, + 0xA8, 0xBE, 0x47, 0xE7, 0x41, 0xBE, 0x9C, 0x19, 0x63, 0x1D, 0xC4, 0x70, 0xA5, 0x2E, 0xF4, + 0xDC, 0x13, 0xA4, 0xF3, 0x63, 0x3F, 0xD4, 0x34, 0xD7, 0x87, 0xC1, 0x70, 0x97, 0x7B, 0x41, + 0x7D, 0xF5, 0x98, 0xE1, 0xD0, 0xDD, 0xE5, 0x06, 0xBB, 0x71, 0xD6, 0xF0, 0xBC, 0x17, 0xEC, + 0x70, 0xE3, 0xB0, 0x3C, 0xDC, 0x19, 0x65, 0xCB, 0x36, 0x99, 0x3F, 0x63, 0x3B, 0x04, 0x72, + 0xE5, 0x0D, 0x09, 0x23, 0xAC, 0x6C, 0x66, 0xFD, 0xF1, 0xD3, 0xE6, 0x45, 0x9C, 0xC1, 0x21, + 0xF0, 0xF5, 0xF9, 0x4D, 0x09, 0xE9, 0xDB, 0xCF, 0x5D, 0x69, 0x0E, 0x23, 0x23, 0x38, 0x38, + 0xA0, 0xBA, 0xCB, 0x7C, 0x63, 0x8D, 0x1B, 0x26, 0x50, 0xA4, 0x30, 0x8C, 0xD1, 0x71, 0xB6, + 0x85, 0x51, 0x26, 0xD1, 0xDA, 0x67, 0x2A, 0x6E, 0xD8, 0x5A, 0x8D, 0x78, 0xC2, 0x86, 0xFB, + 0x56, 0xF4, 0xAB, 0x3D, 0x21, 0x49, 0x75, 0x28, 0x04, 0x5C, 0x63, 0x26, 0x2C, 0x8A, 0x42, + 0xAF, 0x2F, 0x98, 0x02, 0xC5, 0x3B, 0x7B, 0xB8, 0xBE, 0x28, 0xE7, 0x8F, 0xE0, 0xB5, 0xCE, + 0x45, 0xFB, 0xB7, 0xA1, 0xAF, 0x1A, 0x3B, 0x28, 0xA8, 0xD9, 0x4B, 0x78, 0x90, 0xE3, 0xC8, + 0x82, 0xE3, 0x9B, 0xC9, 0x8E, 0x9F, 0x0A, 0xD7, 0x60, 0x25, 0xBF, 0x0D, 0xD2, 0xF0, 0x02, + 0x98, 0xE7, 0x14, 0x1A, 0x22, 0x6B, 0x3D, 0x7C, 0xEE, 0x41, 0x4F, 0x60, 0x4D, 0x1E, 0x0B, + 0xA5, 0x4D, 0x11, 0xD5, 0xFE, 0x58, 0xBC, 0xCE, 0xA6, 0xAD, 0x77, 0xAD, 0x2E, 0x8C, 0x1C, + 0xAA, 0xCF, 0x32, 0x45, 0x90, 0x14, 0xB7, 0xB9, 0x10, 0x01, 0xB1, 0xEF, 0xA8, 0xAD, 0x17, + 0x2A, 0x52, 0x3F, 0xB8, 0xE3, 0x65, 0xB5, 0x77, 0x12, 0x1B, 0xF9, 0xFD, 0x88, 0xA2, 0xC6, + 0x0C, 0x21, 0xE8, 0x21, 0xD7, 0xB6, 0xAC, 0xB4, 0x7A, 0x5A, 0x99, 0x5E, 0x40, 0xCA, 0xCE, + 0xD5, 0xC2, 0x23, 0xB8, 0xFE, 0x6D, 0xE5, 0xE1, 0x8E, 0x9D, 0x2E, 0x58, 0x93, 0xAE, 0xFE, + 0xBB, 0x7A, 0xAE, 0x7F, 0xF1, 0xA1, 0x46, 0x26, 0x0E, 0x2F, 0x11, 0x0E, 0x93, 0x95, 0x28, + 0x21, 0x3A, 0x00, 0x25, 0xA3, 0x8E, 0xC7, 0x9A, 0xAB, 0xC8, 0x61, 0xB2, 0x5E, 0xBC, 0x50, + 0x9A, 0x46, 0x74, 0xC1, 0x32, 0xAA, 0xAC, 0xB7, 0xE0, 0x14, 0x6F, 0x14, 0xEF, 0xD1, 0x1C, + 0xFC, 0xAF, 0x4C, 0xAA, 0x4F, 0x77, 0x5A, 0x71, 0x6C, 0xE3, 0x25, 0xE0, 0xA4, 0x35, 0xA4, + 0xD3, 0x49, 0xD7, 0x20, 0xBC, 0xF1, 0x37, 0x45, 0x0A, 0xFC, 0x45, 0x04, 0x6F, 0xC1, 0xA1, + 0xF8, 0x3A, 0x9D, 0x32, 0x97, 0x77, 0xA7, 0x08, 0x4E, 0x4A, 0xAD, 0xAE, 0x71, 0x22, 0xCE, + 0x97, 0x00, 0x59, 0x30, 0x52, 0x8E, 0xB3, 0xC7, 0xF7, 0xF1, 0x12, 0x9B, 0x37, 0x28, 0x87, + 0xA3, 0x71, 0x15, 0x5A, 0x3B, 0xA2, 0x01, 0xA2, 0x5C, 0xBF, 0x1D, 0xCB, 0x64, 0xE7, 0xCD, + 0xEE, 0x09, 0x2C, 0x31, 0x41, 0xFB, 0x55, 0x50, 0xFE, 0x3D, 0x0D, 0xD8, 0x2E, 0x87, 0x0E, + 0x57, 0x8B, 0x2B, 0x46, 0x50, 0x08, 0x18, 0x11, 0x3B, 0x8F, 0x65, 0x69, 0x77, 0x3C, 0x67, + 0x73, 0x85, 0xB6, 0x9A, 0x42, 0xB7, 0x7D, 0xCB, 0xA7, 0xAC, 0xFF, 0xD9, 0x5F, 0xD4, 0x45, + 0x2E, 0x23, 0xAA, 0xA1, 0xD3, 0x7E, 0x1D, 0xA2, 0x15, 0x1E, 0xA6, 0x58, 0xD4, 0x0A, 0x35, + 0x96, 0xB2, 0x7A, 0xC9, 0xF8, 0x12, 0x9D, 0xC6, 0xCF, 0x06, 0x43, 0x77, 0x26, 0x24, 0xB5, + 0x9F, 0x4F, 0x46, 0x12, 0x30, 0xDF, 0x47, 0x1C, 0xA2, 0x60, 0x87, 0xC3, 0x94, 0x2D, 0x5C, + 0x66, 0x87, 0xDF, 0x60, 0x82, 0x83, 0x59, 0x35, 0xA3, 0xF8, 0x7C, 0xB7, 0x62, 0xB0, 0xC3, + 0xB1, 0xD0, 0xDD, 0xA4, 0xA6, 0x53, 0x39, 0x65, 0xBE, 0xF1, 0xB7, 0xB8, 0x29, 0x2E, 0x25, + 0x4C, 0x01, 0x4D, 0x09, 0x0F, 0xED, 0x85, 0x7C, 0x44, 0xC1, 0x83, 0x9C, 0x69, 0x4C, 0x0A, + 0x64, 0xE3, 0xFA, 0xD9, 0x0A, 0x11, 0xF5, 0x34, 0x72, 0x2B, 0x6E, 0xE1, 0x57, 0x4F, 0x2E, + 0x14, 0x9D, 0x55, 0xD7, 0x44, 0xDE, 0x48, 0x87, 0x02, 0x4E, 0x08, 0x51, 0x14, 0x31, 0xC0, + 0x62, 0x75, 0x0E, 0x16, 0xC7, 0x4A, 0xB9, 0xF3, 0x24, 0x2F, 0x2D, 0xB3, 0xFF, 0xB1, 0x2A, + 0x8D, 0x61, 0x07, 0xFA, 0xA2, 0x29, 0xD6, 0xF6, 0x37, 0x3B, 0x07, 0xF3, 0x6D, 0x39, 0x32, + 0xB3, 0xBD, 0xB0, 0x4C, 0x19, 0xDD, 0x64, 0xEA, 0xDD, 0x7F, 0x93, 0xC3, 0xC5, 0x64, 0xC3, + 0x58, 0xA1, 0xC8, 0x1D, 0xCF, 0x1C, 0x9C, 0x31, 0xE5, 0xB0, 0x65, 0x68, 0xF9, 0x75, 0x44, + 0xC1, 0x7D, 0xC1, 0x56, 0x98, 0xC5, 0xCB, 0x38, 0x98, 0x3A, 0x9A, 0xFC, 0x42, 0x78, 0x3F, + 0xAA, 0x77, 0x3A, 0x52, 0xC9, 0xD8, 0x26, 0x06, 0x90, 0xBE, 0x9E, 0x31, 0x56, 0xAA, 0x5B, + 0xC1, 0x50, 0x9D, 0xEA, 0x3F, 0x69, 0x58, 0x76, 0x95, 0xCD, 0x6F, 0xF1, 0x72, 0xBA, 0x83, + 0xE6, 0xA6, 0xD8, 0xA7, 0xD6, 0xBB, 0xEB, 0xBB, 0xCD, 0xA3, 0x67, 0x27, 0x31, 0x98, 0x3F, + 0x89, 0xBC, 0x58, 0x31, 0xDC, 0x37, 0xC3, 0xF3, 0xC5, 0xC5, 0x6F, 0xAC, 0xC6, 0x97, 0xF3, + 0xCB, 0x20, 0xBD, 0x5D, 0xBA, 0xDB, 0xD7, 0x02, 0xE5, 0x48, 0x44, 0xAC, 0x2F, 0x62, 0x69, + 0x01, 0xFE, 0x15, 0x9D, 0xB9, 0x3D, 0xFD, 0x47, 0x73, 0xD8, 0xFE, 0x73, 0x56, 0x2B, 0x84, + 0x6C, 0x1F, 0xC8, 0x56, 0xD1, 0x80, 0x27, 0x62, 0x84, 0x0E, 0xBC, 0x72, 0xD7, 0x98, 0x8B, + 0xDE, 0x75, 0xCB, 0xCA, 0x70, 0xD3, 0x19, 0xD3, 0x2C, 0xE0, 0xCC, 0x02, 0x53, 0xBB, 0x2A, + 0xD4, 0x55, 0x72, 0x3E, 0xE0, 0xC7, 0xF4, 0x73, 0x6C, 0xE6, 0xE6, 0x66, 0x5C, 0x5A, 0xCA, + 0x32, 0xA4, 0x81, 0xC5, 0x38, 0x39, 0xBC, 0x25, 0x91, 0x67, 0xB0, 0x13, 0xD0, 0x42, 0x33, + 0x95, 0xEE, 0xB9, 0xAA, 0xAE, 0xE3, 0x20, 0x61, 0x49, 0xA7, 0xD5, 0x50, 0xD6, 0x7F, 0xC5, + 0xFD, 0xFE, 0x4A, 0x8A, 0x5C, 0x35, 0xD2, 0x51, 0x0B, 0x66, 0x43, 0x79, 0xAB, 0x8F, 0x72, + 0x85, 0x5A, 0x2A, 0xF4, 0x7A, 0xBC, 0xE2, 0xA6, 0x32, 0x04, 0x8E, 0xAF, 0x89, 0xE5, 0xCB, + 0x4A, 0x88, 0xDE, 0xBC, 0x53, 0xA5, 0x95, 0x10, 0x3A, 0xCC, 0xE4, 0xF1, 0xCF, 0xF1, 0x8A, + 0xCF, 0xF0, 0x7A, 0xFE, 0x1E, 0xB5, 0x71, 0x6A, 0xA1, 0xE4, 0x0B, 0x63, 0x13, 0x4C, 0x3A, + 0x3A, 0xE9, 0x57, 0x9F, 0xA8, 0x7F, 0x51, 0x5B, 0xE0, 0x93, 0xC2, 0xD2, 0x9D, 0xB6, 0xD6, + 0xB6, 0x5C, 0x93, 0x66, 0x1E, 0x00, 0x63, 0x6B, 0x59, 0x27, 0x04, 0xD0, 0x93, 0xCC, 0x67, + 0x16, 0xC2, 0x34, 0x2E, 0xB1, 0x85, 0x3D, 0x48, 0xC8, 0x5C, 0x63, 0xAC, 0x8A, 0x28, 0x54, + 0x46, 0x2C, 0x7B, 0x77, 0xE7, 0xE3, 0xBD, 0x1E, 0xAC, 0x5B, 0xCA, 0x28, 0xFF, 0xAA, 0x00, + 0xB5, 0xD3, 0x49, 0xF8, 0xA5, 0x47, 0xAD, 0x87, 0x5B, 0x96, 0xA8, 0xC2, 0xB2, 0x91, 0x0C, + 0x93, 0x01, 0x30, 0x9A, 0x3F, 0x91, 0x38, 0xA5, 0x69, 0x31, 0x11, 0xF5, 0x5B, 0x3C, 0x00, + 0x9C, 0xA9, 0x47, 0xC3, 0x9D, 0xFC, 0x82, 0xD9, 0x8E, 0xB1, 0xCA, 0xA4, 0xA9, 0xCB, 0xE8, + 0x85, 0xF7, 0x86, 0xFA, 0x86, 0xE5, 0x5B, 0xE0, 0x62, 0x22, 0x2F, 0x8B, 0xA9, 0x0A, 0x97, + 0x40, 0x73, 0x32, 0x6B, 0x31, 0x21, 0x2A, 0xEC, 0xE0, 0xA3, 0x4A, 0x60, + ]) + .unwrap(); let sig = &*hex::decode("781368e64dba542a7eacbd2257335cc943a03241009b797093c615f76a671a7591430441d80bb582304b33b9fce295e0dd57fe169355ddf4453a2aca62d8eb8109ef0d9cf3f5b0a94e04ad81b3e786014243ecde816551aa7fe01c639054256a491756bef59f5034f717ff4f85e70ba7731a49971415b6a7e7d816ab434b9f17a3095ede6fd432be2bfa82724045dda0dfff7a0281e9000939ccba3d8ab3245139c441648c76a6536127e4d1ef0df1531883ab78c8b41323617ad8db03d9908c9e08a9f7321c45051b3c94213347b11c4a84491de7a7be68701e47d7f0e0b33e767bef17694e4d33244ed92ebd74c85ab6c84441cddc14331e6ae8bd23674bda27f09c050d88f7d430feee7f15a72a24d653bb6bec54491b98362ce131d37c7d78a3f9a893db5abdccd6663593b88bc6c97f07f8eafccfd25e8180d918efbcd95bbf3da29f081e3e1932095939198e2a155b2d803a3e84ca4f34569df695c259faf3c0d8f0cd217ebd2dbad542b32fbb54e44aaf0b5dc739fafef2e46db8d68bfc35f44f038cb1f5231a1b5b134ae683e7f3297cc7a95bd191b310f68201450797fe3293cde1672dfeca4b493f53c768ea048a972a4cd84d39ef682957b8f28ba29487b4689b43fec2655823d9bb99ffcf31490366a9860a5d5b8e32a3b8bfeb6f55f88fb80c8c0142086f220e1f6f2862dabda58c3b6f5faa805b39cfac4b6d7ea7acdf1b0690063b0c1ea38c7c4755189966dc631055f153f71b77b114fa5c309316ba512330ea5cdb0bb176001e57461563d17259f35d0c30ef5ac838c0325402bab52c531469526ae3ee6293f7b5769d27e69fa81cd25a31cd095b126e70c57ac3169a5f585a11f1748d9d22f2564911c26a24b2153a78f3a06822f5f1963f237abeb48efd9a9cd478de579c5a0ba84d00e96fbde36d8ce20e7e948547fb6850834ff79d211830f6ee973359781d9d5008fb43a89354782fde4158177f5206ce1d38c889e99e4bb5b4ab34d6a05c42f5d719ea03dbc54adba75a3bb44a3c08c7556462f8c5b7c568a69242cf5be6098eba0a2249c1ca5b2109b6404a962abc1c159c6b48a79fb97e4a3337d99323746221297423f9bd1b12e78489e01e6a10f0fd6bba1cfd6ae1b75dbe69f8b8ee51a4e7f68ba2c407c9c0bad3892b29b0170ab75836fdd49a7ee3c2bb30f2c3d226bcec49140952170b0d160f97b30b7b7b096719538677ebb06922f26925227c8852acc107a8f173b38d96697584bd3dfe169b4073aa58a7bf371d5c4bb0eed30f08212defb3aec902d4546084176bf0f86d93cf36a4689a5e874b32d6b7d3c1e3fbcfd988c35dbc9a8d0a019ad6d7e15ec3ac97125db6abfe00beffe35a81666699a91e15945c62d646690b5b52de8b835ee9be53588fde5d63023b52b2b1f4610c237a829f5901a46042963cce7b85aa040adde02985e14e23c4eadb75221c607d24672e244c66c9c24c3cb7fd90bb23295c9d3d9da516bce3dd462d6660f9f91ef0618a4d4d3d6668c5d1e2e8ed433ebfe0762beb743324e11608f8b14b69ce4c221c1654ba4992a5af2d949c2939f95d1c8fb767af2a843cc7c78f57259d5c0c6ca83fca41ef5ecc4eeeea93e4518c24d3040f2cd90df3e535e989e606fa109e2c453ed7353db1cdb27137f005f9dd8d2aebfb7255a6098b690215e100cfe44ca0f2745fced48322bc9667ab16d2e1c0ff491b96a17b833d4fd44d31c2230ac835796e063ab03000f04f15c70560033763a48552cceaacade9ca5c8055f3745e179068a287183f2bc3ef6327dec5ac7cf7b052ef5a8873e697efde089688f43be464827c2fa83eb531b3674145e95c699c82990e684967dad319d9f64ab16cd9fe9b6c41232ac4ae3795fd8a76aa9b02e970242061c6da45a2af74ad9cb2a79935c92625e242f4bc7fce54d5c10a9e61f875162fa651b66057ba036f062d6d39d0502b93a5640b78c6c2fd20b02ff83676a87a94945d476c349803ea4fe60cdcea65bb2629e2bc09d4472ec63422dee2052f098deaf5531e6c9bed6672a8b699802efe0cded80c8455f585d1ba633d281f1a21adab48e63b44e0c2a4d7608cf98aabf8adc86bcb8f61e8b06cd2385f82e0a3cdd03cab152d5951859c4532f9168e78f17ba2a5772780327dcf4e62b4d26e443762fc488ae4cd4d1156dbd5782595cfd7697a514abc9b160c9ccf08edc86134a755b90e9bb543511e888e3157721a52d1bc5db33029fb335ea2114e21c03368c8d7f4d827960641772a4a32a738df60d19ec77ab09d22f57cc2523b9503b3f5b1cebc5ae15f885f159842db7359a1c89d3d82d3407068f15b6739626eb8c521fc8c5c7491f945d49f14e6989da340bdf49e7f8a792747aa658bc114143ba93f26022d001735b744639bbf22aab2a1851cfc934f9c69d3764fdea3d23db17998e6138cfd7cd9e9a47cb74193bd71aaf28cdd9d1eb595125546a4f4357ebbd1f410e3bf8557892de68509b5b98c5c229e942c910fdd3e54cb6ad54d8dd886cb97ecc06d1e401b8395d0bcb0db9a031dc66c9294f9053c68fc42042b1fa1671fc7d510b70916c0139cfebe3a91244527ce9439860cedb30908197be851cbd1d3b18ca541358449fb34fb5cd569630ed5f67b8795e87828f2ce3becfe457579d82333b0bbab094de391e1f8157bd431e365ca864630932bbebb48f45f8134424e18ab455029b54b19e2f3bfec5e44ad0ea5c03f53d8f925b635838aa7015a7c9e325bdfaff966ea9512dd50f87c8995cea7561c23f4fb06d964ab8f1913a6ca17e4ca60d6bc078e1784f89c673c91d955bcf45f58ca9709579d5e3831df12cfdb7516fd21878cb54243579b9346d2de4be25f508e84b1adc78cb91c03da3c4fd59e4529189838f74f6312820620a5996b791ffcb332f847094613f2148b862034fa89d0d0ff1808d902c5d1af64d5522492d61ecde4c73be89a33782cef1acc1dc327fb2eb9d17642209b85aa8b1dc57cbf067c7aa29da6b7e157d23e171d3ae6f3855834071791402c851ff2dd67109979f7ee5e09e64b4eefdee7112b55ce200bb8c8051e3428c305fa1d576bffbb25a70eb571168fc60dadd928b10cfd07de80a85b8df3edc372d488c21f0d5787611cc6fb73aeeb6f920a109294b49d3870f90de3b360d14df77ef95640bcac7a4dbca901a31db83e83f5c59ce327207ea9b27c3b978d30d53865c1b84764f025e8732d5007554ce5c9cc410b2eefd7e4d990c538557606a6bc47577a43768d30aa3e8598fd6f4fe7ac439f3931c58fd69d90765ac9f456ac7de085e14a0898c4557f5d3baaae07edc607de6900146b97b35aae570153dc107815ef9febdd4fd567d637fee8f8bfb4b3413ea6aead4846ab733a04f1e4bc32a3bbb1c16baf8d0bdb9ccb82fe46479ccfd040b5e64064e539b39c66e4501dc822873ac6119a4a112a1f7cd6df0e5f84356ce853ced34ce69a9e7383534983c51c50269bf8b9586a0e5ba905fd3bce080b00e7f7d48e55f489479b5771e995fb020e58feb74af65c3ee76aa4e69b5ba8bda249a1b2d62c08d418c3635d061846040843991ab475473da85d94981fb84425e7ad951ce0a42be642fe658b7aaae72b147cdc086c24b1571eb2272e2a72b15660d854ebe19ec7d9ab7ea17800d0b6ae727b39217467c662ba08e6f19193951eeff02806a7843eb5c71b2f04dcb605ecedc5128cd67703038c44bf20fe06f3ed8c1368fe38e72944d5c52fca46a45fd48d8fe5da64183d4d62ec01aa3d9d672ec67a01c17f21e02525f0513cce030c664fc8784763086608bc8099c204c255ffed1daf432ceb45fbd135e21e8190c5bfee192171faa77520481e69e87b7f76790bef76cb8d3c88f5c6e32fc59e7bd45351d66696b61d9f40726fb9a98000b68738cf7e34b98b6a4aaa2ac1d7b1407db89783f8077103ea9c9e89247eae078adfb36e21474c3bb1fe0c87687c6233a533a01e1081b93a3521d339f39c075609bace531994988ae314f77fc6034113a138c67eb7e03750cbec8d28bd21afedfefa8f091619ae500b4ca4599d019dc8ca4bf118d70b8676dfc796a4f6d986adba4c8574ed4abaea5465466220e5e53dc8fcb395d1e59d278673cbc4e3f40658df98ac2fd126a94922879e1a3be91c1acc20803c35fa764abbadab07bde85ff4bd9e0fb6f06baf5bf42b8a2cbf6c2f62606becc361552921a12d6c8236fde84db4bddac77e8872478cffc4e148c1c7acfedf6b17d98731c2de36f3cbef1f6f781a940e0874d5b74535bbe066b53064d43b13926570a9e1c4e6da206c8bd252caf2b62e7d223f7ac12939137f330be59374d7295a6c2dff92e07c727510e48d970593e47229fc8bc3bd5b8ea780dacff4d23063df65feda5f8f65b17a333e532acad7916780c74d6a70d38b367f3f6f4e947b85fc15235bbe46b26495d2780098db853a931377cfbedea620f2355ca21e81ce9e0078b0dd6cb70f23ed558682be3b3d594eefe85344e1f275428b316cc088995939298f2a2d15ac9b676ac3e9cb92f2a64dec7732a91fc761aa1b126ea575e3953177da6e1cd78faea824665330a81d9e24572b9860bf0aba4df8bd5d4e3e2c72bbfbb2a985c7ae2f077951fe8401e1d156ecada1e353817b20f41e0b2460a0caaf2b36d6a7f1b35125d797dcc714421027d14171765a646071ed952b6a5294eecf6a3a71c104c843a4a8b3efcc27467b20cb0a94abf5802229ea4d8312783e78791a50b3c0a88fe6497198cd4bf470dac46f34e50019fdee2040cfe99124b312b1122b83e51d878877cec0855f1158c445cfdc2253f4389d5e3a8ba1669abb5976a4617e85f543da9f5e30b10ca7481c8185392782b46fb0a0e5ab408b2945e3c79a1cc49fb7c27254a9b540e7397a5b655bf7e4f83184db32a128aa2e00a624d7dcd6b77efd151f1e5cba8890af9170fa06c555715dc1787e995ad19270973ae95b88dcafdaf62c28843d3f8b9c78dc8e37d911dab3f7ee9d4c7389c654bdcfc05056b360020140e57e31473258a4081e2a708f7caba90c356d0847098fc0762484086aba898a60b023d6a3060402406240748785d51caff52a0ef3dc2a45dadf80ac18502d24422a8cbb10192b88f4e9160206b2ed3e04114f2a339df269e2c36b8613ce37087471701755330cb559575366ee0fa2d3afcda32eede6dd906345daaa04812198e96c42239c242edb90059709f497da5b87705384aef2af22dc2edfef3c00d8c9156d8b3163fe7a7779e04f04911a8b934fb3072eee844484fede5e2ee96d338eefe2da986067ffe0218ada7de1d0e42d823d6b033918278888ba0608ab8f7be997bdf263689a36f5204c802ad836363779b4b0d6ce5083df0b98a2e2c700062a4fa5e57bc73bd45357e01d90c7954bc6904d1ce8166a9168da39a60c5cae8119bb6b9ab074fb2d0aee384fc2c0e4806811d6002b4e2401e7430b50cb0e8075f33d5386aecde256e169d95e2f9c6556c08ba042e68a53ce8aca9cc02818f7382f150dc04de0019b19c7a3ab0d72d6ed013d7a115d74b279f71fd6effef34049877e0b11e0659be938a5de684eaf23513095eb4a1bdf536c3c01a4655c4b4a0673214cef29a481d06a02cc9a5bfc7b8d846c33484cd67b1de98f60b69918f177b64558ca567a6237d35ff01771a42320ce02bb98f3e4ad4ac7db75611bd9961eac662a38b1f785970c99f3dd105ff586f61301c48d66708cbf7d53a733e357b6c256e8b73f0e1305a0bc137989e521100c2ee6259e607fe12198e8bcb988b0854668e40d7cae6adc3ba40ba121b7319d06d988a073d03097b9f5c1c07284b6473ae57bf154811b77baceb0412b8a6983bdd0ccd9e3bf014e520009cb26d5780eabef1bbafa5e25d41098a54c47fce8b68d395291d54284d33aa50b9664d1510b467c8a539361ca9a4448bc01fcb4c4e3ef475e8afb46a494ae13ee9ea8a1266825fba7f32b9712fde252698a68359b50141d90f5c4a06283ddb54ad7e1412ac5ebb12501f7a82b2a7f27b2dbab626c3db4074523b3211d3182ea261397a6f7b187cf2b8a356ded10812f1d305169aedf79b5ff1cf7c2d6e86ee11f28e96aa63b5a03f59fc960ac7d0572e91dda61905c0711a9b26344a2a10aa2041f2b13cb1a9a9a27774b6d0deddc9d81ea1b142ad7b72be47991f2c9261d6708156e38d00b074020766eb0c494392d65b82ca65f7c3352f9bb78325ecb6df596c8ae57826b08ccd6f1d529d2e25925c1ac972425bedeb88a5d0e3138ddc434da3462ebdf6b1239a21f141ec62cbe4bb993ba253b55a76d30fac19c2c1384ef6b9746c07787aa1fe913a1348390bd8c1f386a08c77cf7106c927ce24dffc9d6ee1b32354d95ed2923482531de6b390bf0f5eb80276e90e7ed11131c848bbabec4d317236269a0a3a7cbe0f1272f93949ca6d23ea2a7ee3f697791aab71533423066d400000000000000000000000000000000000000000000000000000000050e181f23292c2f").unwrap(); assert_eq!(sig.len(), MLDSA87_SIG_LEN); @@ -462,8 +2129,8 @@ fn bench_mldsa87_verify() { } fn bench_mldsa87_lowmemory_verify() { - use bouncycastle::mldsa_lowmemory::{MLDSATrait, MLDSA87, MLDSA87_SIG_LEN, MLDSA87PublicKey}; use bouncycastle::hex; + use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SIG_LEN, MLDSA87PublicKey, MLDSATrait}; eprintln!("MLDSA87/Verify"); @@ -494,8 +2161,6 @@ fn bench_mldsa87_lowmemory_verify() { } } - - fn main() { // print_struct_sizes() // bench_do_nothing() @@ -510,11 +2175,11 @@ fn main() { // bench_mldsa65_sign() // bench_mldsa65_lowmemory_sign() // bench_mldsa87_sign() - // bench_mldsa87_lowmemory_sign() + bench_mldsa87_lowmemory_sign() // bench_mldsa44_verify() // bench_mldsa44_lowmemory_verify() // bench_mldsa65_verify() // bench_mldsa65_lowmemory_verify() // bench_mldsa87_verify() - bench_mldsa87_lowmemory_verify() -} \ No newline at end of file + // bench_mldsa87_lowmemory_verify() +} diff --git a/mem_usage_benches/bench_mlkem_mem_usage.rs b/mem_usage_benches/bench_mlkem_mem_usage.rs index 494621f..cce2897 100644 --- a/mem_usage_benches/bench_mlkem_mem_usage.rs +++ b/mem_usage_benches/bench_mlkem_mem_usage.rs @@ -11,7 +11,7 @@ //! //! //! To measure code size, Claude suggests: -//! +//! //! To see code size separately: size ./your_binary or readelf -S ./your_binary (look at .text). //! //! Make sure you build in release mode! @@ -26,9 +26,12 @@ #![allow(unused_imports)] use bouncycastle::core::key_material::{KeyMaterial512, KeyType}; -use bouncycastle::core::traits::{KEMPublicKey, KEM}; -use bouncycastle::mlkem::mlkem::{MLKEMTrait}; -use bouncycastle::mlkem::{MLKEM1024_CT_LEN, MLKEM1024_PK_LEN, MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM768_CT_LEN, MLKEM768_PK_LEN}; +use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPublicKey}; +use bouncycastle::mlkem::mlkem::MLKEMTrait; +use bouncycastle::mlkem::{ + MLKEM512_CT_LEN, MLKEM512_PK_LEN, MLKEM768_CT_LEN, MLKEM768_PK_LEN, MLKEM1024_CT_LEN, + MLKEM1024_PK_LEN, +}; /// This exists so I can use /usr/bin/time to measure the base memory footprint of the cargo bench harness fn bench_do_nothing() { @@ -39,135 +42,209 @@ fn bench_do_nothing() { /// This prints the in-memory size of all the public and private key structs fn print_struct_sizes() { - use core::mem::size_of; use bouncycastle::mlkem; use bouncycastle::mlkem_lowmemory; - + use core::mem::size_of; println!("\nML-KEM-512"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); - + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-768"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-1024"); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); println!("\n\nlowmemory"); println!("\nML-KEM-512_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); - + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-768_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); println!("\nML-KEM-1024_lowmemory"); - println!("size_of: {}", size_of::()); - println!("size_of: {}", size_of::()); + println!( + "size_of: {}", + size_of::() + ); + println!( + "size_of: {}", + size_of::() + ); } fn bench_mlkem512_keygen() { - use bouncycastle::mlkem::{MLKEM512}; + use bouncycastle::mlkem::MLKEM512; eprintln!("MLKEM512/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem512_lowmemory_keygen() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM512}; + use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEMTrait}; eprintln!("MLKEM512_lowmemory/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem768_keygen() { - use bouncycastle::mlkem::{MLKEM768}; + use bouncycastle::mlkem::MLKEM768; eprintln!("MLKEM768/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem768_lowmemory_keygen() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM768}; + use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEMTrait}; eprintln!("MLKEM768_lowmemory/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem1024_keygen() { - use bouncycastle::mlkem::{MLKEM1024}; + use bouncycastle::mlkem::MLKEM1024; eprintln!("MLKEM1024/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem1024_lowmemory_keygen() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM1024}; + use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEMTrait}; eprintln!("MLKEM1024_lowmemory/KeyGen"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } fn bench_mlkem512_encaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM512, MLKEM512PublicKey}; + use bouncycastle::mlkem::{MLKEM512, MLKEM512PublicKey, MLKEMTrait}; eprintln!("MLKEM512/Encaps"); - /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( // &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], @@ -178,7 +255,62 @@ fn bench_mlkem512_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM512_PK_LEN] = [0x39,0x95,0x81,0x5e,0x59,0x7d,0x10,0x43,0x55,0xcf,0x29,0xaa,0x53,0x33,0xc9,0x32,0x51,0x86,0x9d,0x5b,0xcd,0xbe,0x48,0x71,0x24,0xf6,0x02,0xb8,0xb6,0xa6,0x6c,0x16,0xc4,0x76,0x16,0x48,0xad,0x76,0x5c,0xf5,0xd8,0x00,0x6b,0x51,0x5e,0x90,0x5a,0x7f,0x0a,0xc0,0x76,0xb0,0xc6,0x2e,0xfa,0x32,0x81,0x53,0xe7,0xca,0x57,0x01,0x69,0x9f,0x13,0x05,0xf1,0xe6,0xbc,0x6f,0x90,0xb0,0xe4,0x9b,0x69,0x35,0x12,0xb6,0xce,0x99,0x2a,0x8b,0x80,0x16,0xdd,0xfc,0x1a,0x66,0x2c,0x7e,0x3f,0x96,0x19,0xcb,0xd8,0x69,0xdd,0x77,0x1a,0xf3,0x08,0x96,0xcc,0xd5,0x91,0x8a,0xc6,0xcb,0x77,0x46,0x6c,0x5e,0x77,0x99,0x96,0xd6,0x7f,0xf9,0xaa,0xbc,0x97,0x50,0x3f,0x2c,0x7b,0x7e,0x2d,0x00,0x0d,0x86,0x45,0x0f,0xb1,0x80,0x7c,0xa4,0xca,0xbd,0xa4,0x65,0x82,0x5a,0x31,0xc7,0x89,0xa1,0xb7,0xa4,0x91,0xab,0x38,0x72,0x76,0x5d,0x32,0x0d,0x0b,0x71,0x92,0x0f,0xa2,0x13,0xc9,0x40,0x93,0x41,0x6b,0x83,0xb8,0x12,0x4e,0x69,0xf6,0x5e,0x62,0xcb,0x50,0x00,0xdc,0xc3,0x7a,0xa9,0xa0,0xff,0xf7,0x39,0x70,0xc4,0x77,0x2f,0x35,0x7d,0x24,0x18,0x9c,0xa6,0xf5,0x30,0x55,0x68,0xc0,0xe2,0x37,0x6a,0x37,0x62,0xa6,0x8c,0x60,0x5e,0x56,0x3c,0x5d,0x20,0x95,0x72,0xe0,0xfc,0x75,0x32,0xca,0x29,0x47,0x29,0x53,0x55,0x67,0xb5,0xfc,0x41,0x3c,0x5e,0x87,0x92,0xd2,0x46,0x45,0x36,0xcc,0x80,0x8f,0x98,0xad,0xd7,0x46,0x64,0xf1,0x41,0x56,0x6f,0x90,0x16,0xa9,0x0a,0x54,0x18,0x29,0xa9,0x8a,0x04,0x64,0xce,0x41,0xa8,0xbb,0x44,0xc2,0xd4,0xfa,0x3c,0x2c,0x20,0x94,0x60,0x72,0x8e,0xf1,0x4a,0x1a,0x7c,0x4c,0x9b,0x98,0xd1,0x22,0x03,0xb4,0xcc,0x35,0x29,0x16,0x0a,0x9a,0xb2,0xd7,0x83,0x8f,0x7f,0xf6,0xb5,0x3a,0xe0,0x5a,0xa3,0x1a,0x7d,0x64,0x6b,0x7a,0xfa,0x6c,0x45,0x93,0x25,0x26,0xa3,0xc3,0x75,0x56,0x19,0xbe,0x99,0x4c,0x21,0x1c,0x2a,0x31,0xc0,0x5b,0x34,0x47,0x83,0x6c,0xb2,0x15,0x0b,0xe1,0x82,0x9d,0xae,0x6b,0x04,0xc5,0x53,0x5c,0xff,0x54,0x6e,0x39,0x2b,0xa7,0x97,0x41,0x17,0x20,0xf9,0x24,0xf4,0x90,0xa5,0xac,0x54,0x95,0xf2,0x13,0x56,0xd5,0x50,0xb7,0x82,0xa6,0x4c,0x16,0x88,0xb6,0xb6,0x55,0xbc,0xc7,0x84,0x21,0x97,0xa4,0x34,0xc2,0xf6,0x56,0x3b,0x5b,0x7f,0x09,0xa7,0x8b,0xcc,0x48,0x82,0x32,0x78,0x35,0x61,0xd1,0x6f,0x4c,0xba,0xb6,0x75,0x54,0x00,0x05,0x07,0x81,0x57,0x0c,0x66,0x60,0x4b,0x81,0x7a,0xd1,0x25,0x22,0x94,0x73,0x6e,0x8b,0x01,0x86,0x1a,0x4b,0x5a,0x74,0x51,0x9b,0x8b,0x6f,0xe5,0x14,0x89,0xa5,0x07,0x23,0x92,0xe5,0x87,0x62,0x6c,0x71,0x37,0x76,0x57,0x5d,0x33,0x80,0x6a,0x1c,0x8e,0x27,0x32,0xaf,0x97,0xc2,0x68,0x0f,0x51,0x66,0x63,0x31,0xc4,0xeb,0x8b,0xbc,0x04,0x31,0xc4,0xf9,0x68,0x32,0xda,0xf1,0xb3,0xc4,0x55,0x28,0xfb,0xa1,0x53,0xf6,0xc7,0x8b,0x1c,0x19,0x87,0x02,0x94,0x7c,0xcd,0x33,0x77,0x27,0xa4,0x6f,0xb5,0x3b,0xa1,0x1d,0xe5,0xcb,0x41,0x91,0x34,0x68,0x59,0x51,0x6c,0xb6,0xad,0x72,0x40,0x0f,0x3c,0xf2,0x09,0xb2,0x36,0xae,0xf3,0x5a,0x58,0x0a,0xc8,0x7e,0xb3,0xe3,0x0f,0xaf,0xd6,0x69,0x73,0xca,0x8a,0x7d,0xd2,0x67,0x5a,0xf4,0x1f,0x7a,0x17,0xb6,0x14,0x33,0xcd,0x1a,0xf8,0x0f,0x77,0x08,0x86,0x9f,0x66,0x54,0x88,0x49,0x79,0x80,0xb1,0xac,0x10,0xa0,0xcd,0xcb,0x63,0x6a,0x00,0xed,0x86,0x81,0xb3,0x5e,0x42,0x91,0x24,0xca,0x80,0x35,0x07,0x25,0xb8,0x5f,0x83,0xa5,0xea,0xc3,0xa4,0xa3,0xcc,0x16,0x00,0x90,0x3e,0x65,0x29,0x35,0x60,0xb9,0xb3,0x36,0xe5,0xaf,0x0d,0x52,0x9d,0xac,0x1a,0x04,0x81,0x19,0x30,0x2c,0xb7,0xa9,0xbc,0xc1,0x10,0xb9,0x48,0x51,0xbf,0x02,0x11,0x7f,0x19,0x9d,0xc4,0x85,0xa8,0x52,0xb7,0x47,0x3f,0x09,0xb8,0x31,0xa6,0x83,0x1d,0x5b,0x54,0xc0,0xb7,0x90,0xd2,0x25,0xcf,0x6b,0xb9,0x2d,0x94,0x62,0xa2,0x6c,0xdb,0x33,0xdd,0xa5,0x12,0x3c,0x7a,0xaf,0x0e,0x26,0xa0,0xb8,0x36,0x55,0xee,0xa2,0x8b,0xf3,0xa8,0x07,0x47,0x25,0x01,0x8f,0xd6,0xba,0xe4,0xb6,0x01,0xcf,0x61,0xba,0xab,0x71,0xa7,0xa3,0xd3,0x51,0x97,0xa3,0x43,0xe7,0x4b,0x4a,0x27,0x2c,0x12,0x5d,0x54,0x08,0x96,0x42,0x6d,0x85,0xb7,0x95,0x8d,0x3b,0x38,0xa6,0xba,0x98,0x7e,0xc3,0x72,0x25,0xc7,0xb4,0x4c,0xdb,0x12,0xdd,0xe4,0x53,0x9b,0x4a,0xb0,0x82,0x36,0x36,0x83,0xf0,0x4b,0xf7,0xa0,0x9c,0xc5,0xc4,0x1d,0xfe,0x83,0x0a,0x1b,0x16,0x2e,0x0b,0x32,0x43,0x34,0x36,0x2f,0x08,0x4a,0x14,0x46,0x77,0x23,0x34,0x4b,0xad,0xd0,0x00,0xf8,0xd8,0xc5,0x37,0xc4,0x8f,0x99,0x8f,0x05,0x30,0x7c,0xeb,0xd1,0xed,0xe0,0xb8,0x1c,0x3b,0xc5,0x9a,0x06,0x5a,0x1b,0x6d,0x63,0xb2,0x6c]; + let pk_bytes: [u8; MLKEM512_PK_LEN] = [ + 0x39, 0x95, 0x81, 0x5E, 0x59, 0x7D, 0x10, 0x43, 0x55, 0xCF, 0x29, 0xAA, 0x53, 0x33, 0xC9, + 0x32, 0x51, 0x86, 0x9D, 0x5B, 0xCD, 0xBE, 0x48, 0x71, 0x24, 0xF6, 0x02, 0xB8, 0xB6, 0xA6, + 0x6C, 0x16, 0xC4, 0x76, 0x16, 0x48, 0xAD, 0x76, 0x5C, 0xF5, 0xD8, 0x00, 0x6B, 0x51, 0x5E, + 0x90, 0x5A, 0x7F, 0x0A, 0xC0, 0x76, 0xB0, 0xC6, 0x2E, 0xFA, 0x32, 0x81, 0x53, 0xE7, 0xCA, + 0x57, 0x01, 0x69, 0x9F, 0x13, 0x05, 0xF1, 0xE6, 0xBC, 0x6F, 0x90, 0xB0, 0xE4, 0x9B, 0x69, + 0x35, 0x12, 0xB6, 0xCE, 0x99, 0x2A, 0x8B, 0x80, 0x16, 0xDD, 0xFC, 0x1A, 0x66, 0x2C, 0x7E, + 0x3F, 0x96, 0x19, 0xCB, 0xD8, 0x69, 0xDD, 0x77, 0x1A, 0xF3, 0x08, 0x96, 0xCC, 0xD5, 0x91, + 0x8A, 0xC6, 0xCB, 0x77, 0x46, 0x6C, 0x5E, 0x77, 0x99, 0x96, 0xD6, 0x7F, 0xF9, 0xAA, 0xBC, + 0x97, 0x50, 0x3F, 0x2C, 0x7B, 0x7E, 0x2D, 0x00, 0x0D, 0x86, 0x45, 0x0F, 0xB1, 0x80, 0x7C, + 0xA4, 0xCA, 0xBD, 0xA4, 0x65, 0x82, 0x5A, 0x31, 0xC7, 0x89, 0xA1, 0xB7, 0xA4, 0x91, 0xAB, + 0x38, 0x72, 0x76, 0x5D, 0x32, 0x0D, 0x0B, 0x71, 0x92, 0x0F, 0xA2, 0x13, 0xC9, 0x40, 0x93, + 0x41, 0x6B, 0x83, 0xB8, 0x12, 0x4E, 0x69, 0xF6, 0x5E, 0x62, 0xCB, 0x50, 0x00, 0xDC, 0xC3, + 0x7A, 0xA9, 0xA0, 0xFF, 0xF7, 0x39, 0x70, 0xC4, 0x77, 0x2F, 0x35, 0x7D, 0x24, 0x18, 0x9C, + 0xA6, 0xF5, 0x30, 0x55, 0x68, 0xC0, 0xE2, 0x37, 0x6A, 0x37, 0x62, 0xA6, 0x8C, 0x60, 0x5E, + 0x56, 0x3C, 0x5D, 0x20, 0x95, 0x72, 0xE0, 0xFC, 0x75, 0x32, 0xCA, 0x29, 0x47, 0x29, 0x53, + 0x55, 0x67, 0xB5, 0xFC, 0x41, 0x3C, 0x5E, 0x87, 0x92, 0xD2, 0x46, 0x45, 0x36, 0xCC, 0x80, + 0x8F, 0x98, 0xAD, 0xD7, 0x46, 0x64, 0xF1, 0x41, 0x56, 0x6F, 0x90, 0x16, 0xA9, 0x0A, 0x54, + 0x18, 0x29, 0xA9, 0x8A, 0x04, 0x64, 0xCE, 0x41, 0xA8, 0xBB, 0x44, 0xC2, 0xD4, 0xFA, 0x3C, + 0x2C, 0x20, 0x94, 0x60, 0x72, 0x8E, 0xF1, 0x4A, 0x1A, 0x7C, 0x4C, 0x9B, 0x98, 0xD1, 0x22, + 0x03, 0xB4, 0xCC, 0x35, 0x29, 0x16, 0x0A, 0x9A, 0xB2, 0xD7, 0x83, 0x8F, 0x7F, 0xF6, 0xB5, + 0x3A, 0xE0, 0x5A, 0xA3, 0x1A, 0x7D, 0x64, 0x6B, 0x7A, 0xFA, 0x6C, 0x45, 0x93, 0x25, 0x26, + 0xA3, 0xC3, 0x75, 0x56, 0x19, 0xBE, 0x99, 0x4C, 0x21, 0x1C, 0x2A, 0x31, 0xC0, 0x5B, 0x34, + 0x47, 0x83, 0x6C, 0xB2, 0x15, 0x0B, 0xE1, 0x82, 0x9D, 0xAE, 0x6B, 0x04, 0xC5, 0x53, 0x5C, + 0xFF, 0x54, 0x6E, 0x39, 0x2B, 0xA7, 0x97, 0x41, 0x17, 0x20, 0xF9, 0x24, 0xF4, 0x90, 0xA5, + 0xAC, 0x54, 0x95, 0xF2, 0x13, 0x56, 0xD5, 0x50, 0xB7, 0x82, 0xA6, 0x4C, 0x16, 0x88, 0xB6, + 0xB6, 0x55, 0xBC, 0xC7, 0x84, 0x21, 0x97, 0xA4, 0x34, 0xC2, 0xF6, 0x56, 0x3B, 0x5B, 0x7F, + 0x09, 0xA7, 0x8B, 0xCC, 0x48, 0x82, 0x32, 0x78, 0x35, 0x61, 0xD1, 0x6F, 0x4C, 0xBA, 0xB6, + 0x75, 0x54, 0x00, 0x05, 0x07, 0x81, 0x57, 0x0C, 0x66, 0x60, 0x4B, 0x81, 0x7A, 0xD1, 0x25, + 0x22, 0x94, 0x73, 0x6E, 0x8B, 0x01, 0x86, 0x1A, 0x4B, 0x5A, 0x74, 0x51, 0x9B, 0x8B, 0x6F, + 0xE5, 0x14, 0x89, 0xA5, 0x07, 0x23, 0x92, 0xE5, 0x87, 0x62, 0x6C, 0x71, 0x37, 0x76, 0x57, + 0x5D, 0x33, 0x80, 0x6A, 0x1C, 0x8E, 0x27, 0x32, 0xAF, 0x97, 0xC2, 0x68, 0x0F, 0x51, 0x66, + 0x63, 0x31, 0xC4, 0xEB, 0x8B, 0xBC, 0x04, 0x31, 0xC4, 0xF9, 0x68, 0x32, 0xDA, 0xF1, 0xB3, + 0xC4, 0x55, 0x28, 0xFB, 0xA1, 0x53, 0xF6, 0xC7, 0x8B, 0x1C, 0x19, 0x87, 0x02, 0x94, 0x7C, + 0xCD, 0x33, 0x77, 0x27, 0xA4, 0x6F, 0xB5, 0x3B, 0xA1, 0x1D, 0xE5, 0xCB, 0x41, 0x91, 0x34, + 0x68, 0x59, 0x51, 0x6C, 0xB6, 0xAD, 0x72, 0x40, 0x0F, 0x3C, 0xF2, 0x09, 0xB2, 0x36, 0xAE, + 0xF3, 0x5A, 0x58, 0x0A, 0xC8, 0x7E, 0xB3, 0xE3, 0x0F, 0xAF, 0xD6, 0x69, 0x73, 0xCA, 0x8A, + 0x7D, 0xD2, 0x67, 0x5A, 0xF4, 0x1F, 0x7A, 0x17, 0xB6, 0x14, 0x33, 0xCD, 0x1A, 0xF8, 0x0F, + 0x77, 0x08, 0x86, 0x9F, 0x66, 0x54, 0x88, 0x49, 0x79, 0x80, 0xB1, 0xAC, 0x10, 0xA0, 0xCD, + 0xCB, 0x63, 0x6A, 0x00, 0xED, 0x86, 0x81, 0xB3, 0x5E, 0x42, 0x91, 0x24, 0xCA, 0x80, 0x35, + 0x07, 0x25, 0xB8, 0x5F, 0x83, 0xA5, 0xEA, 0xC3, 0xA4, 0xA3, 0xCC, 0x16, 0x00, 0x90, 0x3E, + 0x65, 0x29, 0x35, 0x60, 0xB9, 0xB3, 0x36, 0xE5, 0xAF, 0x0D, 0x52, 0x9D, 0xAC, 0x1A, 0x04, + 0x81, 0x19, 0x30, 0x2C, 0xB7, 0xA9, 0xBC, 0xC1, 0x10, 0xB9, 0x48, 0x51, 0xBF, 0x02, 0x11, + 0x7F, 0x19, 0x9D, 0xC4, 0x85, 0xA8, 0x52, 0xB7, 0x47, 0x3F, 0x09, 0xB8, 0x31, 0xA6, 0x83, + 0x1D, 0x5B, 0x54, 0xC0, 0xB7, 0x90, 0xD2, 0x25, 0xCF, 0x6B, 0xB9, 0x2D, 0x94, 0x62, 0xA2, + 0x6C, 0xDB, 0x33, 0xDD, 0xA5, 0x12, 0x3C, 0x7A, 0xAF, 0x0E, 0x26, 0xA0, 0xB8, 0x36, 0x55, + 0xEE, 0xA2, 0x8B, 0xF3, 0xA8, 0x07, 0x47, 0x25, 0x01, 0x8F, 0xD6, 0xBA, 0xE4, 0xB6, 0x01, + 0xCF, 0x61, 0xBA, 0xAB, 0x71, 0xA7, 0xA3, 0xD3, 0x51, 0x97, 0xA3, 0x43, 0xE7, 0x4B, 0x4A, + 0x27, 0x2C, 0x12, 0x5D, 0x54, 0x08, 0x96, 0x42, 0x6D, 0x85, 0xB7, 0x95, 0x8D, 0x3B, 0x38, + 0xA6, 0xBA, 0x98, 0x7E, 0xC3, 0x72, 0x25, 0xC7, 0xB4, 0x4C, 0xDB, 0x12, 0xDD, 0xE4, 0x53, + 0x9B, 0x4A, 0xB0, 0x82, 0x36, 0x36, 0x83, 0xF0, 0x4B, 0xF7, 0xA0, 0x9C, 0xC5, 0xC4, 0x1D, + 0xFE, 0x83, 0x0A, 0x1B, 0x16, 0x2E, 0x0B, 0x32, 0x43, 0x34, 0x36, 0x2F, 0x08, 0x4A, 0x14, + 0x46, 0x77, 0x23, 0x34, 0x4B, 0xAD, 0xD0, 0x00, 0xF8, 0xD8, 0xC5, 0x37, 0xC4, 0x8F, 0x99, + 0x8F, 0x05, 0x30, 0x7C, 0xEB, 0xD1, 0xED, 0xE0, 0xB8, 0x1C, 0x3B, 0xC5, 0x9A, 0x06, 0x5A, + 0x1B, 0x6D, 0x63, 0xB2, 0x6C, + ]; let pk = MLKEM512PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM512::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -186,11 +318,10 @@ fn bench_mlkem512_encaps() { } fn bench_mlkem512_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM512, MLKEM512PublicKey}; + use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEM512PublicKey, MLKEMTrait}; eprintln!("MLKEM512_lowmemory/Encaps"); - /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( // &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], @@ -201,7 +332,62 @@ fn bench_mlkem512_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM512_PK_LEN] = [0x39,0x95,0x81,0x5e,0x59,0x7d,0x10,0x43,0x55,0xcf,0x29,0xaa,0x53,0x33,0xc9,0x32,0x51,0x86,0x9d,0x5b,0xcd,0xbe,0x48,0x71,0x24,0xf6,0x02,0xb8,0xb6,0xa6,0x6c,0x16,0xc4,0x76,0x16,0x48,0xad,0x76,0x5c,0xf5,0xd8,0x00,0x6b,0x51,0x5e,0x90,0x5a,0x7f,0x0a,0xc0,0x76,0xb0,0xc6,0x2e,0xfa,0x32,0x81,0x53,0xe7,0xca,0x57,0x01,0x69,0x9f,0x13,0x05,0xf1,0xe6,0xbc,0x6f,0x90,0xb0,0xe4,0x9b,0x69,0x35,0x12,0xb6,0xce,0x99,0x2a,0x8b,0x80,0x16,0xdd,0xfc,0x1a,0x66,0x2c,0x7e,0x3f,0x96,0x19,0xcb,0xd8,0x69,0xdd,0x77,0x1a,0xf3,0x08,0x96,0xcc,0xd5,0x91,0x8a,0xc6,0xcb,0x77,0x46,0x6c,0x5e,0x77,0x99,0x96,0xd6,0x7f,0xf9,0xaa,0xbc,0x97,0x50,0x3f,0x2c,0x7b,0x7e,0x2d,0x00,0x0d,0x86,0x45,0x0f,0xb1,0x80,0x7c,0xa4,0xca,0xbd,0xa4,0x65,0x82,0x5a,0x31,0xc7,0x89,0xa1,0xb7,0xa4,0x91,0xab,0x38,0x72,0x76,0x5d,0x32,0x0d,0x0b,0x71,0x92,0x0f,0xa2,0x13,0xc9,0x40,0x93,0x41,0x6b,0x83,0xb8,0x12,0x4e,0x69,0xf6,0x5e,0x62,0xcb,0x50,0x00,0xdc,0xc3,0x7a,0xa9,0xa0,0xff,0xf7,0x39,0x70,0xc4,0x77,0x2f,0x35,0x7d,0x24,0x18,0x9c,0xa6,0xf5,0x30,0x55,0x68,0xc0,0xe2,0x37,0x6a,0x37,0x62,0xa6,0x8c,0x60,0x5e,0x56,0x3c,0x5d,0x20,0x95,0x72,0xe0,0xfc,0x75,0x32,0xca,0x29,0x47,0x29,0x53,0x55,0x67,0xb5,0xfc,0x41,0x3c,0x5e,0x87,0x92,0xd2,0x46,0x45,0x36,0xcc,0x80,0x8f,0x98,0xad,0xd7,0x46,0x64,0xf1,0x41,0x56,0x6f,0x90,0x16,0xa9,0x0a,0x54,0x18,0x29,0xa9,0x8a,0x04,0x64,0xce,0x41,0xa8,0xbb,0x44,0xc2,0xd4,0xfa,0x3c,0x2c,0x20,0x94,0x60,0x72,0x8e,0xf1,0x4a,0x1a,0x7c,0x4c,0x9b,0x98,0xd1,0x22,0x03,0xb4,0xcc,0x35,0x29,0x16,0x0a,0x9a,0xb2,0xd7,0x83,0x8f,0x7f,0xf6,0xb5,0x3a,0xe0,0x5a,0xa3,0x1a,0x7d,0x64,0x6b,0x7a,0xfa,0x6c,0x45,0x93,0x25,0x26,0xa3,0xc3,0x75,0x56,0x19,0xbe,0x99,0x4c,0x21,0x1c,0x2a,0x31,0xc0,0x5b,0x34,0x47,0x83,0x6c,0xb2,0x15,0x0b,0xe1,0x82,0x9d,0xae,0x6b,0x04,0xc5,0x53,0x5c,0xff,0x54,0x6e,0x39,0x2b,0xa7,0x97,0x41,0x17,0x20,0xf9,0x24,0xf4,0x90,0xa5,0xac,0x54,0x95,0xf2,0x13,0x56,0xd5,0x50,0xb7,0x82,0xa6,0x4c,0x16,0x88,0xb6,0xb6,0x55,0xbc,0xc7,0x84,0x21,0x97,0xa4,0x34,0xc2,0xf6,0x56,0x3b,0x5b,0x7f,0x09,0xa7,0x8b,0xcc,0x48,0x82,0x32,0x78,0x35,0x61,0xd1,0x6f,0x4c,0xba,0xb6,0x75,0x54,0x00,0x05,0x07,0x81,0x57,0x0c,0x66,0x60,0x4b,0x81,0x7a,0xd1,0x25,0x22,0x94,0x73,0x6e,0x8b,0x01,0x86,0x1a,0x4b,0x5a,0x74,0x51,0x9b,0x8b,0x6f,0xe5,0x14,0x89,0xa5,0x07,0x23,0x92,0xe5,0x87,0x62,0x6c,0x71,0x37,0x76,0x57,0x5d,0x33,0x80,0x6a,0x1c,0x8e,0x27,0x32,0xaf,0x97,0xc2,0x68,0x0f,0x51,0x66,0x63,0x31,0xc4,0xeb,0x8b,0xbc,0x04,0x31,0xc4,0xf9,0x68,0x32,0xda,0xf1,0xb3,0xc4,0x55,0x28,0xfb,0xa1,0x53,0xf6,0xc7,0x8b,0x1c,0x19,0x87,0x02,0x94,0x7c,0xcd,0x33,0x77,0x27,0xa4,0x6f,0xb5,0x3b,0xa1,0x1d,0xe5,0xcb,0x41,0x91,0x34,0x68,0x59,0x51,0x6c,0xb6,0xad,0x72,0x40,0x0f,0x3c,0xf2,0x09,0xb2,0x36,0xae,0xf3,0x5a,0x58,0x0a,0xc8,0x7e,0xb3,0xe3,0x0f,0xaf,0xd6,0x69,0x73,0xca,0x8a,0x7d,0xd2,0x67,0x5a,0xf4,0x1f,0x7a,0x17,0xb6,0x14,0x33,0xcd,0x1a,0xf8,0x0f,0x77,0x08,0x86,0x9f,0x66,0x54,0x88,0x49,0x79,0x80,0xb1,0xac,0x10,0xa0,0xcd,0xcb,0x63,0x6a,0x00,0xed,0x86,0x81,0xb3,0x5e,0x42,0x91,0x24,0xca,0x80,0x35,0x07,0x25,0xb8,0x5f,0x83,0xa5,0xea,0xc3,0xa4,0xa3,0xcc,0x16,0x00,0x90,0x3e,0x65,0x29,0x35,0x60,0xb9,0xb3,0x36,0xe5,0xaf,0x0d,0x52,0x9d,0xac,0x1a,0x04,0x81,0x19,0x30,0x2c,0xb7,0xa9,0xbc,0xc1,0x10,0xb9,0x48,0x51,0xbf,0x02,0x11,0x7f,0x19,0x9d,0xc4,0x85,0xa8,0x52,0xb7,0x47,0x3f,0x09,0xb8,0x31,0xa6,0x83,0x1d,0x5b,0x54,0xc0,0xb7,0x90,0xd2,0x25,0xcf,0x6b,0xb9,0x2d,0x94,0x62,0xa2,0x6c,0xdb,0x33,0xdd,0xa5,0x12,0x3c,0x7a,0xaf,0x0e,0x26,0xa0,0xb8,0x36,0x55,0xee,0xa2,0x8b,0xf3,0xa8,0x07,0x47,0x25,0x01,0x8f,0xd6,0xba,0xe4,0xb6,0x01,0xcf,0x61,0xba,0xab,0x71,0xa7,0xa3,0xd3,0x51,0x97,0xa3,0x43,0xe7,0x4b,0x4a,0x27,0x2c,0x12,0x5d,0x54,0x08,0x96,0x42,0x6d,0x85,0xb7,0x95,0x8d,0x3b,0x38,0xa6,0xba,0x98,0x7e,0xc3,0x72,0x25,0xc7,0xb4,0x4c,0xdb,0x12,0xdd,0xe4,0x53,0x9b,0x4a,0xb0,0x82,0x36,0x36,0x83,0xf0,0x4b,0xf7,0xa0,0x9c,0xc5,0xc4,0x1d,0xfe,0x83,0x0a,0x1b,0x16,0x2e,0x0b,0x32,0x43,0x34,0x36,0x2f,0x08,0x4a,0x14,0x46,0x77,0x23,0x34,0x4b,0xad,0xd0,0x00,0xf8,0xd8,0xc5,0x37,0xc4,0x8f,0x99,0x8f,0x05,0x30,0x7c,0xeb,0xd1,0xed,0xe0,0xb8,0x1c,0x3b,0xc5,0x9a,0x06,0x5a,0x1b,0x6d,0x63,0xb2,0x6c]; + let pk_bytes: [u8; MLKEM512_PK_LEN] = [ + 0x39, 0x95, 0x81, 0x5E, 0x59, 0x7D, 0x10, 0x43, 0x55, 0xCF, 0x29, 0xAA, 0x53, 0x33, 0xC9, + 0x32, 0x51, 0x86, 0x9D, 0x5B, 0xCD, 0xBE, 0x48, 0x71, 0x24, 0xF6, 0x02, 0xB8, 0xB6, 0xA6, + 0x6C, 0x16, 0xC4, 0x76, 0x16, 0x48, 0xAD, 0x76, 0x5C, 0xF5, 0xD8, 0x00, 0x6B, 0x51, 0x5E, + 0x90, 0x5A, 0x7F, 0x0A, 0xC0, 0x76, 0xB0, 0xC6, 0x2E, 0xFA, 0x32, 0x81, 0x53, 0xE7, 0xCA, + 0x57, 0x01, 0x69, 0x9F, 0x13, 0x05, 0xF1, 0xE6, 0xBC, 0x6F, 0x90, 0xB0, 0xE4, 0x9B, 0x69, + 0x35, 0x12, 0xB6, 0xCE, 0x99, 0x2A, 0x8B, 0x80, 0x16, 0xDD, 0xFC, 0x1A, 0x66, 0x2C, 0x7E, + 0x3F, 0x96, 0x19, 0xCB, 0xD8, 0x69, 0xDD, 0x77, 0x1A, 0xF3, 0x08, 0x96, 0xCC, 0xD5, 0x91, + 0x8A, 0xC6, 0xCB, 0x77, 0x46, 0x6C, 0x5E, 0x77, 0x99, 0x96, 0xD6, 0x7F, 0xF9, 0xAA, 0xBC, + 0x97, 0x50, 0x3F, 0x2C, 0x7B, 0x7E, 0x2D, 0x00, 0x0D, 0x86, 0x45, 0x0F, 0xB1, 0x80, 0x7C, + 0xA4, 0xCA, 0xBD, 0xA4, 0x65, 0x82, 0x5A, 0x31, 0xC7, 0x89, 0xA1, 0xB7, 0xA4, 0x91, 0xAB, + 0x38, 0x72, 0x76, 0x5D, 0x32, 0x0D, 0x0B, 0x71, 0x92, 0x0F, 0xA2, 0x13, 0xC9, 0x40, 0x93, + 0x41, 0x6B, 0x83, 0xB8, 0x12, 0x4E, 0x69, 0xF6, 0x5E, 0x62, 0xCB, 0x50, 0x00, 0xDC, 0xC3, + 0x7A, 0xA9, 0xA0, 0xFF, 0xF7, 0x39, 0x70, 0xC4, 0x77, 0x2F, 0x35, 0x7D, 0x24, 0x18, 0x9C, + 0xA6, 0xF5, 0x30, 0x55, 0x68, 0xC0, 0xE2, 0x37, 0x6A, 0x37, 0x62, 0xA6, 0x8C, 0x60, 0x5E, + 0x56, 0x3C, 0x5D, 0x20, 0x95, 0x72, 0xE0, 0xFC, 0x75, 0x32, 0xCA, 0x29, 0x47, 0x29, 0x53, + 0x55, 0x67, 0xB5, 0xFC, 0x41, 0x3C, 0x5E, 0x87, 0x92, 0xD2, 0x46, 0x45, 0x36, 0xCC, 0x80, + 0x8F, 0x98, 0xAD, 0xD7, 0x46, 0x64, 0xF1, 0x41, 0x56, 0x6F, 0x90, 0x16, 0xA9, 0x0A, 0x54, + 0x18, 0x29, 0xA9, 0x8A, 0x04, 0x64, 0xCE, 0x41, 0xA8, 0xBB, 0x44, 0xC2, 0xD4, 0xFA, 0x3C, + 0x2C, 0x20, 0x94, 0x60, 0x72, 0x8E, 0xF1, 0x4A, 0x1A, 0x7C, 0x4C, 0x9B, 0x98, 0xD1, 0x22, + 0x03, 0xB4, 0xCC, 0x35, 0x29, 0x16, 0x0A, 0x9A, 0xB2, 0xD7, 0x83, 0x8F, 0x7F, 0xF6, 0xB5, + 0x3A, 0xE0, 0x5A, 0xA3, 0x1A, 0x7D, 0x64, 0x6B, 0x7A, 0xFA, 0x6C, 0x45, 0x93, 0x25, 0x26, + 0xA3, 0xC3, 0x75, 0x56, 0x19, 0xBE, 0x99, 0x4C, 0x21, 0x1C, 0x2A, 0x31, 0xC0, 0x5B, 0x34, + 0x47, 0x83, 0x6C, 0xB2, 0x15, 0x0B, 0xE1, 0x82, 0x9D, 0xAE, 0x6B, 0x04, 0xC5, 0x53, 0x5C, + 0xFF, 0x54, 0x6E, 0x39, 0x2B, 0xA7, 0x97, 0x41, 0x17, 0x20, 0xF9, 0x24, 0xF4, 0x90, 0xA5, + 0xAC, 0x54, 0x95, 0xF2, 0x13, 0x56, 0xD5, 0x50, 0xB7, 0x82, 0xA6, 0x4C, 0x16, 0x88, 0xB6, + 0xB6, 0x55, 0xBC, 0xC7, 0x84, 0x21, 0x97, 0xA4, 0x34, 0xC2, 0xF6, 0x56, 0x3B, 0x5B, 0x7F, + 0x09, 0xA7, 0x8B, 0xCC, 0x48, 0x82, 0x32, 0x78, 0x35, 0x61, 0xD1, 0x6F, 0x4C, 0xBA, 0xB6, + 0x75, 0x54, 0x00, 0x05, 0x07, 0x81, 0x57, 0x0C, 0x66, 0x60, 0x4B, 0x81, 0x7A, 0xD1, 0x25, + 0x22, 0x94, 0x73, 0x6E, 0x8B, 0x01, 0x86, 0x1A, 0x4B, 0x5A, 0x74, 0x51, 0x9B, 0x8B, 0x6F, + 0xE5, 0x14, 0x89, 0xA5, 0x07, 0x23, 0x92, 0xE5, 0x87, 0x62, 0x6C, 0x71, 0x37, 0x76, 0x57, + 0x5D, 0x33, 0x80, 0x6A, 0x1C, 0x8E, 0x27, 0x32, 0xAF, 0x97, 0xC2, 0x68, 0x0F, 0x51, 0x66, + 0x63, 0x31, 0xC4, 0xEB, 0x8B, 0xBC, 0x04, 0x31, 0xC4, 0xF9, 0x68, 0x32, 0xDA, 0xF1, 0xB3, + 0xC4, 0x55, 0x28, 0xFB, 0xA1, 0x53, 0xF6, 0xC7, 0x8B, 0x1C, 0x19, 0x87, 0x02, 0x94, 0x7C, + 0xCD, 0x33, 0x77, 0x27, 0xA4, 0x6F, 0xB5, 0x3B, 0xA1, 0x1D, 0xE5, 0xCB, 0x41, 0x91, 0x34, + 0x68, 0x59, 0x51, 0x6C, 0xB6, 0xAD, 0x72, 0x40, 0x0F, 0x3C, 0xF2, 0x09, 0xB2, 0x36, 0xAE, + 0xF3, 0x5A, 0x58, 0x0A, 0xC8, 0x7E, 0xB3, 0xE3, 0x0F, 0xAF, 0xD6, 0x69, 0x73, 0xCA, 0x8A, + 0x7D, 0xD2, 0x67, 0x5A, 0xF4, 0x1F, 0x7A, 0x17, 0xB6, 0x14, 0x33, 0xCD, 0x1A, 0xF8, 0x0F, + 0x77, 0x08, 0x86, 0x9F, 0x66, 0x54, 0x88, 0x49, 0x79, 0x80, 0xB1, 0xAC, 0x10, 0xA0, 0xCD, + 0xCB, 0x63, 0x6A, 0x00, 0xED, 0x86, 0x81, 0xB3, 0x5E, 0x42, 0x91, 0x24, 0xCA, 0x80, 0x35, + 0x07, 0x25, 0xB8, 0x5F, 0x83, 0xA5, 0xEA, 0xC3, 0xA4, 0xA3, 0xCC, 0x16, 0x00, 0x90, 0x3E, + 0x65, 0x29, 0x35, 0x60, 0xB9, 0xB3, 0x36, 0xE5, 0xAF, 0x0D, 0x52, 0x9D, 0xAC, 0x1A, 0x04, + 0x81, 0x19, 0x30, 0x2C, 0xB7, 0xA9, 0xBC, 0xC1, 0x10, 0xB9, 0x48, 0x51, 0xBF, 0x02, 0x11, + 0x7F, 0x19, 0x9D, 0xC4, 0x85, 0xA8, 0x52, 0xB7, 0x47, 0x3F, 0x09, 0xB8, 0x31, 0xA6, 0x83, + 0x1D, 0x5B, 0x54, 0xC0, 0xB7, 0x90, 0xD2, 0x25, 0xCF, 0x6B, 0xB9, 0x2D, 0x94, 0x62, 0xA2, + 0x6C, 0xDB, 0x33, 0xDD, 0xA5, 0x12, 0x3C, 0x7A, 0xAF, 0x0E, 0x26, 0xA0, 0xB8, 0x36, 0x55, + 0xEE, 0xA2, 0x8B, 0xF3, 0xA8, 0x07, 0x47, 0x25, 0x01, 0x8F, 0xD6, 0xBA, 0xE4, 0xB6, 0x01, + 0xCF, 0x61, 0xBA, 0xAB, 0x71, 0xA7, 0xA3, 0xD3, 0x51, 0x97, 0xA3, 0x43, 0xE7, 0x4B, 0x4A, + 0x27, 0x2C, 0x12, 0x5D, 0x54, 0x08, 0x96, 0x42, 0x6D, 0x85, 0xB7, 0x95, 0x8D, 0x3B, 0x38, + 0xA6, 0xBA, 0x98, 0x7E, 0xC3, 0x72, 0x25, 0xC7, 0xB4, 0x4C, 0xDB, 0x12, 0xDD, 0xE4, 0x53, + 0x9B, 0x4A, 0xB0, 0x82, 0x36, 0x36, 0x83, 0xF0, 0x4B, 0xF7, 0xA0, 0x9C, 0xC5, 0xC4, 0x1D, + 0xFE, 0x83, 0x0A, 0x1B, 0x16, 0x2E, 0x0B, 0x32, 0x43, 0x34, 0x36, 0x2F, 0x08, 0x4A, 0x14, + 0x46, 0x77, 0x23, 0x34, 0x4B, 0xAD, 0xD0, 0x00, 0xF8, 0xD8, 0xC5, 0x37, 0xC4, 0x8F, 0x99, + 0x8F, 0x05, 0x30, 0x7C, 0xEB, 0xD1, 0xED, 0xE0, 0xB8, 0x1C, 0x3B, 0xC5, 0x9A, 0x06, 0x5A, + 0x1B, 0x6D, 0x63, 0xB2, 0x6C, + ]; let pk = MLKEM512PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM512::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -209,7 +395,7 @@ fn bench_mlkem512_lowmemory_encaps() { } fn bench_mlkem768_encaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM768, MLKEM768PublicKey}; + use bouncycastle::mlkem::{MLKEM768, MLKEM768PublicKey, MLKEMTrait}; eprintln!("MLKEM768/Encaps"); @@ -223,7 +409,87 @@ fn bench_mlkem768_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM768_PK_LEN] = [0x29,0x8a,0xa1,0x0d,0x42,0x3c,0x8d,0xda,0x06,0x9d,0x02,0xbc,0x59,0xe6,0xcd,0xf0,0x3a,0x09,0x6b,0x8b,0x3d,0xa4,0xca,0xb9,0xb8,0x0c,0xa4,0xa1,0x49,0x07,0x67,0x2c,0xce,0xf1,0xec,0x4f,0xaf,0x23,0x4a,0x0b,0xc5,0xb7,0xe9,0xd4,0x73,0xf2,0xb3,0x13,0x3b,0x3b,0x26,0xa1,0xd1,0x75,0xcb,0x67,0xa7,0x80,0x59,0x19,0x69,0x9c,0x02,0xf7,0x65,0x31,0xb9,0x9c,0x5f,0x89,0x18,0x07,0x04,0xbb,0x4c,0xa4,0x53,0x5c,0x5b,0x89,0x72,0x67,0x9c,0x66,0x0a,0x07,0xc5,0xe5,0x14,0xb8,0x70,0x09,0xc8,0x62,0xeb,0x8f,0x51,0x57,0x69,0x5e,0xfb,0x3f,0xc4,0x0a,0x9d,0xef,0x6b,0x81,0xc1,0xcc,0x02,0xa2,0x49,0xae,0x4f,0x09,0x4a,0xd0,0xd9,0xbd,0x34,0x85,0xc1,0xc1,0xc6,0x80,0x80,0x52,0x0a,0x7c,0x8c,0x63,0x20,0x32,0xce,0xe7,0x38,0x15,0x4e,0x5c,0x51,0x76,0xc0,0x7d,0xa5,0x60,0x24,0x77,0x6a,0x43,0x0f,0xe7,0x6e,0xac,0xf6,0x65,0xa3,0xf7,0xb8,0x32,0x10,0x22,0x15,0xbc,0x82,0xf1,0x09,0x39,0xc8,0x35,0x57,0x04,0x33,0x6a,0x8f,0xac,0x1d,0x81,0xe4,0xbb,0x04,0x85,0xaa,0x5d,0x7c,0x74,0xd6,0xb5,0x9b,0xbe,0x5c,0x5e,0x97,0x2a,0x0d,0x8b,0xac,0x41,0x1b,0x55,0xb5,0xd5,0x55,0x7c,0xd6,0x80,0xa1,0xa8,0xf7,0x1b,0x4e,0xb8,0x6b,0xc4,0x8c,0x9a,0x05,0x09,0x73,0x1a,0x54,0xbd,0x9d,0x72,0x90,0xb2,0x79,0x63,0xe4,0x37,0x2d,0xc9,0xb1,0x99,0xcf,0xdc,0xac,0x0b,0x01,0xac,0xd2,0x8a,0x62,0x39,0x51,0x12,0xe4,0xc4,0x36,0x48,0xd6,0x22,0xc4,0x8c,0x82,0x34,0xd0,0x14,0x40,0xe8,0xcc,0x37,0x6c,0x92,0x7f,0x23,0xa5,0xaf,0xc9,0xac,0x04,0x74,0xc6,0x62,0x27,0x4e,0x42,0x45,0x25,0xc8,0x55,0x2e,0xce,0x3b,0x3f,0xe2,0x65,0x16,0xde,0x90,0x1b,0xc7,0xd5,0x15,0xbd,0xe8,0x95,0x58,0xe6,0x26,0xc9,0x5c,0x80,0xb9,0x33,0x42,0xf8,0x01,0x00,0x04,0xf3,0x9e,0x6c,0x6c,0x94,0x87,0x1c,0x5e,0x34,0x4c,0xab,0x39,0x66,0xc8,0x35,0xf9,0xa9,0x6a,0x59,0xaf,0xd3,0x1c,0x40,0x28,0x6b,0x38,0xb1,0xc1,0xa7,0x84,0x70,0xba,0xb9,0x47,0x51,0x89,0x34,0x45,0x3c,0xe8,0x67,0x36,0xa9,0x19,0xf1,0xf5,0xa6,0xd5,0x10,0xa8,0x6f,0x54,0x54,0xfc,0x39,0x80,0xcb,0x5c,0x76,0x5b,0xd2,0xbd,0x5f,0x7b,0x36,0xb1,0x41,0x0d,0x66,0x35,0xc8,0xce,0xb4,0x7c,0x4d,0xda,0x0d,0x76,0xa2,0x8e,0xac,0x93,0x9c,0x71,0xc3,0x02,0x48,0x04,0x86,0x6c,0x71,0x62,0x66,0x58,0x44,0x21,0x63,0xc2,0xc2,0x21,0x17,0xe5,0x0a,0xce,0xfc,0xe6,0x37,0x8a,0x98,0x56,0x52,0x30,0x2a,0x4e,0xf0,0xc2,0xce,0x0c,0xc7,0x16,0xb7,0x79,0x6e,0x2b,0x6b,0x2e,0x37,0x77,0xdf,0xa1,0xac,0x3d,0xa2,0x59,0xa3,0x1b,0x5a,0x9b,0x53,0x0f,0x8c,0xb6,0x38,0xa8,0x1a,0x62,0xac,0x30,0x18,0x49,0xab,0xaf,0x95,0xa7,0x30,0x1b,0xda,0x30,0x06,0x89,0x09,0xbf,0xdb,0x7e,0x67,0xdb,0xcc,0xbb,0x38,0xa5,0x55,0x1a,0x25,0xb1,0xa3,0xa0,0xf6,0x85,0x74,0x8a,0xd5,0x75,0x3d,0x88,0x80,0xf0,0x01,0x6c,0x62,0x74,0x86,0x16,0x63,0x84,0xc5,0x57,0x1f,0xe2,0x36,0x59,0x00,0x36,0x4d,0x03,0x83,0x11,0xe2,0xd8,0x75,0xdb,0x36,0x66,0x86,0x93,0x2b,0x5e,0xc6,0x02,0x43,0x0a,0x36,0x9e,0x87,0xa6,0xef,0x5c,0x33,0x87,0x86,0x65,0x78,0x25,0xbd,0x4c,0x05,0x7a,0xce,0xb9,0x23,0xeb,0x09,0x35,0xe6,0x90,0x5e,0x63,0xb4,0xce,0xd7,0xf8,0x08,0x57,0xa7,0x73,0xdd,0x64,0xb1,0x50,0xd2,0x66,0x12,0xea,0x9a,0xc1,0x20,0x52,0xdb,0x20,0x17,0xbf,0x18,0x43,0xcc,0xb4,0xb3,0x28,0x1b,0x69,0x0d,0xc7,0x28,0xad,0xfa,0x85,0xc0,0x02,0x81,0xb8,0xe3,0xc0,0x92,0x87,0x33,0x5f,0x85,0x6b,0x4f,0xc2,0x89,0x2f,0x69,0xa2,0xf5,0x79,0x21,0xad,0xa0,0x19,0x14,0xc4,0x09,0x88,0x66,0x2d,0x57,0x76,0x96,0x62,0xa7,0x86,0x35,0x1b,0x9b,0x66,0x49,0x3d,0xab,0x79,0x59,0x4d,0x98,0x6d,0xe2,0x10,0x0d,0x65,0xba,0x0f,0xf4,0xea,0x58,0xb8,0x15,0x38,0xd2,0x4a,0x44,0x35,0xa2,0x58,0xfa,0xc2,0x54,0x04,0xaa,0x7f,0x41,0xf6,0x58,0xb1,0x38,0x50,0x65,0xe1,0x58,0xdc,0xb6,0x01,0x15,0x73,0x27,0x20,0xf4,0x04,0x59,0xaa,0xac,0x15,0xe4,0x06,0x95,0x3a,0x90,0xac,0x52,0x99,0x7d,0x1c,0xcd,0x07,0x00,0x60,0xef,0xc6,0x5d,0xb9,0xe6,0x53,0x35,0x44,0x67,0xfa,0xd5,0x6e,0xc7,0x13,0xc8,0x6e,0x75,0x40,0xc4,0x23,0xac,0xf2,0x66,0x9f,0x52,0xfa,0x6f,0x4a,0xc6,0x88,0x8d,0x87,0x1e,0xf3,0xe8,0x47,0xc0,0x29,0xa8,0xaa,0xfb,0xb9,0x2e,0x17,0xb2,0x4a,0xa0,0x79,0xb1,0xf4,0x19,0xba,0x61,0x75,0xb4,0x42,0xaf,0xb1,0x19,0x09,0xd4,0xa5,0x6b,0x70,0xa0,0x33,0x5b,0x28,0x73,0x92,0x18,0xaa,0x7c,0x93,0x48,0xe2,0xc3,0xc2,0xf3,0xeb,0x3d,0x15,0xa4,0x1e,0x64,0x17,0xc0,0xdd,0x94,0xbf,0xeb,0x21,0x41,0x9b,0x31,0x1a,0x7b,0xb1,0x3a,0x18,0x0b,0xbe,0x83,0x32,0x18,0xa9,0xa6,0xb1,0x74,0x47,0xcc,0x85,0xf2,0x25,0x85,0x95,0x87,0xa7,0x30,0x77,0x04,0x9a,0xcb,0xcf,0xd4,0x4d,0x0f,0x02,0x54,0x38,0xe1,0x5d,0x15,0x38,0x27,0x0d,0x58,0x6e,0x1b,0xf8,0x31,0x92,0xa9,0x45,0x9c,0xf6,0x3c,0x0e,0x97,0x2f,0x85,0x29,0x76,0x79,0x83,0x1e,0xcf,0x12,0x15,0x09,0x85,0x1c,0xb8,0x34,0x0f,0x6f,0x10,0x7b,0x0f,0xa1,0xa0,0xef,0xd1,0xb3,0x6a,0x81,0x89,0xbc,0x08,0x5c,0x4f,0x5c,0xb7,0x84,0xe5,0x53,0xf4,0x1b,0x91,0x8f,0x80,0x39,0x7c,0xe1,0x95,0x6f,0x78,0x5b,0xee,0x37,0x7c,0xa9,0xaa,0x8b,0xe6,0x99,0x8a,0xda,0x30,0xc2,0x6b,0x7c,0x3d,0x8c,0x6b,0x55,0x25,0x4c,0xc9,0x62,0x03,0xb2,0x0c,0x42,0xae,0xe0,0xac,0x4e,0x1e,0xbb,0x40,0x8e,0x49,0xa9,0xe3,0xf8,0x79,0xd0,0xab,0x07,0x85,0xeb,0x70,0x25,0x42,0x5d,0x13,0x05,0xa2,0x29,0x9c,0x01,0x5e,0x12,0x0d,0x16,0x3b,0x0e,0x19,0x49,0x4c,0xe5,0x72,0x53,0xd0,0x24,0x6d,0x18,0x27,0x45,0xcb,0x81,0x97,0xab,0x74,0x38,0xb3,0xc1,0xbb,0x79,0x72,0xbe,0xc5,0xa3,0x06,0xeb,0xa3,0x56,0x78,0x55,0xc0,0x14,0x69,0x9f,0xef,0x65,0xae,0x54,0xc7,0x70,0xa0,0xd8,0x5c,0x18,0x40,0x0c,0xf6,0x42,0xae,0xdc,0x66,0x07,0x77,0xba,0x4b,0x13,0x85,0x02,0xbd,0x5a,0x78,0x12,0xf6,0x21,0xf8,0x4a,0x48,0x29,0x6b,0x98,0xdd,0x43,0x22,0xb6,0xf1,0x58,0x28,0xb8,0xa8,0xf0,0xe0,0x0a,0x8b,0xa4,0x4a,0x53,0xc3,0xa8,0xb1,0x43,0x57,0x1b,0x07,0x40,0xab,0xd5,0x67,0xda,0xf1,0xcd,0xe9,0xc7,0x9c,0x20,0x4b,0x6d,0x5e,0x25,0x9d,0x17,0x66,0xa3,0x1b,0xbb,0xcb,0x4e,0x6a,0x05,0xcf,0x45,0x02,0x17,0x6b,0x30,0x1c,0x1c,0x2f,0x41,0x24,0x77,0x50,0x15,0x7b,0xce,0xc8,0x5e,0x80,0x9b,0x30,0xa4,0xd6,0x0d,0x77,0x47,0xcd,0xd0,0xf5,0xb9,0x9a,0xa8,0xc8,0x26,0x98,0x75,0x17,0x79,0x3a,0xaa,0x80,0x80,0xa0,0xb1,0x24,0xa8,0x55,0x8d,0xf7,0x2b,0xbe,0x37,0xb7,0x5f,0x4e,0xdb,0xb6,0xbe,0x82,0x16,0xd6,0xc6,0x33,0xfb,0x2b,0x22,0x80,0xe2,0x51,0x13,0xd8,0x69,0x5e,0x43,0x48,0x1c,0x3e,0xeb,0x39,0x7e,0xb1,0x92,0x50,0x52,0x29,0xb6,0x7a,0x20,0x1e,0xa8,0x93,0xc3,0xe2,0xcb,0x32,0xda,0x8b,0xc3,0x42,0xfa,0x4d,0xea,0x05,0x78]; + let pk_bytes: [u8; MLKEM768_PK_LEN] = [ + 0x29, 0x8A, 0xA1, 0x0D, 0x42, 0x3C, 0x8D, 0xDA, 0x06, 0x9D, 0x02, 0xBC, 0x59, 0xE6, 0xCD, + 0xF0, 0x3A, 0x09, 0x6B, 0x8B, 0x3D, 0xA4, 0xCA, 0xB9, 0xB8, 0x0C, 0xA4, 0xA1, 0x49, 0x07, + 0x67, 0x2C, 0xCE, 0xF1, 0xEC, 0x4F, 0xAF, 0x23, 0x4A, 0x0B, 0xC5, 0xB7, 0xE9, 0xD4, 0x73, + 0xF2, 0xB3, 0x13, 0x3B, 0x3B, 0x26, 0xA1, 0xD1, 0x75, 0xCB, 0x67, 0xA7, 0x80, 0x59, 0x19, + 0x69, 0x9C, 0x02, 0xF7, 0x65, 0x31, 0xB9, 0x9C, 0x5F, 0x89, 0x18, 0x07, 0x04, 0xBB, 0x4C, + 0xA4, 0x53, 0x5C, 0x5B, 0x89, 0x72, 0x67, 0x9C, 0x66, 0x0A, 0x07, 0xC5, 0xE5, 0x14, 0xB8, + 0x70, 0x09, 0xC8, 0x62, 0xEB, 0x8F, 0x51, 0x57, 0x69, 0x5E, 0xFB, 0x3F, 0xC4, 0x0A, 0x9D, + 0xEF, 0x6B, 0x81, 0xC1, 0xCC, 0x02, 0xA2, 0x49, 0xAE, 0x4F, 0x09, 0x4A, 0xD0, 0xD9, 0xBD, + 0x34, 0x85, 0xC1, 0xC1, 0xC6, 0x80, 0x80, 0x52, 0x0A, 0x7C, 0x8C, 0x63, 0x20, 0x32, 0xCE, + 0xE7, 0x38, 0x15, 0x4E, 0x5C, 0x51, 0x76, 0xC0, 0x7D, 0xA5, 0x60, 0x24, 0x77, 0x6A, 0x43, + 0x0F, 0xE7, 0x6E, 0xAC, 0xF6, 0x65, 0xA3, 0xF7, 0xB8, 0x32, 0x10, 0x22, 0x15, 0xBC, 0x82, + 0xF1, 0x09, 0x39, 0xC8, 0x35, 0x57, 0x04, 0x33, 0x6A, 0x8F, 0xAC, 0x1D, 0x81, 0xE4, 0xBB, + 0x04, 0x85, 0xAA, 0x5D, 0x7C, 0x74, 0xD6, 0xB5, 0x9B, 0xBE, 0x5C, 0x5E, 0x97, 0x2A, 0x0D, + 0x8B, 0xAC, 0x41, 0x1B, 0x55, 0xB5, 0xD5, 0x55, 0x7C, 0xD6, 0x80, 0xA1, 0xA8, 0xF7, 0x1B, + 0x4E, 0xB8, 0x6B, 0xC4, 0x8C, 0x9A, 0x05, 0x09, 0x73, 0x1A, 0x54, 0xBD, 0x9D, 0x72, 0x90, + 0xB2, 0x79, 0x63, 0xE4, 0x37, 0x2D, 0xC9, 0xB1, 0x99, 0xCF, 0xDC, 0xAC, 0x0B, 0x01, 0xAC, + 0xD2, 0x8A, 0x62, 0x39, 0x51, 0x12, 0xE4, 0xC4, 0x36, 0x48, 0xD6, 0x22, 0xC4, 0x8C, 0x82, + 0x34, 0xD0, 0x14, 0x40, 0xE8, 0xCC, 0x37, 0x6C, 0x92, 0x7F, 0x23, 0xA5, 0xAF, 0xC9, 0xAC, + 0x04, 0x74, 0xC6, 0x62, 0x27, 0x4E, 0x42, 0x45, 0x25, 0xC8, 0x55, 0x2E, 0xCE, 0x3B, 0x3F, + 0xE2, 0x65, 0x16, 0xDE, 0x90, 0x1B, 0xC7, 0xD5, 0x15, 0xBD, 0xE8, 0x95, 0x58, 0xE6, 0x26, + 0xC9, 0x5C, 0x80, 0xB9, 0x33, 0x42, 0xF8, 0x01, 0x00, 0x04, 0xF3, 0x9E, 0x6C, 0x6C, 0x94, + 0x87, 0x1C, 0x5E, 0x34, 0x4C, 0xAB, 0x39, 0x66, 0xC8, 0x35, 0xF9, 0xA9, 0x6A, 0x59, 0xAF, + 0xD3, 0x1C, 0x40, 0x28, 0x6B, 0x38, 0xB1, 0xC1, 0xA7, 0x84, 0x70, 0xBA, 0xB9, 0x47, 0x51, + 0x89, 0x34, 0x45, 0x3C, 0xE8, 0x67, 0x36, 0xA9, 0x19, 0xF1, 0xF5, 0xA6, 0xD5, 0x10, 0xA8, + 0x6F, 0x54, 0x54, 0xFC, 0x39, 0x80, 0xCB, 0x5C, 0x76, 0x5B, 0xD2, 0xBD, 0x5F, 0x7B, 0x36, + 0xB1, 0x41, 0x0D, 0x66, 0x35, 0xC8, 0xCE, 0xB4, 0x7C, 0x4D, 0xDA, 0x0D, 0x76, 0xA2, 0x8E, + 0xAC, 0x93, 0x9C, 0x71, 0xC3, 0x02, 0x48, 0x04, 0x86, 0x6C, 0x71, 0x62, 0x66, 0x58, 0x44, + 0x21, 0x63, 0xC2, 0xC2, 0x21, 0x17, 0xE5, 0x0A, 0xCE, 0xFC, 0xE6, 0x37, 0x8A, 0x98, 0x56, + 0x52, 0x30, 0x2A, 0x4E, 0xF0, 0xC2, 0xCE, 0x0C, 0xC7, 0x16, 0xB7, 0x79, 0x6E, 0x2B, 0x6B, + 0x2E, 0x37, 0x77, 0xDF, 0xA1, 0xAC, 0x3D, 0xA2, 0x59, 0xA3, 0x1B, 0x5A, 0x9B, 0x53, 0x0F, + 0x8C, 0xB6, 0x38, 0xA8, 0x1A, 0x62, 0xAC, 0x30, 0x18, 0x49, 0xAB, 0xAF, 0x95, 0xA7, 0x30, + 0x1B, 0xDA, 0x30, 0x06, 0x89, 0x09, 0xBF, 0xDB, 0x7E, 0x67, 0xDB, 0xCC, 0xBB, 0x38, 0xA5, + 0x55, 0x1A, 0x25, 0xB1, 0xA3, 0xA0, 0xF6, 0x85, 0x74, 0x8A, 0xD5, 0x75, 0x3D, 0x88, 0x80, + 0xF0, 0x01, 0x6C, 0x62, 0x74, 0x86, 0x16, 0x63, 0x84, 0xC5, 0x57, 0x1F, 0xE2, 0x36, 0x59, + 0x00, 0x36, 0x4D, 0x03, 0x83, 0x11, 0xE2, 0xD8, 0x75, 0xDB, 0x36, 0x66, 0x86, 0x93, 0x2B, + 0x5E, 0xC6, 0x02, 0x43, 0x0A, 0x36, 0x9E, 0x87, 0xA6, 0xEF, 0x5C, 0x33, 0x87, 0x86, 0x65, + 0x78, 0x25, 0xBD, 0x4C, 0x05, 0x7A, 0xCE, 0xB9, 0x23, 0xEB, 0x09, 0x35, 0xE6, 0x90, 0x5E, + 0x63, 0xB4, 0xCE, 0xD7, 0xF8, 0x08, 0x57, 0xA7, 0x73, 0xDD, 0x64, 0xB1, 0x50, 0xD2, 0x66, + 0x12, 0xEA, 0x9A, 0xC1, 0x20, 0x52, 0xDB, 0x20, 0x17, 0xBF, 0x18, 0x43, 0xCC, 0xB4, 0xB3, + 0x28, 0x1B, 0x69, 0x0D, 0xC7, 0x28, 0xAD, 0xFA, 0x85, 0xC0, 0x02, 0x81, 0xB8, 0xE3, 0xC0, + 0x92, 0x87, 0x33, 0x5F, 0x85, 0x6B, 0x4F, 0xC2, 0x89, 0x2F, 0x69, 0xA2, 0xF5, 0x79, 0x21, + 0xAD, 0xA0, 0x19, 0x14, 0xC4, 0x09, 0x88, 0x66, 0x2D, 0x57, 0x76, 0x96, 0x62, 0xA7, 0x86, + 0x35, 0x1B, 0x9B, 0x66, 0x49, 0x3D, 0xAB, 0x79, 0x59, 0x4D, 0x98, 0x6D, 0xE2, 0x10, 0x0D, + 0x65, 0xBA, 0x0F, 0xF4, 0xEA, 0x58, 0xB8, 0x15, 0x38, 0xD2, 0x4A, 0x44, 0x35, 0xA2, 0x58, + 0xFA, 0xC2, 0x54, 0x04, 0xAA, 0x7F, 0x41, 0xF6, 0x58, 0xB1, 0x38, 0x50, 0x65, 0xE1, 0x58, + 0xDC, 0xB6, 0x01, 0x15, 0x73, 0x27, 0x20, 0xF4, 0x04, 0x59, 0xAA, 0xAC, 0x15, 0xE4, 0x06, + 0x95, 0x3A, 0x90, 0xAC, 0x52, 0x99, 0x7D, 0x1C, 0xCD, 0x07, 0x00, 0x60, 0xEF, 0xC6, 0x5D, + 0xB9, 0xE6, 0x53, 0x35, 0x44, 0x67, 0xFA, 0xD5, 0x6E, 0xC7, 0x13, 0xC8, 0x6E, 0x75, 0x40, + 0xC4, 0x23, 0xAC, 0xF2, 0x66, 0x9F, 0x52, 0xFA, 0x6F, 0x4A, 0xC6, 0x88, 0x8D, 0x87, 0x1E, + 0xF3, 0xE8, 0x47, 0xC0, 0x29, 0xA8, 0xAA, 0xFB, 0xB9, 0x2E, 0x17, 0xB2, 0x4A, 0xA0, 0x79, + 0xB1, 0xF4, 0x19, 0xBA, 0x61, 0x75, 0xB4, 0x42, 0xAF, 0xB1, 0x19, 0x09, 0xD4, 0xA5, 0x6B, + 0x70, 0xA0, 0x33, 0x5B, 0x28, 0x73, 0x92, 0x18, 0xAA, 0x7C, 0x93, 0x48, 0xE2, 0xC3, 0xC2, + 0xF3, 0xEB, 0x3D, 0x15, 0xA4, 0x1E, 0x64, 0x17, 0xC0, 0xDD, 0x94, 0xBF, 0xEB, 0x21, 0x41, + 0x9B, 0x31, 0x1A, 0x7B, 0xB1, 0x3A, 0x18, 0x0B, 0xBE, 0x83, 0x32, 0x18, 0xA9, 0xA6, 0xB1, + 0x74, 0x47, 0xCC, 0x85, 0xF2, 0x25, 0x85, 0x95, 0x87, 0xA7, 0x30, 0x77, 0x04, 0x9A, 0xCB, + 0xCF, 0xD4, 0x4D, 0x0F, 0x02, 0x54, 0x38, 0xE1, 0x5D, 0x15, 0x38, 0x27, 0x0D, 0x58, 0x6E, + 0x1B, 0xF8, 0x31, 0x92, 0xA9, 0x45, 0x9C, 0xF6, 0x3C, 0x0E, 0x97, 0x2F, 0x85, 0x29, 0x76, + 0x79, 0x83, 0x1E, 0xCF, 0x12, 0x15, 0x09, 0x85, 0x1C, 0xB8, 0x34, 0x0F, 0x6F, 0x10, 0x7B, + 0x0F, 0xA1, 0xA0, 0xEF, 0xD1, 0xB3, 0x6A, 0x81, 0x89, 0xBC, 0x08, 0x5C, 0x4F, 0x5C, 0xB7, + 0x84, 0xE5, 0x53, 0xF4, 0x1B, 0x91, 0x8F, 0x80, 0x39, 0x7C, 0xE1, 0x95, 0x6F, 0x78, 0x5B, + 0xEE, 0x37, 0x7C, 0xA9, 0xAA, 0x8B, 0xE6, 0x99, 0x8A, 0xDA, 0x30, 0xC2, 0x6B, 0x7C, 0x3D, + 0x8C, 0x6B, 0x55, 0x25, 0x4C, 0xC9, 0x62, 0x03, 0xB2, 0x0C, 0x42, 0xAE, 0xE0, 0xAC, 0x4E, + 0x1E, 0xBB, 0x40, 0x8E, 0x49, 0xA9, 0xE3, 0xF8, 0x79, 0xD0, 0xAB, 0x07, 0x85, 0xEB, 0x70, + 0x25, 0x42, 0x5D, 0x13, 0x05, 0xA2, 0x29, 0x9C, 0x01, 0x5E, 0x12, 0x0D, 0x16, 0x3B, 0x0E, + 0x19, 0x49, 0x4C, 0xE5, 0x72, 0x53, 0xD0, 0x24, 0x6D, 0x18, 0x27, 0x45, 0xCB, 0x81, 0x97, + 0xAB, 0x74, 0x38, 0xB3, 0xC1, 0xBB, 0x79, 0x72, 0xBE, 0xC5, 0xA3, 0x06, 0xEB, 0xA3, 0x56, + 0x78, 0x55, 0xC0, 0x14, 0x69, 0x9F, 0xEF, 0x65, 0xAE, 0x54, 0xC7, 0x70, 0xA0, 0xD8, 0x5C, + 0x18, 0x40, 0x0C, 0xF6, 0x42, 0xAE, 0xDC, 0x66, 0x07, 0x77, 0xBA, 0x4B, 0x13, 0x85, 0x02, + 0xBD, 0x5A, 0x78, 0x12, 0xF6, 0x21, 0xF8, 0x4A, 0x48, 0x29, 0x6B, 0x98, 0xDD, 0x43, 0x22, + 0xB6, 0xF1, 0x58, 0x28, 0xB8, 0xA8, 0xF0, 0xE0, 0x0A, 0x8B, 0xA4, 0x4A, 0x53, 0xC3, 0xA8, + 0xB1, 0x43, 0x57, 0x1B, 0x07, 0x40, 0xAB, 0xD5, 0x67, 0xDA, 0xF1, 0xCD, 0xE9, 0xC7, 0x9C, + 0x20, 0x4B, 0x6D, 0x5E, 0x25, 0x9D, 0x17, 0x66, 0xA3, 0x1B, 0xBB, 0xCB, 0x4E, 0x6A, 0x05, + 0xCF, 0x45, 0x02, 0x17, 0x6B, 0x30, 0x1C, 0x1C, 0x2F, 0x41, 0x24, 0x77, 0x50, 0x15, 0x7B, + 0xCE, 0xC8, 0x5E, 0x80, 0x9B, 0x30, 0xA4, 0xD6, 0x0D, 0x77, 0x47, 0xCD, 0xD0, 0xF5, 0xB9, + 0x9A, 0xA8, 0xC8, 0x26, 0x98, 0x75, 0x17, 0x79, 0x3A, 0xAA, 0x80, 0x80, 0xA0, 0xB1, 0x24, + 0xA8, 0x55, 0x8D, 0xF7, 0x2B, 0xBE, 0x37, 0xB7, 0x5F, 0x4E, 0xDB, 0xB6, 0xBE, 0x82, 0x16, + 0xD6, 0xC6, 0x33, 0xFB, 0x2B, 0x22, 0x80, 0xE2, 0x51, 0x13, 0xD8, 0x69, 0x5E, 0x43, 0x48, + 0x1C, 0x3E, 0xEB, 0x39, 0x7E, 0xB1, 0x92, 0x50, 0x52, 0x29, 0xB6, 0x7A, 0x20, 0x1E, 0xA8, + 0x93, 0xC3, 0xE2, 0xCB, 0x32, 0xDA, 0x8B, 0xC3, 0x42, 0xFA, 0x4D, 0xEA, 0x05, 0x78, + ]; let pk = MLKEM768PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM768::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -231,7 +497,7 @@ fn bench_mlkem768_encaps() { } fn bench_mlkem768_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM768, MLKEM768PublicKey}; + use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEM768PublicKey, MLKEMTrait}; eprintln!("MLKEM768_lowmemory/Encaps"); @@ -245,7 +511,87 @@ fn bench_mlkem768_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM768_PK_LEN] = [0x29,0x8a,0xa1,0x0d,0x42,0x3c,0x8d,0xda,0x06,0x9d,0x02,0xbc,0x59,0xe6,0xcd,0xf0,0x3a,0x09,0x6b,0x8b,0x3d,0xa4,0xca,0xb9,0xb8,0x0c,0xa4,0xa1,0x49,0x07,0x67,0x2c,0xce,0xf1,0xec,0x4f,0xaf,0x23,0x4a,0x0b,0xc5,0xb7,0xe9,0xd4,0x73,0xf2,0xb3,0x13,0x3b,0x3b,0x26,0xa1,0xd1,0x75,0xcb,0x67,0xa7,0x80,0x59,0x19,0x69,0x9c,0x02,0xf7,0x65,0x31,0xb9,0x9c,0x5f,0x89,0x18,0x07,0x04,0xbb,0x4c,0xa4,0x53,0x5c,0x5b,0x89,0x72,0x67,0x9c,0x66,0x0a,0x07,0xc5,0xe5,0x14,0xb8,0x70,0x09,0xc8,0x62,0xeb,0x8f,0x51,0x57,0x69,0x5e,0xfb,0x3f,0xc4,0x0a,0x9d,0xef,0x6b,0x81,0xc1,0xcc,0x02,0xa2,0x49,0xae,0x4f,0x09,0x4a,0xd0,0xd9,0xbd,0x34,0x85,0xc1,0xc1,0xc6,0x80,0x80,0x52,0x0a,0x7c,0x8c,0x63,0x20,0x32,0xce,0xe7,0x38,0x15,0x4e,0x5c,0x51,0x76,0xc0,0x7d,0xa5,0x60,0x24,0x77,0x6a,0x43,0x0f,0xe7,0x6e,0xac,0xf6,0x65,0xa3,0xf7,0xb8,0x32,0x10,0x22,0x15,0xbc,0x82,0xf1,0x09,0x39,0xc8,0x35,0x57,0x04,0x33,0x6a,0x8f,0xac,0x1d,0x81,0xe4,0xbb,0x04,0x85,0xaa,0x5d,0x7c,0x74,0xd6,0xb5,0x9b,0xbe,0x5c,0x5e,0x97,0x2a,0x0d,0x8b,0xac,0x41,0x1b,0x55,0xb5,0xd5,0x55,0x7c,0xd6,0x80,0xa1,0xa8,0xf7,0x1b,0x4e,0xb8,0x6b,0xc4,0x8c,0x9a,0x05,0x09,0x73,0x1a,0x54,0xbd,0x9d,0x72,0x90,0xb2,0x79,0x63,0xe4,0x37,0x2d,0xc9,0xb1,0x99,0xcf,0xdc,0xac,0x0b,0x01,0xac,0xd2,0x8a,0x62,0x39,0x51,0x12,0xe4,0xc4,0x36,0x48,0xd6,0x22,0xc4,0x8c,0x82,0x34,0xd0,0x14,0x40,0xe8,0xcc,0x37,0x6c,0x92,0x7f,0x23,0xa5,0xaf,0xc9,0xac,0x04,0x74,0xc6,0x62,0x27,0x4e,0x42,0x45,0x25,0xc8,0x55,0x2e,0xce,0x3b,0x3f,0xe2,0x65,0x16,0xde,0x90,0x1b,0xc7,0xd5,0x15,0xbd,0xe8,0x95,0x58,0xe6,0x26,0xc9,0x5c,0x80,0xb9,0x33,0x42,0xf8,0x01,0x00,0x04,0xf3,0x9e,0x6c,0x6c,0x94,0x87,0x1c,0x5e,0x34,0x4c,0xab,0x39,0x66,0xc8,0x35,0xf9,0xa9,0x6a,0x59,0xaf,0xd3,0x1c,0x40,0x28,0x6b,0x38,0xb1,0xc1,0xa7,0x84,0x70,0xba,0xb9,0x47,0x51,0x89,0x34,0x45,0x3c,0xe8,0x67,0x36,0xa9,0x19,0xf1,0xf5,0xa6,0xd5,0x10,0xa8,0x6f,0x54,0x54,0xfc,0x39,0x80,0xcb,0x5c,0x76,0x5b,0xd2,0xbd,0x5f,0x7b,0x36,0xb1,0x41,0x0d,0x66,0x35,0xc8,0xce,0xb4,0x7c,0x4d,0xda,0x0d,0x76,0xa2,0x8e,0xac,0x93,0x9c,0x71,0xc3,0x02,0x48,0x04,0x86,0x6c,0x71,0x62,0x66,0x58,0x44,0x21,0x63,0xc2,0xc2,0x21,0x17,0xe5,0x0a,0xce,0xfc,0xe6,0x37,0x8a,0x98,0x56,0x52,0x30,0x2a,0x4e,0xf0,0xc2,0xce,0x0c,0xc7,0x16,0xb7,0x79,0x6e,0x2b,0x6b,0x2e,0x37,0x77,0xdf,0xa1,0xac,0x3d,0xa2,0x59,0xa3,0x1b,0x5a,0x9b,0x53,0x0f,0x8c,0xb6,0x38,0xa8,0x1a,0x62,0xac,0x30,0x18,0x49,0xab,0xaf,0x95,0xa7,0x30,0x1b,0xda,0x30,0x06,0x89,0x09,0xbf,0xdb,0x7e,0x67,0xdb,0xcc,0xbb,0x38,0xa5,0x55,0x1a,0x25,0xb1,0xa3,0xa0,0xf6,0x85,0x74,0x8a,0xd5,0x75,0x3d,0x88,0x80,0xf0,0x01,0x6c,0x62,0x74,0x86,0x16,0x63,0x84,0xc5,0x57,0x1f,0xe2,0x36,0x59,0x00,0x36,0x4d,0x03,0x83,0x11,0xe2,0xd8,0x75,0xdb,0x36,0x66,0x86,0x93,0x2b,0x5e,0xc6,0x02,0x43,0x0a,0x36,0x9e,0x87,0xa6,0xef,0x5c,0x33,0x87,0x86,0x65,0x78,0x25,0xbd,0x4c,0x05,0x7a,0xce,0xb9,0x23,0xeb,0x09,0x35,0xe6,0x90,0x5e,0x63,0xb4,0xce,0xd7,0xf8,0x08,0x57,0xa7,0x73,0xdd,0x64,0xb1,0x50,0xd2,0x66,0x12,0xea,0x9a,0xc1,0x20,0x52,0xdb,0x20,0x17,0xbf,0x18,0x43,0xcc,0xb4,0xb3,0x28,0x1b,0x69,0x0d,0xc7,0x28,0xad,0xfa,0x85,0xc0,0x02,0x81,0xb8,0xe3,0xc0,0x92,0x87,0x33,0x5f,0x85,0x6b,0x4f,0xc2,0x89,0x2f,0x69,0xa2,0xf5,0x79,0x21,0xad,0xa0,0x19,0x14,0xc4,0x09,0x88,0x66,0x2d,0x57,0x76,0x96,0x62,0xa7,0x86,0x35,0x1b,0x9b,0x66,0x49,0x3d,0xab,0x79,0x59,0x4d,0x98,0x6d,0xe2,0x10,0x0d,0x65,0xba,0x0f,0xf4,0xea,0x58,0xb8,0x15,0x38,0xd2,0x4a,0x44,0x35,0xa2,0x58,0xfa,0xc2,0x54,0x04,0xaa,0x7f,0x41,0xf6,0x58,0xb1,0x38,0x50,0x65,0xe1,0x58,0xdc,0xb6,0x01,0x15,0x73,0x27,0x20,0xf4,0x04,0x59,0xaa,0xac,0x15,0xe4,0x06,0x95,0x3a,0x90,0xac,0x52,0x99,0x7d,0x1c,0xcd,0x07,0x00,0x60,0xef,0xc6,0x5d,0xb9,0xe6,0x53,0x35,0x44,0x67,0xfa,0xd5,0x6e,0xc7,0x13,0xc8,0x6e,0x75,0x40,0xc4,0x23,0xac,0xf2,0x66,0x9f,0x52,0xfa,0x6f,0x4a,0xc6,0x88,0x8d,0x87,0x1e,0xf3,0xe8,0x47,0xc0,0x29,0xa8,0xaa,0xfb,0xb9,0x2e,0x17,0xb2,0x4a,0xa0,0x79,0xb1,0xf4,0x19,0xba,0x61,0x75,0xb4,0x42,0xaf,0xb1,0x19,0x09,0xd4,0xa5,0x6b,0x70,0xa0,0x33,0x5b,0x28,0x73,0x92,0x18,0xaa,0x7c,0x93,0x48,0xe2,0xc3,0xc2,0xf3,0xeb,0x3d,0x15,0xa4,0x1e,0x64,0x17,0xc0,0xdd,0x94,0xbf,0xeb,0x21,0x41,0x9b,0x31,0x1a,0x7b,0xb1,0x3a,0x18,0x0b,0xbe,0x83,0x32,0x18,0xa9,0xa6,0xb1,0x74,0x47,0xcc,0x85,0xf2,0x25,0x85,0x95,0x87,0xa7,0x30,0x77,0x04,0x9a,0xcb,0xcf,0xd4,0x4d,0x0f,0x02,0x54,0x38,0xe1,0x5d,0x15,0x38,0x27,0x0d,0x58,0x6e,0x1b,0xf8,0x31,0x92,0xa9,0x45,0x9c,0xf6,0x3c,0x0e,0x97,0x2f,0x85,0x29,0x76,0x79,0x83,0x1e,0xcf,0x12,0x15,0x09,0x85,0x1c,0xb8,0x34,0x0f,0x6f,0x10,0x7b,0x0f,0xa1,0xa0,0xef,0xd1,0xb3,0x6a,0x81,0x89,0xbc,0x08,0x5c,0x4f,0x5c,0xb7,0x84,0xe5,0x53,0xf4,0x1b,0x91,0x8f,0x80,0x39,0x7c,0xe1,0x95,0x6f,0x78,0x5b,0xee,0x37,0x7c,0xa9,0xaa,0x8b,0xe6,0x99,0x8a,0xda,0x30,0xc2,0x6b,0x7c,0x3d,0x8c,0x6b,0x55,0x25,0x4c,0xc9,0x62,0x03,0xb2,0x0c,0x42,0xae,0xe0,0xac,0x4e,0x1e,0xbb,0x40,0x8e,0x49,0xa9,0xe3,0xf8,0x79,0xd0,0xab,0x07,0x85,0xeb,0x70,0x25,0x42,0x5d,0x13,0x05,0xa2,0x29,0x9c,0x01,0x5e,0x12,0x0d,0x16,0x3b,0x0e,0x19,0x49,0x4c,0xe5,0x72,0x53,0xd0,0x24,0x6d,0x18,0x27,0x45,0xcb,0x81,0x97,0xab,0x74,0x38,0xb3,0xc1,0xbb,0x79,0x72,0xbe,0xc5,0xa3,0x06,0xeb,0xa3,0x56,0x78,0x55,0xc0,0x14,0x69,0x9f,0xef,0x65,0xae,0x54,0xc7,0x70,0xa0,0xd8,0x5c,0x18,0x40,0x0c,0xf6,0x42,0xae,0xdc,0x66,0x07,0x77,0xba,0x4b,0x13,0x85,0x02,0xbd,0x5a,0x78,0x12,0xf6,0x21,0xf8,0x4a,0x48,0x29,0x6b,0x98,0xdd,0x43,0x22,0xb6,0xf1,0x58,0x28,0xb8,0xa8,0xf0,0xe0,0x0a,0x8b,0xa4,0x4a,0x53,0xc3,0xa8,0xb1,0x43,0x57,0x1b,0x07,0x40,0xab,0xd5,0x67,0xda,0xf1,0xcd,0xe9,0xc7,0x9c,0x20,0x4b,0x6d,0x5e,0x25,0x9d,0x17,0x66,0xa3,0x1b,0xbb,0xcb,0x4e,0x6a,0x05,0xcf,0x45,0x02,0x17,0x6b,0x30,0x1c,0x1c,0x2f,0x41,0x24,0x77,0x50,0x15,0x7b,0xce,0xc8,0x5e,0x80,0x9b,0x30,0xa4,0xd6,0x0d,0x77,0x47,0xcd,0xd0,0xf5,0xb9,0x9a,0xa8,0xc8,0x26,0x98,0x75,0x17,0x79,0x3a,0xaa,0x80,0x80,0xa0,0xb1,0x24,0xa8,0x55,0x8d,0xf7,0x2b,0xbe,0x37,0xb7,0x5f,0x4e,0xdb,0xb6,0xbe,0x82,0x16,0xd6,0xc6,0x33,0xfb,0x2b,0x22,0x80,0xe2,0x51,0x13,0xd8,0x69,0x5e,0x43,0x48,0x1c,0x3e,0xeb,0x39,0x7e,0xb1,0x92,0x50,0x52,0x29,0xb6,0x7a,0x20,0x1e,0xa8,0x93,0xc3,0xe2,0xcb,0x32,0xda,0x8b,0xc3,0x42,0xfa,0x4d,0xea,0x05,0x78]; + let pk_bytes: [u8; MLKEM768_PK_LEN] = [ + 0x29, 0x8A, 0xA1, 0x0D, 0x42, 0x3C, 0x8D, 0xDA, 0x06, 0x9D, 0x02, 0xBC, 0x59, 0xE6, 0xCD, + 0xF0, 0x3A, 0x09, 0x6B, 0x8B, 0x3D, 0xA4, 0xCA, 0xB9, 0xB8, 0x0C, 0xA4, 0xA1, 0x49, 0x07, + 0x67, 0x2C, 0xCE, 0xF1, 0xEC, 0x4F, 0xAF, 0x23, 0x4A, 0x0B, 0xC5, 0xB7, 0xE9, 0xD4, 0x73, + 0xF2, 0xB3, 0x13, 0x3B, 0x3B, 0x26, 0xA1, 0xD1, 0x75, 0xCB, 0x67, 0xA7, 0x80, 0x59, 0x19, + 0x69, 0x9C, 0x02, 0xF7, 0x65, 0x31, 0xB9, 0x9C, 0x5F, 0x89, 0x18, 0x07, 0x04, 0xBB, 0x4C, + 0xA4, 0x53, 0x5C, 0x5B, 0x89, 0x72, 0x67, 0x9C, 0x66, 0x0A, 0x07, 0xC5, 0xE5, 0x14, 0xB8, + 0x70, 0x09, 0xC8, 0x62, 0xEB, 0x8F, 0x51, 0x57, 0x69, 0x5E, 0xFB, 0x3F, 0xC4, 0x0A, 0x9D, + 0xEF, 0x6B, 0x81, 0xC1, 0xCC, 0x02, 0xA2, 0x49, 0xAE, 0x4F, 0x09, 0x4A, 0xD0, 0xD9, 0xBD, + 0x34, 0x85, 0xC1, 0xC1, 0xC6, 0x80, 0x80, 0x52, 0x0A, 0x7C, 0x8C, 0x63, 0x20, 0x32, 0xCE, + 0xE7, 0x38, 0x15, 0x4E, 0x5C, 0x51, 0x76, 0xC0, 0x7D, 0xA5, 0x60, 0x24, 0x77, 0x6A, 0x43, + 0x0F, 0xE7, 0x6E, 0xAC, 0xF6, 0x65, 0xA3, 0xF7, 0xB8, 0x32, 0x10, 0x22, 0x15, 0xBC, 0x82, + 0xF1, 0x09, 0x39, 0xC8, 0x35, 0x57, 0x04, 0x33, 0x6A, 0x8F, 0xAC, 0x1D, 0x81, 0xE4, 0xBB, + 0x04, 0x85, 0xAA, 0x5D, 0x7C, 0x74, 0xD6, 0xB5, 0x9B, 0xBE, 0x5C, 0x5E, 0x97, 0x2A, 0x0D, + 0x8B, 0xAC, 0x41, 0x1B, 0x55, 0xB5, 0xD5, 0x55, 0x7C, 0xD6, 0x80, 0xA1, 0xA8, 0xF7, 0x1B, + 0x4E, 0xB8, 0x6B, 0xC4, 0x8C, 0x9A, 0x05, 0x09, 0x73, 0x1A, 0x54, 0xBD, 0x9D, 0x72, 0x90, + 0xB2, 0x79, 0x63, 0xE4, 0x37, 0x2D, 0xC9, 0xB1, 0x99, 0xCF, 0xDC, 0xAC, 0x0B, 0x01, 0xAC, + 0xD2, 0x8A, 0x62, 0x39, 0x51, 0x12, 0xE4, 0xC4, 0x36, 0x48, 0xD6, 0x22, 0xC4, 0x8C, 0x82, + 0x34, 0xD0, 0x14, 0x40, 0xE8, 0xCC, 0x37, 0x6C, 0x92, 0x7F, 0x23, 0xA5, 0xAF, 0xC9, 0xAC, + 0x04, 0x74, 0xC6, 0x62, 0x27, 0x4E, 0x42, 0x45, 0x25, 0xC8, 0x55, 0x2E, 0xCE, 0x3B, 0x3F, + 0xE2, 0x65, 0x16, 0xDE, 0x90, 0x1B, 0xC7, 0xD5, 0x15, 0xBD, 0xE8, 0x95, 0x58, 0xE6, 0x26, + 0xC9, 0x5C, 0x80, 0xB9, 0x33, 0x42, 0xF8, 0x01, 0x00, 0x04, 0xF3, 0x9E, 0x6C, 0x6C, 0x94, + 0x87, 0x1C, 0x5E, 0x34, 0x4C, 0xAB, 0x39, 0x66, 0xC8, 0x35, 0xF9, 0xA9, 0x6A, 0x59, 0xAF, + 0xD3, 0x1C, 0x40, 0x28, 0x6B, 0x38, 0xB1, 0xC1, 0xA7, 0x84, 0x70, 0xBA, 0xB9, 0x47, 0x51, + 0x89, 0x34, 0x45, 0x3C, 0xE8, 0x67, 0x36, 0xA9, 0x19, 0xF1, 0xF5, 0xA6, 0xD5, 0x10, 0xA8, + 0x6F, 0x54, 0x54, 0xFC, 0x39, 0x80, 0xCB, 0x5C, 0x76, 0x5B, 0xD2, 0xBD, 0x5F, 0x7B, 0x36, + 0xB1, 0x41, 0x0D, 0x66, 0x35, 0xC8, 0xCE, 0xB4, 0x7C, 0x4D, 0xDA, 0x0D, 0x76, 0xA2, 0x8E, + 0xAC, 0x93, 0x9C, 0x71, 0xC3, 0x02, 0x48, 0x04, 0x86, 0x6C, 0x71, 0x62, 0x66, 0x58, 0x44, + 0x21, 0x63, 0xC2, 0xC2, 0x21, 0x17, 0xE5, 0x0A, 0xCE, 0xFC, 0xE6, 0x37, 0x8A, 0x98, 0x56, + 0x52, 0x30, 0x2A, 0x4E, 0xF0, 0xC2, 0xCE, 0x0C, 0xC7, 0x16, 0xB7, 0x79, 0x6E, 0x2B, 0x6B, + 0x2E, 0x37, 0x77, 0xDF, 0xA1, 0xAC, 0x3D, 0xA2, 0x59, 0xA3, 0x1B, 0x5A, 0x9B, 0x53, 0x0F, + 0x8C, 0xB6, 0x38, 0xA8, 0x1A, 0x62, 0xAC, 0x30, 0x18, 0x49, 0xAB, 0xAF, 0x95, 0xA7, 0x30, + 0x1B, 0xDA, 0x30, 0x06, 0x89, 0x09, 0xBF, 0xDB, 0x7E, 0x67, 0xDB, 0xCC, 0xBB, 0x38, 0xA5, + 0x55, 0x1A, 0x25, 0xB1, 0xA3, 0xA0, 0xF6, 0x85, 0x74, 0x8A, 0xD5, 0x75, 0x3D, 0x88, 0x80, + 0xF0, 0x01, 0x6C, 0x62, 0x74, 0x86, 0x16, 0x63, 0x84, 0xC5, 0x57, 0x1F, 0xE2, 0x36, 0x59, + 0x00, 0x36, 0x4D, 0x03, 0x83, 0x11, 0xE2, 0xD8, 0x75, 0xDB, 0x36, 0x66, 0x86, 0x93, 0x2B, + 0x5E, 0xC6, 0x02, 0x43, 0x0A, 0x36, 0x9E, 0x87, 0xA6, 0xEF, 0x5C, 0x33, 0x87, 0x86, 0x65, + 0x78, 0x25, 0xBD, 0x4C, 0x05, 0x7A, 0xCE, 0xB9, 0x23, 0xEB, 0x09, 0x35, 0xE6, 0x90, 0x5E, + 0x63, 0xB4, 0xCE, 0xD7, 0xF8, 0x08, 0x57, 0xA7, 0x73, 0xDD, 0x64, 0xB1, 0x50, 0xD2, 0x66, + 0x12, 0xEA, 0x9A, 0xC1, 0x20, 0x52, 0xDB, 0x20, 0x17, 0xBF, 0x18, 0x43, 0xCC, 0xB4, 0xB3, + 0x28, 0x1B, 0x69, 0x0D, 0xC7, 0x28, 0xAD, 0xFA, 0x85, 0xC0, 0x02, 0x81, 0xB8, 0xE3, 0xC0, + 0x92, 0x87, 0x33, 0x5F, 0x85, 0x6B, 0x4F, 0xC2, 0x89, 0x2F, 0x69, 0xA2, 0xF5, 0x79, 0x21, + 0xAD, 0xA0, 0x19, 0x14, 0xC4, 0x09, 0x88, 0x66, 0x2D, 0x57, 0x76, 0x96, 0x62, 0xA7, 0x86, + 0x35, 0x1B, 0x9B, 0x66, 0x49, 0x3D, 0xAB, 0x79, 0x59, 0x4D, 0x98, 0x6D, 0xE2, 0x10, 0x0D, + 0x65, 0xBA, 0x0F, 0xF4, 0xEA, 0x58, 0xB8, 0x15, 0x38, 0xD2, 0x4A, 0x44, 0x35, 0xA2, 0x58, + 0xFA, 0xC2, 0x54, 0x04, 0xAA, 0x7F, 0x41, 0xF6, 0x58, 0xB1, 0x38, 0x50, 0x65, 0xE1, 0x58, + 0xDC, 0xB6, 0x01, 0x15, 0x73, 0x27, 0x20, 0xF4, 0x04, 0x59, 0xAA, 0xAC, 0x15, 0xE4, 0x06, + 0x95, 0x3A, 0x90, 0xAC, 0x52, 0x99, 0x7D, 0x1C, 0xCD, 0x07, 0x00, 0x60, 0xEF, 0xC6, 0x5D, + 0xB9, 0xE6, 0x53, 0x35, 0x44, 0x67, 0xFA, 0xD5, 0x6E, 0xC7, 0x13, 0xC8, 0x6E, 0x75, 0x40, + 0xC4, 0x23, 0xAC, 0xF2, 0x66, 0x9F, 0x52, 0xFA, 0x6F, 0x4A, 0xC6, 0x88, 0x8D, 0x87, 0x1E, + 0xF3, 0xE8, 0x47, 0xC0, 0x29, 0xA8, 0xAA, 0xFB, 0xB9, 0x2E, 0x17, 0xB2, 0x4A, 0xA0, 0x79, + 0xB1, 0xF4, 0x19, 0xBA, 0x61, 0x75, 0xB4, 0x42, 0xAF, 0xB1, 0x19, 0x09, 0xD4, 0xA5, 0x6B, + 0x70, 0xA0, 0x33, 0x5B, 0x28, 0x73, 0x92, 0x18, 0xAA, 0x7C, 0x93, 0x48, 0xE2, 0xC3, 0xC2, + 0xF3, 0xEB, 0x3D, 0x15, 0xA4, 0x1E, 0x64, 0x17, 0xC0, 0xDD, 0x94, 0xBF, 0xEB, 0x21, 0x41, + 0x9B, 0x31, 0x1A, 0x7B, 0xB1, 0x3A, 0x18, 0x0B, 0xBE, 0x83, 0x32, 0x18, 0xA9, 0xA6, 0xB1, + 0x74, 0x47, 0xCC, 0x85, 0xF2, 0x25, 0x85, 0x95, 0x87, 0xA7, 0x30, 0x77, 0x04, 0x9A, 0xCB, + 0xCF, 0xD4, 0x4D, 0x0F, 0x02, 0x54, 0x38, 0xE1, 0x5D, 0x15, 0x38, 0x27, 0x0D, 0x58, 0x6E, + 0x1B, 0xF8, 0x31, 0x92, 0xA9, 0x45, 0x9C, 0xF6, 0x3C, 0x0E, 0x97, 0x2F, 0x85, 0x29, 0x76, + 0x79, 0x83, 0x1E, 0xCF, 0x12, 0x15, 0x09, 0x85, 0x1C, 0xB8, 0x34, 0x0F, 0x6F, 0x10, 0x7B, + 0x0F, 0xA1, 0xA0, 0xEF, 0xD1, 0xB3, 0x6A, 0x81, 0x89, 0xBC, 0x08, 0x5C, 0x4F, 0x5C, 0xB7, + 0x84, 0xE5, 0x53, 0xF4, 0x1B, 0x91, 0x8F, 0x80, 0x39, 0x7C, 0xE1, 0x95, 0x6F, 0x78, 0x5B, + 0xEE, 0x37, 0x7C, 0xA9, 0xAA, 0x8B, 0xE6, 0x99, 0x8A, 0xDA, 0x30, 0xC2, 0x6B, 0x7C, 0x3D, + 0x8C, 0x6B, 0x55, 0x25, 0x4C, 0xC9, 0x62, 0x03, 0xB2, 0x0C, 0x42, 0xAE, 0xE0, 0xAC, 0x4E, + 0x1E, 0xBB, 0x40, 0x8E, 0x49, 0xA9, 0xE3, 0xF8, 0x79, 0xD0, 0xAB, 0x07, 0x85, 0xEB, 0x70, + 0x25, 0x42, 0x5D, 0x13, 0x05, 0xA2, 0x29, 0x9C, 0x01, 0x5E, 0x12, 0x0D, 0x16, 0x3B, 0x0E, + 0x19, 0x49, 0x4C, 0xE5, 0x72, 0x53, 0xD0, 0x24, 0x6D, 0x18, 0x27, 0x45, 0xCB, 0x81, 0x97, + 0xAB, 0x74, 0x38, 0xB3, 0xC1, 0xBB, 0x79, 0x72, 0xBE, 0xC5, 0xA3, 0x06, 0xEB, 0xA3, 0x56, + 0x78, 0x55, 0xC0, 0x14, 0x69, 0x9F, 0xEF, 0x65, 0xAE, 0x54, 0xC7, 0x70, 0xA0, 0xD8, 0x5C, + 0x18, 0x40, 0x0C, 0xF6, 0x42, 0xAE, 0xDC, 0x66, 0x07, 0x77, 0xBA, 0x4B, 0x13, 0x85, 0x02, + 0xBD, 0x5A, 0x78, 0x12, 0xF6, 0x21, 0xF8, 0x4A, 0x48, 0x29, 0x6B, 0x98, 0xDD, 0x43, 0x22, + 0xB6, 0xF1, 0x58, 0x28, 0xB8, 0xA8, 0xF0, 0xE0, 0x0A, 0x8B, 0xA4, 0x4A, 0x53, 0xC3, 0xA8, + 0xB1, 0x43, 0x57, 0x1B, 0x07, 0x40, 0xAB, 0xD5, 0x67, 0xDA, 0xF1, 0xCD, 0xE9, 0xC7, 0x9C, + 0x20, 0x4B, 0x6D, 0x5E, 0x25, 0x9D, 0x17, 0x66, 0xA3, 0x1B, 0xBB, 0xCB, 0x4E, 0x6A, 0x05, + 0xCF, 0x45, 0x02, 0x17, 0x6B, 0x30, 0x1C, 0x1C, 0x2F, 0x41, 0x24, 0x77, 0x50, 0x15, 0x7B, + 0xCE, 0xC8, 0x5E, 0x80, 0x9B, 0x30, 0xA4, 0xD6, 0x0D, 0x77, 0x47, 0xCD, 0xD0, 0xF5, 0xB9, + 0x9A, 0xA8, 0xC8, 0x26, 0x98, 0x75, 0x17, 0x79, 0x3A, 0xAA, 0x80, 0x80, 0xA0, 0xB1, 0x24, + 0xA8, 0x55, 0x8D, 0xF7, 0x2B, 0xBE, 0x37, 0xB7, 0x5F, 0x4E, 0xDB, 0xB6, 0xBE, 0x82, 0x16, + 0xD6, 0xC6, 0x33, 0xFB, 0x2B, 0x22, 0x80, 0xE2, 0x51, 0x13, 0xD8, 0x69, 0x5E, 0x43, 0x48, + 0x1C, 0x3E, 0xEB, 0x39, 0x7E, 0xB1, 0x92, 0x50, 0x52, 0x29, 0xB6, 0x7A, 0x20, 0x1E, 0xA8, + 0x93, 0xC3, 0xE2, 0xCB, 0x32, 0xDA, 0x8B, 0xC3, 0x42, 0xFA, 0x4D, 0xEA, 0x05, 0x78, + ]; let pk = MLKEM768PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM768::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -253,7 +599,7 @@ fn bench_mlkem768_lowmemory_encaps() { } fn bench_mlkem1024_encaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM1024, MLKEM1024PublicKey}; + use bouncycastle::mlkem::{MLKEM1024, MLKEM1024PublicKey, MLKEMTrait}; eprintln!("MLKEM1024/Encaps"); @@ -267,7 +613,113 @@ fn bench_mlkem1024_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM1024_PK_LEN] = [0x4b,0x94,0xc2,0x94,0x50,0x11,0x11,0x91,0x82,0x3b,0x35,0x14,0xc9,0xac,0x1e,0xa3,0xd9,0x82,0x5c,0xcb,0x86,0x39,0x3a,0x2d,0xfb,0x04,0x65,0x4f,0xa2,0x19,0x2d,0x37,0xbf,0xad,0x1c,0x49,0x7c,0x65,0x02,0xee,0xe5,0xca,0x80,0xa7,0x3b,0xfc,0xe0,0xba,0xf5,0xa5,0x4a,0x88,0x58,0x5a,0x40,0x13,0x97,0xa3,0xd2,0x32,0xf4,0x26,0xa7,0xaf,0xb0,0x82,0xbc,0x21,0xa4,0x43,0x17,0x09,0x0e,0xaa,0xc7,0x59,0x2c,0x2e,0xa8,0x8a,0x65,0x3c,0x44,0x91,0xea,0x19,0x39,0x31,0x33,0x5f,0x52,0xe9,0x89,0xa3,0xc4,0xcc,0x56,0xd9,0xc5,0x53,0x73,0x2d,0x57,0xc4,0x70,0xfb,0x41,0xab,0x75,0x9b,0x65,0xd2,0xd0,0x44,0x45,0x38,0x2f,0xcd,0x9c,0x4e,0x34,0x4a,0x11,0x28,0xfa,0x9e,0x11,0xe0,0x43,0x58,0xe1,0x92,0xed,0x01,0x4b,0x23,0x23,0x2a,0x7e,0xe2,0xb2,0x2e,0x23,0x71,0x7f,0x44,0x11,0x1e,0xe3,0x35,0x75,0x39,0x9c,0x37,0x64,0x6d,0xa9,0x81,0x3e,0xc9,0xb2,0x12,0xaf,0xe9,0x4e,0x5d,0xc5,0xc2,0x33,0x0a,0x72,0x94,0xcc,0x1f,0x42,0x34,0xa6,0xd3,0xfb,0xb4,0xf1,0x68,0x5a,0xb8,0x89,0x2c,0x04,0xac,0xb1,0x7c,0xd1,0xc1,0x70,0xd7,0xb0,0x61,0x1b,0x6a,0x71,0x76,0xc7,0x94,0xcc,0x8c,0x67,0xf5,0x5f,0xc9,0x23,0xc2,0xad,0x20,0x31,0x00,0xf3,0x65,0x99,0x18,0x82,0xc3,0x02,0x43,0xd7,0x78,0x13,0x84,0x3b,0x5e,0xc7,0xc9,0x64,0x03,0x22,0x63,0x70,0x60,0x92,0xec,0xf0,0x0c,0x75,0x16,0xbe,0x64,0xe4,0x59,0x8c,0xa4,0x22,0x6c,0x06,0x9b,0xb5,0xe6,0x7e,0x41,0x75,0xcf,0x22,0x86,0xc8,0xdd,0x5c,0x48,0x8a,0x6c,0x58,0x61,0xf3,0x1b,0xaa,0x0b,0xd0,0x26,0x94,0x70,0xe8,0xb5,0x51,0xdd,0x3b,0xcd,0x38,0xc8,0x6c,0x12,0xf9,0xcd,0xb1,0x76,0xc7,0x7d,0xc8,0xb6,0xc0,0x2a,0x70,0x1f,0x47,0x89,0x02,0xc8,0x55,0x3f,0x69,0x4c,0x0d,0x82,0x72,0x7b,0x4c,0x4a,0x5c,0x2c,0x10,0x41,0x21,0x2a,0xa1,0x27,0x48,0x08,0xb8,0x21,0x11,0xb3,0x77,0xec,0x75,0x21,0x4e,0x9b,0x19,0x78,0xf7,0x60,0x04,0xd4,0x13,0x9d,0x98,0x61,0x3f,0x4b,0x8e,0x98,0xd2,0x0a,0xf7,0xb5,0x34,0x07,0x3a,0x50,0x9a,0x95,0x9b,0x7a,0x75,0x64,0xf9,0xb4,0x0c,0xa2,0x18,0xbf,0x61,0x82,0x93,0x20,0xa8,0x50,0x20,0x17,0x95,0x4d,0x32,0x8d,0x7a,0xc6,0xc7,0x69,0xec,0x29,0x70,0x07,0x56,0xe7,0xb0,0x68,0x5b,0x34,0x0d,0x5e,0x11,0x80,0x59,0x50,0x4a,0x49,0xa9,0xa5,0x0a,0x10,0x19,0x8e,0xb1,0x0a,0x57,0x84,0x67,0x8e,0xb4,0x27,0xd7,0xb4,0xba,0xbb,0x95,0x52,0x93,0x3b,0x06,0x28,0x97,0x97,0x3e,0x13,0x18,0xea,0xf0,0xa0,0xea,0xc3,0x75,0x84,0xa6,0x54,0x01,0xb1,0x70,0x3e,0x04,0x2a,0xcc,0xd8,0x37,0x53,0x14,0x83,0xf2,0x41,0xca,0xdc,0xd1,0xc1,0xd3,0x78,0x11,0x9e,0x69,0x44,0x29,0xdb,0x19,0x9a,0xc8,0x91,0xe4,0xc5,0x34,0x37,0x57,0x08,0x5b,0xb3,0xae,0x78,0x36,0x67,0x35,0x0c,0x44,0x58,0xd9,0x76,0x72,0xe8,0x61,0xe8,0x0b,0x1d,0x26,0x79,0x51,0x0e,0xa3,0xa6,0xf2,0x36,0x0c,0x77,0xa4,0x69,0x42,0xc7,0xa0,0x6a,0x55,0x4d,0x22,0x80,0x80,0xc8,0x4b,0x47,0xae,0xf1,0x4d,0xb1,0x76,0x20,0xcb,0x16,0xc0,0x6a,0xb3,0x0a,0x1b,0xe4,0xcd,0xa7,0x08,0x2b,0xe9,0xf8,0x7e,0x9c,0x21,0x1c,0x46,0x91,0x63,0x49,0xa5,0xba,0x8e,0xaa,0x52,0x01,0xc7,0x29,0x4a,0x3c,0x08,0x85,0xb5,0x3b,0x65,0x74,0x52,0x10,0x88,0x25,0xec,0x64,0x6c,0x90,0xa0,0x46,0x12,0x32,0x4e,0xe7,0xd0,0x31,0xaf,0xe5,0x34,0x31,0x32,0xcb,0xef,0x67,0xb6,0xef,0xb1,0xa5,0xec,0x28,0x09,0xb7,0x73,0x53,0x8c,0xe7,0x7b,0x3d,0x8b,0x04,0xeb,0x0b,0x3c,0x22,0x56,0x01,0x1e,0x4c,0x71,0x6c,0x19,0xa8,0xba,0x07,0x52,0xbf,0x71,0x49,0x21,0x17,0x64,0x9f,0x06,0x15,0xc3,0x29,0x0f,0xc2,0x9a,0x46,0xfd,0xe4,0xbd,0x52,0xdb,0x92,0x86,0xd6,0x03,0x38,0x82,0x44,0x25,0x9c,0x15,0xa7,0xac,0x2b,0x64,0x0a,0x60,0xcc,0x03,0x37,0x6a,0x58,0x41,0xa3,0xfb,0x8a,0x47,0x35,0x68,0xfa,0x9b,0x1a,0x26,0x72,0x15,0xf3,0x4c,0x01,0x69,0x7b,0x0f,0x0e,0x62,0x71,0x75,0xd7,0x21,0x05,0xb7,0x70,0x7c,0x29,0xb9,0xe6,0x14,0xbd,0xc3,0x3a,0x6f,0x6c,0x81,0x8a,0x95,0x37,0x0b,0x42,0x78,0x82,0xd7,0xb4,0x76,0x79,0x6a,0x9e,0xc6,0xeb,0x99,0x32,0x74,0xcd,0x9b,0x23,0x91,0xa8,0x2b,0xa4,0x5e,0x33,0x93,0xd2,0xe9,0xae,0x97,0x21,0xca,0x9d,0x6c,0x1b,0x98,0x8b,0x58,0x27,0x71,0x3f,0x90,0xa6,0x58,0x5d,0xe9,0x43,0x35,0x28,0xc0,0x2b,0x03,0xce,0x10,0xbb,0x5f,0x72,0x01,0x38,0xd0,0xfb,0xb4,0xc3,0x0c,0x12,0x66,0xb9,0x18,0xe5,0x29,0x25,0xdf,0xe1,0x7b,0x37,0xf9,0x5d,0x22,0xbc,0xa5,0x4f,0x47,0x59,0x19,0xac,0x85,0x90,0x98,0xc0,0xf0,0xd0,0x8a,0xc5,0x87,0x5e,0xf2,0x9b,0x56,0xfd,0x14,0x1e,0x6e,0xf1,0x5f,0x70,0x0a,0x0b,0x66,0xf3,0x95,0x95,0xc5,0x88,0x17,0x73,0x73,0xc4,0x66,0x9b,0x21,0xbc,0x07,0x1e,0x4c,0x3a,0xa5,0xf0,0xb4,0xa3,0x1b,0x62,0x58,0xf3,0x5d,0xa2,0x4a,0xc3,0xcd,0x29,0xc7,0xf2,0x09,0x24,0x10,0xc5,0x07,0x83,0x55,0xb1,0x38,0xfb,0x53,0xa6,0xb9,0xae,0x6e,0x0b,0x9c,0x08,0x24,0x3e,0x7b,0xaa,0x45,0xc4,0x73,0x76,0xeb,0x8c,0x7f,0x13,0xd4,0xcf,0x51,0xaa,0x73,0x6f,0xa3,0x15,0x40,0xc9,0x24,0x1f,0x37,0x0d,0xa5,0x44,0xbf,0x9f,0x9c,0x28,0xd9,0xa5,0x7e,0x2f,0x2a,0x7c,0xa9,0x5a,0x4e,0x4b,0x46,0x6e,0x64,0x1a,0xb3,0xbc,0xc7,0x6a,0xdf,0x11,0x39,0xd5,0x67,0xa6,0xf1,0x2b,0x52,0xf3,0xa6,0x5e,0x7e,0xc0,0xaa,0xe2,0x6b,0xca,0xa8,0xc5,0x58,0x33,0xb0,0x4e,0x59,0x99,0x8e,0xbc,0x9a,0x19,0x30,0xfb,0xb6,0xd2,0x23,0x3c,0x53,0xd2,0xc1,0xf8,0xb9,0x51,0x8e,0x3c,0x2d,0xe7,0x3a,0x19,0xde,0xe6,0xb3,0x80,0xa5,0xb3,0x29,0x71,0xcf,0x64,0xe1,0x29,0xfd,0x6c,0x1f,0xa6,0xe7,0x5d,0x4a,0x23,0x45,0x01,0xe9,0x66,0xdd,0x3a,0x54,0x0a,0xf5,0xc8,0xf4,0xf3,0x4a,0x6b,0x4a,0x25,0x3e,0xe2,0x84,0x92,0x56,0x6d,0x5e,0x67,0xc6,0xf5,0x58,0x55,0xfc,0xb0,0x50,0x6f,0xb0,0x6c,0x15,0x67,0x44,0xd9,0xa0,0x3a,0x31,0xa2,0x6f,0xa9,0x4c,0xad,0x14,0xf1,0x57,0xb7,0xf3,0x03,0xd0,0x7a,0x69,0xc7,0x73,0x76,0x8f,0xcb,0x4d,0x07,0x9c,0x09,0x05,0x97,0x03,0xa0,0xc3,0xa9,0x4d,0xe4,0xb9,0x9e,0xa3,0xa2,0xf1,0x65,0x83,0xd0,0xf9,0x17,0x0a,0x39,0x50,0xdb,0x07,0xb4,0xf0,0xbc,0x30,0x80,0x29,0x27,0xf9,0xf7,0x96,0x1b,0x62,0x59,0x89,0x26,0x36,0xa9,0x50,0x2a,0x27,0x05,0x30,0x36,0x37,0x79,0x9d,0xd3,0x44,0xda,0x45,0x1c,0x1c,0xf7,0xbf,0x67,0x84,0x0c,0xeb,0x30,0x79,0xab,0x8c,0x6b,0x8c,0x19,0x27,0xf6,0x40,0x53,0xc6,0x12,0x45,0x0c,0x45,0xc9,0xe6,0x03,0xbc,0x16,0x66,0x6e,0x59,0x6b,0x34,0x71,0xe1,0x03,0xb6,0xf1,0x54,0x47,0x42,0x4d,0x17,0x02,0x20,0x48,0x11,0x1f,0xfb,0xd3,0x7e,0x1c,0x67,0x0f,0x64,0xf1,0x4b,0x8a,0x7b,0x32,0xb9,0x4c,0x1a,0x49,0xb4,0x5d,0xd2,0xfc,0x38,0xcd,0x52,0x89,0xd9,0x10,0xad,0x63,0x60,0x2c,0xf5,0xe1,0x30,0x42,0xc6,0x4a,0xc6,0x79,0x7b,0x89,0xfb,0x55,0x1a,0xd0,0x8e,0x05,0xa9,0x2d,0x20,0x0c,0xcc,0xb7,0xe7,0x12,0xef,0x23,0xc9,0x31,0x2c,0xb3,0x50,0xf0,0x29,0xab,0x53,0x7e,0x28,0x73,0x47,0xfd,0x30,0x75,0xac,0x10,0x90,0x6a,0x78,0x3f,0x1c,0x6c,0x07,0xcc,0xb8,0x8f,0x41,0x22,0x8c,0x4b,0xe1,0xc6,0x40,0xf7,0x90,0xb5,0xc3,0xa5,0xd5,0xd3,0xca,0x79,0x24,0x95,0xd7,0x4b,0xc4,0x61,0x56,0x26,0x58,0xc0,0x7a,0xc6,0x00,0x27,0x6b,0x92,0x4a,0xb5,0xbc,0x9b,0xe1,0xf0,0x49,0x4c,0xb7,0x6f,0x82,0xf4,0x60,0xa7,0x48,0x09,0x72,0x66,0x33,0x81,0xe1,0x69,0x99,0x60,0x61,0xd7,0x99,0x85,0x9e,0xc5,0x4d,0x4f,0x5c,0xa5,0xc4,0x11,0xc0,0x1d,0xb1,0x59,0x7b,0x16,0x59,0x77,0x66,0x9d,0xe1,0x3a,0x92,0x8a,0x34,0xaf,0xba,0xc2,0x58,0xfe,0xa8,0xc4,0x76,0x42,0x39,0xc9,0x42,0x1d,0xc3,0x11,0x9b,0xf5,0xb4,0x76,0x99,0x20,0x69,0x78,0x32,0x7b,0x1c,0x53,0x45,0xef,0x74,0x6a,0x79,0x83,0x84,0x1f,0x05,0x6e,0x25,0x34,0x10,0x0a,0xb2,0x4d,0x4e,0x9a,0xbb,0xd0,0xb1,0x7c,0x6a,0x95,0xbd,0x4c,0x3c,0x0e,0x40,0xf6,0x9e,0x16,0x12,0xac,0xee,0xb2,0x8b,0x99,0x08,0x6c,0x95,0x11,0x6e,0x72,0x04,0x27,0x38,0x93,0x39,0x0b,0xf4,0x6b,0x89,0x9b,0x36,0x28,0x6b,0x0e,0xbf,0x19,0x47,0xbb,0x98,0x84,0xf7,0x32,0xca,0x27,0xda,0x82,0xb1,0x9b,0x5d,0xc0,0xcc,0x7f,0x88,0x85,0x71,0x49,0x10,0x88,0x8b,0x23,0x10,0xc4,0xf9,0x31,0x9d,0x41,0x0b,0x34,0xe6,0x43,0x3b,0x90,0x03,0xe2,0x17,0x6b,0xb9,0x95,0x25,0x74,0x56,0x10,0x6e,0x89,0x52,0x16,0x3b,0x8b,0xa5,0x92,0x53,0x0c,0xc5,0xaa,0x0a,0xeb,0x43,0xad,0x39,0x8f,0xe9,0xe9,0x7b,0xaa,0x52,0x3d,0x7a,0x44,0x31,0x67,0x7c,0x3d,0x3a,0xf0,0x71,0x9e,0x47,0x5d,0xb8,0x5c,0xa9,0x5a,0xf5,0x08,0x9b,0xea,0xbe,0xb0,0x5b,0x2f,0xaa,0xb4,0x89,0x6b,0xa6,0x0f,0x81,0xc8,0x84,0x72,0xa5,0x7b,0x46,0xa8,0x28,0x82,0x6a,0x0c,0xdf,0xb4,0x46,0xf8,0x18,0x91,0x82,0xd2,0xbf,0x5e,0xac,0x4e,0xc1,0xcc,0x5d,0xea,0xf5,0x99,0xc8,0xa1,0x3e,0x48,0x23,0x54,0x06,0xd1,0x7f,0xfd,0xdc,0x83,0x44,0xb6,0xc6,0x69,0x84,0xa8,0x68,0xaa,0x92,0xfa,0x02,0x22,0x7a,0x08,0x69,0x50,0xeb,0x0c,0x87,0x01,0xed,0x58,0xdc,0x62,0x87,0x76,0xb9,0x83,0x88,0x2e,0x11,0x75]; + let pk_bytes: [u8; MLKEM1024_PK_LEN] = [ + 0x4B, 0x94, 0xC2, 0x94, 0x50, 0x11, 0x11, 0x91, 0x82, 0x3B, 0x35, 0x14, 0xC9, 0xAC, 0x1E, + 0xA3, 0xD9, 0x82, 0x5C, 0xCB, 0x86, 0x39, 0x3A, 0x2D, 0xFB, 0x04, 0x65, 0x4F, 0xA2, 0x19, + 0x2D, 0x37, 0xBF, 0xAD, 0x1C, 0x49, 0x7C, 0x65, 0x02, 0xEE, 0xE5, 0xCA, 0x80, 0xA7, 0x3B, + 0xFC, 0xE0, 0xBA, 0xF5, 0xA5, 0x4A, 0x88, 0x58, 0x5A, 0x40, 0x13, 0x97, 0xA3, 0xD2, 0x32, + 0xF4, 0x26, 0xA7, 0xAF, 0xB0, 0x82, 0xBC, 0x21, 0xA4, 0x43, 0x17, 0x09, 0x0E, 0xAA, 0xC7, + 0x59, 0x2C, 0x2E, 0xA8, 0x8A, 0x65, 0x3C, 0x44, 0x91, 0xEA, 0x19, 0x39, 0x31, 0x33, 0x5F, + 0x52, 0xE9, 0x89, 0xA3, 0xC4, 0xCC, 0x56, 0xD9, 0xC5, 0x53, 0x73, 0x2D, 0x57, 0xC4, 0x70, + 0xFB, 0x41, 0xAB, 0x75, 0x9B, 0x65, 0xD2, 0xD0, 0x44, 0x45, 0x38, 0x2F, 0xCD, 0x9C, 0x4E, + 0x34, 0x4A, 0x11, 0x28, 0xFA, 0x9E, 0x11, 0xE0, 0x43, 0x58, 0xE1, 0x92, 0xED, 0x01, 0x4B, + 0x23, 0x23, 0x2A, 0x7E, 0xE2, 0xB2, 0x2E, 0x23, 0x71, 0x7F, 0x44, 0x11, 0x1E, 0xE3, 0x35, + 0x75, 0x39, 0x9C, 0x37, 0x64, 0x6D, 0xA9, 0x81, 0x3E, 0xC9, 0xB2, 0x12, 0xAF, 0xE9, 0x4E, + 0x5D, 0xC5, 0xC2, 0x33, 0x0A, 0x72, 0x94, 0xCC, 0x1F, 0x42, 0x34, 0xA6, 0xD3, 0xFB, 0xB4, + 0xF1, 0x68, 0x5A, 0xB8, 0x89, 0x2C, 0x04, 0xAC, 0xB1, 0x7C, 0xD1, 0xC1, 0x70, 0xD7, 0xB0, + 0x61, 0x1B, 0x6A, 0x71, 0x76, 0xC7, 0x94, 0xCC, 0x8C, 0x67, 0xF5, 0x5F, 0xC9, 0x23, 0xC2, + 0xAD, 0x20, 0x31, 0x00, 0xF3, 0x65, 0x99, 0x18, 0x82, 0xC3, 0x02, 0x43, 0xD7, 0x78, 0x13, + 0x84, 0x3B, 0x5E, 0xC7, 0xC9, 0x64, 0x03, 0x22, 0x63, 0x70, 0x60, 0x92, 0xEC, 0xF0, 0x0C, + 0x75, 0x16, 0xBE, 0x64, 0xE4, 0x59, 0x8C, 0xA4, 0x22, 0x6C, 0x06, 0x9B, 0xB5, 0xE6, 0x7E, + 0x41, 0x75, 0xCF, 0x22, 0x86, 0xC8, 0xDD, 0x5C, 0x48, 0x8A, 0x6C, 0x58, 0x61, 0xF3, 0x1B, + 0xAA, 0x0B, 0xD0, 0x26, 0x94, 0x70, 0xE8, 0xB5, 0x51, 0xDD, 0x3B, 0xCD, 0x38, 0xC8, 0x6C, + 0x12, 0xF9, 0xCD, 0xB1, 0x76, 0xC7, 0x7D, 0xC8, 0xB6, 0xC0, 0x2A, 0x70, 0x1F, 0x47, 0x89, + 0x02, 0xC8, 0x55, 0x3F, 0x69, 0x4C, 0x0D, 0x82, 0x72, 0x7B, 0x4C, 0x4A, 0x5C, 0x2C, 0x10, + 0x41, 0x21, 0x2A, 0xA1, 0x27, 0x48, 0x08, 0xB8, 0x21, 0x11, 0xB3, 0x77, 0xEC, 0x75, 0x21, + 0x4E, 0x9B, 0x19, 0x78, 0xF7, 0x60, 0x04, 0xD4, 0x13, 0x9D, 0x98, 0x61, 0x3F, 0x4B, 0x8E, + 0x98, 0xD2, 0x0A, 0xF7, 0xB5, 0x34, 0x07, 0x3A, 0x50, 0x9A, 0x95, 0x9B, 0x7A, 0x75, 0x64, + 0xF9, 0xB4, 0x0C, 0xA2, 0x18, 0xBF, 0x61, 0x82, 0x93, 0x20, 0xA8, 0x50, 0x20, 0x17, 0x95, + 0x4D, 0x32, 0x8D, 0x7A, 0xC6, 0xC7, 0x69, 0xEC, 0x29, 0x70, 0x07, 0x56, 0xE7, 0xB0, 0x68, + 0x5B, 0x34, 0x0D, 0x5E, 0x11, 0x80, 0x59, 0x50, 0x4A, 0x49, 0xA9, 0xA5, 0x0A, 0x10, 0x19, + 0x8E, 0xB1, 0x0A, 0x57, 0x84, 0x67, 0x8E, 0xB4, 0x27, 0xD7, 0xB4, 0xBA, 0xBB, 0x95, 0x52, + 0x93, 0x3B, 0x06, 0x28, 0x97, 0x97, 0x3E, 0x13, 0x18, 0xEA, 0xF0, 0xA0, 0xEA, 0xC3, 0x75, + 0x84, 0xA6, 0x54, 0x01, 0xB1, 0x70, 0x3E, 0x04, 0x2A, 0xCC, 0xD8, 0x37, 0x53, 0x14, 0x83, + 0xF2, 0x41, 0xCA, 0xDC, 0xD1, 0xC1, 0xD3, 0x78, 0x11, 0x9E, 0x69, 0x44, 0x29, 0xDB, 0x19, + 0x9A, 0xC8, 0x91, 0xE4, 0xC5, 0x34, 0x37, 0x57, 0x08, 0x5B, 0xB3, 0xAE, 0x78, 0x36, 0x67, + 0x35, 0x0C, 0x44, 0x58, 0xD9, 0x76, 0x72, 0xE8, 0x61, 0xE8, 0x0B, 0x1D, 0x26, 0x79, 0x51, + 0x0E, 0xA3, 0xA6, 0xF2, 0x36, 0x0C, 0x77, 0xA4, 0x69, 0x42, 0xC7, 0xA0, 0x6A, 0x55, 0x4D, + 0x22, 0x80, 0x80, 0xC8, 0x4B, 0x47, 0xAE, 0xF1, 0x4D, 0xB1, 0x76, 0x20, 0xCB, 0x16, 0xC0, + 0x6A, 0xB3, 0x0A, 0x1B, 0xE4, 0xCD, 0xA7, 0x08, 0x2B, 0xE9, 0xF8, 0x7E, 0x9C, 0x21, 0x1C, + 0x46, 0x91, 0x63, 0x49, 0xA5, 0xBA, 0x8E, 0xAA, 0x52, 0x01, 0xC7, 0x29, 0x4A, 0x3C, 0x08, + 0x85, 0xB5, 0x3B, 0x65, 0x74, 0x52, 0x10, 0x88, 0x25, 0xEC, 0x64, 0x6C, 0x90, 0xA0, 0x46, + 0x12, 0x32, 0x4E, 0xE7, 0xD0, 0x31, 0xAF, 0xE5, 0x34, 0x31, 0x32, 0xCB, 0xEF, 0x67, 0xB6, + 0xEF, 0xB1, 0xA5, 0xEC, 0x28, 0x09, 0xB7, 0x73, 0x53, 0x8C, 0xE7, 0x7B, 0x3D, 0x8B, 0x04, + 0xEB, 0x0B, 0x3C, 0x22, 0x56, 0x01, 0x1E, 0x4C, 0x71, 0x6C, 0x19, 0xA8, 0xBA, 0x07, 0x52, + 0xBF, 0x71, 0x49, 0x21, 0x17, 0x64, 0x9F, 0x06, 0x15, 0xC3, 0x29, 0x0F, 0xC2, 0x9A, 0x46, + 0xFD, 0xE4, 0xBD, 0x52, 0xDB, 0x92, 0x86, 0xD6, 0x03, 0x38, 0x82, 0x44, 0x25, 0x9C, 0x15, + 0xA7, 0xAC, 0x2B, 0x64, 0x0A, 0x60, 0xCC, 0x03, 0x37, 0x6A, 0x58, 0x41, 0xA3, 0xFB, 0x8A, + 0x47, 0x35, 0x68, 0xFA, 0x9B, 0x1A, 0x26, 0x72, 0x15, 0xF3, 0x4C, 0x01, 0x69, 0x7B, 0x0F, + 0x0E, 0x62, 0x71, 0x75, 0xD7, 0x21, 0x05, 0xB7, 0x70, 0x7C, 0x29, 0xB9, 0xE6, 0x14, 0xBD, + 0xC3, 0x3A, 0x6F, 0x6C, 0x81, 0x8A, 0x95, 0x37, 0x0B, 0x42, 0x78, 0x82, 0xD7, 0xB4, 0x76, + 0x79, 0x6A, 0x9E, 0xC6, 0xEB, 0x99, 0x32, 0x74, 0xCD, 0x9B, 0x23, 0x91, 0xA8, 0x2B, 0xA4, + 0x5E, 0x33, 0x93, 0xD2, 0xE9, 0xAE, 0x97, 0x21, 0xCA, 0x9D, 0x6C, 0x1B, 0x98, 0x8B, 0x58, + 0x27, 0x71, 0x3F, 0x90, 0xA6, 0x58, 0x5D, 0xE9, 0x43, 0x35, 0x28, 0xC0, 0x2B, 0x03, 0xCE, + 0x10, 0xBB, 0x5F, 0x72, 0x01, 0x38, 0xD0, 0xFB, 0xB4, 0xC3, 0x0C, 0x12, 0x66, 0xB9, 0x18, + 0xE5, 0x29, 0x25, 0xDF, 0xE1, 0x7B, 0x37, 0xF9, 0x5D, 0x22, 0xBC, 0xA5, 0x4F, 0x47, 0x59, + 0x19, 0xAC, 0x85, 0x90, 0x98, 0xC0, 0xF0, 0xD0, 0x8A, 0xC5, 0x87, 0x5E, 0xF2, 0x9B, 0x56, + 0xFD, 0x14, 0x1E, 0x6E, 0xF1, 0x5F, 0x70, 0x0A, 0x0B, 0x66, 0xF3, 0x95, 0x95, 0xC5, 0x88, + 0x17, 0x73, 0x73, 0xC4, 0x66, 0x9B, 0x21, 0xBC, 0x07, 0x1E, 0x4C, 0x3A, 0xA5, 0xF0, 0xB4, + 0xA3, 0x1B, 0x62, 0x58, 0xF3, 0x5D, 0xA2, 0x4A, 0xC3, 0xCD, 0x29, 0xC7, 0xF2, 0x09, 0x24, + 0x10, 0xC5, 0x07, 0x83, 0x55, 0xB1, 0x38, 0xFB, 0x53, 0xA6, 0xB9, 0xAE, 0x6E, 0x0B, 0x9C, + 0x08, 0x24, 0x3E, 0x7B, 0xAA, 0x45, 0xC4, 0x73, 0x76, 0xEB, 0x8C, 0x7F, 0x13, 0xD4, 0xCF, + 0x51, 0xAA, 0x73, 0x6F, 0xA3, 0x15, 0x40, 0xC9, 0x24, 0x1F, 0x37, 0x0D, 0xA5, 0x44, 0xBF, + 0x9F, 0x9C, 0x28, 0xD9, 0xA5, 0x7E, 0x2F, 0x2A, 0x7C, 0xA9, 0x5A, 0x4E, 0x4B, 0x46, 0x6E, + 0x64, 0x1A, 0xB3, 0xBC, 0xC7, 0x6A, 0xDF, 0x11, 0x39, 0xD5, 0x67, 0xA6, 0xF1, 0x2B, 0x52, + 0xF3, 0xA6, 0x5E, 0x7E, 0xC0, 0xAA, 0xE2, 0x6B, 0xCA, 0xA8, 0xC5, 0x58, 0x33, 0xB0, 0x4E, + 0x59, 0x99, 0x8E, 0xBC, 0x9A, 0x19, 0x30, 0xFB, 0xB6, 0xD2, 0x23, 0x3C, 0x53, 0xD2, 0xC1, + 0xF8, 0xB9, 0x51, 0x8E, 0x3C, 0x2D, 0xE7, 0x3A, 0x19, 0xDE, 0xE6, 0xB3, 0x80, 0xA5, 0xB3, + 0x29, 0x71, 0xCF, 0x64, 0xE1, 0x29, 0xFD, 0x6C, 0x1F, 0xA6, 0xE7, 0x5D, 0x4A, 0x23, 0x45, + 0x01, 0xE9, 0x66, 0xDD, 0x3A, 0x54, 0x0A, 0xF5, 0xC8, 0xF4, 0xF3, 0x4A, 0x6B, 0x4A, 0x25, + 0x3E, 0xE2, 0x84, 0x92, 0x56, 0x6D, 0x5E, 0x67, 0xC6, 0xF5, 0x58, 0x55, 0xFC, 0xB0, 0x50, + 0x6F, 0xB0, 0x6C, 0x15, 0x67, 0x44, 0xD9, 0xA0, 0x3A, 0x31, 0xA2, 0x6F, 0xA9, 0x4C, 0xAD, + 0x14, 0xF1, 0x57, 0xB7, 0xF3, 0x03, 0xD0, 0x7A, 0x69, 0xC7, 0x73, 0x76, 0x8F, 0xCB, 0x4D, + 0x07, 0x9C, 0x09, 0x05, 0x97, 0x03, 0xA0, 0xC3, 0xA9, 0x4D, 0xE4, 0xB9, 0x9E, 0xA3, 0xA2, + 0xF1, 0x65, 0x83, 0xD0, 0xF9, 0x17, 0x0A, 0x39, 0x50, 0xDB, 0x07, 0xB4, 0xF0, 0xBC, 0x30, + 0x80, 0x29, 0x27, 0xF9, 0xF7, 0x96, 0x1B, 0x62, 0x59, 0x89, 0x26, 0x36, 0xA9, 0x50, 0x2A, + 0x27, 0x05, 0x30, 0x36, 0x37, 0x79, 0x9D, 0xD3, 0x44, 0xDA, 0x45, 0x1C, 0x1C, 0xF7, 0xBF, + 0x67, 0x84, 0x0C, 0xEB, 0x30, 0x79, 0xAB, 0x8C, 0x6B, 0x8C, 0x19, 0x27, 0xF6, 0x40, 0x53, + 0xC6, 0x12, 0x45, 0x0C, 0x45, 0xC9, 0xE6, 0x03, 0xBC, 0x16, 0x66, 0x6E, 0x59, 0x6B, 0x34, + 0x71, 0xE1, 0x03, 0xB6, 0xF1, 0x54, 0x47, 0x42, 0x4D, 0x17, 0x02, 0x20, 0x48, 0x11, 0x1F, + 0xFB, 0xD3, 0x7E, 0x1C, 0x67, 0x0F, 0x64, 0xF1, 0x4B, 0x8A, 0x7B, 0x32, 0xB9, 0x4C, 0x1A, + 0x49, 0xB4, 0x5D, 0xD2, 0xFC, 0x38, 0xCD, 0x52, 0x89, 0xD9, 0x10, 0xAD, 0x63, 0x60, 0x2C, + 0xF5, 0xE1, 0x30, 0x42, 0xC6, 0x4A, 0xC6, 0x79, 0x7B, 0x89, 0xFB, 0x55, 0x1A, 0xD0, 0x8E, + 0x05, 0xA9, 0x2D, 0x20, 0x0C, 0xCC, 0xB7, 0xE7, 0x12, 0xEF, 0x23, 0xC9, 0x31, 0x2C, 0xB3, + 0x50, 0xF0, 0x29, 0xAB, 0x53, 0x7E, 0x28, 0x73, 0x47, 0xFD, 0x30, 0x75, 0xAC, 0x10, 0x90, + 0x6A, 0x78, 0x3F, 0x1C, 0x6C, 0x07, 0xCC, 0xB8, 0x8F, 0x41, 0x22, 0x8C, 0x4B, 0xE1, 0xC6, + 0x40, 0xF7, 0x90, 0xB5, 0xC3, 0xA5, 0xD5, 0xD3, 0xCA, 0x79, 0x24, 0x95, 0xD7, 0x4B, 0xC4, + 0x61, 0x56, 0x26, 0x58, 0xC0, 0x7A, 0xC6, 0x00, 0x27, 0x6B, 0x92, 0x4A, 0xB5, 0xBC, 0x9B, + 0xE1, 0xF0, 0x49, 0x4C, 0xB7, 0x6F, 0x82, 0xF4, 0x60, 0xA7, 0x48, 0x09, 0x72, 0x66, 0x33, + 0x81, 0xE1, 0x69, 0x99, 0x60, 0x61, 0xD7, 0x99, 0x85, 0x9E, 0xC5, 0x4D, 0x4F, 0x5C, 0xA5, + 0xC4, 0x11, 0xC0, 0x1D, 0xB1, 0x59, 0x7B, 0x16, 0x59, 0x77, 0x66, 0x9D, 0xE1, 0x3A, 0x92, + 0x8A, 0x34, 0xAF, 0xBA, 0xC2, 0x58, 0xFE, 0xA8, 0xC4, 0x76, 0x42, 0x39, 0xC9, 0x42, 0x1D, + 0xC3, 0x11, 0x9B, 0xF5, 0xB4, 0x76, 0x99, 0x20, 0x69, 0x78, 0x32, 0x7B, 0x1C, 0x53, 0x45, + 0xEF, 0x74, 0x6A, 0x79, 0x83, 0x84, 0x1F, 0x05, 0x6E, 0x25, 0x34, 0x10, 0x0A, 0xB2, 0x4D, + 0x4E, 0x9A, 0xBB, 0xD0, 0xB1, 0x7C, 0x6A, 0x95, 0xBD, 0x4C, 0x3C, 0x0E, 0x40, 0xF6, 0x9E, + 0x16, 0x12, 0xAC, 0xEE, 0xB2, 0x8B, 0x99, 0x08, 0x6C, 0x95, 0x11, 0x6E, 0x72, 0x04, 0x27, + 0x38, 0x93, 0x39, 0x0B, 0xF4, 0x6B, 0x89, 0x9B, 0x36, 0x28, 0x6B, 0x0E, 0xBF, 0x19, 0x47, + 0xBB, 0x98, 0x84, 0xF7, 0x32, 0xCA, 0x27, 0xDA, 0x82, 0xB1, 0x9B, 0x5D, 0xC0, 0xCC, 0x7F, + 0x88, 0x85, 0x71, 0x49, 0x10, 0x88, 0x8B, 0x23, 0x10, 0xC4, 0xF9, 0x31, 0x9D, 0x41, 0x0B, + 0x34, 0xE6, 0x43, 0x3B, 0x90, 0x03, 0xE2, 0x17, 0x6B, 0xB9, 0x95, 0x25, 0x74, 0x56, 0x10, + 0x6E, 0x89, 0x52, 0x16, 0x3B, 0x8B, 0xA5, 0x92, 0x53, 0x0C, 0xC5, 0xAA, 0x0A, 0xEB, 0x43, + 0xAD, 0x39, 0x8F, 0xE9, 0xE9, 0x7B, 0xAA, 0x52, 0x3D, 0x7A, 0x44, 0x31, 0x67, 0x7C, 0x3D, + 0x3A, 0xF0, 0x71, 0x9E, 0x47, 0x5D, 0xB8, 0x5C, 0xA9, 0x5A, 0xF5, 0x08, 0x9B, 0xEA, 0xBE, + 0xB0, 0x5B, 0x2F, 0xAA, 0xB4, 0x89, 0x6B, 0xA6, 0x0F, 0x81, 0xC8, 0x84, 0x72, 0xA5, 0x7B, + 0x46, 0xA8, 0x28, 0x82, 0x6A, 0x0C, 0xDF, 0xB4, 0x46, 0xF8, 0x18, 0x91, 0x82, 0xD2, 0xBF, + 0x5E, 0xAC, 0x4E, 0xC1, 0xCC, 0x5D, 0xEA, 0xF5, 0x99, 0xC8, 0xA1, 0x3E, 0x48, 0x23, 0x54, + 0x06, 0xD1, 0x7F, 0xFD, 0xDC, 0x83, 0x44, 0xB6, 0xC6, 0x69, 0x84, 0xA8, 0x68, 0xAA, 0x92, + 0xFA, 0x02, 0x22, 0x7A, 0x08, 0x69, 0x50, 0xEB, 0x0C, 0x87, 0x01, 0xED, 0x58, 0xDC, 0x62, + 0x87, 0x76, 0xB9, 0x83, 0x88, 0x2E, 0x11, 0x75, + ]; let pk = MLKEM1024PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM1024::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -275,7 +727,7 @@ fn bench_mlkem1024_encaps() { } fn bench_mlkem1024_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM1024, MLKEM1024PublicKey}; + use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEM1024PublicKey, MLKEMTrait}; eprintln!("MLKEM1024_lowmemory/Encaps"); @@ -289,7 +741,113 @@ fn bench_mlkem1024_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM1024_PK_LEN] = [0x4b,0x94,0xc2,0x94,0x50,0x11,0x11,0x91,0x82,0x3b,0x35,0x14,0xc9,0xac,0x1e,0xa3,0xd9,0x82,0x5c,0xcb,0x86,0x39,0x3a,0x2d,0xfb,0x04,0x65,0x4f,0xa2,0x19,0x2d,0x37,0xbf,0xad,0x1c,0x49,0x7c,0x65,0x02,0xee,0xe5,0xca,0x80,0xa7,0x3b,0xfc,0xe0,0xba,0xf5,0xa5,0x4a,0x88,0x58,0x5a,0x40,0x13,0x97,0xa3,0xd2,0x32,0xf4,0x26,0xa7,0xaf,0xb0,0x82,0xbc,0x21,0xa4,0x43,0x17,0x09,0x0e,0xaa,0xc7,0x59,0x2c,0x2e,0xa8,0x8a,0x65,0x3c,0x44,0x91,0xea,0x19,0x39,0x31,0x33,0x5f,0x52,0xe9,0x89,0xa3,0xc4,0xcc,0x56,0xd9,0xc5,0x53,0x73,0x2d,0x57,0xc4,0x70,0xfb,0x41,0xab,0x75,0x9b,0x65,0xd2,0xd0,0x44,0x45,0x38,0x2f,0xcd,0x9c,0x4e,0x34,0x4a,0x11,0x28,0xfa,0x9e,0x11,0xe0,0x43,0x58,0xe1,0x92,0xed,0x01,0x4b,0x23,0x23,0x2a,0x7e,0xe2,0xb2,0x2e,0x23,0x71,0x7f,0x44,0x11,0x1e,0xe3,0x35,0x75,0x39,0x9c,0x37,0x64,0x6d,0xa9,0x81,0x3e,0xc9,0xb2,0x12,0xaf,0xe9,0x4e,0x5d,0xc5,0xc2,0x33,0x0a,0x72,0x94,0xcc,0x1f,0x42,0x34,0xa6,0xd3,0xfb,0xb4,0xf1,0x68,0x5a,0xb8,0x89,0x2c,0x04,0xac,0xb1,0x7c,0xd1,0xc1,0x70,0xd7,0xb0,0x61,0x1b,0x6a,0x71,0x76,0xc7,0x94,0xcc,0x8c,0x67,0xf5,0x5f,0xc9,0x23,0xc2,0xad,0x20,0x31,0x00,0xf3,0x65,0x99,0x18,0x82,0xc3,0x02,0x43,0xd7,0x78,0x13,0x84,0x3b,0x5e,0xc7,0xc9,0x64,0x03,0x22,0x63,0x70,0x60,0x92,0xec,0xf0,0x0c,0x75,0x16,0xbe,0x64,0xe4,0x59,0x8c,0xa4,0x22,0x6c,0x06,0x9b,0xb5,0xe6,0x7e,0x41,0x75,0xcf,0x22,0x86,0xc8,0xdd,0x5c,0x48,0x8a,0x6c,0x58,0x61,0xf3,0x1b,0xaa,0x0b,0xd0,0x26,0x94,0x70,0xe8,0xb5,0x51,0xdd,0x3b,0xcd,0x38,0xc8,0x6c,0x12,0xf9,0xcd,0xb1,0x76,0xc7,0x7d,0xc8,0xb6,0xc0,0x2a,0x70,0x1f,0x47,0x89,0x02,0xc8,0x55,0x3f,0x69,0x4c,0x0d,0x82,0x72,0x7b,0x4c,0x4a,0x5c,0x2c,0x10,0x41,0x21,0x2a,0xa1,0x27,0x48,0x08,0xb8,0x21,0x11,0xb3,0x77,0xec,0x75,0x21,0x4e,0x9b,0x19,0x78,0xf7,0x60,0x04,0xd4,0x13,0x9d,0x98,0x61,0x3f,0x4b,0x8e,0x98,0xd2,0x0a,0xf7,0xb5,0x34,0x07,0x3a,0x50,0x9a,0x95,0x9b,0x7a,0x75,0x64,0xf9,0xb4,0x0c,0xa2,0x18,0xbf,0x61,0x82,0x93,0x20,0xa8,0x50,0x20,0x17,0x95,0x4d,0x32,0x8d,0x7a,0xc6,0xc7,0x69,0xec,0x29,0x70,0x07,0x56,0xe7,0xb0,0x68,0x5b,0x34,0x0d,0x5e,0x11,0x80,0x59,0x50,0x4a,0x49,0xa9,0xa5,0x0a,0x10,0x19,0x8e,0xb1,0x0a,0x57,0x84,0x67,0x8e,0xb4,0x27,0xd7,0xb4,0xba,0xbb,0x95,0x52,0x93,0x3b,0x06,0x28,0x97,0x97,0x3e,0x13,0x18,0xea,0xf0,0xa0,0xea,0xc3,0x75,0x84,0xa6,0x54,0x01,0xb1,0x70,0x3e,0x04,0x2a,0xcc,0xd8,0x37,0x53,0x14,0x83,0xf2,0x41,0xca,0xdc,0xd1,0xc1,0xd3,0x78,0x11,0x9e,0x69,0x44,0x29,0xdb,0x19,0x9a,0xc8,0x91,0xe4,0xc5,0x34,0x37,0x57,0x08,0x5b,0xb3,0xae,0x78,0x36,0x67,0x35,0x0c,0x44,0x58,0xd9,0x76,0x72,0xe8,0x61,0xe8,0x0b,0x1d,0x26,0x79,0x51,0x0e,0xa3,0xa6,0xf2,0x36,0x0c,0x77,0xa4,0x69,0x42,0xc7,0xa0,0x6a,0x55,0x4d,0x22,0x80,0x80,0xc8,0x4b,0x47,0xae,0xf1,0x4d,0xb1,0x76,0x20,0xcb,0x16,0xc0,0x6a,0xb3,0x0a,0x1b,0xe4,0xcd,0xa7,0x08,0x2b,0xe9,0xf8,0x7e,0x9c,0x21,0x1c,0x46,0x91,0x63,0x49,0xa5,0xba,0x8e,0xaa,0x52,0x01,0xc7,0x29,0x4a,0x3c,0x08,0x85,0xb5,0x3b,0x65,0x74,0x52,0x10,0x88,0x25,0xec,0x64,0x6c,0x90,0xa0,0x46,0x12,0x32,0x4e,0xe7,0xd0,0x31,0xaf,0xe5,0x34,0x31,0x32,0xcb,0xef,0x67,0xb6,0xef,0xb1,0xa5,0xec,0x28,0x09,0xb7,0x73,0x53,0x8c,0xe7,0x7b,0x3d,0x8b,0x04,0xeb,0x0b,0x3c,0x22,0x56,0x01,0x1e,0x4c,0x71,0x6c,0x19,0xa8,0xba,0x07,0x52,0xbf,0x71,0x49,0x21,0x17,0x64,0x9f,0x06,0x15,0xc3,0x29,0x0f,0xc2,0x9a,0x46,0xfd,0xe4,0xbd,0x52,0xdb,0x92,0x86,0xd6,0x03,0x38,0x82,0x44,0x25,0x9c,0x15,0xa7,0xac,0x2b,0x64,0x0a,0x60,0xcc,0x03,0x37,0x6a,0x58,0x41,0xa3,0xfb,0x8a,0x47,0x35,0x68,0xfa,0x9b,0x1a,0x26,0x72,0x15,0xf3,0x4c,0x01,0x69,0x7b,0x0f,0x0e,0x62,0x71,0x75,0xd7,0x21,0x05,0xb7,0x70,0x7c,0x29,0xb9,0xe6,0x14,0xbd,0xc3,0x3a,0x6f,0x6c,0x81,0x8a,0x95,0x37,0x0b,0x42,0x78,0x82,0xd7,0xb4,0x76,0x79,0x6a,0x9e,0xc6,0xeb,0x99,0x32,0x74,0xcd,0x9b,0x23,0x91,0xa8,0x2b,0xa4,0x5e,0x33,0x93,0xd2,0xe9,0xae,0x97,0x21,0xca,0x9d,0x6c,0x1b,0x98,0x8b,0x58,0x27,0x71,0x3f,0x90,0xa6,0x58,0x5d,0xe9,0x43,0x35,0x28,0xc0,0x2b,0x03,0xce,0x10,0xbb,0x5f,0x72,0x01,0x38,0xd0,0xfb,0xb4,0xc3,0x0c,0x12,0x66,0xb9,0x18,0xe5,0x29,0x25,0xdf,0xe1,0x7b,0x37,0xf9,0x5d,0x22,0xbc,0xa5,0x4f,0x47,0x59,0x19,0xac,0x85,0x90,0x98,0xc0,0xf0,0xd0,0x8a,0xc5,0x87,0x5e,0xf2,0x9b,0x56,0xfd,0x14,0x1e,0x6e,0xf1,0x5f,0x70,0x0a,0x0b,0x66,0xf3,0x95,0x95,0xc5,0x88,0x17,0x73,0x73,0xc4,0x66,0x9b,0x21,0xbc,0x07,0x1e,0x4c,0x3a,0xa5,0xf0,0xb4,0xa3,0x1b,0x62,0x58,0xf3,0x5d,0xa2,0x4a,0xc3,0xcd,0x29,0xc7,0xf2,0x09,0x24,0x10,0xc5,0x07,0x83,0x55,0xb1,0x38,0xfb,0x53,0xa6,0xb9,0xae,0x6e,0x0b,0x9c,0x08,0x24,0x3e,0x7b,0xaa,0x45,0xc4,0x73,0x76,0xeb,0x8c,0x7f,0x13,0xd4,0xcf,0x51,0xaa,0x73,0x6f,0xa3,0x15,0x40,0xc9,0x24,0x1f,0x37,0x0d,0xa5,0x44,0xbf,0x9f,0x9c,0x28,0xd9,0xa5,0x7e,0x2f,0x2a,0x7c,0xa9,0x5a,0x4e,0x4b,0x46,0x6e,0x64,0x1a,0xb3,0xbc,0xc7,0x6a,0xdf,0x11,0x39,0xd5,0x67,0xa6,0xf1,0x2b,0x52,0xf3,0xa6,0x5e,0x7e,0xc0,0xaa,0xe2,0x6b,0xca,0xa8,0xc5,0x58,0x33,0xb0,0x4e,0x59,0x99,0x8e,0xbc,0x9a,0x19,0x30,0xfb,0xb6,0xd2,0x23,0x3c,0x53,0xd2,0xc1,0xf8,0xb9,0x51,0x8e,0x3c,0x2d,0xe7,0x3a,0x19,0xde,0xe6,0xb3,0x80,0xa5,0xb3,0x29,0x71,0xcf,0x64,0xe1,0x29,0xfd,0x6c,0x1f,0xa6,0xe7,0x5d,0x4a,0x23,0x45,0x01,0xe9,0x66,0xdd,0x3a,0x54,0x0a,0xf5,0xc8,0xf4,0xf3,0x4a,0x6b,0x4a,0x25,0x3e,0xe2,0x84,0x92,0x56,0x6d,0x5e,0x67,0xc6,0xf5,0x58,0x55,0xfc,0xb0,0x50,0x6f,0xb0,0x6c,0x15,0x67,0x44,0xd9,0xa0,0x3a,0x31,0xa2,0x6f,0xa9,0x4c,0xad,0x14,0xf1,0x57,0xb7,0xf3,0x03,0xd0,0x7a,0x69,0xc7,0x73,0x76,0x8f,0xcb,0x4d,0x07,0x9c,0x09,0x05,0x97,0x03,0xa0,0xc3,0xa9,0x4d,0xe4,0xb9,0x9e,0xa3,0xa2,0xf1,0x65,0x83,0xd0,0xf9,0x17,0x0a,0x39,0x50,0xdb,0x07,0xb4,0xf0,0xbc,0x30,0x80,0x29,0x27,0xf9,0xf7,0x96,0x1b,0x62,0x59,0x89,0x26,0x36,0xa9,0x50,0x2a,0x27,0x05,0x30,0x36,0x37,0x79,0x9d,0xd3,0x44,0xda,0x45,0x1c,0x1c,0xf7,0xbf,0x67,0x84,0x0c,0xeb,0x30,0x79,0xab,0x8c,0x6b,0x8c,0x19,0x27,0xf6,0x40,0x53,0xc6,0x12,0x45,0x0c,0x45,0xc9,0xe6,0x03,0xbc,0x16,0x66,0x6e,0x59,0x6b,0x34,0x71,0xe1,0x03,0xb6,0xf1,0x54,0x47,0x42,0x4d,0x17,0x02,0x20,0x48,0x11,0x1f,0xfb,0xd3,0x7e,0x1c,0x67,0x0f,0x64,0xf1,0x4b,0x8a,0x7b,0x32,0xb9,0x4c,0x1a,0x49,0xb4,0x5d,0xd2,0xfc,0x38,0xcd,0x52,0x89,0xd9,0x10,0xad,0x63,0x60,0x2c,0xf5,0xe1,0x30,0x42,0xc6,0x4a,0xc6,0x79,0x7b,0x89,0xfb,0x55,0x1a,0xd0,0x8e,0x05,0xa9,0x2d,0x20,0x0c,0xcc,0xb7,0xe7,0x12,0xef,0x23,0xc9,0x31,0x2c,0xb3,0x50,0xf0,0x29,0xab,0x53,0x7e,0x28,0x73,0x47,0xfd,0x30,0x75,0xac,0x10,0x90,0x6a,0x78,0x3f,0x1c,0x6c,0x07,0xcc,0xb8,0x8f,0x41,0x22,0x8c,0x4b,0xe1,0xc6,0x40,0xf7,0x90,0xb5,0xc3,0xa5,0xd5,0xd3,0xca,0x79,0x24,0x95,0xd7,0x4b,0xc4,0x61,0x56,0x26,0x58,0xc0,0x7a,0xc6,0x00,0x27,0x6b,0x92,0x4a,0xb5,0xbc,0x9b,0xe1,0xf0,0x49,0x4c,0xb7,0x6f,0x82,0xf4,0x60,0xa7,0x48,0x09,0x72,0x66,0x33,0x81,0xe1,0x69,0x99,0x60,0x61,0xd7,0x99,0x85,0x9e,0xc5,0x4d,0x4f,0x5c,0xa5,0xc4,0x11,0xc0,0x1d,0xb1,0x59,0x7b,0x16,0x59,0x77,0x66,0x9d,0xe1,0x3a,0x92,0x8a,0x34,0xaf,0xba,0xc2,0x58,0xfe,0xa8,0xc4,0x76,0x42,0x39,0xc9,0x42,0x1d,0xc3,0x11,0x9b,0xf5,0xb4,0x76,0x99,0x20,0x69,0x78,0x32,0x7b,0x1c,0x53,0x45,0xef,0x74,0x6a,0x79,0x83,0x84,0x1f,0x05,0x6e,0x25,0x34,0x10,0x0a,0xb2,0x4d,0x4e,0x9a,0xbb,0xd0,0xb1,0x7c,0x6a,0x95,0xbd,0x4c,0x3c,0x0e,0x40,0xf6,0x9e,0x16,0x12,0xac,0xee,0xb2,0x8b,0x99,0x08,0x6c,0x95,0x11,0x6e,0x72,0x04,0x27,0x38,0x93,0x39,0x0b,0xf4,0x6b,0x89,0x9b,0x36,0x28,0x6b,0x0e,0xbf,0x19,0x47,0xbb,0x98,0x84,0xf7,0x32,0xca,0x27,0xda,0x82,0xb1,0x9b,0x5d,0xc0,0xcc,0x7f,0x88,0x85,0x71,0x49,0x10,0x88,0x8b,0x23,0x10,0xc4,0xf9,0x31,0x9d,0x41,0x0b,0x34,0xe6,0x43,0x3b,0x90,0x03,0xe2,0x17,0x6b,0xb9,0x95,0x25,0x74,0x56,0x10,0x6e,0x89,0x52,0x16,0x3b,0x8b,0xa5,0x92,0x53,0x0c,0xc5,0xaa,0x0a,0xeb,0x43,0xad,0x39,0x8f,0xe9,0xe9,0x7b,0xaa,0x52,0x3d,0x7a,0x44,0x31,0x67,0x7c,0x3d,0x3a,0xf0,0x71,0x9e,0x47,0x5d,0xb8,0x5c,0xa9,0x5a,0xf5,0x08,0x9b,0xea,0xbe,0xb0,0x5b,0x2f,0xaa,0xb4,0x89,0x6b,0xa6,0x0f,0x81,0xc8,0x84,0x72,0xa5,0x7b,0x46,0xa8,0x28,0x82,0x6a,0x0c,0xdf,0xb4,0x46,0xf8,0x18,0x91,0x82,0xd2,0xbf,0x5e,0xac,0x4e,0xc1,0xcc,0x5d,0xea,0xf5,0x99,0xc8,0xa1,0x3e,0x48,0x23,0x54,0x06,0xd1,0x7f,0xfd,0xdc,0x83,0x44,0xb6,0xc6,0x69,0x84,0xa8,0x68,0xaa,0x92,0xfa,0x02,0x22,0x7a,0x08,0x69,0x50,0xeb,0x0c,0x87,0x01,0xed,0x58,0xdc,0x62,0x87,0x76,0xb9,0x83,0x88,0x2e,0x11,0x75]; + let pk_bytes: [u8; MLKEM1024_PK_LEN] = [ + 0x4B, 0x94, 0xC2, 0x94, 0x50, 0x11, 0x11, 0x91, 0x82, 0x3B, 0x35, 0x14, 0xC9, 0xAC, 0x1E, + 0xA3, 0xD9, 0x82, 0x5C, 0xCB, 0x86, 0x39, 0x3A, 0x2D, 0xFB, 0x04, 0x65, 0x4F, 0xA2, 0x19, + 0x2D, 0x37, 0xBF, 0xAD, 0x1C, 0x49, 0x7C, 0x65, 0x02, 0xEE, 0xE5, 0xCA, 0x80, 0xA7, 0x3B, + 0xFC, 0xE0, 0xBA, 0xF5, 0xA5, 0x4A, 0x88, 0x58, 0x5A, 0x40, 0x13, 0x97, 0xA3, 0xD2, 0x32, + 0xF4, 0x26, 0xA7, 0xAF, 0xB0, 0x82, 0xBC, 0x21, 0xA4, 0x43, 0x17, 0x09, 0x0E, 0xAA, 0xC7, + 0x59, 0x2C, 0x2E, 0xA8, 0x8A, 0x65, 0x3C, 0x44, 0x91, 0xEA, 0x19, 0x39, 0x31, 0x33, 0x5F, + 0x52, 0xE9, 0x89, 0xA3, 0xC4, 0xCC, 0x56, 0xD9, 0xC5, 0x53, 0x73, 0x2D, 0x57, 0xC4, 0x70, + 0xFB, 0x41, 0xAB, 0x75, 0x9B, 0x65, 0xD2, 0xD0, 0x44, 0x45, 0x38, 0x2F, 0xCD, 0x9C, 0x4E, + 0x34, 0x4A, 0x11, 0x28, 0xFA, 0x9E, 0x11, 0xE0, 0x43, 0x58, 0xE1, 0x92, 0xED, 0x01, 0x4B, + 0x23, 0x23, 0x2A, 0x7E, 0xE2, 0xB2, 0x2E, 0x23, 0x71, 0x7F, 0x44, 0x11, 0x1E, 0xE3, 0x35, + 0x75, 0x39, 0x9C, 0x37, 0x64, 0x6D, 0xA9, 0x81, 0x3E, 0xC9, 0xB2, 0x12, 0xAF, 0xE9, 0x4E, + 0x5D, 0xC5, 0xC2, 0x33, 0x0A, 0x72, 0x94, 0xCC, 0x1F, 0x42, 0x34, 0xA6, 0xD3, 0xFB, 0xB4, + 0xF1, 0x68, 0x5A, 0xB8, 0x89, 0x2C, 0x04, 0xAC, 0xB1, 0x7C, 0xD1, 0xC1, 0x70, 0xD7, 0xB0, + 0x61, 0x1B, 0x6A, 0x71, 0x76, 0xC7, 0x94, 0xCC, 0x8C, 0x67, 0xF5, 0x5F, 0xC9, 0x23, 0xC2, + 0xAD, 0x20, 0x31, 0x00, 0xF3, 0x65, 0x99, 0x18, 0x82, 0xC3, 0x02, 0x43, 0xD7, 0x78, 0x13, + 0x84, 0x3B, 0x5E, 0xC7, 0xC9, 0x64, 0x03, 0x22, 0x63, 0x70, 0x60, 0x92, 0xEC, 0xF0, 0x0C, + 0x75, 0x16, 0xBE, 0x64, 0xE4, 0x59, 0x8C, 0xA4, 0x22, 0x6C, 0x06, 0x9B, 0xB5, 0xE6, 0x7E, + 0x41, 0x75, 0xCF, 0x22, 0x86, 0xC8, 0xDD, 0x5C, 0x48, 0x8A, 0x6C, 0x58, 0x61, 0xF3, 0x1B, + 0xAA, 0x0B, 0xD0, 0x26, 0x94, 0x70, 0xE8, 0xB5, 0x51, 0xDD, 0x3B, 0xCD, 0x38, 0xC8, 0x6C, + 0x12, 0xF9, 0xCD, 0xB1, 0x76, 0xC7, 0x7D, 0xC8, 0xB6, 0xC0, 0x2A, 0x70, 0x1F, 0x47, 0x89, + 0x02, 0xC8, 0x55, 0x3F, 0x69, 0x4C, 0x0D, 0x82, 0x72, 0x7B, 0x4C, 0x4A, 0x5C, 0x2C, 0x10, + 0x41, 0x21, 0x2A, 0xA1, 0x27, 0x48, 0x08, 0xB8, 0x21, 0x11, 0xB3, 0x77, 0xEC, 0x75, 0x21, + 0x4E, 0x9B, 0x19, 0x78, 0xF7, 0x60, 0x04, 0xD4, 0x13, 0x9D, 0x98, 0x61, 0x3F, 0x4B, 0x8E, + 0x98, 0xD2, 0x0A, 0xF7, 0xB5, 0x34, 0x07, 0x3A, 0x50, 0x9A, 0x95, 0x9B, 0x7A, 0x75, 0x64, + 0xF9, 0xB4, 0x0C, 0xA2, 0x18, 0xBF, 0x61, 0x82, 0x93, 0x20, 0xA8, 0x50, 0x20, 0x17, 0x95, + 0x4D, 0x32, 0x8D, 0x7A, 0xC6, 0xC7, 0x69, 0xEC, 0x29, 0x70, 0x07, 0x56, 0xE7, 0xB0, 0x68, + 0x5B, 0x34, 0x0D, 0x5E, 0x11, 0x80, 0x59, 0x50, 0x4A, 0x49, 0xA9, 0xA5, 0x0A, 0x10, 0x19, + 0x8E, 0xB1, 0x0A, 0x57, 0x84, 0x67, 0x8E, 0xB4, 0x27, 0xD7, 0xB4, 0xBA, 0xBB, 0x95, 0x52, + 0x93, 0x3B, 0x06, 0x28, 0x97, 0x97, 0x3E, 0x13, 0x18, 0xEA, 0xF0, 0xA0, 0xEA, 0xC3, 0x75, + 0x84, 0xA6, 0x54, 0x01, 0xB1, 0x70, 0x3E, 0x04, 0x2A, 0xCC, 0xD8, 0x37, 0x53, 0x14, 0x83, + 0xF2, 0x41, 0xCA, 0xDC, 0xD1, 0xC1, 0xD3, 0x78, 0x11, 0x9E, 0x69, 0x44, 0x29, 0xDB, 0x19, + 0x9A, 0xC8, 0x91, 0xE4, 0xC5, 0x34, 0x37, 0x57, 0x08, 0x5B, 0xB3, 0xAE, 0x78, 0x36, 0x67, + 0x35, 0x0C, 0x44, 0x58, 0xD9, 0x76, 0x72, 0xE8, 0x61, 0xE8, 0x0B, 0x1D, 0x26, 0x79, 0x51, + 0x0E, 0xA3, 0xA6, 0xF2, 0x36, 0x0C, 0x77, 0xA4, 0x69, 0x42, 0xC7, 0xA0, 0x6A, 0x55, 0x4D, + 0x22, 0x80, 0x80, 0xC8, 0x4B, 0x47, 0xAE, 0xF1, 0x4D, 0xB1, 0x76, 0x20, 0xCB, 0x16, 0xC0, + 0x6A, 0xB3, 0x0A, 0x1B, 0xE4, 0xCD, 0xA7, 0x08, 0x2B, 0xE9, 0xF8, 0x7E, 0x9C, 0x21, 0x1C, + 0x46, 0x91, 0x63, 0x49, 0xA5, 0xBA, 0x8E, 0xAA, 0x52, 0x01, 0xC7, 0x29, 0x4A, 0x3C, 0x08, + 0x85, 0xB5, 0x3B, 0x65, 0x74, 0x52, 0x10, 0x88, 0x25, 0xEC, 0x64, 0x6C, 0x90, 0xA0, 0x46, + 0x12, 0x32, 0x4E, 0xE7, 0xD0, 0x31, 0xAF, 0xE5, 0x34, 0x31, 0x32, 0xCB, 0xEF, 0x67, 0xB6, + 0xEF, 0xB1, 0xA5, 0xEC, 0x28, 0x09, 0xB7, 0x73, 0x53, 0x8C, 0xE7, 0x7B, 0x3D, 0x8B, 0x04, + 0xEB, 0x0B, 0x3C, 0x22, 0x56, 0x01, 0x1E, 0x4C, 0x71, 0x6C, 0x19, 0xA8, 0xBA, 0x07, 0x52, + 0xBF, 0x71, 0x49, 0x21, 0x17, 0x64, 0x9F, 0x06, 0x15, 0xC3, 0x29, 0x0F, 0xC2, 0x9A, 0x46, + 0xFD, 0xE4, 0xBD, 0x52, 0xDB, 0x92, 0x86, 0xD6, 0x03, 0x38, 0x82, 0x44, 0x25, 0x9C, 0x15, + 0xA7, 0xAC, 0x2B, 0x64, 0x0A, 0x60, 0xCC, 0x03, 0x37, 0x6A, 0x58, 0x41, 0xA3, 0xFB, 0x8A, + 0x47, 0x35, 0x68, 0xFA, 0x9B, 0x1A, 0x26, 0x72, 0x15, 0xF3, 0x4C, 0x01, 0x69, 0x7B, 0x0F, + 0x0E, 0x62, 0x71, 0x75, 0xD7, 0x21, 0x05, 0xB7, 0x70, 0x7C, 0x29, 0xB9, 0xE6, 0x14, 0xBD, + 0xC3, 0x3A, 0x6F, 0x6C, 0x81, 0x8A, 0x95, 0x37, 0x0B, 0x42, 0x78, 0x82, 0xD7, 0xB4, 0x76, + 0x79, 0x6A, 0x9E, 0xC6, 0xEB, 0x99, 0x32, 0x74, 0xCD, 0x9B, 0x23, 0x91, 0xA8, 0x2B, 0xA4, + 0x5E, 0x33, 0x93, 0xD2, 0xE9, 0xAE, 0x97, 0x21, 0xCA, 0x9D, 0x6C, 0x1B, 0x98, 0x8B, 0x58, + 0x27, 0x71, 0x3F, 0x90, 0xA6, 0x58, 0x5D, 0xE9, 0x43, 0x35, 0x28, 0xC0, 0x2B, 0x03, 0xCE, + 0x10, 0xBB, 0x5F, 0x72, 0x01, 0x38, 0xD0, 0xFB, 0xB4, 0xC3, 0x0C, 0x12, 0x66, 0xB9, 0x18, + 0xE5, 0x29, 0x25, 0xDF, 0xE1, 0x7B, 0x37, 0xF9, 0x5D, 0x22, 0xBC, 0xA5, 0x4F, 0x47, 0x59, + 0x19, 0xAC, 0x85, 0x90, 0x98, 0xC0, 0xF0, 0xD0, 0x8A, 0xC5, 0x87, 0x5E, 0xF2, 0x9B, 0x56, + 0xFD, 0x14, 0x1E, 0x6E, 0xF1, 0x5F, 0x70, 0x0A, 0x0B, 0x66, 0xF3, 0x95, 0x95, 0xC5, 0x88, + 0x17, 0x73, 0x73, 0xC4, 0x66, 0x9B, 0x21, 0xBC, 0x07, 0x1E, 0x4C, 0x3A, 0xA5, 0xF0, 0xB4, + 0xA3, 0x1B, 0x62, 0x58, 0xF3, 0x5D, 0xA2, 0x4A, 0xC3, 0xCD, 0x29, 0xC7, 0xF2, 0x09, 0x24, + 0x10, 0xC5, 0x07, 0x83, 0x55, 0xB1, 0x38, 0xFB, 0x53, 0xA6, 0xB9, 0xAE, 0x6E, 0x0B, 0x9C, + 0x08, 0x24, 0x3E, 0x7B, 0xAA, 0x45, 0xC4, 0x73, 0x76, 0xEB, 0x8C, 0x7F, 0x13, 0xD4, 0xCF, + 0x51, 0xAA, 0x73, 0x6F, 0xA3, 0x15, 0x40, 0xC9, 0x24, 0x1F, 0x37, 0x0D, 0xA5, 0x44, 0xBF, + 0x9F, 0x9C, 0x28, 0xD9, 0xA5, 0x7E, 0x2F, 0x2A, 0x7C, 0xA9, 0x5A, 0x4E, 0x4B, 0x46, 0x6E, + 0x64, 0x1A, 0xB3, 0xBC, 0xC7, 0x6A, 0xDF, 0x11, 0x39, 0xD5, 0x67, 0xA6, 0xF1, 0x2B, 0x52, + 0xF3, 0xA6, 0x5E, 0x7E, 0xC0, 0xAA, 0xE2, 0x6B, 0xCA, 0xA8, 0xC5, 0x58, 0x33, 0xB0, 0x4E, + 0x59, 0x99, 0x8E, 0xBC, 0x9A, 0x19, 0x30, 0xFB, 0xB6, 0xD2, 0x23, 0x3C, 0x53, 0xD2, 0xC1, + 0xF8, 0xB9, 0x51, 0x8E, 0x3C, 0x2D, 0xE7, 0x3A, 0x19, 0xDE, 0xE6, 0xB3, 0x80, 0xA5, 0xB3, + 0x29, 0x71, 0xCF, 0x64, 0xE1, 0x29, 0xFD, 0x6C, 0x1F, 0xA6, 0xE7, 0x5D, 0x4A, 0x23, 0x45, + 0x01, 0xE9, 0x66, 0xDD, 0x3A, 0x54, 0x0A, 0xF5, 0xC8, 0xF4, 0xF3, 0x4A, 0x6B, 0x4A, 0x25, + 0x3E, 0xE2, 0x84, 0x92, 0x56, 0x6D, 0x5E, 0x67, 0xC6, 0xF5, 0x58, 0x55, 0xFC, 0xB0, 0x50, + 0x6F, 0xB0, 0x6C, 0x15, 0x67, 0x44, 0xD9, 0xA0, 0x3A, 0x31, 0xA2, 0x6F, 0xA9, 0x4C, 0xAD, + 0x14, 0xF1, 0x57, 0xB7, 0xF3, 0x03, 0xD0, 0x7A, 0x69, 0xC7, 0x73, 0x76, 0x8F, 0xCB, 0x4D, + 0x07, 0x9C, 0x09, 0x05, 0x97, 0x03, 0xA0, 0xC3, 0xA9, 0x4D, 0xE4, 0xB9, 0x9E, 0xA3, 0xA2, + 0xF1, 0x65, 0x83, 0xD0, 0xF9, 0x17, 0x0A, 0x39, 0x50, 0xDB, 0x07, 0xB4, 0xF0, 0xBC, 0x30, + 0x80, 0x29, 0x27, 0xF9, 0xF7, 0x96, 0x1B, 0x62, 0x59, 0x89, 0x26, 0x36, 0xA9, 0x50, 0x2A, + 0x27, 0x05, 0x30, 0x36, 0x37, 0x79, 0x9D, 0xD3, 0x44, 0xDA, 0x45, 0x1C, 0x1C, 0xF7, 0xBF, + 0x67, 0x84, 0x0C, 0xEB, 0x30, 0x79, 0xAB, 0x8C, 0x6B, 0x8C, 0x19, 0x27, 0xF6, 0x40, 0x53, + 0xC6, 0x12, 0x45, 0x0C, 0x45, 0xC9, 0xE6, 0x03, 0xBC, 0x16, 0x66, 0x6E, 0x59, 0x6B, 0x34, + 0x71, 0xE1, 0x03, 0xB6, 0xF1, 0x54, 0x47, 0x42, 0x4D, 0x17, 0x02, 0x20, 0x48, 0x11, 0x1F, + 0xFB, 0xD3, 0x7E, 0x1C, 0x67, 0x0F, 0x64, 0xF1, 0x4B, 0x8A, 0x7B, 0x32, 0xB9, 0x4C, 0x1A, + 0x49, 0xB4, 0x5D, 0xD2, 0xFC, 0x38, 0xCD, 0x52, 0x89, 0xD9, 0x10, 0xAD, 0x63, 0x60, 0x2C, + 0xF5, 0xE1, 0x30, 0x42, 0xC6, 0x4A, 0xC6, 0x79, 0x7B, 0x89, 0xFB, 0x55, 0x1A, 0xD0, 0x8E, + 0x05, 0xA9, 0x2D, 0x20, 0x0C, 0xCC, 0xB7, 0xE7, 0x12, 0xEF, 0x23, 0xC9, 0x31, 0x2C, 0xB3, + 0x50, 0xF0, 0x29, 0xAB, 0x53, 0x7E, 0x28, 0x73, 0x47, 0xFD, 0x30, 0x75, 0xAC, 0x10, 0x90, + 0x6A, 0x78, 0x3F, 0x1C, 0x6C, 0x07, 0xCC, 0xB8, 0x8F, 0x41, 0x22, 0x8C, 0x4B, 0xE1, 0xC6, + 0x40, 0xF7, 0x90, 0xB5, 0xC3, 0xA5, 0xD5, 0xD3, 0xCA, 0x79, 0x24, 0x95, 0xD7, 0x4B, 0xC4, + 0x61, 0x56, 0x26, 0x58, 0xC0, 0x7A, 0xC6, 0x00, 0x27, 0x6B, 0x92, 0x4A, 0xB5, 0xBC, 0x9B, + 0xE1, 0xF0, 0x49, 0x4C, 0xB7, 0x6F, 0x82, 0xF4, 0x60, 0xA7, 0x48, 0x09, 0x72, 0x66, 0x33, + 0x81, 0xE1, 0x69, 0x99, 0x60, 0x61, 0xD7, 0x99, 0x85, 0x9E, 0xC5, 0x4D, 0x4F, 0x5C, 0xA5, + 0xC4, 0x11, 0xC0, 0x1D, 0xB1, 0x59, 0x7B, 0x16, 0x59, 0x77, 0x66, 0x9D, 0xE1, 0x3A, 0x92, + 0x8A, 0x34, 0xAF, 0xBA, 0xC2, 0x58, 0xFE, 0xA8, 0xC4, 0x76, 0x42, 0x39, 0xC9, 0x42, 0x1D, + 0xC3, 0x11, 0x9B, 0xF5, 0xB4, 0x76, 0x99, 0x20, 0x69, 0x78, 0x32, 0x7B, 0x1C, 0x53, 0x45, + 0xEF, 0x74, 0x6A, 0x79, 0x83, 0x84, 0x1F, 0x05, 0x6E, 0x25, 0x34, 0x10, 0x0A, 0xB2, 0x4D, + 0x4E, 0x9A, 0xBB, 0xD0, 0xB1, 0x7C, 0x6A, 0x95, 0xBD, 0x4C, 0x3C, 0x0E, 0x40, 0xF6, 0x9E, + 0x16, 0x12, 0xAC, 0xEE, 0xB2, 0x8B, 0x99, 0x08, 0x6C, 0x95, 0x11, 0x6E, 0x72, 0x04, 0x27, + 0x38, 0x93, 0x39, 0x0B, 0xF4, 0x6B, 0x89, 0x9B, 0x36, 0x28, 0x6B, 0x0E, 0xBF, 0x19, 0x47, + 0xBB, 0x98, 0x84, 0xF7, 0x32, 0xCA, 0x27, 0xDA, 0x82, 0xB1, 0x9B, 0x5D, 0xC0, 0xCC, 0x7F, + 0x88, 0x85, 0x71, 0x49, 0x10, 0x88, 0x8B, 0x23, 0x10, 0xC4, 0xF9, 0x31, 0x9D, 0x41, 0x0B, + 0x34, 0xE6, 0x43, 0x3B, 0x90, 0x03, 0xE2, 0x17, 0x6B, 0xB9, 0x95, 0x25, 0x74, 0x56, 0x10, + 0x6E, 0x89, 0x52, 0x16, 0x3B, 0x8B, 0xA5, 0x92, 0x53, 0x0C, 0xC5, 0xAA, 0x0A, 0xEB, 0x43, + 0xAD, 0x39, 0x8F, 0xE9, 0xE9, 0x7B, 0xAA, 0x52, 0x3D, 0x7A, 0x44, 0x31, 0x67, 0x7C, 0x3D, + 0x3A, 0xF0, 0x71, 0x9E, 0x47, 0x5D, 0xB8, 0x5C, 0xA9, 0x5A, 0xF5, 0x08, 0x9B, 0xEA, 0xBE, + 0xB0, 0x5B, 0x2F, 0xAA, 0xB4, 0x89, 0x6B, 0xA6, 0x0F, 0x81, 0xC8, 0x84, 0x72, 0xA5, 0x7B, + 0x46, 0xA8, 0x28, 0x82, 0x6A, 0x0C, 0xDF, 0xB4, 0x46, 0xF8, 0x18, 0x91, 0x82, 0xD2, 0xBF, + 0x5E, 0xAC, 0x4E, 0xC1, 0xCC, 0x5D, 0xEA, 0xF5, 0x99, 0xC8, 0xA1, 0x3E, 0x48, 0x23, 0x54, + 0x06, 0xD1, 0x7F, 0xFD, 0xDC, 0x83, 0x44, 0xB6, 0xC6, 0x69, 0x84, 0xA8, 0x68, 0xAA, 0x92, + 0xFA, 0x02, 0x22, 0x7A, 0x08, 0x69, 0x50, 0xEB, 0x0C, 0x87, 0x01, 0xED, 0x58, 0xDC, 0x62, + 0x87, 0x76, 0xB9, 0x83, 0x88, 0x2E, 0x11, 0x75, + ]; let pk = MLKEM1024PublicKey::from_bytes(&pk_bytes).unwrap(); let (ss, ct) = MLKEM1024::encaps(&pk).unwrap(); print!("{:x?}", ss); @@ -297,14 +855,21 @@ fn bench_mlkem1024_lowmemory_encaps() { } fn bench_mlkem512_decaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM512}; + use bouncycastle::mlkem::{MLKEM512, MLKEMTrait}; eprintln!("MLKEM512/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); @@ -312,7 +877,60 @@ fn bench_mlkem512_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM512_CT_LEN] = [0x5a,0x68,0x40,0x8e,0xeb,0x0e,0xe7,0xd1,0xf5,0x11,0x4f,0x4b,0xed,0xae,0xb6,0x00,0x48,0xf5,0x39,0x7e,0xea,0x20,0xca,0x22,0x89,0xe5,0xb2,0xb1,0xc7,0x01,0x65,0x49,0xac,0x72,0x8d,0xb0,0x97,0x74,0xe1,0xc6,0x79,0x8c,0xd7,0xd1,0x93,0xbd,0x74,0x94,0x16,0xf3,0x75,0x2e,0x9d,0x75,0xbb,0x16,0x09,0x01,0xc6,0x39,0xab,0x39,0x41,0xb4,0xe2,0xb2,0xe5,0xa0,0x68,0x3d,0x49,0xcb,0xa9,0xbc,0xe2,0x07,0xf8,0x45,0x8f,0x9e,0xf9,0x54,0xce,0xbb,0xbf,0x15,0xb1,0x8a,0x14,0x8e,0x37,0x05,0xc6,0xb1,0x9b,0xba,0x2e,0x52,0x54,0x69,0x80,0x3d,0x1b,0x86,0x11,0xe7,0x43,0xc8,0x56,0x8b,0x9f,0x07,0x77,0x84,0x71,0xdd,0x0e,0x6c,0x3a,0x00,0x76,0x6c,0xfa,0xdf,0xf1,0x6d,0xf4,0xdb,0x94,0x9b,0x34,0xf0,0x86,0x07,0x06,0x30,0x3a,0x32,0xb6,0x2a,0x9b,0x9d,0x23,0x44,0x69,0x27,0x63,0x4d,0x02,0x45,0x9f,0x3b,0x69,0xbc,0x3b,0xd2,0xf1,0x2f,0x9f,0x66,0x00,0x92,0x10,0xcb,0xe9,0xdf,0xf1,0x1c,0xde,0x5e,0x63,0x3f,0x37,0xb4,0xdc,0xda,0xbe,0x97,0x93,0x0b,0x1b,0xce,0xe5,0x71,0xe9,0x55,0x49,0xe6,0xfd,0x25,0x54,0x98,0xcf,0x0d,0x75,0x4d,0xd2,0x5c,0x1d,0xda,0xe9,0x4f,0xa0,0x7c,0xed,0xc1,0x64,0x17,0xf5,0x51,0x3a,0xd3,0x5e,0x54,0xae,0x67,0xc6,0x4d,0x70,0x76,0x00,0x6d,0x8b,0x41,0x5b,0x8e,0xe8,0x2e,0x40,0xe0,0x4a,0xfb,0xea,0x24,0x37,0xe6,0x71,0xba,0xee,0x25,0x5c,0x9a,0x6c,0x86,0x60,0x8b,0xb7,0xa2,0x3f,0xc9,0xe7,0x11,0xeb,0x32,0x82,0x90,0x41,0x38,0xb3,0xf9,0x23,0x5d,0xd1,0x02,0x8b,0xe6,0xa5,0x6c,0xfc,0xa3,0x64,0xa6,0x57,0x0f,0xf8,0xde,0x67,0x19,0x2e,0x33,0x3e,0x34,0x56,0xf0,0xc9,0xc6,0x5f,0x77,0x1e,0x25,0x6a,0x59,0x09,0x88,0xdb,0x8b,0x56,0x09,0xf3,0x6c,0x14,0x0f,0x16,0x18,0x64,0x01,0x40,0x94,0xab,0x30,0x44,0xb0,0xa8,0x0b,0x37,0x3f,0x33,0x65,0x3b,0x0c,0x29,0xd5,0xb3,0x80,0xd9,0x72,0x7c,0xf2,0x32,0xc5,0x16,0x79,0x86,0x57,0x98,0x51,0xbb,0xcc,0xc8,0x22,0x88,0x28,0xd5,0xb2,0x58,0xb4,0xb0,0x26,0x7e,0xe6,0x65,0x86,0x07,0x5a,0x30,0x36,0xcc,0xcf,0x79,0xb5,0xbc,0xfa,0xb4,0x50,0x28,0x6a,0x6e,0x3d,0xee,0x31,0xae,0x7a,0x15,0x01,0xb1,0x94,0xe8,0xea,0x35,0x2a,0x6c,0x11,0x00,0x3e,0x87,0xdb,0xc6,0x94,0x1a,0x72,0x96,0x6f,0xd3,0x03,0x83,0x7c,0x42,0x16,0xcb,0x0d,0x36,0xa9,0xd6,0x95,0x9c,0xb1,0xe6,0x99,0xa7,0xe3,0x33,0xc4,0xe8,0xc6,0x9e,0xdd,0x77,0x15,0x8f,0x30,0xcc,0x67,0xf1,0x14,0x9f,0x29,0x33,0xa4,0x1f,0xd4,0xd3,0x75,0x85,0xff,0x62,0x59,0xa0,0x1d,0x96,0x60,0x01,0x05,0xf0,0x79,0xba,0x18,0x16,0x80,0x2c,0xc6,0xf0,0x25,0xfb,0xc2,0x08,0x86,0x69,0x3a,0x0a,0xb5,0x97,0x5b,0xa7,0x1c,0xe2,0xc5,0x38,0xf8,0xd2,0x0e,0x1b,0xa1,0x48,0x29,0x89,0x42,0x2e,0x5f,0xa1,0x11,0xf0,0x8b,0x2f,0x95,0xf1,0x33,0xe1,0xe1,0x72,0x46,0xc2,0x68,0xf8,0x3c,0x3e,0x90,0xfd,0xa6,0x9c,0x71,0x51,0xf1,0x60,0xa6,0xec,0xde,0x7a,0x96,0xea,0xd4,0x33,0xe4,0x05,0x1d,0xc1,0x1c,0x34,0x6f,0x35,0x76,0xef,0x10,0xae,0x7a,0xf4,0x83,0xd8,0xff,0xba,0xa8,0x5e,0x28,0x55,0x8b,0x09,0x3a,0x97,0xc9,0x65,0x2e,0x65,0x51,0xb8,0x13,0x9b,0xa1,0xbf,0x79,0xb7,0x4e,0x17,0x37,0x20,0xde,0x55,0xbb,0x44,0xd2,0x72,0xeb,0xff,0x67,0x79,0xcc,0x73,0xcb,0x4f,0xfd,0x5a,0x9c,0x02,0xed,0xcb,0xe2,0xb7,0x7e,0xe9,0xb7,0x3e,0x61,0xc6,0x9e,0x8b,0x97,0xf5,0xab,0xe9,0x39,0x60,0xd8,0x5b,0xab,0x28,0xf5,0x92,0x47,0x06,0xd0,0x6e,0xfc,0x98,0x12,0xfb,0x1c,0xdd,0xe3,0x17,0xa2,0x8e,0x0e,0x9a,0xae,0x83,0xf2,0x27,0xc3,0xab,0x15,0x11,0xca,0x14,0x15,0x12,0xec,0x37,0x42,0x58,0x59,0x9e,0x32,0xfc,0xf7,0xd3,0x35,0x7c,0x3d,0x40,0xe5,0x8f,0x92,0x6f,0xbf,0x80,0xe8,0x5c,0x62,0x63,0xf1,0x80,0xec,0x48,0x30,0xe7,0xc3,0x5f,0x8c,0xea,0x11,0xb4,0x23,0xa7,0x35,0xdb,0x47,0x1a,0x4b,0x69,0x98,0x2e,0x64,0x79,0x99,0x9c,0x3b,0x06,0x58,0x97,0x2d,0xb3,0x2a,0xff,0xdb,0x56,0xae,0x9d,0x49,0x83,0x43,0xe6,0x99,0xe9,0xea,0x22,0xa1,0xde,0x54,0x17,0xe5,0xaa,0xb4,0xbc,0x0a,0x99,0x9b,0xa8,0x6c,0x19,0xaa,0x8b,0x99,0x3a,0xa1,0x1d,0xdd,0x7e,0x7f,0xfb,0xed,0x4f,0xb7,0xa9,0x22,0x1f,0x0f,0xf9,0xa5,0x14,0x96,0x19,0x61,0xc5,0x48,0x38,0x9f,0x01,0x5d,0x30,0x77,0xc1,0x33,0x11,0x4e,0xee,0x5d,0x91,0x7b,0x51,0x5b,0x75,0x99,0x6d,0x52,0xdb,0x1c,0x03,0x74,0x41,0x77,0x13,0x39,0x86,0xe7,0xee,0x6c,0x77,0x1b,0xad,0x25]; + let ct: [u8; MLKEM512_CT_LEN] = [ + 0x5A, 0x68, 0x40, 0x8E, 0xEB, 0x0E, 0xE7, 0xD1, 0xF5, 0x11, 0x4F, 0x4B, 0xED, 0xAE, 0xB6, + 0x00, 0x48, 0xF5, 0x39, 0x7E, 0xEA, 0x20, 0xCA, 0x22, 0x89, 0xE5, 0xB2, 0xB1, 0xC7, 0x01, + 0x65, 0x49, 0xAC, 0x72, 0x8D, 0xB0, 0x97, 0x74, 0xE1, 0xC6, 0x79, 0x8C, 0xD7, 0xD1, 0x93, + 0xBD, 0x74, 0x94, 0x16, 0xF3, 0x75, 0x2E, 0x9D, 0x75, 0xBB, 0x16, 0x09, 0x01, 0xC6, 0x39, + 0xAB, 0x39, 0x41, 0xB4, 0xE2, 0xB2, 0xE5, 0xA0, 0x68, 0x3D, 0x49, 0xCB, 0xA9, 0xBC, 0xE2, + 0x07, 0xF8, 0x45, 0x8F, 0x9E, 0xF9, 0x54, 0xCE, 0xBB, 0xBF, 0x15, 0xB1, 0x8A, 0x14, 0x8E, + 0x37, 0x05, 0xC6, 0xB1, 0x9B, 0xBA, 0x2E, 0x52, 0x54, 0x69, 0x80, 0x3D, 0x1B, 0x86, 0x11, + 0xE7, 0x43, 0xC8, 0x56, 0x8B, 0x9F, 0x07, 0x77, 0x84, 0x71, 0xDD, 0x0E, 0x6C, 0x3A, 0x00, + 0x76, 0x6C, 0xFA, 0xDF, 0xF1, 0x6D, 0xF4, 0xDB, 0x94, 0x9B, 0x34, 0xF0, 0x86, 0x07, 0x06, + 0x30, 0x3A, 0x32, 0xB6, 0x2A, 0x9B, 0x9D, 0x23, 0x44, 0x69, 0x27, 0x63, 0x4D, 0x02, 0x45, + 0x9F, 0x3B, 0x69, 0xBC, 0x3B, 0xD2, 0xF1, 0x2F, 0x9F, 0x66, 0x00, 0x92, 0x10, 0xCB, 0xE9, + 0xDF, 0xF1, 0x1C, 0xDE, 0x5E, 0x63, 0x3F, 0x37, 0xB4, 0xDC, 0xDA, 0xBE, 0x97, 0x93, 0x0B, + 0x1B, 0xCE, 0xE5, 0x71, 0xE9, 0x55, 0x49, 0xE6, 0xFD, 0x25, 0x54, 0x98, 0xCF, 0x0D, 0x75, + 0x4D, 0xD2, 0x5C, 0x1D, 0xDA, 0xE9, 0x4F, 0xA0, 0x7C, 0xED, 0xC1, 0x64, 0x17, 0xF5, 0x51, + 0x3A, 0xD3, 0x5E, 0x54, 0xAE, 0x67, 0xC6, 0x4D, 0x70, 0x76, 0x00, 0x6D, 0x8B, 0x41, 0x5B, + 0x8E, 0xE8, 0x2E, 0x40, 0xE0, 0x4A, 0xFB, 0xEA, 0x24, 0x37, 0xE6, 0x71, 0xBA, 0xEE, 0x25, + 0x5C, 0x9A, 0x6C, 0x86, 0x60, 0x8B, 0xB7, 0xA2, 0x3F, 0xC9, 0xE7, 0x11, 0xEB, 0x32, 0x82, + 0x90, 0x41, 0x38, 0xB3, 0xF9, 0x23, 0x5D, 0xD1, 0x02, 0x8B, 0xE6, 0xA5, 0x6C, 0xFC, 0xA3, + 0x64, 0xA6, 0x57, 0x0F, 0xF8, 0xDE, 0x67, 0x19, 0x2E, 0x33, 0x3E, 0x34, 0x56, 0xF0, 0xC9, + 0xC6, 0x5F, 0x77, 0x1E, 0x25, 0x6A, 0x59, 0x09, 0x88, 0xDB, 0x8B, 0x56, 0x09, 0xF3, 0x6C, + 0x14, 0x0F, 0x16, 0x18, 0x64, 0x01, 0x40, 0x94, 0xAB, 0x30, 0x44, 0xB0, 0xA8, 0x0B, 0x37, + 0x3F, 0x33, 0x65, 0x3B, 0x0C, 0x29, 0xD5, 0xB3, 0x80, 0xD9, 0x72, 0x7C, 0xF2, 0x32, 0xC5, + 0x16, 0x79, 0x86, 0x57, 0x98, 0x51, 0xBB, 0xCC, 0xC8, 0x22, 0x88, 0x28, 0xD5, 0xB2, 0x58, + 0xB4, 0xB0, 0x26, 0x7E, 0xE6, 0x65, 0x86, 0x07, 0x5A, 0x30, 0x36, 0xCC, 0xCF, 0x79, 0xB5, + 0xBC, 0xFA, 0xB4, 0x50, 0x28, 0x6A, 0x6E, 0x3D, 0xEE, 0x31, 0xAE, 0x7A, 0x15, 0x01, 0xB1, + 0x94, 0xE8, 0xEA, 0x35, 0x2A, 0x6C, 0x11, 0x00, 0x3E, 0x87, 0xDB, 0xC6, 0x94, 0x1A, 0x72, + 0x96, 0x6F, 0xD3, 0x03, 0x83, 0x7C, 0x42, 0x16, 0xCB, 0x0D, 0x36, 0xA9, 0xD6, 0x95, 0x9C, + 0xB1, 0xE6, 0x99, 0xA7, 0xE3, 0x33, 0xC4, 0xE8, 0xC6, 0x9E, 0xDD, 0x77, 0x15, 0x8F, 0x30, + 0xCC, 0x67, 0xF1, 0x14, 0x9F, 0x29, 0x33, 0xA4, 0x1F, 0xD4, 0xD3, 0x75, 0x85, 0xFF, 0x62, + 0x59, 0xA0, 0x1D, 0x96, 0x60, 0x01, 0x05, 0xF0, 0x79, 0xBA, 0x18, 0x16, 0x80, 0x2C, 0xC6, + 0xF0, 0x25, 0xFB, 0xC2, 0x08, 0x86, 0x69, 0x3A, 0x0A, 0xB5, 0x97, 0x5B, 0xA7, 0x1C, 0xE2, + 0xC5, 0x38, 0xF8, 0xD2, 0x0E, 0x1B, 0xA1, 0x48, 0x29, 0x89, 0x42, 0x2E, 0x5F, 0xA1, 0x11, + 0xF0, 0x8B, 0x2F, 0x95, 0xF1, 0x33, 0xE1, 0xE1, 0x72, 0x46, 0xC2, 0x68, 0xF8, 0x3C, 0x3E, + 0x90, 0xFD, 0xA6, 0x9C, 0x71, 0x51, 0xF1, 0x60, 0xA6, 0xEC, 0xDE, 0x7A, 0x96, 0xEA, 0xD4, + 0x33, 0xE4, 0x05, 0x1D, 0xC1, 0x1C, 0x34, 0x6F, 0x35, 0x76, 0xEF, 0x10, 0xAE, 0x7A, 0xF4, + 0x83, 0xD8, 0xFF, 0xBA, 0xA8, 0x5E, 0x28, 0x55, 0x8B, 0x09, 0x3A, 0x97, 0xC9, 0x65, 0x2E, + 0x65, 0x51, 0xB8, 0x13, 0x9B, 0xA1, 0xBF, 0x79, 0xB7, 0x4E, 0x17, 0x37, 0x20, 0xDE, 0x55, + 0xBB, 0x44, 0xD2, 0x72, 0xEB, 0xFF, 0x67, 0x79, 0xCC, 0x73, 0xCB, 0x4F, 0xFD, 0x5A, 0x9C, + 0x02, 0xED, 0xCB, 0xE2, 0xB7, 0x7E, 0xE9, 0xB7, 0x3E, 0x61, 0xC6, 0x9E, 0x8B, 0x97, 0xF5, + 0xAB, 0xE9, 0x39, 0x60, 0xD8, 0x5B, 0xAB, 0x28, 0xF5, 0x92, 0x47, 0x06, 0xD0, 0x6E, 0xFC, + 0x98, 0x12, 0xFB, 0x1C, 0xDD, 0xE3, 0x17, 0xA2, 0x8E, 0x0E, 0x9A, 0xAE, 0x83, 0xF2, 0x27, + 0xC3, 0xAB, 0x15, 0x11, 0xCA, 0x14, 0x15, 0x12, 0xEC, 0x37, 0x42, 0x58, 0x59, 0x9E, 0x32, + 0xFC, 0xF7, 0xD3, 0x35, 0x7C, 0x3D, 0x40, 0xE5, 0x8F, 0x92, 0x6F, 0xBF, 0x80, 0xE8, 0x5C, + 0x62, 0x63, 0xF1, 0x80, 0xEC, 0x48, 0x30, 0xE7, 0xC3, 0x5F, 0x8C, 0xEA, 0x11, 0xB4, 0x23, + 0xA7, 0x35, 0xDB, 0x47, 0x1A, 0x4B, 0x69, 0x98, 0x2E, 0x64, 0x79, 0x99, 0x9C, 0x3B, 0x06, + 0x58, 0x97, 0x2D, 0xB3, 0x2A, 0xFF, 0xDB, 0x56, 0xAE, 0x9D, 0x49, 0x83, 0x43, 0xE6, 0x99, + 0xE9, 0xEA, 0x22, 0xA1, 0xDE, 0x54, 0x17, 0xE5, 0xAA, 0xB4, 0xBC, 0x0A, 0x99, 0x9B, 0xA8, + 0x6C, 0x19, 0xAA, 0x8B, 0x99, 0x3A, 0xA1, 0x1D, 0xDD, 0x7E, 0x7F, 0xFB, 0xED, 0x4F, 0xB7, + 0xA9, 0x22, 0x1F, 0x0F, 0xF9, 0xA5, 0x14, 0x96, 0x19, 0x61, 0xC5, 0x48, 0x38, 0x9F, 0x01, + 0x5D, 0x30, 0x77, 0xC1, 0x33, 0x11, 0x4E, 0xEE, 0x5D, 0x91, 0x7B, 0x51, 0x5B, 0x75, 0x99, + 0x6D, 0x52, 0xDB, 0x1C, 0x03, 0x74, 0x41, 0x77, 0x13, 0x39, 0x86, 0xE7, 0xEE, 0x6C, 0x77, + 0x1B, 0xAD, 0x25, + ]; let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); let ss = MLKEM512::decaps(&sk, &ct).unwrap(); @@ -320,14 +938,21 @@ fn bench_mlkem512_decaps() { } fn bench_mlkem512_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM512}; + use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEMTrait}; eprintln!("MLKEM512_lowmemory/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); @@ -335,7 +960,60 @@ fn bench_mlkem512_lowmemory_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM512_CT_LEN] = [0x5a,0x68,0x40,0x8e,0xeb,0x0e,0xe7,0xd1,0xf5,0x11,0x4f,0x4b,0xed,0xae,0xb6,0x00,0x48,0xf5,0x39,0x7e,0xea,0x20,0xca,0x22,0x89,0xe5,0xb2,0xb1,0xc7,0x01,0x65,0x49,0xac,0x72,0x8d,0xb0,0x97,0x74,0xe1,0xc6,0x79,0x8c,0xd7,0xd1,0x93,0xbd,0x74,0x94,0x16,0xf3,0x75,0x2e,0x9d,0x75,0xbb,0x16,0x09,0x01,0xc6,0x39,0xab,0x39,0x41,0xb4,0xe2,0xb2,0xe5,0xa0,0x68,0x3d,0x49,0xcb,0xa9,0xbc,0xe2,0x07,0xf8,0x45,0x8f,0x9e,0xf9,0x54,0xce,0xbb,0xbf,0x15,0xb1,0x8a,0x14,0x8e,0x37,0x05,0xc6,0xb1,0x9b,0xba,0x2e,0x52,0x54,0x69,0x80,0x3d,0x1b,0x86,0x11,0xe7,0x43,0xc8,0x56,0x8b,0x9f,0x07,0x77,0x84,0x71,0xdd,0x0e,0x6c,0x3a,0x00,0x76,0x6c,0xfa,0xdf,0xf1,0x6d,0xf4,0xdb,0x94,0x9b,0x34,0xf0,0x86,0x07,0x06,0x30,0x3a,0x32,0xb6,0x2a,0x9b,0x9d,0x23,0x44,0x69,0x27,0x63,0x4d,0x02,0x45,0x9f,0x3b,0x69,0xbc,0x3b,0xd2,0xf1,0x2f,0x9f,0x66,0x00,0x92,0x10,0xcb,0xe9,0xdf,0xf1,0x1c,0xde,0x5e,0x63,0x3f,0x37,0xb4,0xdc,0xda,0xbe,0x97,0x93,0x0b,0x1b,0xce,0xe5,0x71,0xe9,0x55,0x49,0xe6,0xfd,0x25,0x54,0x98,0xcf,0x0d,0x75,0x4d,0xd2,0x5c,0x1d,0xda,0xe9,0x4f,0xa0,0x7c,0xed,0xc1,0x64,0x17,0xf5,0x51,0x3a,0xd3,0x5e,0x54,0xae,0x67,0xc6,0x4d,0x70,0x76,0x00,0x6d,0x8b,0x41,0x5b,0x8e,0xe8,0x2e,0x40,0xe0,0x4a,0xfb,0xea,0x24,0x37,0xe6,0x71,0xba,0xee,0x25,0x5c,0x9a,0x6c,0x86,0x60,0x8b,0xb7,0xa2,0x3f,0xc9,0xe7,0x11,0xeb,0x32,0x82,0x90,0x41,0x38,0xb3,0xf9,0x23,0x5d,0xd1,0x02,0x8b,0xe6,0xa5,0x6c,0xfc,0xa3,0x64,0xa6,0x57,0x0f,0xf8,0xde,0x67,0x19,0x2e,0x33,0x3e,0x34,0x56,0xf0,0xc9,0xc6,0x5f,0x77,0x1e,0x25,0x6a,0x59,0x09,0x88,0xdb,0x8b,0x56,0x09,0xf3,0x6c,0x14,0x0f,0x16,0x18,0x64,0x01,0x40,0x94,0xab,0x30,0x44,0xb0,0xa8,0x0b,0x37,0x3f,0x33,0x65,0x3b,0x0c,0x29,0xd5,0xb3,0x80,0xd9,0x72,0x7c,0xf2,0x32,0xc5,0x16,0x79,0x86,0x57,0x98,0x51,0xbb,0xcc,0xc8,0x22,0x88,0x28,0xd5,0xb2,0x58,0xb4,0xb0,0x26,0x7e,0xe6,0x65,0x86,0x07,0x5a,0x30,0x36,0xcc,0xcf,0x79,0xb5,0xbc,0xfa,0xb4,0x50,0x28,0x6a,0x6e,0x3d,0xee,0x31,0xae,0x7a,0x15,0x01,0xb1,0x94,0xe8,0xea,0x35,0x2a,0x6c,0x11,0x00,0x3e,0x87,0xdb,0xc6,0x94,0x1a,0x72,0x96,0x6f,0xd3,0x03,0x83,0x7c,0x42,0x16,0xcb,0x0d,0x36,0xa9,0xd6,0x95,0x9c,0xb1,0xe6,0x99,0xa7,0xe3,0x33,0xc4,0xe8,0xc6,0x9e,0xdd,0x77,0x15,0x8f,0x30,0xcc,0x67,0xf1,0x14,0x9f,0x29,0x33,0xa4,0x1f,0xd4,0xd3,0x75,0x85,0xff,0x62,0x59,0xa0,0x1d,0x96,0x60,0x01,0x05,0xf0,0x79,0xba,0x18,0x16,0x80,0x2c,0xc6,0xf0,0x25,0xfb,0xc2,0x08,0x86,0x69,0x3a,0x0a,0xb5,0x97,0x5b,0xa7,0x1c,0xe2,0xc5,0x38,0xf8,0xd2,0x0e,0x1b,0xa1,0x48,0x29,0x89,0x42,0x2e,0x5f,0xa1,0x11,0xf0,0x8b,0x2f,0x95,0xf1,0x33,0xe1,0xe1,0x72,0x46,0xc2,0x68,0xf8,0x3c,0x3e,0x90,0xfd,0xa6,0x9c,0x71,0x51,0xf1,0x60,0xa6,0xec,0xde,0x7a,0x96,0xea,0xd4,0x33,0xe4,0x05,0x1d,0xc1,0x1c,0x34,0x6f,0x35,0x76,0xef,0x10,0xae,0x7a,0xf4,0x83,0xd8,0xff,0xba,0xa8,0x5e,0x28,0x55,0x8b,0x09,0x3a,0x97,0xc9,0x65,0x2e,0x65,0x51,0xb8,0x13,0x9b,0xa1,0xbf,0x79,0xb7,0x4e,0x17,0x37,0x20,0xde,0x55,0xbb,0x44,0xd2,0x72,0xeb,0xff,0x67,0x79,0xcc,0x73,0xcb,0x4f,0xfd,0x5a,0x9c,0x02,0xed,0xcb,0xe2,0xb7,0x7e,0xe9,0xb7,0x3e,0x61,0xc6,0x9e,0x8b,0x97,0xf5,0xab,0xe9,0x39,0x60,0xd8,0x5b,0xab,0x28,0xf5,0x92,0x47,0x06,0xd0,0x6e,0xfc,0x98,0x12,0xfb,0x1c,0xdd,0xe3,0x17,0xa2,0x8e,0x0e,0x9a,0xae,0x83,0xf2,0x27,0xc3,0xab,0x15,0x11,0xca,0x14,0x15,0x12,0xec,0x37,0x42,0x58,0x59,0x9e,0x32,0xfc,0xf7,0xd3,0x35,0x7c,0x3d,0x40,0xe5,0x8f,0x92,0x6f,0xbf,0x80,0xe8,0x5c,0x62,0x63,0xf1,0x80,0xec,0x48,0x30,0xe7,0xc3,0x5f,0x8c,0xea,0x11,0xb4,0x23,0xa7,0x35,0xdb,0x47,0x1a,0x4b,0x69,0x98,0x2e,0x64,0x79,0x99,0x9c,0x3b,0x06,0x58,0x97,0x2d,0xb3,0x2a,0xff,0xdb,0x56,0xae,0x9d,0x49,0x83,0x43,0xe6,0x99,0xe9,0xea,0x22,0xa1,0xde,0x54,0x17,0xe5,0xaa,0xb4,0xbc,0x0a,0x99,0x9b,0xa8,0x6c,0x19,0xaa,0x8b,0x99,0x3a,0xa1,0x1d,0xdd,0x7e,0x7f,0xfb,0xed,0x4f,0xb7,0xa9,0x22,0x1f,0x0f,0xf9,0xa5,0x14,0x96,0x19,0x61,0xc5,0x48,0x38,0x9f,0x01,0x5d,0x30,0x77,0xc1,0x33,0x11,0x4e,0xee,0x5d,0x91,0x7b,0x51,0x5b,0x75,0x99,0x6d,0x52,0xdb,0x1c,0x03,0x74,0x41,0x77,0x13,0x39,0x86,0xe7,0xee,0x6c,0x77,0x1b,0xad,0x25]; + let ct: [u8; MLKEM512_CT_LEN] = [ + 0x5A, 0x68, 0x40, 0x8E, 0xEB, 0x0E, 0xE7, 0xD1, 0xF5, 0x11, 0x4F, 0x4B, 0xED, 0xAE, 0xB6, + 0x00, 0x48, 0xF5, 0x39, 0x7E, 0xEA, 0x20, 0xCA, 0x22, 0x89, 0xE5, 0xB2, 0xB1, 0xC7, 0x01, + 0x65, 0x49, 0xAC, 0x72, 0x8D, 0xB0, 0x97, 0x74, 0xE1, 0xC6, 0x79, 0x8C, 0xD7, 0xD1, 0x93, + 0xBD, 0x74, 0x94, 0x16, 0xF3, 0x75, 0x2E, 0x9D, 0x75, 0xBB, 0x16, 0x09, 0x01, 0xC6, 0x39, + 0xAB, 0x39, 0x41, 0xB4, 0xE2, 0xB2, 0xE5, 0xA0, 0x68, 0x3D, 0x49, 0xCB, 0xA9, 0xBC, 0xE2, + 0x07, 0xF8, 0x45, 0x8F, 0x9E, 0xF9, 0x54, 0xCE, 0xBB, 0xBF, 0x15, 0xB1, 0x8A, 0x14, 0x8E, + 0x37, 0x05, 0xC6, 0xB1, 0x9B, 0xBA, 0x2E, 0x52, 0x54, 0x69, 0x80, 0x3D, 0x1B, 0x86, 0x11, + 0xE7, 0x43, 0xC8, 0x56, 0x8B, 0x9F, 0x07, 0x77, 0x84, 0x71, 0xDD, 0x0E, 0x6C, 0x3A, 0x00, + 0x76, 0x6C, 0xFA, 0xDF, 0xF1, 0x6D, 0xF4, 0xDB, 0x94, 0x9B, 0x34, 0xF0, 0x86, 0x07, 0x06, + 0x30, 0x3A, 0x32, 0xB6, 0x2A, 0x9B, 0x9D, 0x23, 0x44, 0x69, 0x27, 0x63, 0x4D, 0x02, 0x45, + 0x9F, 0x3B, 0x69, 0xBC, 0x3B, 0xD2, 0xF1, 0x2F, 0x9F, 0x66, 0x00, 0x92, 0x10, 0xCB, 0xE9, + 0xDF, 0xF1, 0x1C, 0xDE, 0x5E, 0x63, 0x3F, 0x37, 0xB4, 0xDC, 0xDA, 0xBE, 0x97, 0x93, 0x0B, + 0x1B, 0xCE, 0xE5, 0x71, 0xE9, 0x55, 0x49, 0xE6, 0xFD, 0x25, 0x54, 0x98, 0xCF, 0x0D, 0x75, + 0x4D, 0xD2, 0x5C, 0x1D, 0xDA, 0xE9, 0x4F, 0xA0, 0x7C, 0xED, 0xC1, 0x64, 0x17, 0xF5, 0x51, + 0x3A, 0xD3, 0x5E, 0x54, 0xAE, 0x67, 0xC6, 0x4D, 0x70, 0x76, 0x00, 0x6D, 0x8B, 0x41, 0x5B, + 0x8E, 0xE8, 0x2E, 0x40, 0xE0, 0x4A, 0xFB, 0xEA, 0x24, 0x37, 0xE6, 0x71, 0xBA, 0xEE, 0x25, + 0x5C, 0x9A, 0x6C, 0x86, 0x60, 0x8B, 0xB7, 0xA2, 0x3F, 0xC9, 0xE7, 0x11, 0xEB, 0x32, 0x82, + 0x90, 0x41, 0x38, 0xB3, 0xF9, 0x23, 0x5D, 0xD1, 0x02, 0x8B, 0xE6, 0xA5, 0x6C, 0xFC, 0xA3, + 0x64, 0xA6, 0x57, 0x0F, 0xF8, 0xDE, 0x67, 0x19, 0x2E, 0x33, 0x3E, 0x34, 0x56, 0xF0, 0xC9, + 0xC6, 0x5F, 0x77, 0x1E, 0x25, 0x6A, 0x59, 0x09, 0x88, 0xDB, 0x8B, 0x56, 0x09, 0xF3, 0x6C, + 0x14, 0x0F, 0x16, 0x18, 0x64, 0x01, 0x40, 0x94, 0xAB, 0x30, 0x44, 0xB0, 0xA8, 0x0B, 0x37, + 0x3F, 0x33, 0x65, 0x3B, 0x0C, 0x29, 0xD5, 0xB3, 0x80, 0xD9, 0x72, 0x7C, 0xF2, 0x32, 0xC5, + 0x16, 0x79, 0x86, 0x57, 0x98, 0x51, 0xBB, 0xCC, 0xC8, 0x22, 0x88, 0x28, 0xD5, 0xB2, 0x58, + 0xB4, 0xB0, 0x26, 0x7E, 0xE6, 0x65, 0x86, 0x07, 0x5A, 0x30, 0x36, 0xCC, 0xCF, 0x79, 0xB5, + 0xBC, 0xFA, 0xB4, 0x50, 0x28, 0x6A, 0x6E, 0x3D, 0xEE, 0x31, 0xAE, 0x7A, 0x15, 0x01, 0xB1, + 0x94, 0xE8, 0xEA, 0x35, 0x2A, 0x6C, 0x11, 0x00, 0x3E, 0x87, 0xDB, 0xC6, 0x94, 0x1A, 0x72, + 0x96, 0x6F, 0xD3, 0x03, 0x83, 0x7C, 0x42, 0x16, 0xCB, 0x0D, 0x36, 0xA9, 0xD6, 0x95, 0x9C, + 0xB1, 0xE6, 0x99, 0xA7, 0xE3, 0x33, 0xC4, 0xE8, 0xC6, 0x9E, 0xDD, 0x77, 0x15, 0x8F, 0x30, + 0xCC, 0x67, 0xF1, 0x14, 0x9F, 0x29, 0x33, 0xA4, 0x1F, 0xD4, 0xD3, 0x75, 0x85, 0xFF, 0x62, + 0x59, 0xA0, 0x1D, 0x96, 0x60, 0x01, 0x05, 0xF0, 0x79, 0xBA, 0x18, 0x16, 0x80, 0x2C, 0xC6, + 0xF0, 0x25, 0xFB, 0xC2, 0x08, 0x86, 0x69, 0x3A, 0x0A, 0xB5, 0x97, 0x5B, 0xA7, 0x1C, 0xE2, + 0xC5, 0x38, 0xF8, 0xD2, 0x0E, 0x1B, 0xA1, 0x48, 0x29, 0x89, 0x42, 0x2E, 0x5F, 0xA1, 0x11, + 0xF0, 0x8B, 0x2F, 0x95, 0xF1, 0x33, 0xE1, 0xE1, 0x72, 0x46, 0xC2, 0x68, 0xF8, 0x3C, 0x3E, + 0x90, 0xFD, 0xA6, 0x9C, 0x71, 0x51, 0xF1, 0x60, 0xA6, 0xEC, 0xDE, 0x7A, 0x96, 0xEA, 0xD4, + 0x33, 0xE4, 0x05, 0x1D, 0xC1, 0x1C, 0x34, 0x6F, 0x35, 0x76, 0xEF, 0x10, 0xAE, 0x7A, 0xF4, + 0x83, 0xD8, 0xFF, 0xBA, 0xA8, 0x5E, 0x28, 0x55, 0x8B, 0x09, 0x3A, 0x97, 0xC9, 0x65, 0x2E, + 0x65, 0x51, 0xB8, 0x13, 0x9B, 0xA1, 0xBF, 0x79, 0xB7, 0x4E, 0x17, 0x37, 0x20, 0xDE, 0x55, + 0xBB, 0x44, 0xD2, 0x72, 0xEB, 0xFF, 0x67, 0x79, 0xCC, 0x73, 0xCB, 0x4F, 0xFD, 0x5A, 0x9C, + 0x02, 0xED, 0xCB, 0xE2, 0xB7, 0x7E, 0xE9, 0xB7, 0x3E, 0x61, 0xC6, 0x9E, 0x8B, 0x97, 0xF5, + 0xAB, 0xE9, 0x39, 0x60, 0xD8, 0x5B, 0xAB, 0x28, 0xF5, 0x92, 0x47, 0x06, 0xD0, 0x6E, 0xFC, + 0x98, 0x12, 0xFB, 0x1C, 0xDD, 0xE3, 0x17, 0xA2, 0x8E, 0x0E, 0x9A, 0xAE, 0x83, 0xF2, 0x27, + 0xC3, 0xAB, 0x15, 0x11, 0xCA, 0x14, 0x15, 0x12, 0xEC, 0x37, 0x42, 0x58, 0x59, 0x9E, 0x32, + 0xFC, 0xF7, 0xD3, 0x35, 0x7C, 0x3D, 0x40, 0xE5, 0x8F, 0x92, 0x6F, 0xBF, 0x80, 0xE8, 0x5C, + 0x62, 0x63, 0xF1, 0x80, 0xEC, 0x48, 0x30, 0xE7, 0xC3, 0x5F, 0x8C, 0xEA, 0x11, 0xB4, 0x23, + 0xA7, 0x35, 0xDB, 0x47, 0x1A, 0x4B, 0x69, 0x98, 0x2E, 0x64, 0x79, 0x99, 0x9C, 0x3B, 0x06, + 0x58, 0x97, 0x2D, 0xB3, 0x2A, 0xFF, 0xDB, 0x56, 0xAE, 0x9D, 0x49, 0x83, 0x43, 0xE6, 0x99, + 0xE9, 0xEA, 0x22, 0xA1, 0xDE, 0x54, 0x17, 0xE5, 0xAA, 0xB4, 0xBC, 0x0A, 0x99, 0x9B, 0xA8, + 0x6C, 0x19, 0xAA, 0x8B, 0x99, 0x3A, 0xA1, 0x1D, 0xDD, 0x7E, 0x7F, 0xFB, 0xED, 0x4F, 0xB7, + 0xA9, 0x22, 0x1F, 0x0F, 0xF9, 0xA5, 0x14, 0x96, 0x19, 0x61, 0xC5, 0x48, 0x38, 0x9F, 0x01, + 0x5D, 0x30, 0x77, 0xC1, 0x33, 0x11, 0x4E, 0xEE, 0x5D, 0x91, 0x7B, 0x51, 0x5B, 0x75, 0x99, + 0x6D, 0x52, 0xDB, 0x1C, 0x03, 0x74, 0x41, 0x77, 0x13, 0x39, 0x86, 0xE7, 0xEE, 0x6C, 0x77, + 0x1B, 0xAD, 0x25, + ]; let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); let ss = MLKEM512::decaps(&sk, &ct).unwrap(); @@ -343,14 +1021,21 @@ fn bench_mlkem512_lowmemory_decaps() { } fn bench_mlkem768_decaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM768}; + use bouncycastle::mlkem::{MLKEM768, MLKEMTrait}; eprintln!("MLKEM768/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); @@ -358,7 +1043,81 @@ fn bench_mlkem768_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM768_CT_LEN] = [0xa8,0xf1,0x73,0xf0,0xf4,0x4b,0xb1,0xc7,0x68,0x12,0x9e,0xf4,0x26,0xea,0x35,0xd1,0x75,0xcf,0x94,0xc5,0xd4,0xae,0x86,0x46,0xcd,0xab,0xef,0x4a,0xa1,0x72,0x78,0x1b,0xbb,0xf8,0x09,0xdc,0x12,0xda,0x50,0x37,0xf6,0x00,0xea,0xf3,0xf7,0xda,0x77,0xa2,0x3d,0xc8,0x46,0x86,0xe6,0xb5,0xec,0xe3,0x72,0xde,0xbd,0xf7,0x8f,0x4b,0x2c,0x06,0xd0,0xea,0x7a,0x8d,0x2a,0xd6,0x78,0x20,0x6f,0x5d,0x51,0x57,0xfd,0x0d,0x7a,0xe0,0x18,0xcb,0x7c,0xba,0xdd,0x42,0x19,0x72,0xf5,0xcd,0xdb,0x86,0xec,0x8e,0x2e,0x4f,0x82,0x83,0x9d,0xc8,0xcc,0x98,0x67,0xec,0x0f,0x2c,0x3b,0x96,0x2f,0x59,0x17,0x7f,0x0f,0x98,0x1d,0x75,0xd8,0xed,0x97,0xfb,0x69,0x53,0xc5,0x5b,0x6a,0xdb,0x4a,0x19,0x60,0x51,0x8c,0xd9,0x59,0x7b,0xd8,0x40,0x1e,0x2d,0xb2,0x03,0xc2,0x69,0xc2,0x61,0x3f,0x0e,0x1f,0xa3,0x2e,0xa0,0x81,0xc4,0x25,0xd8,0x5d,0xfd,0x50,0x97,0x82,0x1f,0x47,0x0c,0x8e,0x99,0x48,0x62,0x4f,0x4b,0x97,0xd7,0x96,0x29,0x2f,0x85,0x0c,0x09,0x95,0xd7,0x72,0x51,0xac,0xc1,0xbb,0xfa,0x76,0x24,0xa5,0x11,0x89,0xe6,0x91,0x18,0x71,0x71,0x05,0x19,0x8b,0x10,0x53,0x1d,0x61,0xad,0x5b,0x13,0x2d,0x0c,0x4c,0x99,0xfa,0x7b,0xc5,0x34,0x4b,0xae,0x1c,0x70,0xd4,0x85,0x4d,0xa8,0x57,0x51,0x57,0xa7,0xd9,0x63,0x0b,0xd9,0x3d,0xa4,0x37,0x21,0xa8,0xf7,0x42,0xd3,0xe1,0x8c,0x8f,0x56,0xf5,0xab,0x31,0x22,0x17,0xe1,0xa7,0xa9,0xc7,0x7a,0x34,0xb7,0x1e,0x23,0x80,0x23,0x1a,0x2a,0x6c,0x9c,0xef,0xcb,0x40,0x04,0x00,0x37,0x26,0xac,0x03,0x69,0xe1,0xdd,0xc7,0x53,0xf3,0xd4,0x3a,0xde,0x56,0x5d,0xff,0x11,0xb7,0x9b,0xf0,0x82,0xc7,0x68,0x47,0xd7,0x4f,0xce,0xeb,0x07,0xa7,0x70,0x6b,0x94,0x91,0x17,0xf3,0xb7,0xfa,0x41,0x46,0xce,0x40,0xc6,0x50,0x51,0xbb,0x5b,0x82,0xce,0x27,0x1c,0x95,0xfd,0x77,0xd6,0xef,0xaa,0x24,0xd4,0x47,0x65,0xcc,0xbc,0xda,0xd6,0xdf,0xb5,0xb5,0x5d,0x01,0x42,0xb6,0x2f,0x91,0x3a,0x41,0x8a,0x82,0x83,0x4c,0x56,0xac,0x70,0x6c,0x40,0x05,0x00,0xe4,0x67,0x9e,0x64,0x24,0xc5,0x33,0xa0,0xb0,0xec,0x6a,0x3b,0x08,0x4c,0x12,0x40,0x3e,0x47,0x1f,0x7a,0x74,0x34,0x37,0xd7,0xc2,0x20,0x3a,0xca,0xeb,0xcf,0x22,0x87,0x23,0x89,0xda,0x78,0x6e,0xff,0xf0,0x7e,0x57,0x99,0x6b,0x0c,0xa8,0x95,0x8f,0xe1,0x90,0x81,0xe6,0x9e,0xc9,0x63,0x91,0xb2,0x61,0xda,0x0a,0x90,0xb0,0x99,0xa1,0xf9,0xdb,0x12,0x08,0xac,0xfc,0x5f,0x0f,0xf1,0xa9,0x04,0xef,0xee,0xa0,0xfe,0x2c,0x42,0x26,0x00,0xa0,0x3f,0xb5,0xa7,0x44,0x19,0x2e,0x0c,0x50,0xf7,0x0f,0x84,0x76,0xf0,0x5c,0x3a,0xb6,0xcb,0x8c,0xd9,0xa2,0x12,0x1c,0x2f,0x4f,0xb9,0x3b,0x53,0x44,0x89,0xf7,0x5d,0xb6,0x97,0xb4,0x80,0x4a,0x44,0xaa,0x3e,0x15,0x8b,0xa1,0x5f,0x0b,0x3c,0x8d,0x3b,0xe0,0x69,0x83,0xd4,0x57,0xdc,0x93,0xa9,0x6a,0x84,0x04,0x53,0x41,0x89,0x59,0xfe,0x5f,0xb6,0xdf,0xc0,0x6a,0x3b,0x2c,0xd3,0xae,0x3e,0x48,0x3e,0x1e,0xe0,0xac,0x74,0x98,0xda,0x0a,0x70,0x65,0xb9,0x46,0x48,0x80,0x64,0x89,0xb6,0xce,0x9f,0xfd,0xce,0x53,0xb8,0x61,0xa0,0x7f,0xed,0xbc,0xa7,0x9b,0x1b,0xc8,0x9b,0xe7,0xb5,0xa2,0x58,0x76,0x5e,0x2c,0xc9,0x09,0xc4,0x89,0x30,0xda,0x50,0xb2,0x38,0xfd,0x6c,0x95,0x7e,0x90,0xab,0x53,0x6a,0x27,0xfa,0x63,0xf5,0xfe,0xa3,0xe2,0x9a,0x2e,0x7a,0x33,0xbf,0x9f,0xb9,0x0e,0xb3,0x8d,0xee,0x0d,0xc2,0xa6,0xd9,0x2f,0x9c,0xec,0x0e,0x78,0xa0,0xd0,0x96,0x47,0xb6,0x51,0x62,0x25,0x07,0x3e,0x6b,0x35,0x00,0xa1,0x49,0xb6,0xe4,0x27,0x27,0x33,0xe4,0xe0,0x5e,0xa8,0x4a,0x0a,0x45,0x67,0x2f,0x62,0xe9,0xa5,0x4f,0xa1,0x76,0xee,0xa6,0x10,0x22,0xd4,0xfc,0xda,0x55,0x3d,0x5b,0xaa,0xb1,0x84,0xd0,0x8a,0xf4,0xb8,0x06,0xbc,0x40,0xc4,0x22,0x9d,0xaf,0x7b,0x5f,0x64,0x4f,0x1a,0xc8,0xd0,0xd4,0xa1,0xa4,0x8a,0xe6,0xb6,0xf2,0xbf,0xaa,0xe5,0xce,0x5a,0xc4,0x6c,0x8b,0x4f,0x1a,0xc8,0x41,0xf9,0xc8,0x2b,0x57,0xb4,0x37,0xd0,0x91,0x7b,0x80,0x5c,0x46,0x88,0x05,0xa3,0x06,0xe9,0x2f,0xb1,0x68,0xaf,0xa8,0xbd,0x34,0xef,0x41,0xeb,0x57,0x57,0x70,0x72,0xd8,0x56,0x83,0xac,0xbe,0x72,0xac,0x9d,0x06,0x10,0x95,0xe5,0xf4,0xd8,0xba,0x1a,0xb7,0x91,0xf9,0xc2,0xb9,0x24,0xc8,0x1e,0xe5,0x33,0xd5,0x81,0x1e,0x15,0x54,0x5e,0xbb,0x01,0x92,0xe8,0xec,0x4e,0xec,0x4e,0xa4,0x95,0x28,0x9a,0x27,0xfd,0x67,0xe5,0x20,0xcf,0x72,0xd7,0xd5,0x95,0x14,0xc0,0x35,0x94,0x66,0x78,0x96,0xed,0x41,0x11,0xf1,0xb2,0x1f,0x0a,0xed,0x19,0xbb,0xf9,0xca,0x4b,0xcb,0x54,0x68,0xe3,0x09,0xca,0xa7,0x45,0xf4,0x0c,0xbf,0x25,0x41,0xa2,0x70,0xa6,0xd6,0x05,0x2b,0x7e,0xd7,0x1e,0x83,0x32,0x8e,0xac,0x0a,0xa3,0xde,0x03,0xeb,0x7c,0x9a,0xde,0x04,0x74,0xab,0x0e,0x42,0x67,0xfb,0xae,0xe5,0xb6,0x8d,0xac,0x44,0x66,0x55,0xdd,0xbb,0x70,0xaf,0x44,0xc1,0x90,0xfb,0x16,0xa0,0x8e,0x98,0xdb,0xf6,0x36,0x94,0xf7,0x04,0xe0,0x7f,0x2b,0xdc,0xee,0x5a,0xbd,0xb6,0x05,0x4e,0x78,0xb5,0x54,0x41,0x22,0xcc,0x93,0xa0,0xbc,0xcd,0x4a,0x8a,0x89,0xc6,0x3c,0x30,0x7b,0x1f,0x4c,0x7d,0xc4,0x2c,0x65,0xfa,0xb5,0xd8,0x95,0x76,0x1b,0x88,0xad,0xc6,0x4a,0x99,0x53,0xc1,0xe5,0x96,0xe0,0x05,0x54,0xd8,0x64,0xd1,0x00,0x2e,0xc6,0x53,0xe8,0x5a,0xf1,0x71,0x72,0x6a,0x6b,0xc2,0x32,0xf0,0xae,0x08,0x1d,0x63,0x76,0xa9,0xa5,0xa0,0x57,0x34,0x6e,0x77,0x54,0x2f,0x73,0x4d,0x1b,0x12,0x7d,0x5d,0xbf,0xae,0x8f,0x5d,0x50,0xbb,0x0d,0x05,0x22,0x9b,0xe2,0xc5,0xd8,0x40,0x3d,0xdb,0xa6,0xdf,0x8f,0xba,0x23,0xa6,0x46,0x0a,0x4c,0x65,0x5d,0x9f,0xbb,0xed,0xff,0x60,0x3a,0x7e,0xa9,0xa4,0xff,0xb5,0x83,0xcf,0xb8,0x95,0xdb,0x37,0x54,0x35,0xdd,0x31,0xf2,0x9f,0x2a,0x8a,0xa2,0x33,0x3b,0x9b,0x48,0x4f,0x33,0xc9,0x70,0xe8,0x07,0xbb,0xd6,0x24,0xeb,0x6f,0xae,0x02,0x32,0x04,0x07,0x6f,0x30,0x9d,0x75,0xb9,0x44,0x73,0x11,0xee,0x16,0x89,0xcf,0x2f,0x01,0x40,0x7b,0xba,0x1f,0x44,0x5e,0xbf,0xbb,0x3e,0x1b,0x3e,0x1b,0x57,0xc1,0x69,0xc0,0x71,0xdd,0x40,0x3c,0x46,0x21,0xcd,0x76,0x61,0x64,0xc5,0x5e,0xdf,0x0f,0xed,0x3b,0xe1,0xff,0xec,0x76,0x46,0x9f,0x86,0x8b,0x88,0xf6,0x53,0xad,0x20,0x4d,0x07,0x0a,0x9e,0x17,0x30,0x49,0xdd,0x4d,0x00,0xbd,0x63,0xe1,0xee,0xde,0xbd,0x6d,0x15]; + let ct: [u8; MLKEM768_CT_LEN] = [ + 0xA8, 0xF1, 0x73, 0xF0, 0xF4, 0x4B, 0xB1, 0xC7, 0x68, 0x12, 0x9E, 0xF4, 0x26, 0xEA, 0x35, + 0xD1, 0x75, 0xCF, 0x94, 0xC5, 0xD4, 0xAE, 0x86, 0x46, 0xCD, 0xAB, 0xEF, 0x4A, 0xA1, 0x72, + 0x78, 0x1B, 0xBB, 0xF8, 0x09, 0xDC, 0x12, 0xDA, 0x50, 0x37, 0xF6, 0x00, 0xEA, 0xF3, 0xF7, + 0xDA, 0x77, 0xA2, 0x3D, 0xC8, 0x46, 0x86, 0xE6, 0xB5, 0xEC, 0xE3, 0x72, 0xDE, 0xBD, 0xF7, + 0x8F, 0x4B, 0x2C, 0x06, 0xD0, 0xEA, 0x7A, 0x8D, 0x2A, 0xD6, 0x78, 0x20, 0x6F, 0x5D, 0x51, + 0x57, 0xFD, 0x0D, 0x7A, 0xE0, 0x18, 0xCB, 0x7C, 0xBA, 0xDD, 0x42, 0x19, 0x72, 0xF5, 0xCD, + 0xDB, 0x86, 0xEC, 0x8E, 0x2E, 0x4F, 0x82, 0x83, 0x9D, 0xC8, 0xCC, 0x98, 0x67, 0xEC, 0x0F, + 0x2C, 0x3B, 0x96, 0x2F, 0x59, 0x17, 0x7F, 0x0F, 0x98, 0x1D, 0x75, 0xD8, 0xED, 0x97, 0xFB, + 0x69, 0x53, 0xC5, 0x5B, 0x6A, 0xDB, 0x4A, 0x19, 0x60, 0x51, 0x8C, 0xD9, 0x59, 0x7B, 0xD8, + 0x40, 0x1E, 0x2D, 0xB2, 0x03, 0xC2, 0x69, 0xC2, 0x61, 0x3F, 0x0E, 0x1F, 0xA3, 0x2E, 0xA0, + 0x81, 0xC4, 0x25, 0xD8, 0x5D, 0xFD, 0x50, 0x97, 0x82, 0x1F, 0x47, 0x0C, 0x8E, 0x99, 0x48, + 0x62, 0x4F, 0x4B, 0x97, 0xD7, 0x96, 0x29, 0x2F, 0x85, 0x0C, 0x09, 0x95, 0xD7, 0x72, 0x51, + 0xAC, 0xC1, 0xBB, 0xFA, 0x76, 0x24, 0xA5, 0x11, 0x89, 0xE6, 0x91, 0x18, 0x71, 0x71, 0x05, + 0x19, 0x8B, 0x10, 0x53, 0x1D, 0x61, 0xAD, 0x5B, 0x13, 0x2D, 0x0C, 0x4C, 0x99, 0xFA, 0x7B, + 0xC5, 0x34, 0x4B, 0xAE, 0x1C, 0x70, 0xD4, 0x85, 0x4D, 0xA8, 0x57, 0x51, 0x57, 0xA7, 0xD9, + 0x63, 0x0B, 0xD9, 0x3D, 0xA4, 0x37, 0x21, 0xA8, 0xF7, 0x42, 0xD3, 0xE1, 0x8C, 0x8F, 0x56, + 0xF5, 0xAB, 0x31, 0x22, 0x17, 0xE1, 0xA7, 0xA9, 0xC7, 0x7A, 0x34, 0xB7, 0x1E, 0x23, 0x80, + 0x23, 0x1A, 0x2A, 0x6C, 0x9C, 0xEF, 0xCB, 0x40, 0x04, 0x00, 0x37, 0x26, 0xAC, 0x03, 0x69, + 0xE1, 0xDD, 0xC7, 0x53, 0xF3, 0xD4, 0x3A, 0xDE, 0x56, 0x5D, 0xFF, 0x11, 0xB7, 0x9B, 0xF0, + 0x82, 0xC7, 0x68, 0x47, 0xD7, 0x4F, 0xCE, 0xEB, 0x07, 0xA7, 0x70, 0x6B, 0x94, 0x91, 0x17, + 0xF3, 0xB7, 0xFA, 0x41, 0x46, 0xCE, 0x40, 0xC6, 0x50, 0x51, 0xBB, 0x5B, 0x82, 0xCE, 0x27, + 0x1C, 0x95, 0xFD, 0x77, 0xD6, 0xEF, 0xAA, 0x24, 0xD4, 0x47, 0x65, 0xCC, 0xBC, 0xDA, 0xD6, + 0xDF, 0xB5, 0xB5, 0x5D, 0x01, 0x42, 0xB6, 0x2F, 0x91, 0x3A, 0x41, 0x8A, 0x82, 0x83, 0x4C, + 0x56, 0xAC, 0x70, 0x6C, 0x40, 0x05, 0x00, 0xE4, 0x67, 0x9E, 0x64, 0x24, 0xC5, 0x33, 0xA0, + 0xB0, 0xEC, 0x6A, 0x3B, 0x08, 0x4C, 0x12, 0x40, 0x3E, 0x47, 0x1F, 0x7A, 0x74, 0x34, 0x37, + 0xD7, 0xC2, 0x20, 0x3A, 0xCA, 0xEB, 0xCF, 0x22, 0x87, 0x23, 0x89, 0xDA, 0x78, 0x6E, 0xFF, + 0xF0, 0x7E, 0x57, 0x99, 0x6B, 0x0C, 0xA8, 0x95, 0x8F, 0xE1, 0x90, 0x81, 0xE6, 0x9E, 0xC9, + 0x63, 0x91, 0xB2, 0x61, 0xDA, 0x0A, 0x90, 0xB0, 0x99, 0xA1, 0xF9, 0xDB, 0x12, 0x08, 0xAC, + 0xFC, 0x5F, 0x0F, 0xF1, 0xA9, 0x04, 0xEF, 0xEE, 0xA0, 0xFE, 0x2C, 0x42, 0x26, 0x00, 0xA0, + 0x3F, 0xB5, 0xA7, 0x44, 0x19, 0x2E, 0x0C, 0x50, 0xF7, 0x0F, 0x84, 0x76, 0xF0, 0x5C, 0x3A, + 0xB6, 0xCB, 0x8C, 0xD9, 0xA2, 0x12, 0x1C, 0x2F, 0x4F, 0xB9, 0x3B, 0x53, 0x44, 0x89, 0xF7, + 0x5D, 0xB6, 0x97, 0xB4, 0x80, 0x4A, 0x44, 0xAA, 0x3E, 0x15, 0x8B, 0xA1, 0x5F, 0x0B, 0x3C, + 0x8D, 0x3B, 0xE0, 0x69, 0x83, 0xD4, 0x57, 0xDC, 0x93, 0xA9, 0x6A, 0x84, 0x04, 0x53, 0x41, + 0x89, 0x59, 0xFE, 0x5F, 0xB6, 0xDF, 0xC0, 0x6A, 0x3B, 0x2C, 0xD3, 0xAE, 0x3E, 0x48, 0x3E, + 0x1E, 0xE0, 0xAC, 0x74, 0x98, 0xDA, 0x0A, 0x70, 0x65, 0xB9, 0x46, 0x48, 0x80, 0x64, 0x89, + 0xB6, 0xCE, 0x9F, 0xFD, 0xCE, 0x53, 0xB8, 0x61, 0xA0, 0x7F, 0xED, 0xBC, 0xA7, 0x9B, 0x1B, + 0xC8, 0x9B, 0xE7, 0xB5, 0xA2, 0x58, 0x76, 0x5E, 0x2C, 0xC9, 0x09, 0xC4, 0x89, 0x30, 0xDA, + 0x50, 0xB2, 0x38, 0xFD, 0x6C, 0x95, 0x7E, 0x90, 0xAB, 0x53, 0x6A, 0x27, 0xFA, 0x63, 0xF5, + 0xFE, 0xA3, 0xE2, 0x9A, 0x2E, 0x7A, 0x33, 0xBF, 0x9F, 0xB9, 0x0E, 0xB3, 0x8D, 0xEE, 0x0D, + 0xC2, 0xA6, 0xD9, 0x2F, 0x9C, 0xEC, 0x0E, 0x78, 0xA0, 0xD0, 0x96, 0x47, 0xB6, 0x51, 0x62, + 0x25, 0x07, 0x3E, 0x6B, 0x35, 0x00, 0xA1, 0x49, 0xB6, 0xE4, 0x27, 0x27, 0x33, 0xE4, 0xE0, + 0x5E, 0xA8, 0x4A, 0x0A, 0x45, 0x67, 0x2F, 0x62, 0xE9, 0xA5, 0x4F, 0xA1, 0x76, 0xEE, 0xA6, + 0x10, 0x22, 0xD4, 0xFC, 0xDA, 0x55, 0x3D, 0x5B, 0xAA, 0xB1, 0x84, 0xD0, 0x8A, 0xF4, 0xB8, + 0x06, 0xBC, 0x40, 0xC4, 0x22, 0x9D, 0xAF, 0x7B, 0x5F, 0x64, 0x4F, 0x1A, 0xC8, 0xD0, 0xD4, + 0xA1, 0xA4, 0x8A, 0xE6, 0xB6, 0xF2, 0xBF, 0xAA, 0xE5, 0xCE, 0x5A, 0xC4, 0x6C, 0x8B, 0x4F, + 0x1A, 0xC8, 0x41, 0xF9, 0xC8, 0x2B, 0x57, 0xB4, 0x37, 0xD0, 0x91, 0x7B, 0x80, 0x5C, 0x46, + 0x88, 0x05, 0xA3, 0x06, 0xE9, 0x2F, 0xB1, 0x68, 0xAF, 0xA8, 0xBD, 0x34, 0xEF, 0x41, 0xEB, + 0x57, 0x57, 0x70, 0x72, 0xD8, 0x56, 0x83, 0xAC, 0xBE, 0x72, 0xAC, 0x9D, 0x06, 0x10, 0x95, + 0xE5, 0xF4, 0xD8, 0xBA, 0x1A, 0xB7, 0x91, 0xF9, 0xC2, 0xB9, 0x24, 0xC8, 0x1E, 0xE5, 0x33, + 0xD5, 0x81, 0x1E, 0x15, 0x54, 0x5E, 0xBB, 0x01, 0x92, 0xE8, 0xEC, 0x4E, 0xEC, 0x4E, 0xA4, + 0x95, 0x28, 0x9A, 0x27, 0xFD, 0x67, 0xE5, 0x20, 0xCF, 0x72, 0xD7, 0xD5, 0x95, 0x14, 0xC0, + 0x35, 0x94, 0x66, 0x78, 0x96, 0xED, 0x41, 0x11, 0xF1, 0xB2, 0x1F, 0x0A, 0xED, 0x19, 0xBB, + 0xF9, 0xCA, 0x4B, 0xCB, 0x54, 0x68, 0xE3, 0x09, 0xCA, 0xA7, 0x45, 0xF4, 0x0C, 0xBF, 0x25, + 0x41, 0xA2, 0x70, 0xA6, 0xD6, 0x05, 0x2B, 0x7E, 0xD7, 0x1E, 0x83, 0x32, 0x8E, 0xAC, 0x0A, + 0xA3, 0xDE, 0x03, 0xEB, 0x7C, 0x9A, 0xDE, 0x04, 0x74, 0xAB, 0x0E, 0x42, 0x67, 0xFB, 0xAE, + 0xE5, 0xB6, 0x8D, 0xAC, 0x44, 0x66, 0x55, 0xDD, 0xBB, 0x70, 0xAF, 0x44, 0xC1, 0x90, 0xFB, + 0x16, 0xA0, 0x8E, 0x98, 0xDB, 0xF6, 0x36, 0x94, 0xF7, 0x04, 0xE0, 0x7F, 0x2B, 0xDC, 0xEE, + 0x5A, 0xBD, 0xB6, 0x05, 0x4E, 0x78, 0xB5, 0x54, 0x41, 0x22, 0xCC, 0x93, 0xA0, 0xBC, 0xCD, + 0x4A, 0x8A, 0x89, 0xC6, 0x3C, 0x30, 0x7B, 0x1F, 0x4C, 0x7D, 0xC4, 0x2C, 0x65, 0xFA, 0xB5, + 0xD8, 0x95, 0x76, 0x1B, 0x88, 0xAD, 0xC6, 0x4A, 0x99, 0x53, 0xC1, 0xE5, 0x96, 0xE0, 0x05, + 0x54, 0xD8, 0x64, 0xD1, 0x00, 0x2E, 0xC6, 0x53, 0xE8, 0x5A, 0xF1, 0x71, 0x72, 0x6A, 0x6B, + 0xC2, 0x32, 0xF0, 0xAE, 0x08, 0x1D, 0x63, 0x76, 0xA9, 0xA5, 0xA0, 0x57, 0x34, 0x6E, 0x77, + 0x54, 0x2F, 0x73, 0x4D, 0x1B, 0x12, 0x7D, 0x5D, 0xBF, 0xAE, 0x8F, 0x5D, 0x50, 0xBB, 0x0D, + 0x05, 0x22, 0x9B, 0xE2, 0xC5, 0xD8, 0x40, 0x3D, 0xDB, 0xA6, 0xDF, 0x8F, 0xBA, 0x23, 0xA6, + 0x46, 0x0A, 0x4C, 0x65, 0x5D, 0x9F, 0xBB, 0xED, 0xFF, 0x60, 0x3A, 0x7E, 0xA9, 0xA4, 0xFF, + 0xB5, 0x83, 0xCF, 0xB8, 0x95, 0xDB, 0x37, 0x54, 0x35, 0xDD, 0x31, 0xF2, 0x9F, 0x2A, 0x8A, + 0xA2, 0x33, 0x3B, 0x9B, 0x48, 0x4F, 0x33, 0xC9, 0x70, 0xE8, 0x07, 0xBB, 0xD6, 0x24, 0xEB, + 0x6F, 0xAE, 0x02, 0x32, 0x04, 0x07, 0x6F, 0x30, 0x9D, 0x75, 0xB9, 0x44, 0x73, 0x11, 0xEE, + 0x16, 0x89, 0xCF, 0x2F, 0x01, 0x40, 0x7B, 0xBA, 0x1F, 0x44, 0x5E, 0xBF, 0xBB, 0x3E, 0x1B, + 0x3E, 0x1B, 0x57, 0xC1, 0x69, 0xC0, 0x71, 0xDD, 0x40, 0x3C, 0x46, 0x21, 0xCD, 0x76, 0x61, + 0x64, 0xC5, 0x5E, 0xDF, 0x0F, 0xED, 0x3B, 0xE1, 0xFF, 0xEC, 0x76, 0x46, 0x9F, 0x86, 0x8B, + 0x88, 0xF6, 0x53, 0xAD, 0x20, 0x4D, 0x07, 0x0A, 0x9E, 0x17, 0x30, 0x49, 0xDD, 0x4D, 0x00, + 0xBD, 0x63, 0xE1, 0xEE, 0xDE, 0xBD, 0x6D, 0x15, + ]; let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); let ss = MLKEM768::decaps(&sk, &ct).unwrap(); @@ -366,14 +1125,21 @@ fn bench_mlkem768_decaps() { } fn bench_mlkem768_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM768}; + use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEMTrait}; eprintln!("MLKEM768_lowmemory/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); @@ -381,7 +1147,81 @@ fn bench_mlkem768_lowmemory_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM768_CT_LEN] = [0xa8,0xf1,0x73,0xf0,0xf4,0x4b,0xb1,0xc7,0x68,0x12,0x9e,0xf4,0x26,0xea,0x35,0xd1,0x75,0xcf,0x94,0xc5,0xd4,0xae,0x86,0x46,0xcd,0xab,0xef,0x4a,0xa1,0x72,0x78,0x1b,0xbb,0xf8,0x09,0xdc,0x12,0xda,0x50,0x37,0xf6,0x00,0xea,0xf3,0xf7,0xda,0x77,0xa2,0x3d,0xc8,0x46,0x86,0xe6,0xb5,0xec,0xe3,0x72,0xde,0xbd,0xf7,0x8f,0x4b,0x2c,0x06,0xd0,0xea,0x7a,0x8d,0x2a,0xd6,0x78,0x20,0x6f,0x5d,0x51,0x57,0xfd,0x0d,0x7a,0xe0,0x18,0xcb,0x7c,0xba,0xdd,0x42,0x19,0x72,0xf5,0xcd,0xdb,0x86,0xec,0x8e,0x2e,0x4f,0x82,0x83,0x9d,0xc8,0xcc,0x98,0x67,0xec,0x0f,0x2c,0x3b,0x96,0x2f,0x59,0x17,0x7f,0x0f,0x98,0x1d,0x75,0xd8,0xed,0x97,0xfb,0x69,0x53,0xc5,0x5b,0x6a,0xdb,0x4a,0x19,0x60,0x51,0x8c,0xd9,0x59,0x7b,0xd8,0x40,0x1e,0x2d,0xb2,0x03,0xc2,0x69,0xc2,0x61,0x3f,0x0e,0x1f,0xa3,0x2e,0xa0,0x81,0xc4,0x25,0xd8,0x5d,0xfd,0x50,0x97,0x82,0x1f,0x47,0x0c,0x8e,0x99,0x48,0x62,0x4f,0x4b,0x97,0xd7,0x96,0x29,0x2f,0x85,0x0c,0x09,0x95,0xd7,0x72,0x51,0xac,0xc1,0xbb,0xfa,0x76,0x24,0xa5,0x11,0x89,0xe6,0x91,0x18,0x71,0x71,0x05,0x19,0x8b,0x10,0x53,0x1d,0x61,0xad,0x5b,0x13,0x2d,0x0c,0x4c,0x99,0xfa,0x7b,0xc5,0x34,0x4b,0xae,0x1c,0x70,0xd4,0x85,0x4d,0xa8,0x57,0x51,0x57,0xa7,0xd9,0x63,0x0b,0xd9,0x3d,0xa4,0x37,0x21,0xa8,0xf7,0x42,0xd3,0xe1,0x8c,0x8f,0x56,0xf5,0xab,0x31,0x22,0x17,0xe1,0xa7,0xa9,0xc7,0x7a,0x34,0xb7,0x1e,0x23,0x80,0x23,0x1a,0x2a,0x6c,0x9c,0xef,0xcb,0x40,0x04,0x00,0x37,0x26,0xac,0x03,0x69,0xe1,0xdd,0xc7,0x53,0xf3,0xd4,0x3a,0xde,0x56,0x5d,0xff,0x11,0xb7,0x9b,0xf0,0x82,0xc7,0x68,0x47,0xd7,0x4f,0xce,0xeb,0x07,0xa7,0x70,0x6b,0x94,0x91,0x17,0xf3,0xb7,0xfa,0x41,0x46,0xce,0x40,0xc6,0x50,0x51,0xbb,0x5b,0x82,0xce,0x27,0x1c,0x95,0xfd,0x77,0xd6,0xef,0xaa,0x24,0xd4,0x47,0x65,0xcc,0xbc,0xda,0xd6,0xdf,0xb5,0xb5,0x5d,0x01,0x42,0xb6,0x2f,0x91,0x3a,0x41,0x8a,0x82,0x83,0x4c,0x56,0xac,0x70,0x6c,0x40,0x05,0x00,0xe4,0x67,0x9e,0x64,0x24,0xc5,0x33,0xa0,0xb0,0xec,0x6a,0x3b,0x08,0x4c,0x12,0x40,0x3e,0x47,0x1f,0x7a,0x74,0x34,0x37,0xd7,0xc2,0x20,0x3a,0xca,0xeb,0xcf,0x22,0x87,0x23,0x89,0xda,0x78,0x6e,0xff,0xf0,0x7e,0x57,0x99,0x6b,0x0c,0xa8,0x95,0x8f,0xe1,0x90,0x81,0xe6,0x9e,0xc9,0x63,0x91,0xb2,0x61,0xda,0x0a,0x90,0xb0,0x99,0xa1,0xf9,0xdb,0x12,0x08,0xac,0xfc,0x5f,0x0f,0xf1,0xa9,0x04,0xef,0xee,0xa0,0xfe,0x2c,0x42,0x26,0x00,0xa0,0x3f,0xb5,0xa7,0x44,0x19,0x2e,0x0c,0x50,0xf7,0x0f,0x84,0x76,0xf0,0x5c,0x3a,0xb6,0xcb,0x8c,0xd9,0xa2,0x12,0x1c,0x2f,0x4f,0xb9,0x3b,0x53,0x44,0x89,0xf7,0x5d,0xb6,0x97,0xb4,0x80,0x4a,0x44,0xaa,0x3e,0x15,0x8b,0xa1,0x5f,0x0b,0x3c,0x8d,0x3b,0xe0,0x69,0x83,0xd4,0x57,0xdc,0x93,0xa9,0x6a,0x84,0x04,0x53,0x41,0x89,0x59,0xfe,0x5f,0xb6,0xdf,0xc0,0x6a,0x3b,0x2c,0xd3,0xae,0x3e,0x48,0x3e,0x1e,0xe0,0xac,0x74,0x98,0xda,0x0a,0x70,0x65,0xb9,0x46,0x48,0x80,0x64,0x89,0xb6,0xce,0x9f,0xfd,0xce,0x53,0xb8,0x61,0xa0,0x7f,0xed,0xbc,0xa7,0x9b,0x1b,0xc8,0x9b,0xe7,0xb5,0xa2,0x58,0x76,0x5e,0x2c,0xc9,0x09,0xc4,0x89,0x30,0xda,0x50,0xb2,0x38,0xfd,0x6c,0x95,0x7e,0x90,0xab,0x53,0x6a,0x27,0xfa,0x63,0xf5,0xfe,0xa3,0xe2,0x9a,0x2e,0x7a,0x33,0xbf,0x9f,0xb9,0x0e,0xb3,0x8d,0xee,0x0d,0xc2,0xa6,0xd9,0x2f,0x9c,0xec,0x0e,0x78,0xa0,0xd0,0x96,0x47,0xb6,0x51,0x62,0x25,0x07,0x3e,0x6b,0x35,0x00,0xa1,0x49,0xb6,0xe4,0x27,0x27,0x33,0xe4,0xe0,0x5e,0xa8,0x4a,0x0a,0x45,0x67,0x2f,0x62,0xe9,0xa5,0x4f,0xa1,0x76,0xee,0xa6,0x10,0x22,0xd4,0xfc,0xda,0x55,0x3d,0x5b,0xaa,0xb1,0x84,0xd0,0x8a,0xf4,0xb8,0x06,0xbc,0x40,0xc4,0x22,0x9d,0xaf,0x7b,0x5f,0x64,0x4f,0x1a,0xc8,0xd0,0xd4,0xa1,0xa4,0x8a,0xe6,0xb6,0xf2,0xbf,0xaa,0xe5,0xce,0x5a,0xc4,0x6c,0x8b,0x4f,0x1a,0xc8,0x41,0xf9,0xc8,0x2b,0x57,0xb4,0x37,0xd0,0x91,0x7b,0x80,0x5c,0x46,0x88,0x05,0xa3,0x06,0xe9,0x2f,0xb1,0x68,0xaf,0xa8,0xbd,0x34,0xef,0x41,0xeb,0x57,0x57,0x70,0x72,0xd8,0x56,0x83,0xac,0xbe,0x72,0xac,0x9d,0x06,0x10,0x95,0xe5,0xf4,0xd8,0xba,0x1a,0xb7,0x91,0xf9,0xc2,0xb9,0x24,0xc8,0x1e,0xe5,0x33,0xd5,0x81,0x1e,0x15,0x54,0x5e,0xbb,0x01,0x92,0xe8,0xec,0x4e,0xec,0x4e,0xa4,0x95,0x28,0x9a,0x27,0xfd,0x67,0xe5,0x20,0xcf,0x72,0xd7,0xd5,0x95,0x14,0xc0,0x35,0x94,0x66,0x78,0x96,0xed,0x41,0x11,0xf1,0xb2,0x1f,0x0a,0xed,0x19,0xbb,0xf9,0xca,0x4b,0xcb,0x54,0x68,0xe3,0x09,0xca,0xa7,0x45,0xf4,0x0c,0xbf,0x25,0x41,0xa2,0x70,0xa6,0xd6,0x05,0x2b,0x7e,0xd7,0x1e,0x83,0x32,0x8e,0xac,0x0a,0xa3,0xde,0x03,0xeb,0x7c,0x9a,0xde,0x04,0x74,0xab,0x0e,0x42,0x67,0xfb,0xae,0xe5,0xb6,0x8d,0xac,0x44,0x66,0x55,0xdd,0xbb,0x70,0xaf,0x44,0xc1,0x90,0xfb,0x16,0xa0,0x8e,0x98,0xdb,0xf6,0x36,0x94,0xf7,0x04,0xe0,0x7f,0x2b,0xdc,0xee,0x5a,0xbd,0xb6,0x05,0x4e,0x78,0xb5,0x54,0x41,0x22,0xcc,0x93,0xa0,0xbc,0xcd,0x4a,0x8a,0x89,0xc6,0x3c,0x30,0x7b,0x1f,0x4c,0x7d,0xc4,0x2c,0x65,0xfa,0xb5,0xd8,0x95,0x76,0x1b,0x88,0xad,0xc6,0x4a,0x99,0x53,0xc1,0xe5,0x96,0xe0,0x05,0x54,0xd8,0x64,0xd1,0x00,0x2e,0xc6,0x53,0xe8,0x5a,0xf1,0x71,0x72,0x6a,0x6b,0xc2,0x32,0xf0,0xae,0x08,0x1d,0x63,0x76,0xa9,0xa5,0xa0,0x57,0x34,0x6e,0x77,0x54,0x2f,0x73,0x4d,0x1b,0x12,0x7d,0x5d,0xbf,0xae,0x8f,0x5d,0x50,0xbb,0x0d,0x05,0x22,0x9b,0xe2,0xc5,0xd8,0x40,0x3d,0xdb,0xa6,0xdf,0x8f,0xba,0x23,0xa6,0x46,0x0a,0x4c,0x65,0x5d,0x9f,0xbb,0xed,0xff,0x60,0x3a,0x7e,0xa9,0xa4,0xff,0xb5,0x83,0xcf,0xb8,0x95,0xdb,0x37,0x54,0x35,0xdd,0x31,0xf2,0x9f,0x2a,0x8a,0xa2,0x33,0x3b,0x9b,0x48,0x4f,0x33,0xc9,0x70,0xe8,0x07,0xbb,0xd6,0x24,0xeb,0x6f,0xae,0x02,0x32,0x04,0x07,0x6f,0x30,0x9d,0x75,0xb9,0x44,0x73,0x11,0xee,0x16,0x89,0xcf,0x2f,0x01,0x40,0x7b,0xba,0x1f,0x44,0x5e,0xbf,0xbb,0x3e,0x1b,0x3e,0x1b,0x57,0xc1,0x69,0xc0,0x71,0xdd,0x40,0x3c,0x46,0x21,0xcd,0x76,0x61,0x64,0xc5,0x5e,0xdf,0x0f,0xed,0x3b,0xe1,0xff,0xec,0x76,0x46,0x9f,0x86,0x8b,0x88,0xf6,0x53,0xad,0x20,0x4d,0x07,0x0a,0x9e,0x17,0x30,0x49,0xdd,0x4d,0x00,0xbd,0x63,0xe1,0xee,0xde,0xbd,0x6d,0x15]; + let ct: [u8; MLKEM768_CT_LEN] = [ + 0xA8, 0xF1, 0x73, 0xF0, 0xF4, 0x4B, 0xB1, 0xC7, 0x68, 0x12, 0x9E, 0xF4, 0x26, 0xEA, 0x35, + 0xD1, 0x75, 0xCF, 0x94, 0xC5, 0xD4, 0xAE, 0x86, 0x46, 0xCD, 0xAB, 0xEF, 0x4A, 0xA1, 0x72, + 0x78, 0x1B, 0xBB, 0xF8, 0x09, 0xDC, 0x12, 0xDA, 0x50, 0x37, 0xF6, 0x00, 0xEA, 0xF3, 0xF7, + 0xDA, 0x77, 0xA2, 0x3D, 0xC8, 0x46, 0x86, 0xE6, 0xB5, 0xEC, 0xE3, 0x72, 0xDE, 0xBD, 0xF7, + 0x8F, 0x4B, 0x2C, 0x06, 0xD0, 0xEA, 0x7A, 0x8D, 0x2A, 0xD6, 0x78, 0x20, 0x6F, 0x5D, 0x51, + 0x57, 0xFD, 0x0D, 0x7A, 0xE0, 0x18, 0xCB, 0x7C, 0xBA, 0xDD, 0x42, 0x19, 0x72, 0xF5, 0xCD, + 0xDB, 0x86, 0xEC, 0x8E, 0x2E, 0x4F, 0x82, 0x83, 0x9D, 0xC8, 0xCC, 0x98, 0x67, 0xEC, 0x0F, + 0x2C, 0x3B, 0x96, 0x2F, 0x59, 0x17, 0x7F, 0x0F, 0x98, 0x1D, 0x75, 0xD8, 0xED, 0x97, 0xFB, + 0x69, 0x53, 0xC5, 0x5B, 0x6A, 0xDB, 0x4A, 0x19, 0x60, 0x51, 0x8C, 0xD9, 0x59, 0x7B, 0xD8, + 0x40, 0x1E, 0x2D, 0xB2, 0x03, 0xC2, 0x69, 0xC2, 0x61, 0x3F, 0x0E, 0x1F, 0xA3, 0x2E, 0xA0, + 0x81, 0xC4, 0x25, 0xD8, 0x5D, 0xFD, 0x50, 0x97, 0x82, 0x1F, 0x47, 0x0C, 0x8E, 0x99, 0x48, + 0x62, 0x4F, 0x4B, 0x97, 0xD7, 0x96, 0x29, 0x2F, 0x85, 0x0C, 0x09, 0x95, 0xD7, 0x72, 0x51, + 0xAC, 0xC1, 0xBB, 0xFA, 0x76, 0x24, 0xA5, 0x11, 0x89, 0xE6, 0x91, 0x18, 0x71, 0x71, 0x05, + 0x19, 0x8B, 0x10, 0x53, 0x1D, 0x61, 0xAD, 0x5B, 0x13, 0x2D, 0x0C, 0x4C, 0x99, 0xFA, 0x7B, + 0xC5, 0x34, 0x4B, 0xAE, 0x1C, 0x70, 0xD4, 0x85, 0x4D, 0xA8, 0x57, 0x51, 0x57, 0xA7, 0xD9, + 0x63, 0x0B, 0xD9, 0x3D, 0xA4, 0x37, 0x21, 0xA8, 0xF7, 0x42, 0xD3, 0xE1, 0x8C, 0x8F, 0x56, + 0xF5, 0xAB, 0x31, 0x22, 0x17, 0xE1, 0xA7, 0xA9, 0xC7, 0x7A, 0x34, 0xB7, 0x1E, 0x23, 0x80, + 0x23, 0x1A, 0x2A, 0x6C, 0x9C, 0xEF, 0xCB, 0x40, 0x04, 0x00, 0x37, 0x26, 0xAC, 0x03, 0x69, + 0xE1, 0xDD, 0xC7, 0x53, 0xF3, 0xD4, 0x3A, 0xDE, 0x56, 0x5D, 0xFF, 0x11, 0xB7, 0x9B, 0xF0, + 0x82, 0xC7, 0x68, 0x47, 0xD7, 0x4F, 0xCE, 0xEB, 0x07, 0xA7, 0x70, 0x6B, 0x94, 0x91, 0x17, + 0xF3, 0xB7, 0xFA, 0x41, 0x46, 0xCE, 0x40, 0xC6, 0x50, 0x51, 0xBB, 0x5B, 0x82, 0xCE, 0x27, + 0x1C, 0x95, 0xFD, 0x77, 0xD6, 0xEF, 0xAA, 0x24, 0xD4, 0x47, 0x65, 0xCC, 0xBC, 0xDA, 0xD6, + 0xDF, 0xB5, 0xB5, 0x5D, 0x01, 0x42, 0xB6, 0x2F, 0x91, 0x3A, 0x41, 0x8A, 0x82, 0x83, 0x4C, + 0x56, 0xAC, 0x70, 0x6C, 0x40, 0x05, 0x00, 0xE4, 0x67, 0x9E, 0x64, 0x24, 0xC5, 0x33, 0xA0, + 0xB0, 0xEC, 0x6A, 0x3B, 0x08, 0x4C, 0x12, 0x40, 0x3E, 0x47, 0x1F, 0x7A, 0x74, 0x34, 0x37, + 0xD7, 0xC2, 0x20, 0x3A, 0xCA, 0xEB, 0xCF, 0x22, 0x87, 0x23, 0x89, 0xDA, 0x78, 0x6E, 0xFF, + 0xF0, 0x7E, 0x57, 0x99, 0x6B, 0x0C, 0xA8, 0x95, 0x8F, 0xE1, 0x90, 0x81, 0xE6, 0x9E, 0xC9, + 0x63, 0x91, 0xB2, 0x61, 0xDA, 0x0A, 0x90, 0xB0, 0x99, 0xA1, 0xF9, 0xDB, 0x12, 0x08, 0xAC, + 0xFC, 0x5F, 0x0F, 0xF1, 0xA9, 0x04, 0xEF, 0xEE, 0xA0, 0xFE, 0x2C, 0x42, 0x26, 0x00, 0xA0, + 0x3F, 0xB5, 0xA7, 0x44, 0x19, 0x2E, 0x0C, 0x50, 0xF7, 0x0F, 0x84, 0x76, 0xF0, 0x5C, 0x3A, + 0xB6, 0xCB, 0x8C, 0xD9, 0xA2, 0x12, 0x1C, 0x2F, 0x4F, 0xB9, 0x3B, 0x53, 0x44, 0x89, 0xF7, + 0x5D, 0xB6, 0x97, 0xB4, 0x80, 0x4A, 0x44, 0xAA, 0x3E, 0x15, 0x8B, 0xA1, 0x5F, 0x0B, 0x3C, + 0x8D, 0x3B, 0xE0, 0x69, 0x83, 0xD4, 0x57, 0xDC, 0x93, 0xA9, 0x6A, 0x84, 0x04, 0x53, 0x41, + 0x89, 0x59, 0xFE, 0x5F, 0xB6, 0xDF, 0xC0, 0x6A, 0x3B, 0x2C, 0xD3, 0xAE, 0x3E, 0x48, 0x3E, + 0x1E, 0xE0, 0xAC, 0x74, 0x98, 0xDA, 0x0A, 0x70, 0x65, 0xB9, 0x46, 0x48, 0x80, 0x64, 0x89, + 0xB6, 0xCE, 0x9F, 0xFD, 0xCE, 0x53, 0xB8, 0x61, 0xA0, 0x7F, 0xED, 0xBC, 0xA7, 0x9B, 0x1B, + 0xC8, 0x9B, 0xE7, 0xB5, 0xA2, 0x58, 0x76, 0x5E, 0x2C, 0xC9, 0x09, 0xC4, 0x89, 0x30, 0xDA, + 0x50, 0xB2, 0x38, 0xFD, 0x6C, 0x95, 0x7E, 0x90, 0xAB, 0x53, 0x6A, 0x27, 0xFA, 0x63, 0xF5, + 0xFE, 0xA3, 0xE2, 0x9A, 0x2E, 0x7A, 0x33, 0xBF, 0x9F, 0xB9, 0x0E, 0xB3, 0x8D, 0xEE, 0x0D, + 0xC2, 0xA6, 0xD9, 0x2F, 0x9C, 0xEC, 0x0E, 0x78, 0xA0, 0xD0, 0x96, 0x47, 0xB6, 0x51, 0x62, + 0x25, 0x07, 0x3E, 0x6B, 0x35, 0x00, 0xA1, 0x49, 0xB6, 0xE4, 0x27, 0x27, 0x33, 0xE4, 0xE0, + 0x5E, 0xA8, 0x4A, 0x0A, 0x45, 0x67, 0x2F, 0x62, 0xE9, 0xA5, 0x4F, 0xA1, 0x76, 0xEE, 0xA6, + 0x10, 0x22, 0xD4, 0xFC, 0xDA, 0x55, 0x3D, 0x5B, 0xAA, 0xB1, 0x84, 0xD0, 0x8A, 0xF4, 0xB8, + 0x06, 0xBC, 0x40, 0xC4, 0x22, 0x9D, 0xAF, 0x7B, 0x5F, 0x64, 0x4F, 0x1A, 0xC8, 0xD0, 0xD4, + 0xA1, 0xA4, 0x8A, 0xE6, 0xB6, 0xF2, 0xBF, 0xAA, 0xE5, 0xCE, 0x5A, 0xC4, 0x6C, 0x8B, 0x4F, + 0x1A, 0xC8, 0x41, 0xF9, 0xC8, 0x2B, 0x57, 0xB4, 0x37, 0xD0, 0x91, 0x7B, 0x80, 0x5C, 0x46, + 0x88, 0x05, 0xA3, 0x06, 0xE9, 0x2F, 0xB1, 0x68, 0xAF, 0xA8, 0xBD, 0x34, 0xEF, 0x41, 0xEB, + 0x57, 0x57, 0x70, 0x72, 0xD8, 0x56, 0x83, 0xAC, 0xBE, 0x72, 0xAC, 0x9D, 0x06, 0x10, 0x95, + 0xE5, 0xF4, 0xD8, 0xBA, 0x1A, 0xB7, 0x91, 0xF9, 0xC2, 0xB9, 0x24, 0xC8, 0x1E, 0xE5, 0x33, + 0xD5, 0x81, 0x1E, 0x15, 0x54, 0x5E, 0xBB, 0x01, 0x92, 0xE8, 0xEC, 0x4E, 0xEC, 0x4E, 0xA4, + 0x95, 0x28, 0x9A, 0x27, 0xFD, 0x67, 0xE5, 0x20, 0xCF, 0x72, 0xD7, 0xD5, 0x95, 0x14, 0xC0, + 0x35, 0x94, 0x66, 0x78, 0x96, 0xED, 0x41, 0x11, 0xF1, 0xB2, 0x1F, 0x0A, 0xED, 0x19, 0xBB, + 0xF9, 0xCA, 0x4B, 0xCB, 0x54, 0x68, 0xE3, 0x09, 0xCA, 0xA7, 0x45, 0xF4, 0x0C, 0xBF, 0x25, + 0x41, 0xA2, 0x70, 0xA6, 0xD6, 0x05, 0x2B, 0x7E, 0xD7, 0x1E, 0x83, 0x32, 0x8E, 0xAC, 0x0A, + 0xA3, 0xDE, 0x03, 0xEB, 0x7C, 0x9A, 0xDE, 0x04, 0x74, 0xAB, 0x0E, 0x42, 0x67, 0xFB, 0xAE, + 0xE5, 0xB6, 0x8D, 0xAC, 0x44, 0x66, 0x55, 0xDD, 0xBB, 0x70, 0xAF, 0x44, 0xC1, 0x90, 0xFB, + 0x16, 0xA0, 0x8E, 0x98, 0xDB, 0xF6, 0x36, 0x94, 0xF7, 0x04, 0xE0, 0x7F, 0x2B, 0xDC, 0xEE, + 0x5A, 0xBD, 0xB6, 0x05, 0x4E, 0x78, 0xB5, 0x54, 0x41, 0x22, 0xCC, 0x93, 0xA0, 0xBC, 0xCD, + 0x4A, 0x8A, 0x89, 0xC6, 0x3C, 0x30, 0x7B, 0x1F, 0x4C, 0x7D, 0xC4, 0x2C, 0x65, 0xFA, 0xB5, + 0xD8, 0x95, 0x76, 0x1B, 0x88, 0xAD, 0xC6, 0x4A, 0x99, 0x53, 0xC1, 0xE5, 0x96, 0xE0, 0x05, + 0x54, 0xD8, 0x64, 0xD1, 0x00, 0x2E, 0xC6, 0x53, 0xE8, 0x5A, 0xF1, 0x71, 0x72, 0x6A, 0x6B, + 0xC2, 0x32, 0xF0, 0xAE, 0x08, 0x1D, 0x63, 0x76, 0xA9, 0xA5, 0xA0, 0x57, 0x34, 0x6E, 0x77, + 0x54, 0x2F, 0x73, 0x4D, 0x1B, 0x12, 0x7D, 0x5D, 0xBF, 0xAE, 0x8F, 0x5D, 0x50, 0xBB, 0x0D, + 0x05, 0x22, 0x9B, 0xE2, 0xC5, 0xD8, 0x40, 0x3D, 0xDB, 0xA6, 0xDF, 0x8F, 0xBA, 0x23, 0xA6, + 0x46, 0x0A, 0x4C, 0x65, 0x5D, 0x9F, 0xBB, 0xED, 0xFF, 0x60, 0x3A, 0x7E, 0xA9, 0xA4, 0xFF, + 0xB5, 0x83, 0xCF, 0xB8, 0x95, 0xDB, 0x37, 0x54, 0x35, 0xDD, 0x31, 0xF2, 0x9F, 0x2A, 0x8A, + 0xA2, 0x33, 0x3B, 0x9B, 0x48, 0x4F, 0x33, 0xC9, 0x70, 0xE8, 0x07, 0xBB, 0xD6, 0x24, 0xEB, + 0x6F, 0xAE, 0x02, 0x32, 0x04, 0x07, 0x6F, 0x30, 0x9D, 0x75, 0xB9, 0x44, 0x73, 0x11, 0xEE, + 0x16, 0x89, 0xCF, 0x2F, 0x01, 0x40, 0x7B, 0xBA, 0x1F, 0x44, 0x5E, 0xBF, 0xBB, 0x3E, 0x1B, + 0x3E, 0x1B, 0x57, 0xC1, 0x69, 0xC0, 0x71, 0xDD, 0x40, 0x3C, 0x46, 0x21, 0xCD, 0x76, 0x61, + 0x64, 0xC5, 0x5E, 0xDF, 0x0F, 0xED, 0x3B, 0xE1, 0xFF, 0xEC, 0x76, 0x46, 0x9F, 0x86, 0x8B, + 0x88, 0xF6, 0x53, 0xAD, 0x20, 0x4D, 0x07, 0x0A, 0x9E, 0x17, 0x30, 0x49, 0xDD, 0x4D, 0x00, + 0xBD, 0x63, 0xE1, 0xEE, 0xDE, 0xBD, 0x6D, 0x15, + ]; let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); let ss = MLKEM768::decaps(&sk, &ct).unwrap(); @@ -389,14 +1229,21 @@ fn bench_mlkem768_lowmemory_decaps() { } fn bench_mlkem1024_decaps() { - use bouncycastle::mlkem::{MLKEMTrait, MLKEM1024}; + use bouncycastle::mlkem::{MLKEM1024, MLKEMTrait}; eprintln!("MLKEM1024/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); @@ -404,7 +1251,113 @@ fn bench_mlkem1024_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM1024_CT_LEN] = [0x6b,0x15,0x08,0x82,0x0e,0x30,0x0e,0x5c,0xfe,0xb3,0xd9,0xd3,0x33,0xf2,0xdf,0x3f,0x6a,0x91,0xb7,0x40,0x07,0x61,0xf0,0x7b,0xf6,0x83,0xf8,0xed,0xcd,0xbe,0x14,0xa0,0xac,0x3f,0x32,0x26,0x0b,0x9b,0x7e,0x74,0xe9,0xa8,0x6e,0x44,0xd5,0x3b,0xa5,0x3b,0xb9,0x1c,0x9a,0x9d,0x4e,0x15,0x7f,0xe5,0x82,0x11,0x5f,0xba,0x56,0x8c,0x29,0x2f,0x2b,0xb9,0x62,0xaa,0x20,0x50,0x41,0xe8,0xd6,0x9d,0xb7,0xa9,0xd7,0x5e,0xaa,0xaa,0xa0,0x21,0xe4,0x5b,0xca,0x83,0x2c,0xa1,0x5b,0xb4,0x4e,0xba,0xa7,0x0e,0x8b,0x95,0x83,0x9b,0xce,0x92,0xf0,0x7c,0x2e,0x29,0xfc,0xca,0x0d,0x7b,0x94,0x00,0x6f,0x99,0x44,0x31,0x40,0x0b,0xf7,0xd6,0xb9,0x34,0x8d,0x2f,0x8a,0x3f,0x57,0x8b,0x52,0x16,0x47,0x50,0xe4,0xaa,0x7d,0x8d,0xda,0xda,0xc1,0x80,0x23,0x21,0x7c,0x26,0xfb,0x4e,0x3a,0xb0,0xec,0x34,0xc3,0x23,0x5d,0xe1,0x2b,0x2b,0xfa,0xf2,0xc3,0x36,0x89,0xd4,0xa5,0x3f,0xe3,0x13,0xcb,0xd2,0x8a,0xbb,0x9d,0xbe,0x23,0x0a,0x35,0x9e,0x41,0x21,0xed,0x9f,0x98,0xf1,0x2f,0x62,0xd1,0x4a,0x9f,0xe8,0x6c,0x46,0x60,0x2f,0xa7,0xc9,0x5d,0x25,0xba,0xcd,0x2c,0xda,0x33,0xd5,0x0b,0xa7,0xb5,0xe2,0xc0,0xb2,0x98,0x17,0x09,0x35,0x8d,0x8f,0xc4,0xe8,0x9e,0x63,0x29,0x67,0xf7,0x88,0xb9,0x34,0xe8,0x9c,0x93,0xbd,0x19,0xcb,0x62,0xd7,0x73,0x7f,0x56,0x5a,0x53,0x25,0xa0,0xc0,0x54,0xf4,0xc4,0xfb,0x27,0x9b,0x1b,0x8f,0x63,0xc2,0x1f,0xd3,0x68,0x0c,0xb8,0x0d,0x35,0x2a,0xc2,0xa9,0x03,0x57,0x24,0x32,0x00,0xa7,0xaa,0xe1,0x60,0xe1,0x26,0xa5,0xbe,0x5f,0xf8,0xf6,0xed,0x4f,0xab,0x22,0x30,0xd9,0x8e,0x1b,0x28,0xfd,0xa6,0xb9,0xd9,0xc5,0xe4,0xdf,0x18,0x85,0xed,0x12,0x54,0x2d,0x98,0xcb,0x1c,0xf0,0xec,0x95,0x9f,0x52,0xb9,0xb7,0x09,0xe6,0xb2,0x8c,0xf1,0xe5,0xa1,0x67,0x23,0x32,0xc8,0x78,0xba,0xf1,0xfd,0x8e,0x5f,0x49,0x1b,0x06,0xae,0xcf,0x5a,0x5d,0x2a,0xd2,0x4c,0x97,0x34,0xf3,0x50,0x08,0xe2,0xdd,0xf6,0x1e,0x4c,0x4f,0x4b,0xdf,0x48,0x42,0x6b,0xbc,0x63,0xde,0x9a,0xe7,0xee,0x5a,0x5d,0x67,0xf3,0x51,0x30,0x27,0xbd,0x68,0xc8,0xc3,0x7f,0x90,0x5b,0x75,0xc1,0x39,0x7d,0x38,0xcc,0xdf,0xe6,0x08,0x79,0xfc,0xbe,0xbe,0xe2,0xce,0xbf,0x19,0x12,0x80,0xb0,0xb8,0xaf,0xba,0xca,0x3d,0x1e,0x03,0xf2,0xaa,0x91,0x11,0x16,0x39,0x34,0xe1,0x28,0xe6,0xea,0x1c,0xc2,0x15,0xb7,0xf3,0x6f,0xb9,0x9c,0x2d,0xaa,0x27,0x25,0xed,0x3a,0x77,0xe9,0x30,0x4f,0x18,0x12,0x0a,0x19,0x78,0x97,0x0c,0xa7,0x60,0x5c,0x8a,0x3c,0x67,0xef,0x3f,0xd3,0x1b,0x02,0x7b,0x06,0x2a,0x35,0x8e,0x32,0xfa,0x48,0xe4,0xb0,0xef,0x31,0xd1,0x1f,0xe0,0x5e,0x8e,0x71,0x0e,0x9a,0x60,0x08,0xb5,0x4b,0xd6,0xac,0x93,0x78,0xc9,0x8c,0x62,0x7b,0x12,0x59,0x82,0x3d,0x6f,0x3e,0xf4,0xf0,0xef,0x03,0x81,0x4e,0x15,0xed,0xde,0x93,0xf7,0x34,0x84,0xe9,0x48,0x27,0x3d,0x76,0x64,0xbf,0xe6,0x60,0x47,0xfc,0x31,0xc4,0x68,0xc8,0x77,0x46,0x78,0xd2,0x7b,0x16,0xb1,0xc0,0xda,0xb8,0x80,0xd1,0xd7,0x16,0xd4,0xdb,0x28,0x89,0x49,0xe3,0x35,0x11,0x17,0x90,0xf0,0xe6,0x6d,0x65,0x29,0xb7,0x2a,0x9b,0x18,0x2f,0xe0,0xb4,0x37,0xaa,0xac,0x2b,0x1f,0xfc,0x09,0x79,0xc1,0x48,0x5c,0x0e,0x16,0x38,0x36,0xc3,0xa5,0xce,0x49,0xc5,0xc8,0x88,0xd3,0xef,0xc8,0xdf,0xd6,0xbc,0x91,0x73,0x9b,0xad,0x0a,0xdd,0x04,0xd9,0x20,0xd3,0x88,0x42,0x28,0x80,0x3a,0xaa,0x2a,0x23,0xd9,0xad,0x75,0xd5,0xd3,0x9b,0x40,0x0e,0x04,0xc9,0xed,0xc2,0x91,0x69,0x7b,0xb4,0xc7,0x1a,0x22,0x91,0xec,0xd8,0x95,0x8b,0x02,0xf0,0x41,0x56,0x11,0x61,0x37,0x68,0x2d,0xd0,0xf3,0x84,0x93,0x14,0x3c,0x91,0x1c,0xf8,0x72,0xf5,0x39,0x8f,0x07,0x83,0x7d,0xf5,0x20,0x1b,0xf6,0x6a,0x06,0xc1,0x4a,0xe2,0x60,0x45,0x69,0xd4,0x27,0xe9,0xdc,0x15,0xb3,0x01,0xa3,0xa9,0xfc,0x7c,0xfb,0xa5,0x02,0x64,0xfc,0x73,0x79,0xe2,0xba,0x83,0xee,0xee,0x68,0xdc,0x33,0x52,0x87,0x74,0xba,0x67,0xfc,0x1e,0x51,0xed,0x3b,0xc4,0x08,0x1b,0xdd,0x3b,0x18,0x16,0xd4,0x0f,0x61,0xbb,0xcf,0x72,0x11,0xd9,0xf4,0x12,0xe1,0x22,0x7f,0x3f,0xbc,0x5d,0x04,0x76,0xf0,0x32,0x86,0x45,0x4e,0xab,0xd5,0xd2,0xa2,0x25,0xaa,0x27,0xa9,0x61,0x61,0x6e,0x24,0xe7,0x53,0x2f,0x28,0x03,0x06,0x64,0xdf,0xc2,0xec,0xbc,0xc5,0xbe,0x77,0x9a,0xf9,0x39,0x92,0xe6,0x5d,0x1c,0x85,0xe2,0x10,0x68,0x8f,0x2b,0x6b,0x91,0xcc,0xc9,0xf1,0xdb,0x32,0x6d,0xd9,0x47,0x53,0xc3,0xaa,0xae,0xd4,0x57,0x1c,0x20,0x4f,0x97,0xc9,0xe1,0x87,0x1f,0xfe,0xcd,0xee,0xf0,0x7b,0x88,0xde,0x4f,0x17,0xf3,0xce,0x40,0xa5,0x8d,0x01,0x5f,0xbe,0xdb,0x8a,0x01,0x1c,0x61,0x8f,0xbb,0x03,0x99,0x87,0x63,0xbc,0x60,0xe7,0x8c,0x3c,0x27,0xab,0xd3,0x70,0x3d,0x67,0xb8,0x6e,0x3a,0x82,0x84,0xfe,0x67,0xd9,0xeb,0x59,0xfc,0x21,0x69,0x39,0x29,0x15,0x7f,0xdb,0x0a,0xe4,0x37,0xa7,0x36,0xc2,0xcf,0xb5,0xb6,0x14,0xd9,0xb8,0x47,0x63,0x5c,0x1b,0x8d,0x04,0x75,0x7c,0xa6,0x2f,0x15,0xf4,0xd5,0x36,0xc7,0x2d,0x7a,0x71,0xd7,0x41,0xd8,0xa0,0x7d,0xd6,0xa8,0x28,0x90,0xb8,0xe1,0xf5,0xad,0x59,0xb1,0x7e,0x5c,0x85,0xfd,0xd7,0x08,0x17,0x76,0xef,0xaa,0x0e,0x29,0x17,0x14,0xde,0xb9,0x79,0xd5,0xe5,0x44,0xbd,0x8b,0xe9,0xf7,0x9a,0x87,0x7e,0x4b,0xad,0x28,0x24,0x6a,0x39,0x33,0x00,0x56,0x97,0x00,0x4b,0xfb,0xe3,0x24,0x23,0xaa,0xda,0x98,0x02,0xc5,0x6f,0xe1,0xa6,0x53,0xa5,0xcb,0x95,0x7d,0x47,0x20,0xf5,0x5d,0x5f,0x40,0x10,0xdb,0x40,0x22,0xa2,0xca,0x49,0xec,0xf5,0xe7,0xdd,0x01,0x45,0xe5,0x82,0xa9,0xbc,0xeb,0x77,0x3a,0x45,0x09,0x7b,0x29,0x9a,0x3b,0x0d,0xc4,0x3b,0x34,0x5d,0x16,0x37,0xe8,0x8f,0xc1,0xd8,0x3e,0x41,0x4e,0x24,0xa0,0xf6,0xbd,0x97,0x75,0x73,0xef,0x52,0x3b,0xba,0x8b,0xa9,0x86,0x45,0x15,0xac,0x71,0xec,0xa5,0xdf,0xef,0xda,0x02,0x3c,0xad,0x07,0xbd,0x88,0x74,0x2b,0x52,0x1f,0x63,0x6b,0xa7,0x08,0xfb,0xa4,0x20,0x81,0x48,0x22,0xae,0x91,0x32,0x75,0xa8,0x51,0xb9,0x0f,0x34,0x62,0xb6,0x4b,0xf9,0x88,0xf8,0xba,0x27,0x18,0xb4,0x9f,0x78,0xca,0xb5,0xba,0xc4,0x9f,0x02,0xcb,0x50,0xf3,0x82,0x1c,0x3a,0x35,0x6d,0x99,0x45,0xc4,0xba,0x8d,0xba,0xb7,0x47,0x0c,0x97,0x79,0x68,0xf7,0xb0,0xcd,0x66,0xb0,0x7b,0xfa,0xcb,0x39,0x7c,0x97,0x38,0x9e,0xb4,0x01,0xc9,0xe7,0x85,0xfa,0x7a,0x98,0x0e,0x89,0xc2,0xf8,0xab,0x0d,0x8a,0xd9,0x1f,0xa2,0xbe,0xf2,0x52,0xa3,0x79,0x25,0x21,0x89,0xf1,0x53,0x7c,0x40,0x4e,0xa3,0x18,0x5a,0xd8,0xf1,0xab,0xf1,0x72,0xce,0x31,0x01,0x5f,0xa5,0xdc,0x4e,0x9a,0xe5,0x44,0x66,0x7a,0xa2,0x12,0x98,0xf0,0x1d,0xb5,0xd9,0xe2,0xc5,0x52,0xc2,0x85,0xd0,0x7a,0x8b,0xc9,0xf5,0xe7,0x69,0xef,0x42,0x73,0x2f,0x08,0x69,0x0c,0xca,0x61,0xd0,0x61,0x34,0x53,0x4e,0x07,0x00,0xef,0x4c,0xc7,0x82,0x70,0x2f,0x30,0x0e,0xb9,0x4a,0x0f,0xf4,0x06,0x5c,0x11,0xff,0x13,0xcf,0x2a,0x3e,0x2d,0x1f,0x85,0x7f,0x3e,0x8e,0xb5,0xdc,0x70,0x51,0xf3,0x1f,0xd6,0xeb,0xf2,0x56,0xa1,0xc5,0xa4,0xee,0x72,0x62,0xb2,0x42,0xdf,0x5f,0xa4,0x6d,0xce,0x43,0x7d,0x20,0xfd,0xf8,0x11,0x82,0xfe,0xac,0x60,0x8a,0xaf,0x51,0x5b,0xfa,0xed,0xa5,0x24,0xc8,0xab,0x1c,0xca,0xc2,0x5e,0x0b,0x76,0x81,0xe3,0x85,0x64,0x5c,0xcf,0xf5,0x33,0x73,0x1e,0x7d,0x11,0x2e,0x22,0x15,0xb3,0x88,0x85,0x52,0x43,0x31,0x54,0x94,0xc6,0x27,0x48,0x46,0x7c,0x49,0xb2,0x68,0x5c,0x02,0xb4,0xcb,0x4c,0x22,0xd5,0xb7,0x1d,0x3e,0x0f,0x63,0xd1,0x11,0xa0,0x55,0x08,0x73,0x85,0x62,0x13,0x57,0x09,0x03,0xcb,0xdb,0x47,0x9d,0xd6,0x6f,0xbd,0xaa,0x05,0x62,0xb9,0x3e,0x8d,0xe8,0x9e,0x1e,0xd8,0x85,0x5a,0x77,0xf2,0x8a,0x26,0x71,0x81,0xe2,0x7f,0xf3,0x35,0x70,0xe9,0x0b,0x97,0xa9,0xfe,0xd9,0xb9,0x25,0xcf,0x68,0x80,0x6d,0x7f,0xb5,0x68,0xf7,0x85,0x07,0x71,0xd9,0xf8,0xef,0x39,0x14,0x80,0x2e,0xc2,0x17,0x46,0xf7,0xfb,0xe8,0xf9,0x10,0xd6,0x17,0xcf,0x3a,0x4f,0x73,0xae,0x08,0xbc,0xc2,0x39,0x10,0x3a,0xd6,0xf3,0x77,0xa1,0x91,0x93,0x5c,0xfe,0xf4,0x76,0x11,0xd9,0xb7,0x25,0xc2,0x9e,0x12,0xda,0x0f,0xbd,0x25,0x69,0x03,0xa8,0xea,0x78,0x88,0x3d,0xa3,0xc9,0xfe,0x38,0x9c,0x04,0xc6,0x38,0x32,0xd2,0x1a,0xc0,0xc7,0x86,0x09,0x73,0x24,0xfa,0x5b,0x66,0x81,0x26,0xb3,0x1b,0x90,0x14,0xb2,0xbe,0x34,0x78,0xb1,0x48,0x0f,0x36,0xde,0xcf,0xf1,0x06,0x7d,0x04,0x7d,0x54,0x16,0x2b,0xeb,0xc7,0x38,0x17,0x71,0x95,0xcc,0xb3,0xd9,0x80,0x64,0x90,0x0e,0x26,0x5a,0x87,0x48,0xff,0x6c,0xa2,0x65,0xfe,0xe5,0xea,0x27,0x7b,0x03,0xd5,0x42,0x68,0xd4,0xe5,0x1e,0x09,0x3e,0x11,0xae,0xdd,0x07,0x14,0x6c,0x3c,0x26,0x2e,0x58,0xde,0xe9,0xe7,0x95,0xa9,0x6d,0x4f,0x6d,0x0e,0x2a,0xbf,0x02,0xf7,0xcf,0x11,0xe9,0x39,0x69,0xf5,0x63,0x9c,0x62,0xbd,0xa7,0x50,0xe9,0x5f,0xf4,0x67,0xc7,0x4c,0xb9,0xbf,0xb6,0xd5,0xd5,0x0b,0x1f,0x8a,0xf8,0x97,0x60,0x42,0xf2,0xc0,0x51,0xc8,0x70,0x97,0x9f,0xc3,0xb7,0x41,0xde,0xc8,0xe6,0x59,0x80,0x28,0x66,0x0b,0x3f,0xbe,0x54,0xc5,0x1d,0x5b,0x7b,0x25,0x11]; + let ct: [u8; MLKEM1024_CT_LEN] = [ + 0x6B, 0x15, 0x08, 0x82, 0x0E, 0x30, 0x0E, 0x5C, 0xFE, 0xB3, 0xD9, 0xD3, 0x33, 0xF2, 0xDF, + 0x3F, 0x6A, 0x91, 0xB7, 0x40, 0x07, 0x61, 0xF0, 0x7B, 0xF6, 0x83, 0xF8, 0xED, 0xCD, 0xBE, + 0x14, 0xA0, 0xAC, 0x3F, 0x32, 0x26, 0x0B, 0x9B, 0x7E, 0x74, 0xE9, 0xA8, 0x6E, 0x44, 0xD5, + 0x3B, 0xA5, 0x3B, 0xB9, 0x1C, 0x9A, 0x9D, 0x4E, 0x15, 0x7F, 0xE5, 0x82, 0x11, 0x5F, 0xBA, + 0x56, 0x8C, 0x29, 0x2F, 0x2B, 0xB9, 0x62, 0xAA, 0x20, 0x50, 0x41, 0xE8, 0xD6, 0x9D, 0xB7, + 0xA9, 0xD7, 0x5E, 0xAA, 0xAA, 0xA0, 0x21, 0xE4, 0x5B, 0xCA, 0x83, 0x2C, 0xA1, 0x5B, 0xB4, + 0x4E, 0xBA, 0xA7, 0x0E, 0x8B, 0x95, 0x83, 0x9B, 0xCE, 0x92, 0xF0, 0x7C, 0x2E, 0x29, 0xFC, + 0xCA, 0x0D, 0x7B, 0x94, 0x00, 0x6F, 0x99, 0x44, 0x31, 0x40, 0x0B, 0xF7, 0xD6, 0xB9, 0x34, + 0x8D, 0x2F, 0x8A, 0x3F, 0x57, 0x8B, 0x52, 0x16, 0x47, 0x50, 0xE4, 0xAA, 0x7D, 0x8D, 0xDA, + 0xDA, 0xC1, 0x80, 0x23, 0x21, 0x7C, 0x26, 0xFB, 0x4E, 0x3A, 0xB0, 0xEC, 0x34, 0xC3, 0x23, + 0x5D, 0xE1, 0x2B, 0x2B, 0xFA, 0xF2, 0xC3, 0x36, 0x89, 0xD4, 0xA5, 0x3F, 0xE3, 0x13, 0xCB, + 0xD2, 0x8A, 0xBB, 0x9D, 0xBE, 0x23, 0x0A, 0x35, 0x9E, 0x41, 0x21, 0xED, 0x9F, 0x98, 0xF1, + 0x2F, 0x62, 0xD1, 0x4A, 0x9F, 0xE8, 0x6C, 0x46, 0x60, 0x2F, 0xA7, 0xC9, 0x5D, 0x25, 0xBA, + 0xCD, 0x2C, 0xDA, 0x33, 0xD5, 0x0B, 0xA7, 0xB5, 0xE2, 0xC0, 0xB2, 0x98, 0x17, 0x09, 0x35, + 0x8D, 0x8F, 0xC4, 0xE8, 0x9E, 0x63, 0x29, 0x67, 0xF7, 0x88, 0xB9, 0x34, 0xE8, 0x9C, 0x93, + 0xBD, 0x19, 0xCB, 0x62, 0xD7, 0x73, 0x7F, 0x56, 0x5A, 0x53, 0x25, 0xA0, 0xC0, 0x54, 0xF4, + 0xC4, 0xFB, 0x27, 0x9B, 0x1B, 0x8F, 0x63, 0xC2, 0x1F, 0xD3, 0x68, 0x0C, 0xB8, 0x0D, 0x35, + 0x2A, 0xC2, 0xA9, 0x03, 0x57, 0x24, 0x32, 0x00, 0xA7, 0xAA, 0xE1, 0x60, 0xE1, 0x26, 0xA5, + 0xBE, 0x5F, 0xF8, 0xF6, 0xED, 0x4F, 0xAB, 0x22, 0x30, 0xD9, 0x8E, 0x1B, 0x28, 0xFD, 0xA6, + 0xB9, 0xD9, 0xC5, 0xE4, 0xDF, 0x18, 0x85, 0xED, 0x12, 0x54, 0x2D, 0x98, 0xCB, 0x1C, 0xF0, + 0xEC, 0x95, 0x9F, 0x52, 0xB9, 0xB7, 0x09, 0xE6, 0xB2, 0x8C, 0xF1, 0xE5, 0xA1, 0x67, 0x23, + 0x32, 0xC8, 0x78, 0xBA, 0xF1, 0xFD, 0x8E, 0x5F, 0x49, 0x1B, 0x06, 0xAE, 0xCF, 0x5A, 0x5D, + 0x2A, 0xD2, 0x4C, 0x97, 0x34, 0xF3, 0x50, 0x08, 0xE2, 0xDD, 0xF6, 0x1E, 0x4C, 0x4F, 0x4B, + 0xDF, 0x48, 0x42, 0x6B, 0xBC, 0x63, 0xDE, 0x9A, 0xE7, 0xEE, 0x5A, 0x5D, 0x67, 0xF3, 0x51, + 0x30, 0x27, 0xBD, 0x68, 0xC8, 0xC3, 0x7F, 0x90, 0x5B, 0x75, 0xC1, 0x39, 0x7D, 0x38, 0xCC, + 0xDF, 0xE6, 0x08, 0x79, 0xFC, 0xBE, 0xBE, 0xE2, 0xCE, 0xBF, 0x19, 0x12, 0x80, 0xB0, 0xB8, + 0xAF, 0xBA, 0xCA, 0x3D, 0x1E, 0x03, 0xF2, 0xAA, 0x91, 0x11, 0x16, 0x39, 0x34, 0xE1, 0x28, + 0xE6, 0xEA, 0x1C, 0xC2, 0x15, 0xB7, 0xF3, 0x6F, 0xB9, 0x9C, 0x2D, 0xAA, 0x27, 0x25, 0xED, + 0x3A, 0x77, 0xE9, 0x30, 0x4F, 0x18, 0x12, 0x0A, 0x19, 0x78, 0x97, 0x0C, 0xA7, 0x60, 0x5C, + 0x8A, 0x3C, 0x67, 0xEF, 0x3F, 0xD3, 0x1B, 0x02, 0x7B, 0x06, 0x2A, 0x35, 0x8E, 0x32, 0xFA, + 0x48, 0xE4, 0xB0, 0xEF, 0x31, 0xD1, 0x1F, 0xE0, 0x5E, 0x8E, 0x71, 0x0E, 0x9A, 0x60, 0x08, + 0xB5, 0x4B, 0xD6, 0xAC, 0x93, 0x78, 0xC9, 0x8C, 0x62, 0x7B, 0x12, 0x59, 0x82, 0x3D, 0x6F, + 0x3E, 0xF4, 0xF0, 0xEF, 0x03, 0x81, 0x4E, 0x15, 0xED, 0xDE, 0x93, 0xF7, 0x34, 0x84, 0xE9, + 0x48, 0x27, 0x3D, 0x76, 0x64, 0xBF, 0xE6, 0x60, 0x47, 0xFC, 0x31, 0xC4, 0x68, 0xC8, 0x77, + 0x46, 0x78, 0xD2, 0x7B, 0x16, 0xB1, 0xC0, 0xDA, 0xB8, 0x80, 0xD1, 0xD7, 0x16, 0xD4, 0xDB, + 0x28, 0x89, 0x49, 0xE3, 0x35, 0x11, 0x17, 0x90, 0xF0, 0xE6, 0x6D, 0x65, 0x29, 0xB7, 0x2A, + 0x9B, 0x18, 0x2F, 0xE0, 0xB4, 0x37, 0xAA, 0xAC, 0x2B, 0x1F, 0xFC, 0x09, 0x79, 0xC1, 0x48, + 0x5C, 0x0E, 0x16, 0x38, 0x36, 0xC3, 0xA5, 0xCE, 0x49, 0xC5, 0xC8, 0x88, 0xD3, 0xEF, 0xC8, + 0xDF, 0xD6, 0xBC, 0x91, 0x73, 0x9B, 0xAD, 0x0A, 0xDD, 0x04, 0xD9, 0x20, 0xD3, 0x88, 0x42, + 0x28, 0x80, 0x3A, 0xAA, 0x2A, 0x23, 0xD9, 0xAD, 0x75, 0xD5, 0xD3, 0x9B, 0x40, 0x0E, 0x04, + 0xC9, 0xED, 0xC2, 0x91, 0x69, 0x7B, 0xB4, 0xC7, 0x1A, 0x22, 0x91, 0xEC, 0xD8, 0x95, 0x8B, + 0x02, 0xF0, 0x41, 0x56, 0x11, 0x61, 0x37, 0x68, 0x2D, 0xD0, 0xF3, 0x84, 0x93, 0x14, 0x3C, + 0x91, 0x1C, 0xF8, 0x72, 0xF5, 0x39, 0x8F, 0x07, 0x83, 0x7D, 0xF5, 0x20, 0x1B, 0xF6, 0x6A, + 0x06, 0xC1, 0x4A, 0xE2, 0x60, 0x45, 0x69, 0xD4, 0x27, 0xE9, 0xDC, 0x15, 0xB3, 0x01, 0xA3, + 0xA9, 0xFC, 0x7C, 0xFB, 0xA5, 0x02, 0x64, 0xFC, 0x73, 0x79, 0xE2, 0xBA, 0x83, 0xEE, 0xEE, + 0x68, 0xDC, 0x33, 0x52, 0x87, 0x74, 0xBA, 0x67, 0xFC, 0x1E, 0x51, 0xED, 0x3B, 0xC4, 0x08, + 0x1B, 0xDD, 0x3B, 0x18, 0x16, 0xD4, 0x0F, 0x61, 0xBB, 0xCF, 0x72, 0x11, 0xD9, 0xF4, 0x12, + 0xE1, 0x22, 0x7F, 0x3F, 0xBC, 0x5D, 0x04, 0x76, 0xF0, 0x32, 0x86, 0x45, 0x4E, 0xAB, 0xD5, + 0xD2, 0xA2, 0x25, 0xAA, 0x27, 0xA9, 0x61, 0x61, 0x6E, 0x24, 0xE7, 0x53, 0x2F, 0x28, 0x03, + 0x06, 0x64, 0xDF, 0xC2, 0xEC, 0xBC, 0xC5, 0xBE, 0x77, 0x9A, 0xF9, 0x39, 0x92, 0xE6, 0x5D, + 0x1C, 0x85, 0xE2, 0x10, 0x68, 0x8F, 0x2B, 0x6B, 0x91, 0xCC, 0xC9, 0xF1, 0xDB, 0x32, 0x6D, + 0xD9, 0x47, 0x53, 0xC3, 0xAA, 0xAE, 0xD4, 0x57, 0x1C, 0x20, 0x4F, 0x97, 0xC9, 0xE1, 0x87, + 0x1F, 0xFE, 0xCD, 0xEE, 0xF0, 0x7B, 0x88, 0xDE, 0x4F, 0x17, 0xF3, 0xCE, 0x40, 0xA5, 0x8D, + 0x01, 0x5F, 0xBE, 0xDB, 0x8A, 0x01, 0x1C, 0x61, 0x8F, 0xBB, 0x03, 0x99, 0x87, 0x63, 0xBC, + 0x60, 0xE7, 0x8C, 0x3C, 0x27, 0xAB, 0xD3, 0x70, 0x3D, 0x67, 0xB8, 0x6E, 0x3A, 0x82, 0x84, + 0xFE, 0x67, 0xD9, 0xEB, 0x59, 0xFC, 0x21, 0x69, 0x39, 0x29, 0x15, 0x7F, 0xDB, 0x0A, 0xE4, + 0x37, 0xA7, 0x36, 0xC2, 0xCF, 0xB5, 0xB6, 0x14, 0xD9, 0xB8, 0x47, 0x63, 0x5C, 0x1B, 0x8D, + 0x04, 0x75, 0x7C, 0xA6, 0x2F, 0x15, 0xF4, 0xD5, 0x36, 0xC7, 0x2D, 0x7A, 0x71, 0xD7, 0x41, + 0xD8, 0xA0, 0x7D, 0xD6, 0xA8, 0x28, 0x90, 0xB8, 0xE1, 0xF5, 0xAD, 0x59, 0xB1, 0x7E, 0x5C, + 0x85, 0xFD, 0xD7, 0x08, 0x17, 0x76, 0xEF, 0xAA, 0x0E, 0x29, 0x17, 0x14, 0xDE, 0xB9, 0x79, + 0xD5, 0xE5, 0x44, 0xBD, 0x8B, 0xE9, 0xF7, 0x9A, 0x87, 0x7E, 0x4B, 0xAD, 0x28, 0x24, 0x6A, + 0x39, 0x33, 0x00, 0x56, 0x97, 0x00, 0x4B, 0xFB, 0xE3, 0x24, 0x23, 0xAA, 0xDA, 0x98, 0x02, + 0xC5, 0x6F, 0xE1, 0xA6, 0x53, 0xA5, 0xCB, 0x95, 0x7D, 0x47, 0x20, 0xF5, 0x5D, 0x5F, 0x40, + 0x10, 0xDB, 0x40, 0x22, 0xA2, 0xCA, 0x49, 0xEC, 0xF5, 0xE7, 0xDD, 0x01, 0x45, 0xE5, 0x82, + 0xA9, 0xBC, 0xEB, 0x77, 0x3A, 0x45, 0x09, 0x7B, 0x29, 0x9A, 0x3B, 0x0D, 0xC4, 0x3B, 0x34, + 0x5D, 0x16, 0x37, 0xE8, 0x8F, 0xC1, 0xD8, 0x3E, 0x41, 0x4E, 0x24, 0xA0, 0xF6, 0xBD, 0x97, + 0x75, 0x73, 0xEF, 0x52, 0x3B, 0xBA, 0x8B, 0xA9, 0x86, 0x45, 0x15, 0xAC, 0x71, 0xEC, 0xA5, + 0xDF, 0xEF, 0xDA, 0x02, 0x3C, 0xAD, 0x07, 0xBD, 0x88, 0x74, 0x2B, 0x52, 0x1F, 0x63, 0x6B, + 0xA7, 0x08, 0xFB, 0xA4, 0x20, 0x81, 0x48, 0x22, 0xAE, 0x91, 0x32, 0x75, 0xA8, 0x51, 0xB9, + 0x0F, 0x34, 0x62, 0xB6, 0x4B, 0xF9, 0x88, 0xF8, 0xBA, 0x27, 0x18, 0xB4, 0x9F, 0x78, 0xCA, + 0xB5, 0xBA, 0xC4, 0x9F, 0x02, 0xCB, 0x50, 0xF3, 0x82, 0x1C, 0x3A, 0x35, 0x6D, 0x99, 0x45, + 0xC4, 0xBA, 0x8D, 0xBA, 0xB7, 0x47, 0x0C, 0x97, 0x79, 0x68, 0xF7, 0xB0, 0xCD, 0x66, 0xB0, + 0x7B, 0xFA, 0xCB, 0x39, 0x7C, 0x97, 0x38, 0x9E, 0xB4, 0x01, 0xC9, 0xE7, 0x85, 0xFA, 0x7A, + 0x98, 0x0E, 0x89, 0xC2, 0xF8, 0xAB, 0x0D, 0x8A, 0xD9, 0x1F, 0xA2, 0xBE, 0xF2, 0x52, 0xA3, + 0x79, 0x25, 0x21, 0x89, 0xF1, 0x53, 0x7C, 0x40, 0x4E, 0xA3, 0x18, 0x5A, 0xD8, 0xF1, 0xAB, + 0xF1, 0x72, 0xCE, 0x31, 0x01, 0x5F, 0xA5, 0xDC, 0x4E, 0x9A, 0xE5, 0x44, 0x66, 0x7A, 0xA2, + 0x12, 0x98, 0xF0, 0x1D, 0xB5, 0xD9, 0xE2, 0xC5, 0x52, 0xC2, 0x85, 0xD0, 0x7A, 0x8B, 0xC9, + 0xF5, 0xE7, 0x69, 0xEF, 0x42, 0x73, 0x2F, 0x08, 0x69, 0x0C, 0xCA, 0x61, 0xD0, 0x61, 0x34, + 0x53, 0x4E, 0x07, 0x00, 0xEF, 0x4C, 0xC7, 0x82, 0x70, 0x2F, 0x30, 0x0E, 0xB9, 0x4A, 0x0F, + 0xF4, 0x06, 0x5C, 0x11, 0xFF, 0x13, 0xCF, 0x2A, 0x3E, 0x2D, 0x1F, 0x85, 0x7F, 0x3E, 0x8E, + 0xB5, 0xDC, 0x70, 0x51, 0xF3, 0x1F, 0xD6, 0xEB, 0xF2, 0x56, 0xA1, 0xC5, 0xA4, 0xEE, 0x72, + 0x62, 0xB2, 0x42, 0xDF, 0x5F, 0xA4, 0x6D, 0xCE, 0x43, 0x7D, 0x20, 0xFD, 0xF8, 0x11, 0x82, + 0xFE, 0xAC, 0x60, 0x8A, 0xAF, 0x51, 0x5B, 0xFA, 0xED, 0xA5, 0x24, 0xC8, 0xAB, 0x1C, 0xCA, + 0xC2, 0x5E, 0x0B, 0x76, 0x81, 0xE3, 0x85, 0x64, 0x5C, 0xCF, 0xF5, 0x33, 0x73, 0x1E, 0x7D, + 0x11, 0x2E, 0x22, 0x15, 0xB3, 0x88, 0x85, 0x52, 0x43, 0x31, 0x54, 0x94, 0xC6, 0x27, 0x48, + 0x46, 0x7C, 0x49, 0xB2, 0x68, 0x5C, 0x02, 0xB4, 0xCB, 0x4C, 0x22, 0xD5, 0xB7, 0x1D, 0x3E, + 0x0F, 0x63, 0xD1, 0x11, 0xA0, 0x55, 0x08, 0x73, 0x85, 0x62, 0x13, 0x57, 0x09, 0x03, 0xCB, + 0xDB, 0x47, 0x9D, 0xD6, 0x6F, 0xBD, 0xAA, 0x05, 0x62, 0xB9, 0x3E, 0x8D, 0xE8, 0x9E, 0x1E, + 0xD8, 0x85, 0x5A, 0x77, 0xF2, 0x8A, 0x26, 0x71, 0x81, 0xE2, 0x7F, 0xF3, 0x35, 0x70, 0xE9, + 0x0B, 0x97, 0xA9, 0xFE, 0xD9, 0xB9, 0x25, 0xCF, 0x68, 0x80, 0x6D, 0x7F, 0xB5, 0x68, 0xF7, + 0x85, 0x07, 0x71, 0xD9, 0xF8, 0xEF, 0x39, 0x14, 0x80, 0x2E, 0xC2, 0x17, 0x46, 0xF7, 0xFB, + 0xE8, 0xF9, 0x10, 0xD6, 0x17, 0xCF, 0x3A, 0x4F, 0x73, 0xAE, 0x08, 0xBC, 0xC2, 0x39, 0x10, + 0x3A, 0xD6, 0xF3, 0x77, 0xA1, 0x91, 0x93, 0x5C, 0xFE, 0xF4, 0x76, 0x11, 0xD9, 0xB7, 0x25, + 0xC2, 0x9E, 0x12, 0xDA, 0x0F, 0xBD, 0x25, 0x69, 0x03, 0xA8, 0xEA, 0x78, 0x88, 0x3D, 0xA3, + 0xC9, 0xFE, 0x38, 0x9C, 0x04, 0xC6, 0x38, 0x32, 0xD2, 0x1A, 0xC0, 0xC7, 0x86, 0x09, 0x73, + 0x24, 0xFA, 0x5B, 0x66, 0x81, 0x26, 0xB3, 0x1B, 0x90, 0x14, 0xB2, 0xBE, 0x34, 0x78, 0xB1, + 0x48, 0x0F, 0x36, 0xDE, 0xCF, 0xF1, 0x06, 0x7D, 0x04, 0x7D, 0x54, 0x16, 0x2B, 0xEB, 0xC7, + 0x38, 0x17, 0x71, 0x95, 0xCC, 0xB3, 0xD9, 0x80, 0x64, 0x90, 0x0E, 0x26, 0x5A, 0x87, 0x48, + 0xFF, 0x6C, 0xA2, 0x65, 0xFE, 0xE5, 0xEA, 0x27, 0x7B, 0x03, 0xD5, 0x42, 0x68, 0xD4, 0xE5, + 0x1E, 0x09, 0x3E, 0x11, 0xAE, 0xDD, 0x07, 0x14, 0x6C, 0x3C, 0x26, 0x2E, 0x58, 0xDE, 0xE9, + 0xE7, 0x95, 0xA9, 0x6D, 0x4F, 0x6D, 0x0E, 0x2A, 0xBF, 0x02, 0xF7, 0xCF, 0x11, 0xE9, 0x39, + 0x69, 0xF5, 0x63, 0x9C, 0x62, 0xBD, 0xA7, 0x50, 0xE9, 0x5F, 0xF4, 0x67, 0xC7, 0x4C, 0xB9, + 0xBF, 0xB6, 0xD5, 0xD5, 0x0B, 0x1F, 0x8A, 0xF8, 0x97, 0x60, 0x42, 0xF2, 0xC0, 0x51, 0xC8, + 0x70, 0x97, 0x9F, 0xC3, 0xB7, 0x41, 0xDE, 0xC8, 0xE6, 0x59, 0x80, 0x28, 0x66, 0x0B, 0x3F, + 0xBE, 0x54, 0xC5, 0x1D, 0x5B, 0x7B, 0x25, 0x11, + ]; let (_pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); let ss = MLKEM1024::decaps(&sk, &ct).unwrap(); @@ -412,14 +1365,21 @@ fn bench_mlkem1024_decaps() { } fn bench_mlkem1024_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEMTrait, MLKEM1024}; + use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEMTrait}; eprintln!("MLKEM1024_lowmemory/Decaps"); let seed = KeyMaterial512::from_bytes_as_type( - &[0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f], + &[ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, + 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, + 0x1C, 0x1D, 0x1E, 0x1F, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, + ], KeyType::Seed, - ).unwrap(); + ) + .unwrap(); /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); @@ -427,15 +1387,119 @@ fn bench_mlkem1024_lowmemory_decaps() { // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); - let ct: [u8; MLKEM1024_CT_LEN] = [0x6b,0x15,0x08,0x82,0x0e,0x30,0x0e,0x5c,0xfe,0xb3,0xd9,0xd3,0x33,0xf2,0xdf,0x3f,0x6a,0x91,0xb7,0x40,0x07,0x61,0xf0,0x7b,0xf6,0x83,0xf8,0xed,0xcd,0xbe,0x14,0xa0,0xac,0x3f,0x32,0x26,0x0b,0x9b,0x7e,0x74,0xe9,0xa8,0x6e,0x44,0xd5,0x3b,0xa5,0x3b,0xb9,0x1c,0x9a,0x9d,0x4e,0x15,0x7f,0xe5,0x82,0x11,0x5f,0xba,0x56,0x8c,0x29,0x2f,0x2b,0xb9,0x62,0xaa,0x20,0x50,0x41,0xe8,0xd6,0x9d,0xb7,0xa9,0xd7,0x5e,0xaa,0xaa,0xa0,0x21,0xe4,0x5b,0xca,0x83,0x2c,0xa1,0x5b,0xb4,0x4e,0xba,0xa7,0x0e,0x8b,0x95,0x83,0x9b,0xce,0x92,0xf0,0x7c,0x2e,0x29,0xfc,0xca,0x0d,0x7b,0x94,0x00,0x6f,0x99,0x44,0x31,0x40,0x0b,0xf7,0xd6,0xb9,0x34,0x8d,0x2f,0x8a,0x3f,0x57,0x8b,0x52,0x16,0x47,0x50,0xe4,0xaa,0x7d,0x8d,0xda,0xda,0xc1,0x80,0x23,0x21,0x7c,0x26,0xfb,0x4e,0x3a,0xb0,0xec,0x34,0xc3,0x23,0x5d,0xe1,0x2b,0x2b,0xfa,0xf2,0xc3,0x36,0x89,0xd4,0xa5,0x3f,0xe3,0x13,0xcb,0xd2,0x8a,0xbb,0x9d,0xbe,0x23,0x0a,0x35,0x9e,0x41,0x21,0xed,0x9f,0x98,0xf1,0x2f,0x62,0xd1,0x4a,0x9f,0xe8,0x6c,0x46,0x60,0x2f,0xa7,0xc9,0x5d,0x25,0xba,0xcd,0x2c,0xda,0x33,0xd5,0x0b,0xa7,0xb5,0xe2,0xc0,0xb2,0x98,0x17,0x09,0x35,0x8d,0x8f,0xc4,0xe8,0x9e,0x63,0x29,0x67,0xf7,0x88,0xb9,0x34,0xe8,0x9c,0x93,0xbd,0x19,0xcb,0x62,0xd7,0x73,0x7f,0x56,0x5a,0x53,0x25,0xa0,0xc0,0x54,0xf4,0xc4,0xfb,0x27,0x9b,0x1b,0x8f,0x63,0xc2,0x1f,0xd3,0x68,0x0c,0xb8,0x0d,0x35,0x2a,0xc2,0xa9,0x03,0x57,0x24,0x32,0x00,0xa7,0xaa,0xe1,0x60,0xe1,0x26,0xa5,0xbe,0x5f,0xf8,0xf6,0xed,0x4f,0xab,0x22,0x30,0xd9,0x8e,0x1b,0x28,0xfd,0xa6,0xb9,0xd9,0xc5,0xe4,0xdf,0x18,0x85,0xed,0x12,0x54,0x2d,0x98,0xcb,0x1c,0xf0,0xec,0x95,0x9f,0x52,0xb9,0xb7,0x09,0xe6,0xb2,0x8c,0xf1,0xe5,0xa1,0x67,0x23,0x32,0xc8,0x78,0xba,0xf1,0xfd,0x8e,0x5f,0x49,0x1b,0x06,0xae,0xcf,0x5a,0x5d,0x2a,0xd2,0x4c,0x97,0x34,0xf3,0x50,0x08,0xe2,0xdd,0xf6,0x1e,0x4c,0x4f,0x4b,0xdf,0x48,0x42,0x6b,0xbc,0x63,0xde,0x9a,0xe7,0xee,0x5a,0x5d,0x67,0xf3,0x51,0x30,0x27,0xbd,0x68,0xc8,0xc3,0x7f,0x90,0x5b,0x75,0xc1,0x39,0x7d,0x38,0xcc,0xdf,0xe6,0x08,0x79,0xfc,0xbe,0xbe,0xe2,0xce,0xbf,0x19,0x12,0x80,0xb0,0xb8,0xaf,0xba,0xca,0x3d,0x1e,0x03,0xf2,0xaa,0x91,0x11,0x16,0x39,0x34,0xe1,0x28,0xe6,0xea,0x1c,0xc2,0x15,0xb7,0xf3,0x6f,0xb9,0x9c,0x2d,0xaa,0x27,0x25,0xed,0x3a,0x77,0xe9,0x30,0x4f,0x18,0x12,0x0a,0x19,0x78,0x97,0x0c,0xa7,0x60,0x5c,0x8a,0x3c,0x67,0xef,0x3f,0xd3,0x1b,0x02,0x7b,0x06,0x2a,0x35,0x8e,0x32,0xfa,0x48,0xe4,0xb0,0xef,0x31,0xd1,0x1f,0xe0,0x5e,0x8e,0x71,0x0e,0x9a,0x60,0x08,0xb5,0x4b,0xd6,0xac,0x93,0x78,0xc9,0x8c,0x62,0x7b,0x12,0x59,0x82,0x3d,0x6f,0x3e,0xf4,0xf0,0xef,0x03,0x81,0x4e,0x15,0xed,0xde,0x93,0xf7,0x34,0x84,0xe9,0x48,0x27,0x3d,0x76,0x64,0xbf,0xe6,0x60,0x47,0xfc,0x31,0xc4,0x68,0xc8,0x77,0x46,0x78,0xd2,0x7b,0x16,0xb1,0xc0,0xda,0xb8,0x80,0xd1,0xd7,0x16,0xd4,0xdb,0x28,0x89,0x49,0xe3,0x35,0x11,0x17,0x90,0xf0,0xe6,0x6d,0x65,0x29,0xb7,0x2a,0x9b,0x18,0x2f,0xe0,0xb4,0x37,0xaa,0xac,0x2b,0x1f,0xfc,0x09,0x79,0xc1,0x48,0x5c,0x0e,0x16,0x38,0x36,0xc3,0xa5,0xce,0x49,0xc5,0xc8,0x88,0xd3,0xef,0xc8,0xdf,0xd6,0xbc,0x91,0x73,0x9b,0xad,0x0a,0xdd,0x04,0xd9,0x20,0xd3,0x88,0x42,0x28,0x80,0x3a,0xaa,0x2a,0x23,0xd9,0xad,0x75,0xd5,0xd3,0x9b,0x40,0x0e,0x04,0xc9,0xed,0xc2,0x91,0x69,0x7b,0xb4,0xc7,0x1a,0x22,0x91,0xec,0xd8,0x95,0x8b,0x02,0xf0,0x41,0x56,0x11,0x61,0x37,0x68,0x2d,0xd0,0xf3,0x84,0x93,0x14,0x3c,0x91,0x1c,0xf8,0x72,0xf5,0x39,0x8f,0x07,0x83,0x7d,0xf5,0x20,0x1b,0xf6,0x6a,0x06,0xc1,0x4a,0xe2,0x60,0x45,0x69,0xd4,0x27,0xe9,0xdc,0x15,0xb3,0x01,0xa3,0xa9,0xfc,0x7c,0xfb,0xa5,0x02,0x64,0xfc,0x73,0x79,0xe2,0xba,0x83,0xee,0xee,0x68,0xdc,0x33,0x52,0x87,0x74,0xba,0x67,0xfc,0x1e,0x51,0xed,0x3b,0xc4,0x08,0x1b,0xdd,0x3b,0x18,0x16,0xd4,0x0f,0x61,0xbb,0xcf,0x72,0x11,0xd9,0xf4,0x12,0xe1,0x22,0x7f,0x3f,0xbc,0x5d,0x04,0x76,0xf0,0x32,0x86,0x45,0x4e,0xab,0xd5,0xd2,0xa2,0x25,0xaa,0x27,0xa9,0x61,0x61,0x6e,0x24,0xe7,0x53,0x2f,0x28,0x03,0x06,0x64,0xdf,0xc2,0xec,0xbc,0xc5,0xbe,0x77,0x9a,0xf9,0x39,0x92,0xe6,0x5d,0x1c,0x85,0xe2,0x10,0x68,0x8f,0x2b,0x6b,0x91,0xcc,0xc9,0xf1,0xdb,0x32,0x6d,0xd9,0x47,0x53,0xc3,0xaa,0xae,0xd4,0x57,0x1c,0x20,0x4f,0x97,0xc9,0xe1,0x87,0x1f,0xfe,0xcd,0xee,0xf0,0x7b,0x88,0xde,0x4f,0x17,0xf3,0xce,0x40,0xa5,0x8d,0x01,0x5f,0xbe,0xdb,0x8a,0x01,0x1c,0x61,0x8f,0xbb,0x03,0x99,0x87,0x63,0xbc,0x60,0xe7,0x8c,0x3c,0x27,0xab,0xd3,0x70,0x3d,0x67,0xb8,0x6e,0x3a,0x82,0x84,0xfe,0x67,0xd9,0xeb,0x59,0xfc,0x21,0x69,0x39,0x29,0x15,0x7f,0xdb,0x0a,0xe4,0x37,0xa7,0x36,0xc2,0xcf,0xb5,0xb6,0x14,0xd9,0xb8,0x47,0x63,0x5c,0x1b,0x8d,0x04,0x75,0x7c,0xa6,0x2f,0x15,0xf4,0xd5,0x36,0xc7,0x2d,0x7a,0x71,0xd7,0x41,0xd8,0xa0,0x7d,0xd6,0xa8,0x28,0x90,0xb8,0xe1,0xf5,0xad,0x59,0xb1,0x7e,0x5c,0x85,0xfd,0xd7,0x08,0x17,0x76,0xef,0xaa,0x0e,0x29,0x17,0x14,0xde,0xb9,0x79,0xd5,0xe5,0x44,0xbd,0x8b,0xe9,0xf7,0x9a,0x87,0x7e,0x4b,0xad,0x28,0x24,0x6a,0x39,0x33,0x00,0x56,0x97,0x00,0x4b,0xfb,0xe3,0x24,0x23,0xaa,0xda,0x98,0x02,0xc5,0x6f,0xe1,0xa6,0x53,0xa5,0xcb,0x95,0x7d,0x47,0x20,0xf5,0x5d,0x5f,0x40,0x10,0xdb,0x40,0x22,0xa2,0xca,0x49,0xec,0xf5,0xe7,0xdd,0x01,0x45,0xe5,0x82,0xa9,0xbc,0xeb,0x77,0x3a,0x45,0x09,0x7b,0x29,0x9a,0x3b,0x0d,0xc4,0x3b,0x34,0x5d,0x16,0x37,0xe8,0x8f,0xc1,0xd8,0x3e,0x41,0x4e,0x24,0xa0,0xf6,0xbd,0x97,0x75,0x73,0xef,0x52,0x3b,0xba,0x8b,0xa9,0x86,0x45,0x15,0xac,0x71,0xec,0xa5,0xdf,0xef,0xda,0x02,0x3c,0xad,0x07,0xbd,0x88,0x74,0x2b,0x52,0x1f,0x63,0x6b,0xa7,0x08,0xfb,0xa4,0x20,0x81,0x48,0x22,0xae,0x91,0x32,0x75,0xa8,0x51,0xb9,0x0f,0x34,0x62,0xb6,0x4b,0xf9,0x88,0xf8,0xba,0x27,0x18,0xb4,0x9f,0x78,0xca,0xb5,0xba,0xc4,0x9f,0x02,0xcb,0x50,0xf3,0x82,0x1c,0x3a,0x35,0x6d,0x99,0x45,0xc4,0xba,0x8d,0xba,0xb7,0x47,0x0c,0x97,0x79,0x68,0xf7,0xb0,0xcd,0x66,0xb0,0x7b,0xfa,0xcb,0x39,0x7c,0x97,0x38,0x9e,0xb4,0x01,0xc9,0xe7,0x85,0xfa,0x7a,0x98,0x0e,0x89,0xc2,0xf8,0xab,0x0d,0x8a,0xd9,0x1f,0xa2,0xbe,0xf2,0x52,0xa3,0x79,0x25,0x21,0x89,0xf1,0x53,0x7c,0x40,0x4e,0xa3,0x18,0x5a,0xd8,0xf1,0xab,0xf1,0x72,0xce,0x31,0x01,0x5f,0xa5,0xdc,0x4e,0x9a,0xe5,0x44,0x66,0x7a,0xa2,0x12,0x98,0xf0,0x1d,0xb5,0xd9,0xe2,0xc5,0x52,0xc2,0x85,0xd0,0x7a,0x8b,0xc9,0xf5,0xe7,0x69,0xef,0x42,0x73,0x2f,0x08,0x69,0x0c,0xca,0x61,0xd0,0x61,0x34,0x53,0x4e,0x07,0x00,0xef,0x4c,0xc7,0x82,0x70,0x2f,0x30,0x0e,0xb9,0x4a,0x0f,0xf4,0x06,0x5c,0x11,0xff,0x13,0xcf,0x2a,0x3e,0x2d,0x1f,0x85,0x7f,0x3e,0x8e,0xb5,0xdc,0x70,0x51,0xf3,0x1f,0xd6,0xeb,0xf2,0x56,0xa1,0xc5,0xa4,0xee,0x72,0x62,0xb2,0x42,0xdf,0x5f,0xa4,0x6d,0xce,0x43,0x7d,0x20,0xfd,0xf8,0x11,0x82,0xfe,0xac,0x60,0x8a,0xaf,0x51,0x5b,0xfa,0xed,0xa5,0x24,0xc8,0xab,0x1c,0xca,0xc2,0x5e,0x0b,0x76,0x81,0xe3,0x85,0x64,0x5c,0xcf,0xf5,0x33,0x73,0x1e,0x7d,0x11,0x2e,0x22,0x15,0xb3,0x88,0x85,0x52,0x43,0x31,0x54,0x94,0xc6,0x27,0x48,0x46,0x7c,0x49,0xb2,0x68,0x5c,0x02,0xb4,0xcb,0x4c,0x22,0xd5,0xb7,0x1d,0x3e,0x0f,0x63,0xd1,0x11,0xa0,0x55,0x08,0x73,0x85,0x62,0x13,0x57,0x09,0x03,0xcb,0xdb,0x47,0x9d,0xd6,0x6f,0xbd,0xaa,0x05,0x62,0xb9,0x3e,0x8d,0xe8,0x9e,0x1e,0xd8,0x85,0x5a,0x77,0xf2,0x8a,0x26,0x71,0x81,0xe2,0x7f,0xf3,0x35,0x70,0xe9,0x0b,0x97,0xa9,0xfe,0xd9,0xb9,0x25,0xcf,0x68,0x80,0x6d,0x7f,0xb5,0x68,0xf7,0x85,0x07,0x71,0xd9,0xf8,0xef,0x39,0x14,0x80,0x2e,0xc2,0x17,0x46,0xf7,0xfb,0xe8,0xf9,0x10,0xd6,0x17,0xcf,0x3a,0x4f,0x73,0xae,0x08,0xbc,0xc2,0x39,0x10,0x3a,0xd6,0xf3,0x77,0xa1,0x91,0x93,0x5c,0xfe,0xf4,0x76,0x11,0xd9,0xb7,0x25,0xc2,0x9e,0x12,0xda,0x0f,0xbd,0x25,0x69,0x03,0xa8,0xea,0x78,0x88,0x3d,0xa3,0xc9,0xfe,0x38,0x9c,0x04,0xc6,0x38,0x32,0xd2,0x1a,0xc0,0xc7,0x86,0x09,0x73,0x24,0xfa,0x5b,0x66,0x81,0x26,0xb3,0x1b,0x90,0x14,0xb2,0xbe,0x34,0x78,0xb1,0x48,0x0f,0x36,0xde,0xcf,0xf1,0x06,0x7d,0x04,0x7d,0x54,0x16,0x2b,0xeb,0xc7,0x38,0x17,0x71,0x95,0xcc,0xb3,0xd9,0x80,0x64,0x90,0x0e,0x26,0x5a,0x87,0x48,0xff,0x6c,0xa2,0x65,0xfe,0xe5,0xea,0x27,0x7b,0x03,0xd5,0x42,0x68,0xd4,0xe5,0x1e,0x09,0x3e,0x11,0xae,0xdd,0x07,0x14,0x6c,0x3c,0x26,0x2e,0x58,0xde,0xe9,0xe7,0x95,0xa9,0x6d,0x4f,0x6d,0x0e,0x2a,0xbf,0x02,0xf7,0xcf,0x11,0xe9,0x39,0x69,0xf5,0x63,0x9c,0x62,0xbd,0xa7,0x50,0xe9,0x5f,0xf4,0x67,0xc7,0x4c,0xb9,0xbf,0xb6,0xd5,0xd5,0x0b,0x1f,0x8a,0xf8,0x97,0x60,0x42,0xf2,0xc0,0x51,0xc8,0x70,0x97,0x9f,0xc3,0xb7,0x41,0xde,0xc8,0xe6,0x59,0x80,0x28,0x66,0x0b,0x3f,0xbe,0x54,0xc5,0x1d,0x5b,0x7b,0x25,0x11]; + let ct: [u8; MLKEM1024_CT_LEN] = [ + 0x6B, 0x15, 0x08, 0x82, 0x0E, 0x30, 0x0E, 0x5C, 0xFE, 0xB3, 0xD9, 0xD3, 0x33, 0xF2, 0xDF, + 0x3F, 0x6A, 0x91, 0xB7, 0x40, 0x07, 0x61, 0xF0, 0x7B, 0xF6, 0x83, 0xF8, 0xED, 0xCD, 0xBE, + 0x14, 0xA0, 0xAC, 0x3F, 0x32, 0x26, 0x0B, 0x9B, 0x7E, 0x74, 0xE9, 0xA8, 0x6E, 0x44, 0xD5, + 0x3B, 0xA5, 0x3B, 0xB9, 0x1C, 0x9A, 0x9D, 0x4E, 0x15, 0x7F, 0xE5, 0x82, 0x11, 0x5F, 0xBA, + 0x56, 0x8C, 0x29, 0x2F, 0x2B, 0xB9, 0x62, 0xAA, 0x20, 0x50, 0x41, 0xE8, 0xD6, 0x9D, 0xB7, + 0xA9, 0xD7, 0x5E, 0xAA, 0xAA, 0xA0, 0x21, 0xE4, 0x5B, 0xCA, 0x83, 0x2C, 0xA1, 0x5B, 0xB4, + 0x4E, 0xBA, 0xA7, 0x0E, 0x8B, 0x95, 0x83, 0x9B, 0xCE, 0x92, 0xF0, 0x7C, 0x2E, 0x29, 0xFC, + 0xCA, 0x0D, 0x7B, 0x94, 0x00, 0x6F, 0x99, 0x44, 0x31, 0x40, 0x0B, 0xF7, 0xD6, 0xB9, 0x34, + 0x8D, 0x2F, 0x8A, 0x3F, 0x57, 0x8B, 0x52, 0x16, 0x47, 0x50, 0xE4, 0xAA, 0x7D, 0x8D, 0xDA, + 0xDA, 0xC1, 0x80, 0x23, 0x21, 0x7C, 0x26, 0xFB, 0x4E, 0x3A, 0xB0, 0xEC, 0x34, 0xC3, 0x23, + 0x5D, 0xE1, 0x2B, 0x2B, 0xFA, 0xF2, 0xC3, 0x36, 0x89, 0xD4, 0xA5, 0x3F, 0xE3, 0x13, 0xCB, + 0xD2, 0x8A, 0xBB, 0x9D, 0xBE, 0x23, 0x0A, 0x35, 0x9E, 0x41, 0x21, 0xED, 0x9F, 0x98, 0xF1, + 0x2F, 0x62, 0xD1, 0x4A, 0x9F, 0xE8, 0x6C, 0x46, 0x60, 0x2F, 0xA7, 0xC9, 0x5D, 0x25, 0xBA, + 0xCD, 0x2C, 0xDA, 0x33, 0xD5, 0x0B, 0xA7, 0xB5, 0xE2, 0xC0, 0xB2, 0x98, 0x17, 0x09, 0x35, + 0x8D, 0x8F, 0xC4, 0xE8, 0x9E, 0x63, 0x29, 0x67, 0xF7, 0x88, 0xB9, 0x34, 0xE8, 0x9C, 0x93, + 0xBD, 0x19, 0xCB, 0x62, 0xD7, 0x73, 0x7F, 0x56, 0x5A, 0x53, 0x25, 0xA0, 0xC0, 0x54, 0xF4, + 0xC4, 0xFB, 0x27, 0x9B, 0x1B, 0x8F, 0x63, 0xC2, 0x1F, 0xD3, 0x68, 0x0C, 0xB8, 0x0D, 0x35, + 0x2A, 0xC2, 0xA9, 0x03, 0x57, 0x24, 0x32, 0x00, 0xA7, 0xAA, 0xE1, 0x60, 0xE1, 0x26, 0xA5, + 0xBE, 0x5F, 0xF8, 0xF6, 0xED, 0x4F, 0xAB, 0x22, 0x30, 0xD9, 0x8E, 0x1B, 0x28, 0xFD, 0xA6, + 0xB9, 0xD9, 0xC5, 0xE4, 0xDF, 0x18, 0x85, 0xED, 0x12, 0x54, 0x2D, 0x98, 0xCB, 0x1C, 0xF0, + 0xEC, 0x95, 0x9F, 0x52, 0xB9, 0xB7, 0x09, 0xE6, 0xB2, 0x8C, 0xF1, 0xE5, 0xA1, 0x67, 0x23, + 0x32, 0xC8, 0x78, 0xBA, 0xF1, 0xFD, 0x8E, 0x5F, 0x49, 0x1B, 0x06, 0xAE, 0xCF, 0x5A, 0x5D, + 0x2A, 0xD2, 0x4C, 0x97, 0x34, 0xF3, 0x50, 0x08, 0xE2, 0xDD, 0xF6, 0x1E, 0x4C, 0x4F, 0x4B, + 0xDF, 0x48, 0x42, 0x6B, 0xBC, 0x63, 0xDE, 0x9A, 0xE7, 0xEE, 0x5A, 0x5D, 0x67, 0xF3, 0x51, + 0x30, 0x27, 0xBD, 0x68, 0xC8, 0xC3, 0x7F, 0x90, 0x5B, 0x75, 0xC1, 0x39, 0x7D, 0x38, 0xCC, + 0xDF, 0xE6, 0x08, 0x79, 0xFC, 0xBE, 0xBE, 0xE2, 0xCE, 0xBF, 0x19, 0x12, 0x80, 0xB0, 0xB8, + 0xAF, 0xBA, 0xCA, 0x3D, 0x1E, 0x03, 0xF2, 0xAA, 0x91, 0x11, 0x16, 0x39, 0x34, 0xE1, 0x28, + 0xE6, 0xEA, 0x1C, 0xC2, 0x15, 0xB7, 0xF3, 0x6F, 0xB9, 0x9C, 0x2D, 0xAA, 0x27, 0x25, 0xED, + 0x3A, 0x77, 0xE9, 0x30, 0x4F, 0x18, 0x12, 0x0A, 0x19, 0x78, 0x97, 0x0C, 0xA7, 0x60, 0x5C, + 0x8A, 0x3C, 0x67, 0xEF, 0x3F, 0xD3, 0x1B, 0x02, 0x7B, 0x06, 0x2A, 0x35, 0x8E, 0x32, 0xFA, + 0x48, 0xE4, 0xB0, 0xEF, 0x31, 0xD1, 0x1F, 0xE0, 0x5E, 0x8E, 0x71, 0x0E, 0x9A, 0x60, 0x08, + 0xB5, 0x4B, 0xD6, 0xAC, 0x93, 0x78, 0xC9, 0x8C, 0x62, 0x7B, 0x12, 0x59, 0x82, 0x3D, 0x6F, + 0x3E, 0xF4, 0xF0, 0xEF, 0x03, 0x81, 0x4E, 0x15, 0xED, 0xDE, 0x93, 0xF7, 0x34, 0x84, 0xE9, + 0x48, 0x27, 0x3D, 0x76, 0x64, 0xBF, 0xE6, 0x60, 0x47, 0xFC, 0x31, 0xC4, 0x68, 0xC8, 0x77, + 0x46, 0x78, 0xD2, 0x7B, 0x16, 0xB1, 0xC0, 0xDA, 0xB8, 0x80, 0xD1, 0xD7, 0x16, 0xD4, 0xDB, + 0x28, 0x89, 0x49, 0xE3, 0x35, 0x11, 0x17, 0x90, 0xF0, 0xE6, 0x6D, 0x65, 0x29, 0xB7, 0x2A, + 0x9B, 0x18, 0x2F, 0xE0, 0xB4, 0x37, 0xAA, 0xAC, 0x2B, 0x1F, 0xFC, 0x09, 0x79, 0xC1, 0x48, + 0x5C, 0x0E, 0x16, 0x38, 0x36, 0xC3, 0xA5, 0xCE, 0x49, 0xC5, 0xC8, 0x88, 0xD3, 0xEF, 0xC8, + 0xDF, 0xD6, 0xBC, 0x91, 0x73, 0x9B, 0xAD, 0x0A, 0xDD, 0x04, 0xD9, 0x20, 0xD3, 0x88, 0x42, + 0x28, 0x80, 0x3A, 0xAA, 0x2A, 0x23, 0xD9, 0xAD, 0x75, 0xD5, 0xD3, 0x9B, 0x40, 0x0E, 0x04, + 0xC9, 0xED, 0xC2, 0x91, 0x69, 0x7B, 0xB4, 0xC7, 0x1A, 0x22, 0x91, 0xEC, 0xD8, 0x95, 0x8B, + 0x02, 0xF0, 0x41, 0x56, 0x11, 0x61, 0x37, 0x68, 0x2D, 0xD0, 0xF3, 0x84, 0x93, 0x14, 0x3C, + 0x91, 0x1C, 0xF8, 0x72, 0xF5, 0x39, 0x8F, 0x07, 0x83, 0x7D, 0xF5, 0x20, 0x1B, 0xF6, 0x6A, + 0x06, 0xC1, 0x4A, 0xE2, 0x60, 0x45, 0x69, 0xD4, 0x27, 0xE9, 0xDC, 0x15, 0xB3, 0x01, 0xA3, + 0xA9, 0xFC, 0x7C, 0xFB, 0xA5, 0x02, 0x64, 0xFC, 0x73, 0x79, 0xE2, 0xBA, 0x83, 0xEE, 0xEE, + 0x68, 0xDC, 0x33, 0x52, 0x87, 0x74, 0xBA, 0x67, 0xFC, 0x1E, 0x51, 0xED, 0x3B, 0xC4, 0x08, + 0x1B, 0xDD, 0x3B, 0x18, 0x16, 0xD4, 0x0F, 0x61, 0xBB, 0xCF, 0x72, 0x11, 0xD9, 0xF4, 0x12, + 0xE1, 0x22, 0x7F, 0x3F, 0xBC, 0x5D, 0x04, 0x76, 0xF0, 0x32, 0x86, 0x45, 0x4E, 0xAB, 0xD5, + 0xD2, 0xA2, 0x25, 0xAA, 0x27, 0xA9, 0x61, 0x61, 0x6E, 0x24, 0xE7, 0x53, 0x2F, 0x28, 0x03, + 0x06, 0x64, 0xDF, 0xC2, 0xEC, 0xBC, 0xC5, 0xBE, 0x77, 0x9A, 0xF9, 0x39, 0x92, 0xE6, 0x5D, + 0x1C, 0x85, 0xE2, 0x10, 0x68, 0x8F, 0x2B, 0x6B, 0x91, 0xCC, 0xC9, 0xF1, 0xDB, 0x32, 0x6D, + 0xD9, 0x47, 0x53, 0xC3, 0xAA, 0xAE, 0xD4, 0x57, 0x1C, 0x20, 0x4F, 0x97, 0xC9, 0xE1, 0x87, + 0x1F, 0xFE, 0xCD, 0xEE, 0xF0, 0x7B, 0x88, 0xDE, 0x4F, 0x17, 0xF3, 0xCE, 0x40, 0xA5, 0x8D, + 0x01, 0x5F, 0xBE, 0xDB, 0x8A, 0x01, 0x1C, 0x61, 0x8F, 0xBB, 0x03, 0x99, 0x87, 0x63, 0xBC, + 0x60, 0xE7, 0x8C, 0x3C, 0x27, 0xAB, 0xD3, 0x70, 0x3D, 0x67, 0xB8, 0x6E, 0x3A, 0x82, 0x84, + 0xFE, 0x67, 0xD9, 0xEB, 0x59, 0xFC, 0x21, 0x69, 0x39, 0x29, 0x15, 0x7F, 0xDB, 0x0A, 0xE4, + 0x37, 0xA7, 0x36, 0xC2, 0xCF, 0xB5, 0xB6, 0x14, 0xD9, 0xB8, 0x47, 0x63, 0x5C, 0x1B, 0x8D, + 0x04, 0x75, 0x7C, 0xA6, 0x2F, 0x15, 0xF4, 0xD5, 0x36, 0xC7, 0x2D, 0x7A, 0x71, 0xD7, 0x41, + 0xD8, 0xA0, 0x7D, 0xD6, 0xA8, 0x28, 0x90, 0xB8, 0xE1, 0xF5, 0xAD, 0x59, 0xB1, 0x7E, 0x5C, + 0x85, 0xFD, 0xD7, 0x08, 0x17, 0x76, 0xEF, 0xAA, 0x0E, 0x29, 0x17, 0x14, 0xDE, 0xB9, 0x79, + 0xD5, 0xE5, 0x44, 0xBD, 0x8B, 0xE9, 0xF7, 0x9A, 0x87, 0x7E, 0x4B, 0xAD, 0x28, 0x24, 0x6A, + 0x39, 0x33, 0x00, 0x56, 0x97, 0x00, 0x4B, 0xFB, 0xE3, 0x24, 0x23, 0xAA, 0xDA, 0x98, 0x02, + 0xC5, 0x6F, 0xE1, 0xA6, 0x53, 0xA5, 0xCB, 0x95, 0x7D, 0x47, 0x20, 0xF5, 0x5D, 0x5F, 0x40, + 0x10, 0xDB, 0x40, 0x22, 0xA2, 0xCA, 0x49, 0xEC, 0xF5, 0xE7, 0xDD, 0x01, 0x45, 0xE5, 0x82, + 0xA9, 0xBC, 0xEB, 0x77, 0x3A, 0x45, 0x09, 0x7B, 0x29, 0x9A, 0x3B, 0x0D, 0xC4, 0x3B, 0x34, + 0x5D, 0x16, 0x37, 0xE8, 0x8F, 0xC1, 0xD8, 0x3E, 0x41, 0x4E, 0x24, 0xA0, 0xF6, 0xBD, 0x97, + 0x75, 0x73, 0xEF, 0x52, 0x3B, 0xBA, 0x8B, 0xA9, 0x86, 0x45, 0x15, 0xAC, 0x71, 0xEC, 0xA5, + 0xDF, 0xEF, 0xDA, 0x02, 0x3C, 0xAD, 0x07, 0xBD, 0x88, 0x74, 0x2B, 0x52, 0x1F, 0x63, 0x6B, + 0xA7, 0x08, 0xFB, 0xA4, 0x20, 0x81, 0x48, 0x22, 0xAE, 0x91, 0x32, 0x75, 0xA8, 0x51, 0xB9, + 0x0F, 0x34, 0x62, 0xB6, 0x4B, 0xF9, 0x88, 0xF8, 0xBA, 0x27, 0x18, 0xB4, 0x9F, 0x78, 0xCA, + 0xB5, 0xBA, 0xC4, 0x9F, 0x02, 0xCB, 0x50, 0xF3, 0x82, 0x1C, 0x3A, 0x35, 0x6D, 0x99, 0x45, + 0xC4, 0xBA, 0x8D, 0xBA, 0xB7, 0x47, 0x0C, 0x97, 0x79, 0x68, 0xF7, 0xB0, 0xCD, 0x66, 0xB0, + 0x7B, 0xFA, 0xCB, 0x39, 0x7C, 0x97, 0x38, 0x9E, 0xB4, 0x01, 0xC9, 0xE7, 0x85, 0xFA, 0x7A, + 0x98, 0x0E, 0x89, 0xC2, 0xF8, 0xAB, 0x0D, 0x8A, 0xD9, 0x1F, 0xA2, 0xBE, 0xF2, 0x52, 0xA3, + 0x79, 0x25, 0x21, 0x89, 0xF1, 0x53, 0x7C, 0x40, 0x4E, 0xA3, 0x18, 0x5A, 0xD8, 0xF1, 0xAB, + 0xF1, 0x72, 0xCE, 0x31, 0x01, 0x5F, 0xA5, 0xDC, 0x4E, 0x9A, 0xE5, 0x44, 0x66, 0x7A, 0xA2, + 0x12, 0x98, 0xF0, 0x1D, 0xB5, 0xD9, 0xE2, 0xC5, 0x52, 0xC2, 0x85, 0xD0, 0x7A, 0x8B, 0xC9, + 0xF5, 0xE7, 0x69, 0xEF, 0x42, 0x73, 0x2F, 0x08, 0x69, 0x0C, 0xCA, 0x61, 0xD0, 0x61, 0x34, + 0x53, 0x4E, 0x07, 0x00, 0xEF, 0x4C, 0xC7, 0x82, 0x70, 0x2F, 0x30, 0x0E, 0xB9, 0x4A, 0x0F, + 0xF4, 0x06, 0x5C, 0x11, 0xFF, 0x13, 0xCF, 0x2A, 0x3E, 0x2D, 0x1F, 0x85, 0x7F, 0x3E, 0x8E, + 0xB5, 0xDC, 0x70, 0x51, 0xF3, 0x1F, 0xD6, 0xEB, 0xF2, 0x56, 0xA1, 0xC5, 0xA4, 0xEE, 0x72, + 0x62, 0xB2, 0x42, 0xDF, 0x5F, 0xA4, 0x6D, 0xCE, 0x43, 0x7D, 0x20, 0xFD, 0xF8, 0x11, 0x82, + 0xFE, 0xAC, 0x60, 0x8A, 0xAF, 0x51, 0x5B, 0xFA, 0xED, 0xA5, 0x24, 0xC8, 0xAB, 0x1C, 0xCA, + 0xC2, 0x5E, 0x0B, 0x76, 0x81, 0xE3, 0x85, 0x64, 0x5C, 0xCF, 0xF5, 0x33, 0x73, 0x1E, 0x7D, + 0x11, 0x2E, 0x22, 0x15, 0xB3, 0x88, 0x85, 0x52, 0x43, 0x31, 0x54, 0x94, 0xC6, 0x27, 0x48, + 0x46, 0x7C, 0x49, 0xB2, 0x68, 0x5C, 0x02, 0xB4, 0xCB, 0x4C, 0x22, 0xD5, 0xB7, 0x1D, 0x3E, + 0x0F, 0x63, 0xD1, 0x11, 0xA0, 0x55, 0x08, 0x73, 0x85, 0x62, 0x13, 0x57, 0x09, 0x03, 0xCB, + 0xDB, 0x47, 0x9D, 0xD6, 0x6F, 0xBD, 0xAA, 0x05, 0x62, 0xB9, 0x3E, 0x8D, 0xE8, 0x9E, 0x1E, + 0xD8, 0x85, 0x5A, 0x77, 0xF2, 0x8A, 0x26, 0x71, 0x81, 0xE2, 0x7F, 0xF3, 0x35, 0x70, 0xE9, + 0x0B, 0x97, 0xA9, 0xFE, 0xD9, 0xB9, 0x25, 0xCF, 0x68, 0x80, 0x6D, 0x7F, 0xB5, 0x68, 0xF7, + 0x85, 0x07, 0x71, 0xD9, 0xF8, 0xEF, 0x39, 0x14, 0x80, 0x2E, 0xC2, 0x17, 0x46, 0xF7, 0xFB, + 0xE8, 0xF9, 0x10, 0xD6, 0x17, 0xCF, 0x3A, 0x4F, 0x73, 0xAE, 0x08, 0xBC, 0xC2, 0x39, 0x10, + 0x3A, 0xD6, 0xF3, 0x77, 0xA1, 0x91, 0x93, 0x5C, 0xFE, 0xF4, 0x76, 0x11, 0xD9, 0xB7, 0x25, + 0xC2, 0x9E, 0x12, 0xDA, 0x0F, 0xBD, 0x25, 0x69, 0x03, 0xA8, 0xEA, 0x78, 0x88, 0x3D, 0xA3, + 0xC9, 0xFE, 0x38, 0x9C, 0x04, 0xC6, 0x38, 0x32, 0xD2, 0x1A, 0xC0, 0xC7, 0x86, 0x09, 0x73, + 0x24, 0xFA, 0x5B, 0x66, 0x81, 0x26, 0xB3, 0x1B, 0x90, 0x14, 0xB2, 0xBE, 0x34, 0x78, 0xB1, + 0x48, 0x0F, 0x36, 0xDE, 0xCF, 0xF1, 0x06, 0x7D, 0x04, 0x7D, 0x54, 0x16, 0x2B, 0xEB, 0xC7, + 0x38, 0x17, 0x71, 0x95, 0xCC, 0xB3, 0xD9, 0x80, 0x64, 0x90, 0x0E, 0x26, 0x5A, 0x87, 0x48, + 0xFF, 0x6C, 0xA2, 0x65, 0xFE, 0xE5, 0xEA, 0x27, 0x7B, 0x03, 0xD5, 0x42, 0x68, 0xD4, 0xE5, + 0x1E, 0x09, 0x3E, 0x11, 0xAE, 0xDD, 0x07, 0x14, 0x6C, 0x3C, 0x26, 0x2E, 0x58, 0xDE, 0xE9, + 0xE7, 0x95, 0xA9, 0x6D, 0x4F, 0x6D, 0x0E, 0x2A, 0xBF, 0x02, 0xF7, 0xCF, 0x11, 0xE9, 0x39, + 0x69, 0xF5, 0x63, 0x9C, 0x62, 0xBD, 0xA7, 0x50, 0xE9, 0x5F, 0xF4, 0x67, 0xC7, 0x4C, 0xB9, + 0xBF, 0xB6, 0xD5, 0xD5, 0x0B, 0x1F, 0x8A, 0xF8, 0x97, 0x60, 0x42, 0xF2, 0xC0, 0x51, 0xC8, + 0x70, 0x97, 0x9F, 0xC3, 0xB7, 0x41, 0xDE, 0xC8, 0xE6, 0x59, 0x80, 0x28, 0x66, 0x0B, 0x3F, + 0xBE, 0x54, 0xC5, 0x1D, 0x5B, 0x7B, 0x25, 0x11, + ]; let (_pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); let ss = MLKEM1024::decaps(&sk, &ct).unwrap(); print!("{:x?}", ss); } - - fn main() { // print_struct_sizes() // bench_do_nothing() @@ -457,4 +1521,4 @@ fn main() { // bench_mlkem768_lowmemory_decaps() // bench_mlkem1024_decaps() // bench_mlkem1024_lowmemory_decaps() -} \ No newline at end of file +} diff --git a/recommendations.md b/recommendations.md new file mode 100644 index 0000000..2b0df5a --- /dev/null +++ b/recommendations.md @@ -0,0 +1,149 @@ +# PR #48 — Round 2: Comments to Post + +Each item = file, line, the exact code it anchors to, and the comment to paste. Ordered by priority. + +--- + +## 1. Keccak deserialization can panic on corrupt state (blocking) + +**File:** `crypto/sha3/src/keccak.rs` +**Line:** 420–422 (inside `KeccakDigest::from_serialized_state`) + +```rust +let bits_in_queue = u64::from_le_bytes(input[392..400].try_into().unwrap()) as usize; +if bits_in_queue > rate { + return Err(SuspendableError::InvalidData); +} +``` + +**Comment to post:** +> This only rejects `bits_in_queue > rate`. A corrupt/tampered state with `squeezing == false` and an +> odd `bits_in_queue` (or `bits_in_queue == rate`) still deserializes here, then panics on the next +> call: `absorb` hits `panic!("attempt to absorb with odd length queue")` (line 227), and +> `pad_and_switch_to_squeezing_phase` trips `debug_assert!(bits_in_queue < rate)`. Per the trait +> contract, corrupt input must return `InvalidData`, not panic. Please tighten to: +> ```rust +> if bits_in_queue > rate || (!/* squeezing */ && (bits_in_queue & 7 != 0 || bits_in_queue == rate)) { +> return Err(SuspendableError::InvalidData); +> } +> ``` +> (i.e. move this check below the `squeezing` parse and gate the extra conditions on `!squeezing`), and +> add a negative test — the current test only corrupts the `squeezing` byte, not `bits_in_queue`. + +--- + +## 2. `HashFactory` ships a knowingly-broken `Algorithm` impl (blocking) + +**File:** `crypto/factory/src/hash_factory.rs` +**Line:** 86–90 + +```rust +// TODO -- this does't work. Perhaps Algorithm needs to be re-worked so that these are functions instead? +impl Algorithm for HashFactory { + const ALG_NAME: &'static str = "TODO"; + const MAX_SECURITY_STRENGTH: SecurityStrength = SecurityStrength::None; +} +``` + +**Comment to post:** +> This public `impl Algorithm for HashFactory` ships `ALG_NAME = "TODO"` and +> `MAX_SECURITY_STRENGTH = None`, with a "this does't work" TODO. It was only added to satisfy the new +> `Hash: Algorithm` supertrait bound (so HMAC's new `Display`/`Debug` can read `HASH::ALG_NAME`), but +> `Algorithm`'s associated **consts** can't express a per-variant value for a runtime factory enum — +> hence the stub. Please don't merge a knowingly-wrong public `Algorithm` impl. Suggest sourcing HMAC's +> display name from HMAC's own `Algorithm` alias (or a small `fn alg_name(&self)`) instead of widening +> the `Hash` supertrait, so `HashFactory` doesn't need this impl at all. + +--- + +## 3. Add a "Breaking changes" changelog entry (three breaking items) + +**File:** `alpha_0.1.2_release_notes.md` +**Line:** 54 (add a section under `## Minor features / bug fixes`, or a new `## Breaking changes`) + +```markdown +## Minor features / bug fixes +``` + +The three breaking items live at: +- `crypto/sha2/src/lib.rs:75` — `pub use self::sha512::SHA512Internal;` (was `Sha512Internal`) +- `crypto/core/src/traits.rs:20` — `pub trait Hash: Algorithm + Default {` (new supertrait) +- `MLDSATrait::verify_mu_internal(->bool)` → public `verify_mu(->Result)` (mldsa & mldsa-lowmemory) + +**Comment to post:** +> Please add a "Breaking changes" section. This PR has three source-breaking public changes not listed: +> (1) `Sha512Internal` → `SHA512Internal` (sha2/src/lib.rs:75); (2) `Hash` now requires `Algorithm` as +> a supertrait (core/src/traits.rs:20), so every external `Hash` impl must now also impl `Algorithm`; +> (3) the public `MLDSATrait` method `verify_mu_internal(->bool)` became `verify_mu(->Result)`. + +--- + +## 4. `LIB_VERSION` is `core`'s version (0.1.1) and is the only compat gate + +**File:** `crypto/core/Cargo.toml` +**Line:** 3 + +```toml +version = "0.1.1" +``` + +Related anchor — **File:** `crypto/core/src/suspendable_state.rs`, **Line:** 112–113: + +```rust +let patch_stream = SemVer::from([LIB_VERSION.major, LIB_VERSION.minor, 255]); +if ver > patch_stream { +``` + +**Comment to post:** +> `LIB_VERSION` comes from `bouncycastle-core`, which is still `0.1.1` here while the release is +> `0.1.2` — so every serialized state gets stamped `0.1.1`. Two asks: (a) reconcile `core`'s version +> with the release; (b) since this stamp is the *only* compat gate and the policy (suspendable_state.rs:112) +> accepts any future patch on the same major.minor, any change to a serialized layout MUST bump `core`'s +> **minor** (never just the patch) or old readers will silently misparse newer blobs. Worth a one-line +> maintainer note next to `check_lib_ver`. + +--- + +## 5. (Optional, style) Uncommented infallible `.unwrap()`s in serialization paths + +QUALITY_AND_STYLE requires each `.unwrap()` to carry a justification. These fixed-size slice→array +unwraps are infallible by const construction but uncommented: + +- `crypto/sha2/src/sha256.rs:322` — `let out: &mut [u8; 105] = add_lib_ver(&mut out_to_return).try_into().unwrap();` +- `crypto/sha2/src/sha256.rs:351` — `let input: &[u8; 105] = check_lib_ver(&serialized_state, None)?.try_into().unwrap();` +- `crypto/sha2/src/sha512.rs:337` — `add_lib_ver(&mut out_to_return).try_into().unwrap();` +- `crypto/sha2/src/sha512.rs:364` — `check_lib_ver(&serialized_state, None)?.try_into().unwrap();` +- `crypto/hkdf/src/lib.rs:852` and `:871` — `state[..].try_into().unwrap()` + +**Comment to post (put on sha256.rs:322, reference the rest):** +> Style nit per QUALITY_AND_STYLE: these `.try_into().unwrap()`s are infallible by const sizing but +> lack the required justification comment. Suggest a one-line `// infallible: slice is exactly N bytes +> by const construction` on each (same pattern in sha512.rs:337/364 and hkdf/src/lib.rs:852/871), and +> a `quality_stats.sh` before/after to confirm the fallibility count didn't rise. + +--- + +## 6. (Separate / pre-existing) wycheproof sign harness fails on the `Randomized` case + +**File:** `crypto/mldsa/tests/wycheproof.rs` +**Line:** 353 (and identically 421, 489, and the `sign_seed` equivalents) + +```rust +let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); +assert_eq!(sig, hex::decode(&self.sig).unwrap().as_slice()); +``` + +**Comment to post:** +> Not introduced by this PR (pre-existing on base), but the 6 `*_sign_*` wycheproof tests fail because +> the harness signs every case with a hardcoded all-zero `rnd` and byte-compares to `sig`, while each +> file's one `Randomized`-flagged case (tcId 73/90/78/109/69/100) was generated with a non-zero `rnd` +> (per wycheproof `doc/mldsa.md` lines 33–36). The `MLDSASign*TestCase` struct doesn't parse `rnd`. +> Fix: skip cases flagged `Randomized` / carrying an `rnd` field, or parse `rnd` and pass it through. +> Worth a tracking issue so the suite goes green. + +--- + +## Just reply "resolved" (no code-anchored comment needed) + +F1 (HKDF hmac/state consistency), F3 (HKDF version header), F5 (HMAC `Secret`/`Drop`), F6 +(`IncorrectKey` removed), F9 (version policy) — all addressed in round 2; close them with a reply. diff --git a/src/bench_mldsa_mem_usage.rs b/src/bench_mldsa_mem_usage.rs new file mode 100644 index 0000000..55bdc24 --- /dev/null +++ b/src/bench_mldsa_mem_usage.rs @@ -0,0 +1,469 @@ +//! The purpose of this binary is to perform a single run of the primitive under test so that +//! its peak memory usage can be measured with: +//! +//! > valgrind --tool=massif --heap=no --stacks=yes -- target/release/bench_mldsa_mem_usage > /dev/null +//! +//! > ms_print massif.out.835000 +//! +//! or, shoved all into one line: +//! +//! > clear; clear; valgrind --tool=massif --heap=no --stacks=yes -- target/release/bench_mldsa_mem_usage > /dev/null; ms_print massif.out.*; rm massif.out.* +//! +//! Make sure you build in release mode! +//! +//! Note: I'm using print!() to force the compiler not to optimize away the actual code. +//! I'm printing the important stuff for benchmarking to stderr so that I can pipe the junk to /dev/null +//! (I'm not doing it the other way because /usr/bin/time prints its useful stuff to stderr as well) +//! +//! Main is at the bottom, controls which this was actually run. + +#![allow(dead_code)] +#![allow(unused_imports)] + +use bouncycastle_core_interface::key_material::{KeyMaterial256, KeyType}; +use bouncycastle_core_interface::traits::{Signature, SignaturePublicKey}; +use bouncycastle_hex as hex; +use bouncycastle_mldsa::MLDSA44PublicKey; + +/// This exists so I can use /usr/bin/time to measure the base memory footprint of the cargo bench harness +fn bench_do_nothing() { + eprintln!("DoNothing"); + + print!("{}", 1 + 1); +} + +fn bench_mldsa44_keygen() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA44}; + + eprintln!("MLDSA44/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa44_lowmem_keygen() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA44}; + + eprintln!("MLDSA44_lowmemory/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa65_keygen() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA65}; + + eprintln!("MLDSA65/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa65_lowmemory_keygen() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA65}; + + eprintln!("MLDSA65_lowmemory/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa87_keygen() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA87}; + + eprintln!("MLDSA87/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa87_lowmemory_keygen() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA87}; + + eprintln!("MLDSA87_lowmemory/KeyGen"); + + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let (pk, _sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + println!("{:x?}", pk.encode()); +} + +fn bench_mldsa44_sign() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA44}; + + eprintln!("MLDSA44/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /*** ML-DSA-44 ***/ + // since the goal here is to measure peak memory usage; we're here making an assumption that + // mem usage of .sign will be higher than .keygen + let (_mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA44::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa44_lowmemory_sign() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA44}; + + eprintln!("MLDSA44_lowmemory/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /*** ML-DSA-44 ***/ + let (_mldsa44_pk, mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA44::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa65_sign() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA65}; + + eprintln!("MLDSA65/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA65::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA65::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa65_lowmemory_sign() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA65}; + + eprintln!("MLDSA65_lowmemory/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /*** ML-DSA-44 ***/ + let (_mldsa44_pk, mldsa44_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA65::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + let sig = MLDSA65::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa87_sign() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA87}; + + eprintln!("MLDSA87/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA87::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA87::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa87_lowmemory_sign() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA87}; + + eprintln!("MLDSA87_lowmemory/Sign"); + + // set up the seeds outside of the timing loop + // Doing different seeds so that the CPU doesn't cache them or do too much branch prediction + let seed = KeyMaterial256::from_bytes_as_type( + &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + KeyType::Seed, + ).unwrap(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /*** ML-DSA-44 ***/ + let (_mldsa44_pk, mldsa44_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + + let mu = MLDSA87::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + let sig = MLDSA87::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); +} + +fn bench_mldsa44_verify() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA44/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa44_pk, _mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + // eprintln!("pk:\n{}", &*hex::encode(&mldsa44_pk.encode())); + // let mu = MLDSA44::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + // let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa44_pk = MLDSA44PublicKey::from_bytes(&*hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap()).unwrap(); + let sig = &*hex::decode("5e93b785c5119c3983a291b18420fdbe4bca53d5a3732922faaacd5a5d32a745c78d105ba10bee1ed8069f19e6c537bda16e89d39004c359d1fd381a0291f1c51f1c38edcdb315c8c69570d8f25f1655ba8ea83aff24b8b6be8de762342e347eab2caa6803ed705952dd6450c5185e9d60ce96e8dca423a02f646cea690164a226e4c3d6a515ce16290f19b2c626da9b450ecf665013c5e226b6c0ac5c07ce90e278f1b0134e385d13e74208a0b3ff052a362579f9207ea01f18a039aa1b97ae3452675b620771f8012ee7a4e55c98bfd2019ed8a3b00acea8e8ab28172faa42ca1fda83c5ffe81a45be736bdedd5fb300ce17078b380f620bdeebad693601372c85eacf79bc98e1b48f2ad7e5dce4279a1295bb2ba60a0c5e3726642d2336c5eb1d37c8623c7558241318d89bc783c4f00098077484623c217560a0c7aaf75dcaccb78ee69c207c27c8bf3965ccf58a80c88efcc7e5deb3615d5045a741c4dac0a021dd060d315d4ec2857eb664d728d0af973bea07e1ca563faa0e19996cea3770316c11a5066665662005ace98f6110e883bae060daa7b6d83379e0878796691708a32b85730de8b92d89f90a3660c949165b14612567662e162232296cbd143517a282e22c46b63606d3c14ed4559a5a1c459bab7f355007ad6f7e3b1e07445dfc96bd9b75080b3d4f68998490a26b5e090be2674071ab925bb650590856c59f8ba7488d2b72f840ac3eafe4dd91f0f51c4364112c1a139e3e942a597b93a1e3f4faded129c14b5978b315e2246a93146a79365f0f597a18340cca86bb15ceed39f175eab1e546535afb966f0a65a8f66f737ab02897eddfe92cf7786894843c2691464776c94bd450a1069138b26df83b2d1dd801143a8fdfdc2514cc5b5831ab53a75c55ef29f40e7c63d2c72abe97e2af14853be49be16f4730a159974970951439e55c1589d0f4a162e3517df9d7abc98d8a307216e7f1cb4627c9175c0eef23337e56d5281b83726fff40a148b0c48e8df3496a2118d80219aef8f40b29fba1f2f78786b67ffb7b7d47d406b765bd136610bedeb95cd7321f58f3b836c9258be35d78b498f3efe1db2b243d734fab159baed8807c3cccf83eb2eaf8a9af01a518d48c60e91a96812ad689c2d83cc4e8e9b3650422bed6f13c24adaad91c95b3e3cf354f0f6bc9ee8941a6b15b6975131d95233d8935de367efc6d86a45dac7d0f1ddd9aebd2c59c027fcda448801e93e733aca51874be9ab927a904f96ddb7a46b2da13261d522b23c950c01d5f5e112b76f851ff234f06f8d5e65b1319abcd79a180ae063d65b28c745878c06dbb69ba73293eab34434bf1a92fba691993bd0ff3edac76a12f80c0ada4b1969c7665589d530a67016a625403c537032904f2e104547cd3ea406260dd357fa06ea012a785826c160e99ffd065b0e3f33c7689d3552ab9e2e09fa7e55bbcef042242bcacad8a3da47bcc54a121f1526c8cd4cc5a892a8131cf4eefaf4248ddd6a11ec427ba378aae89aaf582ce1f4e32690a555e740761d358ad4e92bc38418aa782da916524fb09ab2ca6b3d3113d6f2c2a6a9b9d29d4e7489255252af075cbf9feacedae6f3ec0b070824689dd3c78ac143ed6776d95dd8f13d435a290bdca4c11318e5acce04469644e1374a9451b6204f3b3961b7dd239e306fef5f4f4e51b78b0fb9dcee69c3e790b231f2e65fd1ab1c2a75b07067d5c16dde00983a58ffcdaaaee16d2742e133ed737b48064c8a38eca35ab3fa18f6d62f642b12cfdc7980f2ab7db321fec9dcfe499b4fc1ee7eb297954056617c60a6640b92835d165c3c00a951952614488d5657ba0b5e90ae9e0ef7b3b9ecaebd81b8551b6d70e835b2734761639d42e76ffc5b3272b61c896b45b4bd18f30e58c440643ba159221cc6739a19a65f2911fae47b0d4cac4200a6f043b17a03ad393ecb823ed03c8b6cd68167e6c8234f7432557db272079ee899aede73b6b98d6003f45789a141b60d6db40cd2a5974571a4ad3667b889318ba60285d903a2eac01c21608838c40907de6bbabe042cf2ecdd97f549f95ec698d79222c65ba27c30d332a68d057aecdc9388aa34320e0aa74fdbd4d1b643cace216b6d8ad8f07a99955bfdb743a86b40fc61527baca434ac2a7fbeaa77111dc8098b17e800f59dd77ccb0e67707e60123d334e073a2f5a16ffbcd701389add57c3ceccb88b286ac1e6e3e6485af1a12ea241d14a1b5003d7f3bc9e957d4483c0f9f703b3a187d55e505817615fbc4ae0837616184245cfba61ce3b929e33f52b71cdd7b6a0da55c1f997510b1a9002ca4e0678373a3b1ab2897e6b423f15a440a636cc861491ef41ad0aa627d8e198a5ee7bd7b6cb2c9ce2a8cc015f0d206de4c49e2f87f310954a10d86e294f742ee186f4ae9815f699622792206cafba8f5621738160e6c5d611a8252c6f35085b604ef895164d4ea6ddd310c7d8f0c879fb1f884c5741d096b3d2da0ce1151790dda881d18cb6b19a9fed6f5254b7d52d5d92bbbe24c9d6a65604a0b8ed24ad5c197d683f598743c96b5960e8723732b5bd647e9dbeaa851d0e1cf6d2c070d4442762c28098c5cf5a54b2b5e69a99b10815bf0f477bb71f0d5d3a62ba2b3e29bf84d4b4e574707f5f74af704d277bd6ca38da21e2cdac549e5eae1de7a18ee534c8c2291c908caabf159e90e6549db94ba7a3f3d97dd398a75df5b1a7cdfb25410b7efc4ed00d9995b37b58bf91ed7a3510cffea82f9e1c2a3290406004d09057d63b770fa0e53103199544eba662a2c302cf39008f142d2b16963e95ab10be7c2610168608f353a2f2c41c7056dec1a8c7a6bfa0027f9dedacb7786b67ea2c494d43ba851cf9415c1bcc52f027ec02c65534f608e9d166d51dd431cdf5871f5cdd1579cc06079df075a25062ba7e70d9666c4e7fed34cea0ea0f11ade1eb2a9b397bcaaad1061270ecf497803a5fce7f41e6504fbec71a7de7d066b8261868afc49b9e685f0dcce75e2fcb3ba8cf19057e3941576baf58fb821bd4268f7fae3028601da022e9b468646abdb4fa6098a449b4267d509d9a33f4c3ebcc32dac094d48ed600e765787fb92b1974f74f7bb4c66eb2bbd02895e6a381c1c452eaab1ae4731cf632f61ae2c905921174a3bc9bb4cdc89d630264b614988f3abbea1bd617ffa53d71b7d8a371462b773351a2dccaedd7f59cd728fadee059067bd80c94c8c9a1ffca2dc4f848b829c0561385aa82cc98503d0bb66a6aa4fae0703d12e60e1460efbbcdf2412c13e7c684d1b01102026343a414344585f6e7072748baeb5bbc6d1e2effbfe060e2e3e5160797c9ea6bac7f11024404a52575f6c898c97aab2c3cceaf22f3f535f7b818396a1b1bce6000000000000000000000000000018253642").unwrap(); + assert_eq!(sig.len(), MLDSA44_SIG_LEN); + + if MLDSA44::verify(&mldsa44_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + +fn bench_mldsa44_lowmemory_verify() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA44_lowmemory/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa44_pk, _mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + + // eprintln!("pk:\n{}", &*hex::encode(&mldsa44_pk.encode())); + // let mu = MLDSA44::compute_mu_from_sk(&mldsa44_sk, msg, None).unwrap(); + // let sig = MLDSA44::sign_mu_deterministic(&mldsa44_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa44_pk = MLDSA44PublicKey::from_bytes(&*hex::decode("d7b2b47254aae0db45e7930d4a98d2c97d8f1397d1789dafa17024b316e9bec94fc9946d42f19b79a7413bbaa33e7149cb42ed5115693ac041facb988adeb5fe0e1d8631184995b592c397d2294e2e14f90aa414ba3826899ac43f4cccacbc26e9a832b95118d5cb433cbef9660b00138e0817f61e762ca274c36ad554eb22aac1162e4ab01acba1e38c4efd8f80b65b333d0f72e55dfe71ce9c1ebb9889e7c56106c0fd73803a2aecfeafded7aa3cb2ceda54d12bd8cd36a78cf975943b47abd25e880ac452e5742ed1e8d1a82afa86e590c758c15ae4d2840d92bca1a5090f40496597fca7d8b9513f1a1bda6e950aaa98de467507d4a4f5a4f0599216582c3572f62eda8905ab3581670c4a02777a33e0ca7295fd8f4ff6d1a0a3a7683d65f5f5f7fc60da023e826c5f92144c02f7d1ba1075987553ea9367fcd76d990b7fa99cd45afdb8836d43e459f5187df058479709a01ea6835935fa70460990cd3dc1ba401ba94bab1dde41ac67ab3319dcaca06048d4c4eef27ee13a9c17d0538f430f2d642dc2415660de78877d8d8abc72523978c042e4285f4319846c44126242976844c10e556ba215b5a719e59d0c6b2a96d39859071fdcc2cde7524a7bedae54e85b318e854e8fe2b2f3edfac9719128270aafd1e5044c3a4fdafd9ff31f90784b8e8e4596144a0daf586511d3d9962b9ea95af197b4e5fc60f2b1ed15de3a5bef5f89bdc79d91051d9b2816e74fa54531efdc1cbe74d448857f476bcd58f21c0b653b3b76a4e076a6559a302718555cc63f74859aabab925f023861ca8cd0f7badb2871f67d55326d7451135ad45f4a1ba69118fbb2c8a30eec9392ef3f977066c9add5c710cc647b1514d217d958c7017c3e90fd20c04e674b90486e9370a31a001d32f473979e4906749e7e477fa0b74508f8a5f2378312b83c25bd388ca0b0fff7478baf42b71667edaac97c46b129643e586e5b055a0c211946d4f36e675bed5860fa042a315d9826164d6a9237c35a5fbf495490a5bd4df248b95c4aae7784b605673166ac4245b5b4b082a09e9323e62f2078c5b76783446defd736ad3a3702d49b089844900a61833397bc4419b30d7a97a0b387c1911474c4d41b53e32a977acb6f0ea75db65bb39e59e701e76957def6f2d44559c31a77122b5204e3b5c219f1688b14ed0bc0b801b3e6e82dcd43e9c0e9f41744cd9815bd1bc8820d8bb123f04facd1b1b685dd5a2b1b8dbbf3ed933670f095a180b4f192d08b10b8fabbdfcc2b24518e32eea0a5e0c904ca844780083f3b0cd2d0b8b6af67bc355b9494025dc7b0a78fa80e3a2dbfeb51328851d6078198e9493651ae787ec0251f922ba30e9f51df62a6d72784cf3dd205393176dfa324a512bd94970a36dd34a514a86791f0eb36f0145b09ab64651b4a0313b299611a2a1c48891627598768a3114060ba4443486df51522a1ce88b30985c216f8e6ed178dd567b304a0d4cafba882a28342f17a9aa26ae58db630083d2c358fdf566c3f5d62a428567bc9ea8ce95caa0f35474b0bfa8f339a250ab4dfcf2083be8eefbc1055e18fe15370eecb260566d83ff06b211aaec43ca29b54ccd00f8815a2465ef0b46515cc7e41f3124f09efff739309ab58b29a1459a00bce5038e938c9678f72eb0e4ee5fdaae66d9f8573fc97fc42b4959f4bf8b61d78433e86b0335d6e9191c4d8bf487b3905c108cfd6ac24b0ceb7dcb7cf51f84d0ed687b95eaeb1c533c06f0d97023d92a70825837b59ba6cb7d4e56b0a87c203862ae8f315ba5925e8edefa679369a2202766151f16a965f9f81ece76cc070b55869e4db9784cf05c830b3242c8312").unwrap()).unwrap(); + let sig = &*hex::decode("5e93b785c5119c3983a291b18420fdbe4bca53d5a3732922faaacd5a5d32a745c78d105ba10bee1ed8069f19e6c537bda16e89d39004c359d1fd381a0291f1c51f1c38edcdb315c8c69570d8f25f1655ba8ea83aff24b8b6be8de762342e347eab2caa6803ed705952dd6450c5185e9d60ce96e8dca423a02f646cea690164a226e4c3d6a515ce16290f19b2c626da9b450ecf665013c5e226b6c0ac5c07ce90e278f1b0134e385d13e74208a0b3ff052a362579f9207ea01f18a039aa1b97ae3452675b620771f8012ee7a4e55c98bfd2019ed8a3b00acea8e8ab28172faa42ca1fda83c5ffe81a45be736bdedd5fb300ce17078b380f620bdeebad693601372c85eacf79bc98e1b48f2ad7e5dce4279a1295bb2ba60a0c5e3726642d2336c5eb1d37c8623c7558241318d89bc783c4f00098077484623c217560a0c7aaf75dcaccb78ee69c207c27c8bf3965ccf58a80c88efcc7e5deb3615d5045a741c4dac0a021dd060d315d4ec2857eb664d728d0af973bea07e1ca563faa0e19996cea3770316c11a5066665662005ace98f6110e883bae060daa7b6d83379e0878796691708a32b85730de8b92d89f90a3660c949165b14612567662e162232296cbd143517a282e22c46b63606d3c14ed4559a5a1c459bab7f355007ad6f7e3b1e07445dfc96bd9b75080b3d4f68998490a26b5e090be2674071ab925bb650590856c59f8ba7488d2b72f840ac3eafe4dd91f0f51c4364112c1a139e3e942a597b93a1e3f4faded129c14b5978b315e2246a93146a79365f0f597a18340cca86bb15ceed39f175eab1e546535afb966f0a65a8f66f737ab02897eddfe92cf7786894843c2691464776c94bd450a1069138b26df83b2d1dd801143a8fdfdc2514cc5b5831ab53a75c55ef29f40e7c63d2c72abe97e2af14853be49be16f4730a159974970951439e55c1589d0f4a162e3517df9d7abc98d8a307216e7f1cb4627c9175c0eef23337e56d5281b83726fff40a148b0c48e8df3496a2118d80219aef8f40b29fba1f2f78786b67ffb7b7d47d406b765bd136610bedeb95cd7321f58f3b836c9258be35d78b498f3efe1db2b243d734fab159baed8807c3cccf83eb2eaf8a9af01a518d48c60e91a96812ad689c2d83cc4e8e9b3650422bed6f13c24adaad91c95b3e3cf354f0f6bc9ee8941a6b15b6975131d95233d8935de367efc6d86a45dac7d0f1ddd9aebd2c59c027fcda448801e93e733aca51874be9ab927a904f96ddb7a46b2da13261d522b23c950c01d5f5e112b76f851ff234f06f8d5e65b1319abcd79a180ae063d65b28c745878c06dbb69ba73293eab34434bf1a92fba691993bd0ff3edac76a12f80c0ada4b1969c7665589d530a67016a625403c537032904f2e104547cd3ea406260dd357fa06ea012a785826c160e99ffd065b0e3f33c7689d3552ab9e2e09fa7e55bbcef042242bcacad8a3da47bcc54a121f1526c8cd4cc5a892a8131cf4eefaf4248ddd6a11ec427ba378aae89aaf582ce1f4e32690a555e740761d358ad4e92bc38418aa782da916524fb09ab2ca6b3d3113d6f2c2a6a9b9d29d4e7489255252af075cbf9feacedae6f3ec0b070824689dd3c78ac143ed6776d95dd8f13d435a290bdca4c11318e5acce04469644e1374a9451b6204f3b3961b7dd239e306fef5f4f4e51b78b0fb9dcee69c3e790b231f2e65fd1ab1c2a75b07067d5c16dde00983a58ffcdaaaee16d2742e133ed737b48064c8a38eca35ab3fa18f6d62f642b12cfdc7980f2ab7db321fec9dcfe499b4fc1ee7eb297954056617c60a6640b92835d165c3c00a951952614488d5657ba0b5e90ae9e0ef7b3b9ecaebd81b8551b6d70e835b2734761639d42e76ffc5b3272b61c896b45b4bd18f30e58c440643ba159221cc6739a19a65f2911fae47b0d4cac4200a6f043b17a03ad393ecb823ed03c8b6cd68167e6c8234f7432557db272079ee899aede73b6b98d6003f45789a141b60d6db40cd2a5974571a4ad3667b889318ba60285d903a2eac01c21608838c40907de6bbabe042cf2ecdd97f549f95ec698d79222c65ba27c30d332a68d057aecdc9388aa34320e0aa74fdbd4d1b643cace216b6d8ad8f07a99955bfdb743a86b40fc61527baca434ac2a7fbeaa77111dc8098b17e800f59dd77ccb0e67707e60123d334e073a2f5a16ffbcd701389add57c3ceccb88b286ac1e6e3e6485af1a12ea241d14a1b5003d7f3bc9e957d4483c0f9f703b3a187d55e505817615fbc4ae0837616184245cfba61ce3b929e33f52b71cdd7b6a0da55c1f997510b1a9002ca4e0678373a3b1ab2897e6b423f15a440a636cc861491ef41ad0aa627d8e198a5ee7bd7b6cb2c9ce2a8cc015f0d206de4c49e2f87f310954a10d86e294f742ee186f4ae9815f699622792206cafba8f5621738160e6c5d611a8252c6f35085b604ef895164d4ea6ddd310c7d8f0c879fb1f884c5741d096b3d2da0ce1151790dda881d18cb6b19a9fed6f5254b7d52d5d92bbbe24c9d6a65604a0b8ed24ad5c197d683f598743c96b5960e8723732b5bd647e9dbeaa851d0e1cf6d2c070d4442762c28098c5cf5a54b2b5e69a99b10815bf0f477bb71f0d5d3a62ba2b3e29bf84d4b4e574707f5f74af704d277bd6ca38da21e2cdac549e5eae1de7a18ee534c8c2291c908caabf159e90e6549db94ba7a3f3d97dd398a75df5b1a7cdfb25410b7efc4ed00d9995b37b58bf91ed7a3510cffea82f9e1c2a3290406004d09057d63b770fa0e53103199544eba662a2c302cf39008f142d2b16963e95ab10be7c2610168608f353a2f2c41c7056dec1a8c7a6bfa0027f9dedacb7786b67ea2c494d43ba851cf9415c1bcc52f027ec02c65534f608e9d166d51dd431cdf5871f5cdd1579cc06079df075a25062ba7e70d9666c4e7fed34cea0ea0f11ade1eb2a9b397bcaaad1061270ecf497803a5fce7f41e6504fbec71a7de7d066b8261868afc49b9e685f0dcce75e2fcb3ba8cf19057e3941576baf58fb821bd4268f7fae3028601da022e9b468646abdb4fa6098a449b4267d509d9a33f4c3ebcc32dac094d48ed600e765787fb92b1974f74f7bb4c66eb2bbd02895e6a381c1c452eaab1ae4731cf632f61ae2c905921174a3bc9bb4cdc89d630264b614988f3abbea1bd617ffa53d71b7d8a371462b773351a2dccaedd7f59cd728fadee059067bd80c94c8c9a1ffca2dc4f848b829c0561385aa82cc98503d0bb66a6aa4fae0703d12e60e1460efbbcdf2412c13e7c684d1b01102026343a414344585f6e7072748baeb5bbc6d1e2effbfe060e2e3e5160797c9ea6bac7f11024404a52575f6c898c97aab2c3cceaf22f3f535f7b818396a1b1bce6000000000000000000000000000018253642").unwrap(); + assert_eq!(sig.len(), MLDSA44_SIG_LEN); + + if MLDSA44::verify(&mldsa44_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + +fn bench_mldsa65_verify() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA65/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // + // eprintln!("pk:\n{}", &*hex::encode(&mldsa65_pk.encode())); + // let mu = MLDSA65::compute_mu_from_sk(&mldsa65_sk, msg, None).unwrap(); + // let sig = MLDSA65::sign_mu_deterministic(&mldsa65_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa65_pk = MLDSA65PublicKey::from_bytes(&*hex::decode("48683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d05b38dbb2edf01e664efd81be1ea893688ce68aa2d51c5958f8bbc6eb4e89ee67d2c0320954d57212cac7229ff1d6eaf03928bd51511f8d88d847736c7de2730d5978e5410713160978867711bf5539a0bfc4c350c2be572baf0ee2e2fb16ccfea08028d99ac49aebb75937ddce111cdab62fff3cea8ba2233d1e56fbc5c5a1e726de63fadd2af016b119177fa3d971a2d9277173fce55b67745af0b7c21d597dbeb93e6a32f341c49a5a8be9e825088d1f2aa45155d6c8ae15367e4eb003b8fdf7851071949739f9fff09023eaf45104d2a84a45906eed4671a44dc28d27987bb55df69e9e8561f61a80a72699503865fed9b7ee72a8e17a19c408144f4b29afef7031c3a6d8571610b42c9f421245a88f197e16812b031159b65b9687e5b3e934c5225ae98a79ba73d2b399d73510effad19e53b8450f0ba8fce1012fd98d260a74aaaa13fae249a006b1c34f5ba0b882f26378222fb36f2283c243f0ffeb5f1bb414a0a70d55e3d40a56b6cbc88ae1f03b7b2882d98deea28e145c9dedfd8eaf1cef2ed94a8b050f8964f46d1ea0d0c2a43e0dda6182adbf4f6ed175b6742257859bf22f3a417ecf1f9d89317b5e539d587af16b9e1313e04514ffa64ba8b3ff2b8321f8811cb3fb022c8f644e70a4b80a2fbfee604abb7379091ea8e6c5c74dfc0283666b40c0793870028204a136bf5da9568eb798d349038bdb0c11e03445e7847cb5069c75cf28ac601c7799d958210ddbcb226e51afef9f1de47b073873d6d3f97456bede085082e74a298b2cd48f4b3093155f366c8fa601c6af858dfa32c08491b2a29887f90335949a5d6edaa679882a3a95d6bf6d970a221f4b9d3d8cbf384af81aac95e2b3294e04789ac83727a5dc04559f96af41d8a053516feeeebc52746eb6ab2819e09108710d835f011fa63065872ad334d5cdffb2b2310507e92fc993ae317da97f4f309cdaf0f67ed99d90215576083849f953b246d7fedb3fdb67679850a5ad404e64147fb7cf4f6aeddd05afb4b834968d1fe88014960dce5d942236526e12a478d69e5fbe6970310b308c06845018cfc7b2ab430a13a6b1ac7bb02cccbb3d911ac2f11068613fbe029bfdce02cf5cd38950ed72c83944edfbc75615af87f864c051f3c55456c5412863a40c06d1dab562bdff0571b8d3c3917bbd300880bba5e998239b95fa91b7d6416d4f398b3adbcd30983ed3592b4d9ef7d4236fd00f50d98aa53a235ac4172720f77d96172672980cfe8ff7a5a702783edc2ba31b2259015a112fc7f468a9c2f9464039002d30ef678b4cb798bc116216bf7a9a7c18ba03b7b58fd07515d3115049d3614be7a07e744300750df1d2c58753389059eafc3d785ccdd31c07648bedc03a5c3b8ad46d064d59c13d57374729fc4e295362e2a5191204530428bc1522afa28ff5fe1655e304ca5bc8c27ad0e0c6a39dd4df28956c14b38cc93682cefe402bbd5e82d29c464e44eb5d37b48fc568dfe0cc6e8e16baea05e5135590f19294e73e8367b0216dbb815030b9de55913f08039c42351c59e5515dd5af8e089a15e625e8f6dee639386c46497d7a263288774de581a7de9629b41b4424141f978fb8331208efdec3c6e0de39bc57063f3dcd6c470373c08891ea29cbc7cc6d6483b8889083ace86aa7b51b1c2cfe6e2ad18d97ce36fbc56ea42fae97e6a7ac114864478c366df1ebb1e7b11a9098504fd5975bdf1f49dc70002b63c1739a9d263fbad4073f6a9f6c2b8af4b4c332a103a0cffa5deeb2d062ca3c215fd360026be7c5164f4a4424ef74948804d66f46487732c8202c795478647b4ea71d627c086024cca354a41f0877b38f19b3774ad2095c8da53b069e21c76ae2d2007e16719ed40080d334f7da52e9f5a5990439caf083a95b833f02ad10a08c1a6d0f260c007285bd4a2f47703a5aef465287d253b18ac22514316210ff566814b10f87a293d6f199d3c3959990d0c1268b4f50d5f9fcefbbf237bd0c28b80182d6659741f14f10bfbb21bba12ab620aa2396f56c0686b4ea9017990224216b2fe8ad76c4a9148eef9a86a3635a6aa77bc1dcfb6fba59a77dfda9b7530dc0ca8648c8d973738e01bab8f08b4905e84aa4641bd602410cd97520265f2f231f2b35e15eb2fa04d2bd94d5a77abaf1e0e161010a990087f5b46ea988b2bc0512fda0fa923dadd6c45c5301d09483673265b5ab2e10f4ba520f6bbad564a5c3d5e27bdb080f7d20e13296a3181954c39c649c943ebe17df5c1f7aae0a8fe126c477585a5d4d648a0d008b6af5e8cd31be69a9296d4f3fd25ed86f221e4b93f65f5929967533624b9235750c30707550b58536d109a7131c5a5bbe4a5715567c12534aec7660761eebb9fae2891c774589b80e566ad557ddef7367196b7227ea9870ef09ddfec79d6b9319a6879b5205d76bf7aba5acf33afb59d17fc54e68383d6be5a08e9b66da53dcde008bb294b8582bd132cdcc49959fdbc21e52721880c8ad0352c79f03a43bbd84c4cdfdc6c529005e1e7cd9a349a7168a35569ba5dea818968d5a91466bd6e64e20bf62417198afc4e81c28dd77ed4028232398b52fbde86bc84f475b9016710ce2aabc11a06b4dbac901ec16cf365ca3f2d53813948a693a0f93e79c46ca5d5a6dca3d28ca50ad18bd13fca55059dd9b185f79f9c47196a4e81b2104bc460a051e02f2e8444f").unwrap()).unwrap(); + let sig = &*hex::decode("9061f15cbf2092f744fbcd799eb02414053c1b0f7412124bedc41cf9a3db0166469e874037d7f081e5f8d3d2033a0307d1c49ed01fe64578c4a6fabd80880cdf1911848f184d4bcf536ca795a0fb1aa19ab7ee3ba6b58bd64bbeac9f58650fff1ef5a97ab6916df962072e20e7c6be96090e3a781a504bc4442bd8889a0aa628907a74299f39fa836031f1bd68355bebe7ae93c1e361a9efbed1325d96227070461fcd6f151b8669d9229b977d9ee51fd2260c3e4a2e820416f9e074958dc3b3e2217e6312b7e0b582a048981cf6579f4bc7715b78c808e4c57e3b8aa38b05c04fcedf209f52c1e331ae83dbdff60ba450a17cc397568e54bc3f16ddf30b92747ce460d925b9be20a1d35e2aed97f124af2616a5361df28ba30e522dd08fa00fd28d1ac484d756a89e3a442fefe8332c56cd2a9fde691bdbda43f1cc54cef57bead96120b50c7d4695bdbb1303cc5ddda898e4eeb83083176e40e0232cdd1c3150371df05d6fdad7e1164d90393cf308e99edfeb31fed263e2866ee3b7f3937b399c974d87ba7b489efe3c9b80371d2928446adc31991ab0cefaaa080575b9ec81cfa133a9911c035a8058d0d3f2e34de4a9fb009bb4ccdb16de7b908574a7496725ff857556c1b33917e986c80f1014a9e3083add2fb35f345c5d06159e443329d0da099987b996c3731592b460c2ffd2955f7546f4216100ba43188803ff9b36969685f909fa2539323b8c8ec1c095a5085e554dd450e0e67ab670b6a11ebf6c25520fc13e364060f91f9b7f3d5cb48ff28b8fc83d4293f1f35ad6ff6ae4574ad7a1c6005fc0389a7b21386b0850a05d832fe6a14bb2b1db1f8e20bd09174946cd098b81c8f797e95f2143a949770cf1219bfef039db51a80fc247f65f41554c7173dd805ba82fdf47ab6d4bfd37dfe46fc47904421ae00dc005a22f9c4784b0ea9e665392a412245016d5c6d7673a6a180d228d4255a538e451ffd8b414d40304c0c888992e0ab6de1602109527417bc1c7eb782ae77a8c3cdfc1d13a1e874207898264e38080243109c5969649ac8383417e922ba115331142d0ed35440b15d40bee0cf58af37c0f0524ffac1c71ceed3bb82f76ab108a8ad1a0c8b78d9341148c642369be7bef59d46f49d70c83560607f140848ec9a7607d4a08f8b6e4447f5523f416981888a8de9647ffef79389e4983e5c9387698d0cc2d429322365ce7e7b5fd6d6eb921c813fcf06199fe1ca41e9cfe03b539f321671a2acad0963f876f9db7a1c4371b9f101005217995b5b6a40976246d245da603dba8dac812a5480c3476a99d0ffdf0ef943d72d912543148b2fe78e8b0159324fe9bcd4ced33cd212fe4f3dfd6d4c5e1958beb95ac6b533ace3e78015e3880b52bf45299263a4c0096f8ba5fe3a6298cab675cb7f382e7ef49720eb4cf47376e2d2574122ccf91129c858e948904fecefb91226ed42403ba12dd3258909a87dfcbf65cc3adc3d98d277fdcec7664e2292b7d27afbb5aafb405c20a34b2fe2c0849ee280bb891dfdf59f19b89b0246358db54cf3fdc66eaaaa750c8903f1d42678f3edf0b7530410aa881bc617f94346379854af4532e61f65aae7576c35faf55e155bd6787b4634d54191907e155c239e68480cdfa0c87054bfb62855f409a20d5335fb123e681e64ec847cd985b6062059f436aebac623c038b6c3405ac325191a8d1126a5ef8f38cccbf144a5c324c1e093cf99efbe10ca03d439bcfb8ba5e293b7d318837f7bc42a99964392369da76e79d71d1a2c248a11324a87ae1e3cbeab6fb0d0bcae1ef55e43dfb6f1b4cfb82c7a778fb828a3727ef07685fe38a74b3dd25d015322c2d9f245c08d8c2b43865694233782eb734436c4eddef5406208d6c4572c7371262fe02319cfbbcf2e23bed8aa969d1ae6f5f25ff6b8ebcf0925066f761a39bbff49f0c8dbc3be84f0c442b044ea01b669747e3c8293cfe9ccdf2ef063ae3d28d10720c279a2691616abd23b055cfc6c562125df4ad0fa6631304972ddc3674b1aaa7665bf621320d83eac8d5b371d7d719829f58b23458182558710de31d81ef9a47d8839c79640b2025d1965a418bc90e4115f1423311a8b64fcde0f2d2145ee535b0931b84bc8110445f2ff68d136ed709ddb7ea9ff75f3b4e8b4f836230ca9e81069477f634e07270af60ef96f72557a081d664abcf35548f699484653da645483ff2bf5998617ae8bfa62d56e714f3c0136e5035a3f78e06c2f470df7fd3380d14033f81e2aae6b4d90487dab76b9b3b8761fb56c36f5429da3d4346cb22e641ad8d7d2d80fa240d4e0154e6b3d2f1b3ef6cf174c08d062f575c83a4078174f874364df36a6328beeef69ba7f90e1df9fdcec9a2f15ebf04fa7d6756da2e5a59c9cbbcbc397d6fb28d0fc9a60534dff0752716ed079ad1ab19a224d1c8ae8a53242fd164989ff997489b6520eb3c0e97f4bcc1a9c3cbd44f008c03ef52cf7e626881d246925e0336c0ac668867f853da7820f914115a7c77ac31b66f46fbf97f66fa26416fc4581d459a4f2462d52cf0c79b278955aa73e8fa56e3c320f516bcc54c97e587199c15ab953cc37189b81c70cabb2559e445bcc9d8174ad7574e9acb02f43e0c34ff5e6746ee730ad41ff8eef93c2071c2649063dd92f343c06ef6abaf98f28d98d968071c12cc10a90c22d8b3b3480c76f7a51b7ec594b3435d2e3d779c1a15037697f3a058650472e47eecd5f32eb3243a516f0e703f9888c84690750648d6a9a876bf1f353db6891dc6d317d6e87ac088f42b5f6f20d799ece4fa7aaa928d2ac795e8de83d1e1c7fa2f9a4106693e981c21c63b3221c4fa2649f45f0c6e05dbf24011af16ab2e5fe94a640b485988037ebe1e8ad0b2623d95e9947f0726121d7828614e3b2d77a7a1f9a938bea9a1a7a2627b7d2e358c42ccc6c0b80a15a1c2f6e9aaf0495bdb7bb8d4b0e28a1ab5ab93ca0ff3e3f910c490c13486852534d5e12160835ec5916c5c68349c4e2d8fa956c643277edd3b6c81c88c010421705fd317ff9e3c94df0ed5305f530acbccf8dd0e87140cd38152664a572c168cd72595b7fac243c03f3fb33ef74a28c0e4469f94587c13704e9efe8010b2125aca78c22c33c82366e1a7c4028c2ae2e8d26e1a57e4297fac987f84a0a27f42b4c93a4f4d14569824b0880fb67407ed58f267ac403aa0b1f93784b4b4c67036037e60d58072611b0e90ca316976ef4e0b302cdad1b6dcca92efb8e1f6be2397967508be2c02a25ed0380ba1f7955f857c8fb043297780d136b2b064040c8e55143d715ea997e134ed973c98ef82786f0ccf66c17d863542180c66d54d08e116f2e35d995e214489ad0fad7a55fe9ebc1a777fe34141147c080b98d13463a3bbc6fc82f2fc95f4de7b3591d9c8cd4416917a4338095d5620104b7be13f5a131dd3f7aad5b559d11e8171dfb91e2bb1e47ac3810b1cdc1a1e370c867b7b7b50c4688dce545763157e02f47e1cc661d5bf2fbc336cfae080ab15728b1ab9dd199f2779d451e6178977fb658c17344cffb7aa3af5791a28fc8a089c85187753e5e313c8d1f0fe7755e28be444426a189e8bce2d2f79db31d4c3ca911a83455525355f95d159351cd731a88e55403851236ee2128f279d5be644c042453ae65d9e9f3b40d6c82bdeb002acdee061ecca3f2dceabef9a900e6e063d56ab39cb82dbc77a4677572d7616cd72c0f6d5b9b941dfda1fe7c896b8cc24d65a4322d712a84e94adfc8ed0cc56cc1ae97f775bd3cea5b20b524d9a7a916056e19af095d30171e5e14c7c998f78dc44845edf307363eab7913f680a5e5a1540a6f945507ffa67591f8d1a2920ab3b6e754e35379dd67870c242335e2717903ff3c687e5c33dc953416865d5f23bd752e55492b9d5d888d7b37ef33b0a6774d052b0987c066a2e01767207aa7fbfc393ca62874613dde3794f74fadb5d55b877b877a605918c812610fbcafad72ee245e6dd8721138d6bd3f4eedad853aed1ec437ad02ac937c80dae26fa5f70083bd346779b779387f7b3d2aae57770d8177928833281ccb7a38da24834fd9726fd17eb603cba9041e82bfeed0e33942dde1d48c271f5b39aa7230f41afb89d36f7976eee4f51a036743031c534f64685b94c990a93a5737fe628ee9cda8ed9c08b11d3836f833835c445b317a77ead7599d1a0c08873014510d36bb7ff5fb961277589ea48c32a60c87ec40681be067b17785ec44825bd89faa25249e735a628b6eebcc6cce4e0314c627588118c40b2e0d460d8d5ce358c56458f36914ca203f5a5381c6deb5a76bbc08c40a87437da0d0b571788a05e9f96d9bb770de8a0b1b960ff2a44a964c9b7939853742e83ce8deb79191b2d82454655f227079dd8c5b0216c8470b8e1ac70526301bbfa2bc4adca68a766ccb2a6e0ebf2e99905bf5242590b01703868b3faf841c11c383be145a40fea6375e18a01468e459603b5efdf8a4e9abd179280ae8b5947d78d2f0c4d37715eaa42bc37cf8730e41ffbf9826d46424f2922a96033cefaa8b4bbe4c8b89d43501fd5211d5392ca19a98ba127d9025b5c6e86ba024471940549a2b5d8e14961c9dc19696da1a5bffd01030d5e6100000000000000000000000000000000000000000000000005090f131a1f").unwrap(); + assert_eq!(sig.len(), MLDSA65_SIG_LEN); + + if MLDSA65::verify(&mldsa65_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + +fn bench_mldsa65_lowmemory_verify() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA65_lowmemory/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa65_pk, mldsa65_sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // + // eprintln!("pk:\n{}", &*hex::encode(&mldsa65_pk.encode())); + // let mu = MLDSA65::compute_mu_from_sk(&mldsa65_sk, msg, None).unwrap(); + // let sig = MLDSA65::sign_mu_deterministic(&mldsa65_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa65_pk = MLDSA65PublicKey::from_bytes(&*hex::decode("48683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d05b38dbb2edf01e664efd81be1ea893688ce68aa2d51c5958f8bbc6eb4e89ee67d2c0320954d57212cac7229ff1d6eaf03928bd51511f8d88d847736c7de2730d5978e5410713160978867711bf5539a0bfc4c350c2be572baf0ee2e2fb16ccfea08028d99ac49aebb75937ddce111cdab62fff3cea8ba2233d1e56fbc5c5a1e726de63fadd2af016b119177fa3d971a2d9277173fce55b67745af0b7c21d597dbeb93e6a32f341c49a5a8be9e825088d1f2aa45155d6c8ae15367e4eb003b8fdf7851071949739f9fff09023eaf45104d2a84a45906eed4671a44dc28d27987bb55df69e9e8561f61a80a72699503865fed9b7ee72a8e17a19c408144f4b29afef7031c3a6d8571610b42c9f421245a88f197e16812b031159b65b9687e5b3e934c5225ae98a79ba73d2b399d73510effad19e53b8450f0ba8fce1012fd98d260a74aaaa13fae249a006b1c34f5ba0b882f26378222fb36f2283c243f0ffeb5f1bb414a0a70d55e3d40a56b6cbc88ae1f03b7b2882d98deea28e145c9dedfd8eaf1cef2ed94a8b050f8964f46d1ea0d0c2a43e0dda6182adbf4f6ed175b6742257859bf22f3a417ecf1f9d89317b5e539d587af16b9e1313e04514ffa64ba8b3ff2b8321f8811cb3fb022c8f644e70a4b80a2fbfee604abb7379091ea8e6c5c74dfc0283666b40c0793870028204a136bf5da9568eb798d349038bdb0c11e03445e7847cb5069c75cf28ac601c7799d958210ddbcb226e51afef9f1de47b073873d6d3f97456bede085082e74a298b2cd48f4b3093155f366c8fa601c6af858dfa32c08491b2a29887f90335949a5d6edaa679882a3a95d6bf6d970a221f4b9d3d8cbf384af81aac95e2b3294e04789ac83727a5dc04559f96af41d8a053516feeeebc52746eb6ab2819e09108710d835f011fa63065872ad334d5cdffb2b2310507e92fc993ae317da97f4f309cdaf0f67ed99d90215576083849f953b246d7fedb3fdb67679850a5ad404e64147fb7cf4f6aeddd05afb4b834968d1fe88014960dce5d942236526e12a478d69e5fbe6970310b308c06845018cfc7b2ab430a13a6b1ac7bb02cccbb3d911ac2f11068613fbe029bfdce02cf5cd38950ed72c83944edfbc75615af87f864c051f3c55456c5412863a40c06d1dab562bdff0571b8d3c3917bbd300880bba5e998239b95fa91b7d6416d4f398b3adbcd30983ed3592b4d9ef7d4236fd00f50d98aa53a235ac4172720f77d96172672980cfe8ff7a5a702783edc2ba31b2259015a112fc7f468a9c2f9464039002d30ef678b4cb798bc116216bf7a9a7c18ba03b7b58fd07515d3115049d3614be7a07e744300750df1d2c58753389059eafc3d785ccdd31c07648bedc03a5c3b8ad46d064d59c13d57374729fc4e295362e2a5191204530428bc1522afa28ff5fe1655e304ca5bc8c27ad0e0c6a39dd4df28956c14b38cc93682cefe402bbd5e82d29c464e44eb5d37b48fc568dfe0cc6e8e16baea05e5135590f19294e73e8367b0216dbb815030b9de55913f08039c42351c59e5515dd5af8e089a15e625e8f6dee639386c46497d7a263288774de581a7de9629b41b4424141f978fb8331208efdec3c6e0de39bc57063f3dcd6c470373c08891ea29cbc7cc6d6483b8889083ace86aa7b51b1c2cfe6e2ad18d97ce36fbc56ea42fae97e6a7ac114864478c366df1ebb1e7b11a9098504fd5975bdf1f49dc70002b63c1739a9d263fbad4073f6a9f6c2b8af4b4c332a103a0cffa5deeb2d062ca3c215fd360026be7c5164f4a4424ef74948804d66f46487732c8202c795478647b4ea71d627c086024cca354a41f0877b38f19b3774ad2095c8da53b069e21c76ae2d2007e16719ed40080d334f7da52e9f5a5990439caf083a95b833f02ad10a08c1a6d0f260c007285bd4a2f47703a5aef465287d253b18ac22514316210ff566814b10f87a293d6f199d3c3959990d0c1268b4f50d5f9fcefbbf237bd0c28b80182d6659741f14f10bfbb21bba12ab620aa2396f56c0686b4ea9017990224216b2fe8ad76c4a9148eef9a86a3635a6aa77bc1dcfb6fba59a77dfda9b7530dc0ca8648c8d973738e01bab8f08b4905e84aa4641bd602410cd97520265f2f231f2b35e15eb2fa04d2bd94d5a77abaf1e0e161010a990087f5b46ea988b2bc0512fda0fa923dadd6c45c5301d09483673265b5ab2e10f4ba520f6bbad564a5c3d5e27bdb080f7d20e13296a3181954c39c649c943ebe17df5c1f7aae0a8fe126c477585a5d4d648a0d008b6af5e8cd31be69a9296d4f3fd25ed86f221e4b93f65f5929967533624b9235750c30707550b58536d109a7131c5a5bbe4a5715567c12534aec7660761eebb9fae2891c774589b80e566ad557ddef7367196b7227ea9870ef09ddfec79d6b9319a6879b5205d76bf7aba5acf33afb59d17fc54e68383d6be5a08e9b66da53dcde008bb294b8582bd132cdcc49959fdbc21e52721880c8ad0352c79f03a43bbd84c4cdfdc6c529005e1e7cd9a349a7168a35569ba5dea818968d5a91466bd6e64e20bf62417198afc4e81c28dd77ed4028232398b52fbde86bc84f475b9016710ce2aabc11a06b4dbac901ec16cf365ca3f2d53813948a693a0f93e79c46ca5d5a6dca3d28ca50ad18bd13fca55059dd9b185f79f9c47196a4e81b2104bc460a051e02f2e8444f").unwrap()).unwrap(); + let sig = &*hex::decode("9061f15cbf2092f744fbcd799eb02414053c1b0f7412124bedc41cf9a3db0166469e874037d7f081e5f8d3d2033a0307d1c49ed01fe64578c4a6fabd80880cdf1911848f184d4bcf536ca795a0fb1aa19ab7ee3ba6b58bd64bbeac9f58650fff1ef5a97ab6916df962072e20e7c6be96090e3a781a504bc4442bd8889a0aa628907a74299f39fa836031f1bd68355bebe7ae93c1e361a9efbed1325d96227070461fcd6f151b8669d9229b977d9ee51fd2260c3e4a2e820416f9e074958dc3b3e2217e6312b7e0b582a048981cf6579f4bc7715b78c808e4c57e3b8aa38b05c04fcedf209f52c1e331ae83dbdff60ba450a17cc397568e54bc3f16ddf30b92747ce460d925b9be20a1d35e2aed97f124af2616a5361df28ba30e522dd08fa00fd28d1ac484d756a89e3a442fefe8332c56cd2a9fde691bdbda43f1cc54cef57bead96120b50c7d4695bdbb1303cc5ddda898e4eeb83083176e40e0232cdd1c3150371df05d6fdad7e1164d90393cf308e99edfeb31fed263e2866ee3b7f3937b399c974d87ba7b489efe3c9b80371d2928446adc31991ab0cefaaa080575b9ec81cfa133a9911c035a8058d0d3f2e34de4a9fb009bb4ccdb16de7b908574a7496725ff857556c1b33917e986c80f1014a9e3083add2fb35f345c5d06159e443329d0da099987b996c3731592b460c2ffd2955f7546f4216100ba43188803ff9b36969685f909fa2539323b8c8ec1c095a5085e554dd450e0e67ab670b6a11ebf6c25520fc13e364060f91f9b7f3d5cb48ff28b8fc83d4293f1f35ad6ff6ae4574ad7a1c6005fc0389a7b21386b0850a05d832fe6a14bb2b1db1f8e20bd09174946cd098b81c8f797e95f2143a949770cf1219bfef039db51a80fc247f65f41554c7173dd805ba82fdf47ab6d4bfd37dfe46fc47904421ae00dc005a22f9c4784b0ea9e665392a412245016d5c6d7673a6a180d228d4255a538e451ffd8b414d40304c0c888992e0ab6de1602109527417bc1c7eb782ae77a8c3cdfc1d13a1e874207898264e38080243109c5969649ac8383417e922ba115331142d0ed35440b15d40bee0cf58af37c0f0524ffac1c71ceed3bb82f76ab108a8ad1a0c8b78d9341148c642369be7bef59d46f49d70c83560607f140848ec9a7607d4a08f8b6e4447f5523f416981888a8de9647ffef79389e4983e5c9387698d0cc2d429322365ce7e7b5fd6d6eb921c813fcf06199fe1ca41e9cfe03b539f321671a2acad0963f876f9db7a1c4371b9f101005217995b5b6a40976246d245da603dba8dac812a5480c3476a99d0ffdf0ef943d72d912543148b2fe78e8b0159324fe9bcd4ced33cd212fe4f3dfd6d4c5e1958beb95ac6b533ace3e78015e3880b52bf45299263a4c0096f8ba5fe3a6298cab675cb7f382e7ef49720eb4cf47376e2d2574122ccf91129c858e948904fecefb91226ed42403ba12dd3258909a87dfcbf65cc3adc3d98d277fdcec7664e2292b7d27afbb5aafb405c20a34b2fe2c0849ee280bb891dfdf59f19b89b0246358db54cf3fdc66eaaaa750c8903f1d42678f3edf0b7530410aa881bc617f94346379854af4532e61f65aae7576c35faf55e155bd6787b4634d54191907e155c239e68480cdfa0c87054bfb62855f409a20d5335fb123e681e64ec847cd985b6062059f436aebac623c038b6c3405ac325191a8d1126a5ef8f38cccbf144a5c324c1e093cf99efbe10ca03d439bcfb8ba5e293b7d318837f7bc42a99964392369da76e79d71d1a2c248a11324a87ae1e3cbeab6fb0d0bcae1ef55e43dfb6f1b4cfb82c7a778fb828a3727ef07685fe38a74b3dd25d015322c2d9f245c08d8c2b43865694233782eb734436c4eddef5406208d6c4572c7371262fe02319cfbbcf2e23bed8aa969d1ae6f5f25ff6b8ebcf0925066f761a39bbff49f0c8dbc3be84f0c442b044ea01b669747e3c8293cfe9ccdf2ef063ae3d28d10720c279a2691616abd23b055cfc6c562125df4ad0fa6631304972ddc3674b1aaa7665bf621320d83eac8d5b371d7d719829f58b23458182558710de31d81ef9a47d8839c79640b2025d1965a418bc90e4115f1423311a8b64fcde0f2d2145ee535b0931b84bc8110445f2ff68d136ed709ddb7ea9ff75f3b4e8b4f836230ca9e81069477f634e07270af60ef96f72557a081d664abcf35548f699484653da645483ff2bf5998617ae8bfa62d56e714f3c0136e5035a3f78e06c2f470df7fd3380d14033f81e2aae6b4d90487dab76b9b3b8761fb56c36f5429da3d4346cb22e641ad8d7d2d80fa240d4e0154e6b3d2f1b3ef6cf174c08d062f575c83a4078174f874364df36a6328beeef69ba7f90e1df9fdcec9a2f15ebf04fa7d6756da2e5a59c9cbbcbc397d6fb28d0fc9a60534dff0752716ed079ad1ab19a224d1c8ae8a53242fd164989ff997489b6520eb3c0e97f4bcc1a9c3cbd44f008c03ef52cf7e626881d246925e0336c0ac668867f853da7820f914115a7c77ac31b66f46fbf97f66fa26416fc4581d459a4f2462d52cf0c79b278955aa73e8fa56e3c320f516bcc54c97e587199c15ab953cc37189b81c70cabb2559e445bcc9d8174ad7574e9acb02f43e0c34ff5e6746ee730ad41ff8eef93c2071c2649063dd92f343c06ef6abaf98f28d98d968071c12cc10a90c22d8b3b3480c76f7a51b7ec594b3435d2e3d779c1a15037697f3a058650472e47eecd5f32eb3243a516f0e703f9888c84690750648d6a9a876bf1f353db6891dc6d317d6e87ac088f42b5f6f20d799ece4fa7aaa928d2ac795e8de83d1e1c7fa2f9a4106693e981c21c63b3221c4fa2649f45f0c6e05dbf24011af16ab2e5fe94a640b485988037ebe1e8ad0b2623d95e9947f0726121d7828614e3b2d77a7a1f9a938bea9a1a7a2627b7d2e358c42ccc6c0b80a15a1c2f6e9aaf0495bdb7bb8d4b0e28a1ab5ab93ca0ff3e3f910c490c13486852534d5e12160835ec5916c5c68349c4e2d8fa956c643277edd3b6c81c88c010421705fd317ff9e3c94df0ed5305f530acbccf8dd0e87140cd38152664a572c168cd72595b7fac243c03f3fb33ef74a28c0e4469f94587c13704e9efe8010b2125aca78c22c33c82366e1a7c4028c2ae2e8d26e1a57e4297fac987f84a0a27f42b4c93a4f4d14569824b0880fb67407ed58f267ac403aa0b1f93784b4b4c67036037e60d58072611b0e90ca316976ef4e0b302cdad1b6dcca92efb8e1f6be2397967508be2c02a25ed0380ba1f7955f857c8fb043297780d136b2b064040c8e55143d715ea997e134ed973c98ef82786f0ccf66c17d863542180c66d54d08e116f2e35d995e214489ad0fad7a55fe9ebc1a777fe34141147c080b98d13463a3bbc6fc82f2fc95f4de7b3591d9c8cd4416917a4338095d5620104b7be13f5a131dd3f7aad5b559d11e8171dfb91e2bb1e47ac3810b1cdc1a1e370c867b7b7b50c4688dce545763157e02f47e1cc661d5bf2fbc336cfae080ab15728b1ab9dd199f2779d451e6178977fb658c17344cffb7aa3af5791a28fc8a089c85187753e5e313c8d1f0fe7755e28be444426a189e8bce2d2f79db31d4c3ca911a83455525355f95d159351cd731a88e55403851236ee2128f279d5be644c042453ae65d9e9f3b40d6c82bdeb002acdee061ecca3f2dceabef9a900e6e063d56ab39cb82dbc77a4677572d7616cd72c0f6d5b9b941dfda1fe7c896b8cc24d65a4322d712a84e94adfc8ed0cc56cc1ae97f775bd3cea5b20b524d9a7a916056e19af095d30171e5e14c7c998f78dc44845edf307363eab7913f680a5e5a1540a6f945507ffa67591f8d1a2920ab3b6e754e35379dd67870c242335e2717903ff3c687e5c33dc953416865d5f23bd752e55492b9d5d888d7b37ef33b0a6774d052b0987c066a2e01767207aa7fbfc393ca62874613dde3794f74fadb5d55b877b877a605918c812610fbcafad72ee245e6dd8721138d6bd3f4eedad853aed1ec437ad02ac937c80dae26fa5f70083bd346779b779387f7b3d2aae57770d8177928833281ccb7a38da24834fd9726fd17eb603cba9041e82bfeed0e33942dde1d48c271f5b39aa7230f41afb89d36f7976eee4f51a036743031c534f64685b94c990a93a5737fe628ee9cda8ed9c08b11d3836f833835c445b317a77ead7599d1a0c08873014510d36bb7ff5fb961277589ea48c32a60c87ec40681be067b17785ec44825bd89faa25249e735a628b6eebcc6cce4e0314c627588118c40b2e0d460d8d5ce358c56458f36914ca203f5a5381c6deb5a76bbc08c40a87437da0d0b571788a05e9f96d9bb770de8a0b1b960ff2a44a964c9b7939853742e83ce8deb79191b2d82454655f227079dd8c5b0216c8470b8e1ac70526301bbfa2bc4adca68a766ccb2a6e0ebf2e99905bf5242590b01703868b3faf841c11c383be145a40fea6375e18a01468e459603b5efdf8a4e9abd179280ae8b5947d78d2f0c4d37715eaa42bc37cf8730e41ffbf9826d46424f2922a96033cefaa8b4bbe4c8b89d43501fd5211d5392ca19a98ba127d9025b5c6e86ba024471940549a2b5d8e14961c9dc19696da1a5bffd01030d5e6100000000000000000000000000000000000000000000000005090f131a1f").unwrap(); + assert_eq!(sig.len(), MLDSA65_SIG_LEN); + + if MLDSA65::verify(&mldsa65_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + +fn bench_mldsa87_verify() { + use bouncycastle_mldsa::{MLDSATrait, MLDSA87, MLDSA87_SIG_LEN, MLDSA87PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA87/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa65_pk, mldsa65_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + // + // eprintln!("pk:\n{}", &*hex::encode(&mldsa65_pk.encode())); + // let mu = MLDSA87::compute_mu_from_sk(&mldsa65_sk, msg, None).unwrap(); + // let sig = MLDSA87::sign_mu_deterministic(&mldsa65_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa87_pk = MLDSA87PublicKey::from_bytes(&*hex::decode("9792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124a73b323b9ba21ab64d767c433f5a521effe18f86e46a188952c4467e048b729e7fc4d115e7e48da1896d5fe119b10dcddef62cb307954074b42336e52836de61da941f8d37ea68ac8106fabe19070679af6008537120f70793b8ea9cc0e6e7b7b4c9a5c7421c60f24451ba1e933db1a2ee16c79559f21b3d1b8305850aa42afbb13f1f4d5b9f4835f9d87dfceb162d0ef4a7fdc4cba1743cd1c87bb4967da16cc8764b6569df8ee5bdcbffe9a4e05748e6fdf225af9e4eeb7773b62e8f85f9b56b548945551844fbd89806a4ac369bed2d256100f688a6ad5e0a709826dc4449e91e23c5506e642361ef5a313712f79bc4b3186861ca85a4bab17e7f943d1b8a333aa3ae7ce16b440d6018f9e04daf5725c7f1a93fad1a5a27b67895bd249aa91685de20af32c8b7e268c7f96877d0c85001135a4f0a8f1b8264fa6ebe5a349d8aecad1a16299ccf2fd9c7b85bace2ced3aa1276ba61ee78ed7e5ca5b67cdd458a9354030e6abbbabf56a0a2316fec9dba83b51d42fd3167f1e0f90855d5c66509b210265dc1e54ec44b43ba7cf9aef118b44d80912ce75166a6651e116cebe49229a7062c09931f71abd2293f76f7efc3215ba97800037e58e470bdbbb43c1b0439eaf79c54d93b44aac9efe9fbe151874cfb2a64cbee28cc4c0fe7775e5d870f1c02e5b2e3c5004c995f24c9b779cb753a277d0e71fd425eb6bc2ca56ce129db51f70740f31e63976b50c7312e9797d78c5b1ac24a5fa347cc916e0a83f5c3b675cd30b81e3fa10b93444e07397571cce98b28da51db9056bc728c5b0b1181e2fbd387b4c79ab1a5fefece37167af772ddad14eb4c3982da5a59d0e9eb173ec6315091170027a3ab5ef6aa129cb8585727b9358a28501d713a72f3f1db31714286f9b6408013af06045d75592fc0b7dd47c73ed9c75b11e9d7c69f7cadfc3280a9062c5273c43be1c34f87448864cea7b5c97d6d32f59bd5f25384653bb5c4faa45bea8b89402843e645b6b9269e2bd988ddacb033328ffb060450f7df080053e6969b251e875ecec32cfc592840d69ab69a75e06b379c535d95266b082f4f09c93162b33b0d9f7307a4eaaa52104437fed66f8ee3eabbd45d67b25a8133f496468b52baffdbfad93eef1a9818b5e42ec722788a3d8d3529fc777d2ba570801dfae01ec88302837c1fb9e0355727645ee1046c3f915f6ae82dad4fb6b0356a46518ffc834155c3b4fe6dafa6cc8a5ccf53c73a0849d8d44f7dcf72754e70e1b7dfb447bb4ef49d1a718f6171bbce200950e0ce926106b151a3e871d5ce49731bd6650a9b0ca972da1c5f136d44820ea6383c08f3b384cf2338e789c513f618cc5694a6f0cee104511e1ed7c5f23a1ebfd8a0db8424553240156dbf622831b0c643d1c551b6f3f7a98d29b85c2de05a65fa615eee16495bd90737672115b53e91c5d90028cf3f1a93953a153de53b44084e9ccff6b736693926daefebb2d77aa5ad689b92f31686669df16d1715cc58f7a2cfb72dd1a51e92f825993a74022be7e9eb6054654457094d14928f20215e7b222ac56b51adbec8d8bdb6983979a7e3a21b44b5d1518ca97d0b5195f51ed6a24350c89747e1edea51b448e3e9147054ce927873c90db394d86888e07dff177593d6f79e152302204aeb03be2386af3e24078bd028b1689f5e147c9f452c8ceb02ec59cc9db63a03576ceeafe98239023897da0236630a53c0de7f435a19869792fab36e7b9e635760f09069e6432e700035ac2a02879fff0a1e1bec522047193d94eb5df1efd53eea1144ca78940852f5ec9727904b366ede4f5e2d331fad5fc282ea2c47e923142771c3dd75a87357487def99e5f18e9d9ed623c175d02888c51f82c07a80d54716b3c3c2bdbe2e9f0a9bbaaebeb4d52936876406f5c00e8e4bbd0a5ec05797e6207c5ab6c88f1a688421bd05a114f4d7de2ac241fa0e8bedff47f762ddcbeaa91004f8d31e85095c81054994ad3826e344ba96040810fc0b2ad1de48cfade002c62e5a49a0731ab38344bc1636df16bf607d56855e56d684003c718e4bad9e5a099979fcddeeb1c4a7776cd37a3417cb0e184e29ef9bc0e87475ba663be09e00ab562eb7c0f7165f969a9b42414198ccf1bff2a2c8d689a414ece7662927665689e94db961ebaec5615cbc1a7895c6851ac961432ff1118d4607d32ef9dc732d51333be4b4d0e30ddea784eca8be47e741be9c19631dc470a52ef4dc13a4f3633fd434d787c170977b417df598e1d0dde506bb71d6f0bc17ec70e3b03cdc1965cb36993f633b0472e50d0923ac6c66fdf1d3e6459cc121f0f5f94d09e9dbcf5d690e23233838a0bacb7c638d1b2650a4308cd171b6855126d1da672a6ed85a8d78c286fb56f4ab3d21497528045c63262c8a42af2f9802c53b7bb8be28e78fe0b5ce45fbb7a1af1a3b28a8d94b7890e3c882e39bc98e9f0ad76025bf0dd2f00298e7141a226b3d7cee414f604d1e0ba54d11d5fe58bccea6ad77ad2e8c1caacf32459014b7b91001b1efa8ad172a523fb8e365b577121bf9fd88a2c60c21e821d7b6acb47a5a995e40caced5c223b8fe6de5e18e9d2e5893aefebb7aae7ff1a146260e2f110e939528213a0025a38ec79aabc861b25ebc509a4674c132aaacb7e0146f14efd11cfcaf4caa4f775a716ce325e0a435a4d349d720bcf137450afc45046fc1a1f83a9d329777a7084e4aadae7122ce97005930528eb3c7f7f1129b372887a371155a3ba201a25cbf1dcb64e7cdee092c3141fb5550fe3d0dd82e870e578b2b46500818113b8f6569773c677385b69a42b77dcba7acffd95fd4452e23aaa1d37e1da2151ea658d40a3596b27ac9f8129dc6cf0643772624b59f4f461230df471ca26087c3942d5c6687df6082835935a3f87cb762b0c3b1d0dda4a6533965bef1b7b8292e254c014d090fed857c44c1839c694c0a64e3fad90a11f534722b6ee1574f2e149d55d744de4887024e08511431c062750e16c74ab9f3242f2db3ffb12a8d6107faa229d6f6373b07f36d3932b3bdb04c19dd64eadd7f93c3c564c358a1c81dcf1c9c31e5b06568f97544c17dc15698c5cb38983a9afc42783faa773a52c9d8260690be9e3156aa5bc1509dea3f69587695cd6ff172ba83e6a6d8a7d6bbebbbcda3672731983f89bc5831dc37c3f3c5c56facc697f3cb20bd5dbadbd702e54844ac2f626901fe159db93dfd4773d8fe73562b846c1fc856d1802762840ebc72d7988bde75cbca70d319d32ce0cc0253bb2ad455723ee0c7f4736ce6e6665c5aca32a481c53839bc259167b013d0423395eeb9aaaee3206149a7d550d67fc5fdfe4a8a5c35d2510b664379ab8f72855a2af47abce2a632048eaf89e5cb4a88debc53a595103acce4f1cff18acff07afe1eb5716aa1e40b63134c3a3ae9579fa87f515be093c2d29db6d6b65c93661e00636b592704d093cc6716c2342eb1853d48c85c63ac8a2854462c7b77e7e3bd1eac5bca28ffaa00b5d349f8a547ad875b96a8c2b2910c9301309a3f9138a5693111f55b3c009ca947c39dfc82d98eb1caa4a9cbe885f786fa86e55be062222f8ba90a974073326b31212aece0a34a60").unwrap()).unwrap(); + let sig = &*hex::decode("781368e64dba542a7eacbd2257335cc943a03241009b797093c615f76a671a7591430441d80bb582304b33b9fce295e0dd57fe169355ddf4453a2aca62d8eb8109ef0d9cf3f5b0a94e04ad81b3e786014243ecde816551aa7fe01c639054256a491756bef59f5034f717ff4f85e70ba7731a49971415b6a7e7d816ab434b9f17a3095ede6fd432be2bfa82724045dda0dfff7a0281e9000939ccba3d8ab3245139c441648c76a6536127e4d1ef0df1531883ab78c8b41323617ad8db03d9908c9e08a9f7321c45051b3c94213347b11c4a84491de7a7be68701e47d7f0e0b33e767bef17694e4d33244ed92ebd74c85ab6c84441cddc14331e6ae8bd23674bda27f09c050d88f7d430feee7f15a72a24d653bb6bec54491b98362ce131d37c7d78a3f9a893db5abdccd6663593b88bc6c97f07f8eafccfd25e8180d918efbcd95bbf3da29f081e3e1932095939198e2a155b2d803a3e84ca4f34569df695c259faf3c0d8f0cd217ebd2dbad542b32fbb54e44aaf0b5dc739fafef2e46db8d68bfc35f44f038cb1f5231a1b5b134ae683e7f3297cc7a95bd191b310f68201450797fe3293cde1672dfeca4b493f53c768ea048a972a4cd84d39ef682957b8f28ba29487b4689b43fec2655823d9bb99ffcf31490366a9860a5d5b8e32a3b8bfeb6f55f88fb80c8c0142086f220e1f6f2862dabda58c3b6f5faa805b39cfac4b6d7ea7acdf1b0690063b0c1ea38c7c4755189966dc631055f153f71b77b114fa5c309316ba512330ea5cdb0bb176001e57461563d17259f35d0c30ef5ac838c0325402bab52c531469526ae3ee6293f7b5769d27e69fa81cd25a31cd095b126e70c57ac3169a5f585a11f1748d9d22f2564911c26a24b2153a78f3a06822f5f1963f237abeb48efd9a9cd478de579c5a0ba84d00e96fbde36d8ce20e7e948547fb6850834ff79d211830f6ee973359781d9d5008fb43a89354782fde4158177f5206ce1d38c889e99e4bb5b4ab34d6a05c42f5d719ea03dbc54adba75a3bb44a3c08c7556462f8c5b7c568a69242cf5be6098eba0a2249c1ca5b2109b6404a962abc1c159c6b48a79fb97e4a3337d99323746221297423f9bd1b12e78489e01e6a10f0fd6bba1cfd6ae1b75dbe69f8b8ee51a4e7f68ba2c407c9c0bad3892b29b0170ab75836fdd49a7ee3c2bb30f2c3d226bcec49140952170b0d160f97b30b7b7b096719538677ebb06922f26925227c8852acc107a8f173b38d96697584bd3dfe169b4073aa58a7bf371d5c4bb0eed30f08212defb3aec902d4546084176bf0f86d93cf36a4689a5e874b32d6b7d3c1e3fbcfd988c35dbc9a8d0a019ad6d7e15ec3ac97125db6abfe00beffe35a81666699a91e15945c62d646690b5b52de8b835ee9be53588fde5d63023b52b2b1f4610c237a829f5901a46042963cce7b85aa040adde02985e14e23c4eadb75221c607d24672e244c66c9c24c3cb7fd90bb23295c9d3d9da516bce3dd462d6660f9f91ef0618a4d4d3d6668c5d1e2e8ed433ebfe0762beb743324e11608f8b14b69ce4c221c1654ba4992a5af2d949c2939f95d1c8fb767af2a843cc7c78f57259d5c0c6ca83fca41ef5ecc4eeeea93e4518c24d3040f2cd90df3e535e989e606fa109e2c453ed7353db1cdb27137f005f9dd8d2aebfb7255a6098b690215e100cfe44ca0f2745fced48322bc9667ab16d2e1c0ff491b96a17b833d4fd44d31c2230ac835796e063ab03000f04f15c70560033763a48552cceaacade9ca5c8055f3745e179068a287183f2bc3ef6327dec5ac7cf7b052ef5a8873e697efde089688f43be464827c2fa83eb531b3674145e95c699c82990e684967dad319d9f64ab16cd9fe9b6c41232ac4ae3795fd8a76aa9b02e970242061c6da45a2af74ad9cb2a79935c92625e242f4bc7fce54d5c10a9e61f875162fa651b66057ba036f062d6d39d0502b93a5640b78c6c2fd20b02ff83676a87a94945d476c349803ea4fe60cdcea65bb2629e2bc09d4472ec63422dee2052f098deaf5531e6c9bed6672a8b699802efe0cded80c8455f585d1ba633d281f1a21adab48e63b44e0c2a4d7608cf98aabf8adc86bcb8f61e8b06cd2385f82e0a3cdd03cab152d5951859c4532f9168e78f17ba2a5772780327dcf4e62b4d26e443762fc488ae4cd4d1156dbd5782595cfd7697a514abc9b160c9ccf08edc86134a755b90e9bb543511e888e3157721a52d1bc5db33029fb335ea2114e21c03368c8d7f4d827960641772a4a32a738df60d19ec77ab09d22f57cc2523b9503b3f5b1cebc5ae15f885f159842db7359a1c89d3d82d3407068f15b6739626eb8c521fc8c5c7491f945d49f14e6989da340bdf49e7f8a792747aa658bc114143ba93f26022d001735b744639bbf22aab2a1851cfc934f9c69d3764fdea3d23db17998e6138cfd7cd9e9a47cb74193bd71aaf28cdd9d1eb595125546a4f4357ebbd1f410e3bf8557892de68509b5b98c5c229e942c910fdd3e54cb6ad54d8dd886cb97ecc06d1e401b8395d0bcb0db9a031dc66c9294f9053c68fc42042b1fa1671fc7d510b70916c0139cfebe3a91244527ce9439860cedb30908197be851cbd1d3b18ca541358449fb34fb5cd569630ed5f67b8795e87828f2ce3becfe457579d82333b0bbab094de391e1f8157bd431e365ca864630932bbebb48f45f8134424e18ab455029b54b19e2f3bfec5e44ad0ea5c03f53d8f925b635838aa7015a7c9e325bdfaff966ea9512dd50f87c8995cea7561c23f4fb06d964ab8f1913a6ca17e4ca60d6bc078e1784f89c673c91d955bcf45f58ca9709579d5e3831df12cfdb7516fd21878cb54243579b9346d2de4be25f508e84b1adc78cb91c03da3c4fd59e4529189838f74f6312820620a5996b791ffcb332f847094613f2148b862034fa89d0d0ff1808d902c5d1af64d5522492d61ecde4c73be89a33782cef1acc1dc327fb2eb9d17642209b85aa8b1dc57cbf067c7aa29da6b7e157d23e171d3ae6f3855834071791402c851ff2dd67109979f7ee5e09e64b4eefdee7112b55ce200bb8c8051e3428c305fa1d576bffbb25a70eb571168fc60dadd928b10cfd07de80a85b8df3edc372d488c21f0d5787611cc6fb73aeeb6f920a109294b49d3870f90de3b360d14df77ef95640bcac7a4dbca901a31db83e83f5c59ce327207ea9b27c3b978d30d53865c1b84764f025e8732d5007554ce5c9cc410b2eefd7e4d990c538557606a6bc47577a43768d30aa3e8598fd6f4fe7ac439f3931c58fd69d90765ac9f456ac7de085e14a0898c4557f5d3baaae07edc607de6900146b97b35aae570153dc107815ef9febdd4fd567d637fee8f8bfb4b3413ea6aead4846ab733a04f1e4bc32a3bbb1c16baf8d0bdb9ccb82fe46479ccfd040b5e64064e539b39c66e4501dc822873ac6119a4a112a1f7cd6df0e5f84356ce853ced34ce69a9e7383534983c51c50269bf8b9586a0e5ba905fd3bce080b00e7f7d48e55f489479b5771e995fb020e58feb74af65c3ee76aa4e69b5ba8bda249a1b2d62c08d418c3635d061846040843991ab475473da85d94981fb84425e7ad951ce0a42be642fe658b7aaae72b147cdc086c24b1571eb2272e2a72b15660d854ebe19ec7d9ab7ea17800d0b6ae727b39217467c662ba08e6f19193951eeff02806a7843eb5c71b2f04dcb605ecedc5128cd67703038c44bf20fe06f3ed8c1368fe38e72944d5c52fca46a45fd48d8fe5da64183d4d62ec01aa3d9d672ec67a01c17f21e02525f0513cce030c664fc8784763086608bc8099c204c255ffed1daf432ceb45fbd135e21e8190c5bfee192171faa77520481e69e87b7f76790bef76cb8d3c88f5c6e32fc59e7bd45351d66696b61d9f40726fb9a98000b68738cf7e34b98b6a4aaa2ac1d7b1407db89783f8077103ea9c9e89247eae078adfb36e21474c3bb1fe0c87687c6233a533a01e1081b93a3521d339f39c075609bace531994988ae314f77fc6034113a138c67eb7e03750cbec8d28bd21afedfefa8f091619ae500b4ca4599d019dc8ca4bf118d70b8676dfc796a4f6d986adba4c8574ed4abaea5465466220e5e53dc8fcb395d1e59d278673cbc4e3f40658df98ac2fd126a94922879e1a3be91c1acc20803c35fa764abbadab07bde85ff4bd9e0fb6f06baf5bf42b8a2cbf6c2f62606becc361552921a12d6c8236fde84db4bddac77e8872478cffc4e148c1c7acfedf6b17d98731c2de36f3cbef1f6f781a940e0874d5b74535bbe066b53064d43b13926570a9e1c4e6da206c8bd252caf2b62e7d223f7ac12939137f330be59374d7295a6c2dff92e07c727510e48d970593e47229fc8bc3bd5b8ea780dacff4d23063df65feda5f8f65b17a333e532acad7916780c74d6a70d38b367f3f6f4e947b85fc15235bbe46b26495d2780098db853a931377cfbedea620f2355ca21e81ce9e0078b0dd6cb70f23ed558682be3b3d594eefe85344e1f275428b316cc088995939298f2a2d15ac9b676ac3e9cb92f2a64dec7732a91fc761aa1b126ea575e3953177da6e1cd78faea824665330a81d9e24572b9860bf0aba4df8bd5d4e3e2c72bbfbb2a985c7ae2f077951fe8401e1d156ecada1e353817b20f41e0b2460a0caaf2b36d6a7f1b35125d797dcc714421027d14171765a646071ed952b6a5294eecf6a3a71c104c843a4a8b3efcc27467b20cb0a94abf5802229ea4d8312783e78791a50b3c0a88fe6497198cd4bf470dac46f34e50019fdee2040cfe99124b312b1122b83e51d878877cec0855f1158c445cfdc2253f4389d5e3a8ba1669abb5976a4617e85f543da9f5e30b10ca7481c8185392782b46fb0a0e5ab408b2945e3c79a1cc49fb7c27254a9b540e7397a5b655bf7e4f83184db32a128aa2e00a624d7dcd6b77efd151f1e5cba8890af9170fa06c555715dc1787e995ad19270973ae95b88dcafdaf62c28843d3f8b9c78dc8e37d911dab3f7ee9d4c7389c654bdcfc05056b360020140e57e31473258a4081e2a708f7caba90c356d0847098fc0762484086aba898a60b023d6a3060402406240748785d51caff52a0ef3dc2a45dadf80ac18502d24422a8cbb10192b88f4e9160206b2ed3e04114f2a339df269e2c36b8613ce37087471701755330cb559575366ee0fa2d3afcda32eede6dd906345daaa04812198e96c42239c242edb90059709f497da5b87705384aef2af22dc2edfef3c00d8c9156d8b3163fe7a7779e04f04911a8b934fb3072eee844484fede5e2ee96d338eefe2da986067ffe0218ada7de1d0e42d823d6b033918278888ba0608ab8f7be997bdf263689a36f5204c802ad836363779b4b0d6ce5083df0b98a2e2c700062a4fa5e57bc73bd45357e01d90c7954bc6904d1ce8166a9168da39a60c5cae8119bb6b9ab074fb2d0aee384fc2c0e4806811d6002b4e2401e7430b50cb0e8075f33d5386aecde256e169d95e2f9c6556c08ba042e68a53ce8aca9cc02818f7382f150dc04de0019b19c7a3ab0d72d6ed013d7a115d74b279f71fd6effef34049877e0b11e0659be938a5de684eaf23513095eb4a1bdf536c3c01a4655c4b4a0673214cef29a481d06a02cc9a5bfc7b8d846c33484cd67b1de98f60b69918f177b64558ca567a6237d35ff01771a42320ce02bb98f3e4ad4ac7db75611bd9961eac662a38b1f785970c99f3dd105ff586f61301c48d66708cbf7d53a733e357b6c256e8b73f0e1305a0bc137989e521100c2ee6259e607fe12198e8bcb988b0854668e40d7cae6adc3ba40ba121b7319d06d988a073d03097b9f5c1c07284b6473ae57bf154811b77baceb0412b8a6983bdd0ccd9e3bf014e520009cb26d5780eabef1bbafa5e25d41098a54c47fce8b68d395291d54284d33aa50b9664d1510b467c8a539361ca9a4448bc01fcb4c4e3ef475e8afb46a494ae13ee9ea8a1266825fba7f32b9712fde252698a68359b50141d90f5c4a06283ddb54ad7e1412ac5ebb12501f7a82b2a7f27b2dbab626c3db4074523b3211d3182ea261397a6f7b187cf2b8a356ded10812f1d305169aedf79b5ff1cf7c2d6e86ee11f28e96aa63b5a03f59fc960ac7d0572e91dda61905c0711a9b26344a2a10aa2041f2b13cb1a9a9a27774b6d0deddc9d81ea1b142ad7b72be47991f2c9261d6708156e38d00b074020766eb0c494392d65b82ca65f7c3352f9bb78325ecb6df596c8ae57826b08ccd6f1d529d2e25925c1ac972425bedeb88a5d0e3138ddc434da3462ebdf6b1239a21f141ec62cbe4bb993ba253b55a76d30fac19c2c1384ef6b9746c07787aa1fe913a1348390bd8c1f386a08c77cf7106c927ce24dffc9d6ee1b32354d95ed2923482531de6b390bf0f5eb80276e90e7ed11131c848bbabec4d317236269a0a3a7cbe0f1272f93949ca6d23ea2a7ee3f697791aab71533423066d400000000000000000000000000000000000000000000000000000000050e181f23292c2f").unwrap(); + assert_eq!(sig.len(), MLDSA87_SIG_LEN); + + if MLDSA87::verify(&mldsa87_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + +fn bench_mldsa87_lowmemory_verify() { + use bouncycastle_mldsa_lowmemory::{MLDSATrait, MLDSA87, MLDSA87_SIG_LEN, MLDSA87PublicKey}; + use bouncycastle_hex as hex; + + eprintln!("MLDSA87/Verify"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that we're not capturing keygen in the bench */ + + // let seed = KeyMaterial256::from_bytes_as_type( + // &hex::decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f").unwrap(), + // KeyType::Seed, + // ).unwrap(); + // + // let (mldsa65_pk, mldsa65_sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + // + // eprintln!("pk:\n{}", &*hex::encode(&mldsa65_pk.encode())); + // let mu = MLDSA87::compute_mu_from_sk(&mldsa65_sk, msg, None).unwrap(); + // let sig = MLDSA87::sign_mu_deterministic(&mldsa65_sk, &mu, [0u8; 32]).unwrap(); + // eprintln!("sig:\n{}", &*hex::encode(sig)); + + let mldsa87_pk = MLDSA87PublicKey::from_bytes(&*hex::decode("9792bcec2f2430686a82fccf3c2f5ff665e771d7ab41b90258cfa7e90ec97124a73b323b9ba21ab64d767c433f5a521effe18f86e46a188952c4467e048b729e7fc4d115e7e48da1896d5fe119b10dcddef62cb307954074b42336e52836de61da941f8d37ea68ac8106fabe19070679af6008537120f70793b8ea9cc0e6e7b7b4c9a5c7421c60f24451ba1e933db1a2ee16c79559f21b3d1b8305850aa42afbb13f1f4d5b9f4835f9d87dfceb162d0ef4a7fdc4cba1743cd1c87bb4967da16cc8764b6569df8ee5bdcbffe9a4e05748e6fdf225af9e4eeb7773b62e8f85f9b56b548945551844fbd89806a4ac369bed2d256100f688a6ad5e0a709826dc4449e91e23c5506e642361ef5a313712f79bc4b3186861ca85a4bab17e7f943d1b8a333aa3ae7ce16b440d6018f9e04daf5725c7f1a93fad1a5a27b67895bd249aa91685de20af32c8b7e268c7f96877d0c85001135a4f0a8f1b8264fa6ebe5a349d8aecad1a16299ccf2fd9c7b85bace2ced3aa1276ba61ee78ed7e5ca5b67cdd458a9354030e6abbbabf56a0a2316fec9dba83b51d42fd3167f1e0f90855d5c66509b210265dc1e54ec44b43ba7cf9aef118b44d80912ce75166a6651e116cebe49229a7062c09931f71abd2293f76f7efc3215ba97800037e58e470bdbbb43c1b0439eaf79c54d93b44aac9efe9fbe151874cfb2a64cbee28cc4c0fe7775e5d870f1c02e5b2e3c5004c995f24c9b779cb753a277d0e71fd425eb6bc2ca56ce129db51f70740f31e63976b50c7312e9797d78c5b1ac24a5fa347cc916e0a83f5c3b675cd30b81e3fa10b93444e07397571cce98b28da51db9056bc728c5b0b1181e2fbd387b4c79ab1a5fefece37167af772ddad14eb4c3982da5a59d0e9eb173ec6315091170027a3ab5ef6aa129cb8585727b9358a28501d713a72f3f1db31714286f9b6408013af06045d75592fc0b7dd47c73ed9c75b11e9d7c69f7cadfc3280a9062c5273c43be1c34f87448864cea7b5c97d6d32f59bd5f25384653bb5c4faa45bea8b89402843e645b6b9269e2bd988ddacb033328ffb060450f7df080053e6969b251e875ecec32cfc592840d69ab69a75e06b379c535d95266b082f4f09c93162b33b0d9f7307a4eaaa52104437fed66f8ee3eabbd45d67b25a8133f496468b52baffdbfad93eef1a9818b5e42ec722788a3d8d3529fc777d2ba570801dfae01ec88302837c1fb9e0355727645ee1046c3f915f6ae82dad4fb6b0356a46518ffc834155c3b4fe6dafa6cc8a5ccf53c73a0849d8d44f7dcf72754e70e1b7dfb447bb4ef49d1a718f6171bbce200950e0ce926106b151a3e871d5ce49731bd6650a9b0ca972da1c5f136d44820ea6383c08f3b384cf2338e789c513f618cc5694a6f0cee104511e1ed7c5f23a1ebfd8a0db8424553240156dbf622831b0c643d1c551b6f3f7a98d29b85c2de05a65fa615eee16495bd90737672115b53e91c5d90028cf3f1a93953a153de53b44084e9ccff6b736693926daefebb2d77aa5ad689b92f31686669df16d1715cc58f7a2cfb72dd1a51e92f825993a74022be7e9eb6054654457094d14928f20215e7b222ac56b51adbec8d8bdb6983979a7e3a21b44b5d1518ca97d0b5195f51ed6a24350c89747e1edea51b448e3e9147054ce927873c90db394d86888e07dff177593d6f79e152302204aeb03be2386af3e24078bd028b1689f5e147c9f452c8ceb02ec59cc9db63a03576ceeafe98239023897da0236630a53c0de7f435a19869792fab36e7b9e635760f09069e6432e700035ac2a02879fff0a1e1bec522047193d94eb5df1efd53eea1144ca78940852f5ec9727904b366ede4f5e2d331fad5fc282ea2c47e923142771c3dd75a87357487def99e5f18e9d9ed623c175d02888c51f82c07a80d54716b3c3c2bdbe2e9f0a9bbaaebeb4d52936876406f5c00e8e4bbd0a5ec05797e6207c5ab6c88f1a688421bd05a114f4d7de2ac241fa0e8bedff47f762ddcbeaa91004f8d31e85095c81054994ad3826e344ba96040810fc0b2ad1de48cfade002c62e5a49a0731ab38344bc1636df16bf607d56855e56d684003c718e4bad9e5a099979fcddeeb1c4a7776cd37a3417cb0e184e29ef9bc0e87475ba663be09e00ab562eb7c0f7165f969a9b42414198ccf1bff2a2c8d689a414ece7662927665689e94db961ebaec5615cbc1a7895c6851ac961432ff1118d4607d32ef9dc732d51333be4b4d0e30ddea784eca8be47e741be9c19631dc470a52ef4dc13a4f3633fd434d787c170977b417df598e1d0dde506bb71d6f0bc17ec70e3b03cdc1965cb36993f633b0472e50d0923ac6c66fdf1d3e6459cc121f0f5f94d09e9dbcf5d690e23233838a0bacb7c638d1b2650a4308cd171b6855126d1da672a6ed85a8d78c286fb56f4ab3d21497528045c63262c8a42af2f9802c53b7bb8be28e78fe0b5ce45fbb7a1af1a3b28a8d94b7890e3c882e39bc98e9f0ad76025bf0dd2f00298e7141a226b3d7cee414f604d1e0ba54d11d5fe58bccea6ad77ad2e8c1caacf32459014b7b91001b1efa8ad172a523fb8e365b577121bf9fd88a2c60c21e821d7b6acb47a5a995e40caced5c223b8fe6de5e18e9d2e5893aefebb7aae7ff1a146260e2f110e939528213a0025a38ec79aabc861b25ebc509a4674c132aaacb7e0146f14efd11cfcaf4caa4f775a716ce325e0a435a4d349d720bcf137450afc45046fc1a1f83a9d329777a7084e4aadae7122ce97005930528eb3c7f7f1129b372887a371155a3ba201a25cbf1dcb64e7cdee092c3141fb5550fe3d0dd82e870e578b2b46500818113b8f6569773c677385b69a42b77dcba7acffd95fd4452e23aaa1d37e1da2151ea658d40a3596b27ac9f8129dc6cf0643772624b59f4f461230df471ca26087c3942d5c6687df6082835935a3f87cb762b0c3b1d0dda4a6533965bef1b7b8292e254c014d090fed857c44c1839c694c0a64e3fad90a11f534722b6ee1574f2e149d55d744de4887024e08511431c062750e16c74ab9f3242f2db3ffb12a8d6107faa229d6f6373b07f36d3932b3bdb04c19dd64eadd7f93c3c564c358a1c81dcf1c9c31e5b06568f97544c17dc15698c5cb38983a9afc42783faa773a52c9d8260690be9e3156aa5bc1509dea3f69587695cd6ff172ba83e6a6d8a7d6bbebbbcda3672731983f89bc5831dc37c3f3c5c56facc697f3cb20bd5dbadbd702e54844ac2f626901fe159db93dfd4773d8fe73562b846c1fc856d1802762840ebc72d7988bde75cbca70d319d32ce0cc0253bb2ad455723ee0c7f4736ce6e6665c5aca32a481c53839bc259167b013d0423395eeb9aaaee3206149a7d550d67fc5fdfe4a8a5c35d2510b664379ab8f72855a2af47abce2a632048eaf89e5cb4a88debc53a595103acce4f1cff18acff07afe1eb5716aa1e40b63134c3a3ae9579fa87f515be093c2d29db6d6b65c93661e00636b592704d093cc6716c2342eb1853d48c85c63ac8a2854462c7b77e7e3bd1eac5bca28ffaa00b5d349f8a547ad875b96a8c2b2910c9301309a3f9138a5693111f55b3c009ca947c39dfc82d98eb1caa4a9cbe885f786fa86e55be062222f8ba90a974073326b31212aece0a34a60").unwrap()).unwrap(); + let sig = &*hex::decode("781368e64dba542a7eacbd2257335cc943a03241009b797093c615f76a671a7591430441d80bb582304b33b9fce295e0dd57fe169355ddf4453a2aca62d8eb8109ef0d9cf3f5b0a94e04ad81b3e786014243ecde816551aa7fe01c639054256a491756bef59f5034f717ff4f85e70ba7731a49971415b6a7e7d816ab434b9f17a3095ede6fd432be2bfa82724045dda0dfff7a0281e9000939ccba3d8ab3245139c441648c76a6536127e4d1ef0df1531883ab78c8b41323617ad8db03d9908c9e08a9f7321c45051b3c94213347b11c4a84491de7a7be68701e47d7f0e0b33e767bef17694e4d33244ed92ebd74c85ab6c84441cddc14331e6ae8bd23674bda27f09c050d88f7d430feee7f15a72a24d653bb6bec54491b98362ce131d37c7d78a3f9a893db5abdccd6663593b88bc6c97f07f8eafccfd25e8180d918efbcd95bbf3da29f081e3e1932095939198e2a155b2d803a3e84ca4f34569df695c259faf3c0d8f0cd217ebd2dbad542b32fbb54e44aaf0b5dc739fafef2e46db8d68bfc35f44f038cb1f5231a1b5b134ae683e7f3297cc7a95bd191b310f68201450797fe3293cde1672dfeca4b493f53c768ea048a972a4cd84d39ef682957b8f28ba29487b4689b43fec2655823d9bb99ffcf31490366a9860a5d5b8e32a3b8bfeb6f55f88fb80c8c0142086f220e1f6f2862dabda58c3b6f5faa805b39cfac4b6d7ea7acdf1b0690063b0c1ea38c7c4755189966dc631055f153f71b77b114fa5c309316ba512330ea5cdb0bb176001e57461563d17259f35d0c30ef5ac838c0325402bab52c531469526ae3ee6293f7b5769d27e69fa81cd25a31cd095b126e70c57ac3169a5f585a11f1748d9d22f2564911c26a24b2153a78f3a06822f5f1963f237abeb48efd9a9cd478de579c5a0ba84d00e96fbde36d8ce20e7e948547fb6850834ff79d211830f6ee973359781d9d5008fb43a89354782fde4158177f5206ce1d38c889e99e4bb5b4ab34d6a05c42f5d719ea03dbc54adba75a3bb44a3c08c7556462f8c5b7c568a69242cf5be6098eba0a2249c1ca5b2109b6404a962abc1c159c6b48a79fb97e4a3337d99323746221297423f9bd1b12e78489e01e6a10f0fd6bba1cfd6ae1b75dbe69f8b8ee51a4e7f68ba2c407c9c0bad3892b29b0170ab75836fdd49a7ee3c2bb30f2c3d226bcec49140952170b0d160f97b30b7b7b096719538677ebb06922f26925227c8852acc107a8f173b38d96697584bd3dfe169b4073aa58a7bf371d5c4bb0eed30f08212defb3aec902d4546084176bf0f86d93cf36a4689a5e874b32d6b7d3c1e3fbcfd988c35dbc9a8d0a019ad6d7e15ec3ac97125db6abfe00beffe35a81666699a91e15945c62d646690b5b52de8b835ee9be53588fde5d63023b52b2b1f4610c237a829f5901a46042963cce7b85aa040adde02985e14e23c4eadb75221c607d24672e244c66c9c24c3cb7fd90bb23295c9d3d9da516bce3dd462d6660f9f91ef0618a4d4d3d6668c5d1e2e8ed433ebfe0762beb743324e11608f8b14b69ce4c221c1654ba4992a5af2d949c2939f95d1c8fb767af2a843cc7c78f57259d5c0c6ca83fca41ef5ecc4eeeea93e4518c24d3040f2cd90df3e535e989e606fa109e2c453ed7353db1cdb27137f005f9dd8d2aebfb7255a6098b690215e100cfe44ca0f2745fced48322bc9667ab16d2e1c0ff491b96a17b833d4fd44d31c2230ac835796e063ab03000f04f15c70560033763a48552cceaacade9ca5c8055f3745e179068a287183f2bc3ef6327dec5ac7cf7b052ef5a8873e697efde089688f43be464827c2fa83eb531b3674145e95c699c82990e684967dad319d9f64ab16cd9fe9b6c41232ac4ae3795fd8a76aa9b02e970242061c6da45a2af74ad9cb2a79935c92625e242f4bc7fce54d5c10a9e61f875162fa651b66057ba036f062d6d39d0502b93a5640b78c6c2fd20b02ff83676a87a94945d476c349803ea4fe60cdcea65bb2629e2bc09d4472ec63422dee2052f098deaf5531e6c9bed6672a8b699802efe0cded80c8455f585d1ba633d281f1a21adab48e63b44e0c2a4d7608cf98aabf8adc86bcb8f61e8b06cd2385f82e0a3cdd03cab152d5951859c4532f9168e78f17ba2a5772780327dcf4e62b4d26e443762fc488ae4cd4d1156dbd5782595cfd7697a514abc9b160c9ccf08edc86134a755b90e9bb543511e888e3157721a52d1bc5db33029fb335ea2114e21c03368c8d7f4d827960641772a4a32a738df60d19ec77ab09d22f57cc2523b9503b3f5b1cebc5ae15f885f159842db7359a1c89d3d82d3407068f15b6739626eb8c521fc8c5c7491f945d49f14e6989da340bdf49e7f8a792747aa658bc114143ba93f26022d001735b744639bbf22aab2a1851cfc934f9c69d3764fdea3d23db17998e6138cfd7cd9e9a47cb74193bd71aaf28cdd9d1eb595125546a4f4357ebbd1f410e3bf8557892de68509b5b98c5c229e942c910fdd3e54cb6ad54d8dd886cb97ecc06d1e401b8395d0bcb0db9a031dc66c9294f9053c68fc42042b1fa1671fc7d510b70916c0139cfebe3a91244527ce9439860cedb30908197be851cbd1d3b18ca541358449fb34fb5cd569630ed5f67b8795e87828f2ce3becfe457579d82333b0bbab094de391e1f8157bd431e365ca864630932bbebb48f45f8134424e18ab455029b54b19e2f3bfec5e44ad0ea5c03f53d8f925b635838aa7015a7c9e325bdfaff966ea9512dd50f87c8995cea7561c23f4fb06d964ab8f1913a6ca17e4ca60d6bc078e1784f89c673c91d955bcf45f58ca9709579d5e3831df12cfdb7516fd21878cb54243579b9346d2de4be25f508e84b1adc78cb91c03da3c4fd59e4529189838f74f6312820620a5996b791ffcb332f847094613f2148b862034fa89d0d0ff1808d902c5d1af64d5522492d61ecde4c73be89a33782cef1acc1dc327fb2eb9d17642209b85aa8b1dc57cbf067c7aa29da6b7e157d23e171d3ae6f3855834071791402c851ff2dd67109979f7ee5e09e64b4eefdee7112b55ce200bb8c8051e3428c305fa1d576bffbb25a70eb571168fc60dadd928b10cfd07de80a85b8df3edc372d488c21f0d5787611cc6fb73aeeb6f920a109294b49d3870f90de3b360d14df77ef95640bcac7a4dbca901a31db83e83f5c59ce327207ea9b27c3b978d30d53865c1b84764f025e8732d5007554ce5c9cc410b2eefd7e4d990c538557606a6bc47577a43768d30aa3e8598fd6f4fe7ac439f3931c58fd69d90765ac9f456ac7de085e14a0898c4557f5d3baaae07edc607de6900146b97b35aae570153dc107815ef9febdd4fd567d637fee8f8bfb4b3413ea6aead4846ab733a04f1e4bc32a3bbb1c16baf8d0bdb9ccb82fe46479ccfd040b5e64064e539b39c66e4501dc822873ac6119a4a112a1f7cd6df0e5f84356ce853ced34ce69a9e7383534983c51c50269bf8b9586a0e5ba905fd3bce080b00e7f7d48e55f489479b5771e995fb020e58feb74af65c3ee76aa4e69b5ba8bda249a1b2d62c08d418c3635d061846040843991ab475473da85d94981fb84425e7ad951ce0a42be642fe658b7aaae72b147cdc086c24b1571eb2272e2a72b15660d854ebe19ec7d9ab7ea17800d0b6ae727b39217467c662ba08e6f19193951eeff02806a7843eb5c71b2f04dcb605ecedc5128cd67703038c44bf20fe06f3ed8c1368fe38e72944d5c52fca46a45fd48d8fe5da64183d4d62ec01aa3d9d672ec67a01c17f21e02525f0513cce030c664fc8784763086608bc8099c204c255ffed1daf432ceb45fbd135e21e8190c5bfee192171faa77520481e69e87b7f76790bef76cb8d3c88f5c6e32fc59e7bd45351d66696b61d9f40726fb9a98000b68738cf7e34b98b6a4aaa2ac1d7b1407db89783f8077103ea9c9e89247eae078adfb36e21474c3bb1fe0c87687c6233a533a01e1081b93a3521d339f39c075609bace531994988ae314f77fc6034113a138c67eb7e03750cbec8d28bd21afedfefa8f091619ae500b4ca4599d019dc8ca4bf118d70b8676dfc796a4f6d986adba4c8574ed4abaea5465466220e5e53dc8fcb395d1e59d278673cbc4e3f40658df98ac2fd126a94922879e1a3be91c1acc20803c35fa764abbadab07bde85ff4bd9e0fb6f06baf5bf42b8a2cbf6c2f62606becc361552921a12d6c8236fde84db4bddac77e8872478cffc4e148c1c7acfedf6b17d98731c2de36f3cbef1f6f781a940e0874d5b74535bbe066b53064d43b13926570a9e1c4e6da206c8bd252caf2b62e7d223f7ac12939137f330be59374d7295a6c2dff92e07c727510e48d970593e47229fc8bc3bd5b8ea780dacff4d23063df65feda5f8f65b17a333e532acad7916780c74d6a70d38b367f3f6f4e947b85fc15235bbe46b26495d2780098db853a931377cfbedea620f2355ca21e81ce9e0078b0dd6cb70f23ed558682be3b3d594eefe85344e1f275428b316cc088995939298f2a2d15ac9b676ac3e9cb92f2a64dec7732a91fc761aa1b126ea575e3953177da6e1cd78faea824665330a81d9e24572b9860bf0aba4df8bd5d4e3e2c72bbfbb2a985c7ae2f077951fe8401e1d156ecada1e353817b20f41e0b2460a0caaf2b36d6a7f1b35125d797dcc714421027d14171765a646071ed952b6a5294eecf6a3a71c104c843a4a8b3efcc27467b20cb0a94abf5802229ea4d8312783e78791a50b3c0a88fe6497198cd4bf470dac46f34e50019fdee2040cfe99124b312b1122b83e51d878877cec0855f1158c445cfdc2253f4389d5e3a8ba1669abb5976a4617e85f543da9f5e30b10ca7481c8185392782b46fb0a0e5ab408b2945e3c79a1cc49fb7c27254a9b540e7397a5b655bf7e4f83184db32a128aa2e00a624d7dcd6b77efd151f1e5cba8890af9170fa06c555715dc1787e995ad19270973ae95b88dcafdaf62c28843d3f8b9c78dc8e37d911dab3f7ee9d4c7389c654bdcfc05056b360020140e57e31473258a4081e2a708f7caba90c356d0847098fc0762484086aba898a60b023d6a3060402406240748785d51caff52a0ef3dc2a45dadf80ac18502d24422a8cbb10192b88f4e9160206b2ed3e04114f2a339df269e2c36b8613ce37087471701755330cb559575366ee0fa2d3afcda32eede6dd906345daaa04812198e96c42239c242edb90059709f497da5b87705384aef2af22dc2edfef3c00d8c9156d8b3163fe7a7779e04f04911a8b934fb3072eee844484fede5e2ee96d338eefe2da986067ffe0218ada7de1d0e42d823d6b033918278888ba0608ab8f7be997bdf263689a36f5204c802ad836363779b4b0d6ce5083df0b98a2e2c700062a4fa5e57bc73bd45357e01d90c7954bc6904d1ce8166a9168da39a60c5cae8119bb6b9ab074fb2d0aee384fc2c0e4806811d6002b4e2401e7430b50cb0e8075f33d5386aecde256e169d95e2f9c6556c08ba042e68a53ce8aca9cc02818f7382f150dc04de0019b19c7a3ab0d72d6ed013d7a115d74b279f71fd6effef34049877e0b11e0659be938a5de684eaf23513095eb4a1bdf536c3c01a4655c4b4a0673214cef29a481d06a02cc9a5bfc7b8d846c33484cd67b1de98f60b69918f177b64558ca567a6237d35ff01771a42320ce02bb98f3e4ad4ac7db75611bd9961eac662a38b1f785970c99f3dd105ff586f61301c48d66708cbf7d53a733e357b6c256e8b73f0e1305a0bc137989e521100c2ee6259e607fe12198e8bcb988b0854668e40d7cae6adc3ba40ba121b7319d06d988a073d03097b9f5c1c07284b6473ae57bf154811b77baceb0412b8a6983bdd0ccd9e3bf014e520009cb26d5780eabef1bbafa5e25d41098a54c47fce8b68d395291d54284d33aa50b9664d1510b467c8a539361ca9a4448bc01fcb4c4e3ef475e8afb46a494ae13ee9ea8a1266825fba7f32b9712fde252698a68359b50141d90f5c4a06283ddb54ad7e1412ac5ebb12501f7a82b2a7f27b2dbab626c3db4074523b3211d3182ea261397a6f7b187cf2b8a356ded10812f1d305169aedf79b5ff1cf7c2d6e86ee11f28e96aa63b5a03f59fc960ac7d0572e91dda61905c0711a9b26344a2a10aa2041f2b13cb1a9a9a27774b6d0deddc9d81ea1b142ad7b72be47991f2c9261d6708156e38d00b074020766eb0c494392d65b82ca65f7c3352f9bb78325ecb6df596c8ae57826b08ccd6f1d529d2e25925c1ac972425bedeb88a5d0e3138ddc434da3462ebdf6b1239a21f141ec62cbe4bb993ba253b55a76d30fac19c2c1384ef6b9746c07787aa1fe913a1348390bd8c1f386a08c77cf7106c927ce24dffc9d6ee1b32354d95ed2923482531de6b390bf0f5eb80276e90e7ed11131c848bbabec4d317236269a0a3a7cbe0f1272f93949ca6d23ea2a7ee3f697791aab71533423066d400000000000000000000000000000000000000000000000000000000050e181f23292c2f").unwrap(); + assert_eq!(sig.len(), MLDSA87_SIG_LEN); + + if MLDSA87::verify(&mldsa87_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } +} + + + +fn main() { + // bench_do_nothing(); + // bench_mldsa44_keygen(); + // bench_mldsa44_lowmem_keygen(); + // bench_mldsa65_keygen(); + // bench_mldsa65_lowmemory_keygen() + // bench_mldsa87_keygen(); + // bench_mldsa87_lowmemory_keygen() + // bench_mldsa44_sign(); + // bench_mldsa44_lowmemory_sign(); + // bench_mldsa65_sign(); + // bench_mldsa65_lowmemory_sign(); + // bench_mldsa87_sign(); + // bench_mldsa87_lowmemory_sign(); + // bench_mldsa44_verify(); + // bench_mldsa44_lowmemory_verify(); + // bench_mldsa65_verify(); + // bench_mldsa65_lowmemory_verify(); + // bench_mldsa87_verify(); + bench_mldsa87_lowmemory_verify(); +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 21b4895..b46df8c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,10 +4,10 @@ pub use bouncycastle_factory as factory; pub use bouncycastle_hex as hex; pub use bouncycastle_hkdf as hkdf; pub use bouncycastle_hmac as hmac; -pub use bouncycastle_mlkem as mlkem; -pub use bouncycastle_mlkem_lowmemory as mlkem_lowmemory; pub use bouncycastle_mldsa as mldsa; pub use bouncycastle_mldsa_lowmemory as mldsa_lowmemory; +pub use bouncycastle_mlkem as mlkem; +pub use bouncycastle_mlkem_lowmemory as mlkem_lowmemory; pub use bouncycastle_rng as rng; pub use bouncycastle_sha2 as sha2; -pub use bouncycastle_sha3 as sha3; \ No newline at end of file +pub use bouncycastle_sha3 as sha3;