diff --git a/CLAUDE.md b/CLAUDE.md index 6219e32e..c6440c75 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -28,8 +28,9 @@ fixed. ## Toolchain -- Uses Rust **nightly** (pinned in `rust-toolchain.toml`) — `core/src/lib.rs` uses `#![feature(adt_const_params)]`. -- 2024 edition (set workspace-wide in the root `Cargo.toml`). +- Builds on Rust **stable**: there is no toolchain pin, and no crate enables a `#![feature(...)]` + gate, so nightly-only tooling (`-Z` flags and the like) is not available. +- 2024 edition (set workspace-wide in the root `Cargo.toml`), which needs Rust 1.85 or later. ## Common commands @@ -108,6 +109,13 @@ Repo mechanics behind those rules, which the documents don't spell out: - **CLI commands stream.** The `cli/` binary is stdin→stdout with ~1 KB buffers so commands compose in shell pipelines; preserve that when adding subcommands. - Trait → factory → CLI is the wiring path for a new primitive; see [the workspace architecture](#the-core--core-test-framework--factory-spine) above for the crates involved. +- **Comments describe the code that is there, not the road that led to it.** Do not add a comment + explaining a transient design decision — an approach that was tried and abandoned, what an earlier + version did, why one formulation was chosen over another that is no longer present — or describing + a design the code does not use. Such comments are noise: they age badly, and a reader has to work + out that they describe nothing in front of them. A comment that explains why the present code is + the way it is, and that a naive edit would break it (a spec step, an invariant, a constraint), is + wanted; a comment narrating how it got that way is not. ## Working from specifications diff --git a/alpha_0.1.3_release_notes.md b/alpha_0.1.3_release_notes.md index 57f9e97c..05c3644c 100644 --- a/alpha_0.1.3_release_notes.md +++ b/alpha_0.1.3_release_notes.md @@ -7,3 +7,4 @@ * bug fixes to the way SHA3/SHAKE handled absorbing and squeezing a partial final byte. * Design discussions about whether core::traits::XOF (in the abstract) should allow interleaving absorb -> squeeze -> absorb (ie "absorb-after-squeeze). Outcome: absorb-after-squeeze forbidden. Could be changed in the future. +* Further reductions to the memory usage of the mldsa-lowmemory and mlkem-lowmemory crates. \ No newline at end of file diff --git a/crypto/mldsa-lowmemory/src/aux_functions.rs b/crypto/mldsa-lowmemory/src/aux_functions.rs index 5eaf55f3..caa44599 100644 --- a/crypto/mldsa-lowmemory/src/aux_functions.rs +++ b/crypto/mldsa-lowmemory/src/aux_functions.rs @@ -6,7 +6,7 @@ use crate::params::{ GAMMA1_2_POW_17, GAMMA1_2_POW_19, GAMMA2_Q_MINUS_1_OVER_32, GAMMA2_Q_MINUS_1_OVER_88, MLDSAParams, }; -use crate::polynomial::Polynomial; +use crate::polynomial::{HintRow, Polynomial, ZEROED_HINT_ROW, hint_set}; use bouncycastle_core::traits::XOF; use bouncycastle_utils::secret::ZeroizablePrimitive; @@ -358,13 +358,15 @@ pub(crate) fn unpack_z_row( if z.check_norm(P::gamma1_minus_beta) { Err(()) } else { Ok(z) } } /// Part of unpacking the sig value +/// +/// Returns the decoded row, or `None` if the encoded hint is malformed. pub(crate) fn unpack_h_row( row: usize, sig: &[u8; SIG_LEN], -) -> Option { +) -> Option { debug_assert!(row < P::k); - let mut h = Polynomial::new(); + let mut out = ZEROED_HINT_ROW; // skip over the other stuff in the encoded sig value let pos = P::C_TILDE_LEN + P::l * P::POLY_Z_PACKED_LEN; @@ -401,7 +403,7 @@ pub(crate) fn unpack_h_row( return None; } // 12: 𝐡[𝑖]_𝑦[Index] ← 1 - h[sig[pos + j] as usize] = 1; + hint_set(&mut out, sig[pos + j] as usize); // 13: Index ← Index + 1 // > done by for loop @@ -418,7 +420,7 @@ pub(crate) fn unpack_h_row( } } - Some(h) + Some(out) } /// Algorithm 29 SampleInBall(𝜌) @@ -544,12 +546,22 @@ pub(crate) fn rej_bounded_poly(rho: &[u8; 64], nonce: &[u8; 2]) h.absorb(rho).expect("absorb before squeeze is infallible"); h.absorb(nonce).expect("absorb before squeeze is infallible"); - // SHAKE is fairly inefficient if only 3 bytes are squeezed at a time, so the implementation does a block instead. - // size is not a limitation as long as it is a multiple of 3. - // 312 seems to be the sweet spot after some experimentation - // which is possibly also related with the average rejection rate. - // Also, 312 is a multiple of 8 (efficient for SHAKE) - let mut z_arr = [0u8; 312]; + // Deviation from FIPS 204, Algorithm 31 step 5, which squeezes one byte per loop iteration: + // H is SHAKE256, which produces a whole 136-byte block per Keccak permutation, so squeezing a + // byte at a time wastes most of each block. The squeeze is buffered instead, and the refill + // below makes the byte stream — and therefore the output — identical to the spec's. + // + // 272 is exactly two SHAKE256 blocks (2 × 136), so filling the buffer costs two permutations + // with nothing stranded in the sponge's output queue, and it covers the whole polynomial in a + // single squeeze almost always. Per FIPS 204 §C, each iteration consumes one byte and yields + // Binomial(2, θ) coefficients, θ = 15/16 for η = 2 and 9/16 for η = 4. The worst case is + // η = 4 (ML-DSA-65): 228 bytes needed on average, and over 300k simulated seeds the largest + // requirement was 276 bytes, so the refill runs for roughly 1 seed in 100,000. For η = 2 + // (ML-DSA-44/87) it is 137 bytes on average and never exceeded 150. + // + // This is a buffer, not the iteration cap of FIPS 204 Table 3 (481 bytes for RejBoundedPoly): + // the loop refills rather than giving up, so no cap is imposed. + let mut z_arr = [0u8; 272]; h.squeeze_out(&mut z_arr); let mut idx: usize = 0; diff --git a/crypto/mldsa-lowmemory/src/lib.rs b/crypto/mldsa-lowmemory/src/lib.rs index 51a4780b..784b683b 100644 --- a/crypto/mldsa-lowmemory/src/lib.rs +++ b/crypto/mldsa-lowmemory/src/lib.rs @@ -82,38 +82,37 @@ //! //! //! ## Algorithm Peak Memory Usage -//! The table below shows peak memory usage of the ML-DSA algorithms and the rough performanc (throughput) impact. -//! -//! Measuring peak application memory usage can be a bit tricky, and the numbers obtained depend heavily on how the -//! measurement harness is designed. Here, we aim to provide a conservative measurement, meaning that we are aiming for an -//! over-estimate so that any deployment within an existing application will use incrementally less additional memory -//! than the amount stated here. //! -//! Our measurement methodology is to compile a simple standalone HelloWorld application that only calls the function under test -//! with as minimal as possible hard-coded data (such as keys or ciphertexts) and measure the peak memory usage of running -//! the compiled binary using `valgrind --tool=massif --heap=no --stack=yes`. The flags for heap and stack -//! reflect the fact that this is a `no_std` rust application and therefore the cryptographic functions use no heap memory. -//! The measurements may over-estimate by as much as 3 kb since that that's the measured peak memory usage of a do-nothing -//! HelloWorld rust application. +//! The table below shows peak memory usage of the ML-DSA algorithms and the rough performanc (throughput) impact. //! -//! | Algorithm | Peak swap memory usage (kB) | Throughput (ops/s) | +//! | Algorithm | Peak stack memory usage (kB) | Throughput (ops/s) | //! |---------------------------|-----------------------------|---------------------| -//! | MLDSA44_lowmemory/KeyGen | 12.6 (113.8) | 11,800 (11,300) | -//! | MLDSA65_lowmemory/KeyGen | 15.0 (124.1) | 5,500 (7.000) | -//! | MLDSA87_lowmemory/KeyGen | 15.2 (197.8) | 3,300 (4,200) | -//! | MLDSA44_lowmemory/Sign | 24.8 (117.7) | 850 (4,000) | -//! | MLDSA65_lowmemory/Sign | 28.2 (159.6) | 580 (2,900) | -//! | MLDSA87_lowmemory/Sign | 31.1 (236.7) | 315 (2,000) | -//! | MLDSA44_lowmemory/Verify | 17.1 (73.0) | 10,100 (14,000) | -//! | MLDSA65_lowmemory/Verify | 18.0 (134.4) | 6,300 (8,400) | -//! | MLDSA87_lowmemory/Verify | 20.6 (211.6) | 3,500 (5,000) | +//! | MLDSA44_lowmemory/KeyGen | 12.3 (62.3) | 11,700 (11,400) | +//! | MLDSA65_lowmemory/KeyGen | 14.8 (93.9) | 6,800 (7,000) | +//! | MLDSA87_lowmemory/KeyGen | 16.8 (141.4) | 3,900 (4,200) | +//! | MLDSA44_lowmemory/Sign | 22.3 (84.0) | 990 (3,400) | +//! | MLDSA65_lowmemory/Sign | 27.9 (124.7) | 490 (2,400) | +//! | MLDSA87_lowmemory/Sign | 31.6 (182.6) | 330 (1,700) | +//! | MLDSA44_lowmemory/Verify | 15.2 (52.7) | 11,200 (13,200) | +//! | MLDSA65_lowmemory/Verify | 19.1 (81.3) | 6,400 (8,000) | +//! | MLDSA87_lowmemory/Verify | 22.3 (123.7) | 3,700 (4,800) | //! //! Values in parentheses are the comparison values from the un-optimized implementation in the \[bouncycastle_mldsa] crate. -//! Size numbers were collected with valgrind using a simple main program that calls only the measured function. -//! Performance throughput numbers were collected on my laptop using the library's provided benchmarks, so -//! performance they should be taken with an extreme grain of salt. //! +//! **Caveates** +//! +//! Throughput numbers are meant to show relative difference between the two implementations and not be +//! absolute performance measurements. +//! +//! Size numbers were collected with valgrind using a simple main program that calls only the measured function. +//! Measurements include the public key and signature buffer, which is a realistic setting, but +//! numbers would be lower if those were on the heap and excluded from the measurement. //! Actual values may vary based on build configuration and target architecture. +//! The peak also depends on how the compiler lays out the measured function's stack frame relative +//! to its caller and whether it elides copies at the call boundary, which is outside +//! the library's control. The memory benchmarking framework included in the library attempts to +//! This effect is small for the low-memory crates (well under 1 kB) and larger for the full implementations +//! in parentheses (a few kB), so treat the latter as approximate. //! //! # Usage //! diff --git a/crypto/mldsa-lowmemory/src/low_memory_helpers.rs b/crypto/mldsa-lowmemory/src/low_memory_helpers.rs index 7fb15a42..f28948cf 100644 --- a/crypto/mldsa-lowmemory/src/low_memory_helpers.rs +++ b/crypto/mldsa-lowmemory/src/low_memory_helpers.rs @@ -5,7 +5,6 @@ use crate::aux_functions::{bit_unpack_eta_out, expand_mask_poly, rej_ntt_poly, unpack_z_row}; use crate::params::MLDSAParams; use crate::polynomial::Polynomial; -use bouncycastle_core::errors::SignatureError; use bouncycastle_utils::secret::{Secret, ZeroizablePrimitive}; #[inline(always)] @@ -43,8 +42,8 @@ pub(crate) fn compute_w_row( pub(crate) fn compute_wp_approx_row( rho: &[u8; 32], sig: &[u8; SIG_LEN], - t1: &Polynomial, - c: &Polynomial, + t1: Polynomial, + c_hat: &Polynomial, idx: usize, ) -> Result { // Algorithm 8: line 9: 𝐰′_approx ← NTT−1(𝐀_hat ∘ NTT(𝐳) − NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑)) @@ -72,15 +71,15 @@ pub(crate) fn compute_wp_approx_row( Az_acc.add_ntt(&tmp); } - let ct1 = compute_ct1(t1.clone(), c.clone()); - fn compute_ct1(mut t1_i: Polynomial, mut c: Polynomial) -> Polynomial { - t1_i.shift_left_d(); - t1_i.ntt(); - c.ntt(); - t1_i.multiply_ntt(&c); + // NTT(𝑐) ∘ NTT(𝐭1 ⋅ 2^𝑑), computed in place in the buffer `t1` arrived in. + let ct1 = { + let mut ct1 = t1; + ct1.shift_left_d(); + ct1.ntt(); + ct1.multiply_ntt(c_hat); - t1_i - } + ct1 + }; Az_acc.sub(&ct1); Az_acc.inv_ntt(); @@ -89,34 +88,39 @@ pub(crate) fn compute_wp_approx_row( Ok(Az_acc) } +/// 𝐳 is written into `z_out`, and `true` returned. `false` means the norm check rejected this +/// component, and `z_out` then holds a partial value that the caller must discard. pub(crate) fn compute_z_component( - s1: &Polynomial, + s1: Polynomial, rho_p_p: &[u8; 64], c_hat: &Polynomial, kappa: u16, col: usize, -) -> Result, SignatureError> { + z_out: &mut Polynomial, +) -> bool { let y = expand_mask_poly::

(rho_p_p, kappa + col as u16); - let mut s1_hat = s1.clone(); - s1_hat.ntt(); - s1_hat.multiply_ntt(c_hat); - let mut cs1 = s1_hat; // rename - cs1.inv_ntt(); - let mut z = cs1; - z.add_ntt(&y); - - if z.check_norm(P::gamma1_minus_beta) { Ok(None) } else { Ok(Some(z)) } + + // 𝑐𝐬1 ← NTT−1(𝑐_hat ∘ NTT(𝐬1)), built in place in the caller's buffer. + *z_out = s1; + z_out.ntt(); + z_out.multiply_ntt(c_hat); + z_out.inv_ntt(); + + // 𝐳 ← 𝐲 + 𝑐𝐬1 + z_out.add_ntt(&y); + + !z_out.check_norm(P::gamma1_minus_beta) } pub(crate) fn compute_w0cs2_component( - s2: &Polynomial, + s2: Polynomial, w: &Polynomial, c_hat: &Polynomial, -) -> Option { - let mut s2_hat = s2.clone(); - s2_hat.ntt(); - s2_hat.multiply_ntt(c_hat); - let mut cs2 = s2_hat; // rename + w0cs2_out: &mut Polynomial, +) -> bool { + let mut cs2 = s2; + cs2.ntt(); + cs2.multiply_ntt(c_hat); cs2.inv_ntt(); // Note: this could be further optimized by using the optimization described in @@ -125,23 +129,25 @@ pub(crate) fn compute_w0cs2_component( // and checking whether ‖r0‖∞ < γ2 − β and r1 = w1, it is equivalent to just check that // ‖w0 − cs2‖∞ < γ2 − β, where w0 is the low part of w. If this check passes, w0 − cs2 // is the low part of w − cs2." - let mut w0cs2 = w.clone(); - w0cs2.low_bits::

(); - w0cs2.sub(&cs2); - if w0cs2.check_norm(P::gamma2_minus_beta) { None } else { Some(w0cs2) } + // `w` is still needed by the caller, so its low half is taken in the out-buffer. + *w0cs2_out = *w; + w0cs2_out.low_bits::

(); + w0cs2_out.sub(&cs2); + + !w0cs2_out.check_norm(P::gamma2_minus_beta) } pub(crate) fn compute_ct0_component( - t0_row: &Polynomial, + t0_row: Polynomial, c_hat: &Polynomial, -) -> Option { - let mut t0_hat = t0_row.clone(); - t0_hat.ntt(); - t0_hat.multiply_ntt(c_hat); - let mut ct0 = t0_hat; // rename - ct0.inv_ntt(); - - if ct0.check_norm(P::gamma2) { None } else { Some(ct0) } + ct0_out: &mut Polynomial, +) -> bool { + *ct0_out = t0_row; + ct0_out.ntt(); + ct0_out.multiply_ntt(c_hat); + ct0_out.inv_ntt(); + + !ct0_out.check_norm(P::gamma2) } /// Unpack a single s value from the packed representation. diff --git a/crypto/mldsa-lowmemory/src/mldsa.rs b/crypto/mldsa-lowmemory/src/mldsa.rs index b1658579..032df2e5 100644 --- a/crypto/mldsa-lowmemory/src/mldsa.rs +++ b/crypto/mldsa-lowmemory/src/mldsa.rs @@ -392,6 +392,7 @@ use crate::low_memory_helpers::{ use crate::mldsa_keys::{MLDSAPrivateKeyInternalTrait, MLDSAPrivateKeyTrait}; use crate::mldsa_keys::{MLDSAPublicKeyInternalTrait, MLDSAPublicKeyTrait}; use crate::params::{MLDSA44Params, MLDSA65Params, MLDSA87Params, MLDSAParams}; +use crate::polynomial::{Polynomial, ZEROED_HINT_ROW, hint_get}; use crate::{ MLDSA44PrivateKey, MLDSA44PublicKey, MLDSA65PrivateKey, MLDSA65PublicKey, MLDSA87PrivateKey, MLDSA87PublicKey, @@ -844,24 +845,23 @@ impl< // 18-23 (z path): compute and encode each z polynomial directly into the caller buffer. let mut rejected = false; for col in 0..P::l { - let z = match compute_z_component::

( + let mut z = Polynomial::new(); + if !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), + // sk.compute_s1_row(col), + s_unpack::(&s1_packed, col), &rho_p_p, &c_hat, kappa, col, - )? { - Some(z) => z, - None => { - rejected = true; - break; - } - }; + &mut z, + ) { + rejected = true; + break; + } let start = z_offset + col * P::POLY_Z_PACKED_LEN; bitpack_gamma1::

(&z, &mut output[start..start + P::POLY_Z_PACKED_LEN]); @@ -877,40 +877,39 @@ impl< let mut hint_count = 0usize; for row in 0..P::k { let mut w = compute_w_row::

(&sk.rho(), &rho_p_p, kappa, row); - let mut tmp = match compute_w0cs2_component::

( + let mut tmp = Polynomial::new(); + if !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), + // sk.compute_s2_row(row), + s_unpack::(&s2_packed, row), &w, &c_hat, + &mut tmp, ) { - Some(tmp) => tmp, - None => { - rejected = true; - break; - } - }; + rejected = true; + break; + } - let ct0 = match compute_ct0_component::

( + let mut ct0 = Polynomial::new(); + if !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), + // sk.compute_t0_row(row), &c_hat) { + sk.compute_t0_row(row, &s1_packed, &s2_packed), &c_hat, + &mut ct0, ) { - Some(ct0) => ct0, - None => { - rejected = true; - break; - } - }; + rejected = true; + break; + } tmp.add_ntt(&ct0); tmp.conditional_add_q(); w.high_bits::

(); - let (hint_row, weight) = tmp.make_hint_row::

(&w); + let mut hint_row = ZEROED_HINT_ROW; + let weight = tmp.make_hint_row::

(&w, &mut hint_row); let next_hint_count = hint_count + weight as usize; // mutants note: don't have a test vector that exercises this condition, @@ -921,7 +920,7 @@ impl< } for idx in 0..N { - if hint_row[idx] != 0 { + if hint_get(&hint_row, idx) != 0 { output[hint_offset + hint_count] = idx as u8; hint_count += 1; } @@ -1008,7 +1007,11 @@ impl< // skip because this function is being handed mu // 8: 𝑐 ∈ 𝑅𝑞 ← SampleInBall(c_tilde) - let c = sample_in_ball::

(&unpack_c_tilde::

(sig)); + let mut c_hat = sample_in_ball::

(&unpack_c_tilde::

(sig)); + // Deviation from the FIPS: line 9 below reads NTT(𝑐) inside the per-row expression, but 𝑐 + // does not depend on the row, so it is transformed once here and the 𝑘 rows share it. Same + // value, 𝑘−1 fewer NTTs, and no per-row clone of 𝑐. The signer does the same at lines 16-17. + c_hat.ntt(); // 12: 𝑐_tilde_p ← H(𝜇||w1Encode(𝐰1'), 𝜆/4) // ▷ hash it; this should match 𝑐_tilde @@ -1018,17 +1021,23 @@ impl< for row in 0..P::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::( + pk.rho(), + sig, + pk.unpack_t1_row(row), + &c_hat, + row, + ) } { Ok(wp_approx) => wp_approx, // means the norm check on z failed 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 Err(SignatureError::SignatureVerificationFailed), + // `None` means the encoded hint is malformed: out-of-order indices, more than OMEGA + // bits set, or nonzero padding + let Some(h_i) = unpack_h_row::(row, sig) else { + return Err(SignatureError::SignatureVerificationFailed); }; // 10: 𝐰1′ ← UseHint(𝐡, 𝐰'_approx) @@ -1366,7 +1375,9 @@ impl< 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()) + // The length was checked above, so this conversion cannot fail. + let sig: &[u8; SIG_LEN] = sig.try_into().unwrap(); + Self::verify_mu(pk, &mu, sig) } fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result { @@ -1396,7 +1407,9 @@ impl< return Err(SignatureError::LengthError("Signature value is not the correct length.")); } - Self::verify_mu(&self.pk.unwrap(), &mu, &sig.try_into().unwrap()) + // The length was checked above, so this conversion cannot fail. + let sig: &[u8; SIG_LEN] = sig.try_into().unwrap(); + Self::verify_mu(&self.pk.unwrap(), &mu, sig) } } diff --git a/crypto/mldsa-lowmemory/src/mldsa_keys.rs b/crypto/mldsa-lowmemory/src/mldsa_keys.rs index 76477c63..9487bf4d 100644 --- a/crypto/mldsa-lowmemory/src/mldsa_keys.rs +++ b/crypto/mldsa-lowmemory/src/mldsa_keys.rs @@ -358,15 +358,16 @@ impl Polynomial { debug_assert!(idx < P::k); - // [Optimization Note]: - // This is one of the places that a row of s1 can be re-computed instead of expanded from the compressed form. - // let mut s1 = self.compute_s1_row(0); - let mut s1_hat_i = s_unpack::(s1_packed, 0); - s1_hat_i.ntt(); - let mut t_i = { let mut t_hat_i = expandA_elem(&self.rho, idx, 0); - t_hat_i.multiply_ntt(&s1_hat_i); + { + // [Optimization Note]: + // This is one of the places that a row of s1 can be re-computed instead of expanded from the compressed form. + // let mut s1 = self.compute_s1_row(0); + let mut s1_hat_i = s_unpack::(s1_packed, 0); + s1_hat_i.ntt(); + t_hat_i.multiply_ntt(&s1_hat_i); + } for col in 1..P::l { // [Optimization Note]: diff --git a/crypto/mldsa-lowmemory/src/polynomial.rs b/crypto/mldsa-lowmemory/src/polynomial.rs index 6b38bbbe..404ac43d 100644 --- a/crypto/mldsa-lowmemory/src/polynomial.rs +++ b/crypto/mldsa-lowmemory/src/polynomial.rs @@ -18,6 +18,27 @@ pub(crate) struct Polynomial { pub(crate) coeffs: [i32; N], } +/// One row of the hint vector 𝐡: `N` booleans, one per coefficient, packed one bit each, +/// least-significant bit first. Use [`hint_get`] and [`hint_set`] rather than indexing it. +pub(crate) type HintRow = [u8; N / 8]; + +/// A hint row with every bit clear. +pub(crate) const ZEROED_HINT_ROW: HintRow = [0u8; N / 8]; + +/// Reads coefficient `i`'s hint bit, as the 0 or 1 that FIPS 204 treats as a boolean. +#[inline(always)] +pub(crate) fn hint_get(h: &HintRow, i: usize) -> u8 { + debug_assert!(i < N); + (h[i / 8] >> (i % 8)) & 1 +} + +/// Sets coefficient `i`'s hint bit. The row must have been zeroed first. +#[inline(always)] +pub(crate) fn hint_set(h: &mut HintRow, i: usize) { + debug_assert!(i < N); + h[i / 8] |= 1 << (i % 8); +} + /// Convenience function to avoid ".0" all over the place. impl Index for Polynomial { type Output = i32; @@ -131,17 +152,22 @@ impl Polynomial { } } - /// Creates the hint vector, and also returns its hamming weight (ie the number of 1's). - pub(crate) fn make_hint_row(&self, r: &Self) -> (Self, i32) { - let mut out = Polynomial::new(); + /// Writes one row of the hint vector into `out`, and returns its hamming weight (ie the number + /// of 1's). + pub(crate) fn make_hint_row(&self, r: &Self, out: &mut HintRow) -> i32 { + out.fill(0); + let mut count = 0i32; for i in 0..N { let x = make_hint::

(self[i], r[i]); - out[i] = x; + debug_assert!(x == 0 || x == 1, "a hint is a boolean; see FIPS 204, Algorithm 39"); + if x == 1 { + hint_set(out, i); + } count += x; } - (out, count) + count } /// SimpleBitPack(𝐰1[𝑖], (𝑞 − 1)/(2𝛾2) − 1), the per-coordinate body of @@ -267,9 +293,9 @@ impl Polynomial { } } - pub(crate) fn use_hint(&mut self, h: &Polynomial) { + pub(crate) fn use_hint(&mut self, h: &HintRow) { for i in 0..N { - self[i] = use_hint::

(self[i], h[i]); + self[i] = use_hint::

(self[i], hint_get(h, i) as i32); } } } diff --git a/crypto/mldsa/src/aux_functions.rs b/crypto/mldsa/src/aux_functions.rs index bf0c2f91..3806de8e 100644 --- a/crypto/mldsa/src/aux_functions.rs +++ b/crypto/mldsa/src/aux_functions.rs @@ -421,14 +421,23 @@ pub(crate) fn sig_encode( /// Reverses the procedure sigEncode. /// Input: Signature 𝜎 ∈ 𝔹𝜆/4+ℓ⋅32⋅(1+bitlen (𝛾1−1))+𝜔+𝑘. /// Output: 𝑐 ∈ 𝔹𝜆/4, 𝐳 ∈ 𝑅ℓ with coefficients in \[−𝛾1 + 1, 𝛾1], 𝐡 ∈ 𝑅𝑘, or ⊥. -/// Output: (c_tilde, z, h) +/// +/// Deviation from the FIPS: the outputs (c_tilde, z, h) are written into the caller's buffers +/// instead of being returned. Together they are several kB, and when this function is not inlined +/// a by-value return leaves up to three copies of them live at once (the local, the return slot +/// and the destructured bindings). `Err(())` is ⊥; the buffers then hold a partial decode that the +/// caller must not use. pub(crate) fn sig_decode( sig: &[u8; SIG_LEN], -) -> Result<(P::SigCTilde, P::VecL, P::VecK), ()> { + c_tilde: &mut P::SigCTilde, + z: &mut P::VecL, + h: &mut P::VecK, +) -> Result<(), ()> { debug_assert_eq!(SIG_LEN, P::SIG_LEN); - let mut c_tilde = ::ZEROED; - let mut z = P::VecL::new(); - let mut h = P::VecK::new(); + // HintBitUnpack below only ever sets bits, so 𝐡 has to start out all-zero. + for h_i in h.elems_mut().iter_mut() { + h_i.coeffs.fill(0); + } let mut pos: usize = 0; @@ -485,7 +494,7 @@ pub(crate) fn sig_decode( } } - Ok((c_tilde, z, h)) + Ok(()) } /// Algorithm 29 SampleInBall(𝜌) @@ -612,11 +621,20 @@ pub(crate) fn rej_bounded_poly(rho: &[u8; 64], nonce: &[u8; 2]) h.absorb(rho).expect("absorb before squeeze is infallible"); h.absorb(nonce).expect("absorb before squeeze is infallible"); - // size doesn't really matter - // 312 seemed to be the sweet spot from playing with benchmarks - // maybe something to do with the average rejection rate? - // Also, 312 is a multiple of 8 (efficient for SHAKE) - let mut z_arr = [0u8; 312]; + // Deviation from FIPS 204, Algorithm 31 step 5, which squeezes one byte per loop iteration: + // H is SHAKE256, which produces a whole 136-byte block per Keccak permutation, so squeezing a + // byte at a time wastes most of each block. The squeeze is buffered instead, and the refill + // below makes the byte stream — and therefore the output — identical to the spec's. + // + // 272 is exactly two SHAKE256 blocks (2 × 136), so filling the buffer costs two permutations + // with nothing stranded in the sponge's output queue, and it covers the whole polynomial in a + // single squeeze almost always. The worst case is η = 4 (ML-DSA-65): 228 bytes needed on average, + // with a standard deviation out to 276 bytes, so the refill runs for roughly 1 seed in 100,000. + // For η = 2 (ML-DSA-44/87) it is 137 bytes on average and never exceeded 150. + // + // This is a buffer, not the iteration cap of FIPS 204 Table 3 (481 bytes for RejBoundedPoly): + // the loop refills rather than giving up, so no cap is imposed. + let mut z_arr = [0u8; 272]; h.squeeze_out(&mut z_arr); let mut idx: usize = 0; @@ -648,16 +666,16 @@ pub(crate) fn rej_bounded_poly(rho: &[u8; 64], nonce: &[u8; 2]) /// in other words: derives the public matrix from the public seed. /// Input: A seed 𝜌 ∈ 𝔹32 .̂ /// Output: Matrix  ∈ (𝑇𝑞)𝑘×ℓ . -pub(crate) fn expandA(rho: &[u8; 32]) -> P::MatrixA { - let mut A_hat = P::MatrixA::new(); - +/// +/// Deviation from the FIPS: 𝐀_hat is written into the caller's matrix instead of being returned. +/// Every element is overwritten, so `A_hat` need not be zeroed. A by-value return keeps a second +/// full copy of the matrix (up to 56 kB) alive in this frame while the caller's copy is filled. +pub(crate) fn expandA(rho: &[u8; 32], A_hat: &mut P::MatrixA) { for r in 0..P::k { for s in 0..P::l { A_hat.set_elem(r, s, rej_ntt_poly(rho, &[s as u8, r as u8])); } } - - A_hat } /// Algorithm 33 ExpandS(𝜌) diff --git a/crypto/mldsa/src/hash_mldsa.rs b/crypto/mldsa/src/hash_mldsa.rs index 35747605..481bd791 100644 --- a/crypto/mldsa/src/hash_mldsa.rs +++ b/crypto/mldsa/src/hash_mldsa.rs @@ -462,7 +462,9 @@ impl< let mut ph_m = [0u8; PH_LEN]; _ = ::default().hash_out(msg, &mut ph_m); - Self::verify_ph_internal(&pk.pk, Some(&pk.A_hat()), &ph_m, ctx, sig) + // Borrow the matrix the expanded key already holds rather than calling `pk.A_hat()`, + // which returns a clone. The optimizer elides that clone today, but nothing guarantees it. + Self::verify_ph_internal(&pk.pk, Some(&pk.A_hat), &ph_m, ctx, sig) } fn verify_ph_internal( @@ -514,10 +516,7 @@ impl< sig_sized, ), None => MLDSA::::verify_mu( - pk, - Some(&pk.A_hat()), - &mu, - sig_sized, + pk, None, &mu, sig_sized, ), } } diff --git a/crypto/mldsa/src/lib.rs b/crypto/mldsa/src/lib.rs index 15d0cb01..ce405d0d 100644 --- a/crypto/mldsa/src/lib.rs +++ b/crypto/mldsa/src/lib.rs @@ -71,12 +71,12 @@ //! //! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory | //! |------------|-----------------|-------------------|-----------------|-------------------| -//! | ML-DSA-44 | 1312 | 1312 (4128) | 2560 | 12464 | -//! | ML-DSA-65 | 1952 | 1952 (6176) | 4032 | 17584 | -//! | ML-DSA-87 | 2592 | 2592 (8224) | 4896 | 23728 | +//! | ML-DSA-44 | 1312 | 4128 | 2560 | 12464 | +//! | ML-DSA-65 | 1952 | 6176 | 4032 | 17584 | +//! | ML-DSA-87 | 2592 | 8224 | 4896 | 23728 | //! //! The following table lists the size of the on-disk bytes encoding and the in-memory struct size of the -//! expanded key objects that pre-expand the public matrix A for faster repeated verify() operations: +//! expanded key objects that pre-expand the public matrix A for faster repeated sign() and verify() operations: //! //! | Key Object | PK size on disk | PK size in memory | SK Size on disk | SK size in memory | //! |---------------------|-----------------|-------------------|-----------------|-------------------| @@ -85,8 +85,14 @@ //! | ML-DSA-87_expanded | 2592 | 65568 | 4896 | 81072 | //! //! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`. -//! Values in parentheses are the usual sizes in our un-optimized implementation in the \[bouncycastle_mldsa] crate. //! +//! The (rough) performance gains for the pre-expanded key mode are (keygen not included because it does not change): +//! +//! | Key Object | sign() | verify() | +//! |---------------------|----------|----------| +//! | ML-DSA-44_expanded | x1.13 | x1.9 | +//! | ML-DSA-65_expanded | x1.2 | x2.2 | +//! | ML-DSA-87_expanded | x1.28 | x2.5 | //! //! # 🚨 Security 🚨 //! diff --git a/crypto/mldsa/src/matrix.rs b/crypto/mldsa/src/matrix.rs index e08bb62f..677fa275 100644 --- a/crypto/mldsa/src/matrix.rs +++ b/crypto/mldsa/src/matrix.rs @@ -37,8 +37,11 @@ pub struct Matrix { } impl Matrix { - pub(crate) fn new() -> Self { - Self { elems: [[(); l]; k].map(|_| [(); l].map(|_| Polynomial::new())) } + /// A repeat expression, not `array::map`: `map` builds the whole matrix in a temporary and + /// copies it into place, which for ML-DSA-87 is a 56 kB copy that the optimizer does not + /// always elide. + pub(crate) const fn new() -> Self { + Self { elems: [[Polynomial::new(); l]; k] } } /// Algorithm 48 MatrixVectorNTT(𝐌, 𝐯) @@ -107,8 +110,8 @@ pub trait VectorTrait: /// Computes the sum 𝐯_hat + 𝐰_hat of two vectors 𝐯_hat, 𝐰_hat over 𝑇𝑞. fn add_vector_ntt(&mut self, s: &Self); - /// Subtracts another vector from this one, coordinatewise. - fn sub_vector(&self, s: &Self) -> Self; + /// Subtracts another vector from this one, coordinatewise, in place. + fn sub_vector(&mut self, s: &Self); /// Algorithm 47 ScalarVectorNTT(𝑐,̂ 𝐯)̂ /// Computes the product 𝑐_hat * 𝐯_hat of a scalar 𝑐_hat and a vector 𝐯_hat over 𝑇𝑞. @@ -129,8 +132,8 @@ pub trait VectorTrait: /// Applies Algorithm 37 HighBits(𝑟) coefficientwise. fn high_bits(&self) -> Self; - /// Applies Algorithm 38 LowBits(𝑟) coefficientwise. - fn low_bits(&self) -> Self; + /// Applies Algorithm 38 LowBits(𝑟) coefficientwise, in place. + fn low_bits(&mut self); /// Multiplies every coefficient by 2^𝑑. fn shift_left_d(&self) -> Self; @@ -205,12 +208,10 @@ impl VectorTrait for Vector { } } - fn sub_vector(&self, s: &Self) -> Self { - let mut out = *self; + fn sub_vector(&mut self, s: &Self) { for i in 0..LEN { - out[i].sub(&s[i]); + self[i].sub(&s[i]); } - out } /// Algorithm 47 ScalarVectorNTT(𝑐,̂ 𝐯)̂ @@ -260,14 +261,10 @@ impl VectorTrait for Vector { s } - fn low_bits(&self) -> Self { - let mut s = Vector::::new(); - + fn low_bits(&mut self) { for i in 0..LEN { - s[i] = self[i].low_bits::

(); + self[i] = self[i].low_bits::

(); } - - s } fn shift_left_d(&self) -> Self { diff --git a/crypto/mldsa/src/mldsa.rs b/crypto/mldsa/src/mldsa.rs index 9e003579..b26bc1cf 100644 --- a/crypto/mldsa/src/mldsa.rs +++ b/crypto/mldsa/src/mldsa.rs @@ -711,7 +711,8 @@ impl< let t_hat = { // scope for s1_hat // 3: 𝐀_hat ← ExpandA(𝜌) ▷ 𝐀 is generated and stored in NTT representation as 𝐀 - let A_hat = expandA::

(&rho); + let mut A_hat = P::MatrixA::new(); + expandA::

(&rho, &mut A_hat); // 5: 𝐭 ← NTT−1(𝐀 ∘ NTT(𝐬1)) + 𝐬2 // ▷ compute 𝐭 = 𝐀𝐬1 + 𝐬2 @@ -880,7 +881,11 @@ impl< cs2.inv_ntt(); // 21: 𝐫0 ← LowBits(𝐰 − ⟨⟨𝑐𝐬2⟩⟩) - let mut r0 = w.sub_vector(&cs2).low_bits::

(); + // 𝐰 is not read again below -- 𝐰1 was taken from it above -- so the subtraction and the + // LowBits both run in its buffer. + let mut r0 = w; + r0.sub_vector(&cs2); + r0.low_bits::

(); // 23 (second half): if ||𝐳||∞ ≥ 𝛾1 − 𝛽 or ||𝐫0||∞ ≥ 𝛾2 − 𝛽 then (z, h) ← ⊥ // ▷ validity checks @@ -959,7 +964,10 @@ impl< // 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) + let mut c_tilde = ::ZEROED; + let mut z = P::VecL::new(); + let mut h = P::VecK::new(); + sig_decode::(sig, &mut c_tilde, &mut z, &mut h) .map_err(|_| SignatureError::SignatureVerificationFailed)?; // 13 (first half) return [[ ||𝐳||∞ < 𝛾1 − 𝛽]] @@ -1006,7 +1014,8 @@ impl< t1_shift_hat.ntt(); t1_shift_hat.scalar_vector_ntt(&c_hat) }; - let mut wp_approx = Az.sub_vector(&ct1); + let mut wp_approx = Az; + wp_approx.sub_vector(&ct1); wp_approx.inv_ntt(); wp_approx.conditional_add_q(); @@ -1180,7 +1189,11 @@ impl< 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), + None => { + let mut A_hat = P::MatrixA::new(); + sk.expand_A_hat_into(&mut A_hat); + Self::sign_internal(sk, &A_hat, mu, rnd, output) + } } } fn sign_mu_deterministic_from_seed( @@ -1284,7 +1297,8 @@ impl< // as 20 or even 80 times. So moving expandA() inside the loop would be a pretty drastic speed-for-memory tradeoff // whose generality falls out of the scope of this implementation. // It is left as an optimization that can be made by users that require further reduction of memory usage - let A_hat = expandA::

(&rho); + let mut A_hat = P::MatrixA::new(); + expandA::

(&rho, &mut A_hat); // Alg 7; 8: 𝜅 ← 0 // ▷ initialize counter 𝜅 @@ -1398,7 +1412,10 @@ impl< cs2.inv_ntt(); // 21: 𝐫0 ← LowBits(𝐰 − ⟨⟨𝑐𝐬2⟩⟩) - let r0 = w.sub_vector(&cs2).low_bits::

(); + // 𝐰 is not read again below; see the note at the matching step in sign_internal. + let mut r0 = w; + r0.sub_vector(&cs2); + r0.low_bits::

(); // while s2_hat is in scope, derive t0 let mut t = t_hat; @@ -1508,7 +1525,9 @@ impl< 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) + // Borrow the matrix the expanded key already holds rather than calling `pk.A_hat()`, + // which returns a clone. The optimizer elides that clone today, but nothing guarantees it. + Self::verify_mu(&pk.pk, Some(&pk.A_hat), &mu, sig) } fn verify_mu( @@ -1519,7 +1538,11 @@ impl< ) -> 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), + None => { + let mut A_hat = P::MatrixA::new(); + pk.expand_A_hat_into(&mut A_hat); + Self::verify_internal(pk, &A_hat, mu, sig) + } } } } @@ -1883,7 +1906,7 @@ impl< 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) + Self::verify_mu(pk, None, &mu, sig) } fn verify_init(pk: &PK, ctx: Option<&[u8]>) -> Result { @@ -1911,7 +1934,7 @@ impl< 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) + Self::verify_mu(pk, None, &mu, sig) } } diff --git a/crypto/mldsa/src/mldsa_keys.rs b/crypto/mldsa/src/mldsa_keys.rs index 5d4dee7d..221e04b3 100644 --- a/crypto/mldsa/src/mldsa_keys.rs +++ b/crypto/mldsa/src/mldsa_keys.rs @@ -147,6 +147,10 @@ pub(crate) trait MLDSAPublicKeyInternalTrait &P::VecK; + + /// Algorithm 32 ExpandA(𝜌), written into `out`. Prefer this over [`MLDSAPublicKeyTrait::A_hat`] + /// internally: it fills the caller's matrix in place instead of returning a copy. + fn expand_A_hat_into(&self, out: &mut P::MatrixA); } impl MLDSAPublicKeyTrait @@ -174,7 +178,9 @@ impl MLDSAPublicKeyTrait } fn A_hat(&self) -> P::MatrixA { - expandA::

(&self.rho) + let mut A_hat = P::MatrixA::new(); + expandA::

(&self.rho, &mut A_hat); + A_hat } fn compute_tr(&self) -> [u8; 64] { @@ -195,6 +201,10 @@ impl MLDSAPublicKeyInternalTrait fn t1(&self) -> &P::VecK { &self.t1 } + + fn expand_A_hat_into(&self, out: &mut P::MatrixA) { + expandA::

(&self.rho, out); + } } impl SignaturePublicKey for MLDSAPublicKey { @@ -218,8 +228,8 @@ impl SignaturePublicKey for MLDSAPu "Provided key bytes are the incorrect length", )); } - let bytes_sized: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); - Ok(>::pk_decode(&bytes_sized)) + let bytes_sized: &[u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); + Ok(>::pk_decode(bytes_sized)) } } @@ -298,8 +308,8 @@ impl< "Provided key bytes are the incorrect length", )); } - let bytes_sized: [u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); - Ok(>::pk_decode(&bytes_sized)) + let bytes_sized: &[u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); + Ok(>::pk_decode(bytes_sized)) } } @@ -363,9 +373,10 @@ impl< /// 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(pk: &PK) -> Self { - let A_hat = pk.A_hat(); - - Self { pk: pk.clone(), A_hat } + // Build the struct first and expand straight into its matrix, so only one copy exists. + let mut expanded = Self { pk: pk.clone(), A_hat: P::MatrixA::new() }; + expanded.pk.expand_A_hat_into(&mut expanded.A_hat); + expanded } } @@ -376,9 +387,9 @@ impl< > MLDSAPublicKeyTrait for MLDSAPublicKeyExpanded { fn pk_decode(pk: &[u8; PK_LEN]) -> Self { - let pk1 = PK::pk_decode(pk); - let A_hat = pk1.A_hat(); - Self { pk: pk1, A_hat } + let mut expanded = Self { pk: PK::pk_decode(pk), A_hat: P::MatrixA::new() }; + expanded.pk.expand_A_hat_into(&mut expanded.A_hat); + expanded } fn A_hat(&self) -> P::MatrixA { @@ -542,6 +553,9 @@ pub(crate) trait MLDSAPrivateKeyInternalTrait< fn s1_hat(&self) -> &P::VecL; /// Get a ref to s2 fn s2_hat(&self) -> &P::VecK; + /// Algorithm 32 ExpandA(𝜌), written into `out`. Prefer this over [`MLDSAPrivateKeyTrait::A_hat`] + /// internally: it fills the caller's matrix in place instead of returning a copy. + fn expand_A_hat_into(&self, out: &mut P::MatrixA); /// Get a ref to t0 fn t0_hat(&self) -> &P::VecK; } @@ -561,7 +575,9 @@ impl } fn A_hat(&self) -> P::MatrixA { - expandA::

(&self.rho) + let mut A_hat = P::MatrixA::new(); + expandA::

(&self.rho, &mut A_hat); + A_hat } fn derive_pk(&self) -> MLDSAPublicKey { @@ -571,7 +587,8 @@ impl // scope for A_hat // 3: 𝐀 ← ExpandA(𝜌) // ▷ 𝐀 is generated and stored in NTT representation as 𝐀 - let A_hat = expandA::

(&self.rho); + let mut A_hat = P::MatrixA::new(); + expandA::

(&self.rho, &mut A_hat); let mut t_ntt = A_hat.matrix_vector_ntt(&self.s1_hat); t_ntt.inv_ntt(); @@ -699,6 +716,9 @@ impl fn s2_hat(&self) -> &P::VecK { &self.s2_hat } + fn expand_A_hat_into(&self, out: &mut P::MatrixA) { + expandA::

(&self.rho, out); + } fn t0_hat(&self) -> &P::VecK { &self.t0_hat @@ -728,9 +748,9 @@ impl SignaturePrivateK "Provided key bytes are the incorrect length", )); } - let bytes_sized: [u8; SK_LEN] = bytes[..SK_LEN].try_into().unwrap(); + let bytes_sized: &[u8; SK_LEN] = bytes[..SK_LEN].try_into().unwrap(); - >::sk_decode(&bytes_sized) + >::sk_decode(bytes_sized) } } @@ -880,10 +900,12 @@ impl< /// 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(sk: &SK) -> Self { - let A_hat = - as MLDSAPublicKeyTrait>::A_hat(&sk.derive_pk()); - - Self { _phantom: core::marker::PhantomData, sk: sk.clone(), A_hat } + // Expand straight from the private key's rho into the struct's matrix: one copy, and no + // detour through derive_pk(), which would expand the matrix a second time. + let mut expanded = + Self { _phantom: core::marker::PhantomData, sk: sk.clone(), A_hat: P::MatrixA::new() }; + expanded.sk.expand_A_hat_into(&mut expanded.A_hat); + expanded } } @@ -928,7 +950,7 @@ impl< } fn A_hat(&self) -> P::MatrixA { - self.sk.A_hat() + self.A_hat.clone() } fn derive_pk(&self) -> MLDSAPublicKey { @@ -936,10 +958,12 @@ impl< } fn sk_decode(sk: &[u8; SK_LEN]) -> Result { - let sk1 = SK::sk_decode(sk)?; - let A_hat = - as MLDSAPublicKeyTrait>::A_hat(&sk1.derive_pk()); - - Ok(Self { _phantom: core::marker::PhantomData, sk: sk1, A_hat }) + let mut expanded = Self { + _phantom: core::marker::PhantomData, + sk: SK::sk_decode(sk)?, + A_hat: P::MatrixA::new(), + }; + expanded.sk.expand_A_hat_into(&mut expanded.A_hat); + Ok(expanded) } } diff --git a/crypto/mlkem-lowmemory/src/aux_functions.rs b/crypto/mlkem-lowmemory/src/aux_functions.rs index 406ef47a..72c16022 100644 --- a/crypto/mlkem-lowmemory/src/aux_functions.rs +++ b/crypto/mlkem-lowmemory/src/aux_functions.rs @@ -89,12 +89,24 @@ pub(crate) fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // 3: 𝑗 ← 0 let mut j = 0usize; - // SHAKE is fairly inefficient if only 3 bytes are squeezed at a time, so a block is done instead. - // 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 likely 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; 216]; + // Deviation from FIPS 203, Algorithm 7 step 5, which squeezes 3 bytes per loop iteration: the + // XOF is SHAKE128, which produces a whole 168-byte block per Keccak permutation, so squeezing + // 3 bytes at a time wastes most of each block. The squeeze is buffered instead, and the refill + // below makes the byte stream — and therefore the output — identical to the spec's. + // + // 504 is exactly three SHAKE128 blocks (3 × 168). Three permutations is the floor for this + // function: two blocks yield 336 bytes = 224 candidate coefficients, short of the 256 needed, + // so no buffer can finish in two. 504 reaches that floor and almost always finishes in one + // squeeze — a coefficient is accepted with probability q/4096 = 3329/4096, so 473 bytes are + // needed on average and, over 400k simulated seeds, the refill below ran for 0.8% of them. + // + // 168 is itself a multiple of 3, so a whole number of blocks is automatically a whole number + // of 3-byte groups and the loop never straddles the end of the buffer. + // + // This is a buffer, not the iteration bound of FIPS 203 Appendix B Table 4 (280 iterations, + // reached with probability 2^-261): the loop refills rather than giving up, so it is unbounded + // as Appendix B recommends. + let mut C = [0u8; 504]; xof.squeeze_out(&mut C); let mut idx: usize = 0; diff --git a/crypto/mlkem-lowmemory/src/lib.rs b/crypto/mlkem-lowmemory/src/lib.rs index 7a15b31e..d2f5666a 100644 --- a/crypto/mlkem-lowmemory/src/lib.rs +++ b/crypto/mlkem-lowmemory/src/lib.rs @@ -76,39 +76,36 @@ //! //! ## Algorithm Peak Memory Usage //! -//! The table below shows peak memory usage of the ML-KEM algorithms and the rough performanc (throughput) impact. +//! The table below shows peak memory usage of the ML-KEM algorithms and the rough performance (throughput) impact. //! -//! Measuring peak application memory usage can be a bit tricky, and the numbers that are obtained depend heavily on how the -//! measurement harness is designed. Here, we aim to provide a conservative measurement, meaning that we are aiming for an -//! over-estimate so that any deployment within an existing application will use incrementally less additional memory -//! than the amount stated here. +//! | Algorithm | Peak stack memory usage (kB) | Throughput (Kops/s) | +//! |----------------------------|------------------------------|-------------------| +//! | MLKEM512_lowmemory/KeyGen | 7.7 (13.2) | 51.8 (52.7) | +//! | MLKEM768_lowmemory/KeyGen | 9.5 (19.7) | 29.9 (32.0) | +//! | MLKEM1024_lowmemory/KeyGen | 11.4 (29.2) | 18.3 (21.0) | +//! | MLKEM512_lowmemory/Encaps | 9.3 (14.4) | 39.4 (47.2) | +//! | MLKEM768_lowmemory/Encaps | 11.0 (24.3) | 23.2 (28.6) | +//! | MLKEM1024_lowmemory/Encaps | 13.3 (35.0) | 15.2 (19.6) | +//! | MLKEM512_lowmemory/Decaps | 11.9 (21.6) | 13.8 (36.8) | +//! | MLKEM768_lowmemory/Decaps | 13.6 (33.4) | 8.1 (22.8) | +//! | MLKEM1024_lowmemory/Decaps | 15.8 (49.2) | 5.2 (16.1) | //! -//! Our measurement methodology is to compile a simple standalone HelloWorld application that only calls the function under test -//! with as minimal as possible hard-coded data (such as keys or ciphertexts) and measure the peak memory usage of running -//! the compiled binary using `valgrind --tool=massif --heap=no --stack=yes`. The flags for heap and stack -//! reflect the fact that this is a `no_std` rust application and therefore the cryptographic functions use no heap memory. -//! The measurements may over-estimate by as much as 3 kb since that that's the measured peak memory usage of a do-nothing -//! HelloWorld rust application. +//! Values in parentheses are the comparison values from the un-optimized implementation in the \[bouncycastle_mlkem] crate. //! -//! | Algorithm | Peak stack memory usage (kB) | Throughput (Kops/s) | -//! |----------------------------|------------------------|-------------------| -//! | MLKEM512_lowmemory/KeyGen | 5.8 (21.7) | 50.1 (48.7) | -//! | MLKEM512_lowmemory/KeyGen | 7.3 (28.9) | 28.3 (28.8) | -//! | MLKEM1024_lowmemory/KeyGen | 9.3 (41.4) | 16.9 (18.1) | -//! | MLKEM512_lowmemory/Encaps | 8.9 (18.8) | 37.8 (43.7) | -//! | MLKEM768_lowmemory/Encaps | 9.9 (27.9) | 22.3 (26.0) | -//! | MLKEM1024_lowmemory/Encaps | 11.2 (44.2) | 14.2 (15.6) | -//! | MLKEM512_lowmemory/Decaps | 13.6 (25.7) | 13.4 (31.6) | -//! | MLKEM768_lowmemory/Decaps | 16.6 (40.6) | 7.8 (21.0) | -//! | MLKEM1024_lowmemory/Decaps | 20.5 (58.4) | 4.9 (13.6) | -//! -//! Values in parentheses are the comparison values from the un-optimized implementation in the \[bouncycastle_mldsa] crate. -//! Size numbers were collected with valgrind using a simple main program that calls only the measured function. -//! Performance throughput numbers were collected on my laptop using the library's provided benchmarks, so -//! performance they should be taken with an extreme grain of salt. +//! **Caveates** //! -//! Actual values may vary based on build configuration and target architecture. +//! Throughput numbers are meant to show relative difference between the two implementations and not be +//! absolute performance measurements. //! +//! Size numbers were collected with valgrind using a simple main program that calls only the measured function. +//! Measurements include the public key and signature buffer, which is a realistic setting, but +//! numbers would be lower if those were on the heap and excluded from the measurement. +//! Actual values may vary based on build configuration and target architecture. +//! The peak also depends on how the compiler lays out the measured function's stack frame relative +//! to its caller and whether it elides copies at the call boundary, which is outside +//! the library's control. The memory benchmarking framework included in the library attempts to +//! This effect is small for the low-memory crates (well under 1 kB) and larger for the full implementations +//! in parentheses (a few kB), so treat the latter as approximate. //! //! # Usage //! diff --git a/crypto/mlkem-lowmemory/src/mlkem.rs b/crypto/mlkem-lowmemory/src/mlkem.rs index da61c593..2674a365 100644 --- a/crypto/mlkem-lowmemory/src/mlkem.rs +++ b/crypto/mlkem-lowmemory/src/mlkem.rs @@ -201,8 +201,7 @@ impl< /// correctly-sized [`KeyMaterial512`] using [`KeyMaterialTrait::truncate`]. 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 + let pk = PK::new(sk.t_hat_packed(), *sk.rho()); Ok((pk, sk)) } @@ -330,7 +329,9 @@ impl< /// Input: decryption key dkPKE ∈ 𝔹384𝑘. /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: message 𝑚 ∈ 𝔹32 . - fn pke_decrypt(dk: &SK, ct: [u8; CT_LEN]) -> [u8; 32] { + /// The ciphertext is borrowed rather than taken by value: at ML-KEM-1024 it is 1568 bytes, and + /// an owned parameter would put a second copy of it on the stack alongside the caller's. + fn pke_decrypt(dk: &SK, ct: &[u8; CT_LEN]) -> [u8; 32] { // 1: 𝑐1 ← 𝑐[0 ∶ 32𝑑𝑢𝑘] // 3: 𝐮′ ← Decompress_𝑑𝑢(ByteDecode_𝑑𝑢(𝑐1)) @@ -344,7 +345,7 @@ impl< let mut v1 = { let mut s_hat_i = dk.compute_s_hat_row(0); { - let mut u_prime_i = unpack_ciphertext_u_row::(0, &ct); + let mut u_prime_i = unpack_ciphertext_u_row::(0, ct); u_prime_i.ntt(); s_hat_i.base_mult_montgomery(&u_prime_i); } @@ -356,7 +357,7 @@ impl< for i in 1..P::k { let mut s_hat_i = dk.compute_s_hat_row(i); { - let mut u_prime_i = unpack_ciphertext_u_row::(i, &ct); + let mut u_prime_i = unpack_ciphertext_u_row::(i, ct); u_prime_i.ntt(); s_hat_i.base_mult_montgomery(&u_prime_i); } @@ -372,7 +373,7 @@ impl< let w = { // second half of // 6: 𝑤 ← 𝑣′ − NTT−1(𝐬_hat^T ∘ NTT(𝐮′)) - let mut v_prime = unpack_ciphertext_v::(&ct); + let mut v_prime = unpack_ciphertext_v::(ct); v_prime.sub(&v1); v_prime.poly_reduce(); @@ -390,7 +391,8 @@ impl< /// Input: decapsulation key dk ∈ 𝔹768𝑘+96 . /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: shared secret key 𝐾 ∈ 𝔹32 . - fn decaps_internal(dk: &SK, c: [u8; CT_LEN]) -> [u8; MLKEM_SS_LEN] { + /// The ciphertext is borrowed rather than taken by value; see the note on [`Self::pke_decrypt`]. + 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. @@ -402,7 +404,7 @@ impl< // Nothing to do since dk is already decoded. // 5: 𝑚′ ← K-PKE.Decrypt(dkPKE, 𝑐) - let m_prime = Self::pke_decrypt(&dk, c); + let m_prime = Self::pke_decrypt(dk, c); // Compute the trial shared secret key // 6: (𝐾′, 𝑟′) ← G(𝑚′‖ℎ)̄ @@ -432,7 +434,7 @@ impl< let mut K_bar: Secret<[u8; MLKEM_SS_LEN]> = Secret::new(); let mut j = J::new(); j.absorb(dk.z()).expect("absorb before squeeze is infallible"); - j.absorb(&c).expect("absorb before squeeze is infallible"); + j.absorb(c).expect("absorb before squeeze is infallible"); let bytes_written = j.squeeze_out(&mut *K_bar); debug_assert_eq!(bytes_written, MLKEM_SS_LEN); @@ -447,7 +449,7 @@ impl< // 10: 𝐾′ ← 𝐾_bar // ▷ if ciphertexts do not match, “implicitly reject" let mut K_out = [0u8; MLKEM_SS_LEN]; - conditional_copy_bytes(&K_prime, &K_bar, &mut K_out, ct_eq_bytes(&c, &c_prime)); + conditional_copy_bytes(&K_prime, &K_bar, &mut K_out, ct_eq_bytes(c, &c_prime)); K_out } @@ -636,7 +638,11 @@ impl< return Err(KEMError::LengthError("Invalid ciphertext length")); } - let ss_bytes = Self::decaps_internal(sk, ct.try_into().unwrap()); + // The length was checked above, so the conversion to a fixed-size reference cannot fail. + // Converting to `&[u8; CT_LEN]` rather than `[u8; CT_LEN]` borrows the caller's + // ciphertext instead of copying it onto this stack frame. + let ct: &[u8; CT_LEN] = ct.try_into().unwrap(); + let ss_bytes = Self::decaps_internal(sk, ct); let mut ss_keymaterial = KeyMaterial::::from_bytes_as_type(&ss_bytes, KeyType::CryptographicRandom)?; diff --git a/crypto/mlkem/src/aux_functions.rs b/crypto/mlkem/src/aux_functions.rs index 3dbb8683..4125c32e 100644 --- a/crypto/mlkem/src/aux_functions.rs +++ b/crypto/mlkem/src/aux_functions.rs @@ -98,12 +98,24 @@ pub fn sample_ntt(rho: &[u8; 32], nonce: &[u8; 2]) -> Polynomial { // 3: 𝑗 ← 0 let mut j = 0usize; - // SHAKE is fairly inefficient if you just squeeze 3 bytes at a time, therefore a block is squeezed here. - // Size is not an important factor, so long as it's a multiple of 3. - // 288 seemed to be the sweet spot according to the benchmarks - // 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; 216]; + // Deviation from FIPS 203, Algorithm 7 step 5, which squeezes 3 bytes per loop iteration: the + // XOF is SHAKE128, which produces a whole 168-byte block per Keccak permutation, so squeezing + // 3 bytes at a time wastes most of each block. The squeeze is buffered instead, and the refill + // below makes the byte stream — and therefore the output — identical to the spec's. + // + // 504 is exactly three SHAKE128 blocks (3 × 168). Three permutations is the floor for this + // function: two blocks yield 336 bytes = 224 candidate coefficients, short of the 256 needed, + // so no buffer can finish in two. 504 reaches that floor and almost always finishes in one + // squeeze — a coefficient is accepted with probability q/4096 = 3329/4096, so 473 bytes are + // needed on average and, over 400k simulated seeds, the refill below ran for 0.8% of them. + // + // 168 is itself a multiple of 3, so a whole number of blocks is automatically a whole number + // of 3-byte groups and the loop never straddles the end of the buffer. + // + // This is a buffer, not the iteration bound of FIPS 203 Appendix B Table 4 (280 iterations, + // reached with probability 2^-261): the loop refills rather than giving up, so it is unbounded + // as Appendix B recommends. + let mut C = [0u8; 504]; xof.squeeze_out(&mut C); let mut idx: usize = 0; diff --git a/crypto/mlkem/src/lib.rs b/crypto/mlkem/src/lib.rs index 5c48828b..652f3d5c 100644 --- a/crypto/mlkem/src/lib.rs +++ b/crypto/mlkem/src/lib.rs @@ -111,10 +111,17 @@ //! |----------------------|-----------------|-------------------|-----------------|-------------------| //! | ML-KEM-512_expanded | 800 | 3104 | 1632 | 4226 | //! | ML-KEM-768_expanded | 1184 | 6176 | 2400 | 7810 | -//! | ML-KEM-1024_expanded | 1568 | 10272 | 3168 | 12418 | +//! | ML-KEM-1024_expanded | 1568 | 10272 | 3168 | 12418 | //! //! All values are in bytes. The "in memory" sizes are measured by rust's `std::mem::size_of`. -//! Values in parentheses are the usual sizes in the un-optimized implementation in the \[bouncycastle_mldsa] crate. +//! +//! The (rough) performance gains for the pre-expanded key mode are (keygen not included because it does not change): +//! +//! | Key Object | encaps() | decaps() | +//! |----------------------|----------|-----------| +//! | ML-KEM-512_expanded | x1.3 | x1.2 | +//! | ML-KEM-768_expanded | x1.5 | x1.4 | +//! | ML-KEM-1024_expanded | x1.6 | x1.5 | //! //! # 🚨 Security 🚨 //! diff --git a/crypto/mlkem/src/mlkem.rs b/crypto/mlkem/src/mlkem.rs index 6490a521..62567887 100644 --- a/crypto/mlkem/src/mlkem.rs +++ b/crypto/mlkem/src/mlkem.rs @@ -561,13 +561,15 @@ impl< /// Input: decryption key dkPKE ∈ 𝔹384𝑘. /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: message 𝑚 ∈ 𝔹32 . - fn pke_decrypt(dk: &SK, ct: [u8; CT_LEN]) -> [u8; 32] { + /// The ciphertext is borrowed rather than taken by value: at ML-KEM-1024 it is 1568 bytes, and + /// an owned parameter would put a second copy of it on the stack alongside the caller's. + fn pke_decrypt(dk: &SK, ct: &[u8; CT_LEN]) -> [u8; 32] { // 1: 𝑐1 ← 𝑐[0 ∶ 32𝑑𝑢𝑘] // 2: 𝑐2 ← 𝑐[32𝑑𝑢𝑘 ∶ 32(𝑑𝑢𝑘 + 𝑑𝑣)] // 3: 𝐮′ ← Decompress_𝑑𝑢(ByteDecode_𝑑𝑢(𝑐1)) // 4: 𝑣′ ← Decompress_𝑑𝑣(ByteDecode_𝑑𝑣(𝑐2)) let v1 = { - let mut u_prime = unpack_ciphertext_u::(&ct); + let mut u_prime = unpack_ciphertext_u::(ct); // 5: 𝐬_hat ← ByteDecode12(dkPKE) // Unnecessary here because dk is already decoded @@ -581,7 +583,7 @@ impl< }; let w = { - let mut v_prime = unpack_ciphertext_v::(&ct); + let mut v_prime = unpack_ciphertext_v::(ct); v_prime.sub(&v1); v_prime.poly_reduce(); @@ -598,7 +600,12 @@ impl< /// Input: decapsulation key dk ∈ 𝔹768𝑘+96 . /// Input: ciphertext 𝑐 ∈ 𝔹32(𝑑𝑢𝑘+𝑑𝑣). /// Output: shared secret key 𝐾 ∈ 𝔹32 . - fn decaps_internal(dk: &SK, A_hat: Option<&P::MatrixA>, c: [u8; CT_LEN]) -> [u8; MLKEM_SS_LEN] { + /// The ciphertext is borrowed rather than taken by value; see the note on [`Self::pke_decrypt`]. + fn decaps_internal( + dk: &SK, + A_hat: Option<&P::MatrixA>, + c: &[u8; CT_LEN], + ) -> [u8; MLKEM_SS_LEN] { // Structured to mirror the FIPS as closely as possible, with unnamed scopes // used to limit the number of live stack variables at any given time. @@ -609,7 +616,7 @@ impl< // Nothing to do since dk is already decoded. // 5: 𝑚′ ← K-PKE.Decrypt(dkPKE, 𝑐) - let m_prime = Self::pke_decrypt(&dk, c); + let m_prime = Self::pke_decrypt(dk, c); // Compute the trial shared secret key // 6: (𝐾′, 𝑟′) ← G(𝑚′‖ℎ)̄ @@ -636,7 +643,7 @@ impl< K_bar = { let mut j = J::new(); j.absorb(dk.z().as_ref()).expect("absorb before squeeze is infallible"); - j.absorb(&c).expect("absorb before squeeze is infallible"); + j.absorb(c).expect("absorb before squeeze is infallible"); let mut buf = [0u8; MLKEM_SS_LEN]; let bytes_written = j.squeeze_out(&mut buf); debug_assert_eq!(bytes_written, MLKEM_SS_LEN); @@ -658,7 +665,7 @@ impl< // 10: 𝐾′ ← 𝐾_bar // ▷ if ciphertexts do not match, “implicitly reject" let mut K_out = [0u8; MLKEM_SS_LEN]; - conditional_copy_bytes(&K_prime, &K_bar, &mut K_out, ct_eq_bytes(&c, &c_prime)); + conditional_copy_bytes(&K_prime, &K_bar, &mut K_out, ct_eq_bytes(c, &c_prime)); K_out } @@ -778,7 +785,11 @@ impl< // 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()); + // The length was checked above, so the conversion to a fixed-size reference cannot fail. + // Converting to `&[u8; CT_LEN]` rather than `[u8; CT_LEN]` borrows the caller's + // ciphertext instead of copying it onto this stack frame. + let ct: &[u8; CT_LEN] = ct.try_into().unwrap(); + let K = Self::decaps_internal(&sk.dk, Some(&sk.A_hat), ct); let mut key = KeyMaterial::::from_bytes_as_type(&K, KeyType::CryptographicRandom)?; do_hazardous_operations(&mut key, |key| { diff --git a/crypto/mlkem/src/mlkem_keys.rs b/crypto/mlkem/src/mlkem_keys.rs index 2df7f861..5c274e83 100644 --- a/crypto/mlkem/src/mlkem_keys.rs +++ b/crypto/mlkem/src/mlkem_keys.rs @@ -206,8 +206,8 @@ impl KEMPublicKey for MLKEMPublicKe 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) + let bytes_sized: &[u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); + Self::pk_decode(bytes_sized) } } @@ -289,8 +289,8 @@ impl, const PK_LEN: u 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) + let bytes_sized: &[u8; PK_LEN] = bytes[..PK_LEN].try_into().unwrap(); + Self::pk_decode(bytes_sized) } } @@ -298,7 +298,7 @@ impl, const PK_LEN: u for MLKEMPublicKeyExpanded { fn eq(&self, other: &Self) -> bool { - self.encode() == other.encode() + bouncycastle_utils::ct::ct_eq_bytes(&self.encode(), &other.encode()) } } @@ -616,12 +616,9 @@ impl< 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(); + let bytes_sized: &[u8; SK_LEN] = bytes[..SK_LEN].try_into().unwrap(); - Self::sk_decode(&bytes_sized) + Self::sk_decode(bytes_sized) } } diff --git a/mem_usage_benches/bench_mldsa_mem_usage.rs b/mem_usage_benches/bench_mldsa_mem_usage.rs index a57414e2..2d644785 100644 --- a/mem_usage_benches/bench_mldsa_mem_usage.rs +++ b/mem_usage_benches/bench_mldsa_mem_usage.rs @@ -20,6 +20,18 @@ #![allow(dead_code)] #![allow(unused_imports)] +/// Runs the operation under test in its own stack frame, with the key already loaded by the +/// caller. +/// +/// `#[inline(never)]` matters here too: the bench functions inline the whole operation, so +/// without this boundary the operation's frame would be allocated on entry to the bench and the +/// key-load helper's frame would stack on top of it, reporting their sum instead of their max. +/// The closure returns nothing, so no result is copied back across the boundary. +#[inline(never)] +fn measure(f: impl FnOnce()) { + f() +} + use bouncycastle::core::key_material::{KeyMaterial256, KeyType}; use bouncycastle::core::traits::{SignaturePrivateKey, SignaturePublicKey, SignatureVerifier}; use bouncycastle::hex; @@ -69,7 +81,7 @@ fn print_struct_sizes() { println!("\n\nlowmemory"); - println!("\nML-KEM-512_lowmemory"); + println!("\nML-DSA-44_lowmemory"); println!( "size_of: {}", size_of::() @@ -79,7 +91,7 @@ fn print_struct_sizes() { size_of::() ); - println!("\nML-KEM-768_lowmemory"); + println!("\nML-DSA-65_lowmemory"); println!( "size_of: {}", size_of::() @@ -89,7 +101,7 @@ fn print_struct_sizes() { size_of::() ); - println!("\nML-KEM-1024_lowmemory"); + println!("\nML-DSA-87_lowmemory"); println!( "size_of: {}", size_of::() @@ -221,21 +233,17 @@ fn bench_mldsa87_lowmemory_keygen() { println!("{:x?}", pk.encode()); } -fn bench_mldsa44_sign() { - use bouncycastle::mldsa::{MLDSA44, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSATrait}; - - eprintln!("MLDSA44/Sign"); - - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ - // 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], - // KeyType::Seed, - // ).unwrap(); - // let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); - // use bouncycastle_hex as hex; - // eprintln!("sk:\n{}", &hex::encode(sk.encode())); +/// Loads the private key for the matching sign bench. +/// +/// `#[inline(never)]` matters: the encoded key bytes and the temporaries of decoding then live in +/// this frame, which is popped before the measured operation runs, so the bench's peak is +/// max(key load, sign) rather than their sum. Inlining this into the bench would leave them +/// on the stack underneath it and inflate the measurement by several kB. +#[inline(never)] +fn load_mldsa44_sk() -> bouncycastle::mldsa::MLDSA44PrivateKey { + use bouncycastle::mldsa::MLDSA44PrivateKey; - let sk = MLDSA44PrivateKey::from_bytes(&[ + 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, @@ -408,19 +416,49 @@ fn bench_mldsa44_sign() { 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(); + .unwrap() +} + +fn bench_mldsa44_sign() { + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SK_LEN, MLDSATrait}; + + eprintln!("MLDSA44/Sign"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + // 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], + // KeyType::Seed, + // ).unwrap(); + // let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + + let sk = load_mldsa44_sk(); let msg = b"The quick brown fox jumped over the lazy dog"; - let mu = MLDSA44::compute_mu_from_sk(&sk, msg, None).unwrap(); - let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); - print!("{:x?}", sig); + measure(|| { + let mu = MLDSA44::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA44::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); } -fn bench_mldsa44_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SK_LEN, MLDSA44PrivateKey, MLDSATrait}; +/// Same as bench_mldsa44_sign(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and handed to the signature, which is what makes repeated +/// signatures with the same key cheaper -- at the cost of the memory A_hat occupies for as long +/// as it is held. +/// +/// Only the signature is measured, not the expansion: building an MLDSA44PrivateKeyExpanded +/// inside this function would put its own peak (which exceeds a whole signature) into the +/// measurement, the way a keygen would. This calls the same entry point as bench_mldsa44_sign(), +/// differing only in passing Some(&a_hat) instead of None, so the two are comparable. +/// +fn bench_mldsa44_sign_expanded_sk() { + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SK_LEN, MLDSAPrivateKeyTrait, MLDSATrait}; - eprintln!("MLDSA44_lowmemory/Sign"); + eprintln!("MLDSA44/Sign_with_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // let seed = KeyMaterial256::from_bytes_as_type( @@ -431,36 +469,64 @@ fn bench_mldsa44_lowmemory_sign() { // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA44PrivateKey::from_bytes(&[ + let sk = load_mldsa44_sk(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + measure(|| { + let a_hat = sk.A_hat(); + let mu = MLDSA44::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA44::sign_mu_deterministic(&sk, Some(&a_hat), &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); +} + +/// Loads the private key for the matching sign bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa44_lowmemory_sk() -> bouncycastle::mldsa_lowmemory::MLDSA44PrivateKey { + use bouncycastle::mldsa_lowmemory::MLDSA44PrivateKey; + + 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"; - - let mu = MLDSA44::compute_mu_from_sk(&sk, msg, None).unwrap(); - let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); - print!("{:x?}", sig); + .unwrap() } -fn bench_mldsa65_sign() { - use bouncycastle::mldsa::{MLDSA65, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSATrait}; +fn bench_mldsa44_lowmemory_sign() { + use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SK_LEN, MLDSATrait}; - eprintln!("MLDSA65/Sign"); + eprintln!("MLDSA44_lowmemory/Sign"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // 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], // KeyType::Seed, // ).unwrap(); - // - // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // let (_pk, sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA65PrivateKey::from_bytes(&[ + let sk = load_mldsa44_lowmemory_sk(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + measure(|| { + let mu = MLDSA44::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA44::sign_mu_deterministic(&sk, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); +} + +/// Loads the private key for the matching sign bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa65_sk() -> bouncycastle::mldsa::MLDSA65PrivateKey { + use bouncycastle::mldsa::MLDSA65PrivateKey; + + 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, @@ -731,61 +797,119 @@ fn bench_mldsa65_sign() { 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(); + .unwrap() +} + +fn bench_mldsa65_sign() { + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SK_LEN, MLDSATrait}; + + eprintln!("MLDSA65/Sign"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + // 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], + // KeyType::Seed, + // ).unwrap(); + // + // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + + let sk = load_mldsa65_sk(); let msg = b"The quick brown fox jumped over the lazy dog"; - let mu = MLDSA65::compute_mu_from_sk(&sk, msg, None).unwrap(); - let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); - print!("{:x?}", sig); + measure(|| { + let mu = MLDSA65::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA65::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); } -fn bench_mldsa65_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SK_LEN, MLDSA65PrivateKey, MLDSATrait}; +/// Same as bench_mldsa65_sign(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and handed to the signature, which is what makes repeated +/// signatures with the same key cheaper -- at the cost of the memory A_hat occupies for as long +/// as it is held. +/// +/// Only the signature is measured, not the expansion: building an MLDSA65PrivateKeyExpanded +/// inside this function would put its own peak (which exceeds a whole signature) into the +/// measurement, the way a keygen would. This calls the same entry point as bench_mldsa65_sign(), +/// differing only in passing Some(&a_hat) instead of None, so the two are comparable. +/// +fn bench_mldsa65_sign_expanded_sk() { + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SK_LEN, MLDSAPrivateKeyTrait, MLDSATrait}; - eprintln!("MLDSA65_lowmemory/Sign"); + eprintln!("MLDSA65/Sign_with_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // 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], // KeyType::Seed, // ).unwrap(); + // // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA65PrivateKey::from_bytes(&[ + let sk = load_mldsa65_sk(); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + measure(|| { + let a_hat = sk.A_hat(); + let mu = MLDSA65::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA65::sign_mu_deterministic(&sk, Some(&a_hat), &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); +} + +/// Loads the private key for the matching sign bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa65_lowmemory_sk() -> bouncycastle::mldsa_lowmemory::MLDSA65PrivateKey { + use bouncycastle::mldsa_lowmemory::MLDSA65PrivateKey; + + 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"; - - 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); + .unwrap() } -fn bench_mldsa87_sign() { - use bouncycastle::mldsa::{MLDSA87, MLDSA87PrivateKey, MLDSATrait}; +fn bench_mldsa65_lowmemory_sign() { + use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SK_LEN, MLDSATrait}; - eprintln!("MLDSA87/Sign"); + eprintln!("MLDSA65_lowmemory/Sign"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // 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], // KeyType::Seed, // ).unwrap(); - // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + let sk = load_mldsa65_lowmemory_sk(); + 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(&[ + measure(|| { + 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); + }); +} + +/// Loads the private key for the matching sign bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa87_sk() -> bouncycastle::mldsa::MLDSA87PrivateKey { + use bouncycastle::mldsa::MLDSA87PrivateKey; + + 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, @@ -1114,63 +1238,119 @@ fn bench_mldsa87_sign() { 0xE9, 0x6F, 0xC5, 0x59, 0x65, 0x67, 0xE9, 0x43, 0x64, 0xDF, 0xAA, 0x6D, 0x9D, 0x5A, 0x6E, 0xB9, 0x9A, 0xE4, 0xDD, 0xF4, 0x24, ]) - .unwrap(); + .unwrap() +} + +fn bench_mldsa87_sign() { + use bouncycastle::mldsa::{MLDSA87, MLDSATrait}; + + eprintln!("MLDSA87/Sign"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + // 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], + // KeyType::Seed, + // ).unwrap(); + // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let mu = MLDSA87::compute_mu_from_sk(&sk, msg, None).unwrap(); - let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); - print!("{:x?}", sig); + let msg = b"The quick brown fox jumped over the lazy dog"; + + // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let sk = load_mldsa87_sk(); + + measure(|| { + let mu = MLDSA87::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA87::sign_mu_deterministic(&sk, None, &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); } -fn bench_mldsa87_lowmemory_sign() { - use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SK_LEN, MLDSA87PrivateKey, MLDSATrait}; +/// Same as bench_mldsa87_sign(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and handed to the signature, which is what makes repeated +/// signatures with the same key cheaper -- at the cost of the memory A_hat occupies for as long +/// as it is held. +/// +/// Only the signature is measured, not the expansion: building an MLDSA87PrivateKeyExpanded +/// inside this function would put its own peak (which exceeds a whole signature) into the +/// measurement, the way a keygen would. This calls the same entry point as bench_mldsa87_sign(), +/// differing only in passing Some(&a_hat) instead of None, so the two are comparable. +/// +fn bench_mldsa87_sign_expanded_sk() { + use bouncycastle::mldsa::{MLDSA87, MLDSAPrivateKeyTrait, MLDSATrait}; - eprintln!("MLDSA87_lowmemory/Sign"); + eprintln!("MLDSA87/Sign_with_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // 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], // KeyType::Seed, // ).unwrap(); - // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - let sk = MLDSA87PrivateKey::from_bytes(&[ + let msg = b"The quick brown fox jumped over the lazy dog"; + + // let (_pk, sk) = MLDSA87::keygen_from_seed(&seed).unwrap(); + let sk = load_mldsa87_sk(); + + measure(|| { + let a_hat = sk.A_hat(); + let mu = MLDSA87::compute_mu_from_sk(&sk, msg, None).unwrap(); + let sig = MLDSA87::sign_mu_deterministic(&sk, Some(&a_hat), &mu, [0u8; 32]).unwrap(); + print!("{:x?}", sig); + }); +} + +/// Loads the private key for the matching sign bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa87_lowmemory_sk() -> bouncycastle::mldsa_lowmemory::MLDSA87PrivateKey { + use bouncycastle::mldsa_lowmemory::MLDSA87PrivateKey; + + 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"; - - 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); + .unwrap() } -fn bench_mldsa44_verify() { - use bouncycastle::hex; - use bouncycastle::mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey, MLDSATrait}; - - eprintln!("MLDSA44/Verify"); +fn bench_mldsa87_lowmemory_sign() { + use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SK_LEN, MLDSATrait}; - let msg = b"The quick brown fox jumped over the lazy dog"; + eprintln!("MLDSA87_lowmemory/Sign"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // 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], // KeyType::Seed, // ).unwrap(); - // - // let (mldsa44_pk, _mldsa44_sk) = MLDSA44::keygen_from_seed(&seed).unwrap(); + // let (_pk, sk) = MLDSA65::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); - // 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 sk = load_mldsa87_lowmemory_sk(); + + let msg = b"The quick brown fox jumped over the lazy dog"; - let mldsa44_pk = MLDSA44PublicKey::from_bytes(&[ + measure(|| { + 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); + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa44_pk() -> bouncycastle::mldsa::MLDSA44PublicKey { + use bouncycastle::mldsa::MLDSA44PublicKey; + + 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, @@ -1260,7 +1440,30 @@ fn bench_mldsa44_verify() { 0x81, 0xEC, 0xE7, 0x6C, 0xC0, 0x70, 0xB5, 0x58, 0x69, 0xE4, 0xDB, 0x97, 0x84, 0xCF, 0x05, 0xC8, 0x30, 0xB3, 0x24, 0x2C, 0x83, 0x12, ]) - .unwrap(); + .unwrap() +} + +fn bench_mldsa44_verify() { + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait}; + + 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 keygen is not captured in the bench */ + // 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], + // 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 = load_mldsa44_pk(); 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, @@ -1426,17 +1629,25 @@ fn bench_mldsa44_verify() { 0x00, 0x18, 0x25, 0x36, 0x42, ]; - if MLDSA44::verify(&mldsa44_pk, msg, None, &sig).is_ok() { - println!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + measure(|| { + if MLDSA44::verify(&mldsa44_pk, msg, None, &sig).is_ok() { + println!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); } -fn bench_mldsa44_lowmemory_verify() { - use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKey, MLDSATrait}; +/// Same as bench_mldsa44_verify(), but with a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLDSA44PublicKeyExpanded, which is +/// what makes repeated verifications against the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. Both the expansion and the verification are +/// inside the measurement. +fn bench_mldsa44_verify_expanded_pk() { + use bouncycastle::mldsa::{MLDSA44, MLDSA44_SIG_LEN, MLDSA44PublicKeyExpanded, MLDSATrait}; - eprintln!("MLDSA44_lowmemory/Verify"); + eprintln!("MLDSA44/Verify_with_expanded_key"); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -1453,88 +1664,270 @@ 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, + let mldsa44_pk = load_mldsa44_pk(); + 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, + ]; + + measure(|| { + let pk_expanded = MLDSA44PublicKeyExpanded::from(&mldsa44_pk); + if MLDSA44::verify_with_expanded_key(&pk_expanded, msg, None, &sig).is_ok() { + println!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa44_lowmemory_pk() -> bouncycastle::mldsa_lowmemory::MLDSA44PublicKey { + use bouncycastle::mldsa_lowmemory::MLDSA44PublicKey; + + 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, @@ -1543,7 +1936,30 @@ fn bench_mldsa44_lowmemory_verify() { 0x81, 0xEC, 0xE7, 0x6C, 0xC0, 0x70, 0xB5, 0x58, 0x69, 0xE4, 0xDB, 0x97, 0x84, 0xCF, 0x05, 0xC8, 0x30, 0xB3, 0x24, 0x2C, 0x83, 0x12, ]) - .unwrap(); + .unwrap() +} + +fn bench_mldsa44_lowmemory_verify() { + use bouncycastle::mldsa_lowmemory::{MLDSA44, MLDSA44_SIG_LEN, MLDSATrait}; + + 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 keygen is not captured in the bench */ + // 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], + // 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 pk = load_mldsa44_lowmemory_pk(); 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, @@ -1709,16 +2125,159 @@ fn bench_mldsa44_lowmemory_verify() { 0x00, 0x18, 0x25, 0x36, 0x42, ]; - if MLDSA44::verify(&pk, msg, None, &sig).is_ok() { - println!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + measure(|| { + if MLDSA44::verify(&pk, msg, None, &sig).is_ok() { + println!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa65_pk() -> bouncycastle::mldsa::MLDSA65PublicKey { + use bouncycastle::mldsa::MLDSA65PublicKey; + + 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() } fn bench_mldsa65_verify() { - use bouncycastle::hex; - use bouncycastle::mldsa::{MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey, MLDSATrait}; + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SIG_LEN, MLDSATrait}; eprintln!("MLDSA65/Verify"); @@ -1738,7 +2297,513 @@ 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(&[ + let mldsa65_pk = load_mldsa65_pk(); + // 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: [u8; MLDSA65_SIG_LEN] = [ + 0x90, 0x61, 0xF1, 0x5C, 0xBF, 0x20, 0x92, 0xF7, 0x44, 0xFB, 0xCD, 0x79, 0x9E, 0xB0, 0x24, + 0x14, 0x05, 0x3C, 0x1B, 0x0F, 0x74, 0x12, 0x12, 0x4B, 0xED, 0xC4, 0x1C, 0xF9, 0xA3, 0xDB, + 0x01, 0x66, 0x46, 0x9E, 0x87, 0x40, 0x37, 0xD7, 0xF0, 0x81, 0xE5, 0xF8, 0xD3, 0xD2, 0x03, + 0x3A, 0x03, 0x07, 0xD1, 0xC4, 0x9E, 0xD0, 0x1F, 0xE6, 0x45, 0x78, 0xC4, 0xA6, 0xFA, 0xBD, + 0x80, 0x88, 0x0C, 0xDF, 0x19, 0x11, 0x84, 0x8F, 0x18, 0x4D, 0x4B, 0xCF, 0x53, 0x6C, 0xA7, + 0x95, 0xA0, 0xFB, 0x1A, 0xA1, 0x9A, 0xB7, 0xEE, 0x3B, 0xA6, 0xB5, 0x8B, 0xD6, 0x4B, 0xBE, + 0xAC, 0x9F, 0x58, 0x65, 0x0F, 0xFF, 0x1E, 0xF5, 0xA9, 0x7A, 0xB6, 0x91, 0x6D, 0xF9, 0x62, + 0x07, 0x2E, 0x20, 0xE7, 0xC6, 0xBE, 0x96, 0x09, 0x0E, 0x3A, 0x78, 0x1A, 0x50, 0x4B, 0xC4, + 0x44, 0x2B, 0xD8, 0x88, 0x9A, 0x0A, 0xA6, 0x28, 0x90, 0x7A, 0x74, 0x29, 0x9F, 0x39, 0xFA, + 0x83, 0x60, 0x31, 0xF1, 0xBD, 0x68, 0x35, 0x5B, 0xEB, 0xE7, 0xAE, 0x93, 0xC1, 0xE3, 0x61, + 0xA9, 0xEF, 0xBE, 0xD1, 0x32, 0x5D, 0x96, 0x22, 0x70, 0x70, 0x46, 0x1F, 0xCD, 0x6F, 0x15, + 0x1B, 0x86, 0x69, 0xD9, 0x22, 0x9B, 0x97, 0x7D, 0x9E, 0xE5, 0x1F, 0xD2, 0x26, 0x0C, 0x3E, + 0x4A, 0x2E, 0x82, 0x04, 0x16, 0xF9, 0xE0, 0x74, 0x95, 0x8D, 0xC3, 0xB3, 0xE2, 0x21, 0x7E, + 0x63, 0x12, 0xB7, 0xE0, 0xB5, 0x82, 0xA0, 0x48, 0x98, 0x1C, 0xF6, 0x57, 0x9F, 0x4B, 0xC7, + 0x71, 0x5B, 0x78, 0xC8, 0x08, 0xE4, 0xC5, 0x7E, 0x3B, 0x8A, 0xA3, 0x8B, 0x05, 0xC0, 0x4F, + 0xCE, 0xDF, 0x20, 0x9F, 0x52, 0xC1, 0xE3, 0x31, 0xAE, 0x83, 0xDB, 0xDF, 0xF6, 0x0B, 0xA4, + 0x50, 0xA1, 0x7C, 0xC3, 0x97, 0x56, 0x8E, 0x54, 0xBC, 0x3F, 0x16, 0xDD, 0xF3, 0x0B, 0x92, + 0x74, 0x7C, 0xE4, 0x60, 0xD9, 0x25, 0xB9, 0xBE, 0x20, 0xA1, 0xD3, 0x5E, 0x2A, 0xED, 0x97, + 0xF1, 0x24, 0xAF, 0x26, 0x16, 0xA5, 0x36, 0x1D, 0xF2, 0x8B, 0xA3, 0x0E, 0x52, 0x2D, 0xD0, + 0x8F, 0xA0, 0x0F, 0xD2, 0x8D, 0x1A, 0xC4, 0x84, 0xD7, 0x56, 0xA8, 0x9E, 0x3A, 0x44, 0x2F, + 0xEF, 0xE8, 0x33, 0x2C, 0x56, 0xCD, 0x2A, 0x9F, 0xDE, 0x69, 0x1B, 0xDB, 0xDA, 0x43, 0xF1, + 0xCC, 0x54, 0xCE, 0xF5, 0x7B, 0xEA, 0xD9, 0x61, 0x20, 0xB5, 0x0C, 0x7D, 0x46, 0x95, 0xBD, + 0xBB, 0x13, 0x03, 0xCC, 0x5D, 0xDD, 0xA8, 0x98, 0xE4, 0xEE, 0xB8, 0x30, 0x83, 0x17, 0x6E, + 0x40, 0xE0, 0x23, 0x2C, 0xDD, 0x1C, 0x31, 0x50, 0x37, 0x1D, 0xF0, 0x5D, 0x6F, 0xDA, 0xD7, + 0xE1, 0x16, 0x4D, 0x90, 0x39, 0x3C, 0xF3, 0x08, 0xE9, 0x9E, 0xDF, 0xEB, 0x31, 0xFE, 0xD2, + 0x63, 0xE2, 0x86, 0x6E, 0xE3, 0xB7, 0xF3, 0x93, 0x7B, 0x39, 0x9C, 0x97, 0x4D, 0x87, 0xBA, + 0x7B, 0x48, 0x9E, 0xFE, 0x3C, 0x9B, 0x80, 0x37, 0x1D, 0x29, 0x28, 0x44, 0x6A, 0xDC, 0x31, + 0x99, 0x1A, 0xB0, 0xCE, 0xFA, 0xAA, 0x08, 0x05, 0x75, 0xB9, 0xEC, 0x81, 0xCF, 0xA1, 0x33, + 0xA9, 0x91, 0x1C, 0x03, 0x5A, 0x80, 0x58, 0xD0, 0xD3, 0xF2, 0xE3, 0x4D, 0xE4, 0xA9, 0xFB, + 0x00, 0x9B, 0xB4, 0xCC, 0xDB, 0x16, 0xDE, 0x7B, 0x90, 0x85, 0x74, 0xA7, 0x49, 0x67, 0x25, + 0xFF, 0x85, 0x75, 0x56, 0xC1, 0xB3, 0x39, 0x17, 0xE9, 0x86, 0xC8, 0x0F, 0x10, 0x14, 0xA9, + 0xE3, 0x08, 0x3A, 0xDD, 0x2F, 0xB3, 0x5F, 0x34, 0x5C, 0x5D, 0x06, 0x15, 0x9E, 0x44, 0x33, + 0x29, 0xD0, 0xDA, 0x09, 0x99, 0x87, 0xB9, 0x96, 0xC3, 0x73, 0x15, 0x92, 0xB4, 0x60, 0xC2, + 0xFF, 0xD2, 0x95, 0x5F, 0x75, 0x46, 0xF4, 0x21, 0x61, 0x00, 0xBA, 0x43, 0x18, 0x88, 0x03, + 0xFF, 0x9B, 0x36, 0x96, 0x96, 0x85, 0xF9, 0x09, 0xFA, 0x25, 0x39, 0x32, 0x3B, 0x8C, 0x8E, + 0xC1, 0xC0, 0x95, 0xA5, 0x08, 0x5E, 0x55, 0x4D, 0xD4, 0x50, 0xE0, 0xE6, 0x7A, 0xB6, 0x70, + 0xB6, 0xA1, 0x1E, 0xBF, 0x6C, 0x25, 0x52, 0x0F, 0xC1, 0x3E, 0x36, 0x40, 0x60, 0xF9, 0x1F, + 0x9B, 0x7F, 0x3D, 0x5C, 0xB4, 0x8F, 0xF2, 0x8B, 0x8F, 0xC8, 0x3D, 0x42, 0x93, 0xF1, 0xF3, + 0x5A, 0xD6, 0xFF, 0x6A, 0xE4, 0x57, 0x4A, 0xD7, 0xA1, 0xC6, 0x00, 0x5F, 0xC0, 0x38, 0x9A, + 0x7B, 0x21, 0x38, 0x6B, 0x08, 0x50, 0xA0, 0x5D, 0x83, 0x2F, 0xE6, 0xA1, 0x4B, 0xB2, 0xB1, + 0xDB, 0x1F, 0x8E, 0x20, 0xBD, 0x09, 0x17, 0x49, 0x46, 0xCD, 0x09, 0x8B, 0x81, 0xC8, 0xF7, + 0x97, 0xE9, 0x5F, 0x21, 0x43, 0xA9, 0x49, 0x77, 0x0C, 0xF1, 0x21, 0x9B, 0xFE, 0xF0, 0x39, + 0xDB, 0x51, 0xA8, 0x0F, 0xC2, 0x47, 0xF6, 0x5F, 0x41, 0x55, 0x4C, 0x71, 0x73, 0xDD, 0x80, + 0x5B, 0xA8, 0x2F, 0xDF, 0x47, 0xAB, 0x6D, 0x4B, 0xFD, 0x37, 0xDF, 0xE4, 0x6F, 0xC4, 0x79, + 0x04, 0x42, 0x1A, 0xE0, 0x0D, 0xC0, 0x05, 0xA2, 0x2F, 0x9C, 0x47, 0x84, 0xB0, 0xEA, 0x9E, + 0x66, 0x53, 0x92, 0xA4, 0x12, 0x24, 0x50, 0x16, 0xD5, 0xC6, 0xD7, 0x67, 0x3A, 0x6A, 0x18, + 0x0D, 0x22, 0x8D, 0x42, 0x55, 0xA5, 0x38, 0xE4, 0x51, 0xFF, 0xD8, 0xB4, 0x14, 0xD4, 0x03, + 0x04, 0xC0, 0xC8, 0x88, 0x99, 0x2E, 0x0A, 0xB6, 0xDE, 0x16, 0x02, 0x10, 0x95, 0x27, 0x41, + 0x7B, 0xC1, 0xC7, 0xEB, 0x78, 0x2A, 0xE7, 0x7A, 0x8C, 0x3C, 0xDF, 0xC1, 0xD1, 0x3A, 0x1E, + 0x87, 0x42, 0x07, 0x89, 0x82, 0x64, 0xE3, 0x80, 0x80, 0x24, 0x31, 0x09, 0xC5, 0x96, 0x96, + 0x49, 0xAC, 0x83, 0x83, 0x41, 0x7E, 0x92, 0x2B, 0xA1, 0x15, 0x33, 0x11, 0x42, 0xD0, 0xED, + 0x35, 0x44, 0x0B, 0x15, 0xD4, 0x0B, 0xEE, 0x0C, 0xF5, 0x8A, 0xF3, 0x7C, 0x0F, 0x05, 0x24, + 0xFF, 0xAC, 0x1C, 0x71, 0xCE, 0xED, 0x3B, 0xB8, 0x2F, 0x76, 0xAB, 0x10, 0x8A, 0x8A, 0xD1, + 0xA0, 0xC8, 0xB7, 0x8D, 0x93, 0x41, 0x14, 0x8C, 0x64, 0x23, 0x69, 0xBE, 0x7B, 0xEF, 0x59, + 0xD4, 0x6F, 0x49, 0xD7, 0x0C, 0x83, 0x56, 0x06, 0x07, 0xF1, 0x40, 0x84, 0x8E, 0xC9, 0xA7, + 0x60, 0x7D, 0x4A, 0x08, 0xF8, 0xB6, 0xE4, 0x44, 0x7F, 0x55, 0x23, 0xF4, 0x16, 0x98, 0x18, + 0x88, 0xA8, 0xDE, 0x96, 0x47, 0xFF, 0xEF, 0x79, 0x38, 0x9E, 0x49, 0x83, 0xE5, 0xC9, 0x38, + 0x76, 0x98, 0xD0, 0xCC, 0x2D, 0x42, 0x93, 0x22, 0x36, 0x5C, 0xE7, 0xE7, 0xB5, 0xFD, 0x6D, + 0x6E, 0xB9, 0x21, 0xC8, 0x13, 0xFC, 0xF0, 0x61, 0x99, 0xFE, 0x1C, 0xA4, 0x1E, 0x9C, 0xFE, + 0x03, 0xB5, 0x39, 0xF3, 0x21, 0x67, 0x1A, 0x2A, 0xCA, 0xD0, 0x96, 0x3F, 0x87, 0x6F, 0x9D, + 0xB7, 0xA1, 0xC4, 0x37, 0x1B, 0x9F, 0x10, 0x10, 0x05, 0x21, 0x79, 0x95, 0xB5, 0xB6, 0xA4, + 0x09, 0x76, 0x24, 0x6D, 0x24, 0x5D, 0xA6, 0x03, 0xDB, 0xA8, 0xDA, 0xC8, 0x12, 0xA5, 0x48, + 0x0C, 0x34, 0x76, 0xA9, 0x9D, 0x0F, 0xFD, 0xF0, 0xEF, 0x94, 0x3D, 0x72, 0xD9, 0x12, 0x54, + 0x31, 0x48, 0xB2, 0xFE, 0x78, 0xE8, 0xB0, 0x15, 0x93, 0x24, 0xFE, 0x9B, 0xCD, 0x4C, 0xED, + 0x33, 0xCD, 0x21, 0x2F, 0xE4, 0xF3, 0xDF, 0xD6, 0xD4, 0xC5, 0xE1, 0x95, 0x8B, 0xEB, 0x95, + 0xAC, 0x6B, 0x53, 0x3A, 0xCE, 0x3E, 0x78, 0x01, 0x5E, 0x38, 0x80, 0xB5, 0x2B, 0xF4, 0x52, + 0x99, 0x26, 0x3A, 0x4C, 0x00, 0x96, 0xF8, 0xBA, 0x5F, 0xE3, 0xA6, 0x29, 0x8C, 0xAB, 0x67, + 0x5C, 0xB7, 0xF3, 0x82, 0xE7, 0xEF, 0x49, 0x72, 0x0E, 0xB4, 0xCF, 0x47, 0x37, 0x6E, 0x2D, + 0x25, 0x74, 0x12, 0x2C, 0xCF, 0x91, 0x12, 0x9C, 0x85, 0x8E, 0x94, 0x89, 0x04, 0xFE, 0xCE, + 0xFB, 0x91, 0x22, 0x6E, 0xD4, 0x24, 0x03, 0xBA, 0x12, 0xDD, 0x32, 0x58, 0x90, 0x9A, 0x87, + 0xDF, 0xCB, 0xF6, 0x5C, 0xC3, 0xAD, 0xC3, 0xD9, 0x8D, 0x27, 0x7F, 0xDC, 0xEC, 0x76, 0x64, + 0xE2, 0x29, 0x2B, 0x7D, 0x27, 0xAF, 0xBB, 0x5A, 0xAF, 0xB4, 0x05, 0xC2, 0x0A, 0x34, 0xB2, + 0xFE, 0x2C, 0x08, 0x49, 0xEE, 0x28, 0x0B, 0xB8, 0x91, 0xDF, 0xDF, 0x59, 0xF1, 0x9B, 0x89, + 0xB0, 0x24, 0x63, 0x58, 0xDB, 0x54, 0xCF, 0x3F, 0xDC, 0x66, 0xEA, 0xAA, 0xA7, 0x50, 0xC8, + 0x90, 0x3F, 0x1D, 0x42, 0x67, 0x8F, 0x3E, 0xDF, 0x0B, 0x75, 0x30, 0x41, 0x0A, 0xA8, 0x81, + 0xBC, 0x61, 0x7F, 0x94, 0x34, 0x63, 0x79, 0x85, 0x4A, 0xF4, 0x53, 0x2E, 0x61, 0xF6, 0x5A, + 0xAE, 0x75, 0x76, 0xC3, 0x5F, 0xAF, 0x55, 0xE1, 0x55, 0xBD, 0x67, 0x87, 0xB4, 0x63, 0x4D, + 0x54, 0x19, 0x19, 0x07, 0xE1, 0x55, 0xC2, 0x39, 0xE6, 0x84, 0x80, 0xCD, 0xFA, 0x0C, 0x87, + 0x05, 0x4B, 0xFB, 0x62, 0x85, 0x5F, 0x40, 0x9A, 0x20, 0xD5, 0x33, 0x5F, 0xB1, 0x23, 0xE6, + 0x81, 0xE6, 0x4E, 0xC8, 0x47, 0xCD, 0x98, 0x5B, 0x60, 0x62, 0x05, 0x9F, 0x43, 0x6A, 0xEB, + 0xAC, 0x62, 0x3C, 0x03, 0x8B, 0x6C, 0x34, 0x05, 0xAC, 0x32, 0x51, 0x91, 0xA8, 0xD1, 0x12, + 0x6A, 0x5E, 0xF8, 0xF3, 0x8C, 0xCC, 0xBF, 0x14, 0x4A, 0x5C, 0x32, 0x4C, 0x1E, 0x09, 0x3C, + 0xF9, 0x9E, 0xFB, 0xE1, 0x0C, 0xA0, 0x3D, 0x43, 0x9B, 0xCF, 0xB8, 0xBA, 0x5E, 0x29, 0x3B, + 0x7D, 0x31, 0x88, 0x37, 0xF7, 0xBC, 0x42, 0xA9, 0x99, 0x64, 0x39, 0x23, 0x69, 0xDA, 0x76, + 0xE7, 0x9D, 0x71, 0xD1, 0xA2, 0xC2, 0x48, 0xA1, 0x13, 0x24, 0xA8, 0x7A, 0xE1, 0xE3, 0xCB, + 0xEA, 0xB6, 0xFB, 0x0D, 0x0B, 0xCA, 0xE1, 0xEF, 0x55, 0xE4, 0x3D, 0xFB, 0x6F, 0x1B, 0x4C, + 0xFB, 0x82, 0xC7, 0xA7, 0x78, 0xFB, 0x82, 0x8A, 0x37, 0x27, 0xEF, 0x07, 0x68, 0x5F, 0xE3, + 0x8A, 0x74, 0xB3, 0xDD, 0x25, 0xD0, 0x15, 0x32, 0x2C, 0x2D, 0x9F, 0x24, 0x5C, 0x08, 0xD8, + 0xC2, 0xB4, 0x38, 0x65, 0x69, 0x42, 0x33, 0x78, 0x2E, 0xB7, 0x34, 0x43, 0x6C, 0x4E, 0xDD, + 0xEF, 0x54, 0x06, 0x20, 0x8D, 0x6C, 0x45, 0x72, 0xC7, 0x37, 0x12, 0x62, 0xFE, 0x02, 0x31, + 0x9C, 0xFB, 0xBC, 0xF2, 0xE2, 0x3B, 0xED, 0x8A, 0xA9, 0x69, 0xD1, 0xAE, 0x6F, 0x5F, 0x25, + 0xFF, 0x6B, 0x8E, 0xBC, 0xF0, 0x92, 0x50, 0x66, 0xF7, 0x61, 0xA3, 0x9B, 0xBF, 0xF4, 0x9F, + 0x0C, 0x8D, 0xBC, 0x3B, 0xE8, 0x4F, 0x0C, 0x44, 0x2B, 0x04, 0x4E, 0xA0, 0x1B, 0x66, 0x97, + 0x47, 0xE3, 0xC8, 0x29, 0x3C, 0xFE, 0x9C, 0xCD, 0xF2, 0xEF, 0x06, 0x3A, 0xE3, 0xD2, 0x8D, + 0x10, 0x72, 0x0C, 0x27, 0x9A, 0x26, 0x91, 0x61, 0x6A, 0xBD, 0x23, 0xB0, 0x55, 0xCF, 0xC6, + 0xC5, 0x62, 0x12, 0x5D, 0xF4, 0xAD, 0x0F, 0xA6, 0x63, 0x13, 0x04, 0x97, 0x2D, 0xDC, 0x36, + 0x74, 0xB1, 0xAA, 0xA7, 0x66, 0x5B, 0xF6, 0x21, 0x32, 0x0D, 0x83, 0xEA, 0xC8, 0xD5, 0xB3, + 0x71, 0xD7, 0xD7, 0x19, 0x82, 0x9F, 0x58, 0xB2, 0x34, 0x58, 0x18, 0x25, 0x58, 0x71, 0x0D, + 0xE3, 0x1D, 0x81, 0xEF, 0x9A, 0x47, 0xD8, 0x83, 0x9C, 0x79, 0x64, 0x0B, 0x20, 0x25, 0xD1, + 0x96, 0x5A, 0x41, 0x8B, 0xC9, 0x0E, 0x41, 0x15, 0xF1, 0x42, 0x33, 0x11, 0xA8, 0xB6, 0x4F, + 0xCD, 0xE0, 0xF2, 0xD2, 0x14, 0x5E, 0xE5, 0x35, 0xB0, 0x93, 0x1B, 0x84, 0xBC, 0x81, 0x10, + 0x44, 0x5F, 0x2F, 0xF6, 0x8D, 0x13, 0x6E, 0xD7, 0x09, 0xDD, 0xB7, 0xEA, 0x9F, 0xF7, 0x5F, + 0x3B, 0x4E, 0x8B, 0x4F, 0x83, 0x62, 0x30, 0xCA, 0x9E, 0x81, 0x06, 0x94, 0x77, 0xF6, 0x34, + 0xE0, 0x72, 0x70, 0xAF, 0x60, 0xEF, 0x96, 0xF7, 0x25, 0x57, 0xA0, 0x81, 0xD6, 0x64, 0xAB, + 0xCF, 0x35, 0x54, 0x8F, 0x69, 0x94, 0x84, 0x65, 0x3D, 0xA6, 0x45, 0x48, 0x3F, 0xF2, 0xBF, + 0x59, 0x98, 0x61, 0x7A, 0xE8, 0xBF, 0xA6, 0x2D, 0x56, 0xE7, 0x14, 0xF3, 0xC0, 0x13, 0x6E, + 0x50, 0x35, 0xA3, 0xF7, 0x8E, 0x06, 0xC2, 0xF4, 0x70, 0xDF, 0x7F, 0xD3, 0x38, 0x0D, 0x14, + 0x03, 0x3F, 0x81, 0xE2, 0xAA, 0xE6, 0xB4, 0xD9, 0x04, 0x87, 0xDA, 0xB7, 0x6B, 0x9B, 0x3B, + 0x87, 0x61, 0xFB, 0x56, 0xC3, 0x6F, 0x54, 0x29, 0xDA, 0x3D, 0x43, 0x46, 0xCB, 0x22, 0xE6, + 0x41, 0xAD, 0x8D, 0x7D, 0x2D, 0x80, 0xFA, 0x24, 0x0D, 0x4E, 0x01, 0x54, 0xE6, 0xB3, 0xD2, + 0xF1, 0xB3, 0xEF, 0x6C, 0xF1, 0x74, 0xC0, 0x8D, 0x06, 0x2F, 0x57, 0x5C, 0x83, 0xA4, 0x07, + 0x81, 0x74, 0xF8, 0x74, 0x36, 0x4D, 0xF3, 0x6A, 0x63, 0x28, 0xBE, 0xEE, 0xF6, 0x9B, 0xA7, + 0xF9, 0x0E, 0x1D, 0xF9, 0xFD, 0xCE, 0xC9, 0xA2, 0xF1, 0x5E, 0xBF, 0x04, 0xFA, 0x7D, 0x67, + 0x56, 0xDA, 0x2E, 0x5A, 0x59, 0xC9, 0xCB, 0xBC, 0xBC, 0x39, 0x7D, 0x6F, 0xB2, 0x8D, 0x0F, + 0xC9, 0xA6, 0x05, 0x34, 0xDF, 0xF0, 0x75, 0x27, 0x16, 0xED, 0x07, 0x9A, 0xD1, 0xAB, 0x19, + 0xA2, 0x24, 0xD1, 0xC8, 0xAE, 0x8A, 0x53, 0x24, 0x2F, 0xD1, 0x64, 0x98, 0x9F, 0xF9, 0x97, + 0x48, 0x9B, 0x65, 0x20, 0xEB, 0x3C, 0x0E, 0x97, 0xF4, 0xBC, 0xC1, 0xA9, 0xC3, 0xCB, 0xD4, + 0x4F, 0x00, 0x8C, 0x03, 0xEF, 0x52, 0xCF, 0x7E, 0x62, 0x68, 0x81, 0xD2, 0x46, 0x92, 0x5E, + 0x03, 0x36, 0xC0, 0xAC, 0x66, 0x88, 0x67, 0xF8, 0x53, 0xDA, 0x78, 0x20, 0xF9, 0x14, 0x11, + 0x5A, 0x7C, 0x77, 0xAC, 0x31, 0xB6, 0x6F, 0x46, 0xFB, 0xF9, 0x7F, 0x66, 0xFA, 0x26, 0x41, + 0x6F, 0xC4, 0x58, 0x1D, 0x45, 0x9A, 0x4F, 0x24, 0x62, 0xD5, 0x2C, 0xF0, 0xC7, 0x9B, 0x27, + 0x89, 0x55, 0xAA, 0x73, 0xE8, 0xFA, 0x56, 0xE3, 0xC3, 0x20, 0xF5, 0x16, 0xBC, 0xC5, 0x4C, + 0x97, 0xE5, 0x87, 0x19, 0x9C, 0x15, 0xAB, 0x95, 0x3C, 0xC3, 0x71, 0x89, 0xB8, 0x1C, 0x70, + 0xCA, 0xBB, 0x25, 0x59, 0xE4, 0x45, 0xBC, 0xC9, 0xD8, 0x17, 0x4A, 0xD7, 0x57, 0x4E, 0x9A, + 0xCB, 0x02, 0xF4, 0x3E, 0x0C, 0x34, 0xFF, 0x5E, 0x67, 0x46, 0xEE, 0x73, 0x0A, 0xD4, 0x1F, + 0xF8, 0xEE, 0xF9, 0x3C, 0x20, 0x71, 0xC2, 0x64, 0x90, 0x63, 0xDD, 0x92, 0xF3, 0x43, 0xC0, + 0x6E, 0xF6, 0xAB, 0xAF, 0x98, 0xF2, 0x8D, 0x98, 0xD9, 0x68, 0x07, 0x1C, 0x12, 0xCC, 0x10, + 0xA9, 0x0C, 0x22, 0xD8, 0xB3, 0xB3, 0x48, 0x0C, 0x76, 0xF7, 0xA5, 0x1B, 0x7E, 0xC5, 0x94, + 0xB3, 0x43, 0x5D, 0x2E, 0x3D, 0x77, 0x9C, 0x1A, 0x15, 0x03, 0x76, 0x97, 0xF3, 0xA0, 0x58, + 0x65, 0x04, 0x72, 0xE4, 0x7E, 0xEC, 0xD5, 0xF3, 0x2E, 0xB3, 0x24, 0x3A, 0x51, 0x6F, 0x0E, + 0x70, 0x3F, 0x98, 0x88, 0xC8, 0x46, 0x90, 0x75, 0x06, 0x48, 0xD6, 0xA9, 0xA8, 0x76, 0xBF, + 0x1F, 0x35, 0x3D, 0xB6, 0x89, 0x1D, 0xC6, 0xD3, 0x17, 0xD6, 0xE8, 0x7A, 0xC0, 0x88, 0xF4, + 0x2B, 0x5F, 0x6F, 0x20, 0xD7, 0x99, 0xEC, 0xE4, 0xFA, 0x7A, 0xAA, 0x92, 0x8D, 0x2A, 0xC7, + 0x95, 0xE8, 0xDE, 0x83, 0xD1, 0xE1, 0xC7, 0xFA, 0x2F, 0x9A, 0x41, 0x06, 0x69, 0x3E, 0x98, + 0x1C, 0x21, 0xC6, 0x3B, 0x32, 0x21, 0xC4, 0xFA, 0x26, 0x49, 0xF4, 0x5F, 0x0C, 0x6E, 0x05, + 0xDB, 0xF2, 0x40, 0x11, 0xAF, 0x16, 0xAB, 0x2E, 0x5F, 0xE9, 0x4A, 0x64, 0x0B, 0x48, 0x59, + 0x88, 0x03, 0x7E, 0xBE, 0x1E, 0x8A, 0xD0, 0xB2, 0x62, 0x3D, 0x95, 0xE9, 0x94, 0x7F, 0x07, + 0x26, 0x12, 0x1D, 0x78, 0x28, 0x61, 0x4E, 0x3B, 0x2D, 0x77, 0xA7, 0xA1, 0xF9, 0xA9, 0x38, + 0xBE, 0xA9, 0xA1, 0xA7, 0xA2, 0x62, 0x7B, 0x7D, 0x2E, 0x35, 0x8C, 0x42, 0xCC, 0xC6, 0xC0, + 0xB8, 0x0A, 0x15, 0xA1, 0xC2, 0xF6, 0xE9, 0xAA, 0xF0, 0x49, 0x5B, 0xDB, 0x7B, 0xB8, 0xD4, + 0xB0, 0xE2, 0x8A, 0x1A, 0xB5, 0xAB, 0x93, 0xCA, 0x0F, 0xF3, 0xE3, 0xF9, 0x10, 0xC4, 0x90, + 0xC1, 0x34, 0x86, 0x85, 0x25, 0x34, 0xD5, 0xE1, 0x21, 0x60, 0x83, 0x5E, 0xC5, 0x91, 0x6C, + 0x5C, 0x68, 0x34, 0x9C, 0x4E, 0x2D, 0x8F, 0xA9, 0x56, 0xC6, 0x43, 0x27, 0x7E, 0xDD, 0x3B, + 0x6C, 0x81, 0xC8, 0x8C, 0x01, 0x04, 0x21, 0x70, 0x5F, 0xD3, 0x17, 0xFF, 0x9E, 0x3C, 0x94, + 0xDF, 0x0E, 0xD5, 0x30, 0x5F, 0x53, 0x0A, 0xCB, 0xCC, 0xF8, 0xDD, 0x0E, 0x87, 0x14, 0x0C, + 0xD3, 0x81, 0x52, 0x66, 0x4A, 0x57, 0x2C, 0x16, 0x8C, 0xD7, 0x25, 0x95, 0xB7, 0xFA, 0xC2, + 0x43, 0xC0, 0x3F, 0x3F, 0xB3, 0x3E, 0xF7, 0x4A, 0x28, 0xC0, 0xE4, 0x46, 0x9F, 0x94, 0x58, + 0x7C, 0x13, 0x70, 0x4E, 0x9E, 0xFE, 0x80, 0x10, 0xB2, 0x12, 0x5A, 0xCA, 0x78, 0xC2, 0x2C, + 0x33, 0xC8, 0x23, 0x66, 0xE1, 0xA7, 0xC4, 0x02, 0x8C, 0x2A, 0xE2, 0xE8, 0xD2, 0x6E, 0x1A, + 0x57, 0xE4, 0x29, 0x7F, 0xAC, 0x98, 0x7F, 0x84, 0xA0, 0xA2, 0x7F, 0x42, 0xB4, 0xC9, 0x3A, + 0x4F, 0x4D, 0x14, 0x56, 0x98, 0x24, 0xB0, 0x88, 0x0F, 0xB6, 0x74, 0x07, 0xED, 0x58, 0xF2, + 0x67, 0xAC, 0x40, 0x3A, 0xA0, 0xB1, 0xF9, 0x37, 0x84, 0xB4, 0xB4, 0xC6, 0x70, 0x36, 0x03, + 0x7E, 0x60, 0xD5, 0x80, 0x72, 0x61, 0x1B, 0x0E, 0x90, 0xCA, 0x31, 0x69, 0x76, 0xEF, 0x4E, + 0x0B, 0x30, 0x2C, 0xDA, 0xD1, 0xB6, 0xDC, 0xCA, 0x92, 0xEF, 0xB8, 0xE1, 0xF6, 0xBE, 0x23, + 0x97, 0x96, 0x75, 0x08, 0xBE, 0x2C, 0x02, 0xA2, 0x5E, 0xD0, 0x38, 0x0B, 0xA1, 0xF7, 0x95, + 0x5F, 0x85, 0x7C, 0x8F, 0xB0, 0x43, 0x29, 0x77, 0x80, 0xD1, 0x36, 0xB2, 0xB0, 0x64, 0x04, + 0x0C, 0x8E, 0x55, 0x14, 0x3D, 0x71, 0x5E, 0xA9, 0x97, 0xE1, 0x34, 0xED, 0x97, 0x3C, 0x98, + 0xEF, 0x82, 0x78, 0x6F, 0x0C, 0xCF, 0x66, 0xC1, 0x7D, 0x86, 0x35, 0x42, 0x18, 0x0C, 0x66, + 0xD5, 0x4D, 0x08, 0xE1, 0x16, 0xF2, 0xE3, 0x5D, 0x99, 0x5E, 0x21, 0x44, 0x89, 0xAD, 0x0F, + 0xAD, 0x7A, 0x55, 0xFE, 0x9E, 0xBC, 0x1A, 0x77, 0x7F, 0xE3, 0x41, 0x41, 0x14, 0x7C, 0x08, + 0x0B, 0x98, 0xD1, 0x34, 0x63, 0xA3, 0xBB, 0xC6, 0xFC, 0x82, 0xF2, 0xFC, 0x95, 0xF4, 0xDE, + 0x7B, 0x35, 0x91, 0xD9, 0xC8, 0xCD, 0x44, 0x16, 0x91, 0x7A, 0x43, 0x38, 0x09, 0x5D, 0x56, + 0x20, 0x10, 0x4B, 0x7B, 0xE1, 0x3F, 0x5A, 0x13, 0x1D, 0xD3, 0xF7, 0xAA, 0xD5, 0xB5, 0x59, + 0xD1, 0x1E, 0x81, 0x71, 0xDF, 0xB9, 0x1E, 0x2B, 0xB1, 0xE4, 0x7A, 0xC3, 0x81, 0x0B, 0x1C, + 0xDC, 0x1A, 0x1E, 0x37, 0x0C, 0x86, 0x7B, 0x7B, 0x7B, 0x50, 0xC4, 0x68, 0x8D, 0xCE, 0x54, + 0x57, 0x63, 0x15, 0x7E, 0x02, 0xF4, 0x7E, 0x1C, 0xC6, 0x61, 0xD5, 0xBF, 0x2F, 0xBC, 0x33, + 0x6C, 0xFA, 0xE0, 0x80, 0xAB, 0x15, 0x72, 0x8B, 0x1A, 0xB9, 0xDD, 0x19, 0x9F, 0x27, 0x79, + 0xD4, 0x51, 0xE6, 0x17, 0x89, 0x77, 0xFB, 0x65, 0x8C, 0x17, 0x34, 0x4C, 0xFF, 0xB7, 0xAA, + 0x3A, 0xF5, 0x79, 0x1A, 0x28, 0xFC, 0x8A, 0x08, 0x9C, 0x85, 0x18, 0x77, 0x53, 0xE5, 0xE3, + 0x13, 0xC8, 0xD1, 0xF0, 0xFE, 0x77, 0x55, 0xE2, 0x8B, 0xE4, 0x44, 0x42, 0x6A, 0x18, 0x9E, + 0x8B, 0xCE, 0x2D, 0x2F, 0x79, 0xDB, 0x31, 0xD4, 0xC3, 0xCA, 0x91, 0x1A, 0x83, 0x45, 0x55, + 0x25, 0x35, 0x5F, 0x95, 0xD1, 0x59, 0x35, 0x1C, 0xD7, 0x31, 0xA8, 0x8E, 0x55, 0x40, 0x38, + 0x51, 0x23, 0x6E, 0xE2, 0x12, 0x8F, 0x27, 0x9D, 0x5B, 0xE6, 0x44, 0xC0, 0x42, 0x45, 0x3A, + 0xE6, 0x5D, 0x9E, 0x9F, 0x3B, 0x40, 0xD6, 0xC8, 0x2B, 0xDE, 0xB0, 0x02, 0xAC, 0xDE, 0xE0, + 0x61, 0xEC, 0xCA, 0x3F, 0x2D, 0xCE, 0xAB, 0xEF, 0x9A, 0x90, 0x0E, 0x6E, 0x06, 0x3D, 0x56, + 0xAB, 0x39, 0xCB, 0x82, 0xDB, 0xC7, 0x7A, 0x46, 0x77, 0x57, 0x2D, 0x76, 0x16, 0xCD, 0x72, + 0xC0, 0xF6, 0xD5, 0xB9, 0xB9, 0x41, 0xDF, 0xDA, 0x1F, 0xE7, 0xC8, 0x96, 0xB8, 0xCC, 0x24, + 0xD6, 0x5A, 0x43, 0x22, 0xD7, 0x12, 0xA8, 0x4E, 0x94, 0xAD, 0xFC, 0x8E, 0xD0, 0xCC, 0x56, + 0xCC, 0x1A, 0xE9, 0x7F, 0x77, 0x5B, 0xD3, 0xCE, 0xA5, 0xB2, 0x0B, 0x52, 0x4D, 0x9A, 0x7A, + 0x91, 0x60, 0x56, 0xE1, 0x9A, 0xF0, 0x95, 0xD3, 0x01, 0x71, 0xE5, 0xE1, 0x4C, 0x7C, 0x99, + 0x8F, 0x78, 0xDC, 0x44, 0x84, 0x5E, 0xDF, 0x30, 0x73, 0x63, 0xEA, 0xB7, 0x91, 0x3F, 0x68, + 0x0A, 0x5E, 0x5A, 0x15, 0x40, 0xA6, 0xF9, 0x45, 0x50, 0x7F, 0xFA, 0x67, 0x59, 0x1F, 0x8D, + 0x1A, 0x29, 0x20, 0xAB, 0x3B, 0x6E, 0x75, 0x4E, 0x35, 0x37, 0x9D, 0xD6, 0x78, 0x70, 0xC2, + 0x42, 0x33, 0x5E, 0x27, 0x17, 0x90, 0x3F, 0xF3, 0xC6, 0x87, 0xE5, 0xC3, 0x3D, 0xC9, 0x53, + 0x41, 0x68, 0x65, 0xD5, 0xF2, 0x3B, 0xD7, 0x52, 0xE5, 0x54, 0x92, 0xB9, 0xD5, 0xD8, 0x88, + 0xD7, 0xB3, 0x7E, 0xF3, 0x3B, 0x0A, 0x67, 0x74, 0xD0, 0x52, 0xB0, 0x98, 0x7C, 0x06, 0x6A, + 0x2E, 0x01, 0x76, 0x72, 0x07, 0xAA, 0x7F, 0xBF, 0xC3, 0x93, 0xCA, 0x62, 0x87, 0x46, 0x13, + 0xDD, 0xE3, 0x79, 0x4F, 0x74, 0xFA, 0xDB, 0x5D, 0x55, 0xB8, 0x77, 0xB8, 0x77, 0xA6, 0x05, + 0x91, 0x8C, 0x81, 0x26, 0x10, 0xFB, 0xCA, 0xFA, 0xD7, 0x2E, 0xE2, 0x45, 0xE6, 0xDD, 0x87, + 0x21, 0x13, 0x8D, 0x6B, 0xD3, 0xF4, 0xEE, 0xDA, 0xD8, 0x53, 0xAE, 0xD1, 0xEC, 0x43, 0x7A, + 0xD0, 0x2A, 0xC9, 0x37, 0xC8, 0x0D, 0xAE, 0x26, 0xFA, 0x5F, 0x70, 0x08, 0x3B, 0xD3, 0x46, + 0x77, 0x9B, 0x77, 0x93, 0x87, 0xF7, 0xB3, 0xD2, 0xAA, 0xE5, 0x77, 0x70, 0xD8, 0x17, 0x79, + 0x28, 0x83, 0x32, 0x81, 0xCC, 0xB7, 0xA3, 0x8D, 0xA2, 0x48, 0x34, 0xFD, 0x97, 0x26, 0xFD, + 0x17, 0xEB, 0x60, 0x3C, 0xBA, 0x90, 0x41, 0xE8, 0x2B, 0xFE, 0xED, 0x0E, 0x33, 0x94, 0x2D, + 0xDE, 0x1D, 0x48, 0xC2, 0x71, 0xF5, 0xB3, 0x9A, 0xA7, 0x23, 0x0F, 0x41, 0xAF, 0xB8, 0x9D, + 0x36, 0xF7, 0x97, 0x6E, 0xEE, 0x4F, 0x51, 0xA0, 0x36, 0x74, 0x30, 0x31, 0xC5, 0x34, 0xF6, + 0x46, 0x85, 0xB9, 0x4C, 0x99, 0x0A, 0x93, 0xA5, 0x73, 0x7F, 0xE6, 0x28, 0xEE, 0x9C, 0xDA, + 0x8E, 0xD9, 0xC0, 0x8B, 0x11, 0xD3, 0x83, 0x6F, 0x83, 0x38, 0x35, 0xC4, 0x45, 0xB3, 0x17, + 0xA7, 0x7E, 0xAD, 0x75, 0x99, 0xD1, 0xA0, 0xC0, 0x88, 0x73, 0x01, 0x45, 0x10, 0xD3, 0x6B, + 0xB7, 0xFF, 0x5F, 0xB9, 0x61, 0x27, 0x75, 0x89, 0xEA, 0x48, 0xC3, 0x2A, 0x60, 0xC8, 0x7E, + 0xC4, 0x06, 0x81, 0xBE, 0x06, 0x7B, 0x17, 0x78, 0x5E, 0xC4, 0x48, 0x25, 0xBD, 0x89, 0xFA, + 0xA2, 0x52, 0x49, 0xE7, 0x35, 0xA6, 0x28, 0xB6, 0xEE, 0xBC, 0xC6, 0xCC, 0xE4, 0xE0, 0x31, + 0x4C, 0x62, 0x75, 0x88, 0x11, 0x8C, 0x40, 0xB2, 0xE0, 0xD4, 0x60, 0xD8, 0xD5, 0xCE, 0x35, + 0x8C, 0x56, 0x45, 0x8F, 0x36, 0x91, 0x4C, 0xA2, 0x03, 0xF5, 0xA5, 0x38, 0x1C, 0x6D, 0xEB, + 0x5A, 0x76, 0xBB, 0xC0, 0x8C, 0x40, 0xA8, 0x74, 0x37, 0xDA, 0x0D, 0x0B, 0x57, 0x17, 0x88, + 0xA0, 0x5E, 0x9F, 0x96, 0xD9, 0xBB, 0x77, 0x0D, 0xE8, 0xA0, 0xB1, 0xB9, 0x60, 0xFF, 0x2A, + 0x44, 0xA9, 0x64, 0xC9, 0xB7, 0x93, 0x98, 0x53, 0x74, 0x2E, 0x83, 0xCE, 0x8D, 0xEB, 0x79, + 0x19, 0x1B, 0x2D, 0x82, 0x45, 0x46, 0x55, 0xF2, 0x27, 0x07, 0x9D, 0xD8, 0xC5, 0xB0, 0x21, + 0x6C, 0x84, 0x70, 0xB8, 0xE1, 0xAC, 0x70, 0x52, 0x63, 0x01, 0xBB, 0xFA, 0x2B, 0xC4, 0xAD, + 0xCA, 0x68, 0xA7, 0x66, 0xCC, 0xB2, 0xA6, 0xE0, 0xEB, 0xF2, 0xE9, 0x99, 0x05, 0xBF, 0x52, + 0x42, 0x59, 0x0B, 0x01, 0x70, 0x38, 0x68, 0xB3, 0xFA, 0xF8, 0x41, 0xC1, 0x1C, 0x38, 0x3B, + 0xE1, 0x45, 0xA4, 0x0F, 0xEA, 0x63, 0x75, 0xE1, 0x8A, 0x01, 0x46, 0x8E, 0x45, 0x96, 0x03, + 0xB5, 0xEF, 0xDF, 0x8A, 0x4E, 0x9A, 0xBD, 0x17, 0x92, 0x80, 0xAE, 0x8B, 0x59, 0x47, 0xD7, + 0x8D, 0x2F, 0x0C, 0x4D, 0x37, 0x71, 0x5E, 0xAA, 0x42, 0xBC, 0x37, 0xCF, 0x87, 0x30, 0xE4, + 0x1F, 0xFB, 0xF9, 0x82, 0x6D, 0x46, 0x42, 0x4F, 0x29, 0x22, 0xA9, 0x60, 0x33, 0xCE, 0xFA, + 0xA8, 0xB4, 0xBB, 0xE4, 0xC8, 0xB8, 0x9D, 0x43, 0x50, 0x1F, 0xD5, 0x21, 0x1D, 0x53, 0x92, + 0xCA, 0x19, 0xA9, 0x8B, 0xA1, 0x27, 0xD9, 0x02, 0x5B, 0x5C, 0x6E, 0x86, 0xBA, 0x02, 0x44, + 0x71, 0x94, 0x05, 0x49, 0xA2, 0xB5, 0xD8, 0xE1, 0x49, 0x61, 0xC9, 0xDC, 0x19, 0x69, 0x6D, + 0xA1, 0xA5, 0xBF, 0xFD, 0x01, 0x03, 0x0D, 0x5E, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x09, 0x0F, 0x13, 0x1A, 0x1F, + ]; + + measure(|| { + if MLDSA65::verify(&mldsa65_pk, msg, None, &sig).is_ok() { + println!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Same as bench_mldsa65_verify(), but with a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLDSA65PublicKeyExpanded, which is +/// what makes repeated verifications against the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. Both the expansion and the verification are +/// inside the measurement. +fn bench_mldsa65_verify_expanded_pk() { + use bouncycastle::mldsa::{MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKeyExpanded, MLDSATrait}; + + eprintln!("MLDSA65/Verify_with_expanded_key"); + + let msg = b"The quick brown fox jumped over the lazy dog"; + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + + // 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], + // 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 = load_mldsa65_pk(); + // 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: [u8; MLDSA65_SIG_LEN] = [ + 0x90, 0x61, 0xF1, 0x5C, 0xBF, 0x20, 0x92, 0xF7, 0x44, 0xFB, 0xCD, 0x79, 0x9E, 0xB0, 0x24, + 0x14, 0x05, 0x3C, 0x1B, 0x0F, 0x74, 0x12, 0x12, 0x4B, 0xED, 0xC4, 0x1C, 0xF9, 0xA3, 0xDB, + 0x01, 0x66, 0x46, 0x9E, 0x87, 0x40, 0x37, 0xD7, 0xF0, 0x81, 0xE5, 0xF8, 0xD3, 0xD2, 0x03, + 0x3A, 0x03, 0x07, 0xD1, 0xC4, 0x9E, 0xD0, 0x1F, 0xE6, 0x45, 0x78, 0xC4, 0xA6, 0xFA, 0xBD, + 0x80, 0x88, 0x0C, 0xDF, 0x19, 0x11, 0x84, 0x8F, 0x18, 0x4D, 0x4B, 0xCF, 0x53, 0x6C, 0xA7, + 0x95, 0xA0, 0xFB, 0x1A, 0xA1, 0x9A, 0xB7, 0xEE, 0x3B, 0xA6, 0xB5, 0x8B, 0xD6, 0x4B, 0xBE, + 0xAC, 0x9F, 0x58, 0x65, 0x0F, 0xFF, 0x1E, 0xF5, 0xA9, 0x7A, 0xB6, 0x91, 0x6D, 0xF9, 0x62, + 0x07, 0x2E, 0x20, 0xE7, 0xC6, 0xBE, 0x96, 0x09, 0x0E, 0x3A, 0x78, 0x1A, 0x50, 0x4B, 0xC4, + 0x44, 0x2B, 0xD8, 0x88, 0x9A, 0x0A, 0xA6, 0x28, 0x90, 0x7A, 0x74, 0x29, 0x9F, 0x39, 0xFA, + 0x83, 0x60, 0x31, 0xF1, 0xBD, 0x68, 0x35, 0x5B, 0xEB, 0xE7, 0xAE, 0x93, 0xC1, 0xE3, 0x61, + 0xA9, 0xEF, 0xBE, 0xD1, 0x32, 0x5D, 0x96, 0x22, 0x70, 0x70, 0x46, 0x1F, 0xCD, 0x6F, 0x15, + 0x1B, 0x86, 0x69, 0xD9, 0x22, 0x9B, 0x97, 0x7D, 0x9E, 0xE5, 0x1F, 0xD2, 0x26, 0x0C, 0x3E, + 0x4A, 0x2E, 0x82, 0x04, 0x16, 0xF9, 0xE0, 0x74, 0x95, 0x8D, 0xC3, 0xB3, 0xE2, 0x21, 0x7E, + 0x63, 0x12, 0xB7, 0xE0, 0xB5, 0x82, 0xA0, 0x48, 0x98, 0x1C, 0xF6, 0x57, 0x9F, 0x4B, 0xC7, + 0x71, 0x5B, 0x78, 0xC8, 0x08, 0xE4, 0xC5, 0x7E, 0x3B, 0x8A, 0xA3, 0x8B, 0x05, 0xC0, 0x4F, + 0xCE, 0xDF, 0x20, 0x9F, 0x52, 0xC1, 0xE3, 0x31, 0xAE, 0x83, 0xDB, 0xDF, 0xF6, 0x0B, 0xA4, + 0x50, 0xA1, 0x7C, 0xC3, 0x97, 0x56, 0x8E, 0x54, 0xBC, 0x3F, 0x16, 0xDD, 0xF3, 0x0B, 0x92, + 0x74, 0x7C, 0xE4, 0x60, 0xD9, 0x25, 0xB9, 0xBE, 0x20, 0xA1, 0xD3, 0x5E, 0x2A, 0xED, 0x97, + 0xF1, 0x24, 0xAF, 0x26, 0x16, 0xA5, 0x36, 0x1D, 0xF2, 0x8B, 0xA3, 0x0E, 0x52, 0x2D, 0xD0, + 0x8F, 0xA0, 0x0F, 0xD2, 0x8D, 0x1A, 0xC4, 0x84, 0xD7, 0x56, 0xA8, 0x9E, 0x3A, 0x44, 0x2F, + 0xEF, 0xE8, 0x33, 0x2C, 0x56, 0xCD, 0x2A, 0x9F, 0xDE, 0x69, 0x1B, 0xDB, 0xDA, 0x43, 0xF1, + 0xCC, 0x54, 0xCE, 0xF5, 0x7B, 0xEA, 0xD9, 0x61, 0x20, 0xB5, 0x0C, 0x7D, 0x46, 0x95, 0xBD, + 0xBB, 0x13, 0x03, 0xCC, 0x5D, 0xDD, 0xA8, 0x98, 0xE4, 0xEE, 0xB8, 0x30, 0x83, 0x17, 0x6E, + 0x40, 0xE0, 0x23, 0x2C, 0xDD, 0x1C, 0x31, 0x50, 0x37, 0x1D, 0xF0, 0x5D, 0x6F, 0xDA, 0xD7, + 0xE1, 0x16, 0x4D, 0x90, 0x39, 0x3C, 0xF3, 0x08, 0xE9, 0x9E, 0xDF, 0xEB, 0x31, 0xFE, 0xD2, + 0x63, 0xE2, 0x86, 0x6E, 0xE3, 0xB7, 0xF3, 0x93, 0x7B, 0x39, 0x9C, 0x97, 0x4D, 0x87, 0xBA, + 0x7B, 0x48, 0x9E, 0xFE, 0x3C, 0x9B, 0x80, 0x37, 0x1D, 0x29, 0x28, 0x44, 0x6A, 0xDC, 0x31, + 0x99, 0x1A, 0xB0, 0xCE, 0xFA, 0xAA, 0x08, 0x05, 0x75, 0xB9, 0xEC, 0x81, 0xCF, 0xA1, 0x33, + 0xA9, 0x91, 0x1C, 0x03, 0x5A, 0x80, 0x58, 0xD0, 0xD3, 0xF2, 0xE3, 0x4D, 0xE4, 0xA9, 0xFB, + 0x00, 0x9B, 0xB4, 0xCC, 0xDB, 0x16, 0xDE, 0x7B, 0x90, 0x85, 0x74, 0xA7, 0x49, 0x67, 0x25, + 0xFF, 0x85, 0x75, 0x56, 0xC1, 0xB3, 0x39, 0x17, 0xE9, 0x86, 0xC8, 0x0F, 0x10, 0x14, 0xA9, + 0xE3, 0x08, 0x3A, 0xDD, 0x2F, 0xB3, 0x5F, 0x34, 0x5C, 0x5D, 0x06, 0x15, 0x9E, 0x44, 0x33, + 0x29, 0xD0, 0xDA, 0x09, 0x99, 0x87, 0xB9, 0x96, 0xC3, 0x73, 0x15, 0x92, 0xB4, 0x60, 0xC2, + 0xFF, 0xD2, 0x95, 0x5F, 0x75, 0x46, 0xF4, 0x21, 0x61, 0x00, 0xBA, 0x43, 0x18, 0x88, 0x03, + 0xFF, 0x9B, 0x36, 0x96, 0x96, 0x85, 0xF9, 0x09, 0xFA, 0x25, 0x39, 0x32, 0x3B, 0x8C, 0x8E, + 0xC1, 0xC0, 0x95, 0xA5, 0x08, 0x5E, 0x55, 0x4D, 0xD4, 0x50, 0xE0, 0xE6, 0x7A, 0xB6, 0x70, + 0xB6, 0xA1, 0x1E, 0xBF, 0x6C, 0x25, 0x52, 0x0F, 0xC1, 0x3E, 0x36, 0x40, 0x60, 0xF9, 0x1F, + 0x9B, 0x7F, 0x3D, 0x5C, 0xB4, 0x8F, 0xF2, 0x8B, 0x8F, 0xC8, 0x3D, 0x42, 0x93, 0xF1, 0xF3, + 0x5A, 0xD6, 0xFF, 0x6A, 0xE4, 0x57, 0x4A, 0xD7, 0xA1, 0xC6, 0x00, 0x5F, 0xC0, 0x38, 0x9A, + 0x7B, 0x21, 0x38, 0x6B, 0x08, 0x50, 0xA0, 0x5D, 0x83, 0x2F, 0xE6, 0xA1, 0x4B, 0xB2, 0xB1, + 0xDB, 0x1F, 0x8E, 0x20, 0xBD, 0x09, 0x17, 0x49, 0x46, 0xCD, 0x09, 0x8B, 0x81, 0xC8, 0xF7, + 0x97, 0xE9, 0x5F, 0x21, 0x43, 0xA9, 0x49, 0x77, 0x0C, 0xF1, 0x21, 0x9B, 0xFE, 0xF0, 0x39, + 0xDB, 0x51, 0xA8, 0x0F, 0xC2, 0x47, 0xF6, 0x5F, 0x41, 0x55, 0x4C, 0x71, 0x73, 0xDD, 0x80, + 0x5B, 0xA8, 0x2F, 0xDF, 0x47, 0xAB, 0x6D, 0x4B, 0xFD, 0x37, 0xDF, 0xE4, 0x6F, 0xC4, 0x79, + 0x04, 0x42, 0x1A, 0xE0, 0x0D, 0xC0, 0x05, 0xA2, 0x2F, 0x9C, 0x47, 0x84, 0xB0, 0xEA, 0x9E, + 0x66, 0x53, 0x92, 0xA4, 0x12, 0x24, 0x50, 0x16, 0xD5, 0xC6, 0xD7, 0x67, 0x3A, 0x6A, 0x18, + 0x0D, 0x22, 0x8D, 0x42, 0x55, 0xA5, 0x38, 0xE4, 0x51, 0xFF, 0xD8, 0xB4, 0x14, 0xD4, 0x03, + 0x04, 0xC0, 0xC8, 0x88, 0x99, 0x2E, 0x0A, 0xB6, 0xDE, 0x16, 0x02, 0x10, 0x95, 0x27, 0x41, + 0x7B, 0xC1, 0xC7, 0xEB, 0x78, 0x2A, 0xE7, 0x7A, 0x8C, 0x3C, 0xDF, 0xC1, 0xD1, 0x3A, 0x1E, + 0x87, 0x42, 0x07, 0x89, 0x82, 0x64, 0xE3, 0x80, 0x80, 0x24, 0x31, 0x09, 0xC5, 0x96, 0x96, + 0x49, 0xAC, 0x83, 0x83, 0x41, 0x7E, 0x92, 0x2B, 0xA1, 0x15, 0x33, 0x11, 0x42, 0xD0, 0xED, + 0x35, 0x44, 0x0B, 0x15, 0xD4, 0x0B, 0xEE, 0x0C, 0xF5, 0x8A, 0xF3, 0x7C, 0x0F, 0x05, 0x24, + 0xFF, 0xAC, 0x1C, 0x71, 0xCE, 0xED, 0x3B, 0xB8, 0x2F, 0x76, 0xAB, 0x10, 0x8A, 0x8A, 0xD1, + 0xA0, 0xC8, 0xB7, 0x8D, 0x93, 0x41, 0x14, 0x8C, 0x64, 0x23, 0x69, 0xBE, 0x7B, 0xEF, 0x59, + 0xD4, 0x6F, 0x49, 0xD7, 0x0C, 0x83, 0x56, 0x06, 0x07, 0xF1, 0x40, 0x84, 0x8E, 0xC9, 0xA7, + 0x60, 0x7D, 0x4A, 0x08, 0xF8, 0xB6, 0xE4, 0x44, 0x7F, 0x55, 0x23, 0xF4, 0x16, 0x98, 0x18, + 0x88, 0xA8, 0xDE, 0x96, 0x47, 0xFF, 0xEF, 0x79, 0x38, 0x9E, 0x49, 0x83, 0xE5, 0xC9, 0x38, + 0x76, 0x98, 0xD0, 0xCC, 0x2D, 0x42, 0x93, 0x22, 0x36, 0x5C, 0xE7, 0xE7, 0xB5, 0xFD, 0x6D, + 0x6E, 0xB9, 0x21, 0xC8, 0x13, 0xFC, 0xF0, 0x61, 0x99, 0xFE, 0x1C, 0xA4, 0x1E, 0x9C, 0xFE, + 0x03, 0xB5, 0x39, 0xF3, 0x21, 0x67, 0x1A, 0x2A, 0xCA, 0xD0, 0x96, 0x3F, 0x87, 0x6F, 0x9D, + 0xB7, 0xA1, 0xC4, 0x37, 0x1B, 0x9F, 0x10, 0x10, 0x05, 0x21, 0x79, 0x95, 0xB5, 0xB6, 0xA4, + 0x09, 0x76, 0x24, 0x6D, 0x24, 0x5D, 0xA6, 0x03, 0xDB, 0xA8, 0xDA, 0xC8, 0x12, 0xA5, 0x48, + 0x0C, 0x34, 0x76, 0xA9, 0x9D, 0x0F, 0xFD, 0xF0, 0xEF, 0x94, 0x3D, 0x72, 0xD9, 0x12, 0x54, + 0x31, 0x48, 0xB2, 0xFE, 0x78, 0xE8, 0xB0, 0x15, 0x93, 0x24, 0xFE, 0x9B, 0xCD, 0x4C, 0xED, + 0x33, 0xCD, 0x21, 0x2F, 0xE4, 0xF3, 0xDF, 0xD6, 0xD4, 0xC5, 0xE1, 0x95, 0x8B, 0xEB, 0x95, + 0xAC, 0x6B, 0x53, 0x3A, 0xCE, 0x3E, 0x78, 0x01, 0x5E, 0x38, 0x80, 0xB5, 0x2B, 0xF4, 0x52, + 0x99, 0x26, 0x3A, 0x4C, 0x00, 0x96, 0xF8, 0xBA, 0x5F, 0xE3, 0xA6, 0x29, 0x8C, 0xAB, 0x67, + 0x5C, 0xB7, 0xF3, 0x82, 0xE7, 0xEF, 0x49, 0x72, 0x0E, 0xB4, 0xCF, 0x47, 0x37, 0x6E, 0x2D, + 0x25, 0x74, 0x12, 0x2C, 0xCF, 0x91, 0x12, 0x9C, 0x85, 0x8E, 0x94, 0x89, 0x04, 0xFE, 0xCE, + 0xFB, 0x91, 0x22, 0x6E, 0xD4, 0x24, 0x03, 0xBA, 0x12, 0xDD, 0x32, 0x58, 0x90, 0x9A, 0x87, + 0xDF, 0xCB, 0xF6, 0x5C, 0xC3, 0xAD, 0xC3, 0xD9, 0x8D, 0x27, 0x7F, 0xDC, 0xEC, 0x76, 0x64, + 0xE2, 0x29, 0x2B, 0x7D, 0x27, 0xAF, 0xBB, 0x5A, 0xAF, 0xB4, 0x05, 0xC2, 0x0A, 0x34, 0xB2, + 0xFE, 0x2C, 0x08, 0x49, 0xEE, 0x28, 0x0B, 0xB8, 0x91, 0xDF, 0xDF, 0x59, 0xF1, 0x9B, 0x89, + 0xB0, 0x24, 0x63, 0x58, 0xDB, 0x54, 0xCF, 0x3F, 0xDC, 0x66, 0xEA, 0xAA, 0xA7, 0x50, 0xC8, + 0x90, 0x3F, 0x1D, 0x42, 0x67, 0x8F, 0x3E, 0xDF, 0x0B, 0x75, 0x30, 0x41, 0x0A, 0xA8, 0x81, + 0xBC, 0x61, 0x7F, 0x94, 0x34, 0x63, 0x79, 0x85, 0x4A, 0xF4, 0x53, 0x2E, 0x61, 0xF6, 0x5A, + 0xAE, 0x75, 0x76, 0xC3, 0x5F, 0xAF, 0x55, 0xE1, 0x55, 0xBD, 0x67, 0x87, 0xB4, 0x63, 0x4D, + 0x54, 0x19, 0x19, 0x07, 0xE1, 0x55, 0xC2, 0x39, 0xE6, 0x84, 0x80, 0xCD, 0xFA, 0x0C, 0x87, + 0x05, 0x4B, 0xFB, 0x62, 0x85, 0x5F, 0x40, 0x9A, 0x20, 0xD5, 0x33, 0x5F, 0xB1, 0x23, 0xE6, + 0x81, 0xE6, 0x4E, 0xC8, 0x47, 0xCD, 0x98, 0x5B, 0x60, 0x62, 0x05, 0x9F, 0x43, 0x6A, 0xEB, + 0xAC, 0x62, 0x3C, 0x03, 0x8B, 0x6C, 0x34, 0x05, 0xAC, 0x32, 0x51, 0x91, 0xA8, 0xD1, 0x12, + 0x6A, 0x5E, 0xF8, 0xF3, 0x8C, 0xCC, 0xBF, 0x14, 0x4A, 0x5C, 0x32, 0x4C, 0x1E, 0x09, 0x3C, + 0xF9, 0x9E, 0xFB, 0xE1, 0x0C, 0xA0, 0x3D, 0x43, 0x9B, 0xCF, 0xB8, 0xBA, 0x5E, 0x29, 0x3B, + 0x7D, 0x31, 0x88, 0x37, 0xF7, 0xBC, 0x42, 0xA9, 0x99, 0x64, 0x39, 0x23, 0x69, 0xDA, 0x76, + 0xE7, 0x9D, 0x71, 0xD1, 0xA2, 0xC2, 0x48, 0xA1, 0x13, 0x24, 0xA8, 0x7A, 0xE1, 0xE3, 0xCB, + 0xEA, 0xB6, 0xFB, 0x0D, 0x0B, 0xCA, 0xE1, 0xEF, 0x55, 0xE4, 0x3D, 0xFB, 0x6F, 0x1B, 0x4C, + 0xFB, 0x82, 0xC7, 0xA7, 0x78, 0xFB, 0x82, 0x8A, 0x37, 0x27, 0xEF, 0x07, 0x68, 0x5F, 0xE3, + 0x8A, 0x74, 0xB3, 0xDD, 0x25, 0xD0, 0x15, 0x32, 0x2C, 0x2D, 0x9F, 0x24, 0x5C, 0x08, 0xD8, + 0xC2, 0xB4, 0x38, 0x65, 0x69, 0x42, 0x33, 0x78, 0x2E, 0xB7, 0x34, 0x43, 0x6C, 0x4E, 0xDD, + 0xEF, 0x54, 0x06, 0x20, 0x8D, 0x6C, 0x45, 0x72, 0xC7, 0x37, 0x12, 0x62, 0xFE, 0x02, 0x31, + 0x9C, 0xFB, 0xBC, 0xF2, 0xE2, 0x3B, 0xED, 0x8A, 0xA9, 0x69, 0xD1, 0xAE, 0x6F, 0x5F, 0x25, + 0xFF, 0x6B, 0x8E, 0xBC, 0xF0, 0x92, 0x50, 0x66, 0xF7, 0x61, 0xA3, 0x9B, 0xBF, 0xF4, 0x9F, + 0x0C, 0x8D, 0xBC, 0x3B, 0xE8, 0x4F, 0x0C, 0x44, 0x2B, 0x04, 0x4E, 0xA0, 0x1B, 0x66, 0x97, + 0x47, 0xE3, 0xC8, 0x29, 0x3C, 0xFE, 0x9C, 0xCD, 0xF2, 0xEF, 0x06, 0x3A, 0xE3, 0xD2, 0x8D, + 0x10, 0x72, 0x0C, 0x27, 0x9A, 0x26, 0x91, 0x61, 0x6A, 0xBD, 0x23, 0xB0, 0x55, 0xCF, 0xC6, + 0xC5, 0x62, 0x12, 0x5D, 0xF4, 0xAD, 0x0F, 0xA6, 0x63, 0x13, 0x04, 0x97, 0x2D, 0xDC, 0x36, + 0x74, 0xB1, 0xAA, 0xA7, 0x66, 0x5B, 0xF6, 0x21, 0x32, 0x0D, 0x83, 0xEA, 0xC8, 0xD5, 0xB3, + 0x71, 0xD7, 0xD7, 0x19, 0x82, 0x9F, 0x58, 0xB2, 0x34, 0x58, 0x18, 0x25, 0x58, 0x71, 0x0D, + 0xE3, 0x1D, 0x81, 0xEF, 0x9A, 0x47, 0xD8, 0x83, 0x9C, 0x79, 0x64, 0x0B, 0x20, 0x25, 0xD1, + 0x96, 0x5A, 0x41, 0x8B, 0xC9, 0x0E, 0x41, 0x15, 0xF1, 0x42, 0x33, 0x11, 0xA8, 0xB6, 0x4F, + 0xCD, 0xE0, 0xF2, 0xD2, 0x14, 0x5E, 0xE5, 0x35, 0xB0, 0x93, 0x1B, 0x84, 0xBC, 0x81, 0x10, + 0x44, 0x5F, 0x2F, 0xF6, 0x8D, 0x13, 0x6E, 0xD7, 0x09, 0xDD, 0xB7, 0xEA, 0x9F, 0xF7, 0x5F, + 0x3B, 0x4E, 0x8B, 0x4F, 0x83, 0x62, 0x30, 0xCA, 0x9E, 0x81, 0x06, 0x94, 0x77, 0xF6, 0x34, + 0xE0, 0x72, 0x70, 0xAF, 0x60, 0xEF, 0x96, 0xF7, 0x25, 0x57, 0xA0, 0x81, 0xD6, 0x64, 0xAB, + 0xCF, 0x35, 0x54, 0x8F, 0x69, 0x94, 0x84, 0x65, 0x3D, 0xA6, 0x45, 0x48, 0x3F, 0xF2, 0xBF, + 0x59, 0x98, 0x61, 0x7A, 0xE8, 0xBF, 0xA6, 0x2D, 0x56, 0xE7, 0x14, 0xF3, 0xC0, 0x13, 0x6E, + 0x50, 0x35, 0xA3, 0xF7, 0x8E, 0x06, 0xC2, 0xF4, 0x70, 0xDF, 0x7F, 0xD3, 0x38, 0x0D, 0x14, + 0x03, 0x3F, 0x81, 0xE2, 0xAA, 0xE6, 0xB4, 0xD9, 0x04, 0x87, 0xDA, 0xB7, 0x6B, 0x9B, 0x3B, + 0x87, 0x61, 0xFB, 0x56, 0xC3, 0x6F, 0x54, 0x29, 0xDA, 0x3D, 0x43, 0x46, 0xCB, 0x22, 0xE6, + 0x41, 0xAD, 0x8D, 0x7D, 0x2D, 0x80, 0xFA, 0x24, 0x0D, 0x4E, 0x01, 0x54, 0xE6, 0xB3, 0xD2, + 0xF1, 0xB3, 0xEF, 0x6C, 0xF1, 0x74, 0xC0, 0x8D, 0x06, 0x2F, 0x57, 0x5C, 0x83, 0xA4, 0x07, + 0x81, 0x74, 0xF8, 0x74, 0x36, 0x4D, 0xF3, 0x6A, 0x63, 0x28, 0xBE, 0xEE, 0xF6, 0x9B, 0xA7, + 0xF9, 0x0E, 0x1D, 0xF9, 0xFD, 0xCE, 0xC9, 0xA2, 0xF1, 0x5E, 0xBF, 0x04, 0xFA, 0x7D, 0x67, + 0x56, 0xDA, 0x2E, 0x5A, 0x59, 0xC9, 0xCB, 0xBC, 0xBC, 0x39, 0x7D, 0x6F, 0xB2, 0x8D, 0x0F, + 0xC9, 0xA6, 0x05, 0x34, 0xDF, 0xF0, 0x75, 0x27, 0x16, 0xED, 0x07, 0x9A, 0xD1, 0xAB, 0x19, + 0xA2, 0x24, 0xD1, 0xC8, 0xAE, 0x8A, 0x53, 0x24, 0x2F, 0xD1, 0x64, 0x98, 0x9F, 0xF9, 0x97, + 0x48, 0x9B, 0x65, 0x20, 0xEB, 0x3C, 0x0E, 0x97, 0xF4, 0xBC, 0xC1, 0xA9, 0xC3, 0xCB, 0xD4, + 0x4F, 0x00, 0x8C, 0x03, 0xEF, 0x52, 0xCF, 0x7E, 0x62, 0x68, 0x81, 0xD2, 0x46, 0x92, 0x5E, + 0x03, 0x36, 0xC0, 0xAC, 0x66, 0x88, 0x67, 0xF8, 0x53, 0xDA, 0x78, 0x20, 0xF9, 0x14, 0x11, + 0x5A, 0x7C, 0x77, 0xAC, 0x31, 0xB6, 0x6F, 0x46, 0xFB, 0xF9, 0x7F, 0x66, 0xFA, 0x26, 0x41, + 0x6F, 0xC4, 0x58, 0x1D, 0x45, 0x9A, 0x4F, 0x24, 0x62, 0xD5, 0x2C, 0xF0, 0xC7, 0x9B, 0x27, + 0x89, 0x55, 0xAA, 0x73, 0xE8, 0xFA, 0x56, 0xE3, 0xC3, 0x20, 0xF5, 0x16, 0xBC, 0xC5, 0x4C, + 0x97, 0xE5, 0x87, 0x19, 0x9C, 0x15, 0xAB, 0x95, 0x3C, 0xC3, 0x71, 0x89, 0xB8, 0x1C, 0x70, + 0xCA, 0xBB, 0x25, 0x59, 0xE4, 0x45, 0xBC, 0xC9, 0xD8, 0x17, 0x4A, 0xD7, 0x57, 0x4E, 0x9A, + 0xCB, 0x02, 0xF4, 0x3E, 0x0C, 0x34, 0xFF, 0x5E, 0x67, 0x46, 0xEE, 0x73, 0x0A, 0xD4, 0x1F, + 0xF8, 0xEE, 0xF9, 0x3C, 0x20, 0x71, 0xC2, 0x64, 0x90, 0x63, 0xDD, 0x92, 0xF3, 0x43, 0xC0, + 0x6E, 0xF6, 0xAB, 0xAF, 0x98, 0xF2, 0x8D, 0x98, 0xD9, 0x68, 0x07, 0x1C, 0x12, 0xCC, 0x10, + 0xA9, 0x0C, 0x22, 0xD8, 0xB3, 0xB3, 0x48, 0x0C, 0x76, 0xF7, 0xA5, 0x1B, 0x7E, 0xC5, 0x94, + 0xB3, 0x43, 0x5D, 0x2E, 0x3D, 0x77, 0x9C, 0x1A, 0x15, 0x03, 0x76, 0x97, 0xF3, 0xA0, 0x58, + 0x65, 0x04, 0x72, 0xE4, 0x7E, 0xEC, 0xD5, 0xF3, 0x2E, 0xB3, 0x24, 0x3A, 0x51, 0x6F, 0x0E, + 0x70, 0x3F, 0x98, 0x88, 0xC8, 0x46, 0x90, 0x75, 0x06, 0x48, 0xD6, 0xA9, 0xA8, 0x76, 0xBF, + 0x1F, 0x35, 0x3D, 0xB6, 0x89, 0x1D, 0xC6, 0xD3, 0x17, 0xD6, 0xE8, 0x7A, 0xC0, 0x88, 0xF4, + 0x2B, 0x5F, 0x6F, 0x20, 0xD7, 0x99, 0xEC, 0xE4, 0xFA, 0x7A, 0xAA, 0x92, 0x8D, 0x2A, 0xC7, + 0x95, 0xE8, 0xDE, 0x83, 0xD1, 0xE1, 0xC7, 0xFA, 0x2F, 0x9A, 0x41, 0x06, 0x69, 0x3E, 0x98, + 0x1C, 0x21, 0xC6, 0x3B, 0x32, 0x21, 0xC4, 0xFA, 0x26, 0x49, 0xF4, 0x5F, 0x0C, 0x6E, 0x05, + 0xDB, 0xF2, 0x40, 0x11, 0xAF, 0x16, 0xAB, 0x2E, 0x5F, 0xE9, 0x4A, 0x64, 0x0B, 0x48, 0x59, + 0x88, 0x03, 0x7E, 0xBE, 0x1E, 0x8A, 0xD0, 0xB2, 0x62, 0x3D, 0x95, 0xE9, 0x94, 0x7F, 0x07, + 0x26, 0x12, 0x1D, 0x78, 0x28, 0x61, 0x4E, 0x3B, 0x2D, 0x77, 0xA7, 0xA1, 0xF9, 0xA9, 0x38, + 0xBE, 0xA9, 0xA1, 0xA7, 0xA2, 0x62, 0x7B, 0x7D, 0x2E, 0x35, 0x8C, 0x42, 0xCC, 0xC6, 0xC0, + 0xB8, 0x0A, 0x15, 0xA1, 0xC2, 0xF6, 0xE9, 0xAA, 0xF0, 0x49, 0x5B, 0xDB, 0x7B, 0xB8, 0xD4, + 0xB0, 0xE2, 0x8A, 0x1A, 0xB5, 0xAB, 0x93, 0xCA, 0x0F, 0xF3, 0xE3, 0xF9, 0x10, 0xC4, 0x90, + 0xC1, 0x34, 0x86, 0x85, 0x25, 0x34, 0xD5, 0xE1, 0x21, 0x60, 0x83, 0x5E, 0xC5, 0x91, 0x6C, + 0x5C, 0x68, 0x34, 0x9C, 0x4E, 0x2D, 0x8F, 0xA9, 0x56, 0xC6, 0x43, 0x27, 0x7E, 0xDD, 0x3B, + 0x6C, 0x81, 0xC8, 0x8C, 0x01, 0x04, 0x21, 0x70, 0x5F, 0xD3, 0x17, 0xFF, 0x9E, 0x3C, 0x94, + 0xDF, 0x0E, 0xD5, 0x30, 0x5F, 0x53, 0x0A, 0xCB, 0xCC, 0xF8, 0xDD, 0x0E, 0x87, 0x14, 0x0C, + 0xD3, 0x81, 0x52, 0x66, 0x4A, 0x57, 0x2C, 0x16, 0x8C, 0xD7, 0x25, 0x95, 0xB7, 0xFA, 0xC2, + 0x43, 0xC0, 0x3F, 0x3F, 0xB3, 0x3E, 0xF7, 0x4A, 0x28, 0xC0, 0xE4, 0x46, 0x9F, 0x94, 0x58, + 0x7C, 0x13, 0x70, 0x4E, 0x9E, 0xFE, 0x80, 0x10, 0xB2, 0x12, 0x5A, 0xCA, 0x78, 0xC2, 0x2C, + 0x33, 0xC8, 0x23, 0x66, 0xE1, 0xA7, 0xC4, 0x02, 0x8C, 0x2A, 0xE2, 0xE8, 0xD2, 0x6E, 0x1A, + 0x57, 0xE4, 0x29, 0x7F, 0xAC, 0x98, 0x7F, 0x84, 0xA0, 0xA2, 0x7F, 0x42, 0xB4, 0xC9, 0x3A, + 0x4F, 0x4D, 0x14, 0x56, 0x98, 0x24, 0xB0, 0x88, 0x0F, 0xB6, 0x74, 0x07, 0xED, 0x58, 0xF2, + 0x67, 0xAC, 0x40, 0x3A, 0xA0, 0xB1, 0xF9, 0x37, 0x84, 0xB4, 0xB4, 0xC6, 0x70, 0x36, 0x03, + 0x7E, 0x60, 0xD5, 0x80, 0x72, 0x61, 0x1B, 0x0E, 0x90, 0xCA, 0x31, 0x69, 0x76, 0xEF, 0x4E, + 0x0B, 0x30, 0x2C, 0xDA, 0xD1, 0xB6, 0xDC, 0xCA, 0x92, 0xEF, 0xB8, 0xE1, 0xF6, 0xBE, 0x23, + 0x97, 0x96, 0x75, 0x08, 0xBE, 0x2C, 0x02, 0xA2, 0x5E, 0xD0, 0x38, 0x0B, 0xA1, 0xF7, 0x95, + 0x5F, 0x85, 0x7C, 0x8F, 0xB0, 0x43, 0x29, 0x77, 0x80, 0xD1, 0x36, 0xB2, 0xB0, 0x64, 0x04, + 0x0C, 0x8E, 0x55, 0x14, 0x3D, 0x71, 0x5E, 0xA9, 0x97, 0xE1, 0x34, 0xED, 0x97, 0x3C, 0x98, + 0xEF, 0x82, 0x78, 0x6F, 0x0C, 0xCF, 0x66, 0xC1, 0x7D, 0x86, 0x35, 0x42, 0x18, 0x0C, 0x66, + 0xD5, 0x4D, 0x08, 0xE1, 0x16, 0xF2, 0xE3, 0x5D, 0x99, 0x5E, 0x21, 0x44, 0x89, 0xAD, 0x0F, + 0xAD, 0x7A, 0x55, 0xFE, 0x9E, 0xBC, 0x1A, 0x77, 0x7F, 0xE3, 0x41, 0x41, 0x14, 0x7C, 0x08, + 0x0B, 0x98, 0xD1, 0x34, 0x63, 0xA3, 0xBB, 0xC6, 0xFC, 0x82, 0xF2, 0xFC, 0x95, 0xF4, 0xDE, + 0x7B, 0x35, 0x91, 0xD9, 0xC8, 0xCD, 0x44, 0x16, 0x91, 0x7A, 0x43, 0x38, 0x09, 0x5D, 0x56, + 0x20, 0x10, 0x4B, 0x7B, 0xE1, 0x3F, 0x5A, 0x13, 0x1D, 0xD3, 0xF7, 0xAA, 0xD5, 0xB5, 0x59, + 0xD1, 0x1E, 0x81, 0x71, 0xDF, 0xB9, 0x1E, 0x2B, 0xB1, 0xE4, 0x7A, 0xC3, 0x81, 0x0B, 0x1C, + 0xDC, 0x1A, 0x1E, 0x37, 0x0C, 0x86, 0x7B, 0x7B, 0x7B, 0x50, 0xC4, 0x68, 0x8D, 0xCE, 0x54, + 0x57, 0x63, 0x15, 0x7E, 0x02, 0xF4, 0x7E, 0x1C, 0xC6, 0x61, 0xD5, 0xBF, 0x2F, 0xBC, 0x33, + 0x6C, 0xFA, 0xE0, 0x80, 0xAB, 0x15, 0x72, 0x8B, 0x1A, 0xB9, 0xDD, 0x19, 0x9F, 0x27, 0x79, + 0xD4, 0x51, 0xE6, 0x17, 0x89, 0x77, 0xFB, 0x65, 0x8C, 0x17, 0x34, 0x4C, 0xFF, 0xB7, 0xAA, + 0x3A, 0xF5, 0x79, 0x1A, 0x28, 0xFC, 0x8A, 0x08, 0x9C, 0x85, 0x18, 0x77, 0x53, 0xE5, 0xE3, + 0x13, 0xC8, 0xD1, 0xF0, 0xFE, 0x77, 0x55, 0xE2, 0x8B, 0xE4, 0x44, 0x42, 0x6A, 0x18, 0x9E, + 0x8B, 0xCE, 0x2D, 0x2F, 0x79, 0xDB, 0x31, 0xD4, 0xC3, 0xCA, 0x91, 0x1A, 0x83, 0x45, 0x55, + 0x25, 0x35, 0x5F, 0x95, 0xD1, 0x59, 0x35, 0x1C, 0xD7, 0x31, 0xA8, 0x8E, 0x55, 0x40, 0x38, + 0x51, 0x23, 0x6E, 0xE2, 0x12, 0x8F, 0x27, 0x9D, 0x5B, 0xE6, 0x44, 0xC0, 0x42, 0x45, 0x3A, + 0xE6, 0x5D, 0x9E, 0x9F, 0x3B, 0x40, 0xD6, 0xC8, 0x2B, 0xDE, 0xB0, 0x02, 0xAC, 0xDE, 0xE0, + 0x61, 0xEC, 0xCA, 0x3F, 0x2D, 0xCE, 0xAB, 0xEF, 0x9A, 0x90, 0x0E, 0x6E, 0x06, 0x3D, 0x56, + 0xAB, 0x39, 0xCB, 0x82, 0xDB, 0xC7, 0x7A, 0x46, 0x77, 0x57, 0x2D, 0x76, 0x16, 0xCD, 0x72, + 0xC0, 0xF6, 0xD5, 0xB9, 0xB9, 0x41, 0xDF, 0xDA, 0x1F, 0xE7, 0xC8, 0x96, 0xB8, 0xCC, 0x24, + 0xD6, 0x5A, 0x43, 0x22, 0xD7, 0x12, 0xA8, 0x4E, 0x94, 0xAD, 0xFC, 0x8E, 0xD0, 0xCC, 0x56, + 0xCC, 0x1A, 0xE9, 0x7F, 0x77, 0x5B, 0xD3, 0xCE, 0xA5, 0xB2, 0x0B, 0x52, 0x4D, 0x9A, 0x7A, + 0x91, 0x60, 0x56, 0xE1, 0x9A, 0xF0, 0x95, 0xD3, 0x01, 0x71, 0xE5, 0xE1, 0x4C, 0x7C, 0x99, + 0x8F, 0x78, 0xDC, 0x44, 0x84, 0x5E, 0xDF, 0x30, 0x73, 0x63, 0xEA, 0xB7, 0x91, 0x3F, 0x68, + 0x0A, 0x5E, 0x5A, 0x15, 0x40, 0xA6, 0xF9, 0x45, 0x50, 0x7F, 0xFA, 0x67, 0x59, 0x1F, 0x8D, + 0x1A, 0x29, 0x20, 0xAB, 0x3B, 0x6E, 0x75, 0x4E, 0x35, 0x37, 0x9D, 0xD6, 0x78, 0x70, 0xC2, + 0x42, 0x33, 0x5E, 0x27, 0x17, 0x90, 0x3F, 0xF3, 0xC6, 0x87, 0xE5, 0xC3, 0x3D, 0xC9, 0x53, + 0x41, 0x68, 0x65, 0xD5, 0xF2, 0x3B, 0xD7, 0x52, 0xE5, 0x54, 0x92, 0xB9, 0xD5, 0xD8, 0x88, + 0xD7, 0xB3, 0x7E, 0xF3, 0x3B, 0x0A, 0x67, 0x74, 0xD0, 0x52, 0xB0, 0x98, 0x7C, 0x06, 0x6A, + 0x2E, 0x01, 0x76, 0x72, 0x07, 0xAA, 0x7F, 0xBF, 0xC3, 0x93, 0xCA, 0x62, 0x87, 0x46, 0x13, + 0xDD, 0xE3, 0x79, 0x4F, 0x74, 0xFA, 0xDB, 0x5D, 0x55, 0xB8, 0x77, 0xB8, 0x77, 0xA6, 0x05, + 0x91, 0x8C, 0x81, 0x26, 0x10, 0xFB, 0xCA, 0xFA, 0xD7, 0x2E, 0xE2, 0x45, 0xE6, 0xDD, 0x87, + 0x21, 0x13, 0x8D, 0x6B, 0xD3, 0xF4, 0xEE, 0xDA, 0xD8, 0x53, 0xAE, 0xD1, 0xEC, 0x43, 0x7A, + 0xD0, 0x2A, 0xC9, 0x37, 0xC8, 0x0D, 0xAE, 0x26, 0xFA, 0x5F, 0x70, 0x08, 0x3B, 0xD3, 0x46, + 0x77, 0x9B, 0x77, 0x93, 0x87, 0xF7, 0xB3, 0xD2, 0xAA, 0xE5, 0x77, 0x70, 0xD8, 0x17, 0x79, + 0x28, 0x83, 0x32, 0x81, 0xCC, 0xB7, 0xA3, 0x8D, 0xA2, 0x48, 0x34, 0xFD, 0x97, 0x26, 0xFD, + 0x17, 0xEB, 0x60, 0x3C, 0xBA, 0x90, 0x41, 0xE8, 0x2B, 0xFE, 0xED, 0x0E, 0x33, 0x94, 0x2D, + 0xDE, 0x1D, 0x48, 0xC2, 0x71, 0xF5, 0xB3, 0x9A, 0xA7, 0x23, 0x0F, 0x41, 0xAF, 0xB8, 0x9D, + 0x36, 0xF7, 0x97, 0x6E, 0xEE, 0x4F, 0x51, 0xA0, 0x36, 0x74, 0x30, 0x31, 0xC5, 0x34, 0xF6, + 0x46, 0x85, 0xB9, 0x4C, 0x99, 0x0A, 0x93, 0xA5, 0x73, 0x7F, 0xE6, 0x28, 0xEE, 0x9C, 0xDA, + 0x8E, 0xD9, 0xC0, 0x8B, 0x11, 0xD3, 0x83, 0x6F, 0x83, 0x38, 0x35, 0xC4, 0x45, 0xB3, 0x17, + 0xA7, 0x7E, 0xAD, 0x75, 0x99, 0xD1, 0xA0, 0xC0, 0x88, 0x73, 0x01, 0x45, 0x10, 0xD3, 0x6B, + 0xB7, 0xFF, 0x5F, 0xB9, 0x61, 0x27, 0x75, 0x89, 0xEA, 0x48, 0xC3, 0x2A, 0x60, 0xC8, 0x7E, + 0xC4, 0x06, 0x81, 0xBE, 0x06, 0x7B, 0x17, 0x78, 0x5E, 0xC4, 0x48, 0x25, 0xBD, 0x89, 0xFA, + 0xA2, 0x52, 0x49, 0xE7, 0x35, 0xA6, 0x28, 0xB6, 0xEE, 0xBC, 0xC6, 0xCC, 0xE4, 0xE0, 0x31, + 0x4C, 0x62, 0x75, 0x88, 0x11, 0x8C, 0x40, 0xB2, 0xE0, 0xD4, 0x60, 0xD8, 0xD5, 0xCE, 0x35, + 0x8C, 0x56, 0x45, 0x8F, 0x36, 0x91, 0x4C, 0xA2, 0x03, 0xF5, 0xA5, 0x38, 0x1C, 0x6D, 0xEB, + 0x5A, 0x76, 0xBB, 0xC0, 0x8C, 0x40, 0xA8, 0x74, 0x37, 0xDA, 0x0D, 0x0B, 0x57, 0x17, 0x88, + 0xA0, 0x5E, 0x9F, 0x96, 0xD9, 0xBB, 0x77, 0x0D, 0xE8, 0xA0, 0xB1, 0xB9, 0x60, 0xFF, 0x2A, + 0x44, 0xA9, 0x64, 0xC9, 0xB7, 0x93, 0x98, 0x53, 0x74, 0x2E, 0x83, 0xCE, 0x8D, 0xEB, 0x79, + 0x19, 0x1B, 0x2D, 0x82, 0x45, 0x46, 0x55, 0xF2, 0x27, 0x07, 0x9D, 0xD8, 0xC5, 0xB0, 0x21, + 0x6C, 0x84, 0x70, 0xB8, 0xE1, 0xAC, 0x70, 0x52, 0x63, 0x01, 0xBB, 0xFA, 0x2B, 0xC4, 0xAD, + 0xCA, 0x68, 0xA7, 0x66, 0xCC, 0xB2, 0xA6, 0xE0, 0xEB, 0xF2, 0xE9, 0x99, 0x05, 0xBF, 0x52, + 0x42, 0x59, 0x0B, 0x01, 0x70, 0x38, 0x68, 0xB3, 0xFA, 0xF8, 0x41, 0xC1, 0x1C, 0x38, 0x3B, + 0xE1, 0x45, 0xA4, 0x0F, 0xEA, 0x63, 0x75, 0xE1, 0x8A, 0x01, 0x46, 0x8E, 0x45, 0x96, 0x03, + 0xB5, 0xEF, 0xDF, 0x8A, 0x4E, 0x9A, 0xBD, 0x17, 0x92, 0x80, 0xAE, 0x8B, 0x59, 0x47, 0xD7, + 0x8D, 0x2F, 0x0C, 0x4D, 0x37, 0x71, 0x5E, 0xAA, 0x42, 0xBC, 0x37, 0xCF, 0x87, 0x30, 0xE4, + 0x1F, 0xFB, 0xF9, 0x82, 0x6D, 0x46, 0x42, 0x4F, 0x29, 0x22, 0xA9, 0x60, 0x33, 0xCE, 0xFA, + 0xA8, 0xB4, 0xBB, 0xE4, 0xC8, 0xB8, 0x9D, 0x43, 0x50, 0x1F, 0xD5, 0x21, 0x1D, 0x53, 0x92, + 0xCA, 0x19, 0xA9, 0x8B, 0xA1, 0x27, 0xD9, 0x02, 0x5B, 0x5C, 0x6E, 0x86, 0xBA, 0x02, 0x44, + 0x71, 0x94, 0x05, 0x49, 0xA2, 0xB5, 0xD8, 0xE1, 0x49, 0x61, 0xC9, 0xDC, 0x19, 0x69, 0x6D, + 0xA1, 0xA5, 0xBF, 0xFD, 0x01, 0x03, 0x0D, 0x5E, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x09, 0x0F, 0x13, 0x1A, 0x1F, + ]; + + measure(|| { + let pk_expanded = MLDSA65PublicKeyExpanded::from(&mldsa65_pk); + if MLDSA65::verify_with_expanded_key(&pk_expanded, msg, None, &sig).is_ok() { + println!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa65_lowmemory_pk() -> bouncycastle::mldsa_lowmemory::MLDSA65PublicKey { + use bouncycastle::mldsa_lowmemory::MLDSA65PublicKey; + + 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, @@ -1871,22 +2936,11 @@ fn bench_mldsa65_verify() { 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(); - assert_eq!(sig.len(), MLDSA65_SIG_LEN); - - if MLDSA65::verify(&mldsa65_pk, msg, None, sig).is_ok() { - println!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + .unwrap() } fn bench_mldsa65_lowmemory_verify() { - use bouncycastle::hex; - use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SIG_LEN, MLDSA65PublicKey, MLDSATrait}; + use bouncycastle::mldsa_lowmemory::{MLDSA65, MLDSA65_SIG_LEN, MLDSATrait}; eprintln!("MLDSA65_lowmemory/Verify"); @@ -1906,24 +2960,779 @@ fn bench_mldsa65_lowmemory_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(&*hex::decode("48683d91978e31eb3dddb8b0473482d2b88a5f625949fd8f58a561e696bd4c27d05b38dbb2edf01e664efd81be1ea893688ce68aa2d51c5958f8bbc6eb4e89ee67d2c0320954d57212cac7229ff1d6eaf03928bd51511f8d88d847736c7de2730d5978e5410713160978867711bf5539a0bfc4c350c2be572baf0ee2e2fb16ccfea08028d99ac49aebb75937ddce111cdab62fff3cea8ba2233d1e56fbc5c5a1e726de63fadd2af016b119177fa3d971a2d9277173fce55b67745af0b7c21d597dbeb93e6a32f341c49a5a8be9e825088d1f2aa45155d6c8ae15367e4eb003b8fdf7851071949739f9fff09023eaf45104d2a84a45906eed4671a44dc28d27987bb55df69e9e8561f61a80a72699503865fed9b7ee72a8e17a19c408144f4b29afef7031c3a6d8571610b42c9f421245a88f197e16812b031159b65b9687e5b3e934c5225ae98a79ba73d2b399d73510effad19e53b8450f0ba8fce1012fd98d260a74aaaa13fae249a006b1c34f5ba0b882f26378222fb36f2283c243f0ffeb5f1bb414a0a70d55e3d40a56b6cbc88ae1f03b7b2882d98deea28e145c9dedfd8eaf1cef2ed94a8b050f8964f46d1ea0d0c2a43e0dda6182adbf4f6ed175b6742257859bf22f3a417ecf1f9d89317b5e539d587af16b9e1313e04514ffa64ba8b3ff2b8321f8811cb3fb022c8f644e70a4b80a2fbfee604abb7379091ea8e6c5c74dfc0283666b40c0793870028204a136bf5da9568eb798d349038bdb0c11e03445e7847cb5069c75cf28ac601c7799d958210ddbcb226e51afef9f1de47b073873d6d3f97456bede085082e74a298b2cd48f4b3093155f366c8fa601c6af858dfa32c08491b2a29887f90335949a5d6edaa679882a3a95d6bf6d970a221f4b9d3d8cbf384af81aac95e2b3294e04789ac83727a5dc04559f96af41d8a053516feeeebc52746eb6ab2819e09108710d835f011fa63065872ad334d5cdffb2b2310507e92fc993ae317da97f4f309cdaf0f67ed99d90215576083849f953b246d7fedb3fdb67679850a5ad404e64147fb7cf4f6aeddd05afb4b834968d1fe88014960dce5d942236526e12a478d69e5fbe6970310b308c06845018cfc7b2ab430a13a6b1ac7bb02cccbb3d911ac2f11068613fbe029bfdce02cf5cd38950ed72c83944edfbc75615af87f864c051f3c55456c5412863a40c06d1dab562bdff0571b8d3c3917bbd300880bba5e998239b95fa91b7d6416d4f398b3adbcd30983ed3592b4d9ef7d4236fd00f50d98aa53a235ac4172720f77d96172672980cfe8ff7a5a702783edc2ba31b2259015a112fc7f468a9c2f9464039002d30ef678b4cb798bc116216bf7a9a7c18ba03b7b58fd07515d3115049d3614be7a07e744300750df1d2c58753389059eafc3d785ccdd31c07648bedc03a5c3b8ad46d064d59c13d57374729fc4e295362e2a5191204530428bc1522afa28ff5fe1655e304ca5bc8c27ad0e0c6a39dd4df28956c14b38cc93682cefe402bbd5e82d29c464e44eb5d37b48fc568dfe0cc6e8e16baea05e5135590f19294e73e8367b0216dbb815030b9de55913f08039c42351c59e5515dd5af8e089a15e625e8f6dee639386c46497d7a263288774de581a7de9629b41b4424141f978fb8331208efdec3c6e0de39bc57063f3dcd6c470373c08891ea29cbc7cc6d6483b8889083ace86aa7b51b1c2cfe6e2ad18d97ce36fbc56ea42fae97e6a7ac114864478c366df1ebb1e7b11a9098504fd5975bdf1f49dc70002b63c1739a9d263fbad4073f6a9f6c2b8af4b4c332a103a0cffa5deeb2d062ca3c215fd360026be7c5164f4a4424ef74948804d66f46487732c8202c795478647b4ea71d627c086024cca354a41f0877b38f19b3774ad2095c8da53b069e21c76ae2d2007e16719ed40080d334f7da52e9f5a5990439caf083a95b833f02ad10a08c1a6d0f260c007285bd4a2f47703a5aef465287d253b18ac22514316210ff566814b10f87a293d6f199d3c3959990d0c1268b4f50d5f9fcefbbf237bd0c28b80182d6659741f14f10bfbb21bba12ab620aa2396f56c0686b4ea9017990224216b2fe8ad76c4a9148eef9a86a3635a6aa77bc1dcfb6fba59a77dfda9b7530dc0ca8648c8d973738e01bab8f08b4905e84aa4641bd602410cd97520265f2f231f2b35e15eb2fa04d2bd94d5a77abaf1e0e161010a990087f5b46ea988b2bc0512fda0fa923dadd6c45c5301d09483673265b5ab2e10f4ba520f6bbad564a5c3d5e27bdb080f7d20e13296a3181954c39c649c943ebe17df5c1f7aae0a8fe126c477585a5d4d648a0d008b6af5e8cd31be69a9296d4f3fd25ed86f221e4b93f65f5929967533624b9235750c30707550b58536d109a7131c5a5bbe4a5715567c12534aec7660761eebb9fae2891c774589b80e566ad557ddef7367196b7227ea9870ef09ddfec79d6b9319a6879b5205d76bf7aba5acf33afb59d17fc54e68383d6be5a08e9b66da53dcde008bb294b8582bd132cdcc49959fdbc21e52721880c8ad0352c79f03a43bbd84c4cdfdc6c529005e1e7cd9a349a7168a35569ba5dea818968d5a91466bd6e64e20bf62417198afc4e81c28dd77ed4028232398b52fbde86bc84f475b9016710ce2aabc11a06b4dbac901ec16cf365ca3f2d53813948a693a0f93e79c46ca5d5a6dca3d28ca50ad18bd13fca55059dd9b185f79f9c47196a4e81b2104bc460a051e02f2e8444f").unwrap()).unwrap(); - let sig = &*hex::decode("9061f15cbf2092f744fbcd799eb02414053c1b0f7412124bedc41cf9a3db0166469e874037d7f081e5f8d3d2033a0307d1c49ed01fe64578c4a6fabd80880cdf1911848f184d4bcf536ca795a0fb1aa19ab7ee3ba6b58bd64bbeac9f58650fff1ef5a97ab6916df962072e20e7c6be96090e3a781a504bc4442bd8889a0aa628907a74299f39fa836031f1bd68355bebe7ae93c1e361a9efbed1325d96227070461fcd6f151b8669d9229b977d9ee51fd2260c3e4a2e820416f9e074958dc3b3e2217e6312b7e0b582a048981cf6579f4bc7715b78c808e4c57e3b8aa38b05c04fcedf209f52c1e331ae83dbdff60ba450a17cc397568e54bc3f16ddf30b92747ce460d925b9be20a1d35e2aed97f124af2616a5361df28ba30e522dd08fa00fd28d1ac484d756a89e3a442fefe8332c56cd2a9fde691bdbda43f1cc54cef57bead96120b50c7d4695bdbb1303cc5ddda898e4eeb83083176e40e0232cdd1c3150371df05d6fdad7e1164d90393cf308e99edfeb31fed263e2866ee3b7f3937b399c974d87ba7b489efe3c9b80371d2928446adc31991ab0cefaaa080575b9ec81cfa133a9911c035a8058d0d3f2e34de4a9fb009bb4ccdb16de7b908574a7496725ff857556c1b33917e986c80f1014a9e3083add2fb35f345c5d06159e443329d0da099987b996c3731592b460c2ffd2955f7546f4216100ba43188803ff9b36969685f909fa2539323b8c8ec1c095a5085e554dd450e0e67ab670b6a11ebf6c25520fc13e364060f91f9b7f3d5cb48ff28b8fc83d4293f1f35ad6ff6ae4574ad7a1c6005fc0389a7b21386b0850a05d832fe6a14bb2b1db1f8e20bd09174946cd098b81c8f797e95f2143a949770cf1219bfef039db51a80fc247f65f41554c7173dd805ba82fdf47ab6d4bfd37dfe46fc47904421ae00dc005a22f9c4784b0ea9e665392a412245016d5c6d7673a6a180d228d4255a538e451ffd8b414d40304c0c888992e0ab6de1602109527417bc1c7eb782ae77a8c3cdfc1d13a1e874207898264e38080243109c5969649ac8383417e922ba115331142d0ed35440b15d40bee0cf58af37c0f0524ffac1c71ceed3bb82f76ab108a8ad1a0c8b78d9341148c642369be7bef59d46f49d70c83560607f140848ec9a7607d4a08f8b6e4447f5523f416981888a8de9647ffef79389e4983e5c9387698d0cc2d429322365ce7e7b5fd6d6eb921c813fcf06199fe1ca41e9cfe03b539f321671a2acad0963f876f9db7a1c4371b9f101005217995b5b6a40976246d245da603dba8dac812a5480c3476a99d0ffdf0ef943d72d912543148b2fe78e8b0159324fe9bcd4ced33cd212fe4f3dfd6d4c5e1958beb95ac6b533ace3e78015e3880b52bf45299263a4c0096f8ba5fe3a6298cab675cb7f382e7ef49720eb4cf47376e2d2574122ccf91129c858e948904fecefb91226ed42403ba12dd3258909a87dfcbf65cc3adc3d98d277fdcec7664e2292b7d27afbb5aafb405c20a34b2fe2c0849ee280bb891dfdf59f19b89b0246358db54cf3fdc66eaaaa750c8903f1d42678f3edf0b7530410aa881bc617f94346379854af4532e61f65aae7576c35faf55e155bd6787b4634d54191907e155c239e68480cdfa0c87054bfb62855f409a20d5335fb123e681e64ec847cd985b6062059f436aebac623c038b6c3405ac325191a8d1126a5ef8f38cccbf144a5c324c1e093cf99efbe10ca03d439bcfb8ba5e293b7d318837f7bc42a99964392369da76e79d71d1a2c248a11324a87ae1e3cbeab6fb0d0bcae1ef55e43dfb6f1b4cfb82c7a778fb828a3727ef07685fe38a74b3dd25d015322c2d9f245c08d8c2b43865694233782eb734436c4eddef5406208d6c4572c7371262fe02319cfbbcf2e23bed8aa969d1ae6f5f25ff6b8ebcf0925066f761a39bbff49f0c8dbc3be84f0c442b044ea01b669747e3c8293cfe9ccdf2ef063ae3d28d10720c279a2691616abd23b055cfc6c562125df4ad0fa6631304972ddc3674b1aaa7665bf621320d83eac8d5b371d7d719829f58b23458182558710de31d81ef9a47d8839c79640b2025d1965a418bc90e4115f1423311a8b64fcde0f2d2145ee535b0931b84bc8110445f2ff68d136ed709ddb7ea9ff75f3b4e8b4f836230ca9e81069477f634e07270af60ef96f72557a081d664abcf35548f699484653da645483ff2bf5998617ae8bfa62d56e714f3c0136e5035a3f78e06c2f470df7fd3380d14033f81e2aae6b4d90487dab76b9b3b8761fb56c36f5429da3d4346cb22e641ad8d7d2d80fa240d4e0154e6b3d2f1b3ef6cf174c08d062f575c83a4078174f874364df36a6328beeef69ba7f90e1df9fdcec9a2f15ebf04fa7d6756da2e5a59c9cbbcbc397d6fb28d0fc9a60534dff0752716ed079ad1ab19a224d1c8ae8a53242fd164989ff997489b6520eb3c0e97f4bcc1a9c3cbd44f008c03ef52cf7e626881d246925e0336c0ac668867f853da7820f914115a7c77ac31b66f46fbf97f66fa26416fc4581d459a4f2462d52cf0c79b278955aa73e8fa56e3c320f516bcc54c97e587199c15ab953cc37189b81c70cabb2559e445bcc9d8174ad7574e9acb02f43e0c34ff5e6746ee730ad41ff8eef93c2071c2649063dd92f343c06ef6abaf98f28d98d968071c12cc10a90c22d8b3b3480c76f7a51b7ec594b3435d2e3d779c1a15037697f3a058650472e47eecd5f32eb3243a516f0e703f9888c84690750648d6a9a876bf1f353db6891dc6d317d6e87ac088f42b5f6f20d799ece4fa7aaa928d2ac795e8de83d1e1c7fa2f9a4106693e981c21c63b3221c4fa2649f45f0c6e05dbf24011af16ab2e5fe94a640b485988037ebe1e8ad0b2623d95e9947f0726121d7828614e3b2d77a7a1f9a938bea9a1a7a2627b7d2e358c42ccc6c0b80a15a1c2f6e9aaf0495bdb7bb8d4b0e28a1ab5ab93ca0ff3e3f910c490c13486852534d5e12160835ec5916c5c68349c4e2d8fa956c643277edd3b6c81c88c010421705fd317ff9e3c94df0ed5305f530acbccf8dd0e87140cd38152664a572c168cd72595b7fac243c03f3fb33ef74a28c0e4469f94587c13704e9efe8010b2125aca78c22c33c82366e1a7c4028c2ae2e8d26e1a57e4297fac987f84a0a27f42b4c93a4f4d14569824b0880fb67407ed58f267ac403aa0b1f93784b4b4c67036037e60d58072611b0e90ca316976ef4e0b302cdad1b6dcca92efb8e1f6be2397967508be2c02a25ed0380ba1f7955f857c8fb043297780d136b2b064040c8e55143d715ea997e134ed973c98ef82786f0ccf66c17d863542180c66d54d08e116f2e35d995e214489ad0fad7a55fe9ebc1a777fe34141147c080b98d13463a3bbc6fc82f2fc95f4de7b3591d9c8cd4416917a4338095d5620104b7be13f5a131dd3f7aad5b559d11e8171dfb91e2bb1e47ac3810b1cdc1a1e370c867b7b7b50c4688dce545763157e02f47e1cc661d5bf2fbc336cfae080ab15728b1ab9dd199f2779d451e6178977fb658c17344cffb7aa3af5791a28fc8a089c85187753e5e313c8d1f0fe7755e28be444426a189e8bce2d2f79db31d4c3ca911a83455525355f95d159351cd731a88e55403851236ee2128f279d5be644c042453ae65d9e9f3b40d6c82bdeb002acdee061ecca3f2dceabef9a900e6e063d56ab39cb82dbc77a4677572d7616cd72c0f6d5b9b941dfda1fe7c896b8cc24d65a4322d712a84e94adfc8ed0cc56cc1ae97f775bd3cea5b20b524d9a7a916056e19af095d30171e5e14c7c998f78dc44845edf307363eab7913f680a5e5a1540a6f945507ffa67591f8d1a2920ab3b6e754e35379dd67870c242335e2717903ff3c687e5c33dc953416865d5f23bd752e55492b9d5d888d7b37ef33b0a6774d052b0987c066a2e01767207aa7fbfc393ca62874613dde3794f74fadb5d55b877b877a605918c812610fbcafad72ee245e6dd8721138d6bd3f4eedad853aed1ec437ad02ac937c80dae26fa5f70083bd346779b779387f7b3d2aae57770d8177928833281ccb7a38da24834fd9726fd17eb603cba9041e82bfeed0e33942dde1d48c271f5b39aa7230f41afb89d36f7976eee4f51a036743031c534f64685b94c990a93a5737fe628ee9cda8ed9c08b11d3836f833835c445b317a77ead7599d1a0c08873014510d36bb7ff5fb961277589ea48c32a60c87ec40681be067b17785ec44825bd89faa25249e735a628b6eebcc6cce4e0314c627588118c40b2e0d460d8d5ce358c56458f36914ca203f5a5381c6deb5a76bbc08c40a87437da0d0b571788a05e9f96d9bb770de8a0b1b960ff2a44a964c9b7939853742e83ce8deb79191b2d82454655f227079dd8c5b0216c8470b8e1ac70526301bbfa2bc4adca68a766ccb2a6e0ebf2e99905bf5242590b01703868b3faf841c11c383be145a40fea6375e18a01468e459603b5efdf8a4e9abd179280ae8b5947d78d2f0c4d37715eaa42bc37cf8730e41ffbf9826d46424f2922a96033cefaa8b4bbe4c8b89d43501fd5211d5392ca19a98ba127d9025b5c6e86ba024471940549a2b5d8e14961c9dc19696da1a5bffd01030d5e6100000000000000000000000000000000000000000000000005090f131a1f").unwrap(); - assert_eq!(sig.len(), MLDSA65_SIG_LEN); + let mldsa65_pk = load_mldsa65_lowmemory_pk(); + let sig: [u8; MLDSA65_SIG_LEN] = [ + 0x90, 0x61, 0xF1, 0x5C, 0xBF, 0x20, 0x92, 0xF7, 0x44, 0xFB, 0xCD, 0x79, 0x9E, 0xB0, 0x24, + 0x14, 0x05, 0x3C, 0x1B, 0x0F, 0x74, 0x12, 0x12, 0x4B, 0xED, 0xC4, 0x1C, 0xF9, 0xA3, 0xDB, + 0x01, 0x66, 0x46, 0x9E, 0x87, 0x40, 0x37, 0xD7, 0xF0, 0x81, 0xE5, 0xF8, 0xD3, 0xD2, 0x03, + 0x3A, 0x03, 0x07, 0xD1, 0xC4, 0x9E, 0xD0, 0x1F, 0xE6, 0x45, 0x78, 0xC4, 0xA6, 0xFA, 0xBD, + 0x80, 0x88, 0x0C, 0xDF, 0x19, 0x11, 0x84, 0x8F, 0x18, 0x4D, 0x4B, 0xCF, 0x53, 0x6C, 0xA7, + 0x95, 0xA0, 0xFB, 0x1A, 0xA1, 0x9A, 0xB7, 0xEE, 0x3B, 0xA6, 0xB5, 0x8B, 0xD6, 0x4B, 0xBE, + 0xAC, 0x9F, 0x58, 0x65, 0x0F, 0xFF, 0x1E, 0xF5, 0xA9, 0x7A, 0xB6, 0x91, 0x6D, 0xF9, 0x62, + 0x07, 0x2E, 0x20, 0xE7, 0xC6, 0xBE, 0x96, 0x09, 0x0E, 0x3A, 0x78, 0x1A, 0x50, 0x4B, 0xC4, + 0x44, 0x2B, 0xD8, 0x88, 0x9A, 0x0A, 0xA6, 0x28, 0x90, 0x7A, 0x74, 0x29, 0x9F, 0x39, 0xFA, + 0x83, 0x60, 0x31, 0xF1, 0xBD, 0x68, 0x35, 0x5B, 0xEB, 0xE7, 0xAE, 0x93, 0xC1, 0xE3, 0x61, + 0xA9, 0xEF, 0xBE, 0xD1, 0x32, 0x5D, 0x96, 0x22, 0x70, 0x70, 0x46, 0x1F, 0xCD, 0x6F, 0x15, + 0x1B, 0x86, 0x69, 0xD9, 0x22, 0x9B, 0x97, 0x7D, 0x9E, 0xE5, 0x1F, 0xD2, 0x26, 0x0C, 0x3E, + 0x4A, 0x2E, 0x82, 0x04, 0x16, 0xF9, 0xE0, 0x74, 0x95, 0x8D, 0xC3, 0xB3, 0xE2, 0x21, 0x7E, + 0x63, 0x12, 0xB7, 0xE0, 0xB5, 0x82, 0xA0, 0x48, 0x98, 0x1C, 0xF6, 0x57, 0x9F, 0x4B, 0xC7, + 0x71, 0x5B, 0x78, 0xC8, 0x08, 0xE4, 0xC5, 0x7E, 0x3B, 0x8A, 0xA3, 0x8B, 0x05, 0xC0, 0x4F, + 0xCE, 0xDF, 0x20, 0x9F, 0x52, 0xC1, 0xE3, 0x31, 0xAE, 0x83, 0xDB, 0xDF, 0xF6, 0x0B, 0xA4, + 0x50, 0xA1, 0x7C, 0xC3, 0x97, 0x56, 0x8E, 0x54, 0xBC, 0x3F, 0x16, 0xDD, 0xF3, 0x0B, 0x92, + 0x74, 0x7C, 0xE4, 0x60, 0xD9, 0x25, 0xB9, 0xBE, 0x20, 0xA1, 0xD3, 0x5E, 0x2A, 0xED, 0x97, + 0xF1, 0x24, 0xAF, 0x26, 0x16, 0xA5, 0x36, 0x1D, 0xF2, 0x8B, 0xA3, 0x0E, 0x52, 0x2D, 0xD0, + 0x8F, 0xA0, 0x0F, 0xD2, 0x8D, 0x1A, 0xC4, 0x84, 0xD7, 0x56, 0xA8, 0x9E, 0x3A, 0x44, 0x2F, + 0xEF, 0xE8, 0x33, 0x2C, 0x56, 0xCD, 0x2A, 0x9F, 0xDE, 0x69, 0x1B, 0xDB, 0xDA, 0x43, 0xF1, + 0xCC, 0x54, 0xCE, 0xF5, 0x7B, 0xEA, 0xD9, 0x61, 0x20, 0xB5, 0x0C, 0x7D, 0x46, 0x95, 0xBD, + 0xBB, 0x13, 0x03, 0xCC, 0x5D, 0xDD, 0xA8, 0x98, 0xE4, 0xEE, 0xB8, 0x30, 0x83, 0x17, 0x6E, + 0x40, 0xE0, 0x23, 0x2C, 0xDD, 0x1C, 0x31, 0x50, 0x37, 0x1D, 0xF0, 0x5D, 0x6F, 0xDA, 0xD7, + 0xE1, 0x16, 0x4D, 0x90, 0x39, 0x3C, 0xF3, 0x08, 0xE9, 0x9E, 0xDF, 0xEB, 0x31, 0xFE, 0xD2, + 0x63, 0xE2, 0x86, 0x6E, 0xE3, 0xB7, 0xF3, 0x93, 0x7B, 0x39, 0x9C, 0x97, 0x4D, 0x87, 0xBA, + 0x7B, 0x48, 0x9E, 0xFE, 0x3C, 0x9B, 0x80, 0x37, 0x1D, 0x29, 0x28, 0x44, 0x6A, 0xDC, 0x31, + 0x99, 0x1A, 0xB0, 0xCE, 0xFA, 0xAA, 0x08, 0x05, 0x75, 0xB9, 0xEC, 0x81, 0xCF, 0xA1, 0x33, + 0xA9, 0x91, 0x1C, 0x03, 0x5A, 0x80, 0x58, 0xD0, 0xD3, 0xF2, 0xE3, 0x4D, 0xE4, 0xA9, 0xFB, + 0x00, 0x9B, 0xB4, 0xCC, 0xDB, 0x16, 0xDE, 0x7B, 0x90, 0x85, 0x74, 0xA7, 0x49, 0x67, 0x25, + 0xFF, 0x85, 0x75, 0x56, 0xC1, 0xB3, 0x39, 0x17, 0xE9, 0x86, 0xC8, 0x0F, 0x10, 0x14, 0xA9, + 0xE3, 0x08, 0x3A, 0xDD, 0x2F, 0xB3, 0x5F, 0x34, 0x5C, 0x5D, 0x06, 0x15, 0x9E, 0x44, 0x33, + 0x29, 0xD0, 0xDA, 0x09, 0x99, 0x87, 0xB9, 0x96, 0xC3, 0x73, 0x15, 0x92, 0xB4, 0x60, 0xC2, + 0xFF, 0xD2, 0x95, 0x5F, 0x75, 0x46, 0xF4, 0x21, 0x61, 0x00, 0xBA, 0x43, 0x18, 0x88, 0x03, + 0xFF, 0x9B, 0x36, 0x96, 0x96, 0x85, 0xF9, 0x09, 0xFA, 0x25, 0x39, 0x32, 0x3B, 0x8C, 0x8E, + 0xC1, 0xC0, 0x95, 0xA5, 0x08, 0x5E, 0x55, 0x4D, 0xD4, 0x50, 0xE0, 0xE6, 0x7A, 0xB6, 0x70, + 0xB6, 0xA1, 0x1E, 0xBF, 0x6C, 0x25, 0x52, 0x0F, 0xC1, 0x3E, 0x36, 0x40, 0x60, 0xF9, 0x1F, + 0x9B, 0x7F, 0x3D, 0x5C, 0xB4, 0x8F, 0xF2, 0x8B, 0x8F, 0xC8, 0x3D, 0x42, 0x93, 0xF1, 0xF3, + 0x5A, 0xD6, 0xFF, 0x6A, 0xE4, 0x57, 0x4A, 0xD7, 0xA1, 0xC6, 0x00, 0x5F, 0xC0, 0x38, 0x9A, + 0x7B, 0x21, 0x38, 0x6B, 0x08, 0x50, 0xA0, 0x5D, 0x83, 0x2F, 0xE6, 0xA1, 0x4B, 0xB2, 0xB1, + 0xDB, 0x1F, 0x8E, 0x20, 0xBD, 0x09, 0x17, 0x49, 0x46, 0xCD, 0x09, 0x8B, 0x81, 0xC8, 0xF7, + 0x97, 0xE9, 0x5F, 0x21, 0x43, 0xA9, 0x49, 0x77, 0x0C, 0xF1, 0x21, 0x9B, 0xFE, 0xF0, 0x39, + 0xDB, 0x51, 0xA8, 0x0F, 0xC2, 0x47, 0xF6, 0x5F, 0x41, 0x55, 0x4C, 0x71, 0x73, 0xDD, 0x80, + 0x5B, 0xA8, 0x2F, 0xDF, 0x47, 0xAB, 0x6D, 0x4B, 0xFD, 0x37, 0xDF, 0xE4, 0x6F, 0xC4, 0x79, + 0x04, 0x42, 0x1A, 0xE0, 0x0D, 0xC0, 0x05, 0xA2, 0x2F, 0x9C, 0x47, 0x84, 0xB0, 0xEA, 0x9E, + 0x66, 0x53, 0x92, 0xA4, 0x12, 0x24, 0x50, 0x16, 0xD5, 0xC6, 0xD7, 0x67, 0x3A, 0x6A, 0x18, + 0x0D, 0x22, 0x8D, 0x42, 0x55, 0xA5, 0x38, 0xE4, 0x51, 0xFF, 0xD8, 0xB4, 0x14, 0xD4, 0x03, + 0x04, 0xC0, 0xC8, 0x88, 0x99, 0x2E, 0x0A, 0xB6, 0xDE, 0x16, 0x02, 0x10, 0x95, 0x27, 0x41, + 0x7B, 0xC1, 0xC7, 0xEB, 0x78, 0x2A, 0xE7, 0x7A, 0x8C, 0x3C, 0xDF, 0xC1, 0xD1, 0x3A, 0x1E, + 0x87, 0x42, 0x07, 0x89, 0x82, 0x64, 0xE3, 0x80, 0x80, 0x24, 0x31, 0x09, 0xC5, 0x96, 0x96, + 0x49, 0xAC, 0x83, 0x83, 0x41, 0x7E, 0x92, 0x2B, 0xA1, 0x15, 0x33, 0x11, 0x42, 0xD0, 0xED, + 0x35, 0x44, 0x0B, 0x15, 0xD4, 0x0B, 0xEE, 0x0C, 0xF5, 0x8A, 0xF3, 0x7C, 0x0F, 0x05, 0x24, + 0xFF, 0xAC, 0x1C, 0x71, 0xCE, 0xED, 0x3B, 0xB8, 0x2F, 0x76, 0xAB, 0x10, 0x8A, 0x8A, 0xD1, + 0xA0, 0xC8, 0xB7, 0x8D, 0x93, 0x41, 0x14, 0x8C, 0x64, 0x23, 0x69, 0xBE, 0x7B, 0xEF, 0x59, + 0xD4, 0x6F, 0x49, 0xD7, 0x0C, 0x83, 0x56, 0x06, 0x07, 0xF1, 0x40, 0x84, 0x8E, 0xC9, 0xA7, + 0x60, 0x7D, 0x4A, 0x08, 0xF8, 0xB6, 0xE4, 0x44, 0x7F, 0x55, 0x23, 0xF4, 0x16, 0x98, 0x18, + 0x88, 0xA8, 0xDE, 0x96, 0x47, 0xFF, 0xEF, 0x79, 0x38, 0x9E, 0x49, 0x83, 0xE5, 0xC9, 0x38, + 0x76, 0x98, 0xD0, 0xCC, 0x2D, 0x42, 0x93, 0x22, 0x36, 0x5C, 0xE7, 0xE7, 0xB5, 0xFD, 0x6D, + 0x6E, 0xB9, 0x21, 0xC8, 0x13, 0xFC, 0xF0, 0x61, 0x99, 0xFE, 0x1C, 0xA4, 0x1E, 0x9C, 0xFE, + 0x03, 0xB5, 0x39, 0xF3, 0x21, 0x67, 0x1A, 0x2A, 0xCA, 0xD0, 0x96, 0x3F, 0x87, 0x6F, 0x9D, + 0xB7, 0xA1, 0xC4, 0x37, 0x1B, 0x9F, 0x10, 0x10, 0x05, 0x21, 0x79, 0x95, 0xB5, 0xB6, 0xA4, + 0x09, 0x76, 0x24, 0x6D, 0x24, 0x5D, 0xA6, 0x03, 0xDB, 0xA8, 0xDA, 0xC8, 0x12, 0xA5, 0x48, + 0x0C, 0x34, 0x76, 0xA9, 0x9D, 0x0F, 0xFD, 0xF0, 0xEF, 0x94, 0x3D, 0x72, 0xD9, 0x12, 0x54, + 0x31, 0x48, 0xB2, 0xFE, 0x78, 0xE8, 0xB0, 0x15, 0x93, 0x24, 0xFE, 0x9B, 0xCD, 0x4C, 0xED, + 0x33, 0xCD, 0x21, 0x2F, 0xE4, 0xF3, 0xDF, 0xD6, 0xD4, 0xC5, 0xE1, 0x95, 0x8B, 0xEB, 0x95, + 0xAC, 0x6B, 0x53, 0x3A, 0xCE, 0x3E, 0x78, 0x01, 0x5E, 0x38, 0x80, 0xB5, 0x2B, 0xF4, 0x52, + 0x99, 0x26, 0x3A, 0x4C, 0x00, 0x96, 0xF8, 0xBA, 0x5F, 0xE3, 0xA6, 0x29, 0x8C, 0xAB, 0x67, + 0x5C, 0xB7, 0xF3, 0x82, 0xE7, 0xEF, 0x49, 0x72, 0x0E, 0xB4, 0xCF, 0x47, 0x37, 0x6E, 0x2D, + 0x25, 0x74, 0x12, 0x2C, 0xCF, 0x91, 0x12, 0x9C, 0x85, 0x8E, 0x94, 0x89, 0x04, 0xFE, 0xCE, + 0xFB, 0x91, 0x22, 0x6E, 0xD4, 0x24, 0x03, 0xBA, 0x12, 0xDD, 0x32, 0x58, 0x90, 0x9A, 0x87, + 0xDF, 0xCB, 0xF6, 0x5C, 0xC3, 0xAD, 0xC3, 0xD9, 0x8D, 0x27, 0x7F, 0xDC, 0xEC, 0x76, 0x64, + 0xE2, 0x29, 0x2B, 0x7D, 0x27, 0xAF, 0xBB, 0x5A, 0xAF, 0xB4, 0x05, 0xC2, 0x0A, 0x34, 0xB2, + 0xFE, 0x2C, 0x08, 0x49, 0xEE, 0x28, 0x0B, 0xB8, 0x91, 0xDF, 0xDF, 0x59, 0xF1, 0x9B, 0x89, + 0xB0, 0x24, 0x63, 0x58, 0xDB, 0x54, 0xCF, 0x3F, 0xDC, 0x66, 0xEA, 0xAA, 0xA7, 0x50, 0xC8, + 0x90, 0x3F, 0x1D, 0x42, 0x67, 0x8F, 0x3E, 0xDF, 0x0B, 0x75, 0x30, 0x41, 0x0A, 0xA8, 0x81, + 0xBC, 0x61, 0x7F, 0x94, 0x34, 0x63, 0x79, 0x85, 0x4A, 0xF4, 0x53, 0x2E, 0x61, 0xF6, 0x5A, + 0xAE, 0x75, 0x76, 0xC3, 0x5F, 0xAF, 0x55, 0xE1, 0x55, 0xBD, 0x67, 0x87, 0xB4, 0x63, 0x4D, + 0x54, 0x19, 0x19, 0x07, 0xE1, 0x55, 0xC2, 0x39, 0xE6, 0x84, 0x80, 0xCD, 0xFA, 0x0C, 0x87, + 0x05, 0x4B, 0xFB, 0x62, 0x85, 0x5F, 0x40, 0x9A, 0x20, 0xD5, 0x33, 0x5F, 0xB1, 0x23, 0xE6, + 0x81, 0xE6, 0x4E, 0xC8, 0x47, 0xCD, 0x98, 0x5B, 0x60, 0x62, 0x05, 0x9F, 0x43, 0x6A, 0xEB, + 0xAC, 0x62, 0x3C, 0x03, 0x8B, 0x6C, 0x34, 0x05, 0xAC, 0x32, 0x51, 0x91, 0xA8, 0xD1, 0x12, + 0x6A, 0x5E, 0xF8, 0xF3, 0x8C, 0xCC, 0xBF, 0x14, 0x4A, 0x5C, 0x32, 0x4C, 0x1E, 0x09, 0x3C, + 0xF9, 0x9E, 0xFB, 0xE1, 0x0C, 0xA0, 0x3D, 0x43, 0x9B, 0xCF, 0xB8, 0xBA, 0x5E, 0x29, 0x3B, + 0x7D, 0x31, 0x88, 0x37, 0xF7, 0xBC, 0x42, 0xA9, 0x99, 0x64, 0x39, 0x23, 0x69, 0xDA, 0x76, + 0xE7, 0x9D, 0x71, 0xD1, 0xA2, 0xC2, 0x48, 0xA1, 0x13, 0x24, 0xA8, 0x7A, 0xE1, 0xE3, 0xCB, + 0xEA, 0xB6, 0xFB, 0x0D, 0x0B, 0xCA, 0xE1, 0xEF, 0x55, 0xE4, 0x3D, 0xFB, 0x6F, 0x1B, 0x4C, + 0xFB, 0x82, 0xC7, 0xA7, 0x78, 0xFB, 0x82, 0x8A, 0x37, 0x27, 0xEF, 0x07, 0x68, 0x5F, 0xE3, + 0x8A, 0x74, 0xB3, 0xDD, 0x25, 0xD0, 0x15, 0x32, 0x2C, 0x2D, 0x9F, 0x24, 0x5C, 0x08, 0xD8, + 0xC2, 0xB4, 0x38, 0x65, 0x69, 0x42, 0x33, 0x78, 0x2E, 0xB7, 0x34, 0x43, 0x6C, 0x4E, 0xDD, + 0xEF, 0x54, 0x06, 0x20, 0x8D, 0x6C, 0x45, 0x72, 0xC7, 0x37, 0x12, 0x62, 0xFE, 0x02, 0x31, + 0x9C, 0xFB, 0xBC, 0xF2, 0xE2, 0x3B, 0xED, 0x8A, 0xA9, 0x69, 0xD1, 0xAE, 0x6F, 0x5F, 0x25, + 0xFF, 0x6B, 0x8E, 0xBC, 0xF0, 0x92, 0x50, 0x66, 0xF7, 0x61, 0xA3, 0x9B, 0xBF, 0xF4, 0x9F, + 0x0C, 0x8D, 0xBC, 0x3B, 0xE8, 0x4F, 0x0C, 0x44, 0x2B, 0x04, 0x4E, 0xA0, 0x1B, 0x66, 0x97, + 0x47, 0xE3, 0xC8, 0x29, 0x3C, 0xFE, 0x9C, 0xCD, 0xF2, 0xEF, 0x06, 0x3A, 0xE3, 0xD2, 0x8D, + 0x10, 0x72, 0x0C, 0x27, 0x9A, 0x26, 0x91, 0x61, 0x6A, 0xBD, 0x23, 0xB0, 0x55, 0xCF, 0xC6, + 0xC5, 0x62, 0x12, 0x5D, 0xF4, 0xAD, 0x0F, 0xA6, 0x63, 0x13, 0x04, 0x97, 0x2D, 0xDC, 0x36, + 0x74, 0xB1, 0xAA, 0xA7, 0x66, 0x5B, 0xF6, 0x21, 0x32, 0x0D, 0x83, 0xEA, 0xC8, 0xD5, 0xB3, + 0x71, 0xD7, 0xD7, 0x19, 0x82, 0x9F, 0x58, 0xB2, 0x34, 0x58, 0x18, 0x25, 0x58, 0x71, 0x0D, + 0xE3, 0x1D, 0x81, 0xEF, 0x9A, 0x47, 0xD8, 0x83, 0x9C, 0x79, 0x64, 0x0B, 0x20, 0x25, 0xD1, + 0x96, 0x5A, 0x41, 0x8B, 0xC9, 0x0E, 0x41, 0x15, 0xF1, 0x42, 0x33, 0x11, 0xA8, 0xB6, 0x4F, + 0xCD, 0xE0, 0xF2, 0xD2, 0x14, 0x5E, 0xE5, 0x35, 0xB0, 0x93, 0x1B, 0x84, 0xBC, 0x81, 0x10, + 0x44, 0x5F, 0x2F, 0xF6, 0x8D, 0x13, 0x6E, 0xD7, 0x09, 0xDD, 0xB7, 0xEA, 0x9F, 0xF7, 0x5F, + 0x3B, 0x4E, 0x8B, 0x4F, 0x83, 0x62, 0x30, 0xCA, 0x9E, 0x81, 0x06, 0x94, 0x77, 0xF6, 0x34, + 0xE0, 0x72, 0x70, 0xAF, 0x60, 0xEF, 0x96, 0xF7, 0x25, 0x57, 0xA0, 0x81, 0xD6, 0x64, 0xAB, + 0xCF, 0x35, 0x54, 0x8F, 0x69, 0x94, 0x84, 0x65, 0x3D, 0xA6, 0x45, 0x48, 0x3F, 0xF2, 0xBF, + 0x59, 0x98, 0x61, 0x7A, 0xE8, 0xBF, 0xA6, 0x2D, 0x56, 0xE7, 0x14, 0xF3, 0xC0, 0x13, 0x6E, + 0x50, 0x35, 0xA3, 0xF7, 0x8E, 0x06, 0xC2, 0xF4, 0x70, 0xDF, 0x7F, 0xD3, 0x38, 0x0D, 0x14, + 0x03, 0x3F, 0x81, 0xE2, 0xAA, 0xE6, 0xB4, 0xD9, 0x04, 0x87, 0xDA, 0xB7, 0x6B, 0x9B, 0x3B, + 0x87, 0x61, 0xFB, 0x56, 0xC3, 0x6F, 0x54, 0x29, 0xDA, 0x3D, 0x43, 0x46, 0xCB, 0x22, 0xE6, + 0x41, 0xAD, 0x8D, 0x7D, 0x2D, 0x80, 0xFA, 0x24, 0x0D, 0x4E, 0x01, 0x54, 0xE6, 0xB3, 0xD2, + 0xF1, 0xB3, 0xEF, 0x6C, 0xF1, 0x74, 0xC0, 0x8D, 0x06, 0x2F, 0x57, 0x5C, 0x83, 0xA4, 0x07, + 0x81, 0x74, 0xF8, 0x74, 0x36, 0x4D, 0xF3, 0x6A, 0x63, 0x28, 0xBE, 0xEE, 0xF6, 0x9B, 0xA7, + 0xF9, 0x0E, 0x1D, 0xF9, 0xFD, 0xCE, 0xC9, 0xA2, 0xF1, 0x5E, 0xBF, 0x04, 0xFA, 0x7D, 0x67, + 0x56, 0xDA, 0x2E, 0x5A, 0x59, 0xC9, 0xCB, 0xBC, 0xBC, 0x39, 0x7D, 0x6F, 0xB2, 0x8D, 0x0F, + 0xC9, 0xA6, 0x05, 0x34, 0xDF, 0xF0, 0x75, 0x27, 0x16, 0xED, 0x07, 0x9A, 0xD1, 0xAB, 0x19, + 0xA2, 0x24, 0xD1, 0xC8, 0xAE, 0x8A, 0x53, 0x24, 0x2F, 0xD1, 0x64, 0x98, 0x9F, 0xF9, 0x97, + 0x48, 0x9B, 0x65, 0x20, 0xEB, 0x3C, 0x0E, 0x97, 0xF4, 0xBC, 0xC1, 0xA9, 0xC3, 0xCB, 0xD4, + 0x4F, 0x00, 0x8C, 0x03, 0xEF, 0x52, 0xCF, 0x7E, 0x62, 0x68, 0x81, 0xD2, 0x46, 0x92, 0x5E, + 0x03, 0x36, 0xC0, 0xAC, 0x66, 0x88, 0x67, 0xF8, 0x53, 0xDA, 0x78, 0x20, 0xF9, 0x14, 0x11, + 0x5A, 0x7C, 0x77, 0xAC, 0x31, 0xB6, 0x6F, 0x46, 0xFB, 0xF9, 0x7F, 0x66, 0xFA, 0x26, 0x41, + 0x6F, 0xC4, 0x58, 0x1D, 0x45, 0x9A, 0x4F, 0x24, 0x62, 0xD5, 0x2C, 0xF0, 0xC7, 0x9B, 0x27, + 0x89, 0x55, 0xAA, 0x73, 0xE8, 0xFA, 0x56, 0xE3, 0xC3, 0x20, 0xF5, 0x16, 0xBC, 0xC5, 0x4C, + 0x97, 0xE5, 0x87, 0x19, 0x9C, 0x15, 0xAB, 0x95, 0x3C, 0xC3, 0x71, 0x89, 0xB8, 0x1C, 0x70, + 0xCA, 0xBB, 0x25, 0x59, 0xE4, 0x45, 0xBC, 0xC9, 0xD8, 0x17, 0x4A, 0xD7, 0x57, 0x4E, 0x9A, + 0xCB, 0x02, 0xF4, 0x3E, 0x0C, 0x34, 0xFF, 0x5E, 0x67, 0x46, 0xEE, 0x73, 0x0A, 0xD4, 0x1F, + 0xF8, 0xEE, 0xF9, 0x3C, 0x20, 0x71, 0xC2, 0x64, 0x90, 0x63, 0xDD, 0x92, 0xF3, 0x43, 0xC0, + 0x6E, 0xF6, 0xAB, 0xAF, 0x98, 0xF2, 0x8D, 0x98, 0xD9, 0x68, 0x07, 0x1C, 0x12, 0xCC, 0x10, + 0xA9, 0x0C, 0x22, 0xD8, 0xB3, 0xB3, 0x48, 0x0C, 0x76, 0xF7, 0xA5, 0x1B, 0x7E, 0xC5, 0x94, + 0xB3, 0x43, 0x5D, 0x2E, 0x3D, 0x77, 0x9C, 0x1A, 0x15, 0x03, 0x76, 0x97, 0xF3, 0xA0, 0x58, + 0x65, 0x04, 0x72, 0xE4, 0x7E, 0xEC, 0xD5, 0xF3, 0x2E, 0xB3, 0x24, 0x3A, 0x51, 0x6F, 0x0E, + 0x70, 0x3F, 0x98, 0x88, 0xC8, 0x46, 0x90, 0x75, 0x06, 0x48, 0xD6, 0xA9, 0xA8, 0x76, 0xBF, + 0x1F, 0x35, 0x3D, 0xB6, 0x89, 0x1D, 0xC6, 0xD3, 0x17, 0xD6, 0xE8, 0x7A, 0xC0, 0x88, 0xF4, + 0x2B, 0x5F, 0x6F, 0x20, 0xD7, 0x99, 0xEC, 0xE4, 0xFA, 0x7A, 0xAA, 0x92, 0x8D, 0x2A, 0xC7, + 0x95, 0xE8, 0xDE, 0x83, 0xD1, 0xE1, 0xC7, 0xFA, 0x2F, 0x9A, 0x41, 0x06, 0x69, 0x3E, 0x98, + 0x1C, 0x21, 0xC6, 0x3B, 0x32, 0x21, 0xC4, 0xFA, 0x26, 0x49, 0xF4, 0x5F, 0x0C, 0x6E, 0x05, + 0xDB, 0xF2, 0x40, 0x11, 0xAF, 0x16, 0xAB, 0x2E, 0x5F, 0xE9, 0x4A, 0x64, 0x0B, 0x48, 0x59, + 0x88, 0x03, 0x7E, 0xBE, 0x1E, 0x8A, 0xD0, 0xB2, 0x62, 0x3D, 0x95, 0xE9, 0x94, 0x7F, 0x07, + 0x26, 0x12, 0x1D, 0x78, 0x28, 0x61, 0x4E, 0x3B, 0x2D, 0x77, 0xA7, 0xA1, 0xF9, 0xA9, 0x38, + 0xBE, 0xA9, 0xA1, 0xA7, 0xA2, 0x62, 0x7B, 0x7D, 0x2E, 0x35, 0x8C, 0x42, 0xCC, 0xC6, 0xC0, + 0xB8, 0x0A, 0x15, 0xA1, 0xC2, 0xF6, 0xE9, 0xAA, 0xF0, 0x49, 0x5B, 0xDB, 0x7B, 0xB8, 0xD4, + 0xB0, 0xE2, 0x8A, 0x1A, 0xB5, 0xAB, 0x93, 0xCA, 0x0F, 0xF3, 0xE3, 0xF9, 0x10, 0xC4, 0x90, + 0xC1, 0x34, 0x86, 0x85, 0x25, 0x34, 0xD5, 0xE1, 0x21, 0x60, 0x83, 0x5E, 0xC5, 0x91, 0x6C, + 0x5C, 0x68, 0x34, 0x9C, 0x4E, 0x2D, 0x8F, 0xA9, 0x56, 0xC6, 0x43, 0x27, 0x7E, 0xDD, 0x3B, + 0x6C, 0x81, 0xC8, 0x8C, 0x01, 0x04, 0x21, 0x70, 0x5F, 0xD3, 0x17, 0xFF, 0x9E, 0x3C, 0x94, + 0xDF, 0x0E, 0xD5, 0x30, 0x5F, 0x53, 0x0A, 0xCB, 0xCC, 0xF8, 0xDD, 0x0E, 0x87, 0x14, 0x0C, + 0xD3, 0x81, 0x52, 0x66, 0x4A, 0x57, 0x2C, 0x16, 0x8C, 0xD7, 0x25, 0x95, 0xB7, 0xFA, 0xC2, + 0x43, 0xC0, 0x3F, 0x3F, 0xB3, 0x3E, 0xF7, 0x4A, 0x28, 0xC0, 0xE4, 0x46, 0x9F, 0x94, 0x58, + 0x7C, 0x13, 0x70, 0x4E, 0x9E, 0xFE, 0x80, 0x10, 0xB2, 0x12, 0x5A, 0xCA, 0x78, 0xC2, 0x2C, + 0x33, 0xC8, 0x23, 0x66, 0xE1, 0xA7, 0xC4, 0x02, 0x8C, 0x2A, 0xE2, 0xE8, 0xD2, 0x6E, 0x1A, + 0x57, 0xE4, 0x29, 0x7F, 0xAC, 0x98, 0x7F, 0x84, 0xA0, 0xA2, 0x7F, 0x42, 0xB4, 0xC9, 0x3A, + 0x4F, 0x4D, 0x14, 0x56, 0x98, 0x24, 0xB0, 0x88, 0x0F, 0xB6, 0x74, 0x07, 0xED, 0x58, 0xF2, + 0x67, 0xAC, 0x40, 0x3A, 0xA0, 0xB1, 0xF9, 0x37, 0x84, 0xB4, 0xB4, 0xC6, 0x70, 0x36, 0x03, + 0x7E, 0x60, 0xD5, 0x80, 0x72, 0x61, 0x1B, 0x0E, 0x90, 0xCA, 0x31, 0x69, 0x76, 0xEF, 0x4E, + 0x0B, 0x30, 0x2C, 0xDA, 0xD1, 0xB6, 0xDC, 0xCA, 0x92, 0xEF, 0xB8, 0xE1, 0xF6, 0xBE, 0x23, + 0x97, 0x96, 0x75, 0x08, 0xBE, 0x2C, 0x02, 0xA2, 0x5E, 0xD0, 0x38, 0x0B, 0xA1, 0xF7, 0x95, + 0x5F, 0x85, 0x7C, 0x8F, 0xB0, 0x43, 0x29, 0x77, 0x80, 0xD1, 0x36, 0xB2, 0xB0, 0x64, 0x04, + 0x0C, 0x8E, 0x55, 0x14, 0x3D, 0x71, 0x5E, 0xA9, 0x97, 0xE1, 0x34, 0xED, 0x97, 0x3C, 0x98, + 0xEF, 0x82, 0x78, 0x6F, 0x0C, 0xCF, 0x66, 0xC1, 0x7D, 0x86, 0x35, 0x42, 0x18, 0x0C, 0x66, + 0xD5, 0x4D, 0x08, 0xE1, 0x16, 0xF2, 0xE3, 0x5D, 0x99, 0x5E, 0x21, 0x44, 0x89, 0xAD, 0x0F, + 0xAD, 0x7A, 0x55, 0xFE, 0x9E, 0xBC, 0x1A, 0x77, 0x7F, 0xE3, 0x41, 0x41, 0x14, 0x7C, 0x08, + 0x0B, 0x98, 0xD1, 0x34, 0x63, 0xA3, 0xBB, 0xC6, 0xFC, 0x82, 0xF2, 0xFC, 0x95, 0xF4, 0xDE, + 0x7B, 0x35, 0x91, 0xD9, 0xC8, 0xCD, 0x44, 0x16, 0x91, 0x7A, 0x43, 0x38, 0x09, 0x5D, 0x56, + 0x20, 0x10, 0x4B, 0x7B, 0xE1, 0x3F, 0x5A, 0x13, 0x1D, 0xD3, 0xF7, 0xAA, 0xD5, 0xB5, 0x59, + 0xD1, 0x1E, 0x81, 0x71, 0xDF, 0xB9, 0x1E, 0x2B, 0xB1, 0xE4, 0x7A, 0xC3, 0x81, 0x0B, 0x1C, + 0xDC, 0x1A, 0x1E, 0x37, 0x0C, 0x86, 0x7B, 0x7B, 0x7B, 0x50, 0xC4, 0x68, 0x8D, 0xCE, 0x54, + 0x57, 0x63, 0x15, 0x7E, 0x02, 0xF4, 0x7E, 0x1C, 0xC6, 0x61, 0xD5, 0xBF, 0x2F, 0xBC, 0x33, + 0x6C, 0xFA, 0xE0, 0x80, 0xAB, 0x15, 0x72, 0x8B, 0x1A, 0xB9, 0xDD, 0x19, 0x9F, 0x27, 0x79, + 0xD4, 0x51, 0xE6, 0x17, 0x89, 0x77, 0xFB, 0x65, 0x8C, 0x17, 0x34, 0x4C, 0xFF, 0xB7, 0xAA, + 0x3A, 0xF5, 0x79, 0x1A, 0x28, 0xFC, 0x8A, 0x08, 0x9C, 0x85, 0x18, 0x77, 0x53, 0xE5, 0xE3, + 0x13, 0xC8, 0xD1, 0xF0, 0xFE, 0x77, 0x55, 0xE2, 0x8B, 0xE4, 0x44, 0x42, 0x6A, 0x18, 0x9E, + 0x8B, 0xCE, 0x2D, 0x2F, 0x79, 0xDB, 0x31, 0xD4, 0xC3, 0xCA, 0x91, 0x1A, 0x83, 0x45, 0x55, + 0x25, 0x35, 0x5F, 0x95, 0xD1, 0x59, 0x35, 0x1C, 0xD7, 0x31, 0xA8, 0x8E, 0x55, 0x40, 0x38, + 0x51, 0x23, 0x6E, 0xE2, 0x12, 0x8F, 0x27, 0x9D, 0x5B, 0xE6, 0x44, 0xC0, 0x42, 0x45, 0x3A, + 0xE6, 0x5D, 0x9E, 0x9F, 0x3B, 0x40, 0xD6, 0xC8, 0x2B, 0xDE, 0xB0, 0x02, 0xAC, 0xDE, 0xE0, + 0x61, 0xEC, 0xCA, 0x3F, 0x2D, 0xCE, 0xAB, 0xEF, 0x9A, 0x90, 0x0E, 0x6E, 0x06, 0x3D, 0x56, + 0xAB, 0x39, 0xCB, 0x82, 0xDB, 0xC7, 0x7A, 0x46, 0x77, 0x57, 0x2D, 0x76, 0x16, 0xCD, 0x72, + 0xC0, 0xF6, 0xD5, 0xB9, 0xB9, 0x41, 0xDF, 0xDA, 0x1F, 0xE7, 0xC8, 0x96, 0xB8, 0xCC, 0x24, + 0xD6, 0x5A, 0x43, 0x22, 0xD7, 0x12, 0xA8, 0x4E, 0x94, 0xAD, 0xFC, 0x8E, 0xD0, 0xCC, 0x56, + 0xCC, 0x1A, 0xE9, 0x7F, 0x77, 0x5B, 0xD3, 0xCE, 0xA5, 0xB2, 0x0B, 0x52, 0x4D, 0x9A, 0x7A, + 0x91, 0x60, 0x56, 0xE1, 0x9A, 0xF0, 0x95, 0xD3, 0x01, 0x71, 0xE5, 0xE1, 0x4C, 0x7C, 0x99, + 0x8F, 0x78, 0xDC, 0x44, 0x84, 0x5E, 0xDF, 0x30, 0x73, 0x63, 0xEA, 0xB7, 0x91, 0x3F, 0x68, + 0x0A, 0x5E, 0x5A, 0x15, 0x40, 0xA6, 0xF9, 0x45, 0x50, 0x7F, 0xFA, 0x67, 0x59, 0x1F, 0x8D, + 0x1A, 0x29, 0x20, 0xAB, 0x3B, 0x6E, 0x75, 0x4E, 0x35, 0x37, 0x9D, 0xD6, 0x78, 0x70, 0xC2, + 0x42, 0x33, 0x5E, 0x27, 0x17, 0x90, 0x3F, 0xF3, 0xC6, 0x87, 0xE5, 0xC3, 0x3D, 0xC9, 0x53, + 0x41, 0x68, 0x65, 0xD5, 0xF2, 0x3B, 0xD7, 0x52, 0xE5, 0x54, 0x92, 0xB9, 0xD5, 0xD8, 0x88, + 0xD7, 0xB3, 0x7E, 0xF3, 0x3B, 0x0A, 0x67, 0x74, 0xD0, 0x52, 0xB0, 0x98, 0x7C, 0x06, 0x6A, + 0x2E, 0x01, 0x76, 0x72, 0x07, 0xAA, 0x7F, 0xBF, 0xC3, 0x93, 0xCA, 0x62, 0x87, 0x46, 0x13, + 0xDD, 0xE3, 0x79, 0x4F, 0x74, 0xFA, 0xDB, 0x5D, 0x55, 0xB8, 0x77, 0xB8, 0x77, 0xA6, 0x05, + 0x91, 0x8C, 0x81, 0x26, 0x10, 0xFB, 0xCA, 0xFA, 0xD7, 0x2E, 0xE2, 0x45, 0xE6, 0xDD, 0x87, + 0x21, 0x13, 0x8D, 0x6B, 0xD3, 0xF4, 0xEE, 0xDA, 0xD8, 0x53, 0xAE, 0xD1, 0xEC, 0x43, 0x7A, + 0xD0, 0x2A, 0xC9, 0x37, 0xC8, 0x0D, 0xAE, 0x26, 0xFA, 0x5F, 0x70, 0x08, 0x3B, 0xD3, 0x46, + 0x77, 0x9B, 0x77, 0x93, 0x87, 0xF7, 0xB3, 0xD2, 0xAA, 0xE5, 0x77, 0x70, 0xD8, 0x17, 0x79, + 0x28, 0x83, 0x32, 0x81, 0xCC, 0xB7, 0xA3, 0x8D, 0xA2, 0x48, 0x34, 0xFD, 0x97, 0x26, 0xFD, + 0x17, 0xEB, 0x60, 0x3C, 0xBA, 0x90, 0x41, 0xE8, 0x2B, 0xFE, 0xED, 0x0E, 0x33, 0x94, 0x2D, + 0xDE, 0x1D, 0x48, 0xC2, 0x71, 0xF5, 0xB3, 0x9A, 0xA7, 0x23, 0x0F, 0x41, 0xAF, 0xB8, 0x9D, + 0x36, 0xF7, 0x97, 0x6E, 0xEE, 0x4F, 0x51, 0xA0, 0x36, 0x74, 0x30, 0x31, 0xC5, 0x34, 0xF6, + 0x46, 0x85, 0xB9, 0x4C, 0x99, 0x0A, 0x93, 0xA5, 0x73, 0x7F, 0xE6, 0x28, 0xEE, 0x9C, 0xDA, + 0x8E, 0xD9, 0xC0, 0x8B, 0x11, 0xD3, 0x83, 0x6F, 0x83, 0x38, 0x35, 0xC4, 0x45, 0xB3, 0x17, + 0xA7, 0x7E, 0xAD, 0x75, 0x99, 0xD1, 0xA0, 0xC0, 0x88, 0x73, 0x01, 0x45, 0x10, 0xD3, 0x6B, + 0xB7, 0xFF, 0x5F, 0xB9, 0x61, 0x27, 0x75, 0x89, 0xEA, 0x48, 0xC3, 0x2A, 0x60, 0xC8, 0x7E, + 0xC4, 0x06, 0x81, 0xBE, 0x06, 0x7B, 0x17, 0x78, 0x5E, 0xC4, 0x48, 0x25, 0xBD, 0x89, 0xFA, + 0xA2, 0x52, 0x49, 0xE7, 0x35, 0xA6, 0x28, 0xB6, 0xEE, 0xBC, 0xC6, 0xCC, 0xE4, 0xE0, 0x31, + 0x4C, 0x62, 0x75, 0x88, 0x11, 0x8C, 0x40, 0xB2, 0xE0, 0xD4, 0x60, 0xD8, 0xD5, 0xCE, 0x35, + 0x8C, 0x56, 0x45, 0x8F, 0x36, 0x91, 0x4C, 0xA2, 0x03, 0xF5, 0xA5, 0x38, 0x1C, 0x6D, 0xEB, + 0x5A, 0x76, 0xBB, 0xC0, 0x8C, 0x40, 0xA8, 0x74, 0x37, 0xDA, 0x0D, 0x0B, 0x57, 0x17, 0x88, + 0xA0, 0x5E, 0x9F, 0x96, 0xD9, 0xBB, 0x77, 0x0D, 0xE8, 0xA0, 0xB1, 0xB9, 0x60, 0xFF, 0x2A, + 0x44, 0xA9, 0x64, 0xC9, 0xB7, 0x93, 0x98, 0x53, 0x74, 0x2E, 0x83, 0xCE, 0x8D, 0xEB, 0x79, + 0x19, 0x1B, 0x2D, 0x82, 0x45, 0x46, 0x55, 0xF2, 0x27, 0x07, 0x9D, 0xD8, 0xC5, 0xB0, 0x21, + 0x6C, 0x84, 0x70, 0xB8, 0xE1, 0xAC, 0x70, 0x52, 0x63, 0x01, 0xBB, 0xFA, 0x2B, 0xC4, 0xAD, + 0xCA, 0x68, 0xA7, 0x66, 0xCC, 0xB2, 0xA6, 0xE0, 0xEB, 0xF2, 0xE9, 0x99, 0x05, 0xBF, 0x52, + 0x42, 0x59, 0x0B, 0x01, 0x70, 0x38, 0x68, 0xB3, 0xFA, 0xF8, 0x41, 0xC1, 0x1C, 0x38, 0x3B, + 0xE1, 0x45, 0xA4, 0x0F, 0xEA, 0x63, 0x75, 0xE1, 0x8A, 0x01, 0x46, 0x8E, 0x45, 0x96, 0x03, + 0xB5, 0xEF, 0xDF, 0x8A, 0x4E, 0x9A, 0xBD, 0x17, 0x92, 0x80, 0xAE, 0x8B, 0x59, 0x47, 0xD7, + 0x8D, 0x2F, 0x0C, 0x4D, 0x37, 0x71, 0x5E, 0xAA, 0x42, 0xBC, 0x37, 0xCF, 0x87, 0x30, 0xE4, + 0x1F, 0xFB, 0xF9, 0x82, 0x6D, 0x46, 0x42, 0x4F, 0x29, 0x22, 0xA9, 0x60, 0x33, 0xCE, 0xFA, + 0xA8, 0xB4, 0xBB, 0xE4, 0xC8, 0xB8, 0x9D, 0x43, 0x50, 0x1F, 0xD5, 0x21, 0x1D, 0x53, 0x92, + 0xCA, 0x19, 0xA9, 0x8B, 0xA1, 0x27, 0xD9, 0x02, 0x5B, 0x5C, 0x6E, 0x86, 0xBA, 0x02, 0x44, + 0x71, 0x94, 0x05, 0x49, 0xA2, 0xB5, 0xD8, 0xE1, 0x49, 0x61, 0xC9, 0xDC, 0x19, 0x69, 0x6D, + 0xA1, 0xA5, 0xBF, 0xFD, 0x01, 0x03, 0x0D, 0x5E, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x05, 0x09, 0x0F, 0x13, 0x1A, 0x1F, + ]; + + measure(|| { + if MLDSA65::verify(&mldsa65_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa87_pk() -> bouncycastle::mldsa::MLDSA87PublicKey { + use bouncycastle::mldsa::MLDSA87PublicKey; - if MLDSA65::verify(&mldsa65_pk, msg, None, &sig).is_ok() { - eprintln!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + 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() } fn bench_mldsa87_verify() { - use bouncycastle::hex; + use bouncycastle::mldsa::{MLDSA87, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSATrait}; + + 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 keygen is not captured in the bench */ + + // 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], + // 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 pk = load_mldsa87_pk(); + let sig: [u8; MLDSA87_SIG_LEN] = [ + 0x78, 0x13, 0x68, 0xE6, 0x4D, 0xBA, 0x54, 0x2A, 0x7E, 0xAC, 0xBD, 0x22, 0x57, 0x33, 0x5C, + 0xC9, 0x43, 0xA0, 0x32, 0x41, 0x00, 0x9B, 0x79, 0x70, 0x93, 0xC6, 0x15, 0xF7, 0x6A, 0x67, + 0x1A, 0x75, 0x91, 0x43, 0x04, 0x41, 0xD8, 0x0B, 0xB5, 0x82, 0x30, 0x4B, 0x33, 0xB9, 0xFC, + 0xE2, 0x95, 0xE0, 0xDD, 0x57, 0xFE, 0x16, 0x93, 0x55, 0xDD, 0xF4, 0x45, 0x3A, 0x2A, 0xCA, + 0x62, 0xD8, 0xEB, 0x81, 0x09, 0xEF, 0x0D, 0x9C, 0xF3, 0xF5, 0xB0, 0xA9, 0x4E, 0x04, 0xAD, + 0x81, 0xB3, 0xE7, 0x86, 0x01, 0x42, 0x43, 0xEC, 0xDE, 0x81, 0x65, 0x51, 0xAA, 0x7F, 0xE0, + 0x1C, 0x63, 0x90, 0x54, 0x25, 0x6A, 0x49, 0x17, 0x56, 0xBE, 0xF5, 0x9F, 0x50, 0x34, 0xF7, + 0x17, 0xFF, 0x4F, 0x85, 0xE7, 0x0B, 0xA7, 0x73, 0x1A, 0x49, 0x97, 0x14, 0x15, 0xB6, 0xA7, + 0xE7, 0xD8, 0x16, 0xAB, 0x43, 0x4B, 0x9F, 0x17, 0xA3, 0x09, 0x5E, 0xDE, 0x6F, 0xD4, 0x32, + 0xBE, 0x2B, 0xFA, 0x82, 0x72, 0x40, 0x45, 0xDD, 0xA0, 0xDF, 0xFF, 0x7A, 0x02, 0x81, 0xE9, + 0x00, 0x09, 0x39, 0xCC, 0xBA, 0x3D, 0x8A, 0xB3, 0x24, 0x51, 0x39, 0xC4, 0x41, 0x64, 0x8C, + 0x76, 0xA6, 0x53, 0x61, 0x27, 0xE4, 0xD1, 0xEF, 0x0D, 0xF1, 0x53, 0x18, 0x83, 0xAB, 0x78, + 0xC8, 0xB4, 0x13, 0x23, 0x61, 0x7A, 0xD8, 0xDB, 0x03, 0xD9, 0x90, 0x8C, 0x9E, 0x08, 0xA9, + 0xF7, 0x32, 0x1C, 0x45, 0x05, 0x1B, 0x3C, 0x94, 0x21, 0x33, 0x47, 0xB1, 0x1C, 0x4A, 0x84, + 0x49, 0x1D, 0xE7, 0xA7, 0xBE, 0x68, 0x70, 0x1E, 0x47, 0xD7, 0xF0, 0xE0, 0xB3, 0x3E, 0x76, + 0x7B, 0xEF, 0x17, 0x69, 0x4E, 0x4D, 0x33, 0x24, 0x4E, 0xD9, 0x2E, 0xBD, 0x74, 0xC8, 0x5A, + 0xB6, 0xC8, 0x44, 0x41, 0xCD, 0xDC, 0x14, 0x33, 0x1E, 0x6A, 0xE8, 0xBD, 0x23, 0x67, 0x4B, + 0xDA, 0x27, 0xF0, 0x9C, 0x05, 0x0D, 0x88, 0xF7, 0xD4, 0x30, 0xFE, 0xEE, 0x7F, 0x15, 0xA7, + 0x2A, 0x24, 0xD6, 0x53, 0xBB, 0x6B, 0xEC, 0x54, 0x49, 0x1B, 0x98, 0x36, 0x2C, 0xE1, 0x31, + 0xD3, 0x7C, 0x7D, 0x78, 0xA3, 0xF9, 0xA8, 0x93, 0xDB, 0x5A, 0xBD, 0xCC, 0xD6, 0x66, 0x35, + 0x93, 0xB8, 0x8B, 0xC6, 0xC9, 0x7F, 0x07, 0xF8, 0xEA, 0xFC, 0xCF, 0xD2, 0x5E, 0x81, 0x80, + 0xD9, 0x18, 0xEF, 0xBC, 0xD9, 0x5B, 0xBF, 0x3D, 0xA2, 0x9F, 0x08, 0x1E, 0x3E, 0x19, 0x32, + 0x09, 0x59, 0x39, 0x19, 0x8E, 0x2A, 0x15, 0x5B, 0x2D, 0x80, 0x3A, 0x3E, 0x84, 0xCA, 0x4F, + 0x34, 0x56, 0x9D, 0xF6, 0x95, 0xC2, 0x59, 0xFA, 0xF3, 0xC0, 0xD8, 0xF0, 0xCD, 0x21, 0x7E, + 0xBD, 0x2D, 0xBA, 0xD5, 0x42, 0xB3, 0x2F, 0xBB, 0x54, 0xE4, 0x4A, 0xAF, 0x0B, 0x5D, 0xC7, + 0x39, 0xFA, 0xFE, 0xF2, 0xE4, 0x6D, 0xB8, 0xD6, 0x8B, 0xFC, 0x35, 0xF4, 0x4F, 0x03, 0x8C, + 0xB1, 0xF5, 0x23, 0x1A, 0x1B, 0x5B, 0x13, 0x4A, 0xE6, 0x83, 0xE7, 0xF3, 0x29, 0x7C, 0xC7, + 0xA9, 0x5B, 0xD1, 0x91, 0xB3, 0x10, 0xF6, 0x82, 0x01, 0x45, 0x07, 0x97, 0xFE, 0x32, 0x93, + 0xCD, 0xE1, 0x67, 0x2D, 0xFE, 0xCA, 0x4B, 0x49, 0x3F, 0x53, 0xC7, 0x68, 0xEA, 0x04, 0x8A, + 0x97, 0x2A, 0x4C, 0xD8, 0x4D, 0x39, 0xEF, 0x68, 0x29, 0x57, 0xB8, 0xF2, 0x8B, 0xA2, 0x94, + 0x87, 0xB4, 0x68, 0x9B, 0x43, 0xFE, 0xC2, 0x65, 0x58, 0x23, 0xD9, 0xBB, 0x99, 0xFF, 0xCF, + 0x31, 0x49, 0x03, 0x66, 0xA9, 0x86, 0x0A, 0x5D, 0x5B, 0x8E, 0x32, 0xA3, 0xB8, 0xBF, 0xEB, + 0x6F, 0x55, 0xF8, 0x8F, 0xB8, 0x0C, 0x8C, 0x01, 0x42, 0x08, 0x6F, 0x22, 0x0E, 0x1F, 0x6F, + 0x28, 0x62, 0xDA, 0xBD, 0xA5, 0x8C, 0x3B, 0x6F, 0x5F, 0xAA, 0x80, 0x5B, 0x39, 0xCF, 0xAC, + 0x4B, 0x6D, 0x7E, 0xA7, 0xAC, 0xDF, 0x1B, 0x06, 0x90, 0x06, 0x3B, 0x0C, 0x1E, 0xA3, 0x8C, + 0x7C, 0x47, 0x55, 0x18, 0x99, 0x66, 0xDC, 0x63, 0x10, 0x55, 0xF1, 0x53, 0xF7, 0x1B, 0x77, + 0xB1, 0x14, 0xFA, 0x5C, 0x30, 0x93, 0x16, 0xBA, 0x51, 0x23, 0x30, 0xEA, 0x5C, 0xDB, 0x0B, + 0xB1, 0x76, 0x00, 0x1E, 0x57, 0x46, 0x15, 0x63, 0xD1, 0x72, 0x59, 0xF3, 0x5D, 0x0C, 0x30, + 0xEF, 0x5A, 0xC8, 0x38, 0xC0, 0x32, 0x54, 0x02, 0xBA, 0xB5, 0x2C, 0x53, 0x14, 0x69, 0x52, + 0x6A, 0xE3, 0xEE, 0x62, 0x93, 0xF7, 0xB5, 0x76, 0x9D, 0x27, 0xE6, 0x9F, 0xA8, 0x1C, 0xD2, + 0x5A, 0x31, 0xCD, 0x09, 0x5B, 0x12, 0x6E, 0x70, 0xC5, 0x7A, 0xC3, 0x16, 0x9A, 0x5F, 0x58, + 0x5A, 0x11, 0xF1, 0x74, 0x8D, 0x9D, 0x22, 0xF2, 0x56, 0x49, 0x11, 0xC2, 0x6A, 0x24, 0xB2, + 0x15, 0x3A, 0x78, 0xF3, 0xA0, 0x68, 0x22, 0xF5, 0xF1, 0x96, 0x3F, 0x23, 0x7A, 0xBE, 0xB4, + 0x8E, 0xFD, 0x9A, 0x9C, 0xD4, 0x78, 0xDE, 0x57, 0x9C, 0x5A, 0x0B, 0xA8, 0x4D, 0x00, 0xE9, + 0x6F, 0xBD, 0xE3, 0x6D, 0x8C, 0xE2, 0x0E, 0x7E, 0x94, 0x85, 0x47, 0xFB, 0x68, 0x50, 0x83, + 0x4F, 0xF7, 0x9D, 0x21, 0x18, 0x30, 0xF6, 0xEE, 0x97, 0x33, 0x59, 0x78, 0x1D, 0x9D, 0x50, + 0x08, 0xFB, 0x43, 0xA8, 0x93, 0x54, 0x78, 0x2F, 0xDE, 0x41, 0x58, 0x17, 0x7F, 0x52, 0x06, + 0xCE, 0x1D, 0x38, 0xC8, 0x89, 0xE9, 0x9E, 0x4B, 0xB5, 0xB4, 0xAB, 0x34, 0xD6, 0xA0, 0x5C, + 0x42, 0xF5, 0xD7, 0x19, 0xEA, 0x03, 0xDB, 0xC5, 0x4A, 0xDB, 0xA7, 0x5A, 0x3B, 0xB4, 0x4A, + 0x3C, 0x08, 0xC7, 0x55, 0x64, 0x62, 0xF8, 0xC5, 0xB7, 0xC5, 0x68, 0xA6, 0x92, 0x42, 0xCF, + 0x5B, 0xE6, 0x09, 0x8E, 0xBA, 0x0A, 0x22, 0x49, 0xC1, 0xCA, 0x5B, 0x21, 0x09, 0xB6, 0x40, + 0x4A, 0x96, 0x2A, 0xBC, 0x1C, 0x15, 0x9C, 0x6B, 0x48, 0xA7, 0x9F, 0xB9, 0x7E, 0x4A, 0x33, + 0x37, 0xD9, 0x93, 0x23, 0x74, 0x62, 0x21, 0x29, 0x74, 0x23, 0xF9, 0xBD, 0x1B, 0x12, 0xE7, + 0x84, 0x89, 0xE0, 0x1E, 0x6A, 0x10, 0xF0, 0xFD, 0x6B, 0xBA, 0x1C, 0xFD, 0x6A, 0xE1, 0xB7, + 0x5D, 0xBE, 0x69, 0xF8, 0xB8, 0xEE, 0x51, 0xA4, 0xE7, 0xF6, 0x8B, 0xA2, 0xC4, 0x07, 0xC9, + 0xC0, 0xBA, 0xD3, 0x89, 0x2B, 0x29, 0xB0, 0x17, 0x0A, 0xB7, 0x58, 0x36, 0xFD, 0xD4, 0x9A, + 0x7E, 0xE3, 0xC2, 0xBB, 0x30, 0xF2, 0xC3, 0xD2, 0x26, 0xBC, 0xEC, 0x49, 0x14, 0x09, 0x52, + 0x17, 0x0B, 0x0D, 0x16, 0x0F, 0x97, 0xB3, 0x0B, 0x7B, 0x7B, 0x09, 0x67, 0x19, 0x53, 0x86, + 0x77, 0xEB, 0xB0, 0x69, 0x22, 0xF2, 0x69, 0x25, 0x22, 0x7C, 0x88, 0x52, 0xAC, 0xC1, 0x07, + 0xA8, 0xF1, 0x73, 0xB3, 0x8D, 0x96, 0x69, 0x75, 0x84, 0xBD, 0x3D, 0xFE, 0x16, 0x9B, 0x40, + 0x73, 0xAA, 0x58, 0xA7, 0xBF, 0x37, 0x1D, 0x5C, 0x4B, 0xB0, 0xEE, 0xD3, 0x0F, 0x08, 0x21, + 0x2D, 0xEF, 0xB3, 0xAE, 0xC9, 0x02, 0xD4, 0x54, 0x60, 0x84, 0x17, 0x6B, 0xF0, 0xF8, 0x6D, + 0x93, 0xCF, 0x36, 0xA4, 0x68, 0x9A, 0x5E, 0x87, 0x4B, 0x32, 0xD6, 0xB7, 0xD3, 0xC1, 0xE3, + 0xFB, 0xCF, 0xD9, 0x88, 0xC3, 0x5D, 0xBC, 0x9A, 0x8D, 0x0A, 0x01, 0x9A, 0xD6, 0xD7, 0xE1, + 0x5E, 0xC3, 0xAC, 0x97, 0x12, 0x5D, 0xB6, 0xAB, 0xFE, 0x00, 0xBE, 0xFF, 0xE3, 0x5A, 0x81, + 0x66, 0x66, 0x99, 0xA9, 0x1E, 0x15, 0x94, 0x5C, 0x62, 0xD6, 0x46, 0x69, 0x0B, 0x5B, 0x52, + 0xDE, 0x8B, 0x83, 0x5E, 0xE9, 0xBE, 0x53, 0x58, 0x8F, 0xDE, 0x5D, 0x63, 0x02, 0x3B, 0x52, + 0xB2, 0xB1, 0xF4, 0x61, 0x0C, 0x23, 0x7A, 0x82, 0x9F, 0x59, 0x01, 0xA4, 0x60, 0x42, 0x96, + 0x3C, 0xCE, 0x7B, 0x85, 0xAA, 0x04, 0x0A, 0xDD, 0xE0, 0x29, 0x85, 0xE1, 0x4E, 0x23, 0xC4, + 0xEA, 0xDB, 0x75, 0x22, 0x1C, 0x60, 0x7D, 0x24, 0x67, 0x2E, 0x24, 0x4C, 0x66, 0xC9, 0xC2, + 0x4C, 0x3C, 0xB7, 0xFD, 0x90, 0xBB, 0x23, 0x29, 0x5C, 0x9D, 0x3D, 0x9D, 0xA5, 0x16, 0xBC, + 0xE3, 0xDD, 0x46, 0x2D, 0x66, 0x60, 0xF9, 0xF9, 0x1E, 0xF0, 0x61, 0x8A, 0x4D, 0x4D, 0x3D, + 0x66, 0x68, 0xC5, 0xD1, 0xE2, 0xE8, 0xED, 0x43, 0x3E, 0xBF, 0xE0, 0x76, 0x2B, 0xEB, 0x74, + 0x33, 0x24, 0xE1, 0x16, 0x08, 0xF8, 0xB1, 0x4B, 0x69, 0xCE, 0x4C, 0x22, 0x1C, 0x16, 0x54, + 0xBA, 0x49, 0x92, 0xA5, 0xAF, 0x2D, 0x94, 0x9C, 0x29, 0x39, 0xF9, 0x5D, 0x1C, 0x8F, 0xB7, + 0x67, 0xAF, 0x2A, 0x84, 0x3C, 0xC7, 0xC7, 0x8F, 0x57, 0x25, 0x9D, 0x5C, 0x0C, 0x6C, 0xA8, + 0x3F, 0xCA, 0x41, 0xEF, 0x5E, 0xCC, 0x4E, 0xEE, 0xEA, 0x93, 0xE4, 0x51, 0x8C, 0x24, 0xD3, + 0x04, 0x0F, 0x2C, 0xD9, 0x0D, 0xF3, 0xE5, 0x35, 0xE9, 0x89, 0xE6, 0x06, 0xFA, 0x10, 0x9E, + 0x2C, 0x45, 0x3E, 0xD7, 0x35, 0x3D, 0xB1, 0xCD, 0xB2, 0x71, 0x37, 0xF0, 0x05, 0xF9, 0xDD, + 0x8D, 0x2A, 0xEB, 0xFB, 0x72, 0x55, 0xA6, 0x09, 0x8B, 0x69, 0x02, 0x15, 0xE1, 0x00, 0xCF, + 0xE4, 0x4C, 0xA0, 0xF2, 0x74, 0x5F, 0xCE, 0xD4, 0x83, 0x22, 0xBC, 0x96, 0x67, 0xAB, 0x16, + 0xD2, 0xE1, 0xC0, 0xFF, 0x49, 0x1B, 0x96, 0xA1, 0x7B, 0x83, 0x3D, 0x4F, 0xD4, 0x4D, 0x31, + 0xC2, 0x23, 0x0A, 0xC8, 0x35, 0x79, 0x6E, 0x06, 0x3A, 0xB0, 0x30, 0x00, 0xF0, 0x4F, 0x15, + 0xC7, 0x05, 0x60, 0x03, 0x37, 0x63, 0xA4, 0x85, 0x52, 0xCC, 0xEA, 0xAC, 0xAD, 0xE9, 0xCA, + 0x5C, 0x80, 0x55, 0xF3, 0x74, 0x5E, 0x17, 0x90, 0x68, 0xA2, 0x87, 0x18, 0x3F, 0x2B, 0xC3, + 0xEF, 0x63, 0x27, 0xDE, 0xC5, 0xAC, 0x7C, 0xF7, 0xB0, 0x52, 0xEF, 0x5A, 0x88, 0x73, 0xE6, + 0x97, 0xEF, 0xDE, 0x08, 0x96, 0x88, 0xF4, 0x3B, 0xE4, 0x64, 0x82, 0x7C, 0x2F, 0xA8, 0x3E, + 0xB5, 0x31, 0xB3, 0x67, 0x41, 0x45, 0xE9, 0x5C, 0x69, 0x9C, 0x82, 0x99, 0x0E, 0x68, 0x49, + 0x67, 0xDA, 0xD3, 0x19, 0xD9, 0xF6, 0x4A, 0xB1, 0x6C, 0xD9, 0xFE, 0x9B, 0x6C, 0x41, 0x23, + 0x2A, 0xC4, 0xAE, 0x37, 0x95, 0xFD, 0x8A, 0x76, 0xAA, 0x9B, 0x02, 0xE9, 0x70, 0x24, 0x20, + 0x61, 0xC6, 0xDA, 0x45, 0xA2, 0xAF, 0x74, 0xAD, 0x9C, 0xB2, 0xA7, 0x99, 0x35, 0xC9, 0x26, + 0x25, 0xE2, 0x42, 0xF4, 0xBC, 0x7F, 0xCE, 0x54, 0xD5, 0xC1, 0x0A, 0x9E, 0x61, 0xF8, 0x75, + 0x16, 0x2F, 0xA6, 0x51, 0xB6, 0x60, 0x57, 0xBA, 0x03, 0x6F, 0x06, 0x2D, 0x6D, 0x39, 0xD0, + 0x50, 0x2B, 0x93, 0xA5, 0x64, 0x0B, 0x78, 0xC6, 0xC2, 0xFD, 0x20, 0xB0, 0x2F, 0xF8, 0x36, + 0x76, 0xA8, 0x7A, 0x94, 0x94, 0x5D, 0x47, 0x6C, 0x34, 0x98, 0x03, 0xEA, 0x4F, 0xE6, 0x0C, + 0xDC, 0xEA, 0x65, 0xBB, 0x26, 0x29, 0xE2, 0xBC, 0x09, 0xD4, 0x47, 0x2E, 0xC6, 0x34, 0x22, + 0xDE, 0xE2, 0x05, 0x2F, 0x09, 0x8D, 0xEA, 0xF5, 0x53, 0x1E, 0x6C, 0x9B, 0xED, 0x66, 0x72, + 0xA8, 0xB6, 0x99, 0x80, 0x2E, 0xFE, 0x0C, 0xDE, 0xD8, 0x0C, 0x84, 0x55, 0xF5, 0x85, 0xD1, + 0xBA, 0x63, 0x3D, 0x28, 0x1F, 0x1A, 0x21, 0xAD, 0xAB, 0x48, 0xE6, 0x3B, 0x44, 0xE0, 0xC2, + 0xA4, 0xD7, 0x60, 0x8C, 0xF9, 0x8A, 0xAB, 0xF8, 0xAD, 0xC8, 0x6B, 0xCB, 0x8F, 0x61, 0xE8, + 0xB0, 0x6C, 0xD2, 0x38, 0x5F, 0x82, 0xE0, 0xA3, 0xCD, 0xD0, 0x3C, 0xAB, 0x15, 0x2D, 0x59, + 0x51, 0x85, 0x9C, 0x45, 0x32, 0xF9, 0x16, 0x8E, 0x78, 0xF1, 0x7B, 0xA2, 0xA5, 0x77, 0x27, + 0x80, 0x32, 0x7D, 0xCF, 0x4E, 0x62, 0xB4, 0xD2, 0x6E, 0x44, 0x37, 0x62, 0xFC, 0x48, 0x8A, + 0xE4, 0xCD, 0x4D, 0x11, 0x56, 0xDB, 0xD5, 0x78, 0x25, 0x95, 0xCF, 0xD7, 0x69, 0x7A, 0x51, + 0x4A, 0xBC, 0x9B, 0x16, 0x0C, 0x9C, 0xCF, 0x08, 0xED, 0xC8, 0x61, 0x34, 0xA7, 0x55, 0xB9, + 0x0E, 0x9B, 0xB5, 0x43, 0x51, 0x1E, 0x88, 0x8E, 0x31, 0x57, 0x72, 0x1A, 0x52, 0xD1, 0xBC, + 0x5D, 0xB3, 0x30, 0x29, 0xFB, 0x33, 0x5E, 0xA2, 0x11, 0x4E, 0x21, 0xC0, 0x33, 0x68, 0xC8, + 0xD7, 0xF4, 0xD8, 0x27, 0x96, 0x06, 0x41, 0x77, 0x2A, 0x4A, 0x32, 0xA7, 0x38, 0xDF, 0x60, + 0xD1, 0x9E, 0xC7, 0x7A, 0xB0, 0x9D, 0x22, 0xF5, 0x7C, 0xC2, 0x52, 0x3B, 0x95, 0x03, 0xB3, + 0xF5, 0xB1, 0xCE, 0xBC, 0x5A, 0xE1, 0x5F, 0x88, 0x5F, 0x15, 0x98, 0x42, 0xDB, 0x73, 0x59, + 0xA1, 0xC8, 0x9D, 0x3D, 0x82, 0xD3, 0x40, 0x70, 0x68, 0xF1, 0x5B, 0x67, 0x39, 0x62, 0x6E, + 0xB8, 0xC5, 0x21, 0xFC, 0x8C, 0x5C, 0x74, 0x91, 0xF9, 0x45, 0xD4, 0x9F, 0x14, 0xE6, 0x98, + 0x9D, 0xA3, 0x40, 0xBD, 0xF4, 0x9E, 0x7F, 0x8A, 0x79, 0x27, 0x47, 0xAA, 0x65, 0x8B, 0xC1, + 0x14, 0x14, 0x3B, 0xA9, 0x3F, 0x26, 0x02, 0x2D, 0x00, 0x17, 0x35, 0xB7, 0x44, 0x63, 0x9B, + 0xBF, 0x22, 0xAA, 0xB2, 0xA1, 0x85, 0x1C, 0xFC, 0x93, 0x4F, 0x9C, 0x69, 0xD3, 0x76, 0x4F, + 0xDE, 0xA3, 0xD2, 0x3D, 0xB1, 0x79, 0x98, 0xE6, 0x13, 0x8C, 0xFD, 0x7C, 0xD9, 0xE9, 0xA4, + 0x7C, 0xB7, 0x41, 0x93, 0xBD, 0x71, 0xAA, 0xF2, 0x8C, 0xDD, 0x9D, 0x1E, 0xB5, 0x95, 0x12, + 0x55, 0x46, 0xA4, 0xF4, 0x35, 0x7E, 0xBB, 0xD1, 0xF4, 0x10, 0xE3, 0xBF, 0x85, 0x57, 0x89, + 0x2D, 0xE6, 0x85, 0x09, 0xB5, 0xB9, 0x8C, 0x5C, 0x22, 0x9E, 0x94, 0x2C, 0x91, 0x0F, 0xDD, + 0x3E, 0x54, 0xCB, 0x6A, 0xD5, 0x4D, 0x8D, 0xD8, 0x86, 0xCB, 0x97, 0xEC, 0xC0, 0x6D, 0x1E, + 0x40, 0x1B, 0x83, 0x95, 0xD0, 0xBC, 0xB0, 0xDB, 0x9A, 0x03, 0x1D, 0xC6, 0x6C, 0x92, 0x94, + 0xF9, 0x05, 0x3C, 0x68, 0xFC, 0x42, 0x04, 0x2B, 0x1F, 0xA1, 0x67, 0x1F, 0xC7, 0xD5, 0x10, + 0xB7, 0x09, 0x16, 0xC0, 0x13, 0x9C, 0xFE, 0xBE, 0x3A, 0x91, 0x24, 0x45, 0x27, 0xCE, 0x94, + 0x39, 0x86, 0x0C, 0xED, 0xB3, 0x09, 0x08, 0x19, 0x7B, 0xE8, 0x51, 0xCB, 0xD1, 0xD3, 0xB1, + 0x8C, 0xA5, 0x41, 0x35, 0x84, 0x49, 0xFB, 0x34, 0xFB, 0x5C, 0xD5, 0x69, 0x63, 0x0E, 0xD5, + 0xF6, 0x7B, 0x87, 0x95, 0xE8, 0x78, 0x28, 0xF2, 0xCE, 0x3B, 0xEC, 0xFE, 0x45, 0x75, 0x79, + 0xD8, 0x23, 0x33, 0xB0, 0xBB, 0xAB, 0x09, 0x4D, 0xE3, 0x91, 0xE1, 0xF8, 0x15, 0x7B, 0xD4, + 0x31, 0xE3, 0x65, 0xCA, 0x86, 0x46, 0x30, 0x93, 0x2B, 0xBE, 0xBB, 0x48, 0xF4, 0x5F, 0x81, + 0x34, 0x42, 0x4E, 0x18, 0xAB, 0x45, 0x50, 0x29, 0xB5, 0x4B, 0x19, 0xE2, 0xF3, 0xBF, 0xEC, + 0x5E, 0x44, 0xAD, 0x0E, 0xA5, 0xC0, 0x3F, 0x53, 0xD8, 0xF9, 0x25, 0xB6, 0x35, 0x83, 0x8A, + 0xA7, 0x01, 0x5A, 0x7C, 0x9E, 0x32, 0x5B, 0xDF, 0xAF, 0xF9, 0x66, 0xEA, 0x95, 0x12, 0xDD, + 0x50, 0xF8, 0x7C, 0x89, 0x95, 0xCE, 0xA7, 0x56, 0x1C, 0x23, 0xF4, 0xFB, 0x06, 0xD9, 0x64, + 0xAB, 0x8F, 0x19, 0x13, 0xA6, 0xCA, 0x17, 0xE4, 0xCA, 0x60, 0xD6, 0xBC, 0x07, 0x8E, 0x17, + 0x84, 0xF8, 0x9C, 0x67, 0x3C, 0x91, 0xD9, 0x55, 0xBC, 0xF4, 0x5F, 0x58, 0xCA, 0x97, 0x09, + 0x57, 0x9D, 0x5E, 0x38, 0x31, 0xDF, 0x12, 0xCF, 0xDB, 0x75, 0x16, 0xFD, 0x21, 0x87, 0x8C, + 0xB5, 0x42, 0x43, 0x57, 0x9B, 0x93, 0x46, 0xD2, 0xDE, 0x4B, 0xE2, 0x5F, 0x50, 0x8E, 0x84, + 0xB1, 0xAD, 0xC7, 0x8C, 0xB9, 0x1C, 0x03, 0xDA, 0x3C, 0x4F, 0xD5, 0x9E, 0x45, 0x29, 0x18, + 0x98, 0x38, 0xF7, 0x4F, 0x63, 0x12, 0x82, 0x06, 0x20, 0xA5, 0x99, 0x6B, 0x79, 0x1F, 0xFC, + 0xB3, 0x32, 0xF8, 0x47, 0x09, 0x46, 0x13, 0xF2, 0x14, 0x8B, 0x86, 0x20, 0x34, 0xFA, 0x89, + 0xD0, 0xD0, 0xFF, 0x18, 0x08, 0xD9, 0x02, 0xC5, 0xD1, 0xAF, 0x64, 0xD5, 0x52, 0x24, 0x92, + 0xD6, 0x1E, 0xCD, 0xE4, 0xC7, 0x3B, 0xE8, 0x9A, 0x33, 0x78, 0x2C, 0xEF, 0x1A, 0xCC, 0x1D, + 0xC3, 0x27, 0xFB, 0x2E, 0xB9, 0xD1, 0x76, 0x42, 0x20, 0x9B, 0x85, 0xAA, 0x8B, 0x1D, 0xC5, + 0x7C, 0xBF, 0x06, 0x7C, 0x7A, 0xA2, 0x9D, 0xA6, 0xB7, 0xE1, 0x57, 0xD2, 0x3E, 0x17, 0x1D, + 0x3A, 0xE6, 0xF3, 0x85, 0x58, 0x34, 0x07, 0x17, 0x91, 0x40, 0x2C, 0x85, 0x1F, 0xF2, 0xDD, + 0x67, 0x10, 0x99, 0x79, 0xF7, 0xEE, 0x5E, 0x09, 0xE6, 0x4B, 0x4E, 0xEF, 0xDE, 0xE7, 0x11, + 0x2B, 0x55, 0xCE, 0x20, 0x0B, 0xB8, 0xC8, 0x05, 0x1E, 0x34, 0x28, 0xC3, 0x05, 0xFA, 0x1D, + 0x57, 0x6B, 0xFF, 0xBB, 0x25, 0xA7, 0x0E, 0xB5, 0x71, 0x16, 0x8F, 0xC6, 0x0D, 0xAD, 0xD9, + 0x28, 0xB1, 0x0C, 0xFD, 0x07, 0xDE, 0x80, 0xA8, 0x5B, 0x8D, 0xF3, 0xED, 0xC3, 0x72, 0xD4, + 0x88, 0xC2, 0x1F, 0x0D, 0x57, 0x87, 0x61, 0x1C, 0xC6, 0xFB, 0x73, 0xAE, 0xEB, 0x6F, 0x92, + 0x0A, 0x10, 0x92, 0x94, 0xB4, 0x9D, 0x38, 0x70, 0xF9, 0x0D, 0xE3, 0xB3, 0x60, 0xD1, 0x4D, + 0xF7, 0x7E, 0xF9, 0x56, 0x40, 0xBC, 0xAC, 0x7A, 0x4D, 0xBC, 0xA9, 0x01, 0xA3, 0x1D, 0xB8, + 0x3E, 0x83, 0xF5, 0xC5, 0x9C, 0xE3, 0x27, 0x20, 0x7E, 0xA9, 0xB2, 0x7C, 0x3B, 0x97, 0x8D, + 0x30, 0xD5, 0x38, 0x65, 0xC1, 0xB8, 0x47, 0x64, 0xF0, 0x25, 0xE8, 0x73, 0x2D, 0x50, 0x07, + 0x55, 0x4C, 0xE5, 0xC9, 0xCC, 0x41, 0x0B, 0x2E, 0xEF, 0xD7, 0xE4, 0xD9, 0x90, 0xC5, 0x38, + 0x55, 0x76, 0x06, 0xA6, 0xBC, 0x47, 0x57, 0x7A, 0x43, 0x76, 0x8D, 0x30, 0xAA, 0x3E, 0x85, + 0x98, 0xFD, 0x6F, 0x4F, 0xE7, 0xAC, 0x43, 0x9F, 0x39, 0x31, 0xC5, 0x8F, 0xD6, 0x9D, 0x90, + 0x76, 0x5A, 0xC9, 0xF4, 0x56, 0xAC, 0x7D, 0xE0, 0x85, 0xE1, 0x4A, 0x08, 0x98, 0xC4, 0x55, + 0x7F, 0x5D, 0x3B, 0xAA, 0xAE, 0x07, 0xED, 0xC6, 0x07, 0xDE, 0x69, 0x00, 0x14, 0x6B, 0x97, + 0xB3, 0x5A, 0xAE, 0x57, 0x01, 0x53, 0xDC, 0x10, 0x78, 0x15, 0xEF, 0x9F, 0xEB, 0xDD, 0x4F, + 0xD5, 0x67, 0xD6, 0x37, 0xFE, 0xE8, 0xF8, 0xBF, 0xB4, 0xB3, 0x41, 0x3E, 0xA6, 0xAE, 0xAD, + 0x48, 0x46, 0xAB, 0x73, 0x3A, 0x04, 0xF1, 0xE4, 0xBC, 0x32, 0xA3, 0xBB, 0xB1, 0xC1, 0x6B, + 0xAF, 0x8D, 0x0B, 0xDB, 0x9C, 0xCB, 0x82, 0xFE, 0x46, 0x47, 0x9C, 0xCF, 0xD0, 0x40, 0xB5, + 0xE6, 0x40, 0x64, 0xE5, 0x39, 0xB3, 0x9C, 0x66, 0xE4, 0x50, 0x1D, 0xC8, 0x22, 0x87, 0x3A, + 0xC6, 0x11, 0x9A, 0x4A, 0x11, 0x2A, 0x1F, 0x7C, 0xD6, 0xDF, 0x0E, 0x5F, 0x84, 0x35, 0x6C, + 0xE8, 0x53, 0xCE, 0xD3, 0x4C, 0xE6, 0x9A, 0x9E, 0x73, 0x83, 0x53, 0x49, 0x83, 0xC5, 0x1C, + 0x50, 0x26, 0x9B, 0xF8, 0xB9, 0x58, 0x6A, 0x0E, 0x5B, 0xA9, 0x05, 0xFD, 0x3B, 0xCE, 0x08, + 0x0B, 0x00, 0xE7, 0xF7, 0xD4, 0x8E, 0x55, 0xF4, 0x89, 0x47, 0x9B, 0x57, 0x71, 0xE9, 0x95, + 0xFB, 0x02, 0x0E, 0x58, 0xFE, 0xB7, 0x4A, 0xF6, 0x5C, 0x3E, 0xE7, 0x6A, 0xA4, 0xE6, 0x9B, + 0x5B, 0xA8, 0xBD, 0xA2, 0x49, 0xA1, 0xB2, 0xD6, 0x2C, 0x08, 0xD4, 0x18, 0xC3, 0x63, 0x5D, + 0x06, 0x18, 0x46, 0x04, 0x08, 0x43, 0x99, 0x1A, 0xB4, 0x75, 0x47, 0x3D, 0xA8, 0x5D, 0x94, + 0x98, 0x1F, 0xB8, 0x44, 0x25, 0xE7, 0xAD, 0x95, 0x1C, 0xE0, 0xA4, 0x2B, 0xE6, 0x42, 0xFE, + 0x65, 0x8B, 0x7A, 0xAA, 0xE7, 0x2B, 0x14, 0x7C, 0xDC, 0x08, 0x6C, 0x24, 0xB1, 0x57, 0x1E, + 0xB2, 0x27, 0x2E, 0x2A, 0x72, 0xB1, 0x56, 0x60, 0xD8, 0x54, 0xEB, 0xE1, 0x9E, 0xC7, 0xD9, + 0xAB, 0x7E, 0xA1, 0x78, 0x00, 0xD0, 0xB6, 0xAE, 0x72, 0x7B, 0x39, 0x21, 0x74, 0x67, 0xC6, + 0x62, 0xBA, 0x08, 0xE6, 0xF1, 0x91, 0x93, 0x95, 0x1E, 0xEF, 0xF0, 0x28, 0x06, 0xA7, 0x84, + 0x3E, 0xB5, 0xC7, 0x1B, 0x2F, 0x04, 0xDC, 0xB6, 0x05, 0xEC, 0xED, 0xC5, 0x12, 0x8C, 0xD6, + 0x77, 0x03, 0x03, 0x8C, 0x44, 0xBF, 0x20, 0xFE, 0x06, 0xF3, 0xED, 0x8C, 0x13, 0x68, 0xFE, + 0x38, 0xE7, 0x29, 0x44, 0xD5, 0xC5, 0x2F, 0xCA, 0x46, 0xA4, 0x5F, 0xD4, 0x8D, 0x8F, 0xE5, + 0xDA, 0x64, 0x18, 0x3D, 0x4D, 0x62, 0xEC, 0x01, 0xAA, 0x3D, 0x9D, 0x67, 0x2E, 0xC6, 0x7A, + 0x01, 0xC1, 0x7F, 0x21, 0xE0, 0x25, 0x25, 0xF0, 0x51, 0x3C, 0xCE, 0x03, 0x0C, 0x66, 0x4F, + 0xC8, 0x78, 0x47, 0x63, 0x08, 0x66, 0x08, 0xBC, 0x80, 0x99, 0xC2, 0x04, 0xC2, 0x55, 0xFF, + 0xED, 0x1D, 0xAF, 0x43, 0x2C, 0xEB, 0x45, 0xFB, 0xD1, 0x35, 0xE2, 0x1E, 0x81, 0x90, 0xC5, + 0xBF, 0xEE, 0x19, 0x21, 0x71, 0xFA, 0xA7, 0x75, 0x20, 0x48, 0x1E, 0x69, 0xE8, 0x7B, 0x7F, + 0x76, 0x79, 0x0B, 0xEF, 0x76, 0xCB, 0x8D, 0x3C, 0x88, 0xF5, 0xC6, 0xE3, 0x2F, 0xC5, 0x9E, + 0x7B, 0xD4, 0x53, 0x51, 0xD6, 0x66, 0x96, 0xB6, 0x1D, 0x9F, 0x40, 0x72, 0x6F, 0xB9, 0xA9, + 0x80, 0x00, 0xB6, 0x87, 0x38, 0xCF, 0x7E, 0x34, 0xB9, 0x8B, 0x6A, 0x4A, 0xAA, 0x2A, 0xC1, + 0xD7, 0xB1, 0x40, 0x7D, 0xB8, 0x97, 0x83, 0xF8, 0x07, 0x71, 0x03, 0xEA, 0x9C, 0x9E, 0x89, + 0x24, 0x7E, 0xAE, 0x07, 0x8A, 0xDF, 0xB3, 0x6E, 0x21, 0x47, 0x4C, 0x3B, 0xB1, 0xFE, 0x0C, + 0x87, 0x68, 0x7C, 0x62, 0x33, 0xA5, 0x33, 0xA0, 0x1E, 0x10, 0x81, 0xB9, 0x3A, 0x35, 0x21, + 0xD3, 0x39, 0xF3, 0x9C, 0x07, 0x56, 0x09, 0xBA, 0xCE, 0x53, 0x19, 0x94, 0x98, 0x8A, 0xE3, + 0x14, 0xF7, 0x7F, 0xC6, 0x03, 0x41, 0x13, 0xA1, 0x38, 0xC6, 0x7E, 0xB7, 0xE0, 0x37, 0x50, + 0xCB, 0xEC, 0x8D, 0x28, 0xBD, 0x21, 0xAF, 0xED, 0xFE, 0xFA, 0x8F, 0x09, 0x16, 0x19, 0xAE, + 0x50, 0x0B, 0x4C, 0xA4, 0x59, 0x9D, 0x01, 0x9D, 0xC8, 0xCA, 0x4B, 0xF1, 0x18, 0xD7, 0x0B, + 0x86, 0x76, 0xDF, 0xC7, 0x96, 0xA4, 0xF6, 0xD9, 0x86, 0xAD, 0xBA, 0x4C, 0x85, 0x74, 0xED, + 0x4A, 0xBA, 0xEA, 0x54, 0x65, 0x46, 0x62, 0x20, 0xE5, 0xE5, 0x3D, 0xC8, 0xFC, 0xB3, 0x95, + 0xD1, 0xE5, 0x9D, 0x27, 0x86, 0x73, 0xCB, 0xC4, 0xE3, 0xF4, 0x06, 0x58, 0xDF, 0x98, 0xAC, + 0x2F, 0xD1, 0x26, 0xA9, 0x49, 0x22, 0x87, 0x9E, 0x1A, 0x3B, 0xE9, 0x1C, 0x1A, 0xCC, 0x20, + 0x80, 0x3C, 0x35, 0xFA, 0x76, 0x4A, 0xBB, 0xAD, 0xAB, 0x07, 0xBD, 0xE8, 0x5F, 0xF4, 0xBD, + 0x9E, 0x0F, 0xB6, 0xF0, 0x6B, 0xAF, 0x5B, 0xF4, 0x2B, 0x8A, 0x2C, 0xBF, 0x6C, 0x2F, 0x62, + 0x60, 0x6B, 0xEC, 0xC3, 0x61, 0x55, 0x29, 0x21, 0xA1, 0x2D, 0x6C, 0x82, 0x36, 0xFD, 0xE8, + 0x4D, 0xB4, 0xBD, 0xDA, 0xC7, 0x7E, 0x88, 0x72, 0x47, 0x8C, 0xFF, 0xC4, 0xE1, 0x48, 0xC1, + 0xC7, 0xAC, 0xFE, 0xDF, 0x6B, 0x17, 0xD9, 0x87, 0x31, 0xC2, 0xDE, 0x36, 0xF3, 0xCB, 0xEF, + 0x1F, 0x6F, 0x78, 0x1A, 0x94, 0x0E, 0x08, 0x74, 0xD5, 0xB7, 0x45, 0x35, 0xBB, 0xE0, 0x66, + 0xB5, 0x30, 0x64, 0xD4, 0x3B, 0x13, 0x92, 0x65, 0x70, 0xA9, 0xE1, 0xC4, 0xE6, 0xDA, 0x20, + 0x6C, 0x8B, 0xD2, 0x52, 0xCA, 0xF2, 0xB6, 0x2E, 0x7D, 0x22, 0x3F, 0x7A, 0xC1, 0x29, 0x39, + 0x13, 0x7F, 0x33, 0x0B, 0xE5, 0x93, 0x74, 0xD7, 0x29, 0x5A, 0x6C, 0x2D, 0xFF, 0x92, 0xE0, + 0x7C, 0x72, 0x75, 0x10, 0xE4, 0x8D, 0x97, 0x05, 0x93, 0xE4, 0x72, 0x29, 0xFC, 0x8B, 0xC3, + 0xBD, 0x5B, 0x8E, 0xA7, 0x80, 0xDA, 0xCF, 0xF4, 0xD2, 0x30, 0x63, 0xDF, 0x65, 0xFE, 0xDA, + 0x5F, 0x8F, 0x65, 0xB1, 0x7A, 0x33, 0x3E, 0x53, 0x2A, 0xCA, 0xD7, 0x91, 0x67, 0x80, 0xC7, + 0x4D, 0x6A, 0x70, 0xD3, 0x8B, 0x36, 0x7F, 0x3F, 0x6F, 0x4E, 0x94, 0x7B, 0x85, 0xFC, 0x15, + 0x23, 0x5B, 0xBE, 0x46, 0xB2, 0x64, 0x95, 0xD2, 0x78, 0x00, 0x98, 0xDB, 0x85, 0x3A, 0x93, + 0x13, 0x77, 0xCF, 0xBE, 0xDE, 0xA6, 0x20, 0xF2, 0x35, 0x5C, 0xA2, 0x1E, 0x81, 0xCE, 0x9E, + 0x00, 0x78, 0xB0, 0xDD, 0x6C, 0xB7, 0x0F, 0x23, 0xED, 0x55, 0x86, 0x82, 0xBE, 0x3B, 0x3D, + 0x59, 0x4E, 0xEF, 0xE8, 0x53, 0x44, 0xE1, 0xF2, 0x75, 0x42, 0x8B, 0x31, 0x6C, 0xC0, 0x88, + 0x99, 0x59, 0x39, 0x29, 0x8F, 0x2A, 0x2D, 0x15, 0xAC, 0x9B, 0x67, 0x6A, 0xC3, 0xE9, 0xCB, + 0x92, 0xF2, 0xA6, 0x4D, 0xEC, 0x77, 0x32, 0xA9, 0x1F, 0xC7, 0x61, 0xAA, 0x1B, 0x12, 0x6E, + 0xA5, 0x75, 0xE3, 0x95, 0x31, 0x77, 0xDA, 0x6E, 0x1C, 0xD7, 0x8F, 0xAE, 0xA8, 0x24, 0x66, + 0x53, 0x30, 0xA8, 0x1D, 0x9E, 0x24, 0x57, 0x2B, 0x98, 0x60, 0xBF, 0x0A, 0xBA, 0x4D, 0xF8, + 0xBD, 0x5D, 0x4E, 0x3E, 0x2C, 0x72, 0xBB, 0xFB, 0xB2, 0xA9, 0x85, 0xC7, 0xAE, 0x2F, 0x07, + 0x79, 0x51, 0xFE, 0x84, 0x01, 0xE1, 0xD1, 0x56, 0xEC, 0xAD, 0xA1, 0xE3, 0x53, 0x81, 0x7B, + 0x20, 0xF4, 0x1E, 0x0B, 0x24, 0x60, 0xA0, 0xCA, 0xAF, 0x2B, 0x36, 0xD6, 0xA7, 0xF1, 0xB3, + 0x51, 0x25, 0xD7, 0x97, 0xDC, 0xC7, 0x14, 0x42, 0x10, 0x27, 0xD1, 0x41, 0x71, 0x76, 0x5A, + 0x64, 0x60, 0x71, 0xED, 0x95, 0x2B, 0x6A, 0x52, 0x94, 0xEE, 0xCF, 0x6A, 0x3A, 0x71, 0xC1, + 0x04, 0xC8, 0x43, 0xA4, 0xA8, 0xB3, 0xEF, 0xCC, 0x27, 0x46, 0x7B, 0x20, 0xCB, 0x0A, 0x94, + 0xAB, 0xF5, 0x80, 0x22, 0x29, 0xEA, 0x4D, 0x83, 0x12, 0x78, 0x3E, 0x78, 0x79, 0x1A, 0x50, + 0xB3, 0xC0, 0xA8, 0x8F, 0xE6, 0x49, 0x71, 0x98, 0xCD, 0x4B, 0xF4, 0x70, 0xDA, 0xC4, 0x6F, + 0x34, 0xE5, 0x00, 0x19, 0xFD, 0xEE, 0x20, 0x40, 0xCF, 0xE9, 0x91, 0x24, 0xB3, 0x12, 0xB1, + 0x12, 0x2B, 0x83, 0xE5, 0x1D, 0x87, 0x88, 0x77, 0xCE, 0xC0, 0x85, 0x5F, 0x11, 0x58, 0xC4, + 0x45, 0xCF, 0xDC, 0x22, 0x53, 0xF4, 0x38, 0x9D, 0x5E, 0x3A, 0x8B, 0xA1, 0x66, 0x9A, 0xBB, + 0x59, 0x76, 0xA4, 0x61, 0x7E, 0x85, 0xF5, 0x43, 0xDA, 0x9F, 0x5E, 0x30, 0xB1, 0x0C, 0xA7, + 0x48, 0x1C, 0x81, 0x85, 0x39, 0x27, 0x82, 0xB4, 0x6F, 0xB0, 0xA0, 0xE5, 0xAB, 0x40, 0x8B, + 0x29, 0x45, 0xE3, 0xC7, 0x9A, 0x1C, 0xC4, 0x9F, 0xB7, 0xC2, 0x72, 0x54, 0xA9, 0xB5, 0x40, + 0xE7, 0x39, 0x7A, 0x5B, 0x65, 0x5B, 0xF7, 0xE4, 0xF8, 0x31, 0x84, 0xDB, 0x32, 0xA1, 0x28, + 0xAA, 0x2E, 0x00, 0xA6, 0x24, 0xD7, 0xDC, 0xD6, 0xB7, 0x7E, 0xFD, 0x15, 0x1F, 0x1E, 0x5C, + 0xBA, 0x88, 0x90, 0xAF, 0x91, 0x70, 0xFA, 0x06, 0xC5, 0x55, 0x71, 0x5D, 0xC1, 0x78, 0x7E, + 0x99, 0x5A, 0xD1, 0x92, 0x70, 0x97, 0x3A, 0xE9, 0x5B, 0x88, 0xDC, 0xAF, 0xDA, 0xF6, 0x2C, + 0x28, 0x84, 0x3D, 0x3F, 0x8B, 0x9C, 0x78, 0xDC, 0x8E, 0x37, 0xD9, 0x11, 0xDA, 0xB3, 0xF7, + 0xEE, 0x9D, 0x4C, 0x73, 0x89, 0xC6, 0x54, 0xBD, 0xCF, 0xC0, 0x50, 0x56, 0xB3, 0x60, 0x02, + 0x01, 0x40, 0xE5, 0x7E, 0x31, 0x47, 0x32, 0x58, 0xA4, 0x08, 0x1E, 0x2A, 0x70, 0x8F, 0x7C, + 0xAB, 0xA9, 0x0C, 0x35, 0x6D, 0x08, 0x47, 0x09, 0x8F, 0xC0, 0x76, 0x24, 0x84, 0x08, 0x6A, + 0xBA, 0x89, 0x8A, 0x60, 0xB0, 0x23, 0xD6, 0xA3, 0x06, 0x04, 0x02, 0x40, 0x62, 0x40, 0x74, + 0x87, 0x85, 0xD5, 0x1C, 0xAF, 0xF5, 0x2A, 0x0E, 0xF3, 0xDC, 0x2A, 0x45, 0xDA, 0xDF, 0x80, + 0xAC, 0x18, 0x50, 0x2D, 0x24, 0x42, 0x2A, 0x8C, 0xBB, 0x10, 0x19, 0x2B, 0x88, 0xF4, 0xE9, + 0x16, 0x02, 0x06, 0xB2, 0xED, 0x3E, 0x04, 0x11, 0x4F, 0x2A, 0x33, 0x9D, 0xF2, 0x69, 0xE2, + 0xC3, 0x6B, 0x86, 0x13, 0xCE, 0x37, 0x08, 0x74, 0x71, 0x70, 0x17, 0x55, 0x33, 0x0C, 0xB5, + 0x59, 0x57, 0x53, 0x66, 0xEE, 0x0F, 0xA2, 0xD3, 0xAF, 0xCD, 0xA3, 0x2E, 0xED, 0xE6, 0xDD, + 0x90, 0x63, 0x45, 0xDA, 0xAA, 0x04, 0x81, 0x21, 0x98, 0xE9, 0x6C, 0x42, 0x23, 0x9C, 0x24, + 0x2E, 0xDB, 0x90, 0x05, 0x97, 0x09, 0xF4, 0x97, 0xDA, 0x5B, 0x87, 0x70, 0x53, 0x84, 0xAE, + 0xF2, 0xAF, 0x22, 0xDC, 0x2E, 0xDF, 0xEF, 0x3C, 0x00, 0xD8, 0xC9, 0x15, 0x6D, 0x8B, 0x31, + 0x63, 0xFE, 0x7A, 0x77, 0x79, 0xE0, 0x4F, 0x04, 0x91, 0x1A, 0x8B, 0x93, 0x4F, 0xB3, 0x07, + 0x2E, 0xEE, 0x84, 0x44, 0x84, 0xFE, 0xDE, 0x5E, 0x2E, 0xE9, 0x6D, 0x33, 0x8E, 0xEF, 0xE2, + 0xDA, 0x98, 0x60, 0x67, 0xFF, 0xE0, 0x21, 0x8A, 0xDA, 0x7D, 0xE1, 0xD0, 0xE4, 0x2D, 0x82, + 0x3D, 0x6B, 0x03, 0x39, 0x18, 0x27, 0x88, 0x88, 0xBA, 0x06, 0x08, 0xAB, 0x8F, 0x7B, 0xE9, + 0x97, 0xBD, 0xF2, 0x63, 0x68, 0x9A, 0x36, 0xF5, 0x20, 0x4C, 0x80, 0x2A, 0xD8, 0x36, 0x36, + 0x37, 0x79, 0xB4, 0xB0, 0xD6, 0xCE, 0x50, 0x83, 0xDF, 0x0B, 0x98, 0xA2, 0xE2, 0xC7, 0x00, + 0x06, 0x2A, 0x4F, 0xA5, 0xE5, 0x7B, 0xC7, 0x3B, 0xD4, 0x53, 0x57, 0xE0, 0x1D, 0x90, 0xC7, + 0x95, 0x4B, 0xC6, 0x90, 0x4D, 0x1C, 0xE8, 0x16, 0x6A, 0x91, 0x68, 0xDA, 0x39, 0xA6, 0x0C, + 0x5C, 0xAE, 0x81, 0x19, 0xBB, 0x6B, 0x9A, 0xB0, 0x74, 0xFB, 0x2D, 0x0A, 0xEE, 0x38, 0x4F, + 0xC2, 0xC0, 0xE4, 0x80, 0x68, 0x11, 0xD6, 0x00, 0x2B, 0x4E, 0x24, 0x01, 0xE7, 0x43, 0x0B, + 0x50, 0xCB, 0x0E, 0x80, 0x75, 0xF3, 0x3D, 0x53, 0x86, 0xAE, 0xCD, 0xE2, 0x56, 0xE1, 0x69, + 0xD9, 0x5E, 0x2F, 0x9C, 0x65, 0x56, 0xC0, 0x8B, 0xA0, 0x42, 0xE6, 0x8A, 0x53, 0xCE, 0x8A, + 0xCA, 0x9C, 0xC0, 0x28, 0x18, 0xF7, 0x38, 0x2F, 0x15, 0x0D, 0xC0, 0x4D, 0xE0, 0x01, 0x9B, + 0x19, 0xC7, 0xA3, 0xAB, 0x0D, 0x72, 0xD6, 0xED, 0x01, 0x3D, 0x7A, 0x11, 0x5D, 0x74, 0xB2, + 0x79, 0xF7, 0x1F, 0xD6, 0xEF, 0xFE, 0xF3, 0x40, 0x49, 0x87, 0x7E, 0x0B, 0x11, 0xE0, 0x65, + 0x9B, 0xE9, 0x38, 0xA5, 0xDE, 0x68, 0x4E, 0xAF, 0x23, 0x51, 0x30, 0x95, 0xEB, 0x4A, 0x1B, + 0xDF, 0x53, 0x6C, 0x3C, 0x01, 0xA4, 0x65, 0x5C, 0x4B, 0x4A, 0x06, 0x73, 0x21, 0x4C, 0xEF, + 0x29, 0xA4, 0x81, 0xD0, 0x6A, 0x02, 0xCC, 0x9A, 0x5B, 0xFC, 0x7B, 0x8D, 0x84, 0x6C, 0x33, + 0x48, 0x4C, 0xD6, 0x7B, 0x1D, 0xE9, 0x8F, 0x60, 0xB6, 0x99, 0x18, 0xF1, 0x77, 0xB6, 0x45, + 0x58, 0xCA, 0x56, 0x7A, 0x62, 0x37, 0xD3, 0x5F, 0xF0, 0x17, 0x71, 0xA4, 0x23, 0x20, 0xCE, + 0x02, 0xBB, 0x98, 0xF3, 0xE4, 0xAD, 0x4A, 0xC7, 0xDB, 0x75, 0x61, 0x1B, 0xD9, 0x96, 0x1E, + 0xAC, 0x66, 0x2A, 0x38, 0xB1, 0xF7, 0x85, 0x97, 0x0C, 0x99, 0xF3, 0xDD, 0x10, 0x5F, 0xF5, + 0x86, 0xF6, 0x13, 0x01, 0xC4, 0x8D, 0x66, 0x70, 0x8C, 0xBF, 0x7D, 0x53, 0xA7, 0x33, 0xE3, + 0x57, 0xB6, 0xC2, 0x56, 0xE8, 0xB7, 0x3F, 0x0E, 0x13, 0x05, 0xA0, 0xBC, 0x13, 0x79, 0x89, + 0xE5, 0x21, 0x10, 0x0C, 0x2E, 0xE6, 0x25, 0x9E, 0x60, 0x7F, 0xE1, 0x21, 0x98, 0xE8, 0xBC, + 0xB9, 0x88, 0xB0, 0x85, 0x46, 0x68, 0xE4, 0x0D, 0x7C, 0xAE, 0x6A, 0xDC, 0x3B, 0xA4, 0x0B, + 0xA1, 0x21, 0xB7, 0x31, 0x9D, 0x06, 0xD9, 0x88, 0xA0, 0x73, 0xD0, 0x30, 0x97, 0xB9, 0xF5, + 0xC1, 0xC0, 0x72, 0x84, 0xB6, 0x47, 0x3A, 0xE5, 0x7B, 0xF1, 0x54, 0x81, 0x1B, 0x77, 0xBA, + 0xCE, 0xB0, 0x41, 0x2B, 0x8A, 0x69, 0x83, 0xBD, 0xD0, 0xCC, 0xD9, 0xE3, 0xBF, 0x01, 0x4E, + 0x52, 0x00, 0x09, 0xCB, 0x26, 0xD5, 0x78, 0x0E, 0xAB, 0xEF, 0x1B, 0xBA, 0xFA, 0x5E, 0x25, + 0xD4, 0x10, 0x98, 0xA5, 0x4C, 0x47, 0xFC, 0xE8, 0xB6, 0x8D, 0x39, 0x52, 0x91, 0xD5, 0x42, + 0x84, 0xD3, 0x3A, 0xA5, 0x0B, 0x96, 0x64, 0xD1, 0x51, 0x0B, 0x46, 0x7C, 0x8A, 0x53, 0x93, + 0x61, 0xCA, 0x9A, 0x44, 0x48, 0xBC, 0x01, 0xFC, 0xB4, 0xC4, 0xE3, 0xEF, 0x47, 0x5E, 0x8A, + 0xFB, 0x46, 0xA4, 0x94, 0xAE, 0x13, 0xEE, 0x9E, 0xA8, 0xA1, 0x26, 0x68, 0x25, 0xFB, 0xA7, + 0xF3, 0x2B, 0x97, 0x12, 0xFD, 0xE2, 0x52, 0x69, 0x8A, 0x68, 0x35, 0x9B, 0x50, 0x14, 0x1D, + 0x90, 0xF5, 0xC4, 0xA0, 0x62, 0x83, 0xDD, 0xB5, 0x4A, 0xD7, 0xE1, 0x41, 0x2A, 0xC5, 0xEB, + 0xB1, 0x25, 0x01, 0xF7, 0xA8, 0x2B, 0x2A, 0x7F, 0x27, 0xB2, 0xDB, 0xAB, 0x62, 0x6C, 0x3D, + 0xB4, 0x07, 0x45, 0x23, 0xB3, 0x21, 0x1D, 0x31, 0x82, 0xEA, 0x26, 0x13, 0x97, 0xA6, 0xF7, + 0xB1, 0x87, 0xCF, 0x2B, 0x8A, 0x35, 0x6D, 0xED, 0x10, 0x81, 0x2F, 0x1D, 0x30, 0x51, 0x69, + 0xAE, 0xDF, 0x79, 0xB5, 0xFF, 0x1C, 0xF7, 0xC2, 0xD6, 0xE8, 0x6E, 0xE1, 0x1F, 0x28, 0xE9, + 0x6A, 0xA6, 0x3B, 0x5A, 0x03, 0xF5, 0x9F, 0xC9, 0x60, 0xAC, 0x7D, 0x05, 0x72, 0xE9, 0x1D, + 0xDA, 0x61, 0x90, 0x5C, 0x07, 0x11, 0xA9, 0xB2, 0x63, 0x44, 0xA2, 0xA1, 0x0A, 0xA2, 0x04, + 0x1F, 0x2B, 0x13, 0xCB, 0x1A, 0x9A, 0x9A, 0x27, 0x77, 0x4B, 0x6D, 0x0D, 0xED, 0xDC, 0x9D, + 0x81, 0xEA, 0x1B, 0x14, 0x2A, 0xD7, 0xB7, 0x2B, 0xE4, 0x79, 0x91, 0xF2, 0xC9, 0x26, 0x1D, + 0x67, 0x08, 0x15, 0x6E, 0x38, 0xD0, 0x0B, 0x07, 0x40, 0x20, 0x76, 0x6E, 0xB0, 0xC4, 0x94, + 0x39, 0x2D, 0x65, 0xB8, 0x2C, 0xA6, 0x5F, 0x7C, 0x33, 0x52, 0xF9, 0xBB, 0x78, 0x32, 0x5E, + 0xCB, 0x6D, 0xF5, 0x96, 0xC8, 0xAE, 0x57, 0x82, 0x6B, 0x08, 0xCC, 0xD6, 0xF1, 0xD5, 0x29, + 0xD2, 0xE2, 0x59, 0x25, 0xC1, 0xAC, 0x97, 0x24, 0x25, 0xBE, 0xDE, 0xB8, 0x8A, 0x5D, 0x0E, + 0x31, 0x38, 0xDD, 0xC4, 0x34, 0xDA, 0x34, 0x62, 0xEB, 0xDF, 0x6B, 0x12, 0x39, 0xA2, 0x1F, + 0x14, 0x1E, 0xC6, 0x2C, 0xBE, 0x4B, 0xB9, 0x93, 0xBA, 0x25, 0x3B, 0x55, 0xA7, 0x6D, 0x30, + 0xFA, 0xC1, 0x9C, 0x2C, 0x13, 0x84, 0xEF, 0x6B, 0x97, 0x46, 0xC0, 0x77, 0x87, 0xAA, 0x1F, + 0xE9, 0x13, 0xA1, 0x34, 0x83, 0x90, 0xBD, 0x8C, 0x1F, 0x38, 0x6A, 0x08, 0xC7, 0x7C, 0xF7, + 0x10, 0x6C, 0x92, 0x7C, 0xE2, 0x4D, 0xFF, 0xC9, 0xD6, 0xEE, 0x1B, 0x32, 0x35, 0x4D, 0x95, + 0xED, 0x29, 0x23, 0x48, 0x25, 0x31, 0xDE, 0x6B, 0x39, 0x0B, 0xF0, 0xF5, 0xEB, 0x80, 0x27, + 0x6E, 0x90, 0xE7, 0xED, 0x11, 0x13, 0x1C, 0x84, 0x8B, 0xBA, 0xBE, 0xC4, 0xD3, 0x17, 0x23, + 0x62, 0x69, 0xA0, 0xA3, 0xA7, 0xCB, 0xE0, 0xF1, 0x27, 0x2F, 0x93, 0x94, 0x9C, 0xA6, 0xD2, + 0x3E, 0xA2, 0xA7, 0xEE, 0x3F, 0x69, 0x77, 0x91, 0xAA, 0xB7, 0x15, 0x33, 0x42, 0x30, 0x66, + 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x0E, 0x18, 0x1F, 0x23, 0x29, 0x2C, 0x2F, + ]; + + measure(|| { + if MLDSA87::verify(&pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Same as bench_mldsa87_verify(), but with a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLDSA87PublicKeyExpanded, which is +/// what makes repeated verifications against the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. Both the expansion and the verification are +/// inside the measurement. +fn bench_mldsa87_verify_expanded_pk() { use bouncycastle::mldsa::{ - MLDSA87, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87PublicKey, MLDSATrait, + MLDSA87, MLDSA87_PK_LEN, MLDSA87_SIG_LEN, MLDSA87PublicKeyExpanded, MLDSATrait, }; - eprintln!("MLDSA87/Verify"); + eprintln!("MLDSA87/Verify_with_expanded_key"); let msg = b"The quick brown fox jumped over the lazy dog"; @@ -1941,8 +3750,336 @@ fn bench_mldsa87_verify() { // 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 pk = MLDSA87PublicKey::from_bytes(&[ + let pk = load_mldsa87_pk(); + let sig: [u8; MLDSA87_SIG_LEN] = [ + 0x78, 0x13, 0x68, 0xE6, 0x4D, 0xBA, 0x54, 0x2A, 0x7E, 0xAC, 0xBD, 0x22, 0x57, 0x33, 0x5C, + 0xC9, 0x43, 0xA0, 0x32, 0x41, 0x00, 0x9B, 0x79, 0x70, 0x93, 0xC6, 0x15, 0xF7, 0x6A, 0x67, + 0x1A, 0x75, 0x91, 0x43, 0x04, 0x41, 0xD8, 0x0B, 0xB5, 0x82, 0x30, 0x4B, 0x33, 0xB9, 0xFC, + 0xE2, 0x95, 0xE0, 0xDD, 0x57, 0xFE, 0x16, 0x93, 0x55, 0xDD, 0xF4, 0x45, 0x3A, 0x2A, 0xCA, + 0x62, 0xD8, 0xEB, 0x81, 0x09, 0xEF, 0x0D, 0x9C, 0xF3, 0xF5, 0xB0, 0xA9, 0x4E, 0x04, 0xAD, + 0x81, 0xB3, 0xE7, 0x86, 0x01, 0x42, 0x43, 0xEC, 0xDE, 0x81, 0x65, 0x51, 0xAA, 0x7F, 0xE0, + 0x1C, 0x63, 0x90, 0x54, 0x25, 0x6A, 0x49, 0x17, 0x56, 0xBE, 0xF5, 0x9F, 0x50, 0x34, 0xF7, + 0x17, 0xFF, 0x4F, 0x85, 0xE7, 0x0B, 0xA7, 0x73, 0x1A, 0x49, 0x97, 0x14, 0x15, 0xB6, 0xA7, + 0xE7, 0xD8, 0x16, 0xAB, 0x43, 0x4B, 0x9F, 0x17, 0xA3, 0x09, 0x5E, 0xDE, 0x6F, 0xD4, 0x32, + 0xBE, 0x2B, 0xFA, 0x82, 0x72, 0x40, 0x45, 0xDD, 0xA0, 0xDF, 0xFF, 0x7A, 0x02, 0x81, 0xE9, + 0x00, 0x09, 0x39, 0xCC, 0xBA, 0x3D, 0x8A, 0xB3, 0x24, 0x51, 0x39, 0xC4, 0x41, 0x64, 0x8C, + 0x76, 0xA6, 0x53, 0x61, 0x27, 0xE4, 0xD1, 0xEF, 0x0D, 0xF1, 0x53, 0x18, 0x83, 0xAB, 0x78, + 0xC8, 0xB4, 0x13, 0x23, 0x61, 0x7A, 0xD8, 0xDB, 0x03, 0xD9, 0x90, 0x8C, 0x9E, 0x08, 0xA9, + 0xF7, 0x32, 0x1C, 0x45, 0x05, 0x1B, 0x3C, 0x94, 0x21, 0x33, 0x47, 0xB1, 0x1C, 0x4A, 0x84, + 0x49, 0x1D, 0xE7, 0xA7, 0xBE, 0x68, 0x70, 0x1E, 0x47, 0xD7, 0xF0, 0xE0, 0xB3, 0x3E, 0x76, + 0x7B, 0xEF, 0x17, 0x69, 0x4E, 0x4D, 0x33, 0x24, 0x4E, 0xD9, 0x2E, 0xBD, 0x74, 0xC8, 0x5A, + 0xB6, 0xC8, 0x44, 0x41, 0xCD, 0xDC, 0x14, 0x33, 0x1E, 0x6A, 0xE8, 0xBD, 0x23, 0x67, 0x4B, + 0xDA, 0x27, 0xF0, 0x9C, 0x05, 0x0D, 0x88, 0xF7, 0xD4, 0x30, 0xFE, 0xEE, 0x7F, 0x15, 0xA7, + 0x2A, 0x24, 0xD6, 0x53, 0xBB, 0x6B, 0xEC, 0x54, 0x49, 0x1B, 0x98, 0x36, 0x2C, 0xE1, 0x31, + 0xD3, 0x7C, 0x7D, 0x78, 0xA3, 0xF9, 0xA8, 0x93, 0xDB, 0x5A, 0xBD, 0xCC, 0xD6, 0x66, 0x35, + 0x93, 0xB8, 0x8B, 0xC6, 0xC9, 0x7F, 0x07, 0xF8, 0xEA, 0xFC, 0xCF, 0xD2, 0x5E, 0x81, 0x80, + 0xD9, 0x18, 0xEF, 0xBC, 0xD9, 0x5B, 0xBF, 0x3D, 0xA2, 0x9F, 0x08, 0x1E, 0x3E, 0x19, 0x32, + 0x09, 0x59, 0x39, 0x19, 0x8E, 0x2A, 0x15, 0x5B, 0x2D, 0x80, 0x3A, 0x3E, 0x84, 0xCA, 0x4F, + 0x34, 0x56, 0x9D, 0xF6, 0x95, 0xC2, 0x59, 0xFA, 0xF3, 0xC0, 0xD8, 0xF0, 0xCD, 0x21, 0x7E, + 0xBD, 0x2D, 0xBA, 0xD5, 0x42, 0xB3, 0x2F, 0xBB, 0x54, 0xE4, 0x4A, 0xAF, 0x0B, 0x5D, 0xC7, + 0x39, 0xFA, 0xFE, 0xF2, 0xE4, 0x6D, 0xB8, 0xD6, 0x8B, 0xFC, 0x35, 0xF4, 0x4F, 0x03, 0x8C, + 0xB1, 0xF5, 0x23, 0x1A, 0x1B, 0x5B, 0x13, 0x4A, 0xE6, 0x83, 0xE7, 0xF3, 0x29, 0x7C, 0xC7, + 0xA9, 0x5B, 0xD1, 0x91, 0xB3, 0x10, 0xF6, 0x82, 0x01, 0x45, 0x07, 0x97, 0xFE, 0x32, 0x93, + 0xCD, 0xE1, 0x67, 0x2D, 0xFE, 0xCA, 0x4B, 0x49, 0x3F, 0x53, 0xC7, 0x68, 0xEA, 0x04, 0x8A, + 0x97, 0x2A, 0x4C, 0xD8, 0x4D, 0x39, 0xEF, 0x68, 0x29, 0x57, 0xB8, 0xF2, 0x8B, 0xA2, 0x94, + 0x87, 0xB4, 0x68, 0x9B, 0x43, 0xFE, 0xC2, 0x65, 0x58, 0x23, 0xD9, 0xBB, 0x99, 0xFF, 0xCF, + 0x31, 0x49, 0x03, 0x66, 0xA9, 0x86, 0x0A, 0x5D, 0x5B, 0x8E, 0x32, 0xA3, 0xB8, 0xBF, 0xEB, + 0x6F, 0x55, 0xF8, 0x8F, 0xB8, 0x0C, 0x8C, 0x01, 0x42, 0x08, 0x6F, 0x22, 0x0E, 0x1F, 0x6F, + 0x28, 0x62, 0xDA, 0xBD, 0xA5, 0x8C, 0x3B, 0x6F, 0x5F, 0xAA, 0x80, 0x5B, 0x39, 0xCF, 0xAC, + 0x4B, 0x6D, 0x7E, 0xA7, 0xAC, 0xDF, 0x1B, 0x06, 0x90, 0x06, 0x3B, 0x0C, 0x1E, 0xA3, 0x8C, + 0x7C, 0x47, 0x55, 0x18, 0x99, 0x66, 0xDC, 0x63, 0x10, 0x55, 0xF1, 0x53, 0xF7, 0x1B, 0x77, + 0xB1, 0x14, 0xFA, 0x5C, 0x30, 0x93, 0x16, 0xBA, 0x51, 0x23, 0x30, 0xEA, 0x5C, 0xDB, 0x0B, + 0xB1, 0x76, 0x00, 0x1E, 0x57, 0x46, 0x15, 0x63, 0xD1, 0x72, 0x59, 0xF3, 0x5D, 0x0C, 0x30, + 0xEF, 0x5A, 0xC8, 0x38, 0xC0, 0x32, 0x54, 0x02, 0xBA, 0xB5, 0x2C, 0x53, 0x14, 0x69, 0x52, + 0x6A, 0xE3, 0xEE, 0x62, 0x93, 0xF7, 0xB5, 0x76, 0x9D, 0x27, 0xE6, 0x9F, 0xA8, 0x1C, 0xD2, + 0x5A, 0x31, 0xCD, 0x09, 0x5B, 0x12, 0x6E, 0x70, 0xC5, 0x7A, 0xC3, 0x16, 0x9A, 0x5F, 0x58, + 0x5A, 0x11, 0xF1, 0x74, 0x8D, 0x9D, 0x22, 0xF2, 0x56, 0x49, 0x11, 0xC2, 0x6A, 0x24, 0xB2, + 0x15, 0x3A, 0x78, 0xF3, 0xA0, 0x68, 0x22, 0xF5, 0xF1, 0x96, 0x3F, 0x23, 0x7A, 0xBE, 0xB4, + 0x8E, 0xFD, 0x9A, 0x9C, 0xD4, 0x78, 0xDE, 0x57, 0x9C, 0x5A, 0x0B, 0xA8, 0x4D, 0x00, 0xE9, + 0x6F, 0xBD, 0xE3, 0x6D, 0x8C, 0xE2, 0x0E, 0x7E, 0x94, 0x85, 0x47, 0xFB, 0x68, 0x50, 0x83, + 0x4F, 0xF7, 0x9D, 0x21, 0x18, 0x30, 0xF6, 0xEE, 0x97, 0x33, 0x59, 0x78, 0x1D, 0x9D, 0x50, + 0x08, 0xFB, 0x43, 0xA8, 0x93, 0x54, 0x78, 0x2F, 0xDE, 0x41, 0x58, 0x17, 0x7F, 0x52, 0x06, + 0xCE, 0x1D, 0x38, 0xC8, 0x89, 0xE9, 0x9E, 0x4B, 0xB5, 0xB4, 0xAB, 0x34, 0xD6, 0xA0, 0x5C, + 0x42, 0xF5, 0xD7, 0x19, 0xEA, 0x03, 0xDB, 0xC5, 0x4A, 0xDB, 0xA7, 0x5A, 0x3B, 0xB4, 0x4A, + 0x3C, 0x08, 0xC7, 0x55, 0x64, 0x62, 0xF8, 0xC5, 0xB7, 0xC5, 0x68, 0xA6, 0x92, 0x42, 0xCF, + 0x5B, 0xE6, 0x09, 0x8E, 0xBA, 0x0A, 0x22, 0x49, 0xC1, 0xCA, 0x5B, 0x21, 0x09, 0xB6, 0x40, + 0x4A, 0x96, 0x2A, 0xBC, 0x1C, 0x15, 0x9C, 0x6B, 0x48, 0xA7, 0x9F, 0xB9, 0x7E, 0x4A, 0x33, + 0x37, 0xD9, 0x93, 0x23, 0x74, 0x62, 0x21, 0x29, 0x74, 0x23, 0xF9, 0xBD, 0x1B, 0x12, 0xE7, + 0x84, 0x89, 0xE0, 0x1E, 0x6A, 0x10, 0xF0, 0xFD, 0x6B, 0xBA, 0x1C, 0xFD, 0x6A, 0xE1, 0xB7, + 0x5D, 0xBE, 0x69, 0xF8, 0xB8, 0xEE, 0x51, 0xA4, 0xE7, 0xF6, 0x8B, 0xA2, 0xC4, 0x07, 0xC9, + 0xC0, 0xBA, 0xD3, 0x89, 0x2B, 0x29, 0xB0, 0x17, 0x0A, 0xB7, 0x58, 0x36, 0xFD, 0xD4, 0x9A, + 0x7E, 0xE3, 0xC2, 0xBB, 0x30, 0xF2, 0xC3, 0xD2, 0x26, 0xBC, 0xEC, 0x49, 0x14, 0x09, 0x52, + 0x17, 0x0B, 0x0D, 0x16, 0x0F, 0x97, 0xB3, 0x0B, 0x7B, 0x7B, 0x09, 0x67, 0x19, 0x53, 0x86, + 0x77, 0xEB, 0xB0, 0x69, 0x22, 0xF2, 0x69, 0x25, 0x22, 0x7C, 0x88, 0x52, 0xAC, 0xC1, 0x07, + 0xA8, 0xF1, 0x73, 0xB3, 0x8D, 0x96, 0x69, 0x75, 0x84, 0xBD, 0x3D, 0xFE, 0x16, 0x9B, 0x40, + 0x73, 0xAA, 0x58, 0xA7, 0xBF, 0x37, 0x1D, 0x5C, 0x4B, 0xB0, 0xEE, 0xD3, 0x0F, 0x08, 0x21, + 0x2D, 0xEF, 0xB3, 0xAE, 0xC9, 0x02, 0xD4, 0x54, 0x60, 0x84, 0x17, 0x6B, 0xF0, 0xF8, 0x6D, + 0x93, 0xCF, 0x36, 0xA4, 0x68, 0x9A, 0x5E, 0x87, 0x4B, 0x32, 0xD6, 0xB7, 0xD3, 0xC1, 0xE3, + 0xFB, 0xCF, 0xD9, 0x88, 0xC3, 0x5D, 0xBC, 0x9A, 0x8D, 0x0A, 0x01, 0x9A, 0xD6, 0xD7, 0xE1, + 0x5E, 0xC3, 0xAC, 0x97, 0x12, 0x5D, 0xB6, 0xAB, 0xFE, 0x00, 0xBE, 0xFF, 0xE3, 0x5A, 0x81, + 0x66, 0x66, 0x99, 0xA9, 0x1E, 0x15, 0x94, 0x5C, 0x62, 0xD6, 0x46, 0x69, 0x0B, 0x5B, 0x52, + 0xDE, 0x8B, 0x83, 0x5E, 0xE9, 0xBE, 0x53, 0x58, 0x8F, 0xDE, 0x5D, 0x63, 0x02, 0x3B, 0x52, + 0xB2, 0xB1, 0xF4, 0x61, 0x0C, 0x23, 0x7A, 0x82, 0x9F, 0x59, 0x01, 0xA4, 0x60, 0x42, 0x96, + 0x3C, 0xCE, 0x7B, 0x85, 0xAA, 0x04, 0x0A, 0xDD, 0xE0, 0x29, 0x85, 0xE1, 0x4E, 0x23, 0xC4, + 0xEA, 0xDB, 0x75, 0x22, 0x1C, 0x60, 0x7D, 0x24, 0x67, 0x2E, 0x24, 0x4C, 0x66, 0xC9, 0xC2, + 0x4C, 0x3C, 0xB7, 0xFD, 0x90, 0xBB, 0x23, 0x29, 0x5C, 0x9D, 0x3D, 0x9D, 0xA5, 0x16, 0xBC, + 0xE3, 0xDD, 0x46, 0x2D, 0x66, 0x60, 0xF9, 0xF9, 0x1E, 0xF0, 0x61, 0x8A, 0x4D, 0x4D, 0x3D, + 0x66, 0x68, 0xC5, 0xD1, 0xE2, 0xE8, 0xED, 0x43, 0x3E, 0xBF, 0xE0, 0x76, 0x2B, 0xEB, 0x74, + 0x33, 0x24, 0xE1, 0x16, 0x08, 0xF8, 0xB1, 0x4B, 0x69, 0xCE, 0x4C, 0x22, 0x1C, 0x16, 0x54, + 0xBA, 0x49, 0x92, 0xA5, 0xAF, 0x2D, 0x94, 0x9C, 0x29, 0x39, 0xF9, 0x5D, 0x1C, 0x8F, 0xB7, + 0x67, 0xAF, 0x2A, 0x84, 0x3C, 0xC7, 0xC7, 0x8F, 0x57, 0x25, 0x9D, 0x5C, 0x0C, 0x6C, 0xA8, + 0x3F, 0xCA, 0x41, 0xEF, 0x5E, 0xCC, 0x4E, 0xEE, 0xEA, 0x93, 0xE4, 0x51, 0x8C, 0x24, 0xD3, + 0x04, 0x0F, 0x2C, 0xD9, 0x0D, 0xF3, 0xE5, 0x35, 0xE9, 0x89, 0xE6, 0x06, 0xFA, 0x10, 0x9E, + 0x2C, 0x45, 0x3E, 0xD7, 0x35, 0x3D, 0xB1, 0xCD, 0xB2, 0x71, 0x37, 0xF0, 0x05, 0xF9, 0xDD, + 0x8D, 0x2A, 0xEB, 0xFB, 0x72, 0x55, 0xA6, 0x09, 0x8B, 0x69, 0x02, 0x15, 0xE1, 0x00, 0xCF, + 0xE4, 0x4C, 0xA0, 0xF2, 0x74, 0x5F, 0xCE, 0xD4, 0x83, 0x22, 0xBC, 0x96, 0x67, 0xAB, 0x16, + 0xD2, 0xE1, 0xC0, 0xFF, 0x49, 0x1B, 0x96, 0xA1, 0x7B, 0x83, 0x3D, 0x4F, 0xD4, 0x4D, 0x31, + 0xC2, 0x23, 0x0A, 0xC8, 0x35, 0x79, 0x6E, 0x06, 0x3A, 0xB0, 0x30, 0x00, 0xF0, 0x4F, 0x15, + 0xC7, 0x05, 0x60, 0x03, 0x37, 0x63, 0xA4, 0x85, 0x52, 0xCC, 0xEA, 0xAC, 0xAD, 0xE9, 0xCA, + 0x5C, 0x80, 0x55, 0xF3, 0x74, 0x5E, 0x17, 0x90, 0x68, 0xA2, 0x87, 0x18, 0x3F, 0x2B, 0xC3, + 0xEF, 0x63, 0x27, 0xDE, 0xC5, 0xAC, 0x7C, 0xF7, 0xB0, 0x52, 0xEF, 0x5A, 0x88, 0x73, 0xE6, + 0x97, 0xEF, 0xDE, 0x08, 0x96, 0x88, 0xF4, 0x3B, 0xE4, 0x64, 0x82, 0x7C, 0x2F, 0xA8, 0x3E, + 0xB5, 0x31, 0xB3, 0x67, 0x41, 0x45, 0xE9, 0x5C, 0x69, 0x9C, 0x82, 0x99, 0x0E, 0x68, 0x49, + 0x67, 0xDA, 0xD3, 0x19, 0xD9, 0xF6, 0x4A, 0xB1, 0x6C, 0xD9, 0xFE, 0x9B, 0x6C, 0x41, 0x23, + 0x2A, 0xC4, 0xAE, 0x37, 0x95, 0xFD, 0x8A, 0x76, 0xAA, 0x9B, 0x02, 0xE9, 0x70, 0x24, 0x20, + 0x61, 0xC6, 0xDA, 0x45, 0xA2, 0xAF, 0x74, 0xAD, 0x9C, 0xB2, 0xA7, 0x99, 0x35, 0xC9, 0x26, + 0x25, 0xE2, 0x42, 0xF4, 0xBC, 0x7F, 0xCE, 0x54, 0xD5, 0xC1, 0x0A, 0x9E, 0x61, 0xF8, 0x75, + 0x16, 0x2F, 0xA6, 0x51, 0xB6, 0x60, 0x57, 0xBA, 0x03, 0x6F, 0x06, 0x2D, 0x6D, 0x39, 0xD0, + 0x50, 0x2B, 0x93, 0xA5, 0x64, 0x0B, 0x78, 0xC6, 0xC2, 0xFD, 0x20, 0xB0, 0x2F, 0xF8, 0x36, + 0x76, 0xA8, 0x7A, 0x94, 0x94, 0x5D, 0x47, 0x6C, 0x34, 0x98, 0x03, 0xEA, 0x4F, 0xE6, 0x0C, + 0xDC, 0xEA, 0x65, 0xBB, 0x26, 0x29, 0xE2, 0xBC, 0x09, 0xD4, 0x47, 0x2E, 0xC6, 0x34, 0x22, + 0xDE, 0xE2, 0x05, 0x2F, 0x09, 0x8D, 0xEA, 0xF5, 0x53, 0x1E, 0x6C, 0x9B, 0xED, 0x66, 0x72, + 0xA8, 0xB6, 0x99, 0x80, 0x2E, 0xFE, 0x0C, 0xDE, 0xD8, 0x0C, 0x84, 0x55, 0xF5, 0x85, 0xD1, + 0xBA, 0x63, 0x3D, 0x28, 0x1F, 0x1A, 0x21, 0xAD, 0xAB, 0x48, 0xE6, 0x3B, 0x44, 0xE0, 0xC2, + 0xA4, 0xD7, 0x60, 0x8C, 0xF9, 0x8A, 0xAB, 0xF8, 0xAD, 0xC8, 0x6B, 0xCB, 0x8F, 0x61, 0xE8, + 0xB0, 0x6C, 0xD2, 0x38, 0x5F, 0x82, 0xE0, 0xA3, 0xCD, 0xD0, 0x3C, 0xAB, 0x15, 0x2D, 0x59, + 0x51, 0x85, 0x9C, 0x45, 0x32, 0xF9, 0x16, 0x8E, 0x78, 0xF1, 0x7B, 0xA2, 0xA5, 0x77, 0x27, + 0x80, 0x32, 0x7D, 0xCF, 0x4E, 0x62, 0xB4, 0xD2, 0x6E, 0x44, 0x37, 0x62, 0xFC, 0x48, 0x8A, + 0xE4, 0xCD, 0x4D, 0x11, 0x56, 0xDB, 0xD5, 0x78, 0x25, 0x95, 0xCF, 0xD7, 0x69, 0x7A, 0x51, + 0x4A, 0xBC, 0x9B, 0x16, 0x0C, 0x9C, 0xCF, 0x08, 0xED, 0xC8, 0x61, 0x34, 0xA7, 0x55, 0xB9, + 0x0E, 0x9B, 0xB5, 0x43, 0x51, 0x1E, 0x88, 0x8E, 0x31, 0x57, 0x72, 0x1A, 0x52, 0xD1, 0xBC, + 0x5D, 0xB3, 0x30, 0x29, 0xFB, 0x33, 0x5E, 0xA2, 0x11, 0x4E, 0x21, 0xC0, 0x33, 0x68, 0xC8, + 0xD7, 0xF4, 0xD8, 0x27, 0x96, 0x06, 0x41, 0x77, 0x2A, 0x4A, 0x32, 0xA7, 0x38, 0xDF, 0x60, + 0xD1, 0x9E, 0xC7, 0x7A, 0xB0, 0x9D, 0x22, 0xF5, 0x7C, 0xC2, 0x52, 0x3B, 0x95, 0x03, 0xB3, + 0xF5, 0xB1, 0xCE, 0xBC, 0x5A, 0xE1, 0x5F, 0x88, 0x5F, 0x15, 0x98, 0x42, 0xDB, 0x73, 0x59, + 0xA1, 0xC8, 0x9D, 0x3D, 0x82, 0xD3, 0x40, 0x70, 0x68, 0xF1, 0x5B, 0x67, 0x39, 0x62, 0x6E, + 0xB8, 0xC5, 0x21, 0xFC, 0x8C, 0x5C, 0x74, 0x91, 0xF9, 0x45, 0xD4, 0x9F, 0x14, 0xE6, 0x98, + 0x9D, 0xA3, 0x40, 0xBD, 0xF4, 0x9E, 0x7F, 0x8A, 0x79, 0x27, 0x47, 0xAA, 0x65, 0x8B, 0xC1, + 0x14, 0x14, 0x3B, 0xA9, 0x3F, 0x26, 0x02, 0x2D, 0x00, 0x17, 0x35, 0xB7, 0x44, 0x63, 0x9B, + 0xBF, 0x22, 0xAA, 0xB2, 0xA1, 0x85, 0x1C, 0xFC, 0x93, 0x4F, 0x9C, 0x69, 0xD3, 0x76, 0x4F, + 0xDE, 0xA3, 0xD2, 0x3D, 0xB1, 0x79, 0x98, 0xE6, 0x13, 0x8C, 0xFD, 0x7C, 0xD9, 0xE9, 0xA4, + 0x7C, 0xB7, 0x41, 0x93, 0xBD, 0x71, 0xAA, 0xF2, 0x8C, 0xDD, 0x9D, 0x1E, 0xB5, 0x95, 0x12, + 0x55, 0x46, 0xA4, 0xF4, 0x35, 0x7E, 0xBB, 0xD1, 0xF4, 0x10, 0xE3, 0xBF, 0x85, 0x57, 0x89, + 0x2D, 0xE6, 0x85, 0x09, 0xB5, 0xB9, 0x8C, 0x5C, 0x22, 0x9E, 0x94, 0x2C, 0x91, 0x0F, 0xDD, + 0x3E, 0x54, 0xCB, 0x6A, 0xD5, 0x4D, 0x8D, 0xD8, 0x86, 0xCB, 0x97, 0xEC, 0xC0, 0x6D, 0x1E, + 0x40, 0x1B, 0x83, 0x95, 0xD0, 0xBC, 0xB0, 0xDB, 0x9A, 0x03, 0x1D, 0xC6, 0x6C, 0x92, 0x94, + 0xF9, 0x05, 0x3C, 0x68, 0xFC, 0x42, 0x04, 0x2B, 0x1F, 0xA1, 0x67, 0x1F, 0xC7, 0xD5, 0x10, + 0xB7, 0x09, 0x16, 0xC0, 0x13, 0x9C, 0xFE, 0xBE, 0x3A, 0x91, 0x24, 0x45, 0x27, 0xCE, 0x94, + 0x39, 0x86, 0x0C, 0xED, 0xB3, 0x09, 0x08, 0x19, 0x7B, 0xE8, 0x51, 0xCB, 0xD1, 0xD3, 0xB1, + 0x8C, 0xA5, 0x41, 0x35, 0x84, 0x49, 0xFB, 0x34, 0xFB, 0x5C, 0xD5, 0x69, 0x63, 0x0E, 0xD5, + 0xF6, 0x7B, 0x87, 0x95, 0xE8, 0x78, 0x28, 0xF2, 0xCE, 0x3B, 0xEC, 0xFE, 0x45, 0x75, 0x79, + 0xD8, 0x23, 0x33, 0xB0, 0xBB, 0xAB, 0x09, 0x4D, 0xE3, 0x91, 0xE1, 0xF8, 0x15, 0x7B, 0xD4, + 0x31, 0xE3, 0x65, 0xCA, 0x86, 0x46, 0x30, 0x93, 0x2B, 0xBE, 0xBB, 0x48, 0xF4, 0x5F, 0x81, + 0x34, 0x42, 0x4E, 0x18, 0xAB, 0x45, 0x50, 0x29, 0xB5, 0x4B, 0x19, 0xE2, 0xF3, 0xBF, 0xEC, + 0x5E, 0x44, 0xAD, 0x0E, 0xA5, 0xC0, 0x3F, 0x53, 0xD8, 0xF9, 0x25, 0xB6, 0x35, 0x83, 0x8A, + 0xA7, 0x01, 0x5A, 0x7C, 0x9E, 0x32, 0x5B, 0xDF, 0xAF, 0xF9, 0x66, 0xEA, 0x95, 0x12, 0xDD, + 0x50, 0xF8, 0x7C, 0x89, 0x95, 0xCE, 0xA7, 0x56, 0x1C, 0x23, 0xF4, 0xFB, 0x06, 0xD9, 0x64, + 0xAB, 0x8F, 0x19, 0x13, 0xA6, 0xCA, 0x17, 0xE4, 0xCA, 0x60, 0xD6, 0xBC, 0x07, 0x8E, 0x17, + 0x84, 0xF8, 0x9C, 0x67, 0x3C, 0x91, 0xD9, 0x55, 0xBC, 0xF4, 0x5F, 0x58, 0xCA, 0x97, 0x09, + 0x57, 0x9D, 0x5E, 0x38, 0x31, 0xDF, 0x12, 0xCF, 0xDB, 0x75, 0x16, 0xFD, 0x21, 0x87, 0x8C, + 0xB5, 0x42, 0x43, 0x57, 0x9B, 0x93, 0x46, 0xD2, 0xDE, 0x4B, 0xE2, 0x5F, 0x50, 0x8E, 0x84, + 0xB1, 0xAD, 0xC7, 0x8C, 0xB9, 0x1C, 0x03, 0xDA, 0x3C, 0x4F, 0xD5, 0x9E, 0x45, 0x29, 0x18, + 0x98, 0x38, 0xF7, 0x4F, 0x63, 0x12, 0x82, 0x06, 0x20, 0xA5, 0x99, 0x6B, 0x79, 0x1F, 0xFC, + 0xB3, 0x32, 0xF8, 0x47, 0x09, 0x46, 0x13, 0xF2, 0x14, 0x8B, 0x86, 0x20, 0x34, 0xFA, 0x89, + 0xD0, 0xD0, 0xFF, 0x18, 0x08, 0xD9, 0x02, 0xC5, 0xD1, 0xAF, 0x64, 0xD5, 0x52, 0x24, 0x92, + 0xD6, 0x1E, 0xCD, 0xE4, 0xC7, 0x3B, 0xE8, 0x9A, 0x33, 0x78, 0x2C, 0xEF, 0x1A, 0xCC, 0x1D, + 0xC3, 0x27, 0xFB, 0x2E, 0xB9, 0xD1, 0x76, 0x42, 0x20, 0x9B, 0x85, 0xAA, 0x8B, 0x1D, 0xC5, + 0x7C, 0xBF, 0x06, 0x7C, 0x7A, 0xA2, 0x9D, 0xA6, 0xB7, 0xE1, 0x57, 0xD2, 0x3E, 0x17, 0x1D, + 0x3A, 0xE6, 0xF3, 0x85, 0x58, 0x34, 0x07, 0x17, 0x91, 0x40, 0x2C, 0x85, 0x1F, 0xF2, 0xDD, + 0x67, 0x10, 0x99, 0x79, 0xF7, 0xEE, 0x5E, 0x09, 0xE6, 0x4B, 0x4E, 0xEF, 0xDE, 0xE7, 0x11, + 0x2B, 0x55, 0xCE, 0x20, 0x0B, 0xB8, 0xC8, 0x05, 0x1E, 0x34, 0x28, 0xC3, 0x05, 0xFA, 0x1D, + 0x57, 0x6B, 0xFF, 0xBB, 0x25, 0xA7, 0x0E, 0xB5, 0x71, 0x16, 0x8F, 0xC6, 0x0D, 0xAD, 0xD9, + 0x28, 0xB1, 0x0C, 0xFD, 0x07, 0xDE, 0x80, 0xA8, 0x5B, 0x8D, 0xF3, 0xED, 0xC3, 0x72, 0xD4, + 0x88, 0xC2, 0x1F, 0x0D, 0x57, 0x87, 0x61, 0x1C, 0xC6, 0xFB, 0x73, 0xAE, 0xEB, 0x6F, 0x92, + 0x0A, 0x10, 0x92, 0x94, 0xB4, 0x9D, 0x38, 0x70, 0xF9, 0x0D, 0xE3, 0xB3, 0x60, 0xD1, 0x4D, + 0xF7, 0x7E, 0xF9, 0x56, 0x40, 0xBC, 0xAC, 0x7A, 0x4D, 0xBC, 0xA9, 0x01, 0xA3, 0x1D, 0xB8, + 0x3E, 0x83, 0xF5, 0xC5, 0x9C, 0xE3, 0x27, 0x20, 0x7E, 0xA9, 0xB2, 0x7C, 0x3B, 0x97, 0x8D, + 0x30, 0xD5, 0x38, 0x65, 0xC1, 0xB8, 0x47, 0x64, 0xF0, 0x25, 0xE8, 0x73, 0x2D, 0x50, 0x07, + 0x55, 0x4C, 0xE5, 0xC9, 0xCC, 0x41, 0x0B, 0x2E, 0xEF, 0xD7, 0xE4, 0xD9, 0x90, 0xC5, 0x38, + 0x55, 0x76, 0x06, 0xA6, 0xBC, 0x47, 0x57, 0x7A, 0x43, 0x76, 0x8D, 0x30, 0xAA, 0x3E, 0x85, + 0x98, 0xFD, 0x6F, 0x4F, 0xE7, 0xAC, 0x43, 0x9F, 0x39, 0x31, 0xC5, 0x8F, 0xD6, 0x9D, 0x90, + 0x76, 0x5A, 0xC9, 0xF4, 0x56, 0xAC, 0x7D, 0xE0, 0x85, 0xE1, 0x4A, 0x08, 0x98, 0xC4, 0x55, + 0x7F, 0x5D, 0x3B, 0xAA, 0xAE, 0x07, 0xED, 0xC6, 0x07, 0xDE, 0x69, 0x00, 0x14, 0x6B, 0x97, + 0xB3, 0x5A, 0xAE, 0x57, 0x01, 0x53, 0xDC, 0x10, 0x78, 0x15, 0xEF, 0x9F, 0xEB, 0xDD, 0x4F, + 0xD5, 0x67, 0xD6, 0x37, 0xFE, 0xE8, 0xF8, 0xBF, 0xB4, 0xB3, 0x41, 0x3E, 0xA6, 0xAE, 0xAD, + 0x48, 0x46, 0xAB, 0x73, 0x3A, 0x04, 0xF1, 0xE4, 0xBC, 0x32, 0xA3, 0xBB, 0xB1, 0xC1, 0x6B, + 0xAF, 0x8D, 0x0B, 0xDB, 0x9C, 0xCB, 0x82, 0xFE, 0x46, 0x47, 0x9C, 0xCF, 0xD0, 0x40, 0xB5, + 0xE6, 0x40, 0x64, 0xE5, 0x39, 0xB3, 0x9C, 0x66, 0xE4, 0x50, 0x1D, 0xC8, 0x22, 0x87, 0x3A, + 0xC6, 0x11, 0x9A, 0x4A, 0x11, 0x2A, 0x1F, 0x7C, 0xD6, 0xDF, 0x0E, 0x5F, 0x84, 0x35, 0x6C, + 0xE8, 0x53, 0xCE, 0xD3, 0x4C, 0xE6, 0x9A, 0x9E, 0x73, 0x83, 0x53, 0x49, 0x83, 0xC5, 0x1C, + 0x50, 0x26, 0x9B, 0xF8, 0xB9, 0x58, 0x6A, 0x0E, 0x5B, 0xA9, 0x05, 0xFD, 0x3B, 0xCE, 0x08, + 0x0B, 0x00, 0xE7, 0xF7, 0xD4, 0x8E, 0x55, 0xF4, 0x89, 0x47, 0x9B, 0x57, 0x71, 0xE9, 0x95, + 0xFB, 0x02, 0x0E, 0x58, 0xFE, 0xB7, 0x4A, 0xF6, 0x5C, 0x3E, 0xE7, 0x6A, 0xA4, 0xE6, 0x9B, + 0x5B, 0xA8, 0xBD, 0xA2, 0x49, 0xA1, 0xB2, 0xD6, 0x2C, 0x08, 0xD4, 0x18, 0xC3, 0x63, 0x5D, + 0x06, 0x18, 0x46, 0x04, 0x08, 0x43, 0x99, 0x1A, 0xB4, 0x75, 0x47, 0x3D, 0xA8, 0x5D, 0x94, + 0x98, 0x1F, 0xB8, 0x44, 0x25, 0xE7, 0xAD, 0x95, 0x1C, 0xE0, 0xA4, 0x2B, 0xE6, 0x42, 0xFE, + 0x65, 0x8B, 0x7A, 0xAA, 0xE7, 0x2B, 0x14, 0x7C, 0xDC, 0x08, 0x6C, 0x24, 0xB1, 0x57, 0x1E, + 0xB2, 0x27, 0x2E, 0x2A, 0x72, 0xB1, 0x56, 0x60, 0xD8, 0x54, 0xEB, 0xE1, 0x9E, 0xC7, 0xD9, + 0xAB, 0x7E, 0xA1, 0x78, 0x00, 0xD0, 0xB6, 0xAE, 0x72, 0x7B, 0x39, 0x21, 0x74, 0x67, 0xC6, + 0x62, 0xBA, 0x08, 0xE6, 0xF1, 0x91, 0x93, 0x95, 0x1E, 0xEF, 0xF0, 0x28, 0x06, 0xA7, 0x84, + 0x3E, 0xB5, 0xC7, 0x1B, 0x2F, 0x04, 0xDC, 0xB6, 0x05, 0xEC, 0xED, 0xC5, 0x12, 0x8C, 0xD6, + 0x77, 0x03, 0x03, 0x8C, 0x44, 0xBF, 0x20, 0xFE, 0x06, 0xF3, 0xED, 0x8C, 0x13, 0x68, 0xFE, + 0x38, 0xE7, 0x29, 0x44, 0xD5, 0xC5, 0x2F, 0xCA, 0x46, 0xA4, 0x5F, 0xD4, 0x8D, 0x8F, 0xE5, + 0xDA, 0x64, 0x18, 0x3D, 0x4D, 0x62, 0xEC, 0x01, 0xAA, 0x3D, 0x9D, 0x67, 0x2E, 0xC6, 0x7A, + 0x01, 0xC1, 0x7F, 0x21, 0xE0, 0x25, 0x25, 0xF0, 0x51, 0x3C, 0xCE, 0x03, 0x0C, 0x66, 0x4F, + 0xC8, 0x78, 0x47, 0x63, 0x08, 0x66, 0x08, 0xBC, 0x80, 0x99, 0xC2, 0x04, 0xC2, 0x55, 0xFF, + 0xED, 0x1D, 0xAF, 0x43, 0x2C, 0xEB, 0x45, 0xFB, 0xD1, 0x35, 0xE2, 0x1E, 0x81, 0x90, 0xC5, + 0xBF, 0xEE, 0x19, 0x21, 0x71, 0xFA, 0xA7, 0x75, 0x20, 0x48, 0x1E, 0x69, 0xE8, 0x7B, 0x7F, + 0x76, 0x79, 0x0B, 0xEF, 0x76, 0xCB, 0x8D, 0x3C, 0x88, 0xF5, 0xC6, 0xE3, 0x2F, 0xC5, 0x9E, + 0x7B, 0xD4, 0x53, 0x51, 0xD6, 0x66, 0x96, 0xB6, 0x1D, 0x9F, 0x40, 0x72, 0x6F, 0xB9, 0xA9, + 0x80, 0x00, 0xB6, 0x87, 0x38, 0xCF, 0x7E, 0x34, 0xB9, 0x8B, 0x6A, 0x4A, 0xAA, 0x2A, 0xC1, + 0xD7, 0xB1, 0x40, 0x7D, 0xB8, 0x97, 0x83, 0xF8, 0x07, 0x71, 0x03, 0xEA, 0x9C, 0x9E, 0x89, + 0x24, 0x7E, 0xAE, 0x07, 0x8A, 0xDF, 0xB3, 0x6E, 0x21, 0x47, 0x4C, 0x3B, 0xB1, 0xFE, 0x0C, + 0x87, 0x68, 0x7C, 0x62, 0x33, 0xA5, 0x33, 0xA0, 0x1E, 0x10, 0x81, 0xB9, 0x3A, 0x35, 0x21, + 0xD3, 0x39, 0xF3, 0x9C, 0x07, 0x56, 0x09, 0xBA, 0xCE, 0x53, 0x19, 0x94, 0x98, 0x8A, 0xE3, + 0x14, 0xF7, 0x7F, 0xC6, 0x03, 0x41, 0x13, 0xA1, 0x38, 0xC6, 0x7E, 0xB7, 0xE0, 0x37, 0x50, + 0xCB, 0xEC, 0x8D, 0x28, 0xBD, 0x21, 0xAF, 0xED, 0xFE, 0xFA, 0x8F, 0x09, 0x16, 0x19, 0xAE, + 0x50, 0x0B, 0x4C, 0xA4, 0x59, 0x9D, 0x01, 0x9D, 0xC8, 0xCA, 0x4B, 0xF1, 0x18, 0xD7, 0x0B, + 0x86, 0x76, 0xDF, 0xC7, 0x96, 0xA4, 0xF6, 0xD9, 0x86, 0xAD, 0xBA, 0x4C, 0x85, 0x74, 0xED, + 0x4A, 0xBA, 0xEA, 0x54, 0x65, 0x46, 0x62, 0x20, 0xE5, 0xE5, 0x3D, 0xC8, 0xFC, 0xB3, 0x95, + 0xD1, 0xE5, 0x9D, 0x27, 0x86, 0x73, 0xCB, 0xC4, 0xE3, 0xF4, 0x06, 0x58, 0xDF, 0x98, 0xAC, + 0x2F, 0xD1, 0x26, 0xA9, 0x49, 0x22, 0x87, 0x9E, 0x1A, 0x3B, 0xE9, 0x1C, 0x1A, 0xCC, 0x20, + 0x80, 0x3C, 0x35, 0xFA, 0x76, 0x4A, 0xBB, 0xAD, 0xAB, 0x07, 0xBD, 0xE8, 0x5F, 0xF4, 0xBD, + 0x9E, 0x0F, 0xB6, 0xF0, 0x6B, 0xAF, 0x5B, 0xF4, 0x2B, 0x8A, 0x2C, 0xBF, 0x6C, 0x2F, 0x62, + 0x60, 0x6B, 0xEC, 0xC3, 0x61, 0x55, 0x29, 0x21, 0xA1, 0x2D, 0x6C, 0x82, 0x36, 0xFD, 0xE8, + 0x4D, 0xB4, 0xBD, 0xDA, 0xC7, 0x7E, 0x88, 0x72, 0x47, 0x8C, 0xFF, 0xC4, 0xE1, 0x48, 0xC1, + 0xC7, 0xAC, 0xFE, 0xDF, 0x6B, 0x17, 0xD9, 0x87, 0x31, 0xC2, 0xDE, 0x36, 0xF3, 0xCB, 0xEF, + 0x1F, 0x6F, 0x78, 0x1A, 0x94, 0x0E, 0x08, 0x74, 0xD5, 0xB7, 0x45, 0x35, 0xBB, 0xE0, 0x66, + 0xB5, 0x30, 0x64, 0xD4, 0x3B, 0x13, 0x92, 0x65, 0x70, 0xA9, 0xE1, 0xC4, 0xE6, 0xDA, 0x20, + 0x6C, 0x8B, 0xD2, 0x52, 0xCA, 0xF2, 0xB6, 0x2E, 0x7D, 0x22, 0x3F, 0x7A, 0xC1, 0x29, 0x39, + 0x13, 0x7F, 0x33, 0x0B, 0xE5, 0x93, 0x74, 0xD7, 0x29, 0x5A, 0x6C, 0x2D, 0xFF, 0x92, 0xE0, + 0x7C, 0x72, 0x75, 0x10, 0xE4, 0x8D, 0x97, 0x05, 0x93, 0xE4, 0x72, 0x29, 0xFC, 0x8B, 0xC3, + 0xBD, 0x5B, 0x8E, 0xA7, 0x80, 0xDA, 0xCF, 0xF4, 0xD2, 0x30, 0x63, 0xDF, 0x65, 0xFE, 0xDA, + 0x5F, 0x8F, 0x65, 0xB1, 0x7A, 0x33, 0x3E, 0x53, 0x2A, 0xCA, 0xD7, 0x91, 0x67, 0x80, 0xC7, + 0x4D, 0x6A, 0x70, 0xD3, 0x8B, 0x36, 0x7F, 0x3F, 0x6F, 0x4E, 0x94, 0x7B, 0x85, 0xFC, 0x15, + 0x23, 0x5B, 0xBE, 0x46, 0xB2, 0x64, 0x95, 0xD2, 0x78, 0x00, 0x98, 0xDB, 0x85, 0x3A, 0x93, + 0x13, 0x77, 0xCF, 0xBE, 0xDE, 0xA6, 0x20, 0xF2, 0x35, 0x5C, 0xA2, 0x1E, 0x81, 0xCE, 0x9E, + 0x00, 0x78, 0xB0, 0xDD, 0x6C, 0xB7, 0x0F, 0x23, 0xED, 0x55, 0x86, 0x82, 0xBE, 0x3B, 0x3D, + 0x59, 0x4E, 0xEF, 0xE8, 0x53, 0x44, 0xE1, 0xF2, 0x75, 0x42, 0x8B, 0x31, 0x6C, 0xC0, 0x88, + 0x99, 0x59, 0x39, 0x29, 0x8F, 0x2A, 0x2D, 0x15, 0xAC, 0x9B, 0x67, 0x6A, 0xC3, 0xE9, 0xCB, + 0x92, 0xF2, 0xA6, 0x4D, 0xEC, 0x77, 0x32, 0xA9, 0x1F, 0xC7, 0x61, 0xAA, 0x1B, 0x12, 0x6E, + 0xA5, 0x75, 0xE3, 0x95, 0x31, 0x77, 0xDA, 0x6E, 0x1C, 0xD7, 0x8F, 0xAE, 0xA8, 0x24, 0x66, + 0x53, 0x30, 0xA8, 0x1D, 0x9E, 0x24, 0x57, 0x2B, 0x98, 0x60, 0xBF, 0x0A, 0xBA, 0x4D, 0xF8, + 0xBD, 0x5D, 0x4E, 0x3E, 0x2C, 0x72, 0xBB, 0xFB, 0xB2, 0xA9, 0x85, 0xC7, 0xAE, 0x2F, 0x07, + 0x79, 0x51, 0xFE, 0x84, 0x01, 0xE1, 0xD1, 0x56, 0xEC, 0xAD, 0xA1, 0xE3, 0x53, 0x81, 0x7B, + 0x20, 0xF4, 0x1E, 0x0B, 0x24, 0x60, 0xA0, 0xCA, 0xAF, 0x2B, 0x36, 0xD6, 0xA7, 0xF1, 0xB3, + 0x51, 0x25, 0xD7, 0x97, 0xDC, 0xC7, 0x14, 0x42, 0x10, 0x27, 0xD1, 0x41, 0x71, 0x76, 0x5A, + 0x64, 0x60, 0x71, 0xED, 0x95, 0x2B, 0x6A, 0x52, 0x94, 0xEE, 0xCF, 0x6A, 0x3A, 0x71, 0xC1, + 0x04, 0xC8, 0x43, 0xA4, 0xA8, 0xB3, 0xEF, 0xCC, 0x27, 0x46, 0x7B, 0x20, 0xCB, 0x0A, 0x94, + 0xAB, 0xF5, 0x80, 0x22, 0x29, 0xEA, 0x4D, 0x83, 0x12, 0x78, 0x3E, 0x78, 0x79, 0x1A, 0x50, + 0xB3, 0xC0, 0xA8, 0x8F, 0xE6, 0x49, 0x71, 0x98, 0xCD, 0x4B, 0xF4, 0x70, 0xDA, 0xC4, 0x6F, + 0x34, 0xE5, 0x00, 0x19, 0xFD, 0xEE, 0x20, 0x40, 0xCF, 0xE9, 0x91, 0x24, 0xB3, 0x12, 0xB1, + 0x12, 0x2B, 0x83, 0xE5, 0x1D, 0x87, 0x88, 0x77, 0xCE, 0xC0, 0x85, 0x5F, 0x11, 0x58, 0xC4, + 0x45, 0xCF, 0xDC, 0x22, 0x53, 0xF4, 0x38, 0x9D, 0x5E, 0x3A, 0x8B, 0xA1, 0x66, 0x9A, 0xBB, + 0x59, 0x76, 0xA4, 0x61, 0x7E, 0x85, 0xF5, 0x43, 0xDA, 0x9F, 0x5E, 0x30, 0xB1, 0x0C, 0xA7, + 0x48, 0x1C, 0x81, 0x85, 0x39, 0x27, 0x82, 0xB4, 0x6F, 0xB0, 0xA0, 0xE5, 0xAB, 0x40, 0x8B, + 0x29, 0x45, 0xE3, 0xC7, 0x9A, 0x1C, 0xC4, 0x9F, 0xB7, 0xC2, 0x72, 0x54, 0xA9, 0xB5, 0x40, + 0xE7, 0x39, 0x7A, 0x5B, 0x65, 0x5B, 0xF7, 0xE4, 0xF8, 0x31, 0x84, 0xDB, 0x32, 0xA1, 0x28, + 0xAA, 0x2E, 0x00, 0xA6, 0x24, 0xD7, 0xDC, 0xD6, 0xB7, 0x7E, 0xFD, 0x15, 0x1F, 0x1E, 0x5C, + 0xBA, 0x88, 0x90, 0xAF, 0x91, 0x70, 0xFA, 0x06, 0xC5, 0x55, 0x71, 0x5D, 0xC1, 0x78, 0x7E, + 0x99, 0x5A, 0xD1, 0x92, 0x70, 0x97, 0x3A, 0xE9, 0x5B, 0x88, 0xDC, 0xAF, 0xDA, 0xF6, 0x2C, + 0x28, 0x84, 0x3D, 0x3F, 0x8B, 0x9C, 0x78, 0xDC, 0x8E, 0x37, 0xD9, 0x11, 0xDA, 0xB3, 0xF7, + 0xEE, 0x9D, 0x4C, 0x73, 0x89, 0xC6, 0x54, 0xBD, 0xCF, 0xC0, 0x50, 0x56, 0xB3, 0x60, 0x02, + 0x01, 0x40, 0xE5, 0x7E, 0x31, 0x47, 0x32, 0x58, 0xA4, 0x08, 0x1E, 0x2A, 0x70, 0x8F, 0x7C, + 0xAB, 0xA9, 0x0C, 0x35, 0x6D, 0x08, 0x47, 0x09, 0x8F, 0xC0, 0x76, 0x24, 0x84, 0x08, 0x6A, + 0xBA, 0x89, 0x8A, 0x60, 0xB0, 0x23, 0xD6, 0xA3, 0x06, 0x04, 0x02, 0x40, 0x62, 0x40, 0x74, + 0x87, 0x85, 0xD5, 0x1C, 0xAF, 0xF5, 0x2A, 0x0E, 0xF3, 0xDC, 0x2A, 0x45, 0xDA, 0xDF, 0x80, + 0xAC, 0x18, 0x50, 0x2D, 0x24, 0x42, 0x2A, 0x8C, 0xBB, 0x10, 0x19, 0x2B, 0x88, 0xF4, 0xE9, + 0x16, 0x02, 0x06, 0xB2, 0xED, 0x3E, 0x04, 0x11, 0x4F, 0x2A, 0x33, 0x9D, 0xF2, 0x69, 0xE2, + 0xC3, 0x6B, 0x86, 0x13, 0xCE, 0x37, 0x08, 0x74, 0x71, 0x70, 0x17, 0x55, 0x33, 0x0C, 0xB5, + 0x59, 0x57, 0x53, 0x66, 0xEE, 0x0F, 0xA2, 0xD3, 0xAF, 0xCD, 0xA3, 0x2E, 0xED, 0xE6, 0xDD, + 0x90, 0x63, 0x45, 0xDA, 0xAA, 0x04, 0x81, 0x21, 0x98, 0xE9, 0x6C, 0x42, 0x23, 0x9C, 0x24, + 0x2E, 0xDB, 0x90, 0x05, 0x97, 0x09, 0xF4, 0x97, 0xDA, 0x5B, 0x87, 0x70, 0x53, 0x84, 0xAE, + 0xF2, 0xAF, 0x22, 0xDC, 0x2E, 0xDF, 0xEF, 0x3C, 0x00, 0xD8, 0xC9, 0x15, 0x6D, 0x8B, 0x31, + 0x63, 0xFE, 0x7A, 0x77, 0x79, 0xE0, 0x4F, 0x04, 0x91, 0x1A, 0x8B, 0x93, 0x4F, 0xB3, 0x07, + 0x2E, 0xEE, 0x84, 0x44, 0x84, 0xFE, 0xDE, 0x5E, 0x2E, 0xE9, 0x6D, 0x33, 0x8E, 0xEF, 0xE2, + 0xDA, 0x98, 0x60, 0x67, 0xFF, 0xE0, 0x21, 0x8A, 0xDA, 0x7D, 0xE1, 0xD0, 0xE4, 0x2D, 0x82, + 0x3D, 0x6B, 0x03, 0x39, 0x18, 0x27, 0x88, 0x88, 0xBA, 0x06, 0x08, 0xAB, 0x8F, 0x7B, 0xE9, + 0x97, 0xBD, 0xF2, 0x63, 0x68, 0x9A, 0x36, 0xF5, 0x20, 0x4C, 0x80, 0x2A, 0xD8, 0x36, 0x36, + 0x37, 0x79, 0xB4, 0xB0, 0xD6, 0xCE, 0x50, 0x83, 0xDF, 0x0B, 0x98, 0xA2, 0xE2, 0xC7, 0x00, + 0x06, 0x2A, 0x4F, 0xA5, 0xE5, 0x7B, 0xC7, 0x3B, 0xD4, 0x53, 0x57, 0xE0, 0x1D, 0x90, 0xC7, + 0x95, 0x4B, 0xC6, 0x90, 0x4D, 0x1C, 0xE8, 0x16, 0x6A, 0x91, 0x68, 0xDA, 0x39, 0xA6, 0x0C, + 0x5C, 0xAE, 0x81, 0x19, 0xBB, 0x6B, 0x9A, 0xB0, 0x74, 0xFB, 0x2D, 0x0A, 0xEE, 0x38, 0x4F, + 0xC2, 0xC0, 0xE4, 0x80, 0x68, 0x11, 0xD6, 0x00, 0x2B, 0x4E, 0x24, 0x01, 0xE7, 0x43, 0x0B, + 0x50, 0xCB, 0x0E, 0x80, 0x75, 0xF3, 0x3D, 0x53, 0x86, 0xAE, 0xCD, 0xE2, 0x56, 0xE1, 0x69, + 0xD9, 0x5E, 0x2F, 0x9C, 0x65, 0x56, 0xC0, 0x8B, 0xA0, 0x42, 0xE6, 0x8A, 0x53, 0xCE, 0x8A, + 0xCA, 0x9C, 0xC0, 0x28, 0x18, 0xF7, 0x38, 0x2F, 0x15, 0x0D, 0xC0, 0x4D, 0xE0, 0x01, 0x9B, + 0x19, 0xC7, 0xA3, 0xAB, 0x0D, 0x72, 0xD6, 0xED, 0x01, 0x3D, 0x7A, 0x11, 0x5D, 0x74, 0xB2, + 0x79, 0xF7, 0x1F, 0xD6, 0xEF, 0xFE, 0xF3, 0x40, 0x49, 0x87, 0x7E, 0x0B, 0x11, 0xE0, 0x65, + 0x9B, 0xE9, 0x38, 0xA5, 0xDE, 0x68, 0x4E, 0xAF, 0x23, 0x51, 0x30, 0x95, 0xEB, 0x4A, 0x1B, + 0xDF, 0x53, 0x6C, 0x3C, 0x01, 0xA4, 0x65, 0x5C, 0x4B, 0x4A, 0x06, 0x73, 0x21, 0x4C, 0xEF, + 0x29, 0xA4, 0x81, 0xD0, 0x6A, 0x02, 0xCC, 0x9A, 0x5B, 0xFC, 0x7B, 0x8D, 0x84, 0x6C, 0x33, + 0x48, 0x4C, 0xD6, 0x7B, 0x1D, 0xE9, 0x8F, 0x60, 0xB6, 0x99, 0x18, 0xF1, 0x77, 0xB6, 0x45, + 0x58, 0xCA, 0x56, 0x7A, 0x62, 0x37, 0xD3, 0x5F, 0xF0, 0x17, 0x71, 0xA4, 0x23, 0x20, 0xCE, + 0x02, 0xBB, 0x98, 0xF3, 0xE4, 0xAD, 0x4A, 0xC7, 0xDB, 0x75, 0x61, 0x1B, 0xD9, 0x96, 0x1E, + 0xAC, 0x66, 0x2A, 0x38, 0xB1, 0xF7, 0x85, 0x97, 0x0C, 0x99, 0xF3, 0xDD, 0x10, 0x5F, 0xF5, + 0x86, 0xF6, 0x13, 0x01, 0xC4, 0x8D, 0x66, 0x70, 0x8C, 0xBF, 0x7D, 0x53, 0xA7, 0x33, 0xE3, + 0x57, 0xB6, 0xC2, 0x56, 0xE8, 0xB7, 0x3F, 0x0E, 0x13, 0x05, 0xA0, 0xBC, 0x13, 0x79, 0x89, + 0xE5, 0x21, 0x10, 0x0C, 0x2E, 0xE6, 0x25, 0x9E, 0x60, 0x7F, 0xE1, 0x21, 0x98, 0xE8, 0xBC, + 0xB9, 0x88, 0xB0, 0x85, 0x46, 0x68, 0xE4, 0x0D, 0x7C, 0xAE, 0x6A, 0xDC, 0x3B, 0xA4, 0x0B, + 0xA1, 0x21, 0xB7, 0x31, 0x9D, 0x06, 0xD9, 0x88, 0xA0, 0x73, 0xD0, 0x30, 0x97, 0xB9, 0xF5, + 0xC1, 0xC0, 0x72, 0x84, 0xB6, 0x47, 0x3A, 0xE5, 0x7B, 0xF1, 0x54, 0x81, 0x1B, 0x77, 0xBA, + 0xCE, 0xB0, 0x41, 0x2B, 0x8A, 0x69, 0x83, 0xBD, 0xD0, 0xCC, 0xD9, 0xE3, 0xBF, 0x01, 0x4E, + 0x52, 0x00, 0x09, 0xCB, 0x26, 0xD5, 0x78, 0x0E, 0xAB, 0xEF, 0x1B, 0xBA, 0xFA, 0x5E, 0x25, + 0xD4, 0x10, 0x98, 0xA5, 0x4C, 0x47, 0xFC, 0xE8, 0xB6, 0x8D, 0x39, 0x52, 0x91, 0xD5, 0x42, + 0x84, 0xD3, 0x3A, 0xA5, 0x0B, 0x96, 0x64, 0xD1, 0x51, 0x0B, 0x46, 0x7C, 0x8A, 0x53, 0x93, + 0x61, 0xCA, 0x9A, 0x44, 0x48, 0xBC, 0x01, 0xFC, 0xB4, 0xC4, 0xE3, 0xEF, 0x47, 0x5E, 0x8A, + 0xFB, 0x46, 0xA4, 0x94, 0xAE, 0x13, 0xEE, 0x9E, 0xA8, 0xA1, 0x26, 0x68, 0x25, 0xFB, 0xA7, + 0xF3, 0x2B, 0x97, 0x12, 0xFD, 0xE2, 0x52, 0x69, 0x8A, 0x68, 0x35, 0x9B, 0x50, 0x14, 0x1D, + 0x90, 0xF5, 0xC4, 0xA0, 0x62, 0x83, 0xDD, 0xB5, 0x4A, 0xD7, 0xE1, 0x41, 0x2A, 0xC5, 0xEB, + 0xB1, 0x25, 0x01, 0xF7, 0xA8, 0x2B, 0x2A, 0x7F, 0x27, 0xB2, 0xDB, 0xAB, 0x62, 0x6C, 0x3D, + 0xB4, 0x07, 0x45, 0x23, 0xB3, 0x21, 0x1D, 0x31, 0x82, 0xEA, 0x26, 0x13, 0x97, 0xA6, 0xF7, + 0xB1, 0x87, 0xCF, 0x2B, 0x8A, 0x35, 0x6D, 0xED, 0x10, 0x81, 0x2F, 0x1D, 0x30, 0x51, 0x69, + 0xAE, 0xDF, 0x79, 0xB5, 0xFF, 0x1C, 0xF7, 0xC2, 0xD6, 0xE8, 0x6E, 0xE1, 0x1F, 0x28, 0xE9, + 0x6A, 0xA6, 0x3B, 0x5A, 0x03, 0xF5, 0x9F, 0xC9, 0x60, 0xAC, 0x7D, 0x05, 0x72, 0xE9, 0x1D, + 0xDA, 0x61, 0x90, 0x5C, 0x07, 0x11, 0xA9, 0xB2, 0x63, 0x44, 0xA2, 0xA1, 0x0A, 0xA2, 0x04, + 0x1F, 0x2B, 0x13, 0xCB, 0x1A, 0x9A, 0x9A, 0x27, 0x77, 0x4B, 0x6D, 0x0D, 0xED, 0xDC, 0x9D, + 0x81, 0xEA, 0x1B, 0x14, 0x2A, 0xD7, 0xB7, 0x2B, 0xE4, 0x79, 0x91, 0xF2, 0xC9, 0x26, 0x1D, + 0x67, 0x08, 0x15, 0x6E, 0x38, 0xD0, 0x0B, 0x07, 0x40, 0x20, 0x76, 0x6E, 0xB0, 0xC4, 0x94, + 0x39, 0x2D, 0x65, 0xB8, 0x2C, 0xA6, 0x5F, 0x7C, 0x33, 0x52, 0xF9, 0xBB, 0x78, 0x32, 0x5E, + 0xCB, 0x6D, 0xF5, 0x96, 0xC8, 0xAE, 0x57, 0x82, 0x6B, 0x08, 0xCC, 0xD6, 0xF1, 0xD5, 0x29, + 0xD2, 0xE2, 0x59, 0x25, 0xC1, 0xAC, 0x97, 0x24, 0x25, 0xBE, 0xDE, 0xB8, 0x8A, 0x5D, 0x0E, + 0x31, 0x38, 0xDD, 0xC4, 0x34, 0xDA, 0x34, 0x62, 0xEB, 0xDF, 0x6B, 0x12, 0x39, 0xA2, 0x1F, + 0x14, 0x1E, 0xC6, 0x2C, 0xBE, 0x4B, 0xB9, 0x93, 0xBA, 0x25, 0x3B, 0x55, 0xA7, 0x6D, 0x30, + 0xFA, 0xC1, 0x9C, 0x2C, 0x13, 0x84, 0xEF, 0x6B, 0x97, 0x46, 0xC0, 0x77, 0x87, 0xAA, 0x1F, + 0xE9, 0x13, 0xA1, 0x34, 0x83, 0x90, 0xBD, 0x8C, 0x1F, 0x38, 0x6A, 0x08, 0xC7, 0x7C, 0xF7, + 0x10, 0x6C, 0x92, 0x7C, 0xE2, 0x4D, 0xFF, 0xC9, 0xD6, 0xEE, 0x1B, 0x32, 0x35, 0x4D, 0x95, + 0xED, 0x29, 0x23, 0x48, 0x25, 0x31, 0xDE, 0x6B, 0x39, 0x0B, 0xF0, 0xF5, 0xEB, 0x80, 0x27, + 0x6E, 0x90, 0xE7, 0xED, 0x11, 0x13, 0x1C, 0x84, 0x8B, 0xBA, 0xBE, 0xC4, 0xD3, 0x17, 0x23, + 0x62, 0x69, 0xA0, 0xA3, 0xA7, 0xCB, 0xE0, 0xF1, 0x27, 0x2F, 0x93, 0x94, 0x9C, 0xA6, 0xD2, + 0x3E, 0xA2, 0xA7, 0xEE, 0x3F, 0x69, 0x77, 0x91, 0xAA, 0xB7, 0x15, 0x33, 0x42, 0x30, 0x66, + 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x0E, 0x18, 0x1F, 0x23, 0x29, 0x2C, 0x2F, + ]; + + measure(|| { + let pk_expanded = MLDSA87PublicKeyExpanded::from(&pk); + if MLDSA87::verify_with_expanded_key(&pk_expanded, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); +} + +/// Loads the public key for the matching verify bench. See [`load_mldsa44_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mldsa87_lowmemory_pk() -> bouncycastle::mldsa_lowmemory::MLDSA87PublicKey { + use bouncycastle::mldsa_lowmemory::MLDSA87PublicKey; + + 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, @@ -2117,20 +4254,11 @@ fn bench_mldsa87_verify() { 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); - - if MLDSA87::verify(&pk, msg, None, &sig).is_ok() { - eprintln!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + .unwrap() } fn bench_mldsa87_lowmemory_verify() { - use bouncycastle::hex; - use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SIG_LEN, MLDSA87PublicKey, MLDSATrait}; + use bouncycastle::mldsa_lowmemory::{MLDSA87, MLDSA87_SIG_LEN, MLDSATrait}; eprintln!("MLDSA87/Verify"); @@ -2150,15 +4278,326 @@ fn bench_mldsa87_lowmemory_verify() { // 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); + let mldsa87_pk = load_mldsa87_lowmemory_pk(); + let sig: [u8; MLDSA87_SIG_LEN] = [ + 0x78, 0x13, 0x68, 0xE6, 0x4D, 0xBA, 0x54, 0x2A, 0x7E, 0xAC, 0xBD, 0x22, 0x57, 0x33, 0x5C, + 0xC9, 0x43, 0xA0, 0x32, 0x41, 0x00, 0x9B, 0x79, 0x70, 0x93, 0xC6, 0x15, 0xF7, 0x6A, 0x67, + 0x1A, 0x75, 0x91, 0x43, 0x04, 0x41, 0xD8, 0x0B, 0xB5, 0x82, 0x30, 0x4B, 0x33, 0xB9, 0xFC, + 0xE2, 0x95, 0xE0, 0xDD, 0x57, 0xFE, 0x16, 0x93, 0x55, 0xDD, 0xF4, 0x45, 0x3A, 0x2A, 0xCA, + 0x62, 0xD8, 0xEB, 0x81, 0x09, 0xEF, 0x0D, 0x9C, 0xF3, 0xF5, 0xB0, 0xA9, 0x4E, 0x04, 0xAD, + 0x81, 0xB3, 0xE7, 0x86, 0x01, 0x42, 0x43, 0xEC, 0xDE, 0x81, 0x65, 0x51, 0xAA, 0x7F, 0xE0, + 0x1C, 0x63, 0x90, 0x54, 0x25, 0x6A, 0x49, 0x17, 0x56, 0xBE, 0xF5, 0x9F, 0x50, 0x34, 0xF7, + 0x17, 0xFF, 0x4F, 0x85, 0xE7, 0x0B, 0xA7, 0x73, 0x1A, 0x49, 0x97, 0x14, 0x15, 0xB6, 0xA7, + 0xE7, 0xD8, 0x16, 0xAB, 0x43, 0x4B, 0x9F, 0x17, 0xA3, 0x09, 0x5E, 0xDE, 0x6F, 0xD4, 0x32, + 0xBE, 0x2B, 0xFA, 0x82, 0x72, 0x40, 0x45, 0xDD, 0xA0, 0xDF, 0xFF, 0x7A, 0x02, 0x81, 0xE9, + 0x00, 0x09, 0x39, 0xCC, 0xBA, 0x3D, 0x8A, 0xB3, 0x24, 0x51, 0x39, 0xC4, 0x41, 0x64, 0x8C, + 0x76, 0xA6, 0x53, 0x61, 0x27, 0xE4, 0xD1, 0xEF, 0x0D, 0xF1, 0x53, 0x18, 0x83, 0xAB, 0x78, + 0xC8, 0xB4, 0x13, 0x23, 0x61, 0x7A, 0xD8, 0xDB, 0x03, 0xD9, 0x90, 0x8C, 0x9E, 0x08, 0xA9, + 0xF7, 0x32, 0x1C, 0x45, 0x05, 0x1B, 0x3C, 0x94, 0x21, 0x33, 0x47, 0xB1, 0x1C, 0x4A, 0x84, + 0x49, 0x1D, 0xE7, 0xA7, 0xBE, 0x68, 0x70, 0x1E, 0x47, 0xD7, 0xF0, 0xE0, 0xB3, 0x3E, 0x76, + 0x7B, 0xEF, 0x17, 0x69, 0x4E, 0x4D, 0x33, 0x24, 0x4E, 0xD9, 0x2E, 0xBD, 0x74, 0xC8, 0x5A, + 0xB6, 0xC8, 0x44, 0x41, 0xCD, 0xDC, 0x14, 0x33, 0x1E, 0x6A, 0xE8, 0xBD, 0x23, 0x67, 0x4B, + 0xDA, 0x27, 0xF0, 0x9C, 0x05, 0x0D, 0x88, 0xF7, 0xD4, 0x30, 0xFE, 0xEE, 0x7F, 0x15, 0xA7, + 0x2A, 0x24, 0xD6, 0x53, 0xBB, 0x6B, 0xEC, 0x54, 0x49, 0x1B, 0x98, 0x36, 0x2C, 0xE1, 0x31, + 0xD3, 0x7C, 0x7D, 0x78, 0xA3, 0xF9, 0xA8, 0x93, 0xDB, 0x5A, 0xBD, 0xCC, 0xD6, 0x66, 0x35, + 0x93, 0xB8, 0x8B, 0xC6, 0xC9, 0x7F, 0x07, 0xF8, 0xEA, 0xFC, 0xCF, 0xD2, 0x5E, 0x81, 0x80, + 0xD9, 0x18, 0xEF, 0xBC, 0xD9, 0x5B, 0xBF, 0x3D, 0xA2, 0x9F, 0x08, 0x1E, 0x3E, 0x19, 0x32, + 0x09, 0x59, 0x39, 0x19, 0x8E, 0x2A, 0x15, 0x5B, 0x2D, 0x80, 0x3A, 0x3E, 0x84, 0xCA, 0x4F, + 0x34, 0x56, 0x9D, 0xF6, 0x95, 0xC2, 0x59, 0xFA, 0xF3, 0xC0, 0xD8, 0xF0, 0xCD, 0x21, 0x7E, + 0xBD, 0x2D, 0xBA, 0xD5, 0x42, 0xB3, 0x2F, 0xBB, 0x54, 0xE4, 0x4A, 0xAF, 0x0B, 0x5D, 0xC7, + 0x39, 0xFA, 0xFE, 0xF2, 0xE4, 0x6D, 0xB8, 0xD6, 0x8B, 0xFC, 0x35, 0xF4, 0x4F, 0x03, 0x8C, + 0xB1, 0xF5, 0x23, 0x1A, 0x1B, 0x5B, 0x13, 0x4A, 0xE6, 0x83, 0xE7, 0xF3, 0x29, 0x7C, 0xC7, + 0xA9, 0x5B, 0xD1, 0x91, 0xB3, 0x10, 0xF6, 0x82, 0x01, 0x45, 0x07, 0x97, 0xFE, 0x32, 0x93, + 0xCD, 0xE1, 0x67, 0x2D, 0xFE, 0xCA, 0x4B, 0x49, 0x3F, 0x53, 0xC7, 0x68, 0xEA, 0x04, 0x8A, + 0x97, 0x2A, 0x4C, 0xD8, 0x4D, 0x39, 0xEF, 0x68, 0x29, 0x57, 0xB8, 0xF2, 0x8B, 0xA2, 0x94, + 0x87, 0xB4, 0x68, 0x9B, 0x43, 0xFE, 0xC2, 0x65, 0x58, 0x23, 0xD9, 0xBB, 0x99, 0xFF, 0xCF, + 0x31, 0x49, 0x03, 0x66, 0xA9, 0x86, 0x0A, 0x5D, 0x5B, 0x8E, 0x32, 0xA3, 0xB8, 0xBF, 0xEB, + 0x6F, 0x55, 0xF8, 0x8F, 0xB8, 0x0C, 0x8C, 0x01, 0x42, 0x08, 0x6F, 0x22, 0x0E, 0x1F, 0x6F, + 0x28, 0x62, 0xDA, 0xBD, 0xA5, 0x8C, 0x3B, 0x6F, 0x5F, 0xAA, 0x80, 0x5B, 0x39, 0xCF, 0xAC, + 0x4B, 0x6D, 0x7E, 0xA7, 0xAC, 0xDF, 0x1B, 0x06, 0x90, 0x06, 0x3B, 0x0C, 0x1E, 0xA3, 0x8C, + 0x7C, 0x47, 0x55, 0x18, 0x99, 0x66, 0xDC, 0x63, 0x10, 0x55, 0xF1, 0x53, 0xF7, 0x1B, 0x77, + 0xB1, 0x14, 0xFA, 0x5C, 0x30, 0x93, 0x16, 0xBA, 0x51, 0x23, 0x30, 0xEA, 0x5C, 0xDB, 0x0B, + 0xB1, 0x76, 0x00, 0x1E, 0x57, 0x46, 0x15, 0x63, 0xD1, 0x72, 0x59, 0xF3, 0x5D, 0x0C, 0x30, + 0xEF, 0x5A, 0xC8, 0x38, 0xC0, 0x32, 0x54, 0x02, 0xBA, 0xB5, 0x2C, 0x53, 0x14, 0x69, 0x52, + 0x6A, 0xE3, 0xEE, 0x62, 0x93, 0xF7, 0xB5, 0x76, 0x9D, 0x27, 0xE6, 0x9F, 0xA8, 0x1C, 0xD2, + 0x5A, 0x31, 0xCD, 0x09, 0x5B, 0x12, 0x6E, 0x70, 0xC5, 0x7A, 0xC3, 0x16, 0x9A, 0x5F, 0x58, + 0x5A, 0x11, 0xF1, 0x74, 0x8D, 0x9D, 0x22, 0xF2, 0x56, 0x49, 0x11, 0xC2, 0x6A, 0x24, 0xB2, + 0x15, 0x3A, 0x78, 0xF3, 0xA0, 0x68, 0x22, 0xF5, 0xF1, 0x96, 0x3F, 0x23, 0x7A, 0xBE, 0xB4, + 0x8E, 0xFD, 0x9A, 0x9C, 0xD4, 0x78, 0xDE, 0x57, 0x9C, 0x5A, 0x0B, 0xA8, 0x4D, 0x00, 0xE9, + 0x6F, 0xBD, 0xE3, 0x6D, 0x8C, 0xE2, 0x0E, 0x7E, 0x94, 0x85, 0x47, 0xFB, 0x68, 0x50, 0x83, + 0x4F, 0xF7, 0x9D, 0x21, 0x18, 0x30, 0xF6, 0xEE, 0x97, 0x33, 0x59, 0x78, 0x1D, 0x9D, 0x50, + 0x08, 0xFB, 0x43, 0xA8, 0x93, 0x54, 0x78, 0x2F, 0xDE, 0x41, 0x58, 0x17, 0x7F, 0x52, 0x06, + 0xCE, 0x1D, 0x38, 0xC8, 0x89, 0xE9, 0x9E, 0x4B, 0xB5, 0xB4, 0xAB, 0x34, 0xD6, 0xA0, 0x5C, + 0x42, 0xF5, 0xD7, 0x19, 0xEA, 0x03, 0xDB, 0xC5, 0x4A, 0xDB, 0xA7, 0x5A, 0x3B, 0xB4, 0x4A, + 0x3C, 0x08, 0xC7, 0x55, 0x64, 0x62, 0xF8, 0xC5, 0xB7, 0xC5, 0x68, 0xA6, 0x92, 0x42, 0xCF, + 0x5B, 0xE6, 0x09, 0x8E, 0xBA, 0x0A, 0x22, 0x49, 0xC1, 0xCA, 0x5B, 0x21, 0x09, 0xB6, 0x40, + 0x4A, 0x96, 0x2A, 0xBC, 0x1C, 0x15, 0x9C, 0x6B, 0x48, 0xA7, 0x9F, 0xB9, 0x7E, 0x4A, 0x33, + 0x37, 0xD9, 0x93, 0x23, 0x74, 0x62, 0x21, 0x29, 0x74, 0x23, 0xF9, 0xBD, 0x1B, 0x12, 0xE7, + 0x84, 0x89, 0xE0, 0x1E, 0x6A, 0x10, 0xF0, 0xFD, 0x6B, 0xBA, 0x1C, 0xFD, 0x6A, 0xE1, 0xB7, + 0x5D, 0xBE, 0x69, 0xF8, 0xB8, 0xEE, 0x51, 0xA4, 0xE7, 0xF6, 0x8B, 0xA2, 0xC4, 0x07, 0xC9, + 0xC0, 0xBA, 0xD3, 0x89, 0x2B, 0x29, 0xB0, 0x17, 0x0A, 0xB7, 0x58, 0x36, 0xFD, 0xD4, 0x9A, + 0x7E, 0xE3, 0xC2, 0xBB, 0x30, 0xF2, 0xC3, 0xD2, 0x26, 0xBC, 0xEC, 0x49, 0x14, 0x09, 0x52, + 0x17, 0x0B, 0x0D, 0x16, 0x0F, 0x97, 0xB3, 0x0B, 0x7B, 0x7B, 0x09, 0x67, 0x19, 0x53, 0x86, + 0x77, 0xEB, 0xB0, 0x69, 0x22, 0xF2, 0x69, 0x25, 0x22, 0x7C, 0x88, 0x52, 0xAC, 0xC1, 0x07, + 0xA8, 0xF1, 0x73, 0xB3, 0x8D, 0x96, 0x69, 0x75, 0x84, 0xBD, 0x3D, 0xFE, 0x16, 0x9B, 0x40, + 0x73, 0xAA, 0x58, 0xA7, 0xBF, 0x37, 0x1D, 0x5C, 0x4B, 0xB0, 0xEE, 0xD3, 0x0F, 0x08, 0x21, + 0x2D, 0xEF, 0xB3, 0xAE, 0xC9, 0x02, 0xD4, 0x54, 0x60, 0x84, 0x17, 0x6B, 0xF0, 0xF8, 0x6D, + 0x93, 0xCF, 0x36, 0xA4, 0x68, 0x9A, 0x5E, 0x87, 0x4B, 0x32, 0xD6, 0xB7, 0xD3, 0xC1, 0xE3, + 0xFB, 0xCF, 0xD9, 0x88, 0xC3, 0x5D, 0xBC, 0x9A, 0x8D, 0x0A, 0x01, 0x9A, 0xD6, 0xD7, 0xE1, + 0x5E, 0xC3, 0xAC, 0x97, 0x12, 0x5D, 0xB6, 0xAB, 0xFE, 0x00, 0xBE, 0xFF, 0xE3, 0x5A, 0x81, + 0x66, 0x66, 0x99, 0xA9, 0x1E, 0x15, 0x94, 0x5C, 0x62, 0xD6, 0x46, 0x69, 0x0B, 0x5B, 0x52, + 0xDE, 0x8B, 0x83, 0x5E, 0xE9, 0xBE, 0x53, 0x58, 0x8F, 0xDE, 0x5D, 0x63, 0x02, 0x3B, 0x52, + 0xB2, 0xB1, 0xF4, 0x61, 0x0C, 0x23, 0x7A, 0x82, 0x9F, 0x59, 0x01, 0xA4, 0x60, 0x42, 0x96, + 0x3C, 0xCE, 0x7B, 0x85, 0xAA, 0x04, 0x0A, 0xDD, 0xE0, 0x29, 0x85, 0xE1, 0x4E, 0x23, 0xC4, + 0xEA, 0xDB, 0x75, 0x22, 0x1C, 0x60, 0x7D, 0x24, 0x67, 0x2E, 0x24, 0x4C, 0x66, 0xC9, 0xC2, + 0x4C, 0x3C, 0xB7, 0xFD, 0x90, 0xBB, 0x23, 0x29, 0x5C, 0x9D, 0x3D, 0x9D, 0xA5, 0x16, 0xBC, + 0xE3, 0xDD, 0x46, 0x2D, 0x66, 0x60, 0xF9, 0xF9, 0x1E, 0xF0, 0x61, 0x8A, 0x4D, 0x4D, 0x3D, + 0x66, 0x68, 0xC5, 0xD1, 0xE2, 0xE8, 0xED, 0x43, 0x3E, 0xBF, 0xE0, 0x76, 0x2B, 0xEB, 0x74, + 0x33, 0x24, 0xE1, 0x16, 0x08, 0xF8, 0xB1, 0x4B, 0x69, 0xCE, 0x4C, 0x22, 0x1C, 0x16, 0x54, + 0xBA, 0x49, 0x92, 0xA5, 0xAF, 0x2D, 0x94, 0x9C, 0x29, 0x39, 0xF9, 0x5D, 0x1C, 0x8F, 0xB7, + 0x67, 0xAF, 0x2A, 0x84, 0x3C, 0xC7, 0xC7, 0x8F, 0x57, 0x25, 0x9D, 0x5C, 0x0C, 0x6C, 0xA8, + 0x3F, 0xCA, 0x41, 0xEF, 0x5E, 0xCC, 0x4E, 0xEE, 0xEA, 0x93, 0xE4, 0x51, 0x8C, 0x24, 0xD3, + 0x04, 0x0F, 0x2C, 0xD9, 0x0D, 0xF3, 0xE5, 0x35, 0xE9, 0x89, 0xE6, 0x06, 0xFA, 0x10, 0x9E, + 0x2C, 0x45, 0x3E, 0xD7, 0x35, 0x3D, 0xB1, 0xCD, 0xB2, 0x71, 0x37, 0xF0, 0x05, 0xF9, 0xDD, + 0x8D, 0x2A, 0xEB, 0xFB, 0x72, 0x55, 0xA6, 0x09, 0x8B, 0x69, 0x02, 0x15, 0xE1, 0x00, 0xCF, + 0xE4, 0x4C, 0xA0, 0xF2, 0x74, 0x5F, 0xCE, 0xD4, 0x83, 0x22, 0xBC, 0x96, 0x67, 0xAB, 0x16, + 0xD2, 0xE1, 0xC0, 0xFF, 0x49, 0x1B, 0x96, 0xA1, 0x7B, 0x83, 0x3D, 0x4F, 0xD4, 0x4D, 0x31, + 0xC2, 0x23, 0x0A, 0xC8, 0x35, 0x79, 0x6E, 0x06, 0x3A, 0xB0, 0x30, 0x00, 0xF0, 0x4F, 0x15, + 0xC7, 0x05, 0x60, 0x03, 0x37, 0x63, 0xA4, 0x85, 0x52, 0xCC, 0xEA, 0xAC, 0xAD, 0xE9, 0xCA, + 0x5C, 0x80, 0x55, 0xF3, 0x74, 0x5E, 0x17, 0x90, 0x68, 0xA2, 0x87, 0x18, 0x3F, 0x2B, 0xC3, + 0xEF, 0x63, 0x27, 0xDE, 0xC5, 0xAC, 0x7C, 0xF7, 0xB0, 0x52, 0xEF, 0x5A, 0x88, 0x73, 0xE6, + 0x97, 0xEF, 0xDE, 0x08, 0x96, 0x88, 0xF4, 0x3B, 0xE4, 0x64, 0x82, 0x7C, 0x2F, 0xA8, 0x3E, + 0xB5, 0x31, 0xB3, 0x67, 0x41, 0x45, 0xE9, 0x5C, 0x69, 0x9C, 0x82, 0x99, 0x0E, 0x68, 0x49, + 0x67, 0xDA, 0xD3, 0x19, 0xD9, 0xF6, 0x4A, 0xB1, 0x6C, 0xD9, 0xFE, 0x9B, 0x6C, 0x41, 0x23, + 0x2A, 0xC4, 0xAE, 0x37, 0x95, 0xFD, 0x8A, 0x76, 0xAA, 0x9B, 0x02, 0xE9, 0x70, 0x24, 0x20, + 0x61, 0xC6, 0xDA, 0x45, 0xA2, 0xAF, 0x74, 0xAD, 0x9C, 0xB2, 0xA7, 0x99, 0x35, 0xC9, 0x26, + 0x25, 0xE2, 0x42, 0xF4, 0xBC, 0x7F, 0xCE, 0x54, 0xD5, 0xC1, 0x0A, 0x9E, 0x61, 0xF8, 0x75, + 0x16, 0x2F, 0xA6, 0x51, 0xB6, 0x60, 0x57, 0xBA, 0x03, 0x6F, 0x06, 0x2D, 0x6D, 0x39, 0xD0, + 0x50, 0x2B, 0x93, 0xA5, 0x64, 0x0B, 0x78, 0xC6, 0xC2, 0xFD, 0x20, 0xB0, 0x2F, 0xF8, 0x36, + 0x76, 0xA8, 0x7A, 0x94, 0x94, 0x5D, 0x47, 0x6C, 0x34, 0x98, 0x03, 0xEA, 0x4F, 0xE6, 0x0C, + 0xDC, 0xEA, 0x65, 0xBB, 0x26, 0x29, 0xE2, 0xBC, 0x09, 0xD4, 0x47, 0x2E, 0xC6, 0x34, 0x22, + 0xDE, 0xE2, 0x05, 0x2F, 0x09, 0x8D, 0xEA, 0xF5, 0x53, 0x1E, 0x6C, 0x9B, 0xED, 0x66, 0x72, + 0xA8, 0xB6, 0x99, 0x80, 0x2E, 0xFE, 0x0C, 0xDE, 0xD8, 0x0C, 0x84, 0x55, 0xF5, 0x85, 0xD1, + 0xBA, 0x63, 0x3D, 0x28, 0x1F, 0x1A, 0x21, 0xAD, 0xAB, 0x48, 0xE6, 0x3B, 0x44, 0xE0, 0xC2, + 0xA4, 0xD7, 0x60, 0x8C, 0xF9, 0x8A, 0xAB, 0xF8, 0xAD, 0xC8, 0x6B, 0xCB, 0x8F, 0x61, 0xE8, + 0xB0, 0x6C, 0xD2, 0x38, 0x5F, 0x82, 0xE0, 0xA3, 0xCD, 0xD0, 0x3C, 0xAB, 0x15, 0x2D, 0x59, + 0x51, 0x85, 0x9C, 0x45, 0x32, 0xF9, 0x16, 0x8E, 0x78, 0xF1, 0x7B, 0xA2, 0xA5, 0x77, 0x27, + 0x80, 0x32, 0x7D, 0xCF, 0x4E, 0x62, 0xB4, 0xD2, 0x6E, 0x44, 0x37, 0x62, 0xFC, 0x48, 0x8A, + 0xE4, 0xCD, 0x4D, 0x11, 0x56, 0xDB, 0xD5, 0x78, 0x25, 0x95, 0xCF, 0xD7, 0x69, 0x7A, 0x51, + 0x4A, 0xBC, 0x9B, 0x16, 0x0C, 0x9C, 0xCF, 0x08, 0xED, 0xC8, 0x61, 0x34, 0xA7, 0x55, 0xB9, + 0x0E, 0x9B, 0xB5, 0x43, 0x51, 0x1E, 0x88, 0x8E, 0x31, 0x57, 0x72, 0x1A, 0x52, 0xD1, 0xBC, + 0x5D, 0xB3, 0x30, 0x29, 0xFB, 0x33, 0x5E, 0xA2, 0x11, 0x4E, 0x21, 0xC0, 0x33, 0x68, 0xC8, + 0xD7, 0xF4, 0xD8, 0x27, 0x96, 0x06, 0x41, 0x77, 0x2A, 0x4A, 0x32, 0xA7, 0x38, 0xDF, 0x60, + 0xD1, 0x9E, 0xC7, 0x7A, 0xB0, 0x9D, 0x22, 0xF5, 0x7C, 0xC2, 0x52, 0x3B, 0x95, 0x03, 0xB3, + 0xF5, 0xB1, 0xCE, 0xBC, 0x5A, 0xE1, 0x5F, 0x88, 0x5F, 0x15, 0x98, 0x42, 0xDB, 0x73, 0x59, + 0xA1, 0xC8, 0x9D, 0x3D, 0x82, 0xD3, 0x40, 0x70, 0x68, 0xF1, 0x5B, 0x67, 0x39, 0x62, 0x6E, + 0xB8, 0xC5, 0x21, 0xFC, 0x8C, 0x5C, 0x74, 0x91, 0xF9, 0x45, 0xD4, 0x9F, 0x14, 0xE6, 0x98, + 0x9D, 0xA3, 0x40, 0xBD, 0xF4, 0x9E, 0x7F, 0x8A, 0x79, 0x27, 0x47, 0xAA, 0x65, 0x8B, 0xC1, + 0x14, 0x14, 0x3B, 0xA9, 0x3F, 0x26, 0x02, 0x2D, 0x00, 0x17, 0x35, 0xB7, 0x44, 0x63, 0x9B, + 0xBF, 0x22, 0xAA, 0xB2, 0xA1, 0x85, 0x1C, 0xFC, 0x93, 0x4F, 0x9C, 0x69, 0xD3, 0x76, 0x4F, + 0xDE, 0xA3, 0xD2, 0x3D, 0xB1, 0x79, 0x98, 0xE6, 0x13, 0x8C, 0xFD, 0x7C, 0xD9, 0xE9, 0xA4, + 0x7C, 0xB7, 0x41, 0x93, 0xBD, 0x71, 0xAA, 0xF2, 0x8C, 0xDD, 0x9D, 0x1E, 0xB5, 0x95, 0x12, + 0x55, 0x46, 0xA4, 0xF4, 0x35, 0x7E, 0xBB, 0xD1, 0xF4, 0x10, 0xE3, 0xBF, 0x85, 0x57, 0x89, + 0x2D, 0xE6, 0x85, 0x09, 0xB5, 0xB9, 0x8C, 0x5C, 0x22, 0x9E, 0x94, 0x2C, 0x91, 0x0F, 0xDD, + 0x3E, 0x54, 0xCB, 0x6A, 0xD5, 0x4D, 0x8D, 0xD8, 0x86, 0xCB, 0x97, 0xEC, 0xC0, 0x6D, 0x1E, + 0x40, 0x1B, 0x83, 0x95, 0xD0, 0xBC, 0xB0, 0xDB, 0x9A, 0x03, 0x1D, 0xC6, 0x6C, 0x92, 0x94, + 0xF9, 0x05, 0x3C, 0x68, 0xFC, 0x42, 0x04, 0x2B, 0x1F, 0xA1, 0x67, 0x1F, 0xC7, 0xD5, 0x10, + 0xB7, 0x09, 0x16, 0xC0, 0x13, 0x9C, 0xFE, 0xBE, 0x3A, 0x91, 0x24, 0x45, 0x27, 0xCE, 0x94, + 0x39, 0x86, 0x0C, 0xED, 0xB3, 0x09, 0x08, 0x19, 0x7B, 0xE8, 0x51, 0xCB, 0xD1, 0xD3, 0xB1, + 0x8C, 0xA5, 0x41, 0x35, 0x84, 0x49, 0xFB, 0x34, 0xFB, 0x5C, 0xD5, 0x69, 0x63, 0x0E, 0xD5, + 0xF6, 0x7B, 0x87, 0x95, 0xE8, 0x78, 0x28, 0xF2, 0xCE, 0x3B, 0xEC, 0xFE, 0x45, 0x75, 0x79, + 0xD8, 0x23, 0x33, 0xB0, 0xBB, 0xAB, 0x09, 0x4D, 0xE3, 0x91, 0xE1, 0xF8, 0x15, 0x7B, 0xD4, + 0x31, 0xE3, 0x65, 0xCA, 0x86, 0x46, 0x30, 0x93, 0x2B, 0xBE, 0xBB, 0x48, 0xF4, 0x5F, 0x81, + 0x34, 0x42, 0x4E, 0x18, 0xAB, 0x45, 0x50, 0x29, 0xB5, 0x4B, 0x19, 0xE2, 0xF3, 0xBF, 0xEC, + 0x5E, 0x44, 0xAD, 0x0E, 0xA5, 0xC0, 0x3F, 0x53, 0xD8, 0xF9, 0x25, 0xB6, 0x35, 0x83, 0x8A, + 0xA7, 0x01, 0x5A, 0x7C, 0x9E, 0x32, 0x5B, 0xDF, 0xAF, 0xF9, 0x66, 0xEA, 0x95, 0x12, 0xDD, + 0x50, 0xF8, 0x7C, 0x89, 0x95, 0xCE, 0xA7, 0x56, 0x1C, 0x23, 0xF4, 0xFB, 0x06, 0xD9, 0x64, + 0xAB, 0x8F, 0x19, 0x13, 0xA6, 0xCA, 0x17, 0xE4, 0xCA, 0x60, 0xD6, 0xBC, 0x07, 0x8E, 0x17, + 0x84, 0xF8, 0x9C, 0x67, 0x3C, 0x91, 0xD9, 0x55, 0xBC, 0xF4, 0x5F, 0x58, 0xCA, 0x97, 0x09, + 0x57, 0x9D, 0x5E, 0x38, 0x31, 0xDF, 0x12, 0xCF, 0xDB, 0x75, 0x16, 0xFD, 0x21, 0x87, 0x8C, + 0xB5, 0x42, 0x43, 0x57, 0x9B, 0x93, 0x46, 0xD2, 0xDE, 0x4B, 0xE2, 0x5F, 0x50, 0x8E, 0x84, + 0xB1, 0xAD, 0xC7, 0x8C, 0xB9, 0x1C, 0x03, 0xDA, 0x3C, 0x4F, 0xD5, 0x9E, 0x45, 0x29, 0x18, + 0x98, 0x38, 0xF7, 0x4F, 0x63, 0x12, 0x82, 0x06, 0x20, 0xA5, 0x99, 0x6B, 0x79, 0x1F, 0xFC, + 0xB3, 0x32, 0xF8, 0x47, 0x09, 0x46, 0x13, 0xF2, 0x14, 0x8B, 0x86, 0x20, 0x34, 0xFA, 0x89, + 0xD0, 0xD0, 0xFF, 0x18, 0x08, 0xD9, 0x02, 0xC5, 0xD1, 0xAF, 0x64, 0xD5, 0x52, 0x24, 0x92, + 0xD6, 0x1E, 0xCD, 0xE4, 0xC7, 0x3B, 0xE8, 0x9A, 0x33, 0x78, 0x2C, 0xEF, 0x1A, 0xCC, 0x1D, + 0xC3, 0x27, 0xFB, 0x2E, 0xB9, 0xD1, 0x76, 0x42, 0x20, 0x9B, 0x85, 0xAA, 0x8B, 0x1D, 0xC5, + 0x7C, 0xBF, 0x06, 0x7C, 0x7A, 0xA2, 0x9D, 0xA6, 0xB7, 0xE1, 0x57, 0xD2, 0x3E, 0x17, 0x1D, + 0x3A, 0xE6, 0xF3, 0x85, 0x58, 0x34, 0x07, 0x17, 0x91, 0x40, 0x2C, 0x85, 0x1F, 0xF2, 0xDD, + 0x67, 0x10, 0x99, 0x79, 0xF7, 0xEE, 0x5E, 0x09, 0xE6, 0x4B, 0x4E, 0xEF, 0xDE, 0xE7, 0x11, + 0x2B, 0x55, 0xCE, 0x20, 0x0B, 0xB8, 0xC8, 0x05, 0x1E, 0x34, 0x28, 0xC3, 0x05, 0xFA, 0x1D, + 0x57, 0x6B, 0xFF, 0xBB, 0x25, 0xA7, 0x0E, 0xB5, 0x71, 0x16, 0x8F, 0xC6, 0x0D, 0xAD, 0xD9, + 0x28, 0xB1, 0x0C, 0xFD, 0x07, 0xDE, 0x80, 0xA8, 0x5B, 0x8D, 0xF3, 0xED, 0xC3, 0x72, 0xD4, + 0x88, 0xC2, 0x1F, 0x0D, 0x57, 0x87, 0x61, 0x1C, 0xC6, 0xFB, 0x73, 0xAE, 0xEB, 0x6F, 0x92, + 0x0A, 0x10, 0x92, 0x94, 0xB4, 0x9D, 0x38, 0x70, 0xF9, 0x0D, 0xE3, 0xB3, 0x60, 0xD1, 0x4D, + 0xF7, 0x7E, 0xF9, 0x56, 0x40, 0xBC, 0xAC, 0x7A, 0x4D, 0xBC, 0xA9, 0x01, 0xA3, 0x1D, 0xB8, + 0x3E, 0x83, 0xF5, 0xC5, 0x9C, 0xE3, 0x27, 0x20, 0x7E, 0xA9, 0xB2, 0x7C, 0x3B, 0x97, 0x8D, + 0x30, 0xD5, 0x38, 0x65, 0xC1, 0xB8, 0x47, 0x64, 0xF0, 0x25, 0xE8, 0x73, 0x2D, 0x50, 0x07, + 0x55, 0x4C, 0xE5, 0xC9, 0xCC, 0x41, 0x0B, 0x2E, 0xEF, 0xD7, 0xE4, 0xD9, 0x90, 0xC5, 0x38, + 0x55, 0x76, 0x06, 0xA6, 0xBC, 0x47, 0x57, 0x7A, 0x43, 0x76, 0x8D, 0x30, 0xAA, 0x3E, 0x85, + 0x98, 0xFD, 0x6F, 0x4F, 0xE7, 0xAC, 0x43, 0x9F, 0x39, 0x31, 0xC5, 0x8F, 0xD6, 0x9D, 0x90, + 0x76, 0x5A, 0xC9, 0xF4, 0x56, 0xAC, 0x7D, 0xE0, 0x85, 0xE1, 0x4A, 0x08, 0x98, 0xC4, 0x55, + 0x7F, 0x5D, 0x3B, 0xAA, 0xAE, 0x07, 0xED, 0xC6, 0x07, 0xDE, 0x69, 0x00, 0x14, 0x6B, 0x97, + 0xB3, 0x5A, 0xAE, 0x57, 0x01, 0x53, 0xDC, 0x10, 0x78, 0x15, 0xEF, 0x9F, 0xEB, 0xDD, 0x4F, + 0xD5, 0x67, 0xD6, 0x37, 0xFE, 0xE8, 0xF8, 0xBF, 0xB4, 0xB3, 0x41, 0x3E, 0xA6, 0xAE, 0xAD, + 0x48, 0x46, 0xAB, 0x73, 0x3A, 0x04, 0xF1, 0xE4, 0xBC, 0x32, 0xA3, 0xBB, 0xB1, 0xC1, 0x6B, + 0xAF, 0x8D, 0x0B, 0xDB, 0x9C, 0xCB, 0x82, 0xFE, 0x46, 0x47, 0x9C, 0xCF, 0xD0, 0x40, 0xB5, + 0xE6, 0x40, 0x64, 0xE5, 0x39, 0xB3, 0x9C, 0x66, 0xE4, 0x50, 0x1D, 0xC8, 0x22, 0x87, 0x3A, + 0xC6, 0x11, 0x9A, 0x4A, 0x11, 0x2A, 0x1F, 0x7C, 0xD6, 0xDF, 0x0E, 0x5F, 0x84, 0x35, 0x6C, + 0xE8, 0x53, 0xCE, 0xD3, 0x4C, 0xE6, 0x9A, 0x9E, 0x73, 0x83, 0x53, 0x49, 0x83, 0xC5, 0x1C, + 0x50, 0x26, 0x9B, 0xF8, 0xB9, 0x58, 0x6A, 0x0E, 0x5B, 0xA9, 0x05, 0xFD, 0x3B, 0xCE, 0x08, + 0x0B, 0x00, 0xE7, 0xF7, 0xD4, 0x8E, 0x55, 0xF4, 0x89, 0x47, 0x9B, 0x57, 0x71, 0xE9, 0x95, + 0xFB, 0x02, 0x0E, 0x58, 0xFE, 0xB7, 0x4A, 0xF6, 0x5C, 0x3E, 0xE7, 0x6A, 0xA4, 0xE6, 0x9B, + 0x5B, 0xA8, 0xBD, 0xA2, 0x49, 0xA1, 0xB2, 0xD6, 0x2C, 0x08, 0xD4, 0x18, 0xC3, 0x63, 0x5D, + 0x06, 0x18, 0x46, 0x04, 0x08, 0x43, 0x99, 0x1A, 0xB4, 0x75, 0x47, 0x3D, 0xA8, 0x5D, 0x94, + 0x98, 0x1F, 0xB8, 0x44, 0x25, 0xE7, 0xAD, 0x95, 0x1C, 0xE0, 0xA4, 0x2B, 0xE6, 0x42, 0xFE, + 0x65, 0x8B, 0x7A, 0xAA, 0xE7, 0x2B, 0x14, 0x7C, 0xDC, 0x08, 0x6C, 0x24, 0xB1, 0x57, 0x1E, + 0xB2, 0x27, 0x2E, 0x2A, 0x72, 0xB1, 0x56, 0x60, 0xD8, 0x54, 0xEB, 0xE1, 0x9E, 0xC7, 0xD9, + 0xAB, 0x7E, 0xA1, 0x78, 0x00, 0xD0, 0xB6, 0xAE, 0x72, 0x7B, 0x39, 0x21, 0x74, 0x67, 0xC6, + 0x62, 0xBA, 0x08, 0xE6, 0xF1, 0x91, 0x93, 0x95, 0x1E, 0xEF, 0xF0, 0x28, 0x06, 0xA7, 0x84, + 0x3E, 0xB5, 0xC7, 0x1B, 0x2F, 0x04, 0xDC, 0xB6, 0x05, 0xEC, 0xED, 0xC5, 0x12, 0x8C, 0xD6, + 0x77, 0x03, 0x03, 0x8C, 0x44, 0xBF, 0x20, 0xFE, 0x06, 0xF3, 0xED, 0x8C, 0x13, 0x68, 0xFE, + 0x38, 0xE7, 0x29, 0x44, 0xD5, 0xC5, 0x2F, 0xCA, 0x46, 0xA4, 0x5F, 0xD4, 0x8D, 0x8F, 0xE5, + 0xDA, 0x64, 0x18, 0x3D, 0x4D, 0x62, 0xEC, 0x01, 0xAA, 0x3D, 0x9D, 0x67, 0x2E, 0xC6, 0x7A, + 0x01, 0xC1, 0x7F, 0x21, 0xE0, 0x25, 0x25, 0xF0, 0x51, 0x3C, 0xCE, 0x03, 0x0C, 0x66, 0x4F, + 0xC8, 0x78, 0x47, 0x63, 0x08, 0x66, 0x08, 0xBC, 0x80, 0x99, 0xC2, 0x04, 0xC2, 0x55, 0xFF, + 0xED, 0x1D, 0xAF, 0x43, 0x2C, 0xEB, 0x45, 0xFB, 0xD1, 0x35, 0xE2, 0x1E, 0x81, 0x90, 0xC5, + 0xBF, 0xEE, 0x19, 0x21, 0x71, 0xFA, 0xA7, 0x75, 0x20, 0x48, 0x1E, 0x69, 0xE8, 0x7B, 0x7F, + 0x76, 0x79, 0x0B, 0xEF, 0x76, 0xCB, 0x8D, 0x3C, 0x88, 0xF5, 0xC6, 0xE3, 0x2F, 0xC5, 0x9E, + 0x7B, 0xD4, 0x53, 0x51, 0xD6, 0x66, 0x96, 0xB6, 0x1D, 0x9F, 0x40, 0x72, 0x6F, 0xB9, 0xA9, + 0x80, 0x00, 0xB6, 0x87, 0x38, 0xCF, 0x7E, 0x34, 0xB9, 0x8B, 0x6A, 0x4A, 0xAA, 0x2A, 0xC1, + 0xD7, 0xB1, 0x40, 0x7D, 0xB8, 0x97, 0x83, 0xF8, 0x07, 0x71, 0x03, 0xEA, 0x9C, 0x9E, 0x89, + 0x24, 0x7E, 0xAE, 0x07, 0x8A, 0xDF, 0xB3, 0x6E, 0x21, 0x47, 0x4C, 0x3B, 0xB1, 0xFE, 0x0C, + 0x87, 0x68, 0x7C, 0x62, 0x33, 0xA5, 0x33, 0xA0, 0x1E, 0x10, 0x81, 0xB9, 0x3A, 0x35, 0x21, + 0xD3, 0x39, 0xF3, 0x9C, 0x07, 0x56, 0x09, 0xBA, 0xCE, 0x53, 0x19, 0x94, 0x98, 0x8A, 0xE3, + 0x14, 0xF7, 0x7F, 0xC6, 0x03, 0x41, 0x13, 0xA1, 0x38, 0xC6, 0x7E, 0xB7, 0xE0, 0x37, 0x50, + 0xCB, 0xEC, 0x8D, 0x28, 0xBD, 0x21, 0xAF, 0xED, 0xFE, 0xFA, 0x8F, 0x09, 0x16, 0x19, 0xAE, + 0x50, 0x0B, 0x4C, 0xA4, 0x59, 0x9D, 0x01, 0x9D, 0xC8, 0xCA, 0x4B, 0xF1, 0x18, 0xD7, 0x0B, + 0x86, 0x76, 0xDF, 0xC7, 0x96, 0xA4, 0xF6, 0xD9, 0x86, 0xAD, 0xBA, 0x4C, 0x85, 0x74, 0xED, + 0x4A, 0xBA, 0xEA, 0x54, 0x65, 0x46, 0x62, 0x20, 0xE5, 0xE5, 0x3D, 0xC8, 0xFC, 0xB3, 0x95, + 0xD1, 0xE5, 0x9D, 0x27, 0x86, 0x73, 0xCB, 0xC4, 0xE3, 0xF4, 0x06, 0x58, 0xDF, 0x98, 0xAC, + 0x2F, 0xD1, 0x26, 0xA9, 0x49, 0x22, 0x87, 0x9E, 0x1A, 0x3B, 0xE9, 0x1C, 0x1A, 0xCC, 0x20, + 0x80, 0x3C, 0x35, 0xFA, 0x76, 0x4A, 0xBB, 0xAD, 0xAB, 0x07, 0xBD, 0xE8, 0x5F, 0xF4, 0xBD, + 0x9E, 0x0F, 0xB6, 0xF0, 0x6B, 0xAF, 0x5B, 0xF4, 0x2B, 0x8A, 0x2C, 0xBF, 0x6C, 0x2F, 0x62, + 0x60, 0x6B, 0xEC, 0xC3, 0x61, 0x55, 0x29, 0x21, 0xA1, 0x2D, 0x6C, 0x82, 0x36, 0xFD, 0xE8, + 0x4D, 0xB4, 0xBD, 0xDA, 0xC7, 0x7E, 0x88, 0x72, 0x47, 0x8C, 0xFF, 0xC4, 0xE1, 0x48, 0xC1, + 0xC7, 0xAC, 0xFE, 0xDF, 0x6B, 0x17, 0xD9, 0x87, 0x31, 0xC2, 0xDE, 0x36, 0xF3, 0xCB, 0xEF, + 0x1F, 0x6F, 0x78, 0x1A, 0x94, 0x0E, 0x08, 0x74, 0xD5, 0xB7, 0x45, 0x35, 0xBB, 0xE0, 0x66, + 0xB5, 0x30, 0x64, 0xD4, 0x3B, 0x13, 0x92, 0x65, 0x70, 0xA9, 0xE1, 0xC4, 0xE6, 0xDA, 0x20, + 0x6C, 0x8B, 0xD2, 0x52, 0xCA, 0xF2, 0xB6, 0x2E, 0x7D, 0x22, 0x3F, 0x7A, 0xC1, 0x29, 0x39, + 0x13, 0x7F, 0x33, 0x0B, 0xE5, 0x93, 0x74, 0xD7, 0x29, 0x5A, 0x6C, 0x2D, 0xFF, 0x92, 0xE0, + 0x7C, 0x72, 0x75, 0x10, 0xE4, 0x8D, 0x97, 0x05, 0x93, 0xE4, 0x72, 0x29, 0xFC, 0x8B, 0xC3, + 0xBD, 0x5B, 0x8E, 0xA7, 0x80, 0xDA, 0xCF, 0xF4, 0xD2, 0x30, 0x63, 0xDF, 0x65, 0xFE, 0xDA, + 0x5F, 0x8F, 0x65, 0xB1, 0x7A, 0x33, 0x3E, 0x53, 0x2A, 0xCA, 0xD7, 0x91, 0x67, 0x80, 0xC7, + 0x4D, 0x6A, 0x70, 0xD3, 0x8B, 0x36, 0x7F, 0x3F, 0x6F, 0x4E, 0x94, 0x7B, 0x85, 0xFC, 0x15, + 0x23, 0x5B, 0xBE, 0x46, 0xB2, 0x64, 0x95, 0xD2, 0x78, 0x00, 0x98, 0xDB, 0x85, 0x3A, 0x93, + 0x13, 0x77, 0xCF, 0xBE, 0xDE, 0xA6, 0x20, 0xF2, 0x35, 0x5C, 0xA2, 0x1E, 0x81, 0xCE, 0x9E, + 0x00, 0x78, 0xB0, 0xDD, 0x6C, 0xB7, 0x0F, 0x23, 0xED, 0x55, 0x86, 0x82, 0xBE, 0x3B, 0x3D, + 0x59, 0x4E, 0xEF, 0xE8, 0x53, 0x44, 0xE1, 0xF2, 0x75, 0x42, 0x8B, 0x31, 0x6C, 0xC0, 0x88, + 0x99, 0x59, 0x39, 0x29, 0x8F, 0x2A, 0x2D, 0x15, 0xAC, 0x9B, 0x67, 0x6A, 0xC3, 0xE9, 0xCB, + 0x92, 0xF2, 0xA6, 0x4D, 0xEC, 0x77, 0x32, 0xA9, 0x1F, 0xC7, 0x61, 0xAA, 0x1B, 0x12, 0x6E, + 0xA5, 0x75, 0xE3, 0x95, 0x31, 0x77, 0xDA, 0x6E, 0x1C, 0xD7, 0x8F, 0xAE, 0xA8, 0x24, 0x66, + 0x53, 0x30, 0xA8, 0x1D, 0x9E, 0x24, 0x57, 0x2B, 0x98, 0x60, 0xBF, 0x0A, 0xBA, 0x4D, 0xF8, + 0xBD, 0x5D, 0x4E, 0x3E, 0x2C, 0x72, 0xBB, 0xFB, 0xB2, 0xA9, 0x85, 0xC7, 0xAE, 0x2F, 0x07, + 0x79, 0x51, 0xFE, 0x84, 0x01, 0xE1, 0xD1, 0x56, 0xEC, 0xAD, 0xA1, 0xE3, 0x53, 0x81, 0x7B, + 0x20, 0xF4, 0x1E, 0x0B, 0x24, 0x60, 0xA0, 0xCA, 0xAF, 0x2B, 0x36, 0xD6, 0xA7, 0xF1, 0xB3, + 0x51, 0x25, 0xD7, 0x97, 0xDC, 0xC7, 0x14, 0x42, 0x10, 0x27, 0xD1, 0x41, 0x71, 0x76, 0x5A, + 0x64, 0x60, 0x71, 0xED, 0x95, 0x2B, 0x6A, 0x52, 0x94, 0xEE, 0xCF, 0x6A, 0x3A, 0x71, 0xC1, + 0x04, 0xC8, 0x43, 0xA4, 0xA8, 0xB3, 0xEF, 0xCC, 0x27, 0x46, 0x7B, 0x20, 0xCB, 0x0A, 0x94, + 0xAB, 0xF5, 0x80, 0x22, 0x29, 0xEA, 0x4D, 0x83, 0x12, 0x78, 0x3E, 0x78, 0x79, 0x1A, 0x50, + 0xB3, 0xC0, 0xA8, 0x8F, 0xE6, 0x49, 0x71, 0x98, 0xCD, 0x4B, 0xF4, 0x70, 0xDA, 0xC4, 0x6F, + 0x34, 0xE5, 0x00, 0x19, 0xFD, 0xEE, 0x20, 0x40, 0xCF, 0xE9, 0x91, 0x24, 0xB3, 0x12, 0xB1, + 0x12, 0x2B, 0x83, 0xE5, 0x1D, 0x87, 0x88, 0x77, 0xCE, 0xC0, 0x85, 0x5F, 0x11, 0x58, 0xC4, + 0x45, 0xCF, 0xDC, 0x22, 0x53, 0xF4, 0x38, 0x9D, 0x5E, 0x3A, 0x8B, 0xA1, 0x66, 0x9A, 0xBB, + 0x59, 0x76, 0xA4, 0x61, 0x7E, 0x85, 0xF5, 0x43, 0xDA, 0x9F, 0x5E, 0x30, 0xB1, 0x0C, 0xA7, + 0x48, 0x1C, 0x81, 0x85, 0x39, 0x27, 0x82, 0xB4, 0x6F, 0xB0, 0xA0, 0xE5, 0xAB, 0x40, 0x8B, + 0x29, 0x45, 0xE3, 0xC7, 0x9A, 0x1C, 0xC4, 0x9F, 0xB7, 0xC2, 0x72, 0x54, 0xA9, 0xB5, 0x40, + 0xE7, 0x39, 0x7A, 0x5B, 0x65, 0x5B, 0xF7, 0xE4, 0xF8, 0x31, 0x84, 0xDB, 0x32, 0xA1, 0x28, + 0xAA, 0x2E, 0x00, 0xA6, 0x24, 0xD7, 0xDC, 0xD6, 0xB7, 0x7E, 0xFD, 0x15, 0x1F, 0x1E, 0x5C, + 0xBA, 0x88, 0x90, 0xAF, 0x91, 0x70, 0xFA, 0x06, 0xC5, 0x55, 0x71, 0x5D, 0xC1, 0x78, 0x7E, + 0x99, 0x5A, 0xD1, 0x92, 0x70, 0x97, 0x3A, 0xE9, 0x5B, 0x88, 0xDC, 0xAF, 0xDA, 0xF6, 0x2C, + 0x28, 0x84, 0x3D, 0x3F, 0x8B, 0x9C, 0x78, 0xDC, 0x8E, 0x37, 0xD9, 0x11, 0xDA, 0xB3, 0xF7, + 0xEE, 0x9D, 0x4C, 0x73, 0x89, 0xC6, 0x54, 0xBD, 0xCF, 0xC0, 0x50, 0x56, 0xB3, 0x60, 0x02, + 0x01, 0x40, 0xE5, 0x7E, 0x31, 0x47, 0x32, 0x58, 0xA4, 0x08, 0x1E, 0x2A, 0x70, 0x8F, 0x7C, + 0xAB, 0xA9, 0x0C, 0x35, 0x6D, 0x08, 0x47, 0x09, 0x8F, 0xC0, 0x76, 0x24, 0x84, 0x08, 0x6A, + 0xBA, 0x89, 0x8A, 0x60, 0xB0, 0x23, 0xD6, 0xA3, 0x06, 0x04, 0x02, 0x40, 0x62, 0x40, 0x74, + 0x87, 0x85, 0xD5, 0x1C, 0xAF, 0xF5, 0x2A, 0x0E, 0xF3, 0xDC, 0x2A, 0x45, 0xDA, 0xDF, 0x80, + 0xAC, 0x18, 0x50, 0x2D, 0x24, 0x42, 0x2A, 0x8C, 0xBB, 0x10, 0x19, 0x2B, 0x88, 0xF4, 0xE9, + 0x16, 0x02, 0x06, 0xB2, 0xED, 0x3E, 0x04, 0x11, 0x4F, 0x2A, 0x33, 0x9D, 0xF2, 0x69, 0xE2, + 0xC3, 0x6B, 0x86, 0x13, 0xCE, 0x37, 0x08, 0x74, 0x71, 0x70, 0x17, 0x55, 0x33, 0x0C, 0xB5, + 0x59, 0x57, 0x53, 0x66, 0xEE, 0x0F, 0xA2, 0xD3, 0xAF, 0xCD, 0xA3, 0x2E, 0xED, 0xE6, 0xDD, + 0x90, 0x63, 0x45, 0xDA, 0xAA, 0x04, 0x81, 0x21, 0x98, 0xE9, 0x6C, 0x42, 0x23, 0x9C, 0x24, + 0x2E, 0xDB, 0x90, 0x05, 0x97, 0x09, 0xF4, 0x97, 0xDA, 0x5B, 0x87, 0x70, 0x53, 0x84, 0xAE, + 0xF2, 0xAF, 0x22, 0xDC, 0x2E, 0xDF, 0xEF, 0x3C, 0x00, 0xD8, 0xC9, 0x15, 0x6D, 0x8B, 0x31, + 0x63, 0xFE, 0x7A, 0x77, 0x79, 0xE0, 0x4F, 0x04, 0x91, 0x1A, 0x8B, 0x93, 0x4F, 0xB3, 0x07, + 0x2E, 0xEE, 0x84, 0x44, 0x84, 0xFE, 0xDE, 0x5E, 0x2E, 0xE9, 0x6D, 0x33, 0x8E, 0xEF, 0xE2, + 0xDA, 0x98, 0x60, 0x67, 0xFF, 0xE0, 0x21, 0x8A, 0xDA, 0x7D, 0xE1, 0xD0, 0xE4, 0x2D, 0x82, + 0x3D, 0x6B, 0x03, 0x39, 0x18, 0x27, 0x88, 0x88, 0xBA, 0x06, 0x08, 0xAB, 0x8F, 0x7B, 0xE9, + 0x97, 0xBD, 0xF2, 0x63, 0x68, 0x9A, 0x36, 0xF5, 0x20, 0x4C, 0x80, 0x2A, 0xD8, 0x36, 0x36, + 0x37, 0x79, 0xB4, 0xB0, 0xD6, 0xCE, 0x50, 0x83, 0xDF, 0x0B, 0x98, 0xA2, 0xE2, 0xC7, 0x00, + 0x06, 0x2A, 0x4F, 0xA5, 0xE5, 0x7B, 0xC7, 0x3B, 0xD4, 0x53, 0x57, 0xE0, 0x1D, 0x90, 0xC7, + 0x95, 0x4B, 0xC6, 0x90, 0x4D, 0x1C, 0xE8, 0x16, 0x6A, 0x91, 0x68, 0xDA, 0x39, 0xA6, 0x0C, + 0x5C, 0xAE, 0x81, 0x19, 0xBB, 0x6B, 0x9A, 0xB0, 0x74, 0xFB, 0x2D, 0x0A, 0xEE, 0x38, 0x4F, + 0xC2, 0xC0, 0xE4, 0x80, 0x68, 0x11, 0xD6, 0x00, 0x2B, 0x4E, 0x24, 0x01, 0xE7, 0x43, 0x0B, + 0x50, 0xCB, 0x0E, 0x80, 0x75, 0xF3, 0x3D, 0x53, 0x86, 0xAE, 0xCD, 0xE2, 0x56, 0xE1, 0x69, + 0xD9, 0x5E, 0x2F, 0x9C, 0x65, 0x56, 0xC0, 0x8B, 0xA0, 0x42, 0xE6, 0x8A, 0x53, 0xCE, 0x8A, + 0xCA, 0x9C, 0xC0, 0x28, 0x18, 0xF7, 0x38, 0x2F, 0x15, 0x0D, 0xC0, 0x4D, 0xE0, 0x01, 0x9B, + 0x19, 0xC7, 0xA3, 0xAB, 0x0D, 0x72, 0xD6, 0xED, 0x01, 0x3D, 0x7A, 0x11, 0x5D, 0x74, 0xB2, + 0x79, 0xF7, 0x1F, 0xD6, 0xEF, 0xFE, 0xF3, 0x40, 0x49, 0x87, 0x7E, 0x0B, 0x11, 0xE0, 0x65, + 0x9B, 0xE9, 0x38, 0xA5, 0xDE, 0x68, 0x4E, 0xAF, 0x23, 0x51, 0x30, 0x95, 0xEB, 0x4A, 0x1B, + 0xDF, 0x53, 0x6C, 0x3C, 0x01, 0xA4, 0x65, 0x5C, 0x4B, 0x4A, 0x06, 0x73, 0x21, 0x4C, 0xEF, + 0x29, 0xA4, 0x81, 0xD0, 0x6A, 0x02, 0xCC, 0x9A, 0x5B, 0xFC, 0x7B, 0x8D, 0x84, 0x6C, 0x33, + 0x48, 0x4C, 0xD6, 0x7B, 0x1D, 0xE9, 0x8F, 0x60, 0xB6, 0x99, 0x18, 0xF1, 0x77, 0xB6, 0x45, + 0x58, 0xCA, 0x56, 0x7A, 0x62, 0x37, 0xD3, 0x5F, 0xF0, 0x17, 0x71, 0xA4, 0x23, 0x20, 0xCE, + 0x02, 0xBB, 0x98, 0xF3, 0xE4, 0xAD, 0x4A, 0xC7, 0xDB, 0x75, 0x61, 0x1B, 0xD9, 0x96, 0x1E, + 0xAC, 0x66, 0x2A, 0x38, 0xB1, 0xF7, 0x85, 0x97, 0x0C, 0x99, 0xF3, 0xDD, 0x10, 0x5F, 0xF5, + 0x86, 0xF6, 0x13, 0x01, 0xC4, 0x8D, 0x66, 0x70, 0x8C, 0xBF, 0x7D, 0x53, 0xA7, 0x33, 0xE3, + 0x57, 0xB6, 0xC2, 0x56, 0xE8, 0xB7, 0x3F, 0x0E, 0x13, 0x05, 0xA0, 0xBC, 0x13, 0x79, 0x89, + 0xE5, 0x21, 0x10, 0x0C, 0x2E, 0xE6, 0x25, 0x9E, 0x60, 0x7F, 0xE1, 0x21, 0x98, 0xE8, 0xBC, + 0xB9, 0x88, 0xB0, 0x85, 0x46, 0x68, 0xE4, 0x0D, 0x7C, 0xAE, 0x6A, 0xDC, 0x3B, 0xA4, 0x0B, + 0xA1, 0x21, 0xB7, 0x31, 0x9D, 0x06, 0xD9, 0x88, 0xA0, 0x73, 0xD0, 0x30, 0x97, 0xB9, 0xF5, + 0xC1, 0xC0, 0x72, 0x84, 0xB6, 0x47, 0x3A, 0xE5, 0x7B, 0xF1, 0x54, 0x81, 0x1B, 0x77, 0xBA, + 0xCE, 0xB0, 0x41, 0x2B, 0x8A, 0x69, 0x83, 0xBD, 0xD0, 0xCC, 0xD9, 0xE3, 0xBF, 0x01, 0x4E, + 0x52, 0x00, 0x09, 0xCB, 0x26, 0xD5, 0x78, 0x0E, 0xAB, 0xEF, 0x1B, 0xBA, 0xFA, 0x5E, 0x25, + 0xD4, 0x10, 0x98, 0xA5, 0x4C, 0x47, 0xFC, 0xE8, 0xB6, 0x8D, 0x39, 0x52, 0x91, 0xD5, 0x42, + 0x84, 0xD3, 0x3A, 0xA5, 0x0B, 0x96, 0x64, 0xD1, 0x51, 0x0B, 0x46, 0x7C, 0x8A, 0x53, 0x93, + 0x61, 0xCA, 0x9A, 0x44, 0x48, 0xBC, 0x01, 0xFC, 0xB4, 0xC4, 0xE3, 0xEF, 0x47, 0x5E, 0x8A, + 0xFB, 0x46, 0xA4, 0x94, 0xAE, 0x13, 0xEE, 0x9E, 0xA8, 0xA1, 0x26, 0x68, 0x25, 0xFB, 0xA7, + 0xF3, 0x2B, 0x97, 0x12, 0xFD, 0xE2, 0x52, 0x69, 0x8A, 0x68, 0x35, 0x9B, 0x50, 0x14, 0x1D, + 0x90, 0xF5, 0xC4, 0xA0, 0x62, 0x83, 0xDD, 0xB5, 0x4A, 0xD7, 0xE1, 0x41, 0x2A, 0xC5, 0xEB, + 0xB1, 0x25, 0x01, 0xF7, 0xA8, 0x2B, 0x2A, 0x7F, 0x27, 0xB2, 0xDB, 0xAB, 0x62, 0x6C, 0x3D, + 0xB4, 0x07, 0x45, 0x23, 0xB3, 0x21, 0x1D, 0x31, 0x82, 0xEA, 0x26, 0x13, 0x97, 0xA6, 0xF7, + 0xB1, 0x87, 0xCF, 0x2B, 0x8A, 0x35, 0x6D, 0xED, 0x10, 0x81, 0x2F, 0x1D, 0x30, 0x51, 0x69, + 0xAE, 0xDF, 0x79, 0xB5, 0xFF, 0x1C, 0xF7, 0xC2, 0xD6, 0xE8, 0x6E, 0xE1, 0x1F, 0x28, 0xE9, + 0x6A, 0xA6, 0x3B, 0x5A, 0x03, 0xF5, 0x9F, 0xC9, 0x60, 0xAC, 0x7D, 0x05, 0x72, 0xE9, 0x1D, + 0xDA, 0x61, 0x90, 0x5C, 0x07, 0x11, 0xA9, 0xB2, 0x63, 0x44, 0xA2, 0xA1, 0x0A, 0xA2, 0x04, + 0x1F, 0x2B, 0x13, 0xCB, 0x1A, 0x9A, 0x9A, 0x27, 0x77, 0x4B, 0x6D, 0x0D, 0xED, 0xDC, 0x9D, + 0x81, 0xEA, 0x1B, 0x14, 0x2A, 0xD7, 0xB7, 0x2B, 0xE4, 0x79, 0x91, 0xF2, 0xC9, 0x26, 0x1D, + 0x67, 0x08, 0x15, 0x6E, 0x38, 0xD0, 0x0B, 0x07, 0x40, 0x20, 0x76, 0x6E, 0xB0, 0xC4, 0x94, + 0x39, 0x2D, 0x65, 0xB8, 0x2C, 0xA6, 0x5F, 0x7C, 0x33, 0x52, 0xF9, 0xBB, 0x78, 0x32, 0x5E, + 0xCB, 0x6D, 0xF5, 0x96, 0xC8, 0xAE, 0x57, 0x82, 0x6B, 0x08, 0xCC, 0xD6, 0xF1, 0xD5, 0x29, + 0xD2, 0xE2, 0x59, 0x25, 0xC1, 0xAC, 0x97, 0x24, 0x25, 0xBE, 0xDE, 0xB8, 0x8A, 0x5D, 0x0E, + 0x31, 0x38, 0xDD, 0xC4, 0x34, 0xDA, 0x34, 0x62, 0xEB, 0xDF, 0x6B, 0x12, 0x39, 0xA2, 0x1F, + 0x14, 0x1E, 0xC6, 0x2C, 0xBE, 0x4B, 0xB9, 0x93, 0xBA, 0x25, 0x3B, 0x55, 0xA7, 0x6D, 0x30, + 0xFA, 0xC1, 0x9C, 0x2C, 0x13, 0x84, 0xEF, 0x6B, 0x97, 0x46, 0xC0, 0x77, 0x87, 0xAA, 0x1F, + 0xE9, 0x13, 0xA1, 0x34, 0x83, 0x90, 0xBD, 0x8C, 0x1F, 0x38, 0x6A, 0x08, 0xC7, 0x7C, 0xF7, + 0x10, 0x6C, 0x92, 0x7C, 0xE2, 0x4D, 0xFF, 0xC9, 0xD6, 0xEE, 0x1B, 0x32, 0x35, 0x4D, 0x95, + 0xED, 0x29, 0x23, 0x48, 0x25, 0x31, 0xDE, 0x6B, 0x39, 0x0B, 0xF0, 0xF5, 0xEB, 0x80, 0x27, + 0x6E, 0x90, 0xE7, 0xED, 0x11, 0x13, 0x1C, 0x84, 0x8B, 0xBA, 0xBE, 0xC4, 0xD3, 0x17, 0x23, + 0x62, 0x69, 0xA0, 0xA3, 0xA7, 0xCB, 0xE0, 0xF1, 0x27, 0x2F, 0x93, 0x94, 0x9C, 0xA6, 0xD2, + 0x3E, 0xA2, 0xA7, 0xEE, 0x3F, 0x69, 0x77, 0x91, 0xAA, 0xB7, 0x15, 0x33, 0x42, 0x30, 0x66, + 0xD4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x0E, 0x18, 0x1F, 0x23, 0x29, 0x2C, 0x2F, + ]; - if MLDSA87::verify(&mldsa87_pk, msg, None, &sig).is_ok() { - eprintln!("Verification succeeded!"); - } else { - panic!("Verification failed! -- figure that out"); - } + measure(|| { + if MLDSA87::verify(&mldsa87_pk, msg, None, &sig).is_ok() { + eprintln!("Verification succeeded!"); + } else { + panic!("Verification failed! -- figure that out"); + } + }); } fn main() { @@ -2171,15 +4610,21 @@ fn main() { // bench_mldsa87_keygen() // bench_mldsa87_lowmemory_keygen() // bench_mldsa44_sign() + // bench_mldsa44_sign_expanded_sk() // bench_mldsa44_lowmemory_sign() // bench_mldsa65_sign() + // bench_mldsa65_sign_expanded_sk() // bench_mldsa65_lowmemory_sign() // bench_mldsa87_sign() + // bench_mldsa87_sign_expanded_sk() bench_mldsa87_lowmemory_sign() // bench_mldsa44_verify() + // bench_mldsa44_verify_expanded_pk() // bench_mldsa44_lowmemory_verify() // bench_mldsa65_verify() + // bench_mldsa65_verify_expanded_pk() // bench_mldsa65_lowmemory_verify() // bench_mldsa87_verify() + // bench_mldsa87_verify_expanded_pk() // 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 8a81b71d..f9858530 100644 --- a/mem_usage_benches/bench_mlkem_mem_usage.rs +++ b/mem_usage_benches/bench_mlkem_mem_usage.rs @@ -25,12 +25,24 @@ #![allow(dead_code)] #![allow(unused_imports)] +/// Runs the operation under test in its own stack frame, with the key already loaded by the +/// caller. +/// +/// `#[inline(never)]` matters here too: the bench functions inline the whole operation, so +/// without this boundary the operation's frame would be allocated on entry to the bench and the +/// key-load helper's frame would stack on top of it, reporting their sum instead of their max. +/// The closure returns nothing, so no result is copied back across the boundary. +#[inline(never)] +fn measure(f: impl FnOnce()) { + f() +} + use bouncycastle::core::key_material::{KeyMaterial512, KeyType}; -use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPublicKey}; +use bouncycastle::core::traits::{KEMDecapsulator, KEMEncapsulator, KEMPrivateKey, 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, + 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, }; /// This exists so I can use /usr/bin/time to measure the base memory footprint of the cargo bench harness @@ -240,22 +252,13 @@ fn bench_mlkem1024_lowmemory_keygen() { let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); println!("{:x?}", pk.encode()); } -fn bench_mlkem512_encaps() { - use bouncycastle::mlkem::{MLKEM512, MLKEM512PublicKey, MLKEMTrait}; - - eprintln!("MLKEM512/Encaps"); - - /* One-time setup of the KAT -- commented out so that keygen is not captured 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], - // KeyType::Seed, - // ).unwrap(); - - // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - // use bouncycastle_hex as hex; - // eprintln!("pk:\n{}", &hex::encode(pk.encode())); +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem512_pk() -> bouncycastle::mlkem::MLKEM512PublicKey { + use bouncycastle::mlkem::MLKEM512PublicKey; - let pk_bytes: [u8; MLKEM512_PK_LEN] = [ + MLKEM512PublicKey::from_bytes(&[ 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, @@ -310,17 +313,43 @@ fn bench_mlkem512_encaps() { 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); - print!("{:x?}", ct); + ]) + .unwrap() } -fn bench_mlkem512_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEM512PublicKey, MLKEMTrait}; +fn bench_mlkem512_encaps() { + use bouncycastle::mlkem::{MLKEM512, MLKEMTrait}; - eprintln!("MLKEM512_lowmemory/Encaps"); + eprintln!("MLKEM512/Encaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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], + // KeyType::Seed, + // ).unwrap(); + + // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("pk:\n{}", &hex::encode(pk.encode())); + + let pk = load_mlkem512_pk(); + measure(|| { + let (ss, ct) = MLKEM512::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Same as bench_mlkem512_encaps(), but against a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM512PublicKeyExpanded, which is +/// what makes repeated encapsulations against the same key cheaper -- at the cost of the memory +/// the expanded key occupies for as long as it is held. Both the expansion and the encapsulation +/// are inside the measurement. +fn bench_mlkem512_encaps_expanded_pk() { + use bouncycastle::mlkem::{MLKEM512, MLKEM512PublicKeyExpanded, MLKEMTrait}; + + eprintln!("MLKEM512/Encaps_for_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( @@ -332,7 +361,22 @@ fn bench_mlkem512_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM512_PK_LEN] = [ + let pk = load_mlkem512_pk(); + measure(|| { + let pk_expanded = MLKEM512PublicKeyExpanded::from(&pk); + let (ss, ct) = MLKEM512::encaps_for_expanded_key(&pk_expanded).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem512_lowmemory_pk() -> bouncycastle::mlkem_lowmemory::MLKEM512PublicKey { + use bouncycastle::mlkem_lowmemory::MLKEM512PublicKey; + + MLKEM512PublicKey::from_bytes(&[ 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, @@ -387,29 +431,40 @@ fn bench_mlkem512_lowmemory_encaps() { 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); - print!("{:x?}", ct); + ]) + .unwrap() } -fn bench_mlkem768_encaps() { - use bouncycastle::mlkem::{MLKEM768, MLKEM768PublicKey, MLKEMTrait}; +fn bench_mlkem512_lowmemory_encaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEMTrait}; - eprintln!("MLKEM768/Encaps"); + eprintln!("MLKEM512_lowmemory/Encaps"); /* One-time setup of the KAT -- commented out so that keygen is not captured 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], // KeyType::Seed, // ).unwrap(); - // - // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + + // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM768_PK_LEN] = [ + let pk = load_mlkem512_lowmemory_pk(); + measure(|| { + let (ss, ct) = MLKEM512::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem768_pk() -> bouncycastle::mlkem::MLKEM768PublicKey { + use bouncycastle::mlkem::MLKEM768PublicKey; + + MLKEM768PublicKey::from_bytes(&[ 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, @@ -489,17 +544,43 @@ fn bench_mlkem768_encaps() { 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); - print!("{:x?}", ct); + ]) + .unwrap() } -fn bench_mlkem768_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEM768PublicKey, MLKEMTrait}; +fn bench_mlkem768_encaps() { + use bouncycastle::mlkem::{MLKEM768, MLKEMTrait}; - eprintln!("MLKEM768_lowmemory/Encaps"); + eprintln!("MLKEM768/Encaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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], + // KeyType::Seed, + // ).unwrap(); + // + // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("pk:\n{}", &hex::encode(pk.encode())); + + let pk = load_mlkem768_pk(); + measure(|| { + let (ss, ct) = MLKEM768::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Same as bench_mlkem768_encaps(), but against a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM768PublicKeyExpanded, which is +/// what makes repeated encapsulations against the same key cheaper -- at the cost of the memory +/// the expanded key occupies for as long as it is held. Both the expansion and the encapsulation +/// are inside the measurement. +fn bench_mlkem768_encaps_expanded_pk() { + use bouncycastle::mlkem::{MLKEM768, MLKEM768PublicKeyExpanded, MLKEMTrait}; + + eprintln!("MLKEM768/Encaps_for_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( @@ -511,7 +592,22 @@ fn bench_mlkem768_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM768_PK_LEN] = [ + let pk = load_mlkem768_pk(); + measure(|| { + let pk_expanded = MLKEM768PublicKeyExpanded::from(&pk); + let (ss, ct) = MLKEM768::encaps_for_expanded_key(&pk_expanded).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem768_lowmemory_pk() -> bouncycastle::mlkem_lowmemory::MLKEM768PublicKey { + use bouncycastle::mlkem_lowmemory::MLKEM768PublicKey; + + MLKEM768PublicKey::from_bytes(&[ 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, @@ -591,17 +687,14 @@ fn bench_mlkem768_lowmemory_encaps() { 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); - print!("{:x?}", ct); + ]) + .unwrap() } -fn bench_mlkem1024_encaps() { - use bouncycastle::mlkem::{MLKEM1024, MLKEM1024PublicKey, MLKEMTrait}; +fn bench_mlkem768_lowmemory_encaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEMTrait}; - eprintln!("MLKEM1024/Encaps"); + eprintln!("MLKEM768_lowmemory/Encaps"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( @@ -609,11 +702,25 @@ fn bench_mlkem1024_encaps() { // KeyType::Seed, // ).unwrap(); // - // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM1024_PK_LEN] = [ + let pk = load_mlkem768_lowmemory_pk(); + measure(|| { + let (ss, ct) = MLKEM768::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem1024_pk() -> bouncycastle::mlkem::MLKEM1024PublicKey { + use bouncycastle::mlkem::MLKEM1024PublicKey; + + MLKEM1024PublicKey::from_bytes(&[ 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, @@ -719,17 +826,43 @@ fn bench_mlkem1024_encaps() { 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); - print!("{:x?}", ct); + ]) + .unwrap() } -fn bench_mlkem1024_lowmemory_encaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEM1024PublicKey, MLKEMTrait}; +fn bench_mlkem1024_encaps() { + use bouncycastle::mlkem::{MLKEM1024, MLKEMTrait}; - eprintln!("MLKEM1024_lowmemory/Encaps"); + eprintln!("MLKEM1024/Encaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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], + // KeyType::Seed, + // ).unwrap(); + // + // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("pk:\n{}", &hex::encode(pk.encode())); + + let pk = load_mlkem1024_pk(); + measure(|| { + let (ss, ct) = MLKEM1024::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Same as bench_mlkem1024_encaps(), but against a pre-expanded public key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM1024PublicKeyExpanded, which is +/// what makes repeated encapsulations against the same key cheaper -- at the cost of the memory +/// the expanded key occupies for as long as it is held. Both the expansion and the encapsulation +/// are inside the measurement. +fn bench_mlkem1024_encaps_expanded_pk() { + use bouncycastle::mlkem::{MLKEM1024, MLKEM1024PublicKeyExpanded, MLKEMTrait}; + + eprintln!("MLKEM1024/Encaps_for_expanded_key"); /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ // let seed = KeyMaterial512::from_bytes_as_type( @@ -741,7 +874,22 @@ fn bench_mlkem1024_lowmemory_encaps() { // use bouncycastle_hex as hex; // eprintln!("pk:\n{}", &hex::encode(pk.encode())); - let pk_bytes: [u8; MLKEM1024_PK_LEN] = [ + let pk = load_mlkem1024_pk(); + measure(|| { + let pk_expanded = MLKEM1024PublicKeyExpanded::from(&pk); + let (ss, ct) = MLKEM1024::encaps_for_expanded_key(&pk_expanded).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the public key for the matching encaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem1024_lowmemory_pk() -> bouncycastle::mlkem_lowmemory::MLKEM1024PublicKey { + use bouncycastle::mlkem_lowmemory::MLKEM1024PublicKey; + + MLKEM1024PublicKey::from_bytes(&[ 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, @@ -847,11 +995,157 @@ fn bench_mlkem1024_lowmemory_encaps() { 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, + ]) + .unwrap() +} + +fn bench_mlkem1024_lowmemory_encaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEMTrait}; + + eprintln!("MLKEM1024_lowmemory/Encaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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], + // KeyType::Seed, + // ).unwrap(); + // + // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + // use bouncycastle_hex as hex; + // eprintln!("pk:\n{}", &hex::encode(pk.encode())); + + let pk = load_mlkem1024_lowmemory_pk(); + measure(|| { + let (ss, ct) = MLKEM1024::encaps(&pk).unwrap(); + print!("{:x?}", ss); + print!("{:x?}", ct); + }); +} + +/// Loads the private key for the matching decaps bench. +/// +/// `#[inline(never)]` matters: the encoded key bytes and the temporaries of decoding then live in +/// this frame, which is popped before decapsulation runs, so the bench's peak is +/// max(key load, decaps) rather than their sum. Inlining this into the bench would leave them +/// on the stack underneath decaps and inflate the measurement by several kB. +#[inline(never)] +fn load_mlkem512_sk() -> bouncycastle::mlkem::MLKEM512PrivateKey { + use bouncycastle::mlkem::MLKEM512PrivateKey; + + // The FIPS 203 encoding of the private key, so that key generation stays outside the + // measurement. + let sk_bytes: [u8; MLKEM512_SK_LEN] = [ + 0x70, 0x55, 0x4F, 0xD4, 0x36, 0x34, 0x4F, 0x27, 0x85, 0xB1, 0xB3, 0xB1, 0xBA, 0xC1, 0x84, + 0xB6, 0x67, 0x90, 0x03, 0x33, 0x6C, 0x26, 0xF1, 0x5A, 0x7D, 0xE8, 0x78, 0xC4, 0x82, 0x5C, + 0x6B, 0xE0, 0x3F, 0x3C, 0x4A, 0x48, 0x0F, 0x75, 0xB7, 0x48, 0x6A, 0xAD, 0x31, 0xD3, 0xA0, + 0x05, 0x18, 0x62, 0x3F, 0xD2, 0x07, 0xAB, 0x52, 0x8D, 0xD6, 0x27, 0x21, 0x49, 0x58, 0x35, + 0xAE, 0x00, 0x62, 0xC3, 0x67, 0xB7, 0x4A, 0x71, 0xBA, 0xF1, 0x0A, 0xAD, 0x0E, 0x8A, 0x29, + 0x02, 0x07, 0x6B, 0xE3, 0x13, 0x48, 0xBE, 0xB1, 0x5C, 0xCC, 0x09, 0x57, 0xCD, 0xEB, 0xB4, + 0xAF, 0xF2, 0x26, 0x75, 0x6B, 0xBC, 0x60, 0x1B, 0x65, 0x68, 0xAB, 0x78, 0x4A, 0xCB, 0xAE, + 0xB3, 0x47, 0x02, 0xF0, 0xF8, 0x6A, 0x26, 0x20, 0x21, 0x18, 0xB2, 0x2B, 0x23, 0xF8, 0x35, + 0x58, 0x77, 0x6C, 0x79, 0xC1, 0x4D, 0xBA, 0x98, 0x33, 0x79, 0xC8, 0x03, 0xE0, 0xDC, 0xC3, + 0x16, 0x0A, 0x11, 0x75, 0x70, 0x30, 0xE6, 0x9C, 0x69, 0x19, 0x79, 0x8D, 0x81, 0xEB, 0x69, + 0x8A, 0x9A, 0x44, 0x83, 0xA9, 0x9E, 0x5A, 0x5C, 0xB2, 0xC3, 0x1C, 0x9A, 0x66, 0x17, 0x99, + 0xF3, 0xCC, 0x89, 0xC7, 0x90, 0x70, 0x6E, 0xA0, 0x41, 0x62, 0x90, 0x45, 0xD4, 0x2A, 0x83, + 0xAE, 0xD8, 0x88, 0x60, 0xE3, 0x94, 0xC6, 0x91, 0x87, 0xE2, 0x10, 0x5D, 0x28, 0xCC, 0x14, + 0xEC, 0x39, 0x35, 0x92, 0xD6, 0x7D, 0xD0, 0x0A, 0xA4, 0x3F, 0xE8, 0xB4, 0xEA, 0xE4, 0x41, + 0x40, 0x02, 0x86, 0x6B, 0x5C, 0x71, 0x3C, 0x6A, 0x8D, 0x7D, 0x16, 0xCF, 0x78, 0xB8, 0x19, + 0xD6, 0xF1, 0x2E, 0x9E, 0x5A, 0x74, 0x23, 0x39, 0x08, 0xF0, 0xB1, 0x5E, 0x3C, 0x4B, 0xA8, + 0x32, 0x9C, 0x5C, 0xDD, 0xA5, 0x5C, 0x84, 0x92, 0x8E, 0x3A, 0xA8, 0x06, 0x3E, 0x5A, 0xA9, + 0x67, 0x64, 0x03, 0xF9, 0x17, 0x35, 0xB1, 0x10, 0x10, 0xC7, 0xF5, 0x93, 0x09, 0x13, 0x64, + 0xDC, 0x86, 0x44, 0x5B, 0xC8, 0x04, 0x84, 0x0A, 0x9A, 0x21, 0x72, 0x42, 0x12, 0x46, 0x9F, + 0x8A, 0x7B, 0x0C, 0xE0, 0xAC, 0x69, 0x8E, 0xB8, 0x6C, 0xAD, 0x39, 0xA7, 0xF4, 0x82, 0x4D, + 0x9A, 0x51, 0x63, 0xAA, 0xC2, 0x1E, 0xE6, 0x80, 0x8B, 0x05, 0x3C, 0x8A, 0x3F, 0xAC, 0xB0, + 0xB6, 0x74, 0x4B, 0x52, 0x62, 0xBB, 0xCB, 0x26, 0xA4, 0x3F, 0x66, 0x4C, 0x87, 0x32, 0xB6, + 0x4C, 0xFC, 0x7A, 0xCF, 0x09, 0x96, 0x05, 0xF4, 0x1C, 0x79, 0x60, 0x60, 0x97, 0x6A, 0xC4, + 0x33, 0x83, 0x3F, 0xE0, 0x03, 0x43, 0xFB, 0x18, 0x28, 0x30, 0x0A, 0x42, 0x47, 0x41, 0x11, + 0x6E, 0x4B, 0x45, 0xBB, 0x27, 0x6E, 0xA8, 0x11, 0x29, 0xA0, 0xDB, 0x4C, 0x6E, 0x60, 0xBC, + 0xE6, 0x11, 0x10, 0x1E, 0x8C, 0x62, 0x54, 0x74, 0x92, 0x5E, 0x02, 0x22, 0x67, 0x93, 0x08, + 0xA3, 0xE7, 0x70, 0x8D, 0x19, 0x72, 0xA7, 0xB4, 0x23, 0xEB, 0x23, 0x28, 0x51, 0xC3, 0x6D, + 0x2E, 0xD5, 0x3D, 0x3E, 0xD3, 0xBB, 0x75, 0x00, 0x63, 0x70, 0x61, 0xA5, 0xDC, 0x22, 0x92, + 0xFA, 0x1C, 0x46, 0x6C, 0x07, 0x35, 0x46, 0x83, 0x32, 0x8B, 0xEC, 0x2C, 0x1E, 0xD2, 0xCB, + 0x5C, 0x99, 0xB7, 0x8E, 0xCA, 0x09, 0x69, 0x03, 0x8C, 0xF7, 0xC3, 0x4D, 0xD1, 0x18, 0x72, + 0x4E, 0x31, 0xCA, 0xE0, 0x86, 0x20, 0x6B, 0x34, 0x30, 0x2B, 0x52, 0x0F, 0x5D, 0x17, 0x7A, + 0xDE, 0xD5, 0xB3, 0xCC, 0xE0, 0x2A, 0xCC, 0xE8, 0x08, 0xEA, 0x26, 0xBC, 0xC0, 0x72, 0x62, + 0x5F, 0xDB, 0x93, 0xF1, 0x74, 0x58, 0xA5, 0xFC, 0x1D, 0x4D, 0xA3, 0x94, 0x38, 0x0A, 0x1F, + 0x57, 0xE9, 0xCC, 0x66, 0x10, 0x94, 0x38, 0xA0, 0x75, 0xF0, 0xD2, 0x81, 0x3F, 0xCC, 0x4A, + 0x19, 0x9C, 0xC7, 0x6D, 0xB3, 0x82, 0x3F, 0x27, 0x0B, 0x00, 0x61, 0x59, 0x41, 0x92, 0x94, + 0x04, 0x11, 0xA3, 0x7F, 0xFB, 0xAF, 0xAE, 0x2C, 0x15, 0x01, 0x65, 0xCE, 0xC5, 0xC6, 0xBF, + 0x73, 0xC5, 0x95, 0xFB, 0x92, 0xCD, 0x15, 0x31, 0x26, 0x07, 0xDA, 0x07, 0x07, 0x78, 0x65, + 0x2B, 0xD9, 0x94, 0x4B, 0xC4, 0x8B, 0xC7, 0xD1, 0xA5, 0x34, 0x33, 0x8B, 0xAD, 0x0B, 0xAD, + 0x66, 0x56, 0xC5, 0xD5, 0x02, 0xCE, 0x78, 0x50, 0xAB, 0x15, 0x87, 0x24, 0x4E, 0xEB, 0x58, + 0xF4, 0x39, 0xAB, 0x5E, 0x08, 0x57, 0x4A, 0x71, 0x8C, 0x8A, 0xAC, 0x3D, 0x77, 0xC7, 0x98, + 0xBB, 0xA1, 0x54, 0x27, 0x33, 0xBE, 0x73, 0x44, 0x8F, 0x23, 0xFB, 0x70, 0xC0, 0xE5, 0x35, + 0x3A, 0x27, 0xC8, 0x83, 0x22, 0xC5, 0x21, 0x84, 0x93, 0xAF, 0xBB, 0x38, 0x08, 0x64, 0x34, + 0xD6, 0xD6, 0x0A, 0x56, 0xBA, 0x88, 0x7D, 0xD4, 0x98, 0xC3, 0xAB, 0x26, 0xA0, 0x87, 0x09, + 0x93, 0x81, 0x5A, 0xA6, 0xA4, 0x09, 0x75, 0xF2, 0x18, 0xAD, 0xCA, 0x15, 0x82, 0xD6, 0x4F, + 0xFC, 0x86, 0x52, 0xFB, 0xB3, 0xA9, 0xA6, 0xFB, 0xC3, 0x04, 0xF9, 0x19, 0x45, 0xFA, 0x4A, + 0xAE, 0xF2, 0x87, 0x8F, 0xD7, 0x15, 0xDF, 0x70, 0x11, 0x3D, 0x23, 0x79, 0xF4, 0x48, 0x86, + 0xF8, 0x12, 0xC8, 0x3F, 0xF2, 0xB7, 0x19, 0xA6, 0x9E, 0x1E, 0xC7, 0x4A, 0xE4, 0xB1, 0x5A, + 0xCC, 0xD3, 0xAE, 0xD5, 0xA5, 0x3C, 0xE7, 0x6A, 0x7B, 0x09, 0x82, 0x47, 0x16, 0x33, 0xB9, + 0x73, 0xCB, 0x40, 0xA1, 0xA0, 0x01, 0x5D, 0x0A, 0x42, 0x4F, 0xA1, 0x1A, 0x47, 0x9C, 0x02, + 0x30, 0x17, 0x43, 0x6D, 0x2A, 0x29, 0x00, 0xE9, 0x93, 0xEB, 0x5A, 0x0A, 0x06, 0x74, 0x00, + 0xC7, 0xF4, 0xAA, 0xDF, 0x20, 0x1F, 0xC4, 0xFA, 0x31, 0x26, 0x4A, 0x63, 0xBA, 0xE9, 0x5C, + 0xC8, 0xD6, 0x5C, 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, 0x82, 0xF1, 0x01, 0xFF, 0x64, 0x80, 0x63, + 0xB3, 0x76, 0xE2, 0xBB, 0x6C, 0x5B, 0x74, 0x55, 0xF6, 0x55, 0xA5, 0x0C, 0x2F, 0xEA, 0xDA, + 0xDE, 0x15, 0x0E, 0xFA, 0x0E, 0x0E, 0x6F, 0x36, 0x5A, 0xEA, 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, ]; - let pk = MLKEM1024PublicKey::from_bytes(&pk_bytes).unwrap(); - let (ss, ct) = MLKEM1024::encaps(&pk).unwrap(); - print!("{:x?}", ss); - print!("{:x?}", ct); + MLKEM512PrivateKey::from_bytes(&sk_bytes).unwrap() } fn bench_mlkem512_decaps() { @@ -859,23 +1153,23 @@ fn bench_mlkem512_decaps() { 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, - ], - KeyType::Seed, - ) - .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ - // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + // 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM512::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); let ct: [u8; MLKEM512_CT_LEN] = [ 0x5A, 0x68, 0x40, 0x8E, 0xEB, 0x0E, 0xE7, 0xD1, 0xF5, 0x11, 0x4F, 0x4B, 0xED, 0xAE, 0xB6, @@ -932,15 +1226,114 @@ fn bench_mlkem512_decaps() { 0x1B, 0xAD, 0x25, ]; - let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - let ss = MLKEM512::decaps(&sk, &ct).unwrap(); - print!("{:x?}", ss); + let sk = load_mlkem512_sk(); + measure(|| { + let ss = MLKEM512::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); } -fn bench_mlkem512_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEMTrait}; +/// Same as bench_mlkem512_decaps(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM512PrivateKeyExpanded, which is +/// what makes repeated decapsulations with the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. The expansion and the decapsulation are +/// inside the measurement; key generation is not (the plain key is decoded from bytes in a +/// separate frame, see [`load_mlkem512_sk`]). +fn bench_mlkem512_decaps_expanded_sk() { + use bouncycastle::mlkem::{MLKEM512, MLKEM512PrivateKeyExpanded, MLKEMTrait}; - eprintln!("MLKEM512_lowmemory/Decaps"); + eprintln!("MLKEM512/Decaps_with_expanded_key"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); + // let (_ss, ct) = MLKEM512::encaps_internal(&pk, None, [1u8; 32]); + // use bouncycastle_hex as hex; + // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + + 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 sk = load_mlkem512_sk(); + measure(|| { + let sk_expanded = MLKEM512PrivateKeyExpanded::from(&sk); + let ss = MLKEM512::decaps_with_expanded_key(&sk_expanded, &ct).unwrap(); + print!("{:x?}", ss); + }); +} + +/// Loads the private key for the matching low-memory decaps bench. See [`load_mlkem512_sk`] for +/// why this is `#[inline(never)]`. +/// +/// A low-memory private key is just the seed, so loading one derives (rho, sigma) from it and +/// nothing more; the public key that keygen would also compute stays outside the measurement. +#[inline(never)] +fn load_mlkem512_lowmemory_sk() -> bouncycastle::mlkem_lowmemory::MLKEM512PrivateKey { + use bouncycastle::mlkem_lowmemory::MLKEM512PrivateKey; let seed = KeyMaterial512::from_bytes_as_type( &[ @@ -954,7 +1347,16 @@ fn bench_mlkem512_lowmemory_decaps() { ) .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + MLKEM512PrivateKey::new(&seed).unwrap() +} + +fn bench_mlkem512_lowmemory_decaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM512, MLKEMTrait}; + + eprintln!("MLKEM512_lowmemory/Decaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench. + `seed` is the one in load_mlkem512_lowmemory_sk(). */ // let (pk, _sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM512::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; @@ -1015,9 +1417,184 @@ fn bench_mlkem512_lowmemory_decaps() { 0x1B, 0xAD, 0x25, ]; - let (_pk, sk) = MLKEM512::keygen_from_seed(&seed).unwrap(); - let ss = MLKEM512::decaps(&sk, &ct).unwrap(); - print!("{:x?}", ss); + let sk = load_mlkem512_lowmemory_sk(); + measure(|| { + let ss = MLKEM512::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); +} + +/// Loads the private key for the matching decaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem768_sk() -> bouncycastle::mlkem::MLKEM768PrivateKey { + use bouncycastle::mlkem::MLKEM768PrivateKey; + + // The FIPS 203 encoding of the private key, so that key generation stays outside the + // measurement. + let sk_bytes: [u8; MLKEM768_SK_LEN] = [ + 0x27, 0xD2, 0xA7, 0x7F, 0x33, 0x75, 0x6F, 0x61, 0x20, 0x8E, 0xF1, 0x13, 0xAB, 0xE8, 0x25, + 0x95, 0x87, 0x3D, 0x4A, 0xBC, 0x73, 0x0E, 0x5B, 0x5D, 0x67, 0x95, 0x29, 0xBF, 0x6A, 0x4C, + 0xEB, 0x63, 0x83, 0x42, 0x72, 0x31, 0xA8, 0x61, 0x2F, 0x41, 0x55, 0x05, 0x15, 0xAC, 0xBA, + 0x52, 0xE4, 0x8E, 0xAD, 0x8B, 0x94, 0x28, 0x33, 0xBB, 0xE6, 0x86, 0x5D, 0x13, 0xD1, 0x4A, + 0x79, 0xD2, 0xC5, 0xC3, 0xE0, 0x7F, 0x0A, 0x05, 0x6D, 0x8D, 0xE7, 0xAA, 0xDF, 0xCA, 0xBA, + 0x05, 0x8C, 0x49, 0x3C, 0x80, 0xB3, 0x7C, 0xAB, 0x8C, 0x56, 0x27, 0x53, 0xBB, 0x3B, 0xA6, + 0xB6, 0xEC, 0x82, 0x97, 0xF8, 0x85, 0xEA, 0xA7, 0x54, 0x0D, 0x53, 0x00, 0x15, 0xA8, 0x44, + 0x06, 0xE5, 0x5B, 0x13, 0x66, 0xB5, 0x77, 0xE2, 0x36, 0xCE, 0x58, 0xA2, 0x6D, 0x8A, 0x1E, + 0xB5, 0xA4, 0x4D, 0x54, 0x23, 0x23, 0xC2, 0x16, 0x7D, 0x9B, 0xF4, 0xA4, 0x7F, 0x98, 0x56, + 0x99, 0xCA, 0x05, 0xBA, 0xE4, 0x3B, 0x8D, 0xEC, 0x61, 0x7F, 0x02, 0x38, 0x0A, 0x38, 0x90, + 0xAF, 0xD4, 0xB8, 0xC7, 0xEC, 0x7E, 0xDE, 0x26, 0x55, 0x3A, 0x02, 0x5F, 0x3C, 0xE5, 0xBC, + 0x5D, 0x7A, 0x62, 0x13, 0x03, 0x04, 0x23, 0x5C, 0xB1, 0xAD, 0x48, 0x36, 0xB5, 0x66, 0xB5, + 0xB8, 0x63, 0xBD, 0x9B, 0xDB, 0x45, 0xA2, 0x84, 0x4A, 0x70, 0x47, 0xB6, 0xC8, 0xD3, 0x83, + 0xE4, 0x48, 0x52, 0x5E, 0x04, 0x0B, 0x4D, 0xC8, 0xA2, 0xB4, 0x8C, 0x6C, 0x37, 0xC9, 0x6D, + 0x62, 0xD4, 0x3F, 0x3F, 0xD8, 0x8E, 0x28, 0x81, 0xC4, 0x0A, 0x20, 0x5C, 0x9E, 0x24, 0x8F, + 0x65, 0x2B, 0x59, 0x27, 0x81, 0xA7, 0x79, 0xF8, 0x68, 0x80, 0xF2, 0xA1, 0x47, 0xB6, 0x78, + 0x63, 0xF3, 0x91, 0xCC, 0x1A, 0x5A, 0x90, 0x8C, 0x00, 0x95, 0xE0, 0x72, 0x12, 0x29, 0x1E, + 0x2E, 0xF8, 0xA3, 0x6E, 0xB9, 0xA9, 0xC0, 0xC6, 0x07, 0x32, 0x25, 0xB3, 0x47, 0x03, 0xA4, + 0xAF, 0x04, 0x93, 0x82, 0xC4, 0x75, 0x73, 0xDA, 0x68, 0xFD, 0xE9, 0x24, 0x5A, 0xD4, 0x44, + 0xE3, 0x1B, 0x1F, 0xBD, 0xB5, 0x21, 0xF1, 0xF6, 0x1F, 0x37, 0xBC, 0x0C, 0xEF, 0x29, 0x20, + 0x67, 0xE6, 0x70, 0xD2, 0x8A, 0x1F, 0xFD, 0x90, 0x4F, 0x6F, 0x11, 0x90, 0xA9, 0x96, 0x91, + 0x8A, 0x13, 0x03, 0x7A, 0x6C, 0xAB, 0xF3, 0xC3, 0x73, 0xBF, 0x82, 0x96, 0xCD, 0x37, 0xAB, + 0x33, 0xBA, 0x77, 0x46, 0x80, 0x9C, 0xC3, 0xF8, 0xAD, 0xE1, 0xB3, 0x63, 0x9B, 0xD5, 0x7B, + 0xFC, 0xC6, 0x96, 0x50, 0xAA, 0xAF, 0x1D, 0xE1, 0x98, 0xFC, 0x4C, 0x04, 0x63, 0x29, 0x9E, + 0x52, 0xC4, 0x61, 0x78, 0x0C, 0xC4, 0x28, 0xFC, 0x5D, 0x04, 0xA5, 0xC5, 0x18, 0x50, 0xCB, + 0xA6, 0xC2, 0xA5, 0x27, 0x43, 0x40, 0x67, 0x57, 0x93, 0xDD, 0xA0, 0x9B, 0xE4, 0x4C, 0x29, + 0xE6, 0x39, 0x5C, 0x65, 0xF8, 0x5D, 0x2A, 0x0A, 0x7C, 0x6D, 0xF4, 0x11, 0xE6, 0x91, 0x1B, + 0x1F, 0x2C, 0xB6, 0xC3, 0x51, 0xCD, 0x2E, 0x87, 0x5F, 0x51, 0xB6, 0x38, 0xBE, 0x77, 0x60, + 0x97, 0xE9, 0x3E, 0x2F, 0x2B, 0x2F, 0x83, 0xDA, 0x0B, 0xEE, 0xF4, 0xAA, 0x85, 0xBA, 0x9E, + 0x76, 0x3A, 0xB6, 0x45, 0x02, 0xA0, 0xCA, 0x52, 0x22, 0xE9, 0xEA, 0xB5, 0xB3, 0xB7, 0x08, + 0x8E, 0xD5, 0x20, 0x60, 0xE8, 0xC8, 0x26, 0x9B, 0x94, 0x3A, 0x71, 0xAB, 0x0A, 0xE1, 0xC5, + 0xB1, 0xB6, 0x87, 0xD2, 0xE0, 0x19, 0xCF, 0x80, 0x36, 0xBC, 0xF9, 0xBF, 0x6E, 0x7B, 0xAC, + 0x3A, 0xAA, 0x36, 0xE4, 0x16, 0x60, 0xFA, 0xA4, 0x54, 0x0F, 0x26, 0x48, 0xCD, 0x93, 0xA1, + 0x89, 0xEC, 0x5C, 0x2D, 0xEA, 0x70, 0xBA, 0xCA, 0xAA, 0x4F, 0xFC, 0x90, 0x6F, 0x90, 0x81, + 0x0E, 0xA1, 0xB6, 0x7B, 0xF2, 0x4F, 0x2C, 0x78, 0xCF, 0x6B, 0xA8, 0x81, 0xAA, 0xEA, 0x61, + 0xC0, 0x65, 0x2B, 0xFF, 0x95, 0xB1, 0xBA, 0xE4, 0x42, 0x6D, 0x17, 0x73, 0xB9, 0xCC, 0x2C, + 0xA8, 0x2C, 0x21, 0xE3, 0x8C, 0x63, 0x6E, 0x3B, 0x1C, 0x52, 0x32, 0x44, 0x98, 0x6B, 0x0B, + 0xE8, 0xA8, 0x3F, 0x5D, 0xD5, 0xCF, 0x2D, 0x54, 0x76, 0x2F, 0xB3, 0xC5, 0xEB, 0xF5, 0x9B, + 0x8E, 0x88, 0x53, 0x02, 0xB1, 0xCE, 0x47, 0x03, 0x3E, 0xDF, 0x76, 0x0F, 0x4E, 0x02, 0x9B, + 0xE4, 0x0B, 0x6D, 0x56, 0x6B, 0x19, 0xDD, 0x75, 0x8A, 0xCD, 0x5C, 0x74, 0x12, 0x87, 0x81, + 0x31, 0x24, 0x4F, 0x90, 0x17, 0x2C, 0x53, 0xF2, 0x66, 0x63, 0xC2, 0x1D, 0x90, 0x53, 0x01, + 0xD4, 0x8B, 0xAF, 0x91, 0xC9, 0x17, 0xCC, 0x77, 0x79, 0xE9, 0xD8, 0x80, 0x2C, 0xC1, 0x0D, + 0x89, 0xA3, 0x70, 0x50, 0x99, 0xA2, 0xAD, 0x3A, 0x3A, 0x88, 0x96, 0x74, 0x3C, 0x11, 0x44, + 0x69, 0x80, 0x93, 0xBE, 0x25, 0x7D, 0xAC, 0xB6, 0x6D, 0xC7, 0x85, 0x22, 0x8B, 0x91, 0x2C, + 0x8D, 0x96, 0x5D, 0x14, 0xAA, 0x28, 0x34, 0x2C, 0x3A, 0xC4, 0xA9, 0x3F, 0xEF, 0xA5, 0x32, + 0xB2, 0x09, 0x45, 0xDD, 0xC1, 0x02, 0x01, 0x39, 0xC1, 0x4D, 0x63, 0x8B, 0x90, 0x8C, 0x4D, + 0xDD, 0xE9, 0xA0, 0x64, 0x5B, 0x95, 0xB2, 0xE4, 0x41, 0x4D, 0x40, 0xBB, 0x79, 0xF0, 0x44, + 0x13, 0x83, 0x0F, 0x15, 0xA8, 0x73, 0xC2, 0x8B, 0xB7, 0x05, 0x9C, 0x27, 0x41, 0x00, 0x20, + 0x15, 0xF2, 0x04, 0x08, 0xF0, 0x58, 0xE7, 0x15, 0xB0, 0xBF, 0x99, 0x5B, 0x53, 0x80, 0xB7, + 0xDD, 0x32, 0x5A, 0x05, 0x6A, 0xB9, 0x7E, 0x65, 0x9A, 0x2B, 0xE0, 0xCD, 0xF6, 0xC3, 0x37, + 0x31, 0xC6, 0x83, 0xA6, 0x34, 0xB7, 0x71, 0xE8, 0xC9, 0x2A, 0x13, 0x9A, 0xEE, 0x4B, 0xB0, + 0xE4, 0x9C, 0x70, 0x77, 0x32, 0x1D, 0x42, 0xFC, 0x19, 0x9F, 0x7C, 0x1F, 0x29, 0x8C, 0xA6, + 0x25, 0xD2, 0x23, 0xA5, 0xC2, 0x63, 0xA0, 0x3C, 0xC4, 0x81, 0x59, 0xB7, 0x81, 0x26, 0x65, + 0xB7, 0x86, 0x37, 0xE4, 0xE1, 0x87, 0x20, 0xB2, 0xC2, 0x9A, 0x6B, 0x99, 0xF4, 0x27, 0x66, + 0xA4, 0xCB, 0xC4, 0xDC, 0x50, 0x8B, 0xA9, 0x4B, 0xA8, 0x3B, 0x89, 0xC3, 0xA5, 0xC7, 0x8F, + 0x8B, 0xB2, 0x6B, 0xBD, 0x9B, 0x79, 0xBE, 0xB8, 0xC8, 0x18, 0x24, 0x90, 0xF5, 0x79, 0x3E, + 0xE5, 0xB9, 0x60, 0x13, 0xB7, 0x4B, 0x7E, 0x16, 0x9E, 0x29, 0xD1, 0x62, 0xF1, 0x31, 0x54, + 0x64, 0xEA, 0x7D, 0x72, 0x43, 0x6D, 0x89, 0xB7, 0x55, 0x16, 0x11, 0x92, 0xC8, 0x1C, 0xC2, + 0xDD, 0x1C, 0x8B, 0x8B, 0xBA, 0x79, 0x5E, 0xF4, 0x26, 0xEE, 0x1C, 0xC0, 0x1C, 0x37, 0xAA, + 0xA3, 0x7B, 0x2C, 0xFF, 0x8B, 0x0A, 0x37, 0x8B, 0x47, 0xCB, 0xD0, 0xB4, 0xD4, 0x93, 0x98, + 0xCF, 0xC2, 0x71, 0x29, 0x59, 0x69, 0x9F, 0xA0, 0xBD, 0x8C, 0xD8, 0x46, 0x66, 0xAC, 0xC6, + 0x1F, 0x54, 0x1B, 0x84, 0xFA, 0x96, 0xB9, 0xC8, 0x54, 0xE4, 0xE7, 0x5E, 0x91, 0x44, 0xAD, + 0xDB, 0x44, 0xB8, 0x56, 0x6A, 0x57, 0xDF, 0xBB, 0x54, 0x5C, 0xE4, 0x23, 0xC0, 0x33, 0x46, + 0xF2, 0xB2, 0xC1, 0xA9, 0x17, 0x80, 0xD1, 0x52, 0xA8, 0xDE, 0x1A, 0x4D, 0x4C, 0x9C, 0xAC, + 0xDE, 0x73, 0x92, 0xC9, 0x96, 0x88, 0x8C, 0xC2, 0x39, 0x9C, 0x02, 0xC3, 0x8B, 0x33, 0x53, + 0xAD, 0xF8, 0xAC, 0xAB, 0x28, 0x39, 0x24, 0xDA, 0x00, 0xA0, 0x5B, 0x76, 0xE7, 0x38, 0xC7, + 0x2C, 0x93, 0x0D, 0x6C, 0xBA, 0x09, 0xAE, 0x16, 0x89, 0x90, 0xFA, 0xA1, 0xFE, 0xF2, 0x22, + 0x6E, 0x78, 0x08, 0x61, 0xD4, 0x16, 0xEF, 0xF4, 0x02, 0xF4, 0xF7, 0x59, 0xFC, 0x64, 0x8A, + 0xB1, 0xF9, 0x71, 0x00, 0x10, 0x90, 0x87, 0xF9, 0x6E, 0x4B, 0x14, 0x8D, 0x2C, 0xB3, 0x1E, + 0x48, 0x05, 0x31, 0x4E, 0xA0, 0xCD, 0x95, 0xFB, 0x02, 0x3E, 0xAC, 0x0D, 0x98, 0x94, 0x74, + 0xBA, 0x42, 0x01, 0xD7, 0xB4, 0x1D, 0x26, 0xF5, 0x39, 0x4B, 0x21, 0x7E, 0xEA, 0x5B, 0x34, + 0xB7, 0x1A, 0x8B, 0x37, 0x93, 0x1C, 0x0E, 0x59, 0x42, 0x71, 0xE0, 0xB7, 0xC7, 0x33, 0x25, + 0x72, 0x40, 0x23, 0x3E, 0x7B, 0xA7, 0x35, 0x60, 0x3E, 0x42, 0x5A, 0x87, 0xDE, 0xE7, 0x70, + 0x79, 0xE3, 0x7C, 0xB2, 0x8A, 0x21, 0x76, 0x45, 0x94, 0xCE, 0x53, 0x50, 0xD8, 0xDA, 0x2B, + 0x62, 0xA0, 0x71, 0x74, 0x94, 0x30, 0x32, 0xEC, 0x89, 0xC9, 0x88, 0x09, 0xC7, 0x3B, 0x64, + 0x23, 0xD3, 0x0C, 0x1D, 0x28, 0x3A, 0x76, 0x6A, 0x64, 0xD8, 0x97, 0x03, 0xC3, 0xD6, 0x29, + 0xB4, 0x97, 0x82, 0x8D, 0x48, 0x32, 0x0C, 0x34, 0x62, 0x10, 0x79, 0x7A, 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, 0xA2, 0x4E, 0x16, 0xD8, + 0xF8, 0xF9, 0x38, 0x3A, 0x95, 0xB7, 0x70, 0x50, 0xF4, 0xD9, 0xFD, 0x2F, 0x57, 0x33, 0xEE, + 0xC1, 0xD6, 0x3E, 0xF3, 0xC2, 0x3E, 0xBF, 0x99, 0x18, 0x17, 0x36, 0x69, 0xA7, 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, + ]; + MLKEM768PrivateKey::from_bytes(&sk_bytes).unwrap() } fn bench_mlkem768_decaps() { @@ -1025,23 +1602,23 @@ fn bench_mlkem768_decaps() { 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, - ], - KeyType::Seed, - ) - .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ - // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + // 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM768::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); let ct: [u8; MLKEM768_CT_LEN] = [ 0xA8, 0xF1, 0x73, 0xF0, 0xF4, 0x4B, 0xB1, 0xC7, 0x68, 0x12, 0x9E, 0xF4, 0x26, 0xEA, 0x35, @@ -1119,15 +1696,135 @@ fn bench_mlkem768_decaps() { 0xBD, 0x63, 0xE1, 0xEE, 0xDE, 0xBD, 0x6D, 0x15, ]; - let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); - let ss = MLKEM768::decaps(&sk, &ct).unwrap(); - print!("{:x?}", ss); + let sk = load_mlkem768_sk(); + measure(|| { + let ss = MLKEM768::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); } -fn bench_mlkem768_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEMTrait}; +/// Same as bench_mlkem768_decaps(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM768PrivateKeyExpanded, which is +/// what makes repeated decapsulations with the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. The expansion and the decapsulation are +/// inside the measurement; key generation is not (the plain key is decoded from bytes in a +/// separate frame, see [`load_mlkem512_sk`]). +fn bench_mlkem768_decaps_expanded_sk() { + use bouncycastle::mlkem::{MLKEM768, MLKEM768PrivateKeyExpanded, MLKEMTrait}; - eprintln!("MLKEM768_lowmemory/Decaps"); + eprintln!("MLKEM768/Decaps_with_expanded_key"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); + // let (_ss, ct) = MLKEM768::encaps_internal(&pk, None, [1u8; 32]); + // use bouncycastle_hex as hex; + // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + + 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 sk = load_mlkem768_sk(); + measure(|| { + let sk_expanded = MLKEM768PrivateKeyExpanded::from(&sk); + let ss = MLKEM768::decaps_with_expanded_key(&sk_expanded, &ct).unwrap(); + print!("{:x?}", ss); + }); +} + +/// Loads the private key for the matching low-memory decaps bench. See [`load_mlkem768_sk`] for +/// why this is `#[inline(never)]`. +/// +/// A low-memory private key is just the seed, so loading one derives (rho, sigma) from it and +/// nothing more; the public key that keygen would also compute stays outside the measurement. +#[inline(never)] +fn load_mlkem768_lowmemory_sk() -> bouncycastle::mlkem_lowmemory::MLKEM768PrivateKey { + use bouncycastle::mlkem_lowmemory::MLKEM768PrivateKey; let seed = KeyMaterial512::from_bytes_as_type( &[ @@ -1141,7 +1838,16 @@ fn bench_mlkem768_lowmemory_decaps() { ) .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + MLKEM768PrivateKey::new(&seed).unwrap() +} + +fn bench_mlkem768_lowmemory_decaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM768, MLKEMTrait}; + + eprintln!("MLKEM768_lowmemory/Decaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench. + `seed` is the one in load_mlkem768_lowmemory_sk(). */ // let (pk, _sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM768::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; @@ -1223,9 +1929,236 @@ fn bench_mlkem768_lowmemory_decaps() { 0xBD, 0x63, 0xE1, 0xEE, 0xDE, 0xBD, 0x6D, 0x15, ]; - let (_pk, sk) = MLKEM768::keygen_from_seed(&seed).unwrap(); - let ss = MLKEM768::decaps(&sk, &ct).unwrap(); - print!("{:x?}", ss); + let sk = load_mlkem768_lowmemory_sk(); + measure(|| { + let ss = MLKEM768::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); +} + +/// Loads the private key for the matching decaps bench. See [`load_mlkem512_sk`] for why this is +/// `#[inline(never)]`. +#[inline(never)] +fn load_mlkem1024_sk() -> bouncycastle::mlkem::MLKEM1024PrivateKey { + use bouncycastle::mlkem::MLKEM1024PrivateKey; + + // The FIPS 203 encoding of the private key, so that key generation stays outside the + // measurement. + let sk_bytes: [u8; MLKEM1024_SK_LEN] = [ + 0xF7, 0x7B, 0x7F, 0x6B, 0x15, 0xC7, 0x3F, 0xE2, 0xCC, 0x54, 0x6B, 0x67, 0xFB, 0x77, 0x4C, + 0xA1, 0x9B, 0x42, 0xCD, 0x46, 0x3E, 0xA9, 0xFB, 0xB9, 0x84, 0xCA, 0x47, 0x7A, 0x77, 0xB6, + 0xC7, 0x10, 0x87, 0xCB, 0xF0, 0x51, 0xAB, 0xE4, 0x73, 0x6A, 0x90, 0x72, 0xC6, 0xE8, 0x70, + 0xC8, 0x31, 0x1C, 0x55, 0x96, 0x3F, 0x50, 0x0A, 0x3C, 0x7B, 0x1B, 0x8F, 0x2A, 0x58, 0x55, + 0x8F, 0x49, 0xC6, 0x25, 0x27, 0xB6, 0xC5, 0x94, 0xB5, 0xE7, 0xAC, 0xB3, 0xBC, 0xF5, 0x97, + 0x27, 0x3A, 0x57, 0x43, 0x51, 0x7D, 0x15, 0x12, 0x08, 0xBD, 0x4A, 0xA6, 0x1E, 0x75, 0xBA, + 0x67, 0xB0, 0xBD, 0x59, 0x4A, 0x99, 0x49, 0x19, 0x62, 0x7A, 0xC0, 0xA8, 0x04, 0xD4, 0x89, + 0xE1, 0x71, 0x33, 0x6B, 0xC3, 0x39, 0xF4, 0x66, 0x67, 0x06, 0xE5, 0x13, 0x44, 0x12, 0xB3, + 0x66, 0x82, 0x3D, 0x50, 0x31, 0x8C, 0x8B, 0xF2, 0x61, 0xAB, 0x12, 0x0A, 0x28, 0xA0, 0x4F, + 0xEC, 0x01, 0xCC, 0x15, 0xF2, 0xB7, 0x19, 0x12, 0xCE, 0xE5, 0x4A, 0xA8, 0xEE, 0xD8, 0x54, + 0x69, 0x4B, 0x6B, 0xA8, 0x86, 0xB5, 0xEB, 0x76, 0x61, 0xE6, 0xD5, 0x6A, 0xAC, 0x21, 0x3C, + 0xC1, 0xD8, 0x14, 0xD5, 0x92, 0xB3, 0x95, 0x55, 0x4F, 0xAE, 0x74, 0x47, 0x6D, 0x34, 0x37, + 0x11, 0x63, 0x12, 0x9B, 0xF8, 0x64, 0x52, 0x72, 0x50, 0x60, 0x6C, 0xC2, 0x1A, 0x53, 0x74, + 0x6B, 0x20, 0x99, 0x70, 0x77, 0xBB, 0xA1, 0x55, 0x73, 0x3B, 0x28, 0xA4, 0xE7, 0xFA, 0x07, + 0x76, 0x39, 0x95, 0x24, 0x76, 0x3E, 0xB4, 0x81, 0xCE, 0xAA, 0x11, 0x36, 0x6C, 0x34, 0x74, + 0xA0, 0x46, 0x85, 0xF4, 0x0C, 0x3F, 0x08, 0xB0, 0x42, 0x4F, 0x40, 0xBF, 0xF9, 0x49, 0xA0, + 0xAC, 0x92, 0x70, 0x4C, 0x3B, 0xA0, 0xC6, 0xEB, 0x36, 0xF1, 0xF5, 0xB6, 0x21, 0xD8, 0xBF, + 0x2B, 0x63, 0x27, 0xBE, 0xB5, 0x7C, 0xD3, 0xFA, 0xCB, 0x94, 0x18, 0x6F, 0xE3, 0xFC, 0x9A, + 0xB0, 0xA1, 0x43, 0x4B, 0xB2, 0x91, 0xD2, 0xC9, 0xBB, 0x70, 0x72, 0x30, 0x57, 0xE2, 0x25, + 0x40, 0x59, 0x65, 0x6F, 0x56, 0x59, 0x19, 0xA3, 0x2C, 0xF7, 0x45, 0x79, 0xDE, 0x89, 0x68, + 0x1C, 0xD2, 0xC5, 0xA9, 0x35, 0xA5, 0x2B, 0x4A, 0xAA, 0x2D, 0x24, 0xCB, 0x5D, 0x5C, 0x9E, + 0x20, 0x72, 0x9E, 0xC5, 0x49, 0x2E, 0xC3, 0x69, 0x61, 0xEF, 0xB8, 0xA2, 0x8C, 0xBC, 0x00, + 0xAC, 0x30, 0x35, 0x23, 0x29, 0x5F, 0x3D, 0x80, 0x36, 0xAB, 0xC1, 0x60, 0x33, 0x07, 0xCE, + 0x70, 0xD7, 0x84, 0x8A, 0x35, 0x65, 0x7A, 0x56, 0x87, 0xDD, 0x58, 0x99, 0x27, 0xEA, 0x63, + 0x73, 0x16, 0x26, 0xAB, 0xB2, 0x6E, 0xC4, 0xE4, 0x31, 0xB8, 0xEB, 0x6B, 0x3B, 0x0B, 0xC1, + 0xE8, 0x25, 0x73, 0xEE, 0x73, 0xB1, 0xA0, 0x21, 0x18, 0x31, 0x83, 0x52, 0x81, 0x08, 0xAE, + 0x2E, 0xAC, 0xAD, 0xDB, 0x95, 0xB4, 0x64, 0xA0, 0xB9, 0x84, 0x69, 0xC3, 0x19, 0xCC, 0x27, + 0xBF, 0xA0, 0x1B, 0xC3, 0x10, 0x54, 0xA6, 0x8C, 0x05, 0x50, 0x2B, 0x16, 0x62, 0xB8, 0x79, + 0xFE, 0x98, 0xA1, 0x71, 0x1C, 0x34, 0x26, 0xF6, 0x43, 0x6C, 0xB0, 0x21, 0x4C, 0xEA, 0x37, + 0x9A, 0xC3, 0xA7, 0xE5, 0xFB, 0x60, 0x18, 0x4A, 0x37, 0xC1, 0xDA, 0x1E, 0xDA, 0x61, 0xC6, + 0xC3, 0x9C, 0x1D, 0xD4, 0xE8, 0x47, 0x84, 0x58, 0x11, 0xF2, 0xA3, 0x58, 0xA4, 0x37, 0x31, + 0x52, 0x85, 0x36, 0xD4, 0xA3, 0x29, 0x1B, 0x04, 0x15, 0x8C, 0x2C, 0x3D, 0xC6, 0x41, 0x62, + 0x48, 0x82, 0x67, 0x8B, 0xC7, 0x80, 0x5F, 0x58, 0xA9, 0xD9, 0x4C, 0x71, 0x04, 0x56, 0x78, + 0x46, 0xA2, 0x04, 0x4E, 0x65, 0xAE, 0xCE, 0x2A, 0x22, 0x53, 0x72, 0xB6, 0x02, 0x47, 0x99, + 0xA5, 0x47, 0x7D, 0x60, 0x23, 0x75, 0x04, 0xAA, 0x5C, 0x0A, 0xC5, 0x7B, 0xC7, 0x0A, 0x35, + 0x58, 0xC0, 0x8C, 0x4D, 0xE6, 0x87, 0xEF, 0x13, 0x02, 0xB4, 0xFC, 0xB5, 0x59, 0x44, 0x13, + 0xD2, 0x2C, 0xB9, 0x59, 0xBC, 0x31, 0xBE, 0x42, 0x34, 0x50, 0x40, 0x3C, 0x6B, 0xC5, 0x7D, + 0xC4, 0x11, 0xB3, 0xFE, 0xFA, 0xC1, 0x05, 0x2A, 0xC4, 0xBB, 0x16, 0x2C, 0x44, 0x54, 0x5A, + 0x4C, 0xA8, 0x08, 0x92, 0x65, 0x7F, 0xA1, 0x3A, 0x0B, 0x2C, 0x48, 0x2C, 0xED, 0x62, 0x9C, + 0xC4, 0x99, 0x9D, 0x96, 0x9C, 0x59, 0x3D, 0x4A, 0xAD, 0xF0, 0x73, 0xCC, 0x3E, 0x3A, 0x45, + 0x8E, 0x78, 0xA8, 0xAA, 0x03, 0x94, 0x08, 0xE6, 0x52, 0xBE, 0x93, 0xB2, 0x0C, 0x8B, 0x42, + 0xEC, 0x5B, 0x0E, 0x50, 0x23, 0x9D, 0xAC, 0x72, 0x60, 0x52, 0x85, 0x1A, 0x6D, 0x15, 0x31, + 0x2E, 0xC3, 0x9E, 0xD2, 0x08, 0xB7, 0x22, 0x09, 0xA5, 0x77, 0xC6, 0xB2, 0x77, 0x01, 0x12, + 0x89, 0x57, 0x49, 0xD5, 0x26, 0x0E, 0x7D, 0xD4, 0x46, 0xC0, 0xB0, 0x11, 0x8C, 0x10, 0x00, + 0xBE, 0x68, 0x01, 0xD2, 0x61, 0x1F, 0xCF, 0x00, 0x79, 0x2A, 0x9C, 0xC4, 0xF4, 0xB4, 0x99, + 0x22, 0xF9, 0xA2, 0xD4, 0xB9, 0xC8, 0xFA, 0x5A, 0x5D, 0x0D, 0x60, 0x50, 0x66, 0x31, 0xA7, + 0xE9, 0x71, 0xCE, 0xE8, 0x40, 0xB0, 0x8F, 0xA6, 0x3C, 0x13, 0x72, 0x9D, 0x7E, 0xA5, 0xAA, + 0xC7, 0x03, 0x52, 0xA9, 0x84, 0xCD, 0xB6, 0x69, 0x33, 0x1C, 0xBA, 0x75, 0x8F, 0xE8, 0x7E, + 0xC3, 0x93, 0x1B, 0x3E, 0x31, 0x61, 0xFC, 0xC7, 0x47, 0xAA, 0x74, 0x94, 0x24, 0x68, 0x9F, + 0xEA, 0xE1, 0x4B, 0xF7, 0xC9, 0xA2, 0xFF, 0xBA, 0x13, 0x02, 0xB2, 0x12, 0xB8, 0x03, 0x72, + 0xD8, 0xE9, 0x04, 0x9D, 0xB6, 0x9A, 0x3A, 0x12, 0x61, 0xD0, 0xA2, 0x85, 0x9A, 0x9B, 0x4D, + 0x57, 0x89, 0x9E, 0x0B, 0xA4, 0x16, 0x07, 0xA1, 0xB6, 0x7A, 0x7C, 0x0E, 0x12, 0x92, 0x36, + 0x89, 0xF8, 0xC6, 0x39, 0x53, 0x77, 0xD9, 0x70, 0xC7, 0x49, 0x0A, 0x41, 0x29, 0x61, 0x1A, + 0x1D, 0x05, 0xC3, 0xB7, 0x81, 0x3B, 0xED, 0x94, 0x54, 0x20, 0x72, 0x3F, 0x7F, 0x95, 0x25, + 0xA8, 0x77, 0x93, 0xFA, 0xFB, 0xBF, 0xCA, 0x98, 0x2E, 0x66, 0xBB, 0x80, 0x68, 0x1C, 0x83, + 0x24, 0x8A, 0x89, 0xDA, 0x08, 0x4C, 0x19, 0x88, 0x2F, 0x48, 0xF3, 0x1E, 0x7F, 0xC0, 0x90, + 0x93, 0xA4, 0x9E, 0x9F, 0xD0, 0x96, 0x91, 0xB0, 0x21, 0xED, 0xF4, 0x63, 0xAF, 0xC5, 0x19, + 0xB6, 0x28, 0x53, 0x81, 0x61, 0x18, 0x34, 0x61, 0x15, 0xFB, 0x0B, 0x88, 0x2C, 0xC6, 0x48, + 0x2F, 0x3C, 0x5C, 0xBC, 0xC1, 0xC1, 0x89, 0x46, 0x97, 0xE1, 0x23, 0x95, 0x98, 0xB3, 0x4B, + 0x2A, 0x9A, 0x7A, 0xCD, 0x15, 0x24, 0x4D, 0x06, 0x90, 0xC8, 0x81, 0x94, 0x09, 0x7A, 0x9B, + 0xED, 0xA5, 0x85, 0xE8, 0x7C, 0x43, 0x71, 0x24, 0x62, 0x4C, 0x21, 0x07, 0x68, 0xE6, 0x21, + 0x5D, 0x37, 0x64, 0x82, 0x65, 0x3E, 0xB8, 0x99, 0x47, 0x87, 0x7C, 0x11, 0x8D, 0x37, 0x0C, + 0x69, 0x6A, 0x6F, 0xFC, 0xC1, 0x01, 0x8A, 0xE4, 0x13, 0xA0, 0x8A, 0x8D, 0x0F, 0xFA, 0xA8, + 0x19, 0x94, 0x5D, 0xA7, 0xA1, 0x67, 0xC2, 0x29, 0x91, 0x32, 0x90, 0xCA, 0xD1, 0xC8, 0x0A, + 0x36, 0x92, 0x58, 0x76, 0x26, 0x10, 0xEA, 0x25, 0x3E, 0x62, 0xDC, 0x24, 0x22, 0x6A, 0x30, + 0xC8, 0x92, 0xC1, 0x21, 0x36, 0xC3, 0x26, 0xF1, 0x3F, 0x44, 0x46, 0x66, 0x47, 0x12, 0xB0, + 0xB9, 0x0B, 0xC0, 0x63, 0xB4, 0x02, 0x85, 0x93, 0xCB, 0xDE, 0x06, 0xCD, 0xC2, 0x22, 0x89, + 0xE2, 0x40, 0xC7, 0xE2, 0x96, 0xB5, 0x91, 0x72, 0xC1, 0xAE, 0xDA, 0x8C, 0x99, 0xE0, 0x51, + 0x2D, 0x1A, 0x01, 0x63, 0xA9, 0x42, 0xEA, 0x33, 0x14, 0x8E, 0x69, 0x37, 0xC0, 0x26, 0x02, + 0x94, 0x24, 0xB8, 0x1B, 0x99, 0x6B, 0x1D, 0xF2, 0x2E, 0xA0, 0x62, 0x3E, 0xC6, 0x5C, 0x6B, + 0xF0, 0x93, 0x50, 0x0C, 0xF3, 0xBF, 0x35, 0x37, 0x4A, 0xDC, 0x39, 0x20, 0x35, 0xCA, 0x7C, + 0x58, 0x3B, 0x99, 0x68, 0x5B, 0xCA, 0x54, 0x1A, 0x08, 0x07, 0xB1, 0x63, 0xAC, 0xD0, 0x88, + 0x8B, 0xE0, 0x38, 0x5D, 0xEA, 0x82, 0x0D, 0xA4, 0x6E, 0x4D, 0xBB, 0x44, 0xD2, 0xE4, 0x62, + 0xC7, 0x34, 0xB8, 0x3A, 0x47, 0x3F, 0xED, 0x13, 0x64, 0x27, 0x31, 0x59, 0x25, 0x7C, 0xC2, + 0x59, 0xA8, 0xC5, 0x67, 0x6C, 0x1C, 0x76, 0xD4, 0x1D, 0x56, 0xB9, 0x90, 0x7E, 0xC1, 0xC3, + 0x59, 0x9C, 0x9E, 0x89, 0x07, 0x40, 0x3A, 0x27, 0xA7, 0x05, 0xE3, 0x61, 0x9B, 0x04, 0xB0, + 0xAD, 0x04, 0x6E, 0x8E, 0xC8, 0x16, 0x9C, 0x17, 0xB4, 0x60, 0xD4, 0x4C, 0x0C, 0x0C, 0x44, + 0x64, 0xD0, 0x44, 0xC9, 0x46, 0x18, 0x6B, 0xC7, 0x25, 0x96, 0x50, 0x83, 0xA8, 0x92, 0xBC, + 0xC4, 0x95, 0xC0, 0x54, 0x03, 0x11, 0xFF, 0x9B, 0x3E, 0x51, 0x92, 0xC3, 0x03, 0xD8, 0x8F, + 0x8B, 0xA4, 0x6A, 0x90, 0x1C, 0x78, 0x2E, 0xF0, 0x23, 0x88, 0xF1, 0xB2, 0xAD, 0xDA, 0xB6, + 0xA5, 0x35, 0x0F, 0xC3, 0x63, 0x97, 0x00, 0xE3, 0x15, 0x43, 0x37, 0x33, 0x7E, 0x4A, 0x17, + 0x8D, 0x35, 0x1C, 0xD2, 0xB5, 0x6E, 0xE1, 0xF0, 0xBF, 0xEA, 0x34, 0xAA, 0xCF, 0xA3, 0x3D, + 0x2E, 0xC7, 0x91, 0xE5, 0x07, 0x52, 0xD4, 0xD0, 0x34, 0xCB, 0x1C, 0x95, 0x15, 0x72, 0xCA, + 0xAA, 0x5C, 0x4D, 0x90, 0x94, 0x7B, 0x6B, 0x17, 0x5A, 0x6D, 0xD3, 0xC6, 0x2A, 0x77, 0xBB, + 0x8F, 0x7A, 0xC9, 0xAE, 0x24, 0x71, 0x9B, 0x53, 0xC2, 0xB1, 0x20, 0xA2, 0x87, 0x69, 0x86, + 0xE2, 0x17, 0xB7, 0x2B, 0xD7, 0xCE, 0xE4, 0x4A, 0x72, 0x65, 0xB1, 0x1C, 0xEE, 0x1A, 0xB2, + 0x26, 0x17, 0x62, 0xB3, 0x1A, 0x37, 0x38, 0x38, 0x69, 0x69, 0xC0, 0x82, 0x5F, 0xB7, 0x94, + 0x52, 0xE6, 0x52, 0xE1, 0x14, 0x2F, 0xC7, 0x3C, 0x9D, 0xF6, 0xFB, 0xA4, 0x11, 0x79, 0x5B, + 0x47, 0x17, 0x92, 0x2B, 0x29, 0xBA, 0x2D, 0x53, 0xAB, 0xE5, 0xA8, 0xC0, 0xDC, 0xC1, 0x60, + 0x1B, 0x09, 0x6C, 0x96, 0xD7, 0x93, 0x8F, 0xD5, 0xA6, 0x8A, 0x87, 0x97, 0xC7, 0xB9, 0x47, + 0x7A, 0x86, 0xA4, 0x72, 0xEB, 0x5D, 0xA2, 0x50, 0xCB, 0x2F, 0xEC, 0x31, 0x8D, 0x83, 0xC8, + 0xF4, 0x3B, 0xBE, 0x8E, 0x11, 0xC3, 0x5E, 0x37, 0x7D, 0x34, 0x93, 0x66, 0xC8, 0x5C, 0x43, + 0x82, 0x59, 0x7F, 0x6F, 0xC2, 0x7A, 0x00, 0x51, 0xC0, 0xFB, 0x00, 0xB0, 0x2C, 0x01, 0xCA, + 0x20, 0xF9, 0xA4, 0x27, 0xF1, 0x72, 0x59, 0x94, 0x77, 0xCA, 0x69, 0x0C, 0xC1, 0x32, 0x7E, + 0x0F, 0x02, 0x5F, 0x80, 0xEC, 0x33, 0x8A, 0x80, 0xA1, 0x59, 0xE3, 0x08, 0xC1, 0x2A, 0x27, + 0xDB, 0x1A, 0x7E, 0x1B, 0x96, 0x0A, 0x99, 0xD3, 0x7D, 0xFC, 0x22, 0x87, 0x2E, 0x51, 0x93, + 0x0F, 0x28, 0xC6, 0x51, 0xAB, 0x22, 0x1F, 0x53, 0xAB, 0xAE, 0xE2, 0x0B, 0xAD, 0x9A, 0x3E, + 0xAB, 0xCB, 0xAB, 0x91, 0x32, 0x51, 0xBF, 0x13, 0x5B, 0xEB, 0x29, 0x61, 0x7B, 0x57, 0x54, + 0x33, 0x3C, 0x4D, 0xAA, 0xDB, 0x22, 0x38, 0x34, 0x1C, 0x2A, 0xD9, 0x37, 0x81, 0x86, 0x28, + 0x0F, 0x64, 0x49, 0x44, 0x0B, 0x78, 0x4B, 0xA7, 0x8F, 0x5D, 0xAC, 0x44, 0xD8, 0xF6, 0x5B, + 0x3B, 0x74, 0x21, 0x95, 0x03, 0x97, 0xC3, 0x91, 0x3A, 0x2D, 0xD2, 0x3E, 0xC6, 0xD1, 0xCB, + 0x71, 0x7B, 0x36, 0xA5, 0xFC, 0x95, 0xAF, 0x19, 0x1E, 0x27, 0x82, 0x96, 0x94, 0x8C, 0x12, + 0x54, 0xEA, 0x86, 0xB4, 0xEC, 0x00, 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, 0x61, + 0x34, 0x9E, 0x5C, 0x13, 0x1A, 0x7E, 0x11, 0x6A, 0x04, 0x63, 0x86, 0x1D, 0x7D, 0x18, 0x66, + 0x3C, 0x56, 0x27, 0xC3, 0x8C, 0x71, 0x47, 0xDD, 0xAA, 0xDF, 0xD4, 0x8A, 0xCD, 0x7A, 0x45, + 0x35, 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, + ]; + MLKEM1024PrivateKey::from_bytes(&sk_bytes).unwrap() } fn bench_mlkem1024_decaps() { @@ -1233,23 +2166,23 @@ fn bench_mlkem1024_decaps() { 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, - ], - KeyType::Seed, - ) - .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ - // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + // 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM1024::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); let ct: [u8; MLKEM1024_CT_LEN] = [ 0x6B, 0x15, 0x08, 0x82, 0x0E, 0x30, 0x0E, 0x5C, 0xFE, 0xB3, 0xD9, 0xD3, 0x33, 0xF2, 0xDF, @@ -1359,15 +2292,167 @@ fn bench_mlkem1024_decaps() { 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); + let sk = load_mlkem1024_sk(); + measure(|| { + let ss = MLKEM1024::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); } -fn bench_mlkem1024_lowmemory_decaps() { - use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEMTrait}; +/// Same as bench_mlkem1024_decaps(), but with a pre-expanded private key. +/// +/// The matrix A_hat is expanded up-front and held inside the MLKEM1024PrivateKeyExpanded, which is +/// what makes repeated decapsulations with the same key cheaper -- at the cost of the memory the +/// expanded key occupies for as long as it is held. The expansion and the decapsulation are +/// inside the measurement; key generation is not (the plain key is decoded from bytes in a +/// separate frame, see [`load_mlkem512_sk`]). +fn bench_mlkem1024_decaps_expanded_sk() { + use bouncycastle::mlkem::{MLKEM1024, MLKEM1024PrivateKeyExpanded, MLKEMTrait}; - eprintln!("MLKEM1024_lowmemory/Decaps"); + eprintln!("MLKEM1024/Decaps_with_expanded_key"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured 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, + // ], + // KeyType::Seed, + // ) + // .unwrap(); + // let (pk, sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); + // let (_ss, ct) = MLKEM1024::encaps_internal(&pk, None, [1u8; 32]); + // use bouncycastle_hex as hex; + // eprintln!("ct:\n{}", &hex::encode(ct)); + // eprintln!("sk:\n{}", &hex::encode(sk.encode())); + + 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 sk = load_mlkem1024_sk(); + measure(|| { + let sk_expanded = MLKEM1024PrivateKeyExpanded::from(&sk); + let ss = MLKEM1024::decaps_with_expanded_key(&sk_expanded, &ct).unwrap(); + print!("{:x?}", ss); + }); +} + +/// Loads the private key for the matching low-memory decaps bench. See [`load_mlkem1024_sk`] for +/// why this is `#[inline(never)]`. +/// +/// A low-memory private key is just the seed, so loading one derives (rho, sigma) from it and +/// nothing more; the public key that keygen would also compute stays outside the measurement. +#[inline(never)] +fn load_mlkem1024_lowmemory_sk() -> bouncycastle::mlkem_lowmemory::MLKEM1024PrivateKey { + use bouncycastle::mlkem_lowmemory::MLKEM1024PrivateKey; let seed = KeyMaterial512::from_bytes_as_type( &[ @@ -1381,7 +2466,16 @@ fn bench_mlkem1024_lowmemory_decaps() { ) .unwrap(); - /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench */ + MLKEM1024PrivateKey::new(&seed).unwrap() +} + +fn bench_mlkem1024_lowmemory_decaps() { + use bouncycastle::mlkem_lowmemory::{MLKEM1024, MLKEMTrait}; + + eprintln!("MLKEM1024_lowmemory/Decaps"); + + /* One-time setup of the KAT -- commented out so that keygen is not captured in the bench. + `seed` is the one in load_mlkem1024_lowmemory_sk(). */ // let (pk, _sk) = MLKEM1024::keygen_from_seed(&seed).unwrap(); // let (_ss, ct) = MLKEM1024::encaps_internal(&pk, None, [1u8; 32]); // use bouncycastle_hex as hex; @@ -1495,30 +2589,38 @@ fn bench_mlkem1024_lowmemory_decaps() { 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); + let sk = load_mlkem1024_lowmemory_sk(); + measure(|| { + let ss = MLKEM1024::decaps(&sk, &ct).unwrap(); + print!("{:x?}", ss); + }); } fn main() { // print_struct_sizes() // bench_do_nothing() // bench_mlkem512_keygen() - bench_mlkem512_lowmemory_keygen() + // bench_mlkem512_lowmemory_keygen() // bench_mlkem768_keygen() // bench_mlkem768_lowmemory_keygen() // bench_mlkem1024_keygen() // bench_mlkem1024_lowmemory_keygen() // bench_mlkem512_encaps() + bench_mlkem512_encaps_expanded_pk() // bench_mlkem512_lowmemory_encaps() // bench_mlkem768_encaps() + // bench_mlkem768_encaps_expanded_pk() // bench_mlkem768_lowmemory_encaps() // bench_mlkem1024_encaps() + // bench_mlkem1024_encaps_expanded_pk() // bench_mlkem1024_lowmemory_encaps() // bench_mlkem512_decaps() + // bench_mlkem512_decaps_expanded_sk() // bench_mlkem512_lowmemory_decaps() // bench_mlkem768_decaps() + // bench_mlkem768_decaps_expanded_sk() // bench_mlkem768_lowmemory_decaps() // bench_mlkem1024_decaps() + // bench_mlkem1024_decaps_expanded_sk() // bench_mlkem1024_lowmemory_decaps() }