From aa909108088ec15700967fc554c4faee5af9b8b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 09:14:01 +0000 Subject: [PATCH 1/8] [ml-kem] Add hacspec ML-KEM specification and internal tests --- CHANGELOG.md | 1 + specs/Cargo.toml | 2 +- specs/ml-kem/Cargo.toml | 22 + specs/ml-kem/src/compress.rs | 277 +++++++++++ specs/ml-kem/src/ind_cca.rs | 644 ++++++++++++++++++++++++ specs/ml-kem/src/ind_cpa.rs | 514 +++++++++++++++++++ specs/ml-kem/src/invert_ntt.rs | 153 ++++++ specs/ml-kem/src/lib.rs | 26 + specs/ml-kem/src/matrix.rs | 183 +++++++ specs/ml-kem/src/ntt.rs | 687 ++++++++++++++++++++++++++ specs/ml-kem/src/parameters.rs | 367 ++++++++++++++ specs/ml-kem/src/polynomial.rs | 91 ++++ specs/ml-kem/src/sampling.rs | 309 ++++++++++++ specs/ml-kem/src/serialize.rs | 618 +++++++++++++++++++++++ specs/ml-kem/tests/spec_properties.rs | 60 +++ 15 files changed, 3953 insertions(+), 1 deletion(-) create mode 100644 specs/ml-kem/Cargo.toml create mode 100644 specs/ml-kem/src/compress.rs create mode 100644 specs/ml-kem/src/ind_cca.rs create mode 100644 specs/ml-kem/src/ind_cpa.rs create mode 100644 specs/ml-kem/src/invert_ntt.rs create mode 100644 specs/ml-kem/src/lib.rs create mode 100644 specs/ml-kem/src/matrix.rs create mode 100644 specs/ml-kem/src/ntt.rs create mode 100644 specs/ml-kem/src/parameters.rs create mode 100644 specs/ml-kem/src/polynomial.rs create mode 100644 specs/ml-kem/src/sampling.rs create mode 100644 specs/ml-kem/src/serialize.rs create mode 100644 specs/ml-kem/tests/spec_properties.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 07574461e4..09d5f90a84 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - [#1399](https://github.com/cryspen/libcrux/pull/1399): Add a Rust spec for SHA-3 +- [#1480](https://github.com/cryspen/libcrux/pull/1480): Add a Rust spec for ML-KEM ### Fixed - (libcrux-ml-dsa) [#1398](https://github.com/cryspen/libcrux/pull/1398): Fix incorrect AVX2 use_hint implementation diff --git a/specs/Cargo.toml b/specs/Cargo.toml index 3eb1eb2b77..23f6b8f4fe 100644 --- a/specs/Cargo.toml +++ b/specs/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["hacspec-lib", "kyber", "sha3"] +members = ["hacspec-lib", "kyber", "ml-kem", "sha3"] resolver = "2" diff --git a/specs/ml-kem/Cargo.toml b/specs/ml-kem/Cargo.toml new file mode 100644 index 0000000000..b03abae34b --- /dev/null +++ b/specs/ml-kem/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "hacspec_ml_kem" +version = "0.1.0" +edition = "2021" + +[dependencies] +hacspec_sha3 = { path = "../sha3" } +hax-lib = "=0.3.6" + +[lints.rust] +# `hax` / `hax_backend_lean` are set during extraction (the latter via +# RUSTFLAGS in hax_aeneas.py); declare them so the `unexpected_cfgs` +# check-cfg lint does not warn on `#[cfg(hax)]` / `#[cfg(hax_backend_lean)]`. +unexpected_cfgs = { level = "warn", check-cfg = ['cfg(hax)', 'cfg(hax_backend_lean)'] } + +[dev-dependencies] +hacspec_sha3 = { path = "../sha3" } +hex = { version = "0.4.3", features = ["serde"] } +proptest = "1.2.0" +rand = "0.9" +serde = { version = "1.0.171", features = ["derive"] } +serde_json = "1.0.102" diff --git a/specs/ml-kem/src/compress.rs b/specs/ml-kem/src/compress.rs new file mode 100644 index 0000000000..998a06eba0 --- /dev/null +++ b/specs/ml-kem/src/compress.rs @@ -0,0 +1,277 @@ +use crate::parameters::*; +// `ToProp` is only used inside `#[hax_lib::requires(...)]`, which is stripped +// in non-hax builds; allow the resulting unused-import lint there. +#[cfg(hax)] +use hax_lib::ToProp; + +/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539), +/// compressing a polynomial ring element is accomplished by `compress()`ing its +/// constituent field coefficients. +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(bits_per_compressed_coefficient < 12)] +pub fn compress(re: Polynomial, bits_per_compressed_coefficient: usize) -> Polynomial { + createi(|i| compress_d(re[i], bits_per_compressed_coefficient)) +} + +/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539), +/// compressing a polynomial ring element is accomplished by `decompress()`ing +/// its constituent field coefficients. +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires((bits_per_compressed_coefficient < 12).to_prop() & + hax_lib::forall(|i: usize| + hax_lib::implies(i < 256, re[i].val < (1u16 << bits_per_compressed_coefficient))))] +pub fn decompress(re: Polynomial, bits_per_compressed_coefficient: usize) -> Polynomial { + createi(|i| decompress_d(re[i], bits_per_compressed_coefficient)) +} + +/// This function implements the `Compress` function specified in the NIST FIPS +/// 203 standard (Page 18, Expression 4.5), which is defined as: +/// +/// ```plaintext +/// Compress_d: ℤq -> ℤ_{2ᵈ} +/// Compress_d(x) = ⌈(2ᵈ/q)·x⌋ +/// ``` +/// +/// Since `⌈x⌋ = ⌊x + 1/2⌋` we have: +/// +/// ```plaintext +/// Compress_d(x) = ⌊(2ᵈ/q)·x + 1/2⌋ +/// = ⌊(2^{d+1}·x + q) / 2q⌋ +/// ``` +/// +/// this latter expression is what the code computes, since it enables us to +/// avoid the use of floating point computations as required by the standard. +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(to_bit_size < 12)] +fn compress_d(fe: FieldElement, to_bit_size: usize) -> FieldElement { + hax_lib::debug_assert!(to_bit_size < 12); + let two_pow_bit_size = 2u32.pow(to_bit_size as u32); + + let compressed = + (fe.val as u32 * 2 * two_pow_bit_size + FIELD_MODULUS as u32) / (2 * FIELD_MODULUS as u32); + + FieldElement::new((compressed % two_pow_bit_size) as u16) +} + +/// This function implements the `Decompress` function specified in the NIST FIPS +/// 203 standard (Page 18, Expression 4.6), which is defined as: +/// +/// ```plaintext +/// Decompress_d: ℤ_{2ᵈ} -> ℤq +/// Decompress_d(y) = ⌈(q/2ᵈ)·y⌋ +/// ``` +/// +/// Since `⌈x⌋ = ⌊x + 1/2⌋` we have: +/// +/// ```plaintext +/// Decompress_d(y) = ⌊(q/2ᵈ)·y + 1/2⌋ +/// = ⌊(2·y·q + 2ᵈ) / 2^{d+1})⌋ +/// ``` +/// +/// this latter expression is what the code computes, since it enables us to +/// avoid the use of floating point computations as required by the standard. +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(to_bit_size < 12 && fe.val < (1u16 << to_bit_size))] +fn decompress_d(fe: FieldElement, to_bit_size: usize) -> FieldElement { + hax_lib::debug_assert!(to_bit_size < 12 && fe.val < (1u16 << to_bit_size)); + let two_pow_bit_size = 2u32.pow(to_bit_size as u32); + let numerator = 2 * fe.val as u32 * FIELD_MODULUS as u32 + two_pow_bit_size; + let decompressed = numerator / (two_pow_bit_size * 2); + FieldElement::new(decompressed as u16) +} + +#[cfg(test)] +pub mod tests { + use proptest::collection::vec; + use proptest::prelude::*; + + use crate::{ + compress::{compress, decompress}, + parameters::*, + }; + + prop_compose! { + fn arb_field_element(bit_size : usize) ( + representative in 0..FIELD_MODULUS) -> FieldElement { + FieldElement::new(representative & ((1 << bit_size) - 1)) + } + } + + prop_compose! { + pub fn arb_ring_element(bits_per_coefficient : usize) (arb_ring_coefficients in vec(arb_field_element(bits_per_coefficient), COEFFICIENTS_IN_RING_ELEMENT)) -> Polynomial { + createi(|i| arb_ring_coefficients[i]) + } + } + + use crate::compress::{compress_d, decompress_d}; + + #[test] + fn compress_d_zero_maps_to_zero() { + for d in 1..12 { + assert_eq!( + compress_d(FieldElement::new(0), d), + FieldElement::new(0), + "compress_d(0, {}) should be 0", + d + ); + } + } + + #[test] + fn decompress_d_zero_maps_to_zero() { + for d in 1..12 { + assert_eq!( + decompress_d(FieldElement::new(0), d), + FieldElement::new(0), + "decompress_d(0, {}) should be 0", + d + ); + } + } + + #[test] + fn compress_d_known_values() { + // d=1: compress_1(x) = round(2x/q) mod 2 + // Midpoint of [0, q-1] is ~1664, so values near q/2 map to 1 + assert_eq!(compress_d(FieldElement::new(0), 1), FieldElement::new(0)); + assert_eq!(compress_d(FieldElement::new(1664), 1), FieldElement::new(1)); + assert_eq!(compress_d(FieldElement::new(3328), 1), FieldElement::new(0)); // near q ≈ 0 + + // d=4: compress_4(208) should be 1 + // decompress_4(1) = round(q/16) = round(208.06) = 208 + // So compress_4(208) = 1 + assert_eq!(compress_d(FieldElement::new(208), 4), FieldElement::new(1)); + // compress_4(1665) = round(16*1665/3329) = round(8.0024) = 8 + assert_eq!(compress_d(FieldElement::new(1665), 4), FieldElement::new(8)); + } + + #[test] + fn decompress_d_known_values() { + // decompress_d(y, d) = round(q·y / 2^d) + // d=4, y=1: round(3329/16) = round(208.0625) = 208 + assert_eq!( + decompress_d(FieldElement::new(1), 4), + FieldElement::new(208) + ); + // d=4, y=8: round(3329*8/16) = round(1664.5) = 1665 + assert_eq!( + decompress_d(FieldElement::new(8), 4), + FieldElement::new(1665) + ); + // d=1, y=1: round(3329/2) = round(1664.5) = 1665 + assert_eq!( + decompress_d(FieldElement::new(1), 1), + FieldElement::new(1665) + ); + } + + #[test] + fn compress_d_output_in_range() { + for d in 1..12 { + let upper = 1u16 << d; + for x in (0..FIELD_MODULUS).step_by(17) { + let c = compress_d(FieldElement::new(x), d); + assert!( + c.val < upper, + "compress_d({}, {}) = {} not in [0, {})", + x, + d, + c.val, + upper + ); + } + } + } + + #[test] + fn decompress_d_output_in_range() { + for d in 1..12 { + let upper = 1u16 << d; + for y in 0..upper { + let dec = decompress_d(FieldElement::new(y), d); + assert!( + dec.val < FIELD_MODULUS, + "decompress_d({}, {}) = {} not in [0, q)", + y, + d, + dec.val + ); + } + } + } + + #[test] + fn compress_decompress_roundtrip_is_identity_on_decompressed() { + // compress(decompress(y, d), d) should equal y for all y in [0, 2^d) + for d in 1..12 { + let upper = 1u16 << d; + for y in 0..upper { + let recovered = compress_d(decompress_d(FieldElement::new(y), d), d); + assert_eq!( + recovered, + FieldElement::new(y), + "compress_d(decompress_d({}, {}), {}) = {:?} != {}", + y, + d, + d, + recovered, + y + ); + } + } + } + + #[test] + fn roundtrip_error_is_bounded() { + // FIPS 203 guarantees: |decompress(compress(x)) - x| mod q ≤ B_q + // where B_q = round(q / 2^{d+1}) + let q = FIELD_MODULUS as i32; + for d in 1..12usize { + let two_pow_d_plus_1 = 1u32 << (d + 1); + let b_q = ((q as u32 + two_pow_d_plus_1 / 2) / two_pow_d_plus_1) as i32; + + for x in 0..FIELD_MODULUS { + let roundtripped = decompress_d(compress_d(FieldElement::new(x), d), d); + let mut error = (roundtripped.val as i32 - x as i32).rem_euclid(q); + if error > q / 2 { + error = q - error; + } + assert!( + error <= b_q, + "d={}: |decompress(compress({})) - {}| = {} > B_q = {}", + d, + x, + x, + error, + b_q + ); + } + } + } + + proptest! { + #[test] + fn compress_to_zero_bits(ring_element in arb_ring_element(12)) { + let compressed = compress(ring_element, 0); + for i in 0..256 { + let coefficient = compressed[i]; + assert_eq!(coefficient, FieldElement::new(0)); + } + } + + fn compress_and_decompress_are_inverses_when_no_compression(ring_element in arb_ring_element(12)) { + let compressed = compress(ring_element, 12); + let decompressed = decompress(compressed, 12); + + assert_eq!(compressed, decompressed); + } + } +} diff --git a/specs/ml-kem/src/ind_cca.rs b/specs/ml-kem/src/ind_cca.rs new file mode 100644 index 0000000000..f5922eb64a --- /dev/null +++ b/specs/ml-kem/src/ind_cca.rs @@ -0,0 +1,644 @@ +use crate::matrix::transpose; +use crate::parameters::hash_functions::*; +use crate::parameters::*; +use crate::sampling::BadRejectionSamplingRandomnessError; +use crate::serialize::serialize_public_key; +use crate::{ind_cpa, serialize}; + +/// Algorithm 16: ML-KEM.KeyGen_internal +/// +/// ```plaintext +/// Input: d ∈ 𝔹³², z ∈ 𝔹³². +/// Output: encapsulation key ek ∈ 𝔹^{384k+32}. +/// Output: decapsulation key dk ∈ 𝔹^{768k+96}. +/// +/// (ekₚₖₑ, dkₚₖₑ) ← K-PKE.KeyGen(d) +/// ek ← ekₚₖₑ +/// dk ← (dkₚₖₑ ‖ ek ‖ H(ek) ‖ z) +/// return (ek, dk) +/// ``` +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && (params.eta1 == 2 || params.eta1 == 3) +)] +fn keygen_internal< + const RANK: usize, + const EK_SIZE: usize, + const DK_PKE_SIZE: usize, + const DK_SIZE: usize, +>( + params: &MlKemParams, + d: &[u8; 32], + z: &[u8; 32], +) -> Result<([u8; EK_SIZE], [u8; DK_SIZE]), BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + ); + let (ek, dk_pke) = ind_cpa::generate_keypair::(params, d)?; + + // dk ← (dkₚₖₑ ‖ ek ‖ H(ek) ‖ z) + let mut dk = [0u8; DK_SIZE]; + dk[..DK_PKE_SIZE].copy_from_slice(&dk_pke); + dk[DK_PKE_SIZE..DK_PKE_SIZE + EK_SIZE].copy_from_slice(&ek); + dk[DK_PKE_SIZE + EK_SIZE..DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE].copy_from_slice(&H(&ek)); + dk[DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE..].copy_from_slice(z); + + Ok((ek, dk)) +} + +/// Algorithm 17: ML-KEM.Encaps_internal +/// +/// ```plaintext +/// Input: encapsulation key ek ∈ 𝔹^{384k+32}. +/// Input: m ∈ 𝔹³². +/// Output: shared key K ∈ 𝔹³². +/// Output: ciphertext c ∈ 𝔹^{32(dᵤk+dᵥ)}. +/// +/// (K, r) ← G(m ‖ H(ek)) +/// c ← K-PKE.Encrypt(ek, m, r) +/// return (K, c) +/// ``` +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && ek.len() == RANK * BYTES_PER_RING_ELEMENT + 32 + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +fn encaps_internal< + const RANK: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, +>( + params: &MlKemParams, + ek: &[u8], + m: &[u8; 32], +) -> Result<([u8; 32], [u8; CT_SIZE]), BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && ek.len() == RANK * BYTES_PER_RING_ELEMENT + 32 + ); + // (K, r) ← G(m ‖ H(ek)) + let mut to_hash = [0u8; 64]; + to_hash[..32].copy_from_slice(m); + to_hash[32..].copy_from_slice(&H(ek)); + let hashed = G(&to_hash); + let (shared_secret, pseudorandomness) = hashed.split_at(32); + + let r: [u8; 32] = pseudorandomness[..32].try_into().unwrap(); + + // c ← K-PKE.Encrypt(ek, m, r) + let c = ind_cpa::encrypt::(params, ek, m, &r)?; + + let mut k = [0u8; 32]; + k.copy_from_slice(shared_secret); + Ok((k, c)) +} + +/// Algorithm 18: ML-KEM.Decaps_internal +/// +/// ```plaintext +/// Input: decapsulation key dk ∈ 𝔹^{768k+96}. +/// Input: ciphertext c ∈ 𝔹^{32(dᵤk+dᵥ)}. +/// Output: shared key K ∈ 𝔹³². +/// +/// dkₚₖₑ ← dk[0 : 384k] +/// ekₚₖₑ ← dk[384k : 768k + 32] +/// h ← dk[768k + 32 : 768k + 64] +/// z ← dk[768k + 64 : 768k + 96] +/// m′ ← K-PKE.Decrypt(dkₚₖₑ, c) +/// (K′, r′) ← G(m′ ‖ h) +/// K̃ ← J(z ‖ c) +/// c′ ← K-PKE.Encrypt(ekₚₖₑ, m′, r′) +/// if c ≠ c′ then +/// K′ ← K̃ +/// end if +/// return K′ +/// ``` +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && J_INPUT_SIZE == 32 + CT_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +fn decaps_internal< + const RANK: usize, + const EK_SIZE: usize, + const DK_SIZE: usize, + const DK_PKE_SIZE: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, + const J_INPUT_SIZE: usize, +>( + params: &MlKemParams, + dk: &[u8; DK_SIZE], + c: &[u8; CT_SIZE], +) -> Result<[u8; 32], BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && J_INPUT_SIZE == 32 + CT_SIZE + ); + // dkₚₖₑ ← dk[0 : 384k] + let dk_pke = &dk[..DK_PKE_SIZE]; + + // ekₚₖₑ ← dk[384k : 768k + 32] + let ek = &dk[DK_PKE_SIZE..DK_PKE_SIZE + EK_SIZE]; + + // h ← dk[768k + 32 : 768k + 64] + let h = &dk[DK_PKE_SIZE + EK_SIZE..DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE]; + + // z ← dk[768k + 64 : 768k + 96] + let z = &dk[DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE..]; + + // m′ ← K-PKE.Decrypt(dkₚₖₑ, c) + let m_prime = ind_cpa::decrypt::(params, dk_pke, c); + + // (K′, r′) ← G(m′ ‖ h) + let mut to_hash = [0u8; 64]; + to_hash[..32].copy_from_slice(&m_prime); + to_hash[32..].copy_from_slice(h); + let hashed = G(&to_hash); + let (success_shared_secret, pseudorandomness) = hashed.split_at(32); + + let r_prime: [u8; 32] = pseudorandomness[..32].try_into().unwrap(); + + // K̃ ← J(z ‖ c) + let mut j_input = [0u8; J_INPUT_SIZE]; + j_input[..32].copy_from_slice(z); + j_input[32..].copy_from_slice(c); + let rejection_shared_secret: [u8; 32] = J(&j_input); + + // c′ ← K-PKE.Encrypt(ekₚₖₑ, m′, r′) + let c_prime = + ind_cpa::encrypt::(params, ek, &m_prime, &r_prime)?; + + // if c ≠ c′ then K′ ← K̃ + if c[..] == c_prime[..] { + let mut k = [0u8; 32]; + k.copy_from_slice(success_shared_secret); + Ok(k) + } else { + Ok(rejection_shared_secret) + } +} + +/// Algorithm 19: ML-KEM.KeyGen +/// +/// Generates an encapsulation key and a corresponding decapsulation key. +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && (params.eta1 == 2 || params.eta1 == 3) +)] +pub fn generate_keypair< + const RANK: usize, + const EK_SIZE: usize, + const DK_SIZE: usize, + const DK_PKE_SIZE: usize, +>( + params: &MlKemParams, + randomness: &[u8; 64], +) -> Result<([u8; EK_SIZE], [u8; DK_SIZE]), BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + ); + let d: &[u8; 32] = randomness[..32].try_into().unwrap(); + let z: &[u8; 32] = randomness[32..].try_into().unwrap(); + keygen_internal::(params, d, z) +} + +/// Modulus check for encapsulation key validation (FIPS 203 Section 7.2). +/// +/// Verifies that ByteEncode₁₂(ByteDecode₁₂(ek[..384k])) == ek[..384k]. +#[hax_lib::requires( + params.rank <= 4 + && EK_SIZE == params.rank * BYTES_PER_RING_ELEMENT + 32 +)] +pub fn public_key_modulus_check( + params: &MlKemParams, + ek: &[u8; EK_SIZE], +) -> bool { + let t_size = params.t_as_ntt_encoded_size(); + let encoded_ring_elements = &ek[..t_size]; + let mut valid = true; + // Decode and re-encode; the round-trip should be identity for valid keys + for chunk in encoded_ring_elements.chunks_exact(BYTES_PER_RING_ELEMENT) { + let decoded = + serialize::byte_decode::<{ 32 * 12 }, { 256 * 12 }>(chunk.try_into().unwrap(), 12); + let re_encoded = serialize::byte_encode::<{ 32 * 12 }, { 256 * 12 }>(decoded, 12); + if chunk != re_encoded.as_slice() { + valid = false; + } + } + valid +} + +/// Algorithm 20: ML-KEM.Encaps +/// +/// Uses the encapsulation key to generate a shared key and ciphertext. +/// Includes modulus check on ek per FIPS 203 Section 7.2. +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +pub fn encapsulate< + const RANK: usize, + const EK_SIZE: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, +>( + params: &MlKemParams, + ek: &[u8; EK_SIZE], + m: &[u8; 32], +) -> Result<([u8; 32], [u8; CT_SIZE]), BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + ); + // Modulus check + hax_lib::debug_assert!( + public_key_modulus_check(params, ek), + "encapsulation key modulus check failed" + ); + + encaps_internal::(params, ek, m) +} + +/// Algorithm 21: ML-KEM.Decaps +/// +/// Uses the decapsulation key to produce a shared key from a ciphertext. +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && J_INPUT_SIZE == 32 + CT_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +pub fn decapsulate< + const RANK: usize, + const EK_SIZE: usize, + const DK_SIZE: usize, + const DK_PKE_SIZE: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, + const J_INPUT_SIZE: usize, +>( + params: &MlKemParams, + dk: &[u8; DK_SIZE], + c: &[u8; CT_SIZE], +) -> Result<[u8; 32], BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && DK_SIZE == DK_PKE_SIZE + EK_SIZE + H_DIGEST_SIZE + 32 + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && J_INPUT_SIZE == 32 + CT_SIZE + ); + decaps_internal::( + params, dk, c, + ) +} + +// ── Unpacked-API CCA helpers (P5) ──────────────────────────────────── +// +// Convention: `m_A` is in **libcrux convention** (transposed form, +// `A_transposed[j][i] = sampled(i, j)`), matching the `A` field on +// `IndCpaPublicKeyUnpacked` in the libcrux impl. The helpers transpose +// internally before delegating to the raw-form `ind_cpa::*_unpacked` +// helpers. + +/// Tuple returned by `ind_cca_unpack_generate_keypair`. Components in +/// libcrux-impl order: +/// `(secret_as_ntt, t_as_ntt, m_A_transposed, seed_for_A, public_key_hash, implicit_rejection_value)`. +pub type IndCcaUnpackedKeyPair = ( + Vector, + Vector, + Matrix, + [u8; 32], + [u8; 32], + [u8; 32], +); + +/// ML-KEM.KeyGen — unpacked variant. Returns the components used by +/// `MlKemKeyPairUnpacked` in the libcrux impl, instead of the +/// serialized `(ek, dk)` pair. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && (params.eta1 == 2 || params.eta1 == 3) +)] +pub fn ind_cca_unpack_generate_keypair( + params: &MlKemParams, + randomness: &[u8; 64], +) -> Result, BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!(EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32); + + let d: &[u8; 32] = randomness[..32].try_into().unwrap(); + let z: &[u8; 32] = randomness[32..].try_into().unwrap(); + + let (secret_as_ntt, t_as_ntt, A_as_ntt, seed_for_A) = + ind_cpa::generate_keypair_unpacked::(params, d)?; + + // Libcrux stores A in transposed form on `IndCpaPublicKeyUnpacked.A`. + let m_A: Matrix = transpose(&A_as_ntt); + + // public_key_hash = H(serialize_public_key(t_as_ntt, seed_for_A)) + let ek: [u8; EK_SIZE] = serialize_public_key::(&t_as_ntt, &seed_for_A); + let public_key_hash: [u8; 32] = H(&ek); + + let mut implicit_rejection_value = [0u8; 32]; + implicit_rejection_value.copy_from_slice(z); + + Ok(( + secret_as_ntt, + t_as_ntt, + m_A, + seed_for_A, + public_key_hash, + implicit_rejection_value, + )) +} + +/// ML-KEM.Encaps — unpacked variant. Skips the `H(ek)` and +/// `ByteDecode₁₂(ek)` decoding steps; consumes the precomputed +/// `public_key_hash`, `t_as_ntt`, and `m_A` (libcrux-transposed form) +/// directly. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +pub fn ind_cca_unpack_encapsulate< + const RANK: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, +>( + params: &MlKemParams, + public_key_hash: &[u8; 32], + t_as_ntt: &Vector, + m_A: &Matrix, + randomness: &[u8; 32], +) -> Result<([u8; 32], [u8; CT_SIZE]), BadRejectionSamplingRandomnessError> { + // (K, r) ← G(m ‖ H(ek)) + let mut to_hash = [0u8; 64]; + to_hash[..32].copy_from_slice(randomness); + to_hash[32..].copy_from_slice(public_key_hash); + let hashed = G(&to_hash); + let (shared_secret, pseudorandomness) = hashed.split_at(32); + + // Un-transpose to raw form for ind_cpa::encrypt_unpacked. + let A_as_ntt: Matrix = transpose(m_A); + + let c = ind_cpa::encrypt_unpacked::( + params, + t_as_ntt, + &A_as_ntt, + randomness, + pseudorandomness, + )?; + + let mut k = [0u8; 32]; + k.copy_from_slice(shared_secret); + Ok((k, c)) +} + +/// ML-KEM.Decaps — unpacked variant. Skips the `dk` deconstruction +/// (`dkₚₖₑ`, `ekₚₖₑ`, `h`, `z`) and the `ByteDecode₁₂(dkₚₖₑ)` step; +/// consumes the components precomputed by `ind_cca_unpack_generate_keypair` +/// directly. Re-encrypts via `ind_cpa::encrypt_unpacked` to check the +/// FO transform's c == c′ condition. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && J_INPUT_SIZE == 32 + CT_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) +)] +pub fn ind_cca_unpack_decapsulate< + const RANK: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, + const J_INPUT_SIZE: usize, +>( + params: &MlKemParams, + public_key_hash: &[u8; 32], + implicit_rejection_value: &[u8; 32], + ciphertext: &[u8; CT_SIZE], + secret_as_ntt: &Vector, + t_as_ntt: &Vector, + m_A: &Matrix, +) -> Result<[u8; 32], BadRejectionSamplingRandomnessError> { + // m′ ← K-PKE.Decrypt(secret_as_ntt, c) + let m_prime = ind_cpa::decrypt_unpacked::(params, secret_as_ntt, ciphertext); + + // (K′, r′) ← G(m′ ‖ h) + let mut to_hash = [0u8; 64]; + to_hash[..32].copy_from_slice(&m_prime); + to_hash[32..].copy_from_slice(public_key_hash); + let hashed = G(&to_hash); + let (success_shared_secret, pseudorandomness) = hashed.split_at(32); + + // K̃ ← J(z ‖ c) + let mut j_input = [0u8; J_INPUT_SIZE]; + j_input[..32].copy_from_slice(implicit_rejection_value); + j_input[32..].copy_from_slice(ciphertext); + let rejection_shared_secret: [u8; 32] = J(&j_input); + + // c′ ← K-PKE.Encrypt_unpacked(t_as_ntt, A, m′, r′) + let A_as_ntt: Matrix = transpose(m_A); + let c_prime = ind_cpa::encrypt_unpacked::( + params, + t_as_ntt, + &A_as_ntt, + &m_prime, + pseudorandomness, + )?; + + // if c ≠ c′ then K′ ← K̃ + if ciphertext[..] == c_prime[..] { + let mut k = [0u8; 32]; + k.copy_from_slice(success_shared_secret); + Ok(k) + } else { + Ok(rejection_shared_secret) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::parameters::hash_functions::J; + + #[test] + fn keygen_encaps_decaps_consistency() { + let randomness = [42u8; 64]; + let (ek, dk) = generate_keypair::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + >(&ML_KEM_768, &randomness) + .unwrap(); + + let m = [0xABu8; 32]; + let (shared_secret, ciphertext) = encapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + >(&ML_KEM_768, &ek, &m) + .unwrap(); + + let shared_secret_decapsulated = decapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + { ML_KEM_768_J_INPUT_SIZE }, + >(&ML_KEM_768, &dk, &ciphertext) + .unwrap(); + + assert_eq!(shared_secret, shared_secret_decapsulated); + } + + #[test] + fn modified_ciphertext_implicit_rejection() { + let randomness = [1u8; 64]; + let (ek, dk) = generate_keypair::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + >(&ML_KEM_768, &randomness) + .unwrap(); + + let m = [0x55u8; 32]; + let (shared_secret, mut ciphertext) = encapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + >(&ML_KEM_768, &ek, &m) + .unwrap(); + + // Tamper with ciphertext + ciphertext[0] ^= 0xFF; + + let shared_secret_decapsulated = decapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + { ML_KEM_768_J_INPUT_SIZE }, + >(&ML_KEM_768, &dk, &ciphertext) + .unwrap(); + + assert_ne!(shared_secret, shared_secret_decapsulated); + + // Verify implicit rejection: K̃ = J(z ‖ c) + let z = &dk[ML_KEM_768_DK_PKE_SIZE + ML_KEM_768_EK_SIZE + H_DIGEST_SIZE..]; + let mut j_input = [0u8; ML_KEM_768_J_INPUT_SIZE]; + j_input[..32].copy_from_slice(z); + j_input[32..].copy_from_slice(&ciphertext); + let expected_rejection: [u8; 32] = J(&j_input); + assert_eq!(shared_secret_decapsulated, expected_rejection); + } + + #[test] + fn modified_secret_key() { + let randomness = [3u8; 64]; + let (ek, mut dk) = generate_keypair::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + >(&ML_KEM_768, &randomness) + .unwrap(); + + let m = [0x77u8; 32]; + let (shared_secret, ciphertext) = encapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + >(&ML_KEM_768, &ek, &m) + .unwrap(); + + // Tamper with the secret key (not the z portion) + dk[0] ^= 0xFF; + + let shared_secret_decapsulated = decapsulate::< + 3, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + { ML_KEM_768_J_INPUT_SIZE }, + >(&ML_KEM_768, &dk, &ciphertext) + .unwrap(); + assert_ne!(shared_secret, shared_secret_decapsulated); + } +} diff --git a/specs/ml-kem/src/ind_cpa.rs b/specs/ml-kem/src/ind_cpa.rs new file mode 100644 index 0000000000..6640dc2f20 --- /dev/null +++ b/specs/ml-kem/src/ind_cpa.rs @@ -0,0 +1,514 @@ +use crate::{ + matrix::{ + compute_As_plus_e, compute_message, compute_ring_element_v, compute_vector_u, + sample_matrix_A, + }, + ntt::vector_ntt, + parameters::{hash_functions::*, *}, + sampling::{sample_poly_cbd, BadRejectionSamplingRandomnessError}, + serialize::{ + compress_then_serialize_message, compress_then_serialize_u, compress_then_serialize_v, + deserialize_ring_elements_reduced, deserialize_then_decompress_message, + deserialize_then_decompress_u, deserialize_then_decompress_v, serialize_secret_key, + }, +}; + +/// Helper to sample a polynomial from CBD with dynamic eta. +#[hax_lib::requires(eta == 2 || eta == 3)] +fn sample_secret(eta: usize, prf_input: &[u8; 33]) -> Polynomial { + match eta { + 2 => { + let out: [u8; 128] = PRF(prf_input); + sample_poly_cbd::<128, 1024>(2, &out) + } + 3 => { + let out: [u8; 192] = PRF(prf_input); + sample_poly_cbd::<192, 1536>(3, &out) + } + _ => panic!("unsupported eta={}", eta), + } +} + +#[hax_lib::requires(N1 > 0 && N == N1 - 1)] +fn concat_byte(a: &[u8; N], b: u8) -> [u8; N1] { + let mut result = [0u8; N1]; + result[..N].copy_from_slice(a); + result[N] = b; + result +} + +/// FIPS 203 inner loop: sample `RANK` polynomials from CBD_η using +/// PRF_η(seed ‖ domain_separator + i) for i ∈ {0, …, RANK-1}. +/// +/// Captures the "for i ∈ {0, …, k-1}: v[i] ← SamplePolyCBD_η(PRF_η(seed, N))" +/// pattern that appears in K-PKE.KeyGen (Alg. 13) for s/e and in +/// K-PKE.Encrypt (Alg. 14) for r/e₁. +#[hax_lib::requires( + seed.len() == 32 + && (eta == 2 || eta == 3) + && RANK <= 4 + && (domain_separator as usize) + RANK < 256 +)] +pub fn sample_vector_cbd( + eta: usize, + seed: &[u8], + domain_separator: u8, +) -> Vector { + createi(|i| { + let prf_input: [u8; 33] = concat_byte::<32, 33>( + seed.try_into().unwrap(), + domain_separator + i as u8, + ); + sample_secret(eta, &prf_input) + }) +} + +/// `sample_vector_cbd` followed by NTT. Captures the "ŝ ← NTT(s)" / +/// "ê ← NTT(e)" / "r̂ ← NTT(r)" steps in K-PKE.{KeyGen,Encrypt}. +#[hax_lib::requires( + seed.len() == 32 + && (eta == 2 || eta == 3) + && RANK <= 4 + && (domain_separator as usize) + RANK < 256 +)] +pub fn sample_vector_cbd_then_ntt( + eta: usize, + seed: &[u8], + domain_separator: u8, +) -> Vector { + vector_ntt(sample_vector_cbd::(eta, seed, domain_separator)) +} + +/// Unpacked output of `generate_keypair_unpacked`. Each tuple slot +/// matches the corresponding field on the libcrux `IndCpa{Public,Private}KeyUnpacked`: +/// `(secret_as_ntt, t_as_ntt, A_as_ntt, seed_for_A)`. +/// +/// `A_as_ntt` is the raw sample-matrix output (`sample_matrix_A(seed, false)`); +/// callers that want the libcrux-impl convention `A[j][i] = sampled(i,j)` apply +/// `matrix::transpose` after. +pub type IndCpaKeypairUnpacked = + (Vector, Vector, Matrix, [u8; 32]); + +/// Algorithm 13: K-PKE.KeyGen — unpacked variant. Returns the four +/// components separately instead of the serialized `(ek, dk)` byte +/// pair. The packed `generate_keypair` is a thin serialization +/// wrapper around this function. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && (params.eta1 == 2 || params.eta1 == 3) + && key_generation_seed.len() == 32 +)] +pub fn generate_keypair_unpacked( + params: &MlKemParams, + key_generation_seed: &[u8], +) -> Result, BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!(key_generation_seed.len() == 32); + + // (ρ,σ) ← G(d ‖ k) + let mut g_input = [0u8; 33]; + g_input[..32].copy_from_slice(key_generation_seed); + g_input[32] = RANK as u8; + let hashed = G(&g_input); + let (seed_for_A_slice, seed_for_secret_and_error) = hashed.split_at(32); + + // Â[i,j] ← SampleNTT(XOF(ρ, i, j)) + let A_as_ntt: Matrix = sample_matrix_A(seed_for_A_slice, false)?; + + // s[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) ; ŝ ← NTT(s) + let secret_as_ntt = + sample_vector_cbd_then_ntt::(params.eta1, seed_for_secret_and_error, 0); + + // e[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) ; ê ← NTT(e) + let error_as_ntt = + sample_vector_cbd_then_ntt::(params.eta1, seed_for_secret_and_error, RANK as u8); + + // t̂ ← Â◦ŝ + ê + let t_as_ntt = compute_As_plus_e(&A_as_ntt, &secret_as_ntt, &error_as_ntt); + + let mut seed_for_A = [0u8; 32]; + seed_for_A.copy_from_slice(seed_for_A_slice); + + Ok((secret_as_ntt, t_as_ntt, A_as_ntt, seed_for_A)) +} + +/// Algorithm 13: K-PKE.KeyGen +/// +/// Generates an encryption key and a corresponding decryption key. +/// +/// ```plaintext +/// Output: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}. +/// Output: decryption key dkₚₖₑ ∈ 𝔹^{384k}. +/// +/// d ←$ B +/// (ρ,σ) ← G(d) +/// N ← 0 +/// for (i ← 0; i < k; i++) +/// for(j ← 0; j < k; j++) +/// Â[i,j] ← SampleNTT(XOF(ρ, j, i)) +/// end for +/// end for +/// for(i ← 0; i < k; i++) +/// s[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) +/// N ← N + 1 +/// end for +/// for(i ← 0; i < k; i++) +/// e[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) +/// N ← N + 1 +/// end for +/// ŝ ← NTT(s) +/// ê ← NTT(e) +/// t̂ ← Â◦ŝ + ê +/// ekₚₖₑ ← ByteEncode₁₂(t̂) ‖ ρ +/// dkₚₖₑ ← ByteEncode₁₂(ŝ) +/// ``` +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && (params.eta1 == 2 || params.eta1 == 3) + && key_generation_seed.len() == 32 +)] +pub fn generate_keypair( + params: &MlKemParams, + key_generation_seed: &[u8], +) -> Result<([u8; EK_SIZE], [u8; DK_PKE_SIZE]), BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 + && DK_PKE_SIZE == RANK * BYTES_PER_RING_ELEMENT + && key_generation_seed.len() == 32 + ); + + let (secret_as_ntt, t_as_ntt, _A_as_ntt, seed_for_A) = + generate_keypair_unpacked::(params, key_generation_seed)?; + + // ekₚₖₑ ← ByteEncode₁₂(t̂) ‖ ρ + let t_encoded: [u8; DK_PKE_SIZE] = serialize_secret_key::(&t_as_ntt); + let mut ek = [0u8; EK_SIZE]; + ek[..DK_PKE_SIZE].copy_from_slice(&t_encoded); + ek[DK_PKE_SIZE..].copy_from_slice(&seed_for_A); + + // dkₚₖₑ ← ByteEncode₁₂(ŝ) + let dk: [u8; DK_PKE_SIZE] = serialize_secret_key::(&secret_as_ntt); + + Ok((ek, dk)) +} + +/// Algorithm 14: K-PKE.Encrypt +/// +/// Uses the encryption key to encrypt a plaintext message using the randomness r. +/// +/// ```plaintext +/// Input: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}. +/// Input: message m ∈ 𝔹^{32}. +/// Input: encryption randomness r ∈ 𝔹^{32}. +/// Output: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}. +/// +/// N ← 0 +/// t̂ ← ByteDecode₁₂(ekₚₖₑ[0:384k]) +/// ρ ← ekₚₖₑ[384k: 384k + 32] +/// for (i ← 0; i < k; i++) +/// for(j ← 0; j < k; j++) +/// Â[i,j] ← SampleNTT(XOF(ρ, j, i)) +/// end for +/// end for +/// for(i ← 0; i < k; i++) +/// r[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(r,N)) +/// N ← N + 1 +/// end for +/// for(i ← 0; i < k; i++) +/// e₁[i] ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) +/// N ← N + 1 +/// end for +/// e₂ ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) +/// r̂ ← NTT(r) +/// u ← NTT⁻¹(Âᵀ ◦ r̂) + e₁ +/// μ ← Decompress₁(ByteDecode₁(m)) +/// v ← NTT⁻¹(t̂ᵀ ◦ r̂) + e₂ + μ +/// c₁ ← ByteEncode_{dᵤ}(Compress_{dᵤ}(u)) +/// c₂ ← ByteEncode_{dᵥ}(Compress_{dᵥ}(v)) +/// return c ← (c₁ ‖ c₂) +/// ``` +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && ek.len() == RANK * BYTES_PER_RING_ELEMENT + 32 + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) + && randomness.len() == 32 +)] +pub fn encrypt< + const RANK: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, +>( + params: &MlKemParams, + ek: &[u8], + message: &[u8; 32], + randomness: &[u8], +) -> Result<[u8; CT_SIZE], BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && ek.len() == RANK * BYTES_PER_RING_ELEMENT + 32 + && randomness.len() == 32 + ); + + let t_encoded_size = params.t_as_ntt_encoded_size(); + + // t̂ ← ByteDecode₁₂(ekₚₖₑ[0:384k]) + let t_as_ntt: Vector = deserialize_ring_elements_reduced::(&ek[..t_encoded_size]); + + // ρ ← ekₚₖₑ[384k: 384k + 32] + let seed_for_A = &ek[t_encoded_size..]; + + // Â[i,j] ← SampleNTT(XOF(ρ, j, i)) + let A_as_ntt: Matrix = sample_matrix_A(seed_for_A, false)?; + + encrypt_unpacked::( + params, + &t_as_ntt, + &A_as_ntt, + message, + randomness, + ) +} + +/// K-PKE.Encrypt — unpacked variant. Skips the +/// `ByteDecode₁₂(ek)` / `sample_matrix_A(seed_for_A)` decoding step +/// and consumes the already-decoded `t_as_ntt` and `A_as_ntt` +/// directly. The packed `encrypt` is a thin decoding wrapper. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && (params.eta1 == 2 || params.eta1 == 3) + && (params.eta2 == 2 || params.eta2 == 3) + && randomness.len() == 32 +)] +pub fn encrypt_unpacked< + const RANK: usize, + const U_SIZE: usize, + const V_SIZE: usize, + const CT_SIZE: usize, +>( + params: &MlKemParams, + t_as_ntt: &Vector, + A_as_ntt: &Matrix, + message: &[u8; 32], + randomness: &[u8], +) -> Result<[u8; CT_SIZE], BadRejectionSamplingRandomnessError> { + hax_lib::debug_assert!( + U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du) / 8 + && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 + && CT_SIZE == U_SIZE + V_SIZE + && randomness.len() == 32 + ); + + // r[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(r,N)) ; r̂ ← NTT(r) + let r_as_ntt = sample_vector_cbd_then_ntt::(params.eta1, randomness, 0); + + // e₁[i] ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) + let error_1 = sample_vector_cbd::(params.eta2, randomness, RANK as u8); + + // e₂ ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) + let mut prf_input = [0u8; 33]; + prf_input[..32].copy_from_slice(randomness); + prf_input[32] = (RANK * 2) as u8; + let error_2 = sample_secret(params.eta2, &prf_input); + + // u ← NTT⁻¹(Âᵀ ◦ r̂) + e₁ + let u = compute_vector_u(A_as_ntt, &r_as_ntt, &error_1); + + // μ ← Decompress₁(ByteDecode₁(m)) + let message_as_ring_element = deserialize_then_decompress_message(message); + + // v ← NTT⁻¹(t̂ᵀ ◦ r̂) + e₂ + μ + let v = compute_ring_element_v(t_as_ntt, &r_as_ntt, &error_2, &message_as_ring_element); + + // c₁ ← ByteEncode_{dᵤ}(Compress_{dᵤ}(u)) + let c1: [u8; U_SIZE] = compress_then_serialize_u::(&u, params.du); + + // c₂ ← ByteEncode_{dᵥ}(Compress_{dᵥ}(v)) + let c2: [u8; V_SIZE] = compress_then_serialize_v::(&v, params.dv); + + // c ← (c₁ ‖ c₂) + let mut c = [0u8; CT_SIZE]; + c[..U_SIZE].copy_from_slice(&c1); + c[U_SIZE..].copy_from_slice(&c2); + + Ok(c) +} + +/// Algorithm 15: K-PKE.Decrypt +/// +/// Uses the decryption key to decrypt a ciphertext. +/// +/// ```plaintext +/// Input: decryption key dkₚₖₑ ∈ 𝔹^{384k}. +/// Input: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}. +/// Output: message m ∈ 𝔹^{32}. +/// +/// c₁ ← c[0 : 32dᵤk] +/// c₂ ← c[32dᵤk : 32(dᵤk + dᵥ)] +/// u ← Decompress_{dᵤ}(ByteDecode_{dᵤ}(c₁)) +/// v ← Decompress_{dᵥ}(ByteDecode_{dᵥ}(c₂)) +/// ŝ ← ByteDecode₁₂(dkₚₖₑ) +/// w ← v - NTT⁻¹(ŝᵀ ◦ NTT(u)) +/// m ← ByteEncode₁(Compress₁(w)) +/// return m +/// ``` +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && dk.len() == RANK * BYTES_PER_RING_ELEMENT + && ciphertext.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du + COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 +)] +pub fn decrypt(params: &MlKemParams, dk: &[u8], ciphertext: &[u8]) -> [u8; 32] { + hax_lib::debug_assert!( + dk.len() == RANK * BYTES_PER_RING_ELEMENT + && ciphertext.len() + == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du + + COEFFICIENTS_IN_RING_ELEMENT * params.dv) + / 8 + ); + + // ŝ ← ByteDecode₁₂(dkₚₖₑ) + let secret_as_ntt: Vector = deserialize_ring_elements_reduced::(dk); + + decrypt_unpacked::(params, &secret_as_ntt, ciphertext) +} + +/// K-PKE.Decrypt — unpacked variant. Skips the +/// `ByteDecode₁₂(dk)` decoding step and consumes the already-decoded +/// `secret_as_ntt` directly. The packed `decrypt` is a thin +/// decoding wrapper. +#[allow(non_snake_case)] +#[hax_lib::requires( + RANK <= 4 && params.rank == RANK + && ciphertext.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du + COEFFICIENTS_IN_RING_ELEMENT * params.dv) / 8 +)] +pub fn decrypt_unpacked( + params: &MlKemParams, + secret_as_ntt: &Vector, + ciphertext: &[u8], +) -> [u8; 32] { + hax_lib::debug_assert!( + ciphertext.len() + == (RANK * COEFFICIENTS_IN_RING_ELEMENT * params.du + + COEFFICIENTS_IN_RING_ELEMENT * params.dv) + / 8 + ); + let u_encoded_size = params.u_encoded_size(); + + // u ← Decompress_{dᵤ}(ByteDecode_{dᵤ}(c₁)) + let u: Vector = + deserialize_then_decompress_u::(&ciphertext[0..u_encoded_size], params.du); + + // v ← Decompress_{dᵥ}(ByteDecode_{dᵥ}(c₂)) + let v = deserialize_then_decompress_v(&ciphertext[u_encoded_size..], params.dv); + + // w ← v - NTT⁻¹(ŝᵀ ◦ NTT(u)) + let u_as_ntt = vector_ntt(u); + let w = compute_message(&v, secret_as_ntt, &u_as_ntt); + + // m ← ByteEncode₁(Compress₁(w)) + compress_then_serialize_message(w) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encrypt_decrypt_roundtrip() { + let seed = [42u8; 32]; + let (ek, dk) = generate_keypair::<3, { ML_KEM_768_EK_SIZE }, { ML_KEM_768_DK_PKE_SIZE }>( + &ML_KEM_768, + &seed, + ) + .unwrap(); + + let message = [0xABu8; 32]; + let randomness = [0xCDu8; 32]; + let ciphertext = encrypt::< + 3, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + >(&ML_KEM_768, &ek, &message, &randomness) + .unwrap(); + let decrypted = decrypt::<3>(&ML_KEM_768, &dk, &ciphertext); + + assert_eq!(decrypted, message); + } + + #[test] + fn encrypt_deterministic() { + let seed = [1u8; 32]; + let (ek, _dk) = generate_keypair::<3, { ML_KEM_768_EK_SIZE }, { ML_KEM_768_DK_PKE_SIZE }>( + &ML_KEM_768, + &seed, + ) + .unwrap(); + + let message = [0x55u8; 32]; + let randomness = [0x77u8; 32]; + let c1 = + encrypt::<3, { ML_KEM_768_U_SIZE }, { ML_KEM_768_V_SIZE }, { ML_KEM_768_CT_SIZE }>( + &ML_KEM_768, + &ek, + &message, + &randomness, + ) + .unwrap(); + let c2 = + encrypt::<3, { ML_KEM_768_U_SIZE }, { ML_KEM_768_V_SIZE }, { ML_KEM_768_CT_SIZE }>( + &ML_KEM_768, + &ek, + &message, + &randomness, + ) + .unwrap(); + + assert_eq!(c1, c2); + } + + #[test] + fn decrypt_with_wrong_key() { + let seed1 = [1u8; 32]; + let seed2 = [2u8; 32]; + let (ek, _dk1) = generate_keypair::<3, { ML_KEM_768_EK_SIZE }, { ML_KEM_768_DK_PKE_SIZE }>( + &ML_KEM_768, + &seed1, + ) + .unwrap(); + let (_ek2, dk2) = + generate_keypair::<3, { ML_KEM_768_EK_SIZE }, { ML_KEM_768_DK_PKE_SIZE }>( + &ML_KEM_768, + &seed2, + ) + .unwrap(); + + let message = [0xAAu8; 32]; + let randomness = [0xBBu8; 32]; + let ciphertext = encrypt::< + 3, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + { ML_KEM_768_CT_SIZE }, + >(&ML_KEM_768, &ek, &message, &randomness) + .unwrap(); + let decrypted = decrypt::<3>(&ML_KEM_768, &dk2, &ciphertext); + + assert_ne!(decrypted, message); + } +} diff --git a/specs/ml-kem/src/invert_ntt.rs b/specs/ml-kem/src/invert_ntt.rs new file mode 100644 index 0000000000..13fd14a618 --- /dev/null +++ b/specs/ml-kem/src/invert_ntt.rs @@ -0,0 +1,153 @@ +use crate::ntt::ZETAS; +use crate::parameters::*; + +const INVERSE_OF_128: FieldElement = FieldElement::new(3303); + +/// Use the Gentleman-Sande butterfly to invert, in-place, the NTT representation +/// of a `Polynomial`. +/// +/// This function implements Algorithm 9 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// +/// ```plaintext +/// Input: array fˆ ∈ ℤ₂₅₆. +/// Output: array f ∈ ℤ₂₅₆. +/// +/// f ← fˆ +/// k ← 127 +/// for (len ← 2; len ≤ 128; len ← 2·len) +/// for (start ← 0; start < 256; start ← start + 2·len) +/// zeta ← ζ^(BitRev₇(k)) mod q +/// k ← k − 1 +/// for (j ← start; j < start + len; j++) +/// t ← f[j] +/// f[j] ← t + f[j + len] +/// f[j + len] ← zeta·(f[j+len] − t) +/// end for +/// end for +/// end for +/// +/// f ← f·3303 mod q +/// return f +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +/// +/// Gentleman–Sande butterfly: `(a, b, ζ) ↦ (a + b, ζ·(b − a))`. +/// Used in the inverse NTT. +pub fn inv_butterfly( + zeta: FieldElement, + a: FieldElement, + b: FieldElement, +) -> (FieldElement, FieldElement) { + (a.add(b), zeta.mul(b.sub(a))) +} + +/// One layer of the inverse NTT, generic over the array length `N`. +/// +/// As in `ntt_layer_n`, `N = 2·groups·len` where `groups = zetas.len()`. +/// Each group spans `2·len` coefficients and uses one zeta for its `len` +/// Gentleman–Sande butterflies. +/// +/// This is FIPS 203 Algorithm 9 lines 3-8 applied once at butterfly +/// half-size `len`. +/// Per-index body of the `createi` in `ntt_inverse_layer_n`, hoisted to +/// dodge Aeneas issue https://github.com/AeneasVerif/aeneas/issues/924. +fn ntt_inverse_layer_n_at( + p: &[FieldElement; N], + len: usize, + zetas: &[FieldElement], + i: usize, +) -> FieldElement { + let group = i / (2 * len); + let idx = i % (2 * len); + if idx < len { + inv_butterfly(zetas[group], p[i], p[i + len]).0 + } else { + inv_butterfly(zetas[group], p[i - len], p[i]).1 + } +} + +#[hax_lib::requires( + len >= 1 && len < 1024 && zetas.len() < 1024 && zetas.len() * 2 * len == N +)] +pub fn ntt_inverse_layer_n( + p: [FieldElement; N], + len: usize, + zetas: &[FieldElement], +) -> [FieldElement; N] { + createi(|i| ntt_inverse_layer_n_at::(&p, len, zetas, i)) +} + +/// Per-index body of the `createi` in `ntt_inverse_layer`, hoisted to +/// dodge Aeneas issue https://github.com/AeneasVerif/aeneas/issues/924 +/// (capturing closure with `if`-then-`else` body fails the `Fn::call` +/// extraction path). With the body lifted into a free function, the +/// remaining closure is a single non-branching call, which aeneas +/// translates fine. +fn ntt_inverse_layer_zeta(groups: usize, round: usize) -> FieldElement { + if round < groups { + ZETAS[2 * groups - 1 - round] + } else { + FieldElement::new(0) + } +} + +/// One layer of the 256-coefficient inverse NTT. +/// +/// Follows FIPS 203 Algorithm 9. Butterfly half-size `len = 2^layer`, +/// groups = `128 / len`, zetas used = `ZETAS[groups .. 2·groups]` reversed +/// (the inverse NTT consumes the zeta table top-down). +#[hax_lib::requires(layer >= 1 && layer <= 7)] +fn ntt_inverse_layer(p: Polynomial, layer: usize) -> Polynomial { + let len = 1 << layer; + let groups = 128 / len; + // ZETAS[groups .. 2·groups] reversed: zetas[round] = ZETAS[2·groups − 1 − round] + let zetas: [FieldElement; 128] = createi(|round| ntt_inverse_layer_zeta(groups, round)); + ntt_inverse_layer_n(p, len, &zetas[0..groups]) +} + +pub fn reduce_polynomial(p: Polynomial) -> Polynomial { + createi(|i| p[i].mul(INVERSE_OF_128)) +} + +/// FIPS 203 Algorithm 9 lines 3-8 only — the seven layers of +/// Gentleman–Sande inverse butterflies, *without* the final +/// `f ← f · 3303 mod q` finalization (the `· 128⁻¹` factor). +/// +/// This is the natural intermediate form of the inverse NTT and +/// matches the impl's `invert_ntt_montgomery`, which deliberately +/// omits the `· 3303` finalization because every call site fuses it +/// with the next per-element operation (see `polynomial.rs::subtract_reduce`, +/// `add_error_reduce`, `add_message_error_reduce`'s `mont_mul(b, 1441)` +/// where `1441 ≡ R²/128 mod q`). Reference: +/// `pq-crystals/kyber/ref/ntt.c` line 106 (the `1441 = mont²/128` comment). +/// +/// The fully-finalized FIPS-203 INTT (`ntt_inverse` above) factors as +/// `reduce_polynomial ∘ ntt_inverse_butterflies`. +pub fn ntt_inverse_butterflies(p: Polynomial) -> Polynomial { + let p = ntt_inverse_layer(p, 1); + let p = ntt_inverse_layer(p, 2); + let p = ntt_inverse_layer(p, 3); + let p = ntt_inverse_layer(p, 4); + let p = ntt_inverse_layer(p, 5); + let p = ntt_inverse_layer(p, 6); + let p = ntt_inverse_layer(p, 7); + p +} + +pub fn ntt_inverse(p: Polynomial) -> Polynomial { + reduce_polynomial(ntt_inverse_butterflies(p)) +} + +/// Inverse NTT applied to each polynomial in a vector. +pub fn vector_inverse_ntt(vector_as_ntt: Vector) -> Vector { + createi(|i| ntt_inverse(vector_as_ntt[i])) +} + +/// Performs Barrett reduction on all coefficients of a polynomial. +/// This is the spec equivalent of `poly_barrett_reduce` in the implementation. +pub fn poly_barrett_reduce(p: Polynomial) -> Polynomial { + createi(|i| FieldElement::new(p[i].val % FIELD_MODULUS)) +} diff --git a/specs/ml-kem/src/lib.rs b/specs/ml-kem/src/lib.rs new file mode 100644 index 0000000000..247b51b048 --- /dev/null +++ b/specs/ml-kem/src/lib.rs @@ -0,0 +1,26 @@ +pub mod compress; +mod ind_cca; +pub mod ind_cpa; +pub mod invert_ntt; +pub mod matrix; +pub mod ntt; +pub mod parameters; +pub mod polynomial; +pub mod sampling; +pub mod serialize; + +pub use parameters::{ + MlKemParams, ML_KEM_1024, ML_KEM_1024_CT_SIZE, ML_KEM_1024_DK_PKE_SIZE, ML_KEM_1024_DK_SIZE, + ML_KEM_1024_EK_SIZE, ML_KEM_1024_J_INPUT_SIZE, ML_KEM_1024_U_SIZE, ML_KEM_1024_V_SIZE, + ML_KEM_512, ML_KEM_512_CT_SIZE, ML_KEM_512_DK_PKE_SIZE, ML_KEM_512_DK_SIZE, ML_KEM_512_EK_SIZE, + ML_KEM_512_J_INPUT_SIZE, ML_KEM_512_U_SIZE, ML_KEM_512_V_SIZE, ML_KEM_768, ML_KEM_768_CT_SIZE, + ML_KEM_768_DK_PKE_SIZE, ML_KEM_768_DK_SIZE, ML_KEM_768_EK_SIZE, ML_KEM_768_J_INPUT_SIZE, + ML_KEM_768_U_SIZE, ML_KEM_768_V_SIZE, +}; +pub use sampling::BadRejectionSamplingRandomnessError; + +pub use ind_cca::{ + decapsulate, encapsulate, generate_keypair, ind_cca_unpack_decapsulate, + ind_cca_unpack_encapsulate, ind_cca_unpack_generate_keypair, public_key_modulus_check, + IndCcaUnpackedKeyPair, +}; diff --git a/specs/ml-kem/src/matrix.rs b/specs/ml-kem/src/matrix.rs new file mode 100644 index 0000000000..181d0b5064 --- /dev/null +++ b/specs/ml-kem/src/matrix.rs @@ -0,0 +1,183 @@ +use crate::{ + invert_ntt::ntt_inverse, + ntt::multiply_ntts, + parameters::{hash_functions::*, *}, + sampling::BadRejectionSamplingRandomnessError, +}; + +/// N.B.: According to the NIST FIPS 203 standard (Page 9, Line 519), a matrix is +/// a set of column vectors. +/// +/// The NIST FIPS 203 standard can be found at +/// . +/// +pub fn add_polynomials(p1: &Polynomial, p2: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new(((p1[j].val as u32 + p2[j].val as u32) % FIELD_MODULUS as u32) as u16) + }) +} + +pub fn sub_polynomials(p1: &Polynomial, p2: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new( + ((p1[j].val as u32 + FIELD_MODULUS as u32 - p2[j].val as u32) % FIELD_MODULUS as u32) + as u16, + ) + }) +} + +pub fn add_vectors(v1: &Vector, v2: &Vector) -> Vector { + createi(|i| add_polynomials(&v1[i], &v2[i])) +} + +/// Per-index body of the `createi` in `multiply_matrix_by_column`, +/// hoisted to dodge Aeneas issue +/// https://github.com/AeneasVerif/aeneas/issues/924. +/// +/// NOTE: the matrix parameter is named `m` (not `matrix`) so the +/// aeneas-Lean extraction's call to `matrix.add_polynomials` doesn't +/// get mis-parsed as a dot-projection on the local `matrix` value. +fn multiply_matrix_by_column_at( + m: &Matrix, + vector: &Vector, + i: usize, +) -> Polynomial { + let mut result = [FieldElement::new(0); 256]; + for j in 0..RANK { + let product = multiply_ntts(&m[j][i], &vector[j]); + result = add_polynomials(&result, &product); + } + result +} + +pub fn multiply_matrix_by_column( + m: &Matrix, + vector: &Vector, +) -> Vector { + createi(|i| multiply_matrix_by_column_at::(m, vector, i)) +} + +pub fn multiply_vectors(v1: &Vector, v2: &Vector) -> Polynomial { + let mut result = [FieldElement::new(0); 256]; + for j in 0..RANK { + let product = multiply_ntts(&v1[j], &v2[j]); + result = add_polynomials(&result, &product); + } + result +} + +// NOTE: the parameter is named `m` (not `matrix`) so the aeneas-Lean +// extraction's `matrix.transpose.closure.Insts.…` references don't get +// mis-parsed as a dot-projection on the local `matrix` value. +pub fn transpose(m: &Matrix) -> Matrix { + createi(|i| createi(|j| m[j][i])) +} + +// ── Decomposed operations matching the implementation's matrix.rs ── + +/// Sample the matrix A from a seed. Corresponds to `sample_matrix_A` in the implementation. +/// +/// When `transpose` is true, A_transpose[j][i] = sampled(i, j). +/// When `transpose` is false, A_transpose[i][j] = sampled(i, j). +/// Inner `for j` loop of `sample_matrix_A`, hoisted out so the `?` +/// (early-return on rejection-sampling failure) lives inside a single +/// non-nested loop. Aeneas can't yet handle early returns inside +/// nested loops — see https://github.com/AeneasVerif/aeneas/issues/822. +#[allow(non_snake_case)] +#[hax_lib::requires(RANK <= 4)] +fn sample_matrix_A_row( + xof_input: &mut [u8; 34], + i: usize, + transpose: bool, + A_as_ntt: &mut Matrix, +) -> Result<(), BadRejectionSamplingRandomnessError> { + for j in 0..RANK { + xof_input[32] = i as u8; + xof_input[33] = j as u8; + let xof_bytes: [u8; REJECTION_SAMPLING_SEED_SIZE] = XOF(xof_input); + let sampled = crate::sampling::sample_ntt::<70, 560, 840, 6720>(xof_bytes)?; + if transpose { + A_as_ntt[j][i] = sampled; + } else { + A_as_ntt[i][j] = sampled; + } + } + Ok(()) +} + +#[allow(non_snake_case)] +#[hax_lib::requires(seed_for_A.len() == 32 && RANK <= 4)] +pub fn sample_matrix_A( + seed_for_A: &[u8], + transpose: bool, +) -> Result, BadRejectionSamplingRandomnessError> { + let mut A_as_ntt: Matrix = [[[FieldElement::new(0); 256]; RANK]; RANK]; + let mut xof_input = [0u8; 34]; + xof_input[..32].copy_from_slice(seed_for_A); + for i in 0..RANK { + sample_matrix_A_row::(&mut xof_input, i, transpose, &mut A_as_ntt)?; + } + Ok(A_as_ntt) +} + +/// Compute v − InverseNTT(sᵀ ◦ NTT(u)). +/// Corresponds to `compute_message` in the implementation's `matrix.rs`. +/// +/// Used in K-PKE.Decrypt (Algorithm 15) to recover the message: +/// w ← v - NTT⁻¹(ŝᵀ ◦ NTT(u)) +pub fn compute_message( + v: &Polynomial, + secret_as_ntt: &Vector, + u_as_ntt: &Vector, +) -> Polynomial { + let inner_product = multiply_vectors(secret_as_ntt, u_as_ntt); + let inner_product_inv = ntt_inverse(inner_product); + sub_polynomials(v, &inner_product_inv) +} + +/// Compute InverseNTT(tᵀ ◦ r̂) + e₂ + message. +/// Corresponds to `compute_ring_element_v` in the implementation's `matrix.rs`. +/// +/// Used in K-PKE.Encrypt (Algorithm 14): +/// v ← NTT⁻¹(t̂ᵀ ◦ r̂) + e₂ + μ +pub fn compute_ring_element_v( + t_as_ntt: &Vector, + r_as_ntt: &Vector, + error_2: &Polynomial, + message: &Polynomial, +) -> Polynomial { + let inner_product = multiply_vectors(t_as_ntt, r_as_ntt); + let inner_product_inv = ntt_inverse(inner_product); + add_polynomials(&add_polynomials(&inner_product_inv, error_2), message) +} + +/// Compute u := InvertNTT(Aᵀ ◦ r̂) + e₁. +/// Corresponds to `compute_vector_u` in the implementation's `matrix.rs`. +/// +/// Used in K-PKE.Encrypt (Algorithm 14): +/// u ← NTT⁻¹(Âᵀ ◦ r̂) + e₁ +pub fn compute_vector_u( + a_as_ntt: &Matrix, + r_as_ntt: &Vector, + error_1: &Vector, +) -> Vector { + let a_transpose = transpose(a_as_ntt); + let product = multiply_matrix_by_column(&a_transpose, r_as_ntt); + let product_inv: Vector = createi(|i| ntt_inverse(product[i])); + add_vectors(&product_inv, error_1) +} + +/// Compute t̂ := Â ◦ ŝ + ê. +/// Corresponds to `compute_As_plus_e` in the implementation's `matrix.rs`. +/// +/// Used in K-PKE.KeyGen (Algorithm 13): +/// t̂ ← Â◦ŝ + ê +#[allow(non_snake_case)] +pub fn compute_As_plus_e( + a_as_ntt: &Matrix, + s_as_ntt: &Vector, + error_as_ntt: &Vector, +) -> Vector { + let product = multiply_matrix_by_column(a_as_ntt, s_as_ntt); + add_vectors(&product, error_as_ntt) +} diff --git a/specs/ml-kem/src/ntt.rs b/specs/ml-kem/src/ntt.rs new file mode 100644 index 0000000000..a20bb6c4c2 --- /dev/null +++ b/specs/ml-kem/src/ntt.rs @@ -0,0 +1,687 @@ +use crate::parameters::*; + +// `ZETA` and `bit_rev_7` are referenced only from `#[cfg(test)]` code, but +// are still extracted; allow the dead-code lint in the (non-test) lib build. +#[allow(dead_code)] +const ZETA: FieldElement = FieldElement::new(17); + +/// Convert a field element to its unsigned representative in [0, q). +/// Corresponds to `to_unsigned_field_modulus` / `Vector::to_unsigned_representative` +/// in the implementation. +/// +/// In the spec, field elements are already non-negative after reduction, so this +/// is a plain modular reduction. +pub fn to_unsigned_field_modulus(a: FieldElement) -> FieldElement { + FieldElement::new(a.val % FIELD_MODULUS) // already unsigned, just reduce +} + +#[allow(dead_code)] +fn bit_rev_7(x: usize) -> usize { + let mut result = 0; + for i in 0..7 { + if (x >> i) & 1 == 1 { + result |= 1 << (6 - i); + } + } + result +} + +/// Use the Cooley–Tukey butterfly to compute an in-place NTT representation +/// of a `Polynomial`. +/// +/// Given a `Polynomial` `f`, the NTT representation `f^` is: +/// +/// ```plaintext +/// f^ := (f mod(X² - ζ^(2*BitRev₇(0) + 1), ..., f mod (X² − ζ^(2·BitRev₇(127) + 1)) +/// ``` +/// +/// This function implements Algorithm 8 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// +/// ```plaintext +/// Input: array f ∈ ℤ₂₅₆. +/// Output: array fˆ ∈ ℤ₂₅₆. +/// +/// fˆ ← f +/// k ← 1 +/// for (len ← 128; len ≥ 2; len ← len/2) +/// for (start ← 0; start < 256; start ← start + 2·len) +/// zeta ← ζ^(BitRev₇(k)) mod q +/// k ← k + 1 +/// for (j ← start; j < start + len; j++) +/// t ← zeta·fˆ[j+len] +/// fˆ[j+len] ← fˆ[j] − t +/// fˆ[j] ← fˆ[j] + t +/// end for +/// end for +/// end for +/// return fˆ +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +pub const ZETAS: [FieldElement; 128] = [ + FieldElement::new(1), + FieldElement::new(1729), + FieldElement::new(2580), + FieldElement::new(3289), + FieldElement::new(2642), + FieldElement::new(630), + FieldElement::new(1897), + FieldElement::new(848), + FieldElement::new(1062), + FieldElement::new(1919), + FieldElement::new(193), + FieldElement::new(797), + FieldElement::new(2786), + FieldElement::new(3260), + FieldElement::new(569), + FieldElement::new(1746), + FieldElement::new(296), + FieldElement::new(2447), + FieldElement::new(1339), + FieldElement::new(1476), + FieldElement::new(3046), + FieldElement::new(56), + FieldElement::new(2240), + FieldElement::new(1333), + FieldElement::new(1426), + FieldElement::new(2094), + FieldElement::new(535), + FieldElement::new(2882), + FieldElement::new(2393), + FieldElement::new(2879), + FieldElement::new(1974), + FieldElement::new(821), + FieldElement::new(289), + FieldElement::new(331), + FieldElement::new(3253), + FieldElement::new(1756), + FieldElement::new(1197), + FieldElement::new(2304), + FieldElement::new(2277), + FieldElement::new(2055), + FieldElement::new(650), + FieldElement::new(1977), + FieldElement::new(2513), + FieldElement::new(632), + FieldElement::new(2865), + FieldElement::new(33), + FieldElement::new(1320), + FieldElement::new(1915), + FieldElement::new(2319), + FieldElement::new(1435), + FieldElement::new(807), + FieldElement::new(452), + FieldElement::new(1438), + FieldElement::new(2868), + FieldElement::new(1534), + FieldElement::new(2402), + FieldElement::new(2647), + FieldElement::new(2617), + FieldElement::new(1481), + FieldElement::new(648), + FieldElement::new(2474), + FieldElement::new(3110), + FieldElement::new(1227), + FieldElement::new(910), + FieldElement::new(17), + FieldElement::new(2761), + FieldElement::new(583), + FieldElement::new(2649), + FieldElement::new(1637), + FieldElement::new(723), + FieldElement::new(2288), + FieldElement::new(1100), + FieldElement::new(1409), + FieldElement::new(2662), + FieldElement::new(3281), + FieldElement::new(233), + FieldElement::new(756), + FieldElement::new(2156), + FieldElement::new(3015), + FieldElement::new(3050), + FieldElement::new(1703), + FieldElement::new(1651), + FieldElement::new(2789), + FieldElement::new(1789), + FieldElement::new(1847), + FieldElement::new(952), + FieldElement::new(1461), + FieldElement::new(2687), + FieldElement::new(939), + FieldElement::new(2308), + FieldElement::new(2437), + FieldElement::new(2388), + FieldElement::new(733), + FieldElement::new(2337), + FieldElement::new(268), + FieldElement::new(641), + FieldElement::new(1584), + FieldElement::new(2298), + FieldElement::new(2037), + FieldElement::new(3220), + FieldElement::new(375), + FieldElement::new(2549), + FieldElement::new(2090), + FieldElement::new(1645), + FieldElement::new(1063), + FieldElement::new(319), + FieldElement::new(2773), + FieldElement::new(757), + FieldElement::new(2099), + FieldElement::new(561), + FieldElement::new(2466), + FieldElement::new(2594), + FieldElement::new(2804), + FieldElement::new(1092), + FieldElement::new(403), + FieldElement::new(1026), + FieldElement::new(1143), + FieldElement::new(2150), + FieldElement::new(2775), + FieldElement::new(886), + FieldElement::new(1722), + FieldElement::new(1212), + FieldElement::new(1874), + FieldElement::new(1029), + FieldElement::new(2110), + FieldElement::new(2935), + FieldElement::new(885), + FieldElement::new(2154), +]; + +#[hax_lib::requires(i < 128)] +pub fn get_zeta(i: usize) -> FieldElement { + ZETAS[i] +} + +/// Cooley–Tukey butterfly: `(a, b, ζ) ↦ (a + ζ·b, a − ζ·b)`. +/// Used in the forward NTT. +pub fn butterfly( + zeta: FieldElement, + a: FieldElement, + b: FieldElement, +) -> (FieldElement, FieldElement) { + let t = zeta.mul(b); + (a.add(t), a.sub(t)) +} + +/// One layer of the NTT, generic over the array length `N`. +/// +/// The layer is characterised by `(len, groups)` where `groups = zetas.len()` +/// and `N = 2·groups·len`. Each of the `groups` butterfly groups spans `2·len` +/// consecutive coefficients, uses one zeta, and runs `len` independent +/// butterflies. +/// +/// This is FIPS 203 Algorithm 8 lines 3-8 applied once at butterfly half-size +/// `len`. The within-chunk case (N = 16, len ∈ {2, 4, 8}) corresponds to the +/// trait's `ntt_layer_{1,2,3}_step`; the full-polynomial case (N = 256, +/// len = 2^layer) is what `ntt_layer` below instantiates. +/// Per-index body of the `createi` in `ntt_layer_n`, hoisted to dodge +/// Aeneas issue https://github.com/AeneasVerif/aeneas/issues/924. +fn ntt_layer_n_at( + p: &[FieldElement; N], + len: usize, + zetas: &[FieldElement], + i: usize, +) -> FieldElement { + let group = i / (2 * len); + let idx = i % (2 * len); + if idx < len { + butterfly(zetas[group], p[i], p[i + len]).0 + } else { + butterfly(zetas[group], p[i - len], p[i]).1 + } +} + +#[hax_lib::requires( + len >= 1 && len < 1024 && zetas.len() < 1024 && zetas.len() * 2 * len == N +)] +pub fn ntt_layer_n( + p: [FieldElement; N], + len: usize, + zetas: &[FieldElement], +) -> [FieldElement; N] { + createi(|i| ntt_layer_n_at::(&p, len, zetas, i)) +} + +/// One layer of the 256-coefficient NTT. Thin wrapper over `ntt_layer_n` +/// that selects the zeta slice for this layer out of the global `ZETAS` +/// table. +/// +/// Follows FIPS 203 Algorithm 8. Butterfly half-size `len = 2^layer`, +/// groups = `128 / len`, zetas used = `ZETAS[groups .. 2·groups]`. +#[hax_lib::requires(layer >= 1 && layer <= 7)] +fn ntt_layer(p: Polynomial, layer: usize) -> Polynomial { + let len = 1 << layer; + let groups = 128 / len; + ntt_layer_n(p, len, &ZETAS[groups..2 * groups]) +} + +fn ntt(p: Polynomial) -> Polynomial { + let p = ntt_layer(p, 7); + let p = ntt_layer(p, 6); + let p = ntt_layer(p, 5); + let p = ntt_layer(p, 4); + let p = ntt_layer(p, 3); + let p = ntt_layer(p, 2); + let p = ntt_layer(p, 1); + p +} + +/// Compute the product of two `KyberBinomial`s with respect to the +/// modulus `X² - zeta`. +/// +/// This function implements Algorithm 11 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// +/// ```plaintext +/// Input: a₀, a₁, b₀, b₁ ∈ ℤq. +/// Input: γ ∈ ℤq. +/// Output: c₀, c₁ ∈ ℤq. +/// +/// c₀ ← a₀·b₀ + a₁·b₁·γ +/// c₁ ← a₀·b₁ + a₁·b₀ +/// return c₀, c₁ +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +fn base_case_multiply_even( + a0: FieldElement, + a1: FieldElement, + b0: FieldElement, + b1: FieldElement, + zeta: FieldElement, +) -> FieldElement { + // c₀ = a₀·b₀ + a₁·b₁·ζ + a0.mul(b0).add(a1.mul(b1).mul(zeta)) +} + +fn base_case_multiply_odd( + a0: FieldElement, + a1: FieldElement, + b0: FieldElement, + b1: FieldElement, +) -> FieldElement { + // c₁ = a₀·b₁ + a₁·b₀ + a0.mul(b1).add(a1.mul(b0)) +} + +/// Given two `Polynomial`s in their NTT representations, +/// compute their product. Given two polynomials in the NTT domain `f^` and `ĵ`, +/// the `iᵗʰ` coefficient of the product `k̂` is determined by the calculation: +/// +/// ```plaintext +/// ĥ[2·i] + ĥ[2·i + 1]X = (f^[2·i] + f^[2·i + 1]X)·(ĝ[2·i] + ĝ[2·i + 1]X) mod (X² - ζ^(2·BitRev₇(i) + 1)) +/// ``` +/// +/// This function implements Algorithm 10 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// +/// ```plaintext +/// Input: Two arrays fˆ ∈ ℤ₂₅₆ and ĝ ∈ ℤ₂₅₆. +/// Output: An array ĥ ∈ ℤq. +/// +/// for(i ← 0; i < 128; i++) +/// (ĥ[2i], ĥ[2i+1]) ← BaseCaseMultiply(fˆ[2i], fˆ[2i+1], ĝ[2i], ĝ[2i+1], ζ^(2·BitRev₇(i) + 1)) +/// end for +/// return ĥ +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +/// Pointwise polynomial multiplication in the NTT domain, generic over +/// the array length `N`. +/// +/// The input is two NTT-domain arrays of `N` coefficients and an array of +/// `N/4` zetas. Consecutive 4-coefficient groups are treated as two +/// quadratic polynomials multiplied modulo `X² − ζ`: the first pair uses +/// `+ζ`, the second pair uses `−ζ`. This is the trait-compatible +/// restriction of FIPS 203 Algorithm 10. +/// +/// When instantiated at N=256 with `zetas = ZETAS[64..128]` this is the +/// full `multiply_ntts` below. When instantiated at N=16 with 4 zetas +/// this is the trait's `ntt_multiply(lhs, rhs, z0, z1, z2, z3)`. +/// Per-index body of the `createi` in `ntt_multiply_n`, hoisted to +/// dodge Aeneas issue https://github.com/AeneasVerif/aeneas/issues/924. +fn ntt_multiply_n_at( + p1: &[FieldElement; N], + p2: &[FieldElement; N], + zetas: &[FieldElement], + i: usize, +) -> FieldElement { + let group = i / 4; + let zeta = if i % 4 < 2 { + zetas[group] + } else { + zetas[group].neg() + }; + if i % 2 == 0 { + base_case_multiply_even(p1[i], p1[i + 1], p2[i], p2[i + 1], zeta) + } else { + base_case_multiply_odd(p1[i - 1], p1[i], p2[i - 1], p2[i]) + } +} + +#[hax_lib::requires(zetas.len() < 1024 && zetas.len() * 4 == N)] +pub fn ntt_multiply_n( + p1: &[FieldElement; N], + p2: &[FieldElement; N], + zetas: &[FieldElement], +) -> [FieldElement; N] { + createi(|i| ntt_multiply_n_at::(p1, p2, zetas, i)) +} + +pub fn multiply_ntts(p1: &Polynomial, p2: &Polynomial) -> Polynomial { + ntt_multiply_n(p1, p2, &ZETAS[64..128]) +} + +pub fn vector_ntt(vector: Vector) -> Vector { + createi(|i| ntt(vector[i])) +} + +#[cfg(test)] +mod tests { + use super::*; + + use proptest::prelude::*; + + use crate::{ + compress::tests::arb_ring_element, invert_ntt::ntt_inverse, parameters::FIELD_MODULUS, + }; + + const Q: i32 = FIELD_MODULUS as i32; + + fn mod_q(x: i32) -> FieldElement { + FieldElement::new(x.rem_euclid(Q) as u16) + } + + fn mod_pow(base: i32, exp: u32, modulus: i32) -> i32 { + let mut result: i64 = 1; + let mut b: i64 = (base as i64).rem_euclid(modulus as i64); + let mut e = exp; + while e > 0 { + if e % 2 == 1 { + result = (result * b).rem_euclid(modulus as i64); + } + b = (b * b).rem_euclid(modulus as i64); + e /= 2; + } + result as i32 + } + + /// Reference NTT implementing FIPS 203 Algorithm 8 directly with loops. + fn ref_ntt(f: &Polynomial) -> Polynomial { + let mut fhat = *f; + let mut k: usize = 1; + let mut len: usize = 128; + while len >= 2 { + let mut start = 0; + while start < 256 { + let zeta = get_zeta(k); + k += 1; + for j in start..(start + len) { + let t = (zeta.val as i32 * fhat[j + len].val as i32).rem_euclid(Q); + fhat[j + len] = mod_q(fhat[j].val as i32 - t); + fhat[j] = mod_q(fhat[j].val as i32 + t); + } + start += 2 * len; + } + len /= 2; + } + fhat + } + + const INVERSE_OF_128: FieldElement = FieldElement::new(3303); + + /// Reference inverse NTT implementing FIPS 203 Algorithm 9 directly with loops. + fn ref_ntt_inverse(fhat: &Polynomial) -> Polynomial { + let mut f = *fhat; + let mut k: usize = 127; + let mut len: usize = 2; + while len <= 128 { + let mut start = 0; + while start < 256 { + let zeta = get_zeta(k); + k -= 1; + for j in start..(start + len) { + let t = f[j]; + f[j] = mod_q(t.val as i32 + f[j + len].val as i32); + f[j + len] = mod_q(zeta.val as i32 * (f[j + len].val as i32 - t.val as i32)); + } + start += 2 * len; + } + len *= 2; + } + let inv128 = INVERSE_OF_128.val as i32; + for coeff in f.iter_mut() { + *coeff = mod_q(coeff.val as i32 * inv128); + } + f + } + + /// Multiply polynomials in Z_q[X]/(X^256+1) using schoolbook, reducing mod q. + fn poly_mul_schoolbook(a: &Polynomial, b: &Polynomial) -> Polynomial { + let mut result = [0i64; 256]; + for i in 0..256 { + for j in 0..256 { + let prod = a[i].val as i64 * b[j].val as i64; + if i + j < 256 { + result[i + j] += prod; + } else { + result[i + j - 256] -= prod; + } + } + } + createi(|i| FieldElement::new((result[i].rem_euclid(Q as i64)) as u16)) + } + + #[test] + fn seven_bit_reverse() { + assert_eq!(64, bit_rev_7(1)); + assert_eq!(127, bit_rev_7(255)); + assert_eq!(78, bit_rev_7(185)); + } + + #[test] + fn zetas_are_correct() { + for i in 0..128 { + let expected = mod_pow(ZETA.val as i32, bit_rev_7(i) as u32, Q); + assert_eq!( + get_zeta(i).val as i32, + expected, + "get_zeta({}] = {} but expected 17^BitRev7({}) = {}", + i, + get_zeta(i).val, + i, + expected + ); + } + } + + #[test] + fn ntt_of_zero_is_zero() { + let zero = [FieldElement::new(0); 256]; + assert_eq!(ntt(zero), zero); + } + + #[test] + fn ntt_matches_reference() { + // Test with a simple input: f[0] = 1, rest zero (the constant polynomial 1) + let mut f = [FieldElement::new(0); 256]; + f[0] = FieldElement::new(1); + let ntt_result = ntt(f); + let ref_result = ref_ntt(&f); + assert_eq!( + ntt_result, ref_result, + "NTT mismatch for constant polynomial 1" + ); + + // Test with f[1] = 1 (the polynomial X) + let mut f = [FieldElement::new(0); 256]; + f[1] = FieldElement::new(1); + let ntt_result = ntt(f); + let ref_result = ref_ntt(&f); + assert_eq!(ntt_result, ref_result, "NTT mismatch for polynomial X"); + + // Test with a more complex polynomial + let f: Polynomial = createi(|i| FieldElement::new((i as u16 * 7 + 3) % FIELD_MODULUS)); + let ntt_result = ntt(f); + let ref_result = ref_ntt(&f); + assert_eq!( + ntt_result, ref_result, + "NTT mismatch for complex polynomial" + ); + } + + #[test] + fn ntt_inverse_matches_reference() { + let mut fhat = [FieldElement::new(0); 256]; + fhat[0] = FieldElement::new(1); + let inv_result = ntt_inverse(fhat); + let ref_result = ref_ntt_inverse(&fhat); + assert_eq!( + inv_result, ref_result, + "Inverse NTT mismatch for unit input" + ); + + let fhat: Polynomial = createi(|i| FieldElement::new((i as u16 * 13 + 5) % FIELD_MODULUS)); + let inv_result = ntt_inverse(fhat); + let ref_result = ref_ntt_inverse(&fhat); + assert_eq!( + inv_result, ref_result, + "Inverse NTT mismatch for complex input" + ); + } + + #[test] + fn base_case_multiply_known_values() { + // (a0 + a1*X) * (b0 + b1*X) mod (X^2 - zeta) = c0 + c1*X + // c0 = a0*b0 + a1*b1*zeta + // c1 = a0*b1 + a1*b0 + let a0 = FieldElement::new(5); + let a1 = FieldElement::new(3); + let b0 = FieldElement::new(7); + let b1 = FieldElement::new(2); + let zeta = FieldElement::new(17); + + // c0 = 5*7 + 3*2*17 = 35 + 102 = 137 + let c0 = base_case_multiply_even(a0, a1, b0, b1, zeta); + assert_eq!(c0, FieldElement::new(137)); + + // c1 = 5*2 + 3*7 = 10 + 21 = 31 + let c1 = base_case_multiply_odd(a0, a1, b0, b1); + assert_eq!(c1, FieldElement::new(31)); + } + + #[test] + fn base_case_multiply_reduces_mod_q() { + let a0 = FieldElement::new(3000); + let a1 = FieldElement::new(3000); + let b0 = FieldElement::new(3000); + let b1 = FieldElement::new(3000); + let zeta = FieldElement::new(1729); + + let c0 = base_case_multiply_even(a0, a1, b0, b1, zeta); + assert!(c0.val < FIELD_MODULUS, "c0 = {} not in [0, q)", c0.val); + + let c1 = base_case_multiply_odd(a0, a1, b0, b1); + assert!(c1.val < FIELD_MODULUS, "c1 = {} not in [0, q)", c1.val); + } + + #[test] + fn multiply_ntts_known_vector() { + // Multiply NTT(1) * NTT(X) and verify result + let mut f = [FieldElement::new(0); 256]; + f[0] = FieldElement::new(1); + let mut g = [FieldElement::new(0); 256]; + g[1] = FieldElement::new(1); + + let f_ntt = ntt(f); + let g_ntt = ntt(g); + let product_ntt = multiply_ntts(&f_ntt, &g_ntt); + let product = ntt_inverse(product_ntt); + + // f*g = 1*X = X, so product should be [0, 1, 0, 0, ...] + let mut expected = [FieldElement::new(0); 256]; + expected[1] = FieldElement::new(1); + assert_eq!(product, expected, "1 * X should equal X"); + } + + #[test] + fn ntt_multiply_corresponds_to_polynomial_multiply() { + // f = 1 + 2X + 3X^2 + let mut f = [FieldElement::new(0); 256]; + f[0] = FieldElement::new(1); + f[1] = FieldElement::new(2); + f[2] = FieldElement::new(3); + + // g = 4 + 5X + let mut g = [FieldElement::new(0); 256]; + g[0] = FieldElement::new(4); + g[1] = FieldElement::new(5); + + // f*g = 4 + 13X + 22X^2 + 15X^3 (schoolbook) + let expected = poly_mul_schoolbook(&f, &g); + + let f_ntt = ntt(f); + let g_ntt = ntt(g); + let product_ntt = multiply_ntts(&f_ntt, &g_ntt); + let product = ntt_inverse(product_ntt); + + assert_eq!( + product, expected, + "NTT multiplication should correspond to polynomial multiplication" + ); + } + + #[test] + fn ntt_multiply_with_reduction() { + // Test multiplication where X^256 + 1 reduction matters + // f = X^200, g = X^100 + // f*g = X^300 = X^300 mod (X^256+1) = -X^44 + let mut f = [FieldElement::new(0); 256]; + f[200] = FieldElement::new(1); + let mut g = [FieldElement::new(0); 256]; + g[100] = FieldElement::new(1); + + let expected = poly_mul_schoolbook(&f, &g); + // expected[44] should be -1 mod q = 3328 + assert_eq!(expected[44], FieldElement::new(FIELD_MODULUS - 1)); + + let f_ntt = ntt(f); + let g_ntt = ntt(g); + let product_ntt = multiply_ntts(&f_ntt, &g_ntt); + let product = ntt_inverse(product_ntt); + + assert_eq!(product, expected); + } + + proptest! { + #[test] + fn to_ntt_and_back(ring_element in arb_ring_element(12)) { + assert_eq!(ring_element, ntt_inverse(ntt(ring_element))); + } + + #[test] + fn ntt_matches_reference_proptest(ring_element in arb_ring_element(12)) { + assert_eq!(ntt(ring_element), ref_ntt(&ring_element)); + } + + #[test] + fn ntt_inverse_matches_reference_proptest(ring_element in arb_ring_element(12)) { + assert_eq!(ntt_inverse(ring_element), ref_ntt_inverse(&ring_element)); + } + + #[test] + fn ntt_multiply_is_poly_multiply(f_elem in arb_ring_element(8), g_elem in arb_ring_element(8)) { + let expected = poly_mul_schoolbook(&f_elem, &g_elem); + let f_ntt = ntt(f_elem); + let g_ntt = ntt(g_elem); + let product = ntt_inverse(multiply_ntts(&f_ntt, &g_ntt)); + assert_eq!(product, expected); + } + } +} diff --git a/specs/ml-kem/src/parameters.rs b/specs/ml-kem/src/parameters.rs new file mode 100644 index 0000000000..fcfba22885 --- /dev/null +++ b/specs/ml-kem/src/parameters.rs @@ -0,0 +1,367 @@ +/// Field modulus: 3329 +pub const FIELD_MODULUS: u16 = 3329; + +/// Each field element needs floor(log_2(FIELD_MODULUS)) + 1 = 12 bits to represent +pub const BITS_PER_COEFFICIENT: usize = 12; + +/// Coefficients per ring element +pub const COEFFICIENTS_IN_RING_ELEMENT: usize = 256; + +/// Bits required per (uncompressed) ring element +pub const BITS_PER_RING_ELEMENT: usize = COEFFICIENTS_IN_RING_ELEMENT * 12; + +/// Bytes required per (uncompressed) ring element +pub const BYTES_PER_RING_ELEMENT: usize = BITS_PER_RING_ELEMENT / 8; + +/// Seed size for rejection sampling. +/// +/// See for some background regarding +/// this choice. +pub const REJECTION_SAMPLING_SEED_SIZE: usize = 168 * 5; + +pub use hash_functions::H_DIGEST_SIZE; + +/// ML-KEM parameter set +#[hax_lib::attributes] +pub struct MlKemParams { + #[hax_lib::refine(rank <= 4)] + pub rank: usize, + #[hax_lib::refine(eta1 <= 2 || eta1 == 3)] + pub eta1: usize, + #[hax_lib::refine(eta2 == 2)] + pub eta2: usize, + #[hax_lib::refine(du == 10 || du == 11)] + pub du: usize, + #[hax_lib::refine(dv == 4 || dv == 5)] + pub dv: usize, +} + +impl MlKemParams { + pub const fn t_as_ntt_encoded_size(&self) -> usize { + self.rank * BYTES_PER_RING_ELEMENT + } + pub const fn ek_size(&self) -> usize { + self.t_as_ntt_encoded_size() + 32 + } + pub const fn dk_pke_size(&self) -> usize { + self.rank * BYTES_PER_RING_ELEMENT + } + pub const fn dk_size(&self) -> usize { + self.dk_pke_size() + self.ek_size() + H_DIGEST_SIZE + 32 + } + pub const fn u_encoded_size(&self) -> usize { + (self.rank * COEFFICIENTS_IN_RING_ELEMENT * self.du) / 8 + } + pub const fn v_encoded_size(&self) -> usize { + (COEFFICIENTS_IN_RING_ELEMENT * self.dv) / 8 + } + pub const fn ciphertext_size(&self) -> usize { + self.u_encoded_size() + self.v_encoded_size() + } +} + +pub const ML_KEM_512: MlKemParams = MlKemParams { + rank: 2, + eta1: 3, + eta2: 2, + du: 10, + dv: 4, +}; +pub const ML_KEM_768: MlKemParams = MlKemParams { + rank: 3, + eta1: 2, + eta2: 2, + du: 10, + dv: 4, +}; +pub const ML_KEM_1024: MlKemParams = MlKemParams { + rank: 4, + eta1: 2, + eta2: 2, + du: 11, + dv: 5, +}; + +// Derived sizes for ML-KEM-512 (k=2, du=10, dv=4) +pub const ML_KEM_512_EK_SIZE: usize = 800; // 2*384 + 32 +pub const ML_KEM_512_DK_PKE_SIZE: usize = 768; // 2*384 +pub const ML_KEM_512_DK_SIZE: usize = 1632; // 768 + 800 + 32 + 32 +pub const ML_KEM_512_U_SIZE: usize = 640; // 2*256*10/8 +pub const ML_KEM_512_V_SIZE: usize = 128; // 256*4/8 +pub const ML_KEM_512_CT_SIZE: usize = 768; // U_SIZE + V_SIZE +pub const ML_KEM_512_J_INPUT_SIZE: usize = 800; // 32 + 768 + +// Derived sizes for ML-KEM-768 (k=3, du=10, dv=4) +pub const ML_KEM_768_EK_SIZE: usize = 1184; // 3*384 + 32 +pub const ML_KEM_768_DK_PKE_SIZE: usize = 1152; // 3*384 +pub const ML_KEM_768_DK_SIZE: usize = 2400; // 1152 + 1184 + 32 + 32 +pub const ML_KEM_768_U_SIZE: usize = 960; // 3*256*10/8 +pub const ML_KEM_768_V_SIZE: usize = 128; // 256*4/8 +pub const ML_KEM_768_CT_SIZE: usize = 1088; // U_SIZE + V_SIZE +pub const ML_KEM_768_J_INPUT_SIZE: usize = 1120; // 32 + 1088 + +// Derived sizes for ML-KEM-1024 (k=4, du=11, dv=5) +pub const ML_KEM_1024_EK_SIZE: usize = 1568; // 4*384 + 32 +pub const ML_KEM_1024_DK_PKE_SIZE: usize = 1536; // 4*384 +pub const ML_KEM_1024_DK_SIZE: usize = 3168; // 1536 + 1568 + 32 + 32 +pub const ML_KEM_1024_U_SIZE: usize = 1408; // 4*256*11/8 +pub const ML_KEM_1024_V_SIZE: usize = 160; // 256*5/8 +pub const ML_KEM_1024_CT_SIZE: usize = 1568; // U_SIZE + V_SIZE +pub const ML_KEM_1024_J_INPUT_SIZE: usize = 1600; // 32 + 1568 + +/// Rank-generic CPA ciphertext size, matching the Spec.MLKEM +/// `v_CPA_CIPHERTEXT_SIZE` shape. Use this in rank-generic +/// `hax_lib::requires`/`ensures` annotations where threading a +/// `MlKemParams` value would be an architectural refactor. For +/// fixed-rank consumers, prefer the `ML_KEM_{512,768,1024}_CT_SIZE` +/// constants directly; for `MlKemParams`-aware callers, prefer +/// `MlKemParams::ciphertext_size()`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn cpa_ciphertext_size(rank: usize) -> usize { + if rank == 2 { + ML_KEM_512_CT_SIZE + } else if rank == 3 { + ML_KEM_768_CT_SIZE + } else { + ML_KEM_1024_CT_SIZE + } +} + +/// Rank-to-`MlKemParams` lookup for rank-generic consumers that need to +/// invoke a `MlKemParams`-shape Hacspec function (e.g. +/// `Hacspec_ml_kem.Ind_cca.generate_keypair`) from an +/// `hax_lib::requires`/`ensures` annotation. This is the canonical +/// adapter from the libcrux-side `const K: usize` shape to the +/// Hacspec-side `params: MlKemParams` shape. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn rank_to_params(rank: usize) -> MlKemParams { + if rank == 2 { + ML_KEM_512 + } else if rank == 3 { + ML_KEM_768 + } else { + ML_KEM_1024 + } +} + +/// Rank predicate: ML-KEM is parameterised over rank ∈ {2, 3, 4}. +/// Use in `hax_lib::requires` to express rank refinement in pure Rust. +pub const fn is_rank(rank: usize) -> bool { + rank == 2 || rank == 3 || rank == 4 +} + +// ───────────────────────────────────────────────────────────────────────────── +// Rank-generic free-function size helpers (for `hax_lib::requires`/`ensures` annotations) +// ───────────────────────────────────────────────────────────────────────────── + +/// Shared-secret length in bytes (FIPS 203 §7). +pub const SHARED_SECRET_SIZE: usize = 32; + +/// CPA key-generation seed length in bytes (FIPS 203 §7). +pub const CPA_KEY_GENERATION_SEED_SIZE: usize = 32; + +/// Rank-generic encoded-NTT-vector size: `rank * 384`. Mirrors +/// `Spec.MLKEM.v_T_AS_NTT_ENCODED_SIZE` and +/// `Spec.MLKEM.v_RANKED_BYTES_PER_RING_ELEMENT`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn t_as_ntt_encoded_size(rank: usize) -> usize { + rank * BYTES_PER_RING_ELEMENT +} + +/// Synonym of `t_as_ntt_encoded_size` matching `Spec.MLKEM`'s +/// `v_RANKED_BYTES_PER_RING_ELEMENT` naming. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn ranked_bytes_per_ring_element(rank: usize) -> usize { + rank * BYTES_PER_RING_ELEMENT +} + +/// CPA encryption-key size: `rank * 384 + 32`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn cpa_public_key_size(rank: usize) -> usize { + rank * BYTES_PER_RING_ELEMENT + 32 +} + +/// CPA decryption-key size: `rank * 384`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn cpa_private_key_size(rank: usize) -> usize { + rank * BYTES_PER_RING_ELEMENT +} + +/// CCA decapsulation-key size: `cpa_private_key + cpa_public_key + H_DIGEST + z`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn cca_private_key_size(rank: usize) -> usize { + cpa_private_key_size(rank) + cpa_public_key_size(rank) + H_DIGEST_SIZE + 32 +} + +/// `du` compression factor for vector u: 10 for ranks 2,3 and 11 for rank 4. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn vector_u_compression_factor(rank: usize) -> usize { + if rank == 4 { + 11 + } else { + 10 + } +} + +/// `dv` compression factor for ring element v: 4 for ranks 2,3 and 5 for rank 4. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn vector_v_compression_factor(rank: usize) -> usize { + if rank == 4 { + 5 + } else { + 4 + } +} + +/// Per-block (per-ring-element) size of c1 in bytes: `(256 * du)/8`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn c1_block_size(rank: usize) -> usize { + (COEFFICIENTS_IN_RING_ELEMENT * vector_u_compression_factor(rank)) / 8 +} + +/// Total c1 (encoded vector u) size in bytes: `rank * c1_block_size`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn c1_size(rank: usize) -> usize { + rank * c1_block_size(rank) +} + +/// Total c2 (encoded ring element v) size in bytes: `(256 * dv)/8`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn c2_size(rank: usize) -> usize { + (COEFFICIENTS_IN_RING_ELEMENT * vector_v_compression_factor(rank)) / 8 +} + +/// `eta1` CBD parameter: 3 for rank 2, 2 for ranks 3,4. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn eta1(rank: usize) -> usize { + if rank == 2 { + 3 + } else { + 2 + } +} + +/// `eta2` CBD parameter: always 2. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn eta2(rank: usize) -> usize { + let _ = rank; + 2 +} + +/// PRF output size for eta1 CBD sampling: `64 * eta1`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn eta1_randomness_size(rank: usize) -> usize { + 64 * eta1(rank) +} + +/// PRF output size for eta2 CBD sampling: `64 * eta2`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn eta2_randomness_size(rank: usize) -> usize { + 64 * eta2(rank) +} + +/// Implicit-rejection hash input length: `32 + cpa_ciphertext_size`. +#[hax_lib::requires(rank == 2 || rank == 3 || rank == 4)] +pub const fn implicit_rejection_hash_input_size(rank: usize) -> usize { + 32 + cpa_ciphertext_size(rank) +} + +#[allow(non_snake_case)] +pub mod hash_functions { + #[hax_lib::opaque] + pub fn G(input: &[u8]) -> [u8; 64] { + hacspec_sha3::sha3_512(input) + } + + pub const H_DIGEST_SIZE: usize = 32; + + #[hax_lib::opaque] + pub fn H(input: &[u8]) -> [u8; H_DIGEST_SIZE] { + hacspec_sha3::sha3_256(input) + } + + #[hax_lib::opaque] + pub fn PRF(input: &[u8]) -> [u8; LEN] { + hacspec_sha3::shake256::(input) + } + + #[hax_lib::opaque] + pub fn XOF(input: &[u8]) -> [u8; LEN] { + hacspec_sha3::shake128::(input) + } + + #[hax_lib::opaque] + pub fn J(input: &[u8]) -> [u8; LEN] { + hacspec_sha3::shake256::(input) + } +} + +/// An ML-KEM field element: +/// - after reduction modulo FIELD_MODULUS, it is an integer in the range [0, FIELD_MODULUS - 1] +/// - it is represented as a u16 + +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +#[cfg_attr(not(any(hax, hax_backend_lean)), derive(Debug))] +#[hax_lib::attributes] +pub struct FieldElement { + #[refine(val < FIELD_MODULUS)] + pub val: u16, +} + +#[hax_lib::attributes] +impl FieldElement { + #[hax_lib::requires(val < FIELD_MODULUS)] + pub const fn new(val: u16) -> Self { + Self { val } + } + + /// Reduce an arbitrary `i16` (e.g. an impl-side coefficient or + /// Montgomery-domain value) into a canonical `FieldElement` in + /// [0, FIELD_MODULUS). Used by the impl→spec lift functions + /// (`Libcrux_ml_kem.Vector.to_spec_*_t`) to bridge the trait-layer + /// `i16` representation to the spec-layer `FieldElement` form. + pub const fn from_i16(v: i16) -> FieldElement { + let q = FIELD_MODULUS as i32; + let r = (((v as i32) % q + q) % q) as u16; + FieldElement::new(r) + } + + /// Addition in ℤ/q. + pub fn add(self, other: FieldElement) -> FieldElement { + FieldElement::new(((self.val as u32 + other.val as u32) % FIELD_MODULUS as u32) as u16) + } + + /// Subtraction in ℤ/q. Adding q avoids unsigned underflow before reducing. + pub fn sub(self, other: FieldElement) -> FieldElement { + FieldElement::new( + ((self.val as u32 + FIELD_MODULUS as u32 - other.val as u32) % FIELD_MODULUS as u32) + as u16, + ) + } + + /// Multiplication in ℤ/q. + pub fn mul(self, other: FieldElement) -> FieldElement { + FieldElement::new(((self.val as u32 * other.val as u32) % FIELD_MODULUS as u32) as u16) + } + + /// Additive inverse in ℤ/q. `a.neg()` = q − a (0 when a = 0). + pub fn neg(self) -> FieldElement { + FieldElement::new((FIELD_MODULUS - self.val) % FIELD_MODULUS) + } +} + +/// An ML-KEM polynomial ring element +pub type Polynomial = [FieldElement; 256]; + +/// An ML-KEM vector +pub type Vector = [Polynomial; RANK]; + +/// Am ML-KEM matrix +pub type Matrix = [Vector; RANK]; + +/// Utility function to create an array of size `N` by applying a function `f` to each index. +pub fn createi T>(f: F) -> [T; N] { + core::array::from_fn(f) +} + +pub type BitVector = [bool; N]; diff --git a/specs/ml-kem/src/polynomial.rs b/specs/ml-kem/src/polynomial.rs new file mode 100644 index 0000000000..0009ba19c2 --- /dev/null +++ b/specs/ml-kem/src/polynomial.rs @@ -0,0 +1,91 @@ +//! Polynomial operations matching the implementation's PolynomialRingElement interface. +//! +//! In the spec, a Polynomial is just `[FieldElement; 256]`. These functions provide +//! named operations that correspond to methods on the implementation's +//! `PolynomialRingElement` type, enabling function-by-function verification. + +use crate::ntt::multiply_ntts; +use crate::parameters::*; + +/// The zero polynomial. Corresponds to `PolynomialRingElement::ZERO()` in the implementation. +pub const fn poly_zero() -> Polynomial { + [FieldElement::new(0); 256] +} + +/// Add `rhs` into `self` in-place. Corresponds to `PolynomialRingElement::add_to_ring_element()`. +/// +/// Note: In the spec we return a new polynomial; in the implementation this is `&mut self`. +/// The mathematical operation is identical. +pub fn add_to_ring_element(lhs: &Polynomial, rhs: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new(((lhs[j].val as u32 + rhs[j].val as u32) % FIELD_MODULUS as u32) as u16) + }) +} + +/// Barrett reduction of all coefficients. Corresponds to `PolynomialRingElement::poly_barrett_reduce()`. +/// +/// In the spec, this is a no-op modular reduction since we always work with exact arithmetic. +/// In the implementation, this is needed because intermediate values may exceed the field modulus. +pub fn poly_barrett_reduce(p: &Polynomial) -> Polynomial { + createi(|i| FieldElement::new(p[i].val % FIELD_MODULUS)) +} + +/// Subtract `b` from `a` and reduce. Corresponds to `PolynomialRingElement::subtract_reduce()`. +pub fn subtract_reduce(a: &Polynomial, b: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new( + ((a[j].val as u32 + FIELD_MODULUS as u32 - b[j].val as u32) % FIELD_MODULUS as u32) + as u16, + ) + }) +} + +/// NTT-domain polynomial multiplication. Corresponds to `PolynomialRingElement::ntt_multiply()`. +/// +/// Given two polynomials in NTT form, returns their product in NTT form. +pub fn ntt_multiply(a: &Polynomial, b: &Polynomial) -> Polynomial { + multiply_ntts(a, b) +} + +/// Fused add(error, message, result). Corresponds to `PolynomialRingElement::add_message_error_reduce()`. +/// +/// Computes: self + message + result, where: +/// - `self` is error_2 +/// - `message` is the decompressed message +/// - `result` is NTT⁻¹(tᵀ ◦ r̂) +/// +/// In the implementation, this fuses the addition to avoid extra temporaries. +pub fn add_message_error_reduce( + error_2: &Polynomial, + message: &Polynomial, + ntt_product: &Polynomial, +) -> Polynomial { + createi(|j| { + FieldElement::new( + ((error_2[j].val as u32 + message[j].val as u32 + ntt_product[j].val as u32) + % FIELD_MODULUS as u32) as u16, + ) + }) +} + +/// Fused add(self, error) with reduction. Corresponds to `PolynomialRingElement::add_error_reduce()`. +/// +/// Used to compute u = NTT⁻¹(Aᵀ ◦ r̂) + e₁ +pub fn add_error_reduce(ntt_product: &Polynomial, error: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new( + ((ntt_product[j].val as u32 + error[j].val as u32) % FIELD_MODULUS as u32) as u16, + ) + }) +} + +/// Fused add(self, error) for NTT-domain values. Corresponds to `PolynomialRingElement::add_standard_error_reduce()`. +/// +/// Used to compute t̂ = Â◦ŝ + ê (both operands are in NTT domain). +pub fn add_standard_error_reduce(ntt_product: &Polynomial, error_ntt: &Polynomial) -> Polynomial { + createi(|j| { + FieldElement::new( + ((ntt_product[j].val as u32 + error_ntt[j].val as u32) % FIELD_MODULUS as u32) as u16, + ) + }) +} diff --git a/specs/ml-kem/src/sampling.rs b/specs/ml-kem/src/sampling.rs new file mode 100644 index 0000000000..5b07d80218 --- /dev/null +++ b/specs/ml-kem/src/sampling.rs @@ -0,0 +1,309 @@ +use crate::{parameters::*, serialize::*}; + +#[cfg_attr(not(any(hax, hax_backend_lean)), derive(Debug))] +pub struct BadRejectionSamplingRandomnessError; + +/// If `bytes` contains a set of uniformly random bytes, this function +/// uniformly samples a ring element `â` that is treated as being the NTT representation +/// of the corresponding polynomial `a`. +/// +/// Since rejection sampling is used, it is possible the supplied bytes are +/// not enough to sample the element, in which case an `Err` is returned and the +/// caller must try again with a fresh set of bytes. +/// +/// This function partially implements Algorithm 6 of the NIST FIPS 203 standard, +/// We say "partially" because this implementation only accepts a finite set of +/// bytes as input and returns an error if the set is not enough; Algorithm 6 of +/// the FIPS 203 standard on the other hand samples from an infinite stream of bytes +/// until the ring element is filled. Algorithm 6 is reproduced below: +/// +/// ```plaintext +/// Input: byte stream B ∈ 𝔹*. +/// Output: array â ∈ ℤ₂₅₆. +/// +/// i ← 0 +/// j ← 0 +/// while j < 256 do +/// d₁ ← B[i] + 256·(B[i+1] mod 16) +/// d₂ ← ⌊B[i+1]/16⌋ + 16·B[i+2] +/// if d₁ < q then +/// â[j] ← d₁ +/// j ← j + 1 +/// end if +/// if d₂ < q and j < 256 then +/// â[j] ← d₂ +/// j ← j + 1 +/// end if +/// i ← i + 3 +/// end while +/// return â +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +/// One iteration of rejection sampling. +/// +/// Decodes 24 bytes into 16 u16 values using 12-bit little-endian packing +/// (FIPS 203 Algorithm 6, inner loop body), keeps those `< q`, and returns +/// the accepted values packed at the start of a 16-element array together +/// with the count. The unused tail of the array is filled with zeros. +/// +/// This is the natural unit of rejection sampling at the trait boundary +/// (`Operations::rej_sample` consumes exactly 24 bytes); `sample_ntt` +/// below repeatedly applies this step until 256 coefficients have been +/// accepted or the byte stream is exhausted. +pub fn rej_sample_step(bytes: [u8; 24]) -> ([FieldElement; 16], usize) { + let decoded = byte_decode_generic::<2, 16, 24, 192>(&bytes, 12); + let mut result = [FieldElement::new(0); 16]; + let mut count: usize = 0; + for i in 0..16 { + hax_lib::loop_invariant!(|i: usize| count <= i); + if decoded[i] < FIELD_MODULUS { + result[count] = FieldElement::new(decoded[i]); + count += 1; + } + } + (result, count) +} + +#[hax_lib::requires( + N <= MAX_BYTES / 96 + && N8 == N * 8 + && N12 == N * 12 + && N96 == N12 * 8 +)] +pub fn sample_ntt( + bytes: [u8; N12], +) -> Result { + let decoded = byte_decode_generic::(&bytes, 12); + let mut result = [FieldElement::new(0); 256]; + let mut sampled_coefficients: usize = 0; + for i in 0..N8 { + if decoded[i] < FIELD_MODULUS && sampled_coefficients < 256 { + result[sampled_coefficients] = FieldElement::new(decoded[i]); + sampled_coefficients += 1 + } + } + if sampled_coefficients == 256 { + Ok(result) + } else { + Err(BadRejectionSamplingRandomnessError) + } +} + +#[hax_lib::requires(eta <= 4 && coins.len() == eta)] +#[hax_lib::ensures(|r| r.val <= eta as u16)] +fn sum_coins(eta: usize, coins: &[bool]) -> FieldElement { + hax_lib::debug_assert!(eta <= 4 && coins.len() == eta); + let mut sum: u16 = 0; + for i in 0..eta { + hax_lib::loop_invariant!(|i: usize| sum <= (i as u16)); + sum += coins[i] as u16; + } + FieldElement::new(sum) +} + +/// Given a series of uniformly random bytes in `randomness`, sample +/// a ring element from a binomial distribution centered at 0 that uses two sets +/// of `eta` coin flips. If, for example, +/// `eta = ETA`, each ring coefficient is a value `v` such +/// such that `v ∈ {-ETA, -ETA + 1, ..., 0, ..., ETA + 1, ETA}` and: +/// +/// ```plaintext +/// - If v < 0, Pr[v] = Pr[-v] +/// - If v >= 0, Pr[v] = BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) / 2 ^ (2 * ETA) +/// ``` +/// +/// The values `v < 0` are mapped to the appropriate `KyberFieldElement`. +/// +/// The expected value is: +/// +/// ```plaintext +/// E[X] = (-ETA)Pr[-ETA] + (-(ETA - 1))Pr[-(ETA - 1)] + ... + (ETA - 1)Pr[ETA - 1] + (ETA)Pr[ETA] +/// = 0 since Pr[-v] = Pr[v] when v < 0. +/// ``` +/// +/// And the variance is: +/// +/// ```plaintext +/// Var(X) = E[(X - E[X])^2] +/// = E[X^2] +/// = sum_(v=-ETA to ETA)v^2 * (BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) / 2^(2 * ETA)) +/// = ETA / 2 +/// ``` +/// +/// This function implements Algorithm 7 of the NIST FIPS 203 standard, which is +/// reproduced below: +/// +/// ```plaintext +/// Input: byte array B ∈ 𝔹^{64η}. +/// Output: array f ∈ ℤ₂₅₆. +/// +/// b ← BytesToBits(B) +/// for (i ← 0; i < 256; i++) +/// x ← ∑(j=0 to η - 1) b[2iη + j] +/// y ← ∑(j=0 to η - 1) b[2iη + η + j] +/// f[i] ← x−y mod q +/// end for +/// return f +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(eta <= 4 && ETA64 == eta * 64 && ETA512 == eta * 512)] +pub fn sample_poly_cbd( + eta: usize, + bytes: &[u8; ETA64], +) -> Polynomial { + hax_lib::debug_assert!(ETA64 == eta * 64 && ETA512 == eta * 512); + let bits: [bool; ETA512] = bytes_to_bits(bytes); + createi(|i| { + let x: FieldElement = sum_coins(eta, &bits[(2 * i * eta)..(2 * i * eta + eta)]); + let y: FieldElement = sum_coins(eta, &bits[(2 * i * eta + eta)..(2 * i * eta + 2 * eta)]); + FieldElement::new((x.val + FIELD_MODULUS - y.val) % FIELD_MODULUS) + }) +} + +#[cfg(test)] +mod tests { + use proptest::collection::vec; + use proptest::prelude::*; + + use super::*; + + fn field_coefficient_to_binomial_sample(sampling_coins: usize, coefficient: u16) -> f64 { + if sampling_coins == 2 { + match coefficient { + 3327 => -2.0, + 3328 => -1.0, + 0 => 0.0, + 1 => 1.0, + 2 => 2.0, + other => panic!( + "{} should not be a coefficient when ETA = {}.", + other, sampling_coins + ), + } + } else if sampling_coins == 3 { + match coefficient { + 3326 => -3.0, + 3327 => -2.0, + 3328 => -1.0, + 0 => 0.0, + 1 => 1.0, + 2 => 2.0, + 3 => 3.0, + other => panic!( + "{} should not be a coefficient when ETA = {}.", + other, sampling_coins + ), + } + } else { + panic!("ETA is neither 2 nor 3."); + } + } + + fn percentage_error(actual: f64, expected: f64) -> f64 { + ((actual - expected).abs() / expected.abs()) * 100.0 + } + + const REJECTION_SAMPLING_ATTEMPTS: usize = 3; + + #[test] + fn uniform_sample_from_all_zeros() { + let r = sample_ntt::<105, 840, 1260, 10080>([0; 1260]).unwrap(); + for coefficient in r.into_iter() { + assert_eq!(coefficient, FieldElement::new(0)); + } + } + + #[test] + #[should_panic] + fn uniform_sample_from_all_u8_max() { + let _ = sample_ntt::<105, 840, 1260, 10080>([u8::MAX; 1260]).unwrap(); + } + + proptest! { + #[test] + #[ignore = "see https://github.com/cryspen/libcrux/issues/112"] + fn uniform_sampler_mean_and_variance(randomness in vec(any::(), 3 * 1260)) { + let mut sampled_ring_element = [FieldElement::new(0); 256]; + + let mut sampling_attempts = (0..REJECTION_SAMPLING_ATTEMPTS).peekable(); + while let Some(attempt) = sampling_attempts.next() { + let sampled = sample_ntt::<105, 840, 1260, 10080>(randomness[attempt*1260..(attempt+1)*1260].try_into().unwrap()); + + if sampled.is_ok() { + sampled_ring_element = sampled.unwrap(); + break; + } else if sampling_attempts.peek().is_none() { + panic!("Unable to uniformly sample a ring element with the given randomness."); + } + } + + // Mean = (a + b) / 2 = (0 + 3328) / 2 + let expected_mean : f64 = 1664.0; + + // Variance = (n^2 - 1) / 12 where: + // n = b - a + 1 = 3328 - 0 + 1 = 3329 + let expected_variance : f64 = 923_520.0; + + let mut mean : f64 = 0.0; + let mut variance : f64 = 0.0; + + for coefficient in sampled_ring_element.iter() { + mean += f64::from(coefficient.val); + } + mean /= sampled_ring_element.len() as f64; + + for coefficient in sampled_ring_element.iter() { + let coefficient_value : f64 = f64::from(coefficient.val); + variance += (coefficient_value - mean) * (coefficient_value - mean); + } + variance /= (sampled_ring_element.len() - 1) as f64; + + // We use looser tolerances as compared to in the binomial + // sampling test since we're not sampling as many elements here + // (we can never be sure what randomness we have will allow for + // a ring element to be sampled uniformly). + assert!(percentage_error(mean, expected_mean) < 25.0, "The variance is {}.", variance); + assert!(percentage_error(variance, expected_variance) < 25.0, "The variance is {}.", variance); + } + + #[test] + #[ignore = "see https://github.com/cryspen/libcrux/issues/112"] + fn binomial_sampler_mean_and_variance( + randomness in vec(any::(), 2 * 2 * 64) + ) + { + // TODO: Generalize sampling coins to be in {2, 3} + let sampling_coins : usize = 2; + let expected_variance : f64 = (sampling_coins as f64) / 2.0; + + let mut mean : f64 = 0.0; + let mut variance : f64 = 0.0; + + let ring_element_1 = sample_poly_cbd::<128, 1024>(sampling_coins, &randomness[0..sampling_coins * 64].try_into().unwrap()); + let ring_element_2 = sample_poly_cbd::<128, 1024>(sampling_coins, &randomness[sampling_coins * 64..].try_into().unwrap()); + + let all_coefficients = ring_element_1 + .into_iter() + .chain(ring_element_2.into_iter()) + .collect::>(); + + for coefficient in &all_coefficients { + mean += field_coefficient_to_binomial_sample(sampling_coins, coefficient.val); + } + mean /= all_coefficients.len() as f64; + + for coefficient in &all_coefficients { + let binomial_sample = field_coefficient_to_binomial_sample(sampling_coins, coefficient.val); + variance += (binomial_sample - mean) * (binomial_sample - mean); + } + variance /= (all_coefficients.len() - 1) as f64; + + assert!(mean < 0.3, "The mean is {}.", mean); // The expected mean is 0 + assert!(percentage_error(variance, expected_variance) < 26.0, "The variance is {}.", variance); + } + } +} diff --git a/specs/ml-kem/src/serialize.rs b/specs/ml-kem/src/serialize.rs new file mode 100644 index 0000000000..a769af6c07 --- /dev/null +++ b/specs/ml-kem/src/serialize.rs @@ -0,0 +1,618 @@ +use crate::compress::{compress, decompress}; +use crate::ntt::vector_ntt; +use crate::parameters::*; + +pub const MAX_BYTES: usize = 16384; + +/// Converts a set of bytes in `bytes` into a set of bits. +/// +/// This function implements Algorithm 3 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// +/// ```plaintext +/// Input: byte array B ∈ 𝔹ˡ. +/// Output: bit array b ∈ {0,1}⁸ˡ. +/// for (i ← 0; i < l; i++) +/// for(j ← 0; j < 8; j++) +/// b[8i + j] ← B[i] mod 2 +/// B[i] ← ⌊B[i]/2⌋ +/// end for +/// end for +/// return b +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(N < 16384 && N8 == N * 8)] +pub fn bytes_to_bits(bytes: &[u8; N]) -> BitVector { + hax_lib::debug_assert!(N8 == N * 8); + createi(|i| (bytes[i / 8] >> (i % 8)) & 1 == 1) +} + +/// Converts a bit string `bits` into an array of bytes. This function asserts +/// that `bits.len()` is a multiple of 8. +/// +/// This function implements Algorithm 2 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// +/// ```plaintext +/// Input: bit array b ∈ {0,1}⁸ˡ. +/// Output: byte array B ∈ 𝔹ˡ. +/// +/// B ← (0,...,0) +/// for (i ← 0; i < 8l; i++) +/// B[⌊i/8⌋] ← B[⌊i/8⌋] + b[i]·2^{i} mod 8 +/// end for +/// return B +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +#[hax_lib::requires(N < 16384 && N8 == N * 8)] +pub fn bits_to_bytes(bv: &BitVector) -> [u8; N] { + hax_lib::debug_assert!(N8 == N * 8); + let result = createi(|i| { + bv[8 * i] as u8 + | ((bv[8 * i + 1] as u8) << 1) + | ((bv[8 * i + 2] as u8) << 2) + | ((bv[8 * i + 3] as u8) << 3) + | ((bv[8 * i + 4] as u8) << 4) + | ((bv[8 * i + 5] as u8) << 5) + | ((bv[8 * i + 6] as u8) << 6) + | ((bv[8 * i + 7] as u8) << 7) + }); + result +} + +/// Convert the associated ring element into a vector of +/// `COEFFICIENTS_IN_RING_ELEMENT * bits_per_coefficient` +/// bits, and output this vector as a byte array such that the first 8 bits of +/// the vector represent the first byte of the output, the next 8 bits the +/// next byte of the output, and so on ... +/// +/// N.B.: `byte_encode` is only the inverse of `byte_decode` when: +/// +/// - each ring coefficient can fit into `bits_per_coefficient` (otherwise +/// lossy compression takes place) +/// - `bits_per_coefficient < BITS_PER_COEFFICIENT`, since +/// otherwise when `byte_decode` operates on 12 bits at a time, +/// it is not injective: the values 3329 + 1 and 1 for example both fit into +/// 12 bits and map to the same `KyberFieldElement` +/// +/// Otherwise `byte_decode` is not injective and therefore has no left inverse. +/// +/// N.B.: This function asserts that `bits_per_coefficient <= 12` +/// +/// This function implements Algorithm 4 of the NIST FIPS 203 standard, which is +/// reproduced below: +/// +/// ```plaintext +/// Input: integer array F ∈ ℤₘ²⁵⁶, where m = 2ᵈ if d < 12 and m = q if d = 12. +/// Output: byte array B ∈ 𝔹^{32d}. +/// for(i ← 0; i < 256; i++) +/// a ← F[i] +/// for(j ← 0; j < d; j++) +/// b[i·d + j] ← a mod 2 +/// a ← (a − b[i·d + j])/2 +/// end for +/// B ← BitsToBytes(b) +/// return B +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . + +#[allow(non_upper_case_globals)] +#[hax_lib::requires(N < 16384 && d <= BITS_PER_COEFFICIENT && Nd == N * d)] +pub fn bitvector_from_bounded_ints( + input: &[u16; N], + d: usize, +) -> BitVector { + hax_lib::debug_assert!(Nd == N * d); + createi(|i| (input[i / d] >> (i % d)) & 1u16 == 1) +} + +#[hax_lib::requires(d <= BITS_PER_COEFFICIENT && D32 == 32 * d && D256 == 256 * d)] +pub fn byte_encode(p: Polynomial, d: usize) -> [u8; D32] { + hax_lib::debug_assert!(d <= BITS_PER_COEFFICIENT && D32 == 32 * d && D256 == 256 * d); + let p_raw: [u16; 256] = createi(|i| p[i].val); + let bv = bitvector_from_bounded_ints::<256, D256>(&p_raw, d); + bits_to_bytes(&bv) +} + +/// Given a series of bytes representing a ring element in `re_bytes`, +/// first convert them into a vector of bits in little-endian order; i.e. +/// the least significant `bits_per_coefficient` of `re_bytes[0]` +/// are the first set of bits in the bitstream. +/// +/// This vector is deserialized into a `Polynomial` structure. +/// The first `bits_per_coefficient` represent the first coefficient of +/// the ring element, the second `bits_per_coefficient` the second coefficient, +/// and so on. +/// +/// N.B.: This function asserts that `bits_per_coefficient <= 12` +/// +/// This function implements Algorithm 5 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// +/// ```plaintext +/// Input: byte array B ∈ 𝔹^{32d}. +/// Output: integer array F ∈ ℤₘ²⁵⁶, where m = 2ᵈ if d < 12 and m = q if d = 12. +/// +/// b ← BytesToBits(B) +/// for (i ← 0; i < 256; i++) +/// F[i] ← ∑(j = 0 to d−1) b[i·d + j] · 2ʲ mod m +/// end for +/// return F +/// ``` +/// +/// The NIST FIPS 203 standard can be found at +/// . +/// Per-index body of the `createi` in `bitvector_to_bounded_ints`, +/// hoisted to dodge Aeneas issue +/// https://github.com/AeneasVerif/aeneas/issues/924. +#[allow(non_upper_case_globals)] +fn bitvector_to_bounded_ints_at( + input: &BitVector, + d: usize, + i: usize, +) -> u16 { + let mut coefficient: u16 = 0; + for j in 0..d { + // Loop invariant: coefficient holds the value assembled + // from the lower j bits, so it's strictly less than 2^j. + // Using addition instead of bit-OR makes the bound + // discharge tractable for Z3 (the bits are disjoint by + // construction, so OR == +). + hax_lib::loop_invariant!(|j: usize| coefficient < (1u16 << j)); + if input[i * d + j] { + coefficient += 1u16 << j; + } + } + coefficient +} + +#[hax_lib::requires(N < 16384 && d <= BITS_PER_COEFFICIENT && Nd == N * d)] +#[hax_lib::ensures(|result| + hax_lib::forall(|i: usize| hax_lib::implies(i < N, result[i] < (1u16 << d))))] +#[allow(non_upper_case_globals)] +pub fn bitvector_to_bounded_ints( + input: &BitVector, + d: usize, +) -> [u16; N] { + hax_lib::debug_assert!(Nd == N * d); + let result: [u16; N] = createi(|i| bitvector_to_bounded_ints_at::(input, d, i)); + hax_lib::debug_assert!(*input == bitvector_from_bounded_ints(&result, d)); + result +} + +#[hax_lib::requires(d > 0 && d <= BITS_PER_COEFFICIENT && N < 16384 / d && N < 16384 / 8 && N8 == N * 8 && Nd == N * d && Nd8 == Nd * 8)] +#[hax_lib::ensures(|result| + hax_lib::forall(|i: usize| hax_lib::implies(i < N8, result[i] < (1u16 << d))))] +#[allow(non_upper_case_globals)] +pub fn byte_decode_generic( + b: &[u8; Nd], + d: usize, +) -> [u16; N8] { + hax_lib::debug_assert!( + d <= BITS_PER_COEFFICIENT && N8 == N * 8 && Nd == N * d && Nd8 == Nd * 8 + ); + let bv: [bool; Nd8] = bytes_to_bits::(&b); + bitvector_to_bounded_ints(&bv, d) +} + +#[hax_lib::requires(d > 0 && d <= BITS_PER_COEFFICIENT && b.len() == 32 * d && D32 == 32 * d && D256 == 256 * d)] +#[hax_lib::ensures(|result| hax_lib::forall(|i: usize| hax_lib::implies(i < 256, result[i].val < (1u16 << d))))] +pub fn byte_decode(b: &[u8; D32], d: usize) -> Polynomial { + hax_lib::debug_assert!( + d <= BITS_PER_COEFFICIENT && b.len() == 32 * d && D32 == 32 * d && D256 == 256 * d + ); + let decoded = byte_decode_generic::<32, 256, D32, D256>(b, d); + createi(|i| FieldElement::new(decoded[i] % FIELD_MODULUS)) +} + +/// Encode each polynomial of an NTT vector at 12 bits per coefficient, +/// writing the result into `out`. Companion to value-returning +/// [`serialize_secret_key`]. Mirrors the existing +/// [`byte_encode_into`] convention: the `_into` form is the canonical +/// primitive; the value-returning form is a thin allocating wrapper. +#[hax_lib::requires(RANK <= 4 && out.len() == RANK * BYTES_PER_RING_ELEMENT)] +pub fn serialize_secret_key_into(vector: &Vector, out: &mut [u8]) { + hax_lib::debug_assert!(out.len() == RANK * BYTES_PER_RING_ELEMENT); + for i in 0..RANK { + hax_lib::loop_invariant!(|_i: usize| out.len() == RANK * BYTES_PER_RING_ELEMENT); + let encoded = byte_encode::<{ 32 * 12 }, { 256 * 12 }>(vector[i], 12); + out[i * BYTES_PER_RING_ELEMENT..(i + 1) * BYTES_PER_RING_ELEMENT] + .copy_from_slice(&encoded); + } +} + +#[hax_lib::requires(RANK <= 4 && encoded.len() == RANK * BYTES_PER_RING_ELEMENT)] +pub fn vector_decode_12(encoded: &[u8]) -> Vector { + hax_lib::debug_assert!(encoded.len() == RANK * BYTES_PER_RING_ELEMENT); + createi(|i| { + let start = i * BYTES_PER_RING_ELEMENT; + let chunk: &[u8; 384] = encoded[start..start + 384].try_into().unwrap(); + byte_decode::<{ 32 * 12 }, { 256 * 12 }>(chunk, 12) + }) +} + +#[hax_lib::requires((d == 1 || d == 4 || d == 5 || d == 10 || d == 11 || d == 12) && out.len() == 32 * d)] +pub fn byte_encode_into(p: Polynomial, d: usize, out: &mut [u8]) { + hax_lib::debug_assert!(d <= BITS_PER_COEFFICIENT && out.len() == 32 * d); + match d { + 1 => out.copy_from_slice(&byte_encode::<32, 256>(p, 1)), + 4 => out.copy_from_slice(&byte_encode::<128, 1024>(p, 4)), + 5 => out.copy_from_slice(&byte_encode::<160, 1280>(p, 5)), + 10 => out.copy_from_slice(&byte_encode::<320, 2560>(p, 10)), + 11 => out.copy_from_slice(&byte_encode::<352, 2816>(p, 11)), + 12 => out.copy_from_slice(&byte_encode::<384, 3072>(p, 12)), + _ => panic!("unsupported d={}", d), + } +} + +#[hax_lib::requires((d == 1 || d == 4 || d == 5 || d == 10 || d == 11 || d == 12) && b.len() == 32 * d)] +pub fn byte_decode_dyn(b: &[u8], d: usize) -> Polynomial { + hax_lib::debug_assert!(d <= BITS_PER_COEFFICIENT && b.len() == 32 * d); + match d { + 1 => byte_decode::<32, 256>(b.try_into().unwrap(), 1), + 4 => byte_decode::<128, 1024>(b.try_into().unwrap(), 4), + 5 => byte_decode::<160, 1280>(b.try_into().unwrap(), 5), + 10 => byte_decode::<320, 2560>(b.try_into().unwrap(), 10), + 11 => byte_decode::<352, 2816>(b.try_into().unwrap(), 11), + 12 => byte_decode::<384, 3072>(b.try_into().unwrap(), 12), + _ => panic!("unsupported d={}", d), + } +} + +// ── Fused compress+serialize / deserialize+decompress functions ── +// These match the implementation's decomposition in serialize.rs, +// composing existing spec operations. + +/// Compress a polynomial to 1 bit per coefficient, then serialize to bytes. +/// Corresponds to `compress_then_serialize_message` in the implementation. +/// +/// Used for encoding/decoding the message in K-PKE. +pub fn compress_then_serialize_message(re: Polynomial) -> [u8; 32] { + byte_encode::<32, 256>(compress(re, 1), 1) +} + +/// Deserialize bytes to a polynomial, then decompress from 1 bit per coefficient. +/// Corresponds to `deserialize_then_decompress_message` in the implementation. +pub fn deserialize_then_decompress_message(serialized: &[u8; 32]) -> Polynomial { + decompress(byte_decode::<32, 256>(serialized, 1), 1) +} + +/// Serialize a polynomial with 12-bit coefficients (no compression). +/// Corresponds to `serialize_uncompressed_ring_element` in the implementation. +pub fn serialize_uncompressed_ring_element(re: &Polynomial) -> [u8; BYTES_PER_RING_ELEMENT] { + byte_encode::<{ 32 * 12 }, { 256 * 12 }>(*re, 12) +} + +/// Deserialize bytes to a polynomial with 12-bit coefficients (no decompression). +/// Corresponds to `deserialize_to_uncompressed_ring_element` in the implementation. +pub fn deserialize_to_uncompressed_ring_element( + serialized: &[u8; BYTES_PER_RING_ELEMENT], +) -> Polynomial { + byte_decode::<{ 32 * 12 }, { 256 * 12 }>(serialized, 12) +} + +/// Compress each polynomial in u to du bits, then serialize, writing +/// into `out`. Companion to value-returning [`compress_then_serialize_u`]. +/// Mirrors the [`byte_encode_into`] / [`serialize_secret_key_into`] +/// convention: the `_into` form is the canonical primitive. +#[hax_lib::requires( + RANK <= 4 + && (du == 10 || du == 11) + && out.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * du) / 8 +)] +pub fn compress_then_serialize_u_into( + u: &Vector, + du: usize, + out: &mut [u8], +) { + let du_poly_size = (COEFFICIENTS_IN_RING_ELEMENT * du) / 8; + for i in 0..RANK { + hax_lib::loop_invariant!( + |_i: usize| out.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * du) / 8 + ); + byte_encode_into( + compress(u[i], du), + du, + &mut out[i * du_poly_size..(i + 1) * du_poly_size], + ); + } +} + +/// Compress each polynomial in u to du bits, then serialize. +/// Corresponds to `compress_then_serialize_ring_element_u` in the implementation. +/// Thin allocating wrapper around [`compress_then_serialize_u_into`]. +/// +/// Note: The implementation dispatches on the compression factor (10 or 11). +/// In the spec we use the generic compress + byte_encode path. +#[hax_lib::requires(RANK <= 4 && (du == 10 || du == 11) && U_SIZE == (RANK * COEFFICIENTS_IN_RING_ELEMENT * du) / 8)] +pub fn compress_then_serialize_u( + u: &Vector, + du: usize, +) -> [u8; U_SIZE] { + let mut out = [0u8; U_SIZE]; + compress_then_serialize_u_into::(u, du, &mut out); + out +} + +/// Compress v to dv bits, then serialize. +/// Corresponds to `compress_then_serialize_ring_element_v` in the implementation. +#[hax_lib::requires((dv == 4 || dv == 5) && V_SIZE == (COEFFICIENTS_IN_RING_ELEMENT * dv) / 8)] +pub fn compress_then_serialize_v(v: &Polynomial, dv: usize) -> [u8; V_SIZE] { + let mut out = [0u8; V_SIZE]; + byte_encode_into(compress(*v, dv), dv, &mut out); + out +} + +/// Deserialize and decompress u from ciphertext bytes. +/// Corresponds to `deserialize_then_decompress_ring_element_u` in the implementation. +#[hax_lib::requires(RANK <= 4 && (du == 10 || du == 11) && ciphertext.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * du) / 8)] +pub fn deserialize_then_decompress_u( + ciphertext: &[u8], + du: usize, +) -> Vector { + let du_poly_size = (COEFFICIENTS_IN_RING_ELEMENT * du) / 8; + createi(|i| { + let start = i * du_poly_size; + decompress( + byte_decode_dyn(&ciphertext[start..start + du_poly_size], du), + du, + ) + }) +} + +/// Deserialize, decompress, then NTT-transform u from ciphertext bytes. +/// Composes [`deserialize_then_decompress_u`] with [`vector_ntt`]. Used +/// by `ind_cpa::decrypt` and matches the libcrux-impl +/// `deserialize_then_decompress_u` function (which fuses the NTT into +/// the per-element decompress loop). +#[hax_lib::requires(RANK <= 4 && (du == 10 || du == 11) && ciphertext.len() == (RANK * COEFFICIENTS_IN_RING_ELEMENT * du) / 8)] +pub fn deserialize_then_decompress_u_then_ntt( + ciphertext: &[u8], + du: usize, +) -> Vector { + vector_ntt(deserialize_then_decompress_u::(ciphertext, du)) +} + +/// Deserialize and decompress v from ciphertext bytes. +/// Corresponds to `deserialize_then_decompress_ring_element_v` in the implementation. +#[hax_lib::requires((dv == 4 || dv == 5) && serialized.len() == (COEFFICIENTS_IN_RING_ELEMENT * dv) / 8)] +pub fn deserialize_then_decompress_v(serialized: &[u8], dv: usize) -> Polynomial { + decompress(byte_decode_dyn(serialized, dv), dv) +} + +/// Deserialize ring elements from a byte slice, reducing mod q. +/// Corresponds to `deserialize_ring_elements_reduced` in the implementation. +/// +/// This is equivalent to `vector_decode_12` but named to match the implementation. +#[hax_lib::requires(RANK <= 4 && encoded.len() == RANK * BYTES_PER_RING_ELEMENT)] +pub fn deserialize_ring_elements_reduced(encoded: &[u8]) -> Vector { + vector_decode_12::(encoded) +} + +/// Serialize a vector of polynomials with 12-bit coefficients. +/// Corresponds to `serialize_secret_key` / `serialize_vector` in the implementation. +#[hax_lib::requires(RANK <= 4 && T_SIZE == RANK * BYTES_PER_RING_ELEMENT)] +pub fn serialize_secret_key( + vector: &Vector, +) -> [u8; T_SIZE] { + createi(|k| { + let i = k / BYTES_PER_RING_ELEMENT; + let j = k % BYTES_PER_RING_ELEMENT; + let encoded = byte_encode::<{ 32 * 12 }, { 256 * 12 }>(vector[i], 12); + encoded[j] + }) +} + +/// Serialize a public key: encode the NTT vector t̂ concatenated with the seed ρ. +/// Corresponds to `serialize_public_key` in the implementation's `ind_cpa.rs`. +/// +/// Layout: bytes [0, RANK*384) hold byte_encode(t_as_ntt[i], 12) per polynomial, +/// bytes [RANK*384, EK_SIZE) hold seed_for_A[0..32]. +/// Per-index body of the `createi` in `serialize_public_key`, hoisted +/// to dodge Aeneas issue +/// https://github.com/AeneasVerif/aeneas/issues/924. +#[allow(non_snake_case)] +fn serialize_public_key_at( + t_as_ntt: &Vector, + seed_for_A: &[u8], + k: usize, +) -> u8 { + if k < RANK * BYTES_PER_RING_ELEMENT { + let i = k / BYTES_PER_RING_ELEMENT; + let j = k % BYTES_PER_RING_ELEMENT; + let encoded = byte_encode::<{ 32 * 12 }, { 256 * 12 }>(t_as_ntt[i], 12); + encoded[j] + } else { + seed_for_A[k - RANK * BYTES_PER_RING_ELEMENT] + } +} + +#[allow(non_snake_case)] +#[hax_lib::requires(RANK <= 4 && EK_SIZE == RANK * BYTES_PER_RING_ELEMENT + 32 && seed_for_A.len() >= 32)] +pub fn serialize_public_key( + t_as_ntt: &Vector, + seed_for_A: &[u8], +) -> [u8; EK_SIZE] { + createi(|k| serialize_public_key_at::(t_as_ntt, seed_for_A, k)) +} + + +#[cfg(test)] +mod tests { + use proptest::prelude::*; + + use super::*; + use crate::compress::tests::arb_ring_element; + use crate::parameters::FIELD_MODULUS; + + #[test] + fn bytes_to_bits_known_vector() { + // 0xA5 = 10100101 -> bits in LE: [1,0,1,0,0,1,0,1] + // 0x3C = 00111100 -> bits in LE: [0,0,1,1,1,1,0,0] + let bytes = [0xA5u8, 0x3C]; + let bits: [bool; 16] = bytes_to_bits(&bytes); + assert_eq!( + bits[0..8], + [true, false, true, false, false, true, false, true] + ); + assert_eq!( + bits[8..16], + [false, false, true, true, true, true, false, false] + ); + } + + #[test] + fn bits_to_bytes_known_vector() { + let mut bits = [false; 16]; + // Encode 0xA5 = LE bits [1,0,1,0,0,1,0,1] + bits[0] = true; + bits[2] = true; + bits[5] = true; + bits[7] = true; + // Encode 0x3C = LE bits [0,0,1,1,1,1,0,0] + bits[10] = true; + bits[11] = true; + bits[12] = true; + bits[13] = true; + + let bytes: [u8; 2] = bits_to_bytes(&bits); + assert_eq!(bytes, [0xA5, 0x3C]); + } + + #[test] + fn bytes_to_bits_all_zeros() { + let bytes = [0u8; 4]; + let bits: [bool; 32] = bytes_to_bits(&bytes); + assert!(bits.iter().all(|&b| !b)); + } + + #[test] + fn bytes_to_bits_all_ones() { + let bytes = [0xFFu8; 4]; + let bits: [bool; 32] = bytes_to_bits(&bytes); + assert!(bits.iter().all(|&b| b)); + } + + #[test] + fn bitvector_from_bounded_ints_known_vector() { + // d=4, integers [5, 11] -> bits for 5=0101 and 11=1011 + let ints: [u16; 2] = [5, 11]; + let bits: [bool; 8] = bitvector_from_bounded_ints(&ints, 4); + // 5 in LE bits: [1,0,1,0] + assert_eq!(bits[0..4], [true, false, true, false]); + // 11 in LE bits: [1,1,0,1] + assert_eq!(bits[4..8], [true, true, false, true]); + } + + #[test] + fn bitvector_to_bounded_ints_known_vector() { + // Reverse of above: bits for 5 and 11 with d=4 + let bits = [true, false, true, false, true, true, false, true]; + let ints: [u16; 2] = bitvector_to_bounded_ints(&bits, 4); + assert_eq!(ints, [5, 11]); + } + + #[test] + fn bitvector_roundtrip() { + // from_bounded -> to_bounded should recover original values + let ints: [u16; 4] = [0, 7, 15, 3]; + let bits: [bool; 16] = bitvector_from_bounded_ints(&ints, 4); + let recovered: [u16; 4] = bitvector_to_bounded_ints(&bits, 4); + assert_eq!(recovered, ints); + } + + #[test] + fn byte_encode_decode_roundtrip_d1() { + // d=1: coefficients are 0 or 1 + let poly: Polynomial = createi(|i| FieldElement::new((i % 2) as u16)); + let encoded: [u8; 32] = byte_encode::<32, 256>(poly, 1); + let decoded: Polynomial = byte_decode::<32, 256>(&encoded, 1); + assert_eq!(decoded, poly); + } + + #[test] + fn byte_encode_decode_roundtrip_d4() { + let poly: Polynomial = createi(|i| FieldElement::new((i % 16) as u16)); + let encoded: [u8; 128] = byte_encode::<128, 1024>(poly, 4); + let decoded: Polynomial = byte_decode::<128, 1024>(&encoded, 4); + assert_eq!(decoded, poly); + } + + #[test] + fn byte_encode_decode_roundtrip_d10() { + let poly: Polynomial = createi(|i| FieldElement::new((i % 1024) as u16)); + let encoded: [u8; 320] = byte_encode::<320, 2560>(poly, 10); + let decoded: Polynomial = byte_decode::<320, 2560>(&encoded, 10); + assert_eq!(decoded, poly); + } + + #[test] + fn byte_decode_d12_reduces_mod_q() { + // Encode a polynomial with values in [0, q-1], decode it, verify reduction + let poly: Polynomial = createi(|i| FieldElement::new((i as u16 * 13) % FIELD_MODULUS)); + let encoded: [u8; 384] = byte_encode::<384, 3072>(poly, 12); + let decoded: Polynomial = byte_decode::<384, 3072>(&encoded, 12); + // All decoded values should be in [0, q-1] + for (i, coeff) in decoded.iter().enumerate() { + assert!( + coeff.val < FIELD_MODULUS, + "decoded[{}] = {} not in [0, q)", + i, + coeff.val + ); + } + assert_eq!(decoded, poly); + } + + #[test] + fn byte_encode_known_vector_d1() { + // All zeros + let poly = [FieldElement::new(0); 256]; + let encoded: [u8; 32] = byte_encode::<32, 256>(poly, 1); + assert_eq!(encoded, [0u8; 32]); + + // All ones + let poly = [FieldElement::new(1); 256]; + let encoded: [u8; 32] = byte_encode::<32, 256>(poly, 1); + assert_eq!(encoded, [0xFFu8; 32]); + } + + #[test] + fn byte_decode_known_vector_d1() { + let bytes = [0u8; 32]; + let decoded: Polynomial = byte_decode::<32, 256>(&bytes, 1); + assert!(decoded.iter().all(|c| c.val == 0)); + + let bytes = [0xFFu8; 32]; + let decoded: Polynomial = byte_decode::<32, 256>(&bytes, 1); + assert!(decoded.iter().all(|c| c.val == 1)); + } + + proptest! { + #[test] + fn bytes_to_bits_and_back_roundtrip(b0 in any::(), b1 in any::(), b2 in any::(), b3 in any::()) { + let bytes = [b0, b1, b2, b3]; + let bits: [bool; 32] = bytes_to_bits(&bytes); + let recovered: [u8; 4] = bits_to_bytes(&bits); + assert_eq!(recovered, bytes); + } + + #[test] + fn deserialize_is_left_inverse_of_serialize_when_no_compression(ring_element in arb_ring_element(12)) { + let ring_element_serialized: [u8; 32 * 12]= byte_encode::<{32 * 12}, {256 * 12}>(ring_element, 12); + assert_eq!(ring_element, byte_decode::<{32 * 12}, {256 * 12}>(&ring_element_serialized, 12)); + } + + #[test] + fn serialize_is_sometimes_left_inverse_of_deserialize_when_no_compression(ring_element in arb_ring_element(12)) { + let ring_element_serialized: [u8; 32 * 12]= byte_encode::<{32 * 12}, {256 * 12}>(ring_element, 12); + assert_eq!(ring_element_serialized, byte_encode::<{32 * 12}, {256 * 12}>(byte_decode::<{32 * 12}, {256 * 12}>(&ring_element_serialized, 12), 12)); + } + } +} diff --git a/specs/ml-kem/tests/spec_properties.rs b/specs/ml-kem/tests/spec_properties.rs new file mode 100644 index 0000000000..d8ee7581d2 --- /dev/null +++ b/specs/ml-kem/tests/spec_properties.rs @@ -0,0 +1,60 @@ +//! Spec-internal property tests for `hacspec_ml_kem` that are *not* already +//! covered by the inline `#[cfg(test)]` unit tests in `src/`. +//! +//! These exercise the public spec API without any reference to a concrete +//! implementation. Roundtrip / range properties that the in-module unit tests +//! already cover (byte_encode/decode, compress/decompress, NTT roundtrip and +//! multiplication, IND-CPA encrypt/decrypt, rejection sampling of zeros) are +//! intentionally not duplicated here. +//! +//! Spec ↔ impl byte equality lives on the impl side +//! (`libcrux-ml-kem/tests/cross_spec*.rs`). + +mod serialization { + use hacspec_ml_kem::serialize::*; + + /// `compress_then_serialize_message` ∘ `deserialize_then_decompress_message` + /// is the identity on 32-byte messages (the 1-bit message encoding). + #[test] + fn message_serialize_roundtrip() { + let msg_bytes = [0xABu8; 32]; + let poly = deserialize_then_decompress_message(&msg_bytes); + let reencoded = compress_then_serialize_message(poly); + assert_eq!(msg_bytes, reencoded); + } +} + +mod sampling_tests { + use hacspec_ml_kem::parameters::*; + use hacspec_ml_kem::sampling::*; + + /// CBD with eta=2: all coefficients should be in {0, 1, 2, 3327, 3328}. + #[test] + fn cbd_eta2_range() { + let bytes = [0x55u8; 128]; // deterministic pattern + let poly = sample_poly_cbd::<128, 1024>(2, &bytes); + for (i, coeff) in poly.iter().enumerate() { + assert!( + coeff.val <= 2 || coeff.val >= FIELD_MODULUS - 2, + "CBD eta=2 coefficient {} out of range: {}", + i, + coeff.val + ); + } + } + + /// CBD with eta=3: all coefficients should be in {0,1,2,3, 3326,3327,3328}. + #[test] + fn cbd_eta3_range() { + let bytes = [0xAAu8; 192]; // deterministic pattern + let poly = sample_poly_cbd::<192, 1536>(3, &bytes); + for (i, coeff) in poly.iter().enumerate() { + assert!( + coeff.val <= 3 || coeff.val >= FIELD_MODULUS - 3, + "CBD eta=3 coefficient {} out of range: {}", + i, + coeff.val + ); + } + } +} From 99c99386bb5b20309f46db1d67537b44589a130d Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 09:14:01 +0000 Subject: [PATCH 2/8] [ml-kem] Add cross-spec equivalence tests against the implementation --- libcrux-ml-kem/Cargo.toml | 16 +- libcrux-ml-kem/tests/cross_spec.rs | 297 +++++++++++++++++++ libcrux-ml-kem/tests/cross_spec_proptests.rs | 162 ++++++++++ 3 files changed, 472 insertions(+), 3 deletions(-) create mode 100644 libcrux-ml-kem/tests/cross_spec.rs create mode 100644 libcrux-ml-kem/tests/cross_spec_proptests.rs diff --git a/libcrux-ml-kem/Cargo.toml b/libcrux-ml-kem/Cargo.toml index efd8ccbb9d..28d7fa81e3 100644 --- a/libcrux-ml-kem/Cargo.toml +++ b/libcrux-ml-kem/Cargo.toml @@ -81,11 +81,21 @@ serde_json = { version = "1.0" } serde = { version = "1.0", features = ["derive"] } hex = { version = "0.4.3", features = ["serde"] } criterion = "0.8" -libcrux-traits = { workspace = true, features = [ - "generic-tests", -] } +libcrux-traits = { workspace = true, features = ["generic-tests"] } libcrux-kats = { path = "../crates/testing/kats", features = ["mlkem"] } +# Cross-spec equivalence tests against the hacspec ML-KEM +hacspec_ml_kem = { path = "../specs/ml-kem" } +proptest = "1" + +[[test]] +name = "cross_spec" +required-features = ["mlkem512", "mlkem768", "mlkem1024"] + +[[test]] +name = "cross_spec_proptests" +required-features = ["mlkem512", "mlkem768", "mlkem1024"] + [[bench]] name = "ml-kem" harness = false diff --git a/libcrux-ml-kem/tests/cross_spec.rs b/libcrux-ml-kem/tests/cross_spec.rs new file mode 100644 index 0000000000..e2081b425a --- /dev/null +++ b/libcrux-ml-kem/tests/cross_spec.rs @@ -0,0 +1,297 @@ +//! Cross-spec equivalence tests between `libcrux-ml-kem` and `hacspec_ml_kem`. +//! +//! These tests assert byte-identical agreement between the optimized impl and +//! the FIPS-203 hacspec reference on the IND-CCA surface (KeyGen / Encaps / +//! Decaps) at all three parameter sets. + +use hacspec_ml_kem::{ + self as spec, ML_KEM_1024, ML_KEM_1024_CT_SIZE, ML_KEM_1024_DK_PKE_SIZE, ML_KEM_1024_DK_SIZE, + ML_KEM_1024_EK_SIZE, ML_KEM_1024_J_INPUT_SIZE, ML_KEM_1024_U_SIZE, ML_KEM_1024_V_SIZE, + ML_KEM_512, ML_KEM_512_CT_SIZE, ML_KEM_512_DK_PKE_SIZE, ML_KEM_512_DK_SIZE, ML_KEM_512_EK_SIZE, + ML_KEM_512_J_INPUT_SIZE, ML_KEM_512_U_SIZE, ML_KEM_512_V_SIZE, ML_KEM_768, ML_KEM_768_CT_SIZE, + ML_KEM_768_DK_PKE_SIZE, ML_KEM_768_DK_SIZE, ML_KEM_768_EK_SIZE, ML_KEM_768_J_INPUT_SIZE, + ML_KEM_768_U_SIZE, ML_KEM_768_V_SIZE, +}; + +// --------------------------------------------------------------------------- +// Const-generic size assertions — fail fast on any drift between the impl's +// hard-coded sizes (per FIPS-203 §8 table) and the spec's exported constants. +// --------------------------------------------------------------------------- + +#[cfg(feature = "mlkem512")] +#[test] +fn size_constants_agree_512() { + use libcrux_ml_kem::mlkem512::*; + assert_eq!(MlKem512PublicKey::len(), ML_KEM_512_EK_SIZE); + assert_eq!(MlKem512PrivateKey::len(), ML_KEM_512_DK_SIZE); + assert_eq!(MlKem512Ciphertext::len(), ML_KEM_512_CT_SIZE); +} + +#[cfg(feature = "mlkem768")] +#[test] +fn size_constants_agree_768() { + use libcrux_ml_kem::mlkem768::*; + assert_eq!(MlKem768PublicKey::len(), ML_KEM_768_EK_SIZE); + assert_eq!(MlKem768PrivateKey::len(), ML_KEM_768_DK_SIZE); + assert_eq!(MlKem768Ciphertext::len(), ML_KEM_768_CT_SIZE); +} + +#[cfg(feature = "mlkem1024")] +#[test] +fn size_constants_agree_1024() { + use libcrux_ml_kem::mlkem1024::*; + assert_eq!(MlKem1024PublicKey::len(), ML_KEM_1024_EK_SIZE); + assert_eq!(MlKem1024PrivateKey::len(), ML_KEM_1024_DK_SIZE); + assert_eq!(MlKem1024Ciphertext::len(), ML_KEM_1024_CT_SIZE); +} + +// --------------------------------------------------------------------------- +// Seed tables — deterministic vectors exercised by every variant. The last +// entry in each is the first NIST ML-KEM KAT seed (identical across the +// 512/768/1024 KAT files), so the cross-comparison also runs on a known NIST +// vector. (Agreement with the published KAT outputs themselves is checked in +// `nistkats.rs`.) +// --------------------------------------------------------------------------- + +const KEYGEN_SEEDS: &[[u8; 64]] = &[ + [0x00; 64], + [0xFF; 64], + [0xAA; 64], + { + let mut s = [0u8; 64]; + let mut i = 0usize; + while i < 64 { + s[i] = i as u8; + i += 1; + } + s + }, + // First NIST ML-KEM KAT keygen seed. + [ + 0x7c, 0x99, 0x35, 0xa0, 0xb0, 0x76, 0x94, 0xaa, 0x0c, 0x6d, 0x10, 0xe4, 0xdb, 0x6b, 0x1a, + 0xdd, 0x2f, 0xd8, 0x1a, 0x25, 0xcc, 0xb1, 0x48, 0x03, 0x2d, 0xcd, 0x73, 0x99, 0x36, 0x73, + 0x7f, 0x2d, 0x86, 0x26, 0xed, 0x79, 0xd4, 0x51, 0x14, 0x08, 0x00, 0xe0, 0x3b, 0x59, 0xb9, + 0x56, 0xf8, 0x21, 0x0e, 0x55, 0x60, 0x67, 0x40, 0x7d, 0x13, 0xdc, 0x90, 0xfa, 0x9e, 0x8b, + 0x87, 0x2b, 0xfb, 0x8f, + ], +]; +const ENCAPS_SEEDS: &[[u8; 32]] = &[ + [0x00; 32], + [0xFF; 32], + [0xCD; 32], + // First NIST ML-KEM KAT encaps seed. + [ + 0x14, 0x7c, 0x03, 0xf7, 0xa5, 0xbe, 0xbb, 0xa4, 0x06, 0xc8, 0xfa, 0xe1, 0x87, 0x4d, 0x7f, + 0x13, 0xc8, 0x0e, 0xfe, 0x79, 0xa3, 0xa9, 0xa8, 0x74, 0xcc, 0x09, 0xfe, 0x76, 0xf6, 0x99, + 0x76, 0x15, + ], +]; + +// --------------------------------------------------------------------------- +// IND-CCA byte-level cross-comparison (impl vs spec). +// +// Generated per parameter set. Each variant gets: +// 1. keygen_matches_spec — pk, sk byte-equal. +// 2. encapsulate_matches_spec — ct, ss byte-equal on a fresh keypair. +// 3. full_roundtrip_matches_spec — encaps then decaps; impl ss matches +// spec ss on both sides. +// 4. decaps_rejection_matches_spec — flipping a ct byte triggers implicit +// rejection; impl ss matches spec ss +// (i.e., both produce J(s, c')). +// --------------------------------------------------------------------------- + +macro_rules! cross_spec_tests { + ( + $mod_name:ident, + $feature:literal, + $impl_mod:path, + $k:literal, + $params:expr, + $ek:expr, $dk:expr, $dk_pke:expr, + $u:expr, $v:expr, $ct:expr, $j:expr + ) => { + #[cfg(feature = $feature)] + mod $mod_name { + use super::*; + use $impl_mod as impl_mod; + + #[test] + fn keygen_matches_spec() { + for (i, randomness) in KEYGEN_SEEDS.iter().enumerate() { + let (spec_ek, spec_dk) = + spec::generate_keypair::<$k, $ek, $dk, $dk_pke>(&$params, randomness) + .unwrap_or_else(|_| { + panic!( + concat!( + stringify!($mod_name), + " spec keygen failed for seed index {}; ", + "consider replacing KEYGEN_SEEDS[{}]" + ), + i, i + ) + }); + + let kp = impl_mod::generate_key_pair(*randomness); + + assert_eq!( + &kp.public_key().as_slice()[..], + &spec_ek[..], + concat!(stringify!($mod_name), " ek mismatch (seed idx={})"), + i + ); + assert_eq!( + &kp.private_key().as_slice()[..], + &spec_dk[..], + concat!(stringify!($mod_name), " dk mismatch (seed idx={})"), + i + ); + } + } + + #[test] + fn encapsulate_matches_spec() { + let keygen_rand = [1u8; 64]; + let (spec_ek, _) = + spec::generate_keypair::<$k, $ek, $dk, $dk_pke>(&$params, &keygen_rand) + .expect("spec keygen failed"); + let kp = impl_mod::generate_key_pair(keygen_rand); + + for (i, encaps_rand) in ENCAPS_SEEDS.iter().enumerate() { + let (spec_ss, spec_ct) = + spec::encapsulate::<$k, $ek, $u, $v, $ct>(&$params, &spec_ek, encaps_rand) + .expect("spec encaps failed"); + + let (impl_ct, impl_ss) = impl_mod::encapsulate(kp.public_key(), *encaps_rand); + + assert_eq!( + impl_ct.as_ref(), + &spec_ct[..], + concat!(stringify!($mod_name), " ct mismatch (encaps idx={})"), + i + ); + assert_eq!( + &impl_ss[..], + &spec_ss[..], + concat!(stringify!($mod_name), " ss mismatch (encaps idx={})"), + i + ); + } + } + + #[test] + fn full_roundtrip_matches_spec() { + for (i, randomness) in KEYGEN_SEEDS.iter().enumerate() { + let encaps_rand = ENCAPS_SEEDS[i % ENCAPS_SEEDS.len()]; + + let (spec_ek, spec_dk) = + spec::generate_keypair::<$k, $ek, $dk, $dk_pke>(&$params, randomness) + .unwrap_or_else(|_| { + panic!( + concat!(stringify!($mod_name), " spec keygen failed for seed idx {}"), + i + ) + }); + let (spec_ss, spec_ct) = + spec::encapsulate::<$k, $ek, $u, $v, $ct>(&$params, &spec_ek, &encaps_rand) + .expect("spec encaps failed"); + let spec_ss_d = + spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( + &$params, &spec_dk, &spec_ct, + ) + .expect("spec decaps failed"); + + let kp = impl_mod::generate_key_pair(*randomness); + let (impl_ct, impl_ss_e) = impl_mod::encapsulate(kp.public_key(), encaps_rand); + let impl_ss_d = impl_mod::decapsulate(kp.private_key(), &impl_ct); + + assert_eq!(impl_ct.as_ref(), &spec_ct[..], "ct (seed idx={})", i); + assert_eq!(&impl_ss_e[..], &spec_ss[..], "encaps ss"); + assert_eq!(&impl_ss_d[..], &spec_ss_d[..], "decaps ss"); + assert_eq!(&impl_ss_e[..], &impl_ss_d[..], "impl encaps/decaps ss differ"); + } + } + + #[test] + fn decaps_rejection_matches_spec() { + // Implicit rejection: tampered ct should produce J(s, c'). + // Both impl and spec implement the FIPS-203 implicit rejection; + // they should agree byte-for-byte. + let keygen_rand = KEYGEN_SEEDS[1]; // 0xFF seed + let encaps_rand = ENCAPS_SEEDS[0]; + + let (spec_ek, spec_dk) = + spec::generate_keypair::<$k, $ek, $dk, $dk_pke>(&$params, &keygen_rand) + .expect("spec keygen failed"); + let (_spec_ss, spec_ct) = + spec::encapsulate::<$k, $ek, $u, $v, $ct>(&$params, &spec_ek, &encaps_rand) + .expect("spec encaps failed"); + + let kp = impl_mod::generate_key_pair(keygen_rand); + + // Tamper one byte of the ciphertext. + let mut tampered_ct: [u8; $ct] = spec_ct; + tampered_ct[7] ^= 0xA5; + + let spec_ss_rej = + spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( + &$params, &spec_dk, &tampered_ct, + ) + .expect("spec decaps (rejection branch) failed"); + + let impl_tampered_ct = tampered_ct.into(); + let impl_ss_rej = impl_mod::decapsulate(kp.private_key(), &impl_tampered_ct); + + assert_eq!( + &impl_ss_rej[..], + &spec_ss_rej[..], + concat!(stringify!($mod_name), " implicit rejection ss mismatch") + ); + } + } + }; +} + +cross_spec_tests!( + mlkem512_cross, + "mlkem512", + libcrux_ml_kem::mlkem512, + 2, + ML_KEM_512, + { ML_KEM_512_EK_SIZE }, + { ML_KEM_512_DK_SIZE }, + { ML_KEM_512_DK_PKE_SIZE }, + { ML_KEM_512_U_SIZE }, + { ML_KEM_512_V_SIZE }, + ML_KEM_512_CT_SIZE, + { ML_KEM_512_J_INPUT_SIZE } +); + +cross_spec_tests!( + mlkem768_cross, + "mlkem768", + libcrux_ml_kem::mlkem768, + 3, + ML_KEM_768, + { ML_KEM_768_EK_SIZE }, + { ML_KEM_768_DK_SIZE }, + { ML_KEM_768_DK_PKE_SIZE }, + { ML_KEM_768_U_SIZE }, + { ML_KEM_768_V_SIZE }, + ML_KEM_768_CT_SIZE, + { ML_KEM_768_J_INPUT_SIZE } +); + +cross_spec_tests!( + mlkem1024_cross, + "mlkem1024", + libcrux_ml_kem::mlkem1024, + 4, + ML_KEM_1024, + { ML_KEM_1024_EK_SIZE }, + { ML_KEM_1024_DK_SIZE }, + { ML_KEM_1024_DK_PKE_SIZE }, + { ML_KEM_1024_U_SIZE }, + { ML_KEM_1024_V_SIZE }, + ML_KEM_1024_CT_SIZE, + { ML_KEM_1024_J_INPUT_SIZE } +); diff --git a/libcrux-ml-kem/tests/cross_spec_proptests.rs b/libcrux-ml-kem/tests/cross_spec_proptests.rs new file mode 100644 index 0000000000..7cd867c3a8 --- /dev/null +++ b/libcrux-ml-kem/tests/cross_spec_proptests.rs @@ -0,0 +1,162 @@ +//! Property-based cross-spec tests: random seeds in × byte equality out. +//! +//! Complements the deterministic table in `cross_spec.rs`. 32 cases per +//! variant by default — keep the per-variant budget under a few seconds so +//! the suite stays usable on every `cargo test`. Tune via +//! `PROPTEST_CASES` env var for deeper smoke runs. +//! +//! Unlucky-seed handling: `hacspec_ml_kem::generate_keypair` can return +//! `Err(BadRejectionSamplingRandomnessError)`. We `prop_assume!` past such +//! seeds. + +use proptest::prelude::*; + +use hacspec_ml_kem::{ + self as spec, ML_KEM_1024, ML_KEM_1024_CT_SIZE, ML_KEM_1024_DK_PKE_SIZE, ML_KEM_1024_DK_SIZE, + ML_KEM_1024_EK_SIZE, ML_KEM_1024_J_INPUT_SIZE, ML_KEM_1024_U_SIZE, ML_KEM_1024_V_SIZE, + ML_KEM_512, ML_KEM_512_CT_SIZE, ML_KEM_512_DK_PKE_SIZE, ML_KEM_512_DK_SIZE, ML_KEM_512_EK_SIZE, + ML_KEM_512_J_INPUT_SIZE, ML_KEM_512_U_SIZE, ML_KEM_512_V_SIZE, ML_KEM_768, ML_KEM_768_CT_SIZE, + ML_KEM_768_DK_PKE_SIZE, ML_KEM_768_DK_SIZE, ML_KEM_768_EK_SIZE, ML_KEM_768_J_INPUT_SIZE, + ML_KEM_768_U_SIZE, ML_KEM_768_V_SIZE, +}; + +/// Strategy: arbitrary 64-byte arrays for KeyGen randomness. +fn keygen_seed_strategy() -> impl Strategy { + proptest::collection::vec(any::(), 64..=64).prop_map(|v| { + let mut a = [0u8; 64]; + a.copy_from_slice(&v); + a + }) +} + +/// Strategy: arbitrary 32-byte arrays for Encaps randomness. +fn encaps_seed_strategy() -> impl Strategy { + proptest::collection::vec(any::(), 32..=32).prop_map(|v| { + let mut a = [0u8; 32]; + a.copy_from_slice(&v); + a + }) +} + +proptest! { + #![proptest_config(ProptestConfig { + cases: 32, + .. ProptestConfig::default() + })] + + /// ML-KEM-512: spec ↔ impl agreement across KeyGen → Encaps → Decaps. + #[cfg(feature = "mlkem512")] + #[test] + fn mlkem512_roundtrip_matches_spec( + keygen_seed in keygen_seed_strategy(), + encaps_seed in encaps_seed_strategy(), + ) { + let spec_kp = spec::generate_keypair::< + 2, ML_KEM_512_EK_SIZE, ML_KEM_512_DK_SIZE, ML_KEM_512_DK_PKE_SIZE, + >(&ML_KEM_512, &keygen_seed); + + // Skip seeds the spec rejects. + prop_assume!(spec_kp.is_ok()); + let (spec_ek, spec_dk) = spec_kp.unwrap(); + + let kp = libcrux_ml_kem::mlkem512::generate_key_pair(keygen_seed); + + prop_assert_eq!(&kp.public_key().as_slice()[..], &spec_ek[..]); + prop_assert_eq!(&kp.private_key().as_slice()[..], &spec_dk[..]); + + let (spec_ss, spec_ct) = spec::encapsulate::< + 2, ML_KEM_512_EK_SIZE, ML_KEM_512_U_SIZE, ML_KEM_512_V_SIZE, ML_KEM_512_CT_SIZE, + >(&ML_KEM_512, &spec_ek, &encaps_seed) + .expect("encaps on a successfully-generated key should not fail"); + + let (impl_ct, impl_ss_e) = + libcrux_ml_kem::mlkem512::encapsulate(kp.public_key(), encaps_seed); + prop_assert_eq!(impl_ct.as_ref(), &spec_ct[..]); + prop_assert_eq!(&impl_ss_e[..], &spec_ss[..]); + + let spec_ss_d = spec::decapsulate::< + 2, ML_KEM_512_EK_SIZE, ML_KEM_512_DK_SIZE, ML_KEM_512_DK_PKE_SIZE, + ML_KEM_512_U_SIZE, ML_KEM_512_V_SIZE, ML_KEM_512_CT_SIZE, ML_KEM_512_J_INPUT_SIZE, + >(&ML_KEM_512, &spec_dk, &spec_ct) + .expect("spec decaps on a valid (ek, ct) should not fail"); + + let impl_ss_d = libcrux_ml_kem::mlkem512::decapsulate(kp.private_key(), &impl_ct); + prop_assert_eq!(&impl_ss_d[..], &spec_ss_d[..]); + } + + /// ML-KEM-768: spec ↔ impl agreement across KeyGen → Encaps → Decaps. + #[cfg(feature = "mlkem768")] + #[test] + fn mlkem768_roundtrip_matches_spec( + keygen_seed in keygen_seed_strategy(), + encaps_seed in encaps_seed_strategy(), + ) { + let spec_kp = spec::generate_keypair::< + 3, ML_KEM_768_EK_SIZE, ML_KEM_768_DK_SIZE, ML_KEM_768_DK_PKE_SIZE, + >(&ML_KEM_768, &keygen_seed); + prop_assume!(spec_kp.is_ok()); + let (spec_ek, spec_dk) = spec_kp.unwrap(); + + let kp = libcrux_ml_kem::mlkem768::generate_key_pair(keygen_seed); + + prop_assert_eq!(&kp.public_key().as_slice()[..], &spec_ek[..]); + prop_assert_eq!(&kp.private_key().as_slice()[..], &spec_dk[..]); + + let (spec_ss, spec_ct) = spec::encapsulate::< + 3, ML_KEM_768_EK_SIZE, ML_KEM_768_U_SIZE, ML_KEM_768_V_SIZE, ML_KEM_768_CT_SIZE, + >(&ML_KEM_768, &spec_ek, &encaps_seed) + .expect("encaps on a successfully-generated key should not fail"); + + let (impl_ct, impl_ss_e) = + libcrux_ml_kem::mlkem768::encapsulate(kp.public_key(), encaps_seed); + prop_assert_eq!(impl_ct.as_ref(), &spec_ct[..]); + prop_assert_eq!(&impl_ss_e[..], &spec_ss[..]); + + let spec_ss_d = spec::decapsulate::< + 3, ML_KEM_768_EK_SIZE, ML_KEM_768_DK_SIZE, ML_KEM_768_DK_PKE_SIZE, + ML_KEM_768_U_SIZE, ML_KEM_768_V_SIZE, ML_KEM_768_CT_SIZE, ML_KEM_768_J_INPUT_SIZE, + >(&ML_KEM_768, &spec_dk, &spec_ct) + .expect("spec decaps on a valid (ek, ct) should not fail"); + + let impl_ss_d = libcrux_ml_kem::mlkem768::decapsulate(kp.private_key(), &impl_ct); + prop_assert_eq!(&impl_ss_d[..], &spec_ss_d[..]); + } + + /// ML-KEM-1024: spec ↔ impl agreement across KeyGen → Encaps → Decaps. + #[cfg(feature = "mlkem1024")] + #[test] + fn mlkem1024_roundtrip_matches_spec( + keygen_seed in keygen_seed_strategy(), + encaps_seed in encaps_seed_strategy(), + ) { + let spec_kp = spec::generate_keypair::< + 4, ML_KEM_1024_EK_SIZE, ML_KEM_1024_DK_SIZE, ML_KEM_1024_DK_PKE_SIZE, + >(&ML_KEM_1024, &keygen_seed); + prop_assume!(spec_kp.is_ok()); + let (spec_ek, spec_dk) = spec_kp.unwrap(); + + let kp = libcrux_ml_kem::mlkem1024::generate_key_pair(keygen_seed); + + prop_assert_eq!(&kp.public_key().as_slice()[..], &spec_ek[..]); + prop_assert_eq!(&kp.private_key().as_slice()[..], &spec_dk[..]); + + let (spec_ss, spec_ct) = spec::encapsulate::< + 4, ML_KEM_1024_EK_SIZE, ML_KEM_1024_U_SIZE, ML_KEM_1024_V_SIZE, ML_KEM_1024_CT_SIZE, + >(&ML_KEM_1024, &spec_ek, &encaps_seed) + .expect("encaps on a successfully-generated key should not fail"); + + let (impl_ct, impl_ss_e) = + libcrux_ml_kem::mlkem1024::encapsulate(kp.public_key(), encaps_seed); + prop_assert_eq!(impl_ct.as_ref(), &spec_ct[..]); + prop_assert_eq!(&impl_ss_e[..], &spec_ss[..]); + + let spec_ss_d = spec::decapsulate::< + 4, ML_KEM_1024_EK_SIZE, ML_KEM_1024_DK_SIZE, ML_KEM_1024_DK_PKE_SIZE, + ML_KEM_1024_U_SIZE, ML_KEM_1024_V_SIZE, ML_KEM_1024_CT_SIZE, ML_KEM_1024_J_INPUT_SIZE, + >(&ML_KEM_1024, &spec_dk, &spec_ct) + .expect("spec decaps on a valid (ek, ct) should not fail"); + + let impl_ss_d = libcrux_ml_kem::mlkem1024::decapsulate(kp.private_key(), &impl_ct); + prop_assert_eq!(&impl_ss_d[..], &spec_ss_d[..]); + } +} From 52a6b05fd201ea1b966e5ed341f6378764916dea Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 09:14:02 +0000 Subject: [PATCH 3/8] [ml-kem] Add F* extraction of the spec --- .../extraction/Hacspec_ml_kem.Compress.fst | 142 +++ .../extraction/Hacspec_ml_kem.Ind_cca.fst | 1134 +++++++++++++++++ .../extraction/Hacspec_ml_kem.Ind_cpa.fst | 696 ++++++++++ .../extraction/Hacspec_ml_kem.Invert_ntt.fst | 236 ++++ .../extraction/Hacspec_ml_kem.Matrix.fst | 643 ++++++++++ .../fstar/extraction/Hacspec_ml_kem.Ntt.fst | 509 ++++++++ ...cspec_ml_kem.Parameters.Hash_functions.fst | 36 + .../extraction/Hacspec_ml_kem.Parameters.fst | 409 ++++++ .../extraction/Hacspec_ml_kem.Polynomial.fst | 221 ++++ .../extraction/Hacspec_ml_kem.Sampling.fst | 307 +++++ .../extraction/Hacspec_ml_kem.Serialize.fst | 949 ++++++++++++++ specs/ml-kem/proofs/fstar/extraction/Makefile | 1 + 12 files changed, 5283 insertions(+) create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Compress.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cca.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cpa.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Invert_ntt.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Matrix.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ntt.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.Hash_functions.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Polynomial.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Sampling.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Serialize.fst create mode 100644 specs/ml-kem/proofs/fstar/extraction/Makefile diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Compress.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Compress.fst new file mode 100644 index 0000000000..fcd7f959a2 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Compress.fst @@ -0,0 +1,142 @@ +module Hacspec_ml_kem.Compress +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +#push-options "--z3rlimit 1500" + +/// This function implements the `Compress` function specified in the NIST FIPS +/// 203 standard (Page 18, Expression 4.5), which is defined as: +/// ```plaintext +/// Compress_d: ℤq -> ℤ_{2ᵈ} +/// Compress_d(x) = ⌈(2ᵈ/q)·x⌋ +/// ``` +/// Since `⌈x⌋ = ⌊x + 1/2⌋` we have: +/// ```plaintext +/// Compress_d(x) = ⌊(2ᵈ/q)·x + 1/2⌋ +/// = ⌊(2^{d+1}·x + q) / 2q⌋ +/// ``` +/// this latter expression is what the code computes, since it enables us to +/// avoid the use of floating point computations as required by the standard. +/// The NIST FIPS 203 standard can be found at +/// . +let compress_d (fe: Hacspec_ml_kem.Parameters.t_FieldElement) (to_bit_size: usize) + : Prims.Pure Hacspec_ml_kem.Parameters.t_FieldElement + (requires to_bit_size <. mk_usize 12) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let two_pow_bit_size:u32 = + Core_models.Num.impl_u32__pow (mk_u32 2) (cast (to_bit_size <: usize) <: u32) + in + let compressed:u32 = + ((((cast (fe.Hacspec_ml_kem.Parameters.f_val <: u16) <: u32) *! mk_u32 2 <: u32) *! + two_pow_bit_size + <: + u32) +! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) /! + (mk_u32 2 *! (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) <: u32) + in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (compressed %! two_pow_bit_size <: u32) + <: + u16) + +#pop-options + +#push-options "--z3rlimit 150" + +/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539), +/// compressing a polynomial ring element is accomplished by `compress()`ing its +/// constituent field coefficients. +/// The NIST FIPS 203 standard can be found at +/// . +let compress + (re: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (bits_per_compressed_coefficient: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires bits_per_compressed_coefficient <. mk_usize 12) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + compress_d (re.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + bits_per_compressed_coefficient + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#pop-options + +#push-options "--z3rlimit 150" + +/// This function implements the `Decompress` function specified in the NIST FIPS +/// 203 standard (Page 18, Expression 4.6), which is defined as: +/// ```plaintext +/// Decompress_d: ℤ_{2ᵈ} -> ℤq +/// Decompress_d(y) = ⌈(q/2ᵈ)·y⌋ +/// ``` +/// Since `⌈x⌋ = ⌊x + 1/2⌋` we have: +/// ```plaintext +/// Decompress_d(y) = ⌊(q/2ᵈ)·y + 1/2⌋ +/// = ⌊(2·y·q + 2ᵈ) / 2^{d+1})⌋ +/// ``` +/// this latter expression is what the code computes, since it enables us to +/// avoid the use of floating point computations as required by the standard. +/// The NIST FIPS 203 standard can be found at +/// . +let decompress_d (fe: Hacspec_ml_kem.Parameters.t_FieldElement) (to_bit_size: usize) + : Prims.Pure Hacspec_ml_kem.Parameters.t_FieldElement + (requires + to_bit_size <. mk_usize 12 && + fe.Hacspec_ml_kem.Parameters.f_val <. (mk_u16 1 < Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let two_pow_bit_size:u32 = + Core_models.Num.impl_u32__pow (mk_u32 2) (cast (to_bit_size <: usize) <: u32) + in + let numerator:u32 = + ((mk_u32 2 *! (cast (fe.Hacspec_ml_kem.Parameters.f_val <: u16) <: u32) <: u32) *! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) +! + two_pow_bit_size + in + let decompressed:u32 = numerator /! (two_pow_bit_size *! mk_u32 2 <: u32) in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (decompressed <: u32) <: u16) + +#pop-options + +#push-options "--z3rlimit 150" + +/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539), +/// compressing a polynomial ring element is accomplished by `decompress()`ing +/// its constituent field coefficients. +/// The NIST FIPS 203 standard can be found at +/// . +let decompress + (re: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (bits_per_compressed_coefficient: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires + b2t (bits_per_compressed_coefficient <. mk_usize 12 <: bool) /\ + (forall (i: usize). + b2t (i <. mk_usize 256 <: bool) ==> + b2t + ((re.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement).Hacspec_ml_kem.Parameters.f_val <. + (mk_u16 1 < Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + decompress_d (re.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + bits_per_compressed_coefficient + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#pop-options diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cca.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cca.fst new file mode 100644 index 0000000000..12b7bc5a05 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cca.fst @@ -0,0 +1,1134 @@ +module Hacspec_ml_kem.Ind_cca +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +#push-options "--z3rlimit 1500" + +/// Algorithm 16: ML-KEM.KeyGen_internal +/// ```plaintext +/// Input: d ∈ 𝔹³², z ∈ 𝔹³². +/// Output: encapsulation key ek ∈ 𝔹^{384k+32}. +/// Output: decapsulation key dk ∈ 𝔹^{768k+96}. +/// (ekₚₖₑ, dkₚₖₑ) ← K-PKE.KeyGen(d) +/// ek ← ekₚₖₑ +/// dk ← (dkₚₖₑ ‖ ek ‖ H(ek) ‖ z) +/// return (ek, dk) +/// ``` +let keygen_internal + (v_RANK v_EK_SIZE v_DK_PKE_SIZE v_DK_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (d z: t_Array u8 (mk_usize 32)) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_DK_PKE_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + v_DK_SIZE =. + (((v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + <: + usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + match + Hacspec_ml_kem.Ind_cpa.generate_keypair v_RANK v_EK_SIZE v_DK_PKE_SIZE params (d <: t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_PKE_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok (ek, dk_pke) -> + let dk:t_Array u8 v_DK_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_DK_SIZE in + let dk:t_Array u8 v_DK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to dk + ({ Core_models.Ops.Range.f_end = v_DK_PKE_SIZE } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (dk.[ { Core_models.Ops.Range.f_end = v_DK_PKE_SIZE } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (dk_pke <: t_Slice u8) + <: + t_Slice u8) + in + let dk:t_Array u8 v_DK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range dk + ({ + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE; + Core_models.Ops.Range.f_end = v_DK_PKE_SIZE +! v_EK_SIZE <: usize + } + <: + Core_models.Ops.Range.t_Range usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (dk.[ { + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE; + Core_models.Ops.Range.f_end = v_DK_PKE_SIZE +! v_EK_SIZE <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + (ek <: t_Slice u8) + <: + t_Slice u8) + in + let dk:t_Array u8 v_DK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range dk + ({ + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE +! v_EK_SIZE <: usize; + Core_models.Ops.Range.f_end + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (dk.[ { + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE +! v_EK_SIZE <: usize; + Core_models.Ops.Range.f_end + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + (Hacspec_ml_kem.Parameters.Hash_functions.v_H (ek <: t_Slice u8) <: t_Slice u8) + <: + t_Slice u8) + in + let dk:t_Array u8 v_DK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from dk + ({ + Core_models.Ops.Range.f_start + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (dk.[ { + Core_models.Ops.Range.f_start + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (z <: t_Slice u8) + <: + t_Slice u8) + in + Core_models.Result.Result_Ok (ek, dk <: (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_SIZE)) + <: + Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Algorithm 17: ML-KEM.Encaps_internal +/// ```plaintext +/// Input: encapsulation key ek ∈ 𝔹^{384k+32}. +/// Input: m ∈ 𝔹³². +/// Output: shared key K ∈ 𝔹³². +/// Output: ciphertext c ∈ 𝔹^{32(dᵤk+dᵥ)}. +/// (K, r) ← G(m ‖ H(ek)) +/// c ← K-PKE.Encrypt(ek, m, r) +/// return (K, c) +/// ``` +let encaps_internal + (v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (ek: t_Slice u8) + (m: t_Array u8 (mk_usize 32)) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + (Core_models.Slice.impl__len #u8 ek <: usize) =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let to_hash:t_Array u8 (mk_usize 64) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 64) in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to to_hash + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (m <: t_Slice u8) + <: + t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from to_hash + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (Hacspec_ml_kem.Parameters.Hash_functions.v_H ek <: t_Slice u8) + <: + t_Slice u8) + in + let hashed:t_Array u8 (mk_usize 64) = + Hacspec_ml_kem.Parameters.Hash_functions.v_G (to_hash <: t_Slice u8) + in + let (shared_secret: t_Slice u8), (pseudorandomness: t_Slice u8) = + Core_models.Slice.impl__split_at #u8 (hashed <: t_Slice u8) (mk_usize 32) + in + let (r: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (pseudorandomness.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + match + Hacspec_ml_kem.Ind_cpa.encrypt v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE params ek m (r <: t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok c -> + let k:t_Array u8 (mk_usize 32) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) in + let k:t_Array u8 (mk_usize 32) = Core_models.Slice.impl__copy_from_slice #u8 k shared_secret in + Core_models.Result.Result_Ok (k, c <: (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE)) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Algorithm 18: ML-KEM.Decaps_internal +/// ```plaintext +/// Input: decapsulation key dk ∈ 𝔹^{768k+96}. +/// Input: ciphertext c ∈ 𝔹^{32(dᵤk+dᵥ)}. +/// Output: shared key K ∈ 𝔹³². +/// dkₚₖₑ ← dk[0 : 384k] +/// ekₚₖₑ ← dk[384k : 768k + 32] +/// h ← dk[768k + 32 : 768k + 64] +/// z ← dk[768k + 64 : 768k + 96] +/// m′ ← K-PKE.Decrypt(dkₚₖₑ, c) +/// (K′, r′) ← G(m′ ‖ h) +/// K\u{303} ← J(z ‖ c) +/// c′ ← K-PKE.Encrypt(ekₚₖₑ, m′, r′) +/// if c ≠ c′ then +/// K′ ← K\u{303} +/// end if +/// return K′ +/// ``` +let decaps_internal + (v_RANK v_EK_SIZE v_DK_SIZE v_DK_PKE_SIZE v_U_SIZE v_V_SIZE v_CT_SIZE v_J_INPUT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (dk: t_Array u8 v_DK_SIZE) + (c: t_Array u8 v_CT_SIZE) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_DK_PKE_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + v_DK_SIZE =. + (((v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + <: + usize) && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + v_J_INPUT_SIZE =. (mk_usize 32 +! v_CT_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let dk_pke:t_Slice u8 = + dk.[ { Core_models.Ops.Range.f_end = v_DK_PKE_SIZE } <: Core_models.Ops.Range.t_RangeTo usize ] + in + let ek:t_Slice u8 = + dk.[ { + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE; + Core_models.Ops.Range.f_end = v_DK_PKE_SIZE +! v_EK_SIZE <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + in + let h:t_Slice u8 = + dk.[ { + Core_models.Ops.Range.f_start = v_DK_PKE_SIZE +! v_EK_SIZE <: usize; + Core_models.Ops.Range.f_end + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize ] + in + let z:t_Slice u8 = + dk.[ { + Core_models.Ops.Range.f_start + = + (v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize + } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + in + let m_prime:t_Array u8 (mk_usize 32) = + Hacspec_ml_kem.Ind_cpa.decrypt v_RANK params dk_pke (c <: t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 64) in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to to_hash + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (m_prime <: t_Slice u8) + <: + t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from to_hash + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + h + <: + t_Slice u8) + in + let hashed:t_Array u8 (mk_usize 64) = + Hacspec_ml_kem.Parameters.Hash_functions.v_G (to_hash <: t_Slice u8) + in + let (success_shared_secret: t_Slice u8), (pseudorandomness: t_Slice u8) = + Core_models.Slice.impl__split_at #u8 (hashed <: t_Slice u8) (mk_usize 32) + in + let (r_prime: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (pseudorandomness.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + let j_input:t_Array u8 v_J_INPUT_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_J_INPUT_SIZE in + let j_input:t_Array u8 v_J_INPUT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to j_input + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (j_input.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + z + <: + t_Slice u8) + in + let j_input:t_Array u8 v_J_INPUT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from j_input + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (j_input.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (c <: t_Slice u8) + <: + t_Slice u8) + in + let (rejection_shared_secret: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Hacspec_ml_kem.Parameters.Hash_functions.v_J (mk_usize 32) (j_input <: t_Slice u8) + in + match + Hacspec_ml_kem.Ind_cpa.encrypt v_RANK + v_U_SIZE + v_V_SIZE + v_CT_SIZE + params + ek + m_prime + (r_prime <: t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok c_prime -> + if + (c.[ Core_models.Ops.Range.RangeFull <: Core_models.Ops.Range.t_RangeFull ] <: t_Slice u8) =. + (c_prime.[ Core_models.Ops.Range.RangeFull <: Core_models.Ops.Range.t_RangeFull ] + <: + t_Slice u8) + then + let k:t_Array u8 (mk_usize 32) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) in + let k:t_Array u8 (mk_usize 32) = + Core_models.Slice.impl__copy_from_slice #u8 k success_shared_secret + in + Core_models.Result.Result_Ok k + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + else + Core_models.Result.Result_Ok rejection_shared_secret + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Algorithm 19: ML-KEM.KeyGen +/// Generates an encapsulation key and a corresponding decapsulation key. +let generate_keypair + (v_RANK v_EK_SIZE v_DK_SIZE v_DK_PKE_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (randomness: t_Array u8 (mk_usize 64)) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_DK_PKE_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + v_DK_SIZE =. + (((v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + <: + usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let (d: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (randomness.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + let (z: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (randomness.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + keygen_internal v_RANK v_EK_SIZE v_DK_PKE_SIZE v_DK_SIZE params d z + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Modulus check for encapsulation key validation (FIPS 203 Section 7.2). +/// Verifies that ByteEncode₁₂(ByteDecode₁₂(ek[..384k])) == ek[..384k]. +let public_key_modulus_check + (v_EK_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (ek: t_Array u8 v_EK_SIZE) + : Prims.Pure bool + (requires + params.Hacspec_ml_kem.Parameters.f_rank <=. mk_usize 4 && + v_EK_SIZE =. + ((params.Hacspec_ml_kem.Parameters.f_rank *! + Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT + <: + usize) +! + mk_usize 32 + <: + usize)) + (fun _ -> Prims.l_True) = + let tt_size:usize = Hacspec_ml_kem.Parameters.impl_MlKemParams__tt_as_ntt_encoded_size params in + let encoded_ring_elements:t_Slice u8 = + ek.[ { Core_models.Ops.Range.f_end = tt_size } <: Core_models.Ops.Range.t_RangeTo usize ] + in + let valid:bool = true in + let valid:bool = + Rust_primitives.Hax.Folds.fold_chunked_slice Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT + encoded_ring_elements + (fun valid temp_1_ -> + let valid:bool = valid in + let _:usize = temp_1_ in + true) + valid + (fun valid chunk -> + let valid:bool = valid in + let chunk:t_Slice u8 = chunk in + let decoded:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Serialize.byte_decode (mk_usize 384) + (mk_usize 3072) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 384)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 384)) + #FStar.Tactics.Typeclasses.solve + chunk + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 384)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 384)) + (mk_usize 12) + in + let re_encoded:t_Array u8 (mk_usize 384) = + Hacspec_ml_kem.Serialize.byte_encode (mk_usize 384) + (mk_usize 3072) + decoded + (mk_usize 12) + in + if + chunk <>. + (Core_models.Array.impl_23__as_slice #u8 (mk_usize 384) re_encoded <: t_Slice u8) + then + let valid:bool = false in + valid + else valid) + in + valid + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Algorithm 20: ML-KEM.Encaps +/// Uses the encapsulation key to generate a shared key and ciphertext. +/// Includes modulus check on ek per FIPS 203 Section 7.2. +let encapsulate + (v_RANK v_EK_SIZE v_U_SIZE v_V_SIZE v_CT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (ek: t_Array u8 v_EK_SIZE) + (m: t_Array u8 (mk_usize 32)) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let _:Prims.unit = () <: Prims.unit in + encaps_internal v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE params (ek <: t_Slice u8) m + +#pop-options + +#push-options "--z3rlimit 1500" + +/// Algorithm 21: ML-KEM.Decaps +/// Uses the decapsulation key to produce a shared key from a ciphertext. +let decapsulate + (v_RANK v_EK_SIZE v_DK_SIZE v_DK_PKE_SIZE v_U_SIZE v_V_SIZE v_CT_SIZE v_J_INPUT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (dk: t_Array u8 v_DK_SIZE) + (c: t_Array u8 v_CT_SIZE) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_DK_PKE_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + v_DK_SIZE =. + (((v_DK_PKE_SIZE +! v_EK_SIZE <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + <: + usize) && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + v_J_INPUT_SIZE =. (mk_usize 32 +! v_CT_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + decaps_internal v_RANK v_EK_SIZE v_DK_SIZE v_DK_PKE_SIZE v_U_SIZE v_V_SIZE v_CT_SIZE + v_J_INPUT_SIZE params dk c + +#pop-options + +#push-options "--z3rlimit 1500" + +/// ML-KEM.KeyGen — unpacked variant. Returns the components used by +/// `MlKemKeyPairUnpacked` in the libcrux impl, instead of the +/// serialized `(ek, dk)` pair. +let ind_cca_unpack_generate_keypair + (v_RANK v_EK_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (randomness: t_Array u8 (mk_usize 64)) + : Prims.Pure + (Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let (d: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (randomness.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + let (z: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + (randomness.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) Core_models.Array.t_TryFromSliceError + ) + in + match + Hacspec_ml_kem.Ind_cpa.generate_keypair_unpacked v_RANK params (d <: t_Slice u8) + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok (secret_as_ntt, tt_as_ntt, v_A_as_ntt, seed_for_A) -> + let + (m_A: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK):t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) v_RANK = + Hacspec_ml_kem.Matrix.transpose v_RANK v_A_as_ntt + in + let (ek: t_Array u8 v_EK_SIZE):t_Array u8 v_EK_SIZE = + Hacspec_ml_kem.Serialize.serialize_public_key v_RANK + v_EK_SIZE + tt_as_ntt + (seed_for_A <: t_Slice u8) + in + let (public_key_hash: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Hacspec_ml_kem.Parameters.Hash_functions.v_H (ek <: t_Slice u8) + in + let implicit_rejection_value:t_Array u8 (mk_usize 32) = + Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) + in + let implicit_rejection_value:t_Array u8 (mk_usize 32) = + Core_models.Slice.impl__copy_from_slice #u8 implicit_rejection_value (z <: t_Slice u8) + in + Core_models.Result.Result_Ok + (secret_as_ntt, tt_as_ntt, m_A, seed_for_A, public_key_hash, implicit_rejection_value + <: + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32))) + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32) & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 1500" + +/// ML-KEM.Encaps — unpacked variant. Skips the `H(ek)` and +/// `ByteDecode₁₂(ek)` decoding steps; consumes the precomputed +/// `public_key_hash`, `t_as_ntt`, and `m_A` (libcrux-transposed form) +/// directly. +let ind_cca_unpack_encapsulate + (v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (public_key_hash: t_Array u8 (mk_usize 32)) + (tt_as_ntt: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (m_A: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + (randomness: t_Array u8 (mk_usize 32)) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let to_hash:t_Array u8 (mk_usize 64) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 64) in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to to_hash + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (randomness <: t_Slice u8) + <: + t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from to_hash + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (public_key_hash <: t_Slice u8) + <: + t_Slice u8) + in + let hashed:t_Array u8 (mk_usize 64) = + Hacspec_ml_kem.Parameters.Hash_functions.v_G (to_hash <: t_Slice u8) + in + let (shared_secret: t_Slice u8), (pseudorandomness: t_Slice u8) = + Core_models.Slice.impl__split_at #u8 (hashed <: t_Slice u8) (mk_usize 32) + in + let + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK):t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) v_RANK = + Hacspec_ml_kem.Matrix.transpose v_RANK m_A + in + match + Hacspec_ml_kem.Ind_cpa.encrypt_unpacked v_RANK + v_U_SIZE + v_V_SIZE + v_CT_SIZE + params + tt_as_ntt + v_A_as_ntt + randomness + pseudorandomness + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok c -> + let k:t_Array u8 (mk_usize 32) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) in + let k:t_Array u8 (mk_usize 32) = Core_models.Slice.impl__copy_from_slice #u8 k shared_secret in + Core_models.Result.Result_Ok (k, c <: (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE)) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32) & t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 1500" + +/// ML-KEM.Decaps — unpacked variant. Skips the `dk` deconstruction +/// (`dkₚₖₑ`, `ekₚₖₑ`, `h`, `z`) and the `ByteDecode₁₂(dkₚₖₑ)` step; +/// consumes the components precomputed by `ind_cca_unpack_generate_keypair` +/// directly. Re-encrypts via `ind_cpa::encrypt_unpacked` to check the +/// FO transform\'s c == c′ condition. +let ind_cca_unpack_decapsulate + (v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE v_J_INPUT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (public_key_hash implicit_rejection_value: t_Array u8 (mk_usize 32)) + (ciphertext: t_Array u8 v_CT_SIZE) + (secret_as_ntt tt_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (m_A: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + v_J_INPUT_SIZE =. (mk_usize 32 +! v_CT_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3)) + (fun _ -> Prims.l_True) = + let m_prime:t_Array u8 (mk_usize 32) = + Hacspec_ml_kem.Ind_cpa.decrypt_unpacked v_RANK params secret_as_ntt (ciphertext <: t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 64) in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to to_hash + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (m_prime <: t_Slice u8) + <: + t_Slice u8) + in + let to_hash:t_Array u8 (mk_usize 64) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from to_hash + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (to_hash.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (public_key_hash <: t_Slice u8) + <: + t_Slice u8) + in + let hashed:t_Array u8 (mk_usize 64) = + Hacspec_ml_kem.Parameters.Hash_functions.v_G (to_hash <: t_Slice u8) + in + let (success_shared_secret: t_Slice u8), (pseudorandomness: t_Slice u8) = + Core_models.Slice.impl__split_at #u8 (hashed <: t_Slice u8) (mk_usize 32) + in + let j_input:t_Array u8 v_J_INPUT_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_J_INPUT_SIZE in + let j_input:t_Array u8 v_J_INPUT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to j_input + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (j_input.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (implicit_rejection_value <: t_Slice u8) + <: + t_Slice u8) + in + let j_input:t_Array u8 v_J_INPUT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from j_input + ({ Core_models.Ops.Range.f_start = mk_usize 32 } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (j_input.[ { Core_models.Ops.Range.f_start = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (ciphertext <: t_Slice u8) + <: + t_Slice u8) + in + let (rejection_shared_secret: t_Array u8 (mk_usize 32)):t_Array u8 (mk_usize 32) = + Hacspec_ml_kem.Parameters.Hash_functions.v_J (mk_usize 32) (j_input <: t_Slice u8) + in + let + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK):t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) v_RANK = + Hacspec_ml_kem.Matrix.transpose v_RANK m_A + in + match + Hacspec_ml_kem.Ind_cpa.encrypt_unpacked v_RANK + v_U_SIZE + v_V_SIZE + v_CT_SIZE + params + tt_as_ntt + v_A_as_ntt + m_prime + pseudorandomness + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok c_prime -> + if + (ciphertext.[ Core_models.Ops.Range.RangeFull <: Core_models.Ops.Range.t_RangeFull ] + <: + t_Slice u8) =. + (c_prime.[ Core_models.Ops.Range.RangeFull <: Core_models.Ops.Range.t_RangeFull ] + <: + t_Slice u8) + then + let k:t_Array u8 (mk_usize 32) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) in + let k:t_Array u8 (mk_usize 32) = + Core_models.Slice.impl__copy_from_slice #u8 k success_shared_secret + in + Core_models.Result.Result_Ok k + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + else + Core_models.Result.Result_Ok rejection_shared_secret + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cpa.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cpa.fst new file mode 100644 index 0000000000..d0b78227f8 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ind_cpa.fst @@ -0,0 +1,696 @@ +module Hacspec_ml_kem.Ind_cpa +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +#push-options "--z3rlimit 150" + +/// Helper to sample a polynomial from CBD with dynamic eta. +let sample_secret (eta: usize) (prf_input: t_Array u8 (mk_usize 33)) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires eta =. mk_usize 2 || eta =. mk_usize 3) + (fun _ -> Prims.l_True) = + match eta <: usize with + | Rust_primitives.Integers.MkInt 2 -> + let (out: t_Array u8 (mk_usize 128)):t_Array u8 (mk_usize 128) = + Hacspec_ml_kem.Parameters.Hash_functions.v_PRF (mk_usize 128) (prf_input <: t_Slice u8) + in + Hacspec_ml_kem.Sampling.sample_poly_cbd (mk_usize 128) (mk_usize 1024) (mk_usize 2) out + | Rust_primitives.Integers.MkInt 3 -> + let (out: t_Array u8 (mk_usize 192)):t_Array u8 (mk_usize 192) = + Hacspec_ml_kem.Parameters.Hash_functions.v_PRF (mk_usize 192) (prf_input <: t_Slice u8) + in + Hacspec_ml_kem.Sampling.sample_poly_cbd (mk_usize 192) (mk_usize 1536) (mk_usize 3) out + | _ -> + let args:usize = eta <: usize in + let args:t_Array Core_models.Fmt.Rt.t_Argument (mk_usize 1) = + let list = [Core_models.Fmt.Rt.impl__new_display #usize args] in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 1); + Rust_primitives.Hax.array_of_list 1 list + in + Rust_primitives.Hax.never_to_any (Core_models.Panicking.panic_fmt (Core_models.Fmt.Rt.impl_1__new_v1 + (mk_usize 1) + (mk_usize 1) + (let list = ["unsupported eta="] in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 1); + Rust_primitives.Hax.array_of_list 1 list) + args + <: + Core_models.Fmt.t_Arguments) + <: + Rust_primitives.Hax.t_Never) + +#pop-options + +let concat_byte (v_N v_N1: usize) (a: t_Array u8 v_N) (b: u8) + : Prims.Pure (t_Array u8 v_N1) + (requires v_N1 >. mk_usize 0 && v_N =. (v_N1 -! mk_usize 1 <: usize)) + (fun _ -> Prims.l_True) = + let result:t_Array u8 v_N1 = Rust_primitives.Hax.repeat (mk_u8 0) v_N1 in + let result:t_Array u8 v_N1 = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to result + ({ Core_models.Ops.Range.f_end = v_N } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (result.[ { Core_models.Ops.Range.f_end = v_N } <: Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (a <: t_Slice u8) + <: + t_Slice u8) + in + let result:t_Array u8 v_N1 = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize result v_N b + in + result + +/// FIPS 203 inner loop: sample `RANK` polynomials from CBD_η using +/// PRF_η(seed ‖ domain_separator + i) for i ∈ {0, …, RANK-1}. +/// Captures the \"for i ∈ {0, …, k-1}: v[i] ← SamplePolyCBD_η(PRF_η(seed, N))\" +/// pattern that appears in K-PKE.KeyGen (Alg. 13) for s/e and in +/// K-PKE.Encrypt (Alg. 14) for r/e₁. +let sample_vector_cbd (v_RANK eta: usize) (seed: t_Slice u8) (domain_separator: u8) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + (Core_models.Slice.impl__len #u8 seed <: usize) =. mk_usize 32 && + (eta =. mk_usize 2 || eta =. mk_usize 3) && + v_RANK <=. mk_usize 4 && + ((cast (domain_separator <: u8) <: usize) +! v_RANK <: usize) <. mk_usize 256) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + let (prf_input: t_Array u8 (mk_usize 33)):t_Array u8 (mk_usize 33) = + concat_byte (mk_usize 32) + (mk_usize 33) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + seed + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 32)) + (domain_separator +! (cast (i <: usize) <: u8) <: u8) + in + sample_secret eta prf_input) + +/// `sample_vector_cbd` followed by NTT. Captures the \"ŝ ← NTT(s)\" / +/// \"ê ← NTT(e)\" / \"r\u{302} ← NTT(r)\" steps in K-PKE.{KeyGen,Encrypt}. +let sample_vector_cbd_then_ntt (v_RANK eta: usize) (seed: t_Slice u8) (domain_separator: u8) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + (Core_models.Slice.impl__len #u8 seed <: usize) =. mk_usize 32 && + (eta =. mk_usize 2 || eta =. mk_usize 3) && + v_RANK <=. mk_usize 4 && + ((cast (domain_separator <: u8) <: usize) +! v_RANK <: usize) <. mk_usize 256) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Ntt.vector_ntt v_RANK + (sample_vector_cbd v_RANK eta seed domain_separator + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + +#push-options "--z3rlimit 150" + +/// Algorithm 13: K-PKE.KeyGen — unpacked variant. Returns the four +/// components separately instead of the serialized `(ek, dk)` byte +/// pair. The packed `generate_keypair` is a thin serialization +/// wrapper around this function. +let generate_keypair_unpacked + (v_RANK: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (key_generation_seed: t_Slice u8) + : Prims.Pure + (Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (Core_models.Slice.impl__len #u8 key_generation_seed <: usize) =. mk_usize 32) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let g_input:t_Array u8 (mk_usize 33) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 33) in + let g_input:t_Array u8 (mk_usize 33) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to g_input + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (g_input.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + key_generation_seed + <: + t_Slice u8) + in + let g_input:t_Array u8 (mk_usize 33) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize g_input + (mk_usize 32) + (cast (v_RANK <: usize) <: u8) + in + let hashed:t_Array u8 (mk_usize 64) = + Hacspec_ml_kem.Parameters.Hash_functions.v_G (g_input <: t_Slice u8) + in + let (seed_for_A_slice: t_Slice u8), (seed_for_secret_and_error: t_Slice u8) = + Core_models.Slice.impl__split_at #u8 (hashed <: t_Slice u8) (mk_usize 32) + in + match + Hacspec_ml_kem.Matrix.sample_matrix_A v_RANK seed_for_A_slice false + <: + Core_models.Result.t_Result + (t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) -> + let secret_as_ntt:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK = + sample_vector_cbd_then_ntt v_RANK + params.Hacspec_ml_kem.Parameters.f_eta1 + seed_for_secret_and_error + (mk_u8 0) + in + let error_as_ntt:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK = + sample_vector_cbd_then_ntt v_RANK + params.Hacspec_ml_kem.Parameters.f_eta1 + seed_for_secret_and_error + (cast (v_RANK <: usize) <: u8) + in + let tt_as_ntt:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Matrix.compute_As_plus_e v_RANK v_A_as_ntt secret_as_ntt error_as_ntt + in + let seed_for_A:t_Array u8 (mk_usize 32) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 32) in + let seed_for_A:t_Array u8 (mk_usize 32) = + Core_models.Slice.impl__copy_from_slice #u8 seed_for_A seed_for_A_slice + in + Core_models.Result.Result_Ok + (secret_as_ntt, tt_as_ntt, v_A_as_ntt, seed_for_A + <: + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32))) + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 150" + +/// Algorithm 13: K-PKE.KeyGen +/// Generates an encryption key and a corresponding decryption key. +/// ```plaintext +/// Output: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}. +/// Output: decryption key dkₚₖₑ ∈ 𝔹^{384k}. +/// d ←$ B +/// (ρ,σ) ← G(d) +/// N ← 0 +/// for (i ← 0; i < k; i++) +/// for(j ← 0; j < k; j++) +/// Â[i,j] ← SampleNTT(XOF(ρ, j, i)) +/// end for +/// end for +/// for(i ← 0; i < k; i++) +/// s[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) +/// N ← N + 1 +/// end for +/// for(i ← 0; i < k; i++) +/// e[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(σ,N)) +/// N ← N + 1 +/// end for +/// ŝ ← NTT(s) +/// ê ← NTT(e) +/// t\u{302} ← Â◦ŝ + ê +/// ekₚₖₑ ← ByteEncode₁₂(t\u{302}) ‖ ρ +/// dkₚₖₑ ← ByteEncode₁₂(ŝ) +/// ``` +let generate_keypair + (v_RANK v_EK_SIZE v_DK_PKE_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (key_generation_seed: t_Slice u8) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_PKE_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + v_DK_PKE_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (Core_models.Slice.impl__len #u8 key_generation_seed <: usize) =. mk_usize 32) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + match + generate_keypair_unpacked v_RANK params key_generation_seed + <: + Core_models.Result.t_Result + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK & + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 32)) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok (secret_as_ntt, tt_as_ntt, e_A_as_ntt, seed_for_A) -> + let (tt_encoded: t_Array u8 v_DK_PKE_SIZE):t_Array u8 v_DK_PKE_SIZE = + Hacspec_ml_kem.Serialize.serialize_secret_key v_RANK v_DK_PKE_SIZE tt_as_ntt + in + let ek:t_Array u8 v_EK_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_EK_SIZE in + let ek:t_Array u8 v_EK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to ek + ({ Core_models.Ops.Range.f_end = v_DK_PKE_SIZE } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (ek.[ { Core_models.Ops.Range.f_end = v_DK_PKE_SIZE } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (tt_encoded <: t_Slice u8) + <: + t_Slice u8) + in + let ek:t_Array u8 v_EK_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from ek + ({ Core_models.Ops.Range.f_start = v_DK_PKE_SIZE } + <: + Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (ek.[ { Core_models.Ops.Range.f_start = v_DK_PKE_SIZE } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (seed_for_A <: t_Slice u8) + <: + t_Slice u8) + in + let (dk: t_Array u8 v_DK_PKE_SIZE):t_Array u8 v_DK_PKE_SIZE = + Hacspec_ml_kem.Serialize.serialize_secret_key v_RANK v_DK_PKE_SIZE secret_as_ntt + in + Core_models.Result.Result_Ok (ek, dk <: (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_PKE_SIZE)) + <: + Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_PKE_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 v_EK_SIZE & t_Array u8 v_DK_PKE_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 150" + +/// K-PKE.Encrypt — unpacked variant. Skips the +/// `ByteDecode₁₂(ek)` / `sample_matrix_A(seed_for_A)` decoding step +/// and consumes the already-decoded `t_as_ntt` and `A_as_ntt` +/// directly. The packed `encrypt` is a thin decoding wrapper. +let encrypt_unpacked + (v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (tt_as_ntt: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + (message: t_Array u8 (mk_usize 32)) + (randomness: t_Slice u8) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3) && + (Core_models.Slice.impl__len #u8 randomness <: usize) =. mk_usize 32) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let r_as_ntt:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + sample_vector_cbd_then_ntt v_RANK params.Hacspec_ml_kem.Parameters.f_eta1 randomness (mk_u8 0) + in + let error_1_:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + sample_vector_cbd v_RANK + params.Hacspec_ml_kem.Parameters.f_eta2 + randomness + (cast (v_RANK <: usize) <: u8) + in + let prf_input:t_Array u8 (mk_usize 33) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 33) in + let prf_input:t_Array u8 (mk_usize 33) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to prf_input + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (prf_input.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + randomness + <: + t_Slice u8) + in + let prf_input:t_Array u8 (mk_usize 33) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize prf_input + (mk_usize 32) + (cast (v_RANK *! mk_usize 2 <: usize) <: u8) + in + let error_2_:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + sample_secret params.Hacspec_ml_kem.Parameters.f_eta2 prf_input + in + let u:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Matrix.compute_vector_u v_RANK v_A_as_ntt r_as_ntt error_1_ + in + let message_as_ring_element:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Serialize.deserialize_then_decompress_message message + in + let v:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Matrix.compute_ring_element_v v_RANK + tt_as_ntt + r_as_ntt + error_2_ + message_as_ring_element + in + let (c1: t_Array u8 v_U_SIZE):t_Array u8 v_U_SIZE = + Hacspec_ml_kem.Serialize.compress_then_serialize_u v_RANK + v_U_SIZE + u + params.Hacspec_ml_kem.Parameters.f_du + in + let (c2: t_Array u8 v_V_SIZE):t_Array u8 v_V_SIZE = + Hacspec_ml_kem.Serialize.compress_then_serialize_v v_V_SIZE + v + params.Hacspec_ml_kem.Parameters.f_dv + in + let c:t_Array u8 v_CT_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_CT_SIZE in + let c:t_Array u8 v_CT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to c + ({ Core_models.Ops.Range.f_end = v_U_SIZE } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (c.[ { Core_models.Ops.Range.f_end = v_U_SIZE } <: Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + (c1 <: t_Slice u8) + <: + t_Slice u8) + in + let c:t_Array u8 v_CT_SIZE = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_from c + ({ Core_models.Ops.Range.f_start = v_U_SIZE } <: Core_models.Ops.Range.t_RangeFrom usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (c.[ { Core_models.Ops.Range.f_start = v_U_SIZE } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + (c2 <: t_Slice u8) + <: + t_Slice u8) + in + Core_models.Result.Result_Ok c + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 150" + +/// Algorithm 14: K-PKE.Encrypt +/// Uses the encryption key to encrypt a plaintext message using the randomness r. +/// ```plaintext +/// Input: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}. +/// Input: message m ∈ 𝔹^{32}. +/// Input: encryption randomness r ∈ 𝔹^{32}. +/// Output: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}. +/// N ← 0 +/// t\u{302} ← ByteDecode₁₂(ekₚₖₑ[0:384k]) +/// ρ ← ekₚₖₑ[384k: 384k + 32] +/// for (i ← 0; i < k; i++) +/// for(j ← 0; j < k; j++) +/// Â[i,j] ← SampleNTT(XOF(ρ, j, i)) +/// end for +/// end for +/// for(i ← 0; i < k; i++) +/// r[i] ← SamplePolyCBD_{η₁}(PRF_{η₁}(r,N)) +/// N ← N + 1 +/// end for +/// for(i ← 0; i < k; i++) +/// e₁[i] ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) +/// N ← N + 1 +/// end for +/// e₂ ← SamplePolyCBD_{η₂}(PRF_{η₂}(r,N)) +/// r\u{302} ← NTT(r) +/// u ← NTT⁻¹(Âᵀ ◦ r\u{302}) + e₁ +/// μ ← Decompress₁(ByteDecode₁(m)) +/// v ← NTT⁻¹(t\u{302}ᵀ ◦ r\u{302}) + e₂ + μ +/// c₁ ← ByteEncode_{dᵤ}(Compress_{dᵤ}(u)) +/// c₂ ← ByteEncode_{dᵥ}(Compress_{dᵥ}(v)) +/// return c ← (c₁ ‖ c₂) +/// ``` +let encrypt + (v_RANK v_U_SIZE v_V_SIZE v_CT_SIZE: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (ek: t_Slice u8) + (message: t_Array u8 (mk_usize 32)) + (randomness: t_Slice u8) + : Prims.Pure + (Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) /! + mk_usize 8 + <: + usize) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) /! + mk_usize 8 + <: + usize) && + v_CT_SIZE =. (v_U_SIZE +! v_V_SIZE <: usize) && + (Core_models.Slice.impl__len #u8 ek <: usize) =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + (params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta1 =. mk_usize 3) && + (params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 2 || + params.Hacspec_ml_kem.Parameters.f_eta2 =. mk_usize 3) && + (Core_models.Slice.impl__len #u8 randomness <: usize) =. mk_usize 32) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let tt_encoded_size:usize = + Hacspec_ml_kem.Parameters.impl_MlKemParams__tt_as_ntt_encoded_size params + in + let (tt_as_ntt: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK):t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Serialize.deserialize_ring_elements_reduced v_RANK + (ek.[ { Core_models.Ops.Range.f_end = tt_encoded_size } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + in + let seed_for_A:t_Slice u8 = + ek.[ { Core_models.Ops.Range.f_start = tt_encoded_size } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + in + match + Hacspec_ml_kem.Matrix.sample_matrix_A v_RANK seed_for_A false + <: + Core_models.Result.t_Result + (t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) -> + encrypt_unpacked v_RANK + v_U_SIZE + v_V_SIZE + v_CT_SIZE + params + tt_as_ntt + v_A_as_ntt + message + randomness + | Core_models.Result.Result_Err err -> + Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result (t_Array u8 v_CT_SIZE) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +#pop-options + +#push-options "--z3rlimit 150" + +/// K-PKE.Decrypt — unpacked variant. Skips the +/// `ByteDecode₁₂(dk)` decoding step and consumes the already-decoded +/// `secret_as_ntt` directly. The packed `decrypt` is a thin +/// decoding wrapper. +let decrypt_unpacked + (v_RANK: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (secret_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (ciphertext: t_Slice u8) + : Prims.Pure (t_Array u8 (mk_usize 32)) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + (Core_models.Slice.impl__len #u8 ciphertext <: usize) =. + ((((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) +! + (Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let u_encoded_size:usize = Hacspec_ml_kem.Parameters.impl_MlKemParams__u_encoded_size params in + let (u: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK):t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Serialize.deserialize_then_decompress_u v_RANK + (ciphertext.[ { + Core_models.Ops.Range.f_start = mk_usize 0; + Core_models.Ops.Range.f_end = u_encoded_size + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + params.Hacspec_ml_kem.Parameters.f_du + in + let v:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Serialize.deserialize_then_decompress_v (ciphertext.[ { + Core_models.Ops.Range.f_start = u_encoded_size + } + <: + Core_models.Ops.Range.t_RangeFrom usize ] + <: + t_Slice u8) + params.Hacspec_ml_kem.Parameters.f_dv + in + let u_as_ntt:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Ntt.vector_ntt v_RANK u + in + let w:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Matrix.compute_message v_RANK v secret_as_ntt u_as_ntt + in + Hacspec_ml_kem.Serialize.compress_then_serialize_message w + +#pop-options + +#push-options "--z3rlimit 150" + +/// Algorithm 15: K-PKE.Decrypt +/// Uses the decryption key to decrypt a ciphertext. +/// ```plaintext +/// Input: decryption key dkₚₖₑ ∈ 𝔹^{384k}. +/// Input: ciphertext c ∈ 𝔹^{32(dᵤk + dᵥ)}. +/// Output: message m ∈ 𝔹^{32}. +/// c₁ ← c[0 : 32dᵤk] +/// c₂ ← c[32dᵤk : 32(dᵤk + dᵥ)] +/// u ← Decompress_{dᵤ}(ByteDecode_{dᵤ}(c₁)) +/// v ← Decompress_{dᵥ}(ByteDecode_{dᵥ}(c₂)) +/// ŝ ← ByteDecode₁₂(dkₚₖₑ) +/// w ← v - NTT⁻¹(ŝᵀ ◦ NTT(u)) +/// m ← ByteEncode₁(Compress₁(w)) +/// return m +/// ``` +let decrypt + (v_RANK: usize) + (params: Hacspec_ml_kem.Parameters.t_MlKemParams) + (dk ciphertext: t_Slice u8) + : Prims.Pure (t_Array u8 (mk_usize 32)) + (requires + v_RANK <=. mk_usize 4 && params.Hacspec_ml_kem.Parameters.f_rank =. v_RANK && + (Core_models.Slice.impl__len #u8 dk <: usize) =. + (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) && + (Core_models.Slice.impl__len #u8 ciphertext <: usize) =. + ((((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! + params.Hacspec_ml_kem.Parameters.f_du + <: + usize) +! + (Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! + params.Hacspec_ml_kem.Parameters.f_dv + <: + usize) + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let + (secret_as_ntt: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK):t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Serialize.deserialize_ring_elements_reduced v_RANK dk + in + decrypt_unpacked v_RANK params secret_as_ntt ciphertext + +#pop-options diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Invert_ntt.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Invert_ntt.fst new file mode 100644 index 0000000000..3490da0aa0 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Invert_ntt.fst @@ -0,0 +1,236 @@ +module Hacspec_ml_kem.Invert_ntt +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +let v_INVERSE_OF_128_: Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3303) + +/// Use the Gentleman-Sande butterfly to invert, in-place, the NTT representation +/// of a `Polynomial`. +/// This function implements Algorithm 9 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// ```plaintext +/// Input: array fˆ ∈ ℤ₂₅₆. +/// Output: array f ∈ ℤ₂₅₆. +/// f ← fˆ +/// k ← 127 +/// for (len ← 2; len ≤ 128; len ← 2·len) +/// for (start ← 0; start < 256; start ← start + 2·len) +/// zeta ← ζ^(BitRev₇(k)) mod q +/// k ← k − 1 +/// for (j ← start; j < start + len; j++) +/// t ← f[j] +/// f[j] ← t + f[j + len] +/// f[j + len] ← zeta·(f[j+len] − t) +/// end for +/// end for +/// end for +/// f ← f·3303 mod q +/// return f +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +/// Gentleman–Sande butterfly: `(a, b, ζ) ↦ (a + b, ζ·(b − a))`. +/// Used in the inverse NTT. +let inv_butterfly (zeta a b: Hacspec_ml_kem.Parameters.t_FieldElement) + : (Hacspec_ml_kem.Parameters.t_FieldElement & Hacspec_ml_kem.Parameters.t_FieldElement) = + Hacspec_ml_kem.Parameters.impl_FieldElement__add a b, + Hacspec_ml_kem.Parameters.impl_FieldElement__mul zeta + (Hacspec_ml_kem.Parameters.impl_FieldElement__sub b a + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + <: + (Hacspec_ml_kem.Parameters.t_FieldElement & Hacspec_ml_kem.Parameters.t_FieldElement) + +#push-options "--z3rlimit 150" + +/// One layer of the inverse NTT, generic over the array length `N`. +/// As in `ntt_layer_n`, `N = 2·groups·len` where `groups = zetas.len()`. +/// Each group spans `2·len` coefficients and uses one zeta for its `len` +/// Gentleman–Sande butterflies. +/// This is FIPS 203 Algorithm 9 lines 3-8 applied once at butterfly +/// half-size `len`. +let ntt_inverse_layer_n + (v_N: usize) + (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (len: usize) + (zetas: t_Slice Hacspec_ml_kem.Parameters.t_FieldElement) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (requires + len >=. mk_usize 1 && len <. mk_usize 1024 && + (Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) <. + mk_usize 1024 && + (((Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) *! + mk_usize 2 + <: + usize) *! + len + <: + usize) =. + v_N) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + v_N + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + let group:usize = i /! (mk_usize 2 *! len <: usize) in + let idx:usize = i %! (mk_usize 2 *! len <: usize) in + if idx <. len + then + (inv_butterfly (zetas.[ group ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i +! len <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement)) + ._1 + else + (inv_butterfly (zetas.[ group ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i -! len <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement)) + ._2) + +#pop-options + +#push-options "--z3rlimit 150" + +/// One layer of the 256-coefficient inverse NTT. +/// Follows FIPS 203 Algorithm 9. Butterfly half-size `len = 2^layer`, +/// groups = `128 / len`, zetas used = `ZETAS[groups .. 2·groups]` reversed +/// (the inverse NTT consumes the zeta table top-down). +let ntt_inverse_layer + (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (layer: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires layer >=. mk_usize 1 && layer <=. mk_usize 7) + (fun _ -> Prims.l_True) = + let len:usize = mk_usize 1 < Hacspec_ml_kem.Parameters.t_FieldElement) + (fun round -> + let round:usize = round in + if round <. groups <: bool + then + Hacspec_ml_kem.Ntt.v_ZETAS.[ ((mk_usize 2 *! groups <: usize) -! mk_usize 1 <: usize) -! + round + <: + usize ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement + else + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + in + ntt_inverse_layer_n (mk_usize 256) + p + len + (zetas.[ { Core_models.Ops.Range.f_start = mk_usize 0; Core_models.Ops.Range.f_end = groups } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice Hacspec_ml_kem.Parameters.t_FieldElement) + +#pop-options + +let reduce_polynomial (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Parameters.impl_FieldElement__mul (p.[ i ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + v_INVERSE_OF_128_ + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#push-options "--z3rlimit 150" + +/// FIPS 203 Algorithm 9 lines 3-8 only — the seven layers of +/// Gentleman–Sande inverse butterflies, *without* the final +/// `f ← f · 3303 mod q` finalization (the `· 128⁻¹` factor). +/// This is the natural intermediate form of the inverse NTT and +/// matches the impl's `invert_ntt_montgomery`, which deliberately +/// omits the `· 3303` finalization because every call site fuses it +/// with the next per-element operation (see `polynomial.rs::subtract_reduce`, +/// `add_error_reduce`, `add_message_error_reduce`'s `mont_mul(b, 1441)` +/// where `1441 ≡ R²/128 mod q`). Reference: +/// `pq-crystals/kyber/ref/ntt.c` line 106 (the `1441 = mont²/128` comment). +/// The fully-finalized FIPS-203 INTT (`ntt_inverse` above) factors as +/// `reduce_polynomial ∘ ntt_inverse_butterflies`. +let ntt_inverse_butterflies (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 1) + in + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 2) + in + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 3) + in + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 4) + in + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 5) + in + let p:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_inverse_layer p (mk_usize 6) + in + ntt_inverse_layer p (mk_usize 7) + +#pop-options + +#push-options "--z3rlimit 150" + +let ntt_inverse (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + reduce_polynomial (ntt_inverse_butterflies p + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + +#pop-options + +/// Inverse NTT applied to each polynomial in a vector. +let vector_inverse_ntt + (v_RANK: usize) + (vector_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + ntt_inverse (vector_as_ntt.[ i ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + +/// Performs Barrett reduction on all coefficients of a polynomial. +/// This is the spec equivalent of `poly_barrett_reduce` in the implementation. +let poly_barrett_reduce (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Parameters.impl_FieldElement__new ((p.[ i ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val %! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Matrix.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Matrix.fst new file mode 100644 index 0000000000..9da2a4bd6e --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Matrix.fst @@ -0,0 +1,643 @@ +module Hacspec_ml_kem.Matrix +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +/// N.B.: According to the NIST FIPS 203 standard (Page 9, Line 519), a matrix is +/// a set of column vectors. +/// The NIST FIPS 203 standard can be found at +/// . +let add_polynomials (p1 p2: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (((cast ((p1.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast ((p2.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +let sub_polynomials (p1 p2: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast ((((cast ((p1.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) -! + (cast ((p2.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +let add_vectors + (v_RANK: usize) + (v1 v2: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + add_polynomials (v1.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) + ) + (v2.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + +let multiply_matrix_by_column + (v_RANK: usize) + (matrix: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + (vector: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 256) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + v_RANK + (fun result temp_1_ -> + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + result + in + let _:usize = temp_1_ in + true) + result + (fun result j -> + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + result + in + let j:usize = j in + let product:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Ntt.multiply_ntts ((matrix.[ j ] + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK).[ i ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (vector.[ j ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) + ) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + add_polynomials result product + in + result) + in + result) + +let multiply_vectors + (v_RANK: usize) + (v1 v2: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 256) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + v_RANK + (fun result temp_1_ -> + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = result in + let _:usize = temp_1_ in + true) + result + (fun result j -> + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = result in + let j:usize = j in + let product:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Ntt.multiply_ntts (v1.[ j ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (v2.[ j ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + add_polynomials result product + in + result) + in + result + +let transpose + (v_RANK: usize) + (matrix: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + : t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK + #(usize -> t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun j -> + let j:usize = j in + (matrix.[ j ] + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK).[ i + ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + +/// Sample the matrix A from a seed. Corresponds to `sample_matrix_A` in the implementation. +/// When `transpose` is true, A_transpose[j][i] = sampled(i, j). +/// When `transpose` is false, A_transpose[i][j] = sampled(i, j). +let sample_matrix_A (v_RANK: usize) (seed_for_A: t_Slice u8) (transpose: bool) + : Prims.Pure + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (requires + (Core_models.Slice.impl__len #u8 seed_for_A <: usize) =. mk_usize 32 && + v_RANK <=. mk_usize 4) + (fun _ -> Prims.l_True) = + let + (v_A_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK):t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) v_RANK = + Rust_primitives.Hax.repeat (Rust_primitives.Hax.repeat (Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new + (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 256) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK + in + let xof_input:t_Array u8 (mk_usize 34) = Rust_primitives.Hax.repeat (mk_u8 0) (mk_usize 34) in + let xof_input:t_Array u8 (mk_usize 34) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range_to xof_input + ({ Core_models.Ops.Range.f_end = mk_usize 32 } <: Core_models.Ops.Range.t_RangeTo usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (xof_input.[ { Core_models.Ops.Range.f_end = mk_usize 32 } + <: + Core_models.Ops.Range.t_RangeTo usize ] + <: + t_Slice u8) + seed_for_A + <: + t_Slice u8) + in + match + Rust_primitives.Hax.Folds.fold_range_return (mk_usize 0) + v_RANK + (fun temp_0_ temp_1_ -> + let + (v_A_as_ntt: + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK), + (xof_input: t_Array u8 (mk_usize 34)) = + temp_0_ + in + let _:usize = temp_1_ in + true) + (v_A_as_ntt, xof_input + <: + (t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 34))) + (fun temp_0_ i -> + let + (v_A_as_ntt: + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK), + (xof_input: t_Array u8 (mk_usize 34)) = + temp_0_ + in + let i:usize = i in + match + Rust_primitives.Hax.Folds.fold_range_return (mk_usize 0) + v_RANK + (fun temp_0_ temp_1_ -> + let + (v_A_as_ntt: + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK), + (xof_input: t_Array u8 (mk_usize 34)) = + temp_0_ + in + let _:usize = temp_1_ in + true) + (v_A_as_ntt, xof_input + <: + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34))) + (fun temp_0_ j -> + let + (v_A_as_ntt: + t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK), + (xof_input: t_Array u8 (mk_usize 34)) = + temp_0_ + in + let j:usize = j in + let xof_input:t_Array u8 (mk_usize 34) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize xof_input + (mk_usize 32) + (cast (i <: usize) <: u8) + in + let xof_input:t_Array u8 (mk_usize 34) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize xof_input + (mk_usize 33) + (cast (j <: usize) <: u8) + in + let (xof_bytes: t_Array u8 (mk_usize 840)):t_Array u8 (mk_usize 840) = + Hacspec_ml_kem.Parameters.Hash_functions.v_XOF (mk_usize 840) + (xof_input <: t_Slice u8) + in + match + Hacspec_ml_kem.Sampling.sample_ntt (mk_usize 70) + (mk_usize 560) + (mk_usize 840) + (mk_usize 6720) + xof_bytes + <: + Core_models.Result.t_Result + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + with + | Core_models.Result.Result_Ok sampled -> + if transpose + then + let v_A_as_ntt:t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize v_A_as_ntt + j + (Rust_primitives.Hax.Monomorphized_update_at.update_at_usize (v_A_as_ntt.[ + j ] + <: + t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) + i + sampled + <: + t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) + in + Core_models.Ops.Control_flow.ControlFlow_Continue + (v_A_as_ntt, xof_input + <: + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)) + else + let v_A_as_ntt:t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize v_A_as_ntt + i + (Rust_primitives.Hax.Monomorphized_update_at.update_at_usize (v_A_as_ntt.[ + i ] + <: + t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) + j + sampled + <: + t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) + in + Core_models.Ops.Control_flow.ControlFlow_Continue + (v_A_as_ntt, xof_input + <: + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)) + | Core_models.Result.Result_Err err -> + Core_models.Ops.Control_flow.ControlFlow_Break + (Core_models.Ops.Control_flow.ControlFlow_Break + (Core_models.Result.Result_Err err + <: + Core_models.Result.t_Result + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) + ) v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) + ) v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 34)) + with + | Core_models.Ops.Control_flow.ControlFlow_Break ret -> + Core_models.Ops.Control_flow.ControlFlow_Break + (Core_models.Ops.Control_flow.ControlFlow_Break ret + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 34)) + | Core_models.Ops.Control_flow.ControlFlow_Continue loop_res -> + Core_models.Ops.Control_flow.ControlFlow_Continue loop_res + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK) + Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (Prims.unit & + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + v_RANK) v_RANK & + t_Array u8 (mk_usize 34)))) + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 34))) + <: + Core_models.Ops.Control_flow.t_ControlFlow + (Core_models.Result.t_Result + (t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError) + (t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK & + t_Array u8 (mk_usize 34)) + with + | Core_models.Ops.Control_flow.ControlFlow_Break ret -> ret + | Core_models.Ops.Control_flow.ControlFlow_Continue (v_A_as_ntt, xof_input) -> + Core_models.Result.Result_Ok v_A_as_ntt + <: + Core_models.Result.t_Result + (t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) Hacspec_ml_kem.Sampling.t_BadRejectionSamplingRandomnessError + +/// Compute v − InverseNTT(sᵀ ◦ NTT(u)). +/// Corresponds to `compute_message` in the implementation's `matrix.rs`. +/// Used in K-PKE.Decrypt (Algorithm 15) to recover the message: +/// w ← v - NTT⁻¹(ŝᵀ ◦ NTT(u)) +let compute_message + (v_RANK: usize) + (v: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (secret_as_ntt u_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + let inner_product:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + multiply_vectors v_RANK secret_as_ntt u_as_ntt + in + let inner_product_inv:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Invert_ntt.ntt_inverse inner_product + in + sub_polynomials v inner_product_inv + +/// Compute InverseNTT(tᵀ ◦ r̂) + e₂ + message. +/// Corresponds to `compute_ring_element_v` in the implementation's `matrix.rs`. +/// Used in K-PKE.Encrypt (Algorithm 14): +/// v ← NTT⁻¹(t̂ᵀ ◦ r̂) + e₂ + μ +let compute_ring_element_v + (v_RANK: usize) + (tt_as_ntt r_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (error_2_ message: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + let inner_product:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + multiply_vectors v_RANK tt_as_ntt r_as_ntt + in + let inner_product_inv:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Invert_ntt.ntt_inverse inner_product + in + add_polynomials (add_polynomials inner_product_inv error_2_ + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + message + +/// Compute u := InvertNTT(Aᵀ ◦ r̂) + e₁. +/// Corresponds to `compute_vector_u` in the implementation's `matrix.rs`. +/// Used in K-PKE.Encrypt (Algorithm 14): +/// u ← NTT⁻¹(Âᵀ ◦ r̂) + e₁ +let compute_vector_u + (v_RANK: usize) + (a_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + (r_as_ntt error_1_: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + let a_transpose:t_Array + (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) v_RANK = + transpose v_RANK a_as_ntt + in + let product:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + multiply_matrix_by_column v_RANK a_transpose r_as_ntt + in + let + (product_inv: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK):t_Array + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Invert_ntt.ntt_inverse (product.[ i ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + in + add_vectors v_RANK product_inv error_1_ + +/// Compute t̂ := Â ◦ ŝ + ê. +/// Corresponds to `compute_As_plus_e` in the implementation's `matrix.rs`. +/// Used in K-PKE.KeyGen (Algorithm 13): +/// t̂ ← Â◦ŝ + ê +let compute_As_plus_e + (v_RANK: usize) + (a_as_ntt: + t_Array (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + v_RANK) + (s_as_ntt error_as_ntt: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + let product:t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + multiply_matrix_by_column v_RANK a_as_ntt s_as_ntt + in + add_vectors v_RANK product error_as_ntt diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ntt.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ntt.fst new file mode 100644 index 0000000000..61fee8f198 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Ntt.fst @@ -0,0 +1,509 @@ +module Hacspec_ml_kem.Ntt +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +let v_ZETA: Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 17) + +/// Montgomery constant R = 2^16 mod q. +/// In the implementation, coefficients are stored in Montgomery form (a * R mod q). +/// In the spec, we use plain modular arithmetic, so R is conceptually 1. +/// This constant documents the correspondence. +let v_MONTGOMERY_R: i32 = mk_i32 1 + +/// Montgomery domain conversion: identity in the spec. +/// In the implementation, `to_standard_domain(a)` converts from Montgomery form +/// by computing `a * MONTGOMERY_R_INV mod q`. Since the spec uses plain arithmetic +/// (effectively MONTGOMERY_R = 1), this is an identity operation. +/// Documenting this correspondence enables function-by-function verification by +/// showing that the implementation's Montgomery conversions compose to identity. +let to_standard_domain (a: Hacspec_ml_kem.Parameters.t_FieldElement) + : Hacspec_ml_kem.Parameters.t_FieldElement = a + +/// Montgomery multiplication: identity wrapper in the spec. +/// In the implementation, `montgomery_multiply_by_constant(a, c)` computes +/// `a * c * R^{-1} mod q`. In the spec, this simplifies to `a * c mod q` since R = 1. +let montgomery_multiply_by_constant (a c: Hacspec_ml_kem.Parameters.t_FieldElement) + : Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (((cast (a.Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) *! + (cast (c.Hacspec_ml_kem.Parameters.f_val <: u16) <: u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + +/// Convert a field element to its unsigned representative in [0, q). +/// Corresponds to `to_unsigned_field_modulus` / `Vector::to_unsigned_representative` +/// in the implementation. +/// In the spec, field elements are already non-negative after reduction, so this +/// is a plain modular reduction. +let to_unsigned_field_modulus (a: Hacspec_ml_kem.Parameters.t_FieldElement) + : Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__new (a.Hacspec_ml_kem.Parameters.f_val %! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16) + +let bit_rev_7_ (x: usize) : usize = + let result:usize = mk_usize 0 in + let result:usize = + Rust_primitives.Hax.Folds.fold_range (mk_i32 0) + (mk_i32 7) + (fun result temp_1_ -> + let result:usize = result in + let _:i32 = temp_1_ in + true) + result + (fun result i -> + let result:usize = result in + let i:i32 = i in + if ((x >>! i <: usize) &. mk_usize 1 <: usize) =. mk_usize 1 <: bool + then + let result:usize = result |. (mk_usize 1 <Algorithm 8 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// ```plaintext +/// Input: array f ∈ ℤ₂₅₆. +/// Output: array fˆ ∈ ℤ₂₅₆. +/// fˆ ← f +/// k ← 1 +/// for (len ← 128; len ≥ 2; len ← len/2) +/// for (start ← 0; start < 256; start ← start + 2·len) +/// zeta ← ζ^(BitRev₇(k)) mod q +/// k ← k + 1 +/// for (j ← start; j < start + len; j++) +/// t ← zeta·fˆ[j+len] +/// fˆ[j+len] ← fˆ[j] − t +/// fˆ[j] ← fˆ[j] + t +/// end for +/// end for +/// end for +/// return fˆ +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let v_ZETAS: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 128) = + let list = + [ + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1729); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2580); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3289); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2642); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 630); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1897); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 848); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1062); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1919); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 193); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 797); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2786); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3260); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 569); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1746); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 296); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2447); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1339); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1476); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3046); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 56); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2240); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1333); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1426); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2094); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 535); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2882); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2393); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2879); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1974); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 821); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 289); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 331); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3253); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1756); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1197); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2304); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2277); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2055); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 650); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1977); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2513); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 632); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2865); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 33); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1320); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1915); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2319); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1435); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 807); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 452); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1438); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2868); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1534); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2402); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2647); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2617); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1481); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 648); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2474); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3110); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1227); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 910); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 17); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2761); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 583); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2649); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1637); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 723); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2288); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1100); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1409); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2662); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3281); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 233); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 756); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2156); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3015); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3050); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1703); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1651); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2789); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1789); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1847); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 952); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1461); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2687); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 939); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2308); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2437); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2388); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 733); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2337); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 268); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 641); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1584); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2298); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2037); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 3220); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 375); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2549); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2090); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1645); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1063); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 319); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2773); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 757); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2099); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 561); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2466); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2594); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2804); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1092); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 403); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1026); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1143); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2150); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2775); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 886); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1722); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1212); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1874); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 1029); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2110); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2935); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 885); + Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 2154) + ] + in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 128); + Rust_primitives.Hax.array_of_list 128 list + +/// In the implementation, zetas are pre-multiplied by Montgomery R. +/// In the spec, ZETAS are plain values, so ZETAS_TIMES_MONTGOMERY_R == ZETAS. +/// This alias documents the correspondence with the implementation's `ZETAS_TIMES_MONTGOMERY_R`. +let v_ZETAS_TIMES_MONTGOMERY_R: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 128) = + v_ZETAS + +let get_zeta (i: usize) + : Prims.Pure Hacspec_ml_kem.Parameters.t_FieldElement + (requires i <. mk_usize 128) + (fun _ -> Prims.l_True) = v_ZETAS.[ i ] + +/// Cooley–Tukey butterfly: `(a, b, ζ) ↦ (a + ζ·b, a − ζ·b)`. +/// Used in the forward NTT. +let butterfly (zeta a b: Hacspec_ml_kem.Parameters.t_FieldElement) + : (Hacspec_ml_kem.Parameters.t_FieldElement & Hacspec_ml_kem.Parameters.t_FieldElement) = + let t:Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__mul zeta b + in + Hacspec_ml_kem.Parameters.impl_FieldElement__add a t, + Hacspec_ml_kem.Parameters.impl_FieldElement__sub a t + <: + (Hacspec_ml_kem.Parameters.t_FieldElement & Hacspec_ml_kem.Parameters.t_FieldElement) + +#push-options "--z3rlimit 150" + +/// One layer of the NTT, generic over the array length `N`. +/// The layer is characterised by `(len, groups)` where `groups = zetas.len()` +/// and `N = 2·groups·len`. Each of the `groups` butterfly groups spans `2·len` +/// consecutive coefficients, uses one zeta, and runs `len` independent +/// butterflies. +/// This is FIPS 203 Algorithm 8 lines 3-8 applied once at butterfly half-size +/// `len`. The within-chunk case (N = 16, len ∈ {2, 4, 8}) corresponds to the +/// trait\'s `ntt_layer_{1,2,3}_step`; the full-polynomial case (N = 256, +/// len = 2^layer) is what `ntt_layer` below instantiates. +let ntt_layer_n + (v_N: usize) + (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (len: usize) + (zetas: t_Slice Hacspec_ml_kem.Parameters.t_FieldElement) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (requires + len >=. mk_usize 1 && len <. mk_usize 1024 && + (Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) <. + mk_usize 1024 && + (((Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) *! + mk_usize 2 + <: + usize) *! + len + <: + usize) =. + v_N) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + v_N + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + let group:usize = i /! (mk_usize 2 *! len <: usize) in + let idx:usize = i %! (mk_usize 2 *! len <: usize) in + if idx <. len + then + (butterfly (zetas.[ group ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i +! len <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement)) + ._1 + else + (butterfly (zetas.[ group ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i -! len <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement)) + ._2) + +#pop-options + +#push-options "--z3rlimit 150" + +/// One layer of the 256-coefficient NTT. Thin wrapper over `ntt_layer_n` +/// that selects the zeta slice for this layer out of the global `ZETAS` +/// table. +/// Follows FIPS 203 Algorithm 8. Butterfly half-size `len = 2^layer`, +/// groups = `128 / len`, zetas used = `ZETAS[groups .. 2·groups]`. +let ntt_layer (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) (layer: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires layer >=. mk_usize 1 && layer <=. mk_usize 7) + (fun _ -> Prims.l_True) = + let len:usize = mk_usize 1 <Algorithm 11 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// ```plaintext +/// Input: a₀, a₁, b₀, b₁ ∈ ℤq. +/// Input: γ ∈ ℤq. +/// Output: c₀, c₁ ∈ ℤq. +/// c₀ ← a₀·b₀ + a₁·b₁·γ +/// c₁ ← a₀·b₁ + a₁·b₀ +/// return c₀, c₁ +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let base_case_multiply_even (a0 a1 b0 b1 zeta: Hacspec_ml_kem.Parameters.t_FieldElement) + : Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__add (Hacspec_ml_kem.Parameters.impl_FieldElement__mul + a0 + b0 + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (Hacspec_ml_kem.Parameters.impl_FieldElement__mul (Hacspec_ml_kem.Parameters.impl_FieldElement__mul + a1 + b1 + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + zeta + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#pop-options + +let base_case_multiply_odd (a0 a1 b0 b1: Hacspec_ml_kem.Parameters.t_FieldElement) + : Hacspec_ml_kem.Parameters.t_FieldElement = + Hacspec_ml_kem.Parameters.impl_FieldElement__add (Hacspec_ml_kem.Parameters.impl_FieldElement__mul + a0 + b1 + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (Hacspec_ml_kem.Parameters.impl_FieldElement__mul a1 b0 + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#push-options "--z3rlimit 150" + +/// Given two `Polynomial`s in their NTT representations, +/// compute their product. Given two polynomials in the NTT domain `f^` and `ĵ`, +/// the `iᵗʰ` coefficient of the product `k\u{302}` is determined by the calculation: +/// ```plaintext +/// ĥ[2·i] + ĥ[2·i + 1]X = (f^[2·i] + f^[2·i + 1]X)·(ĝ[2·i] + ĝ[2·i + 1]X) mod (X² - ζ^(2·BitRev₇(i) + 1)) +/// ``` +/// This function implements Algorithm 10 of the NIST FIPS 203 standard, which +/// is reproduced below: +/// ```plaintext +/// Input: Two arrays fˆ ∈ ℤ₂₅₆ and ĝ ∈ ℤ₂₅₆. +/// Output: An array ĥ ∈ ℤq. +/// for(i ← 0; i < 128; i++) +/// (ĥ[2i], ĥ[2i+1]) ← BaseCaseMultiply(fˆ[2i], fˆ[2i+1], ĝ[2i], ĝ[2i+1], ζ^(2·BitRev₇(i) + 1)) +/// end for +/// return ĥ +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +/// Pointwise polynomial multiplication in the NTT domain, generic over +/// the array length `N`. +/// The input is two NTT-domain arrays of `N` coefficients and an array of +/// `N/4` zetas. Consecutive 4-coefficient groups are treated as two +/// quadratic polynomials multiplied modulo `X² − ζ`: the first pair uses +/// `+ζ`, the second pair uses `−ζ`. This is the trait-compatible +/// restriction of FIPS 203 Algorithm 10. +/// When instantiated at N=256 with `zetas = ZETAS[64..128]` this is the +/// full `multiply_ntts` below. When instantiated at N=16 with 4 zetas +/// this is the trait\'s `ntt_multiply(lhs, rhs, z0, z1, z2, z3)`. +let ntt_multiply_n + (v_N: usize) + (p1 p2: t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (zetas: t_Slice Hacspec_ml_kem.Parameters.t_FieldElement) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement v_N) + (requires + (Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) <. + mk_usize 1024 && + ((Core_models.Slice.impl__len #Hacspec_ml_kem.Parameters.t_FieldElement zetas <: usize) *! + mk_usize 4 + <: + usize) =. + v_N) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + v_N + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + let group:usize = i /! mk_usize 4 in + let zeta:Hacspec_ml_kem.Parameters.t_FieldElement = + if (i %! mk_usize 4 <: usize) <. mk_usize 2 + then zetas.[ group ] + else + Hacspec_ml_kem.Parameters.impl_FieldElement__neg (zetas.[ group ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + in + if (i %! mk_usize 2 <: usize) =. mk_usize 0 + then + base_case_multiply_even (p1.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p1.[ i +! mk_usize 1 <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p2.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p2.[ i +! mk_usize 1 <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + zeta + else + base_case_multiply_odd (p1.[ i -! mk_usize 1 <: usize ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (p1.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p2.[ i -! mk_usize 1 <: usize ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + (p2.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement)) + +#pop-options + +let multiply_ntts (p1 p2: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + ntt_multiply_n (mk_usize 256) + p1 + p2 + (v_ZETAS.[ { + Core_models.Ops.Range.f_start = mk_usize 64; + Core_models.Ops.Range.f_end = mk_usize 128 + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice Hacspec_ml_kem.Parameters.t_FieldElement) + +let vector_ntt + (v_RANK: usize) + (vector: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK = + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + ntt (vector.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.Hash_functions.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.Hash_functions.fst new file mode 100644 index 0000000000..b7516449c9 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.Hash_functions.fst @@ -0,0 +1,36 @@ +module Hacspec_ml_kem.Parameters.Hash_functions +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +assume +val v_G': input: t_Slice u8 -> t_Array u8 (mk_usize 64) + +unfold +let v_G = v_G' + +let v_H_DIGEST_SIZE: usize = mk_usize 32 + +assume +val v_H': input: t_Slice u8 -> t_Array u8 (mk_usize 32) + +unfold +let v_H = v_H' + +assume +val v_PRF': v_LEN: usize -> input: t_Slice u8 -> t_Array u8 v_LEN + +unfold +let v_PRF (v_LEN: usize) = v_PRF' v_LEN + +assume +val v_XOF': v_LEN: usize -> input: t_Slice u8 -> t_Array u8 v_LEN + +unfold +let v_XOF (v_LEN: usize) = v_XOF' v_LEN + +assume +val v_J': v_LEN: usize -> input: t_Slice u8 -> t_Array u8 v_LEN + +unfold +let v_J (v_LEN: usize) = v_J' v_LEN diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.fst new file mode 100644 index 0000000000..7b9fc6f805 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Parameters.fst @@ -0,0 +1,409 @@ +module Hacspec_ml_kem.Parameters +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +/// Field modulus: 3329 +let v_FIELD_MODULUS: u16 = mk_u16 3329 + +/// Each field element needs floor(log_2(FIELD_MODULUS)) + 1 = 12 bits to represent +let v_BITS_PER_COEFFICIENT: usize = mk_usize 12 + +/// Coefficients per ring element +let v_COEFFICIENTS_IN_RING_ELEMENT: usize = mk_usize 256 + +/// Bits required per (uncompressed) ring element +let v_BITS_PER_RING_ELEMENT: usize = v_COEFFICIENTS_IN_RING_ELEMENT *! mk_usize 12 + +/// Bytes required per (uncompressed) ring element +let v_BYTES_PER_RING_ELEMENT: usize = v_BITS_PER_RING_ELEMENT /! mk_usize 8 + +/// Seed size for rejection sampling. +/// See for some background regarding +/// this choice. +let v_REJECTION_SAMPLING_SEED_SIZE: usize = mk_usize 168 *! mk_usize 5 + +/// ML-KEM parameter set +type t_MlKemParams = { + f_rank:f_rank: usize{b2t (f_rank <=. mk_usize 4 <: bool)}; + f_eta1:f_eta1: usize{b2t ((f_eta1 <=. mk_usize 2 <: bool) || (f_eta1 =. mk_usize 3 <: bool))}; + f_eta2:f_eta2: usize{b2t (f_eta2 =. mk_usize 2 <: bool)}; + f_du:f_du: usize{b2t ((f_du =. mk_usize 10 <: bool) || (f_du =. mk_usize 11 <: bool))}; + f_dv:f_dv: usize{b2t ((f_dv =. mk_usize 4 <: bool) || (f_dv =. mk_usize 5 <: bool))} +} + +let impl_MlKemParams__tt_as_ntt_encoded_size (self: t_MlKemParams) : usize = + self.f_rank *! v_BYTES_PER_RING_ELEMENT + +let impl_MlKemParams__ek_size (self: t_MlKemParams) : usize = + (impl_MlKemParams__tt_as_ntt_encoded_size self <: usize) +! mk_usize 32 + +let impl_MlKemParams__dk_pke_size (self: t_MlKemParams) : usize = + self.f_rank *! v_BYTES_PER_RING_ELEMENT + +let impl_MlKemParams__dk_size (self: t_MlKemParams) : usize = + (((impl_MlKemParams__dk_pke_size self <: usize) +! (impl_MlKemParams__ek_size self <: usize) + <: + usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + +let impl_MlKemParams__u_encoded_size (self: t_MlKemParams) : usize = + ((self.f_rank *! v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! self.f_du <: usize) /! mk_usize 8 + +let impl_MlKemParams__vv_encoded_size (self: t_MlKemParams) : usize = + (v_COEFFICIENTS_IN_RING_ELEMENT *! self.f_dv <: usize) /! mk_usize 8 + +let impl_MlKemParams__ciphertext_size (self: t_MlKemParams) : usize = + (impl_MlKemParams__u_encoded_size self <: usize) +! + (impl_MlKemParams__vv_encoded_size self <: usize) + +let v_ML_KEM_512_: t_MlKemParams = + { + f_rank = mk_usize 2; + f_eta1 = mk_usize 3; + f_eta2 = mk_usize 2; + f_du = mk_usize 10; + f_dv = mk_usize 4 + } + <: + t_MlKemParams + +let v_ML_KEM_768_: t_MlKemParams = + { + f_rank = mk_usize 3; + f_eta1 = mk_usize 2; + f_eta2 = mk_usize 2; + f_du = mk_usize 10; + f_dv = mk_usize 4 + } + <: + t_MlKemParams + +let v_ML_KEM_1024_: t_MlKemParams = + { + f_rank = mk_usize 4; + f_eta1 = mk_usize 2; + f_eta2 = mk_usize 2; + f_du = mk_usize 11; + f_dv = mk_usize 5 + } + <: + t_MlKemParams + +let v_ML_KEM_512_EK_SIZE: usize = mk_usize 800 + +let v_ML_KEM_512_DK_PKE_SIZE: usize = mk_usize 768 + +let v_ML_KEM_512_DK_SIZE: usize = mk_usize 1632 + +let v_ML_KEM_512_U_SIZE: usize = mk_usize 640 + +let v_ML_KEM_512_V_SIZE: usize = mk_usize 128 + +let v_ML_KEM_512_CT_SIZE: usize = mk_usize 768 + +let v_ML_KEM_512_J_INPUT_SIZE: usize = mk_usize 800 + +let v_ML_KEM_768_EK_SIZE: usize = mk_usize 1184 + +let v_ML_KEM_768_DK_PKE_SIZE: usize = mk_usize 1152 + +let v_ML_KEM_768_DK_SIZE: usize = mk_usize 2400 + +let v_ML_KEM_768_U_SIZE: usize = mk_usize 960 + +let v_ML_KEM_768_V_SIZE: usize = mk_usize 128 + +let v_ML_KEM_768_CT_SIZE: usize = mk_usize 1088 + +let v_ML_KEM_768_J_INPUT_SIZE: usize = mk_usize 1120 + +let v_ML_KEM_1024_EK_SIZE: usize = mk_usize 1568 + +let v_ML_KEM_1024_DK_PKE_SIZE: usize = mk_usize 1536 + +let v_ML_KEM_1024_DK_SIZE: usize = mk_usize 3168 + +let v_ML_KEM_1024_U_SIZE: usize = mk_usize 1408 + +let v_ML_KEM_1024_V_SIZE: usize = mk_usize 160 + +let v_ML_KEM_1024_CT_SIZE: usize = mk_usize 1568 + +let v_ML_KEM_1024_J_INPUT_SIZE: usize = mk_usize 1600 + +/// Rank-generic CPA ciphertext size, matching the Spec.MLKEM +/// `v_CPA_CIPHERTEXT_SIZE` shape. Use this in rank-generic +/// `hax_lib::requires`/`ensures` annotations where threading a +/// `MlKemParams` value would be an architectural refactor. For +/// fixed-rank consumers, prefer the `ML_KEM_{512,768,1024}_CT_SIZE` +/// constants directly; for `MlKemParams`-aware callers, prefer +/// `MlKemParams::ciphertext_size()`. +let cpa_ciphertext_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + if rank =. mk_usize 2 + then v_ML_KEM_512_CT_SIZE + else if rank =. mk_usize 3 then v_ML_KEM_768_CT_SIZE else v_ML_KEM_1024_CT_SIZE + +/// Rank-to-`MlKemParams` lookup for rank-generic consumers that need to +/// invoke a `MlKemParams`-shape Hacspec function (e.g. +/// `Hacspec_ml_kem.Ind_cca.generate_keypair`) from an +/// `hax_lib::requires`/`ensures` annotation. This is the canonical +/// adapter from the libcrux-side `const K: usize` shape to the +/// Hacspec-side `params: MlKemParams` shape. +let rank_to_params (rank: usize) + : Prims.Pure t_MlKemParams + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + if rank =. mk_usize 2 + then v_ML_KEM_512_ + else if rank =. mk_usize 3 then v_ML_KEM_768_ else v_ML_KEM_1024_ + +/// Rank predicate: ML-KEM is parameterised over rank ∈ {2, 3, 4}. +/// Use in `hax_lib::requires` to express rank refinement in pure Rust. +let is_rank (rank: usize) : bool = rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4 + +/// Shared-secret length in bytes (FIPS 203 §7). +let v_SHARED_SECRET_SIZE: usize = mk_usize 32 + +/// CPA key-generation seed length in bytes (FIPS 203 §7). +let v_CPA_KEY_GENERATION_SEED_SIZE: usize = mk_usize 32 + +/// Rank-generic encoded-NTT-vector size: `rank * 384`. Mirrors +/// `Spec.MLKEM.v_T_AS_NTT_ENCODED_SIZE` and +/// `Spec.MLKEM.v_RANKED_BYTES_PER_RING_ELEMENT`. +let tt_as_ntt_encoded_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = rank *! v_BYTES_PER_RING_ELEMENT + +/// Synonym of `t_as_ntt_encoded_size` matching `Spec.MLKEM`\'s +/// `v_RANKED_BYTES_PER_RING_ELEMENT` naming. +let ranked_bytes_per_ring_element (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = rank *! v_BYTES_PER_RING_ELEMENT + +/// CPA encryption-key size: `rank * 384 + 32`. +let cpa_public_key_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = (rank *! v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + +/// CPA decryption-key size: `rank * 384`. +let cpa_private_key_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = rank *! v_BYTES_PER_RING_ELEMENT + +/// CCA decapsulation-key size: `cpa_private_key + cpa_public_key + H_DIGEST + z`. +let cca_private_key_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + (((cpa_private_key_size rank <: usize) +! (cpa_public_key_size rank <: usize) <: usize) +! + Hacspec_ml_kem.Parameters.Hash_functions.v_H_DIGEST_SIZE + <: + usize) +! + mk_usize 32 + +/// `du` compression factor for vector u: 10 for ranks 2,3 and 11 for rank 4. +let vector_u_compression_factor (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = if rank =. mk_usize 4 then mk_usize 11 else mk_usize 10 + +/// `dv` compression factor for ring element v: 4 for ranks 2,3 and 5 for rank 4. +let vector_v_compression_factor (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = if rank =. mk_usize 4 then mk_usize 5 else mk_usize 4 + +/// Per-block (per-ring-element) size of c1 in bytes: `(256 * du)/8`. +let c1_block_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + (v_COEFFICIENTS_IN_RING_ELEMENT *! (vector_u_compression_factor rank <: usize) <: usize) /! + mk_usize 8 + +/// Total c1 (encoded vector u) size in bytes: `rank * c1_block_size`. +let c1_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = rank *! (c1_block_size rank <: usize) + +/// Total c2 (encoded ring element v) size in bytes: `(256 * dv)/8`. +let c2_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + (v_COEFFICIENTS_IN_RING_ELEMENT *! (vector_v_compression_factor rank <: usize) <: usize) /! + mk_usize 8 + +/// `eta1` CBD parameter: 3 for rank 2, 2 for ranks 3,4. +let eta1 (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = if rank =. mk_usize 2 then mk_usize 3 else mk_usize 2 + +/// `eta2` CBD parameter: always 2. +let eta2 (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = + let _:usize = rank in + mk_usize 2 + +/// PRF output size for eta1 CBD sampling: `64 * eta1`. +let eta1_randomness_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = mk_usize 64 *! (eta1 rank <: usize) + +/// PRF output size for eta2 CBD sampling: `64 * eta2`. +let eta2_randomness_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = mk_usize 64 *! (eta2 rank <: usize) + +/// Implicit-rejection hash input length: `32 + cpa_ciphertext_size`. +let implicit_rejection_hash_input_size (rank: usize) + : Prims.Pure usize + (requires rank =. mk_usize 2 || rank =. mk_usize 3 || rank =. mk_usize 4) + (fun _ -> Prims.l_True) = mk_usize 32 +! (cpa_ciphertext_size rank <: usize) + +/// An ML-KEM field element: +/// - after reduction modulo FIELD_MODULUS, it is an integer in the range [0, FIELD_MODULUS - 1] +/// - it is represented as a u16 +type t_FieldElement = { f_val:f_val: u16{b2t (f_val <. v_FIELD_MODULUS <: bool)} } + +let impl_1: Core_models.Clone.t_Clone t_FieldElement = + { f_clone = (fun x -> x); f_clone_pre = (fun _ -> True); f_clone_post = (fun _ _ -> True) } + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_2': Core_models.Marker.t_Copy t_FieldElement + +unfold +let impl_2 = impl_2' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_3': Core_models.Marker.t_StructuralPartialEq t_FieldElement + +unfold +let impl_3 = impl_3' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_4': Core_models.Cmp.t_PartialEq t_FieldElement t_FieldElement + +unfold +let impl_4 = impl_4' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_5': Core_models.Cmp.t_Eq t_FieldElement + +unfold +let impl_5 = impl_5' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_6': Core_models.Cmp.t_PartialOrd t_FieldElement t_FieldElement + +unfold +let impl_6 = impl_6' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_7': Core_models.Cmp.t_Ord t_FieldElement + +unfold +let impl_7 = impl_7' + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl_8': Core_models.Fmt.t_Debug t_FieldElement + +unfold +let impl_8 = impl_8' + +let impl_FieldElement__new (v_val: u16) + : Prims.Pure t_FieldElement (requires v_val <. v_FIELD_MODULUS) (fun _ -> Prims.l_True) = + { f_val = v_val } <: t_FieldElement + +/// Reduce an arbitrary `i16` (e.g. an impl-side coefficient or +/// Montgomery-domain value) into a canonical `FieldElement` in +/// [0, FIELD_MODULUS). Used by the impl→spec lift functions +/// (`Libcrux_ml_kem.Vector.to_spec_*_t`) to bridge the trait-layer +/// `i16` representation to the spec-layer `FieldElement` form. +let impl_FieldElement__from_i16 (v: i16) : t_FieldElement = + let q:i32 = cast (v_FIELD_MODULUS <: u16) <: i32 in + let r:u16 = cast ((((cast (v <: i16) <: i32) %! q <: i32) +! q <: i32) %! q <: i32) <: u16 in + impl_FieldElement__new r + +/// Addition in ℤ/q. +let impl_FieldElement__add (self other: t_FieldElement) : t_FieldElement = + impl_FieldElement__new (cast (((cast (self.f_val <: u16) <: u32) +! + (cast (other.f_val <: u16) <: u32) + <: + u32) %! + (cast (v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + +/// Subtraction in ℤ/q. Adding q avoids unsigned underflow before reducing. +let impl_FieldElement__sub (self other: t_FieldElement) : t_FieldElement = + impl_FieldElement__new (cast ((((cast (self.f_val <: u16) <: u32) +! + (cast (v_FIELD_MODULUS <: u16) <: u32) + <: + u32) -! + (cast (other.f_val <: u16) <: u32) + <: + u32) %! + (cast (v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + +/// Multiplication in ℤ/q. +let impl_FieldElement__mul (self other: t_FieldElement) : t_FieldElement = + impl_FieldElement__new (cast (((cast (self.f_val <: u16) <: u32) *! + (cast (other.f_val <: u16) <: u32) + <: + u32) %! + (cast (v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + +/// Additive inverse in ℤ/q. `a.neg()` = q − a (0 when a = 0). +let impl_FieldElement__neg (self: t_FieldElement) : t_FieldElement = + impl_FieldElement__new ((v_FIELD_MODULUS -! self.f_val <: u16) %! v_FIELD_MODULUS <: u16) + +assume val createi + (#v_T: Type0) + (v_N: usize) + (#v_F: Type0) + (f: (x:usize{x <. v_N}) -> v_T) + : t_Array v_T v_N + +assume val createi_lemma + (#v_T: Type0) + (v_N: usize) + (#v_F: Type0) + (f: (x:usize{x <. v_N}) -> v_T) + (i: usize{i <. v_N}) + : Lemma (Seq.index (createi #v_T v_N #v_F f) (v i) == f i) + [SMTPat (Seq.index (createi #v_T v_N #v_F f) (v i))] diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Polynomial.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Polynomial.fst new file mode 100644 index 0000000000..9d53a9b3b5 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Polynomial.fst @@ -0,0 +1,221 @@ +module Hacspec_ml_kem.Polynomial +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +/// The zero polynomial. Corresponds to `PolynomialRingElement::ZERO()` in the implementation. +let poly_zero (_: Prims.unit) : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 256) + +/// Add `rhs` into `self` in-place. Corresponds to `PolynomialRingElement::add_to_ring_element()`. +/// Note: In the spec we return a new polynomial; in the implementation this is `&mut self`. +/// The mathematical operation is identical. +let add_to_ring_element (lhs rhs: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (((cast ((lhs.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast ((rhs.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +/// Barrett reduction of all coefficients. Corresponds to `PolynomialRingElement::poly_barrett_reduce()`. +/// In the spec, this is a no-op modular reduction since we always work with exact arithmetic. +/// In the implementation, this is needed because intermediate values may exceed the field modulus. +let poly_barrett_reduce (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Parameters.impl_FieldElement__new ((p.[ i ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val %! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +/// Subtract `b` from `a` and reduce. Corresponds to `PolynomialRingElement::subtract_reduce()`. +let subtract_reduce (a b: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast ((((cast ((a.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) -! + (cast ((b.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +/// NTT-domain polynomial multiplication. Corresponds to `PolynomialRingElement::ntt_multiply()`. +/// Given two polynomials in NTT form, returns their product in NTT form. +let ntt_multiply (a b: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Ntt.multiply_ntts a b + +/// Fused add(error, message, result). Corresponds to `PolynomialRingElement::add_message_error_reduce()`. +/// Computes: self + message + result, where: +/// - `self` is error_2 +/// - `message` is the decompressed message +/// - `result` is NTT⁻¹(tᵀ ◦ r̂) +/// In the implementation, this fuses the addition to avoid extra temporaries. +let add_message_error_reduce + (error_2_ message ntt_product: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast ((((cast ((error_2_.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast ((message.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) +! + (cast ((ntt_product.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +/// Fused add(self, error) with reduction. Corresponds to `PolynomialRingElement::add_error_reduce()`. +/// Used to compute u = NTT⁻¹(Aᵀ ◦ r̂) + e₁ +let add_error_reduce + (ntt_product error: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (((cast ((ntt_product.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast ((error.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +/// Fused add(self, error) for NTT-domain values. Corresponds to `PolynomialRingElement::add_standard_error_reduce()`. +/// Used to compute t̂ = Â◦ŝ + ê (both operands are in NTT domain). +let add_standard_error_reduce + (ntt_product error_ntt: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun j -> + let j:usize = j in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (cast (((cast ((ntt_product.[ j ] + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) +! + (cast ((error_ntt.[ j ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val + <: + u16) + <: + u32) + <: + u32) %! + (cast (Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: u16) <: u32) + <: + u32) + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Sampling.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Sampling.fst new file mode 100644 index 0000000000..f5ce076b7f --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Sampling.fst @@ -0,0 +1,307 @@ +module Hacspec_ml_kem.Sampling +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +type t_BadRejectionSamplingRandomnessError = + | BadRejectionSamplingRandomnessError : t_BadRejectionSamplingRandomnessError + +[@@ FStar.Tactics.Typeclasses.tcinstance] +assume +val impl': Core_models.Fmt.t_Debug t_BadRejectionSamplingRandomnessError + +unfold +let impl = impl' + +/// If `bytes` contains a set of uniformly random bytes, this function +/// uniformly samples a ring element `â` that is treated as being the NTT representation +/// of the corresponding polynomial `a`. +/// Since rejection sampling is used, it is possible the supplied bytes are +/// not enough to sample the element, in which case an `Err` is returned and the +/// caller must try again with a fresh set of bytes. +/// This function partially implements Algorithm 6 of the NIST FIPS 203 standard, +/// We say "partially" because this implementation only accepts a finite set of +/// bytes as input and returns an error if the set is not enough; Algorithm 6 of +/// the FIPS 203 standard on the other hand samples from an infinite stream of bytes +/// until the ring element is filled. Algorithm 6 is reproduced below: +/// ```plaintext +/// Input: byte stream B ∈ 𝔹*. +/// Output: array â ∈ ℤ₂₅₆. +/// i ← 0 +/// j ← 0 +/// while j < 256 do +/// d₁ ← B[i] + 256·(B[i+1] mod 16) +/// d₂ ← ⌊B[i+1]/16⌋ + 16·B[i+2] +/// if d₁ < q then +/// â[j] ← d₁ +/// j ← j + 1 +/// end if +/// if d₂ < q and j < 256 then +/// â[j] ← d₂ +/// j ← j + 1 +/// end if +/// i ← i + 3 +/// end while +/// return â +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +/// One iteration of rejection sampling. +/// Decodes 24 bytes into 16 u16 values using 12-bit little-endian packing +/// (FIPS 203 Algorithm 6, inner loop body), keeps those `< q`, and returns +/// the accepted values packed at the start of a 16-element array together +/// with the count. The unused tail of the array is filled with zeros. +/// This is the natural unit of rejection sampling at the trait boundary +/// (`Operations::rej_sample` consumes exactly 24 bytes); `sample_ntt` +/// below repeatedly applies this step until 256 coefficients have been +/// accepted or the byte stream is exhausted. +let rej_sample_step (bytes: t_Array u8 (mk_usize 24)) + : (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16) & usize) = + let decoded:t_Array u16 (mk_usize 16) = + Hacspec_ml_kem.Serialize.byte_decode_generic (mk_usize 2) + (mk_usize 16) + (mk_usize 24) + (mk_usize 192) + bytes + (mk_usize 12) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16) = + Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 16) + in + let (count: usize):usize = mk_usize 0 in + let (count: usize), (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16)) = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + (mk_usize 16) + (fun temp_0_ i -> + let + (count: usize), (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16)) = + temp_0_ + in + let i:usize = i in + count <=. i <: bool) + (count, result <: (usize & t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16))) + (fun temp_0_ i -> + let + (count: usize), (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16)) = + temp_0_ + in + let i:usize = i in + if (decoded.[ i ] <: u16) <. Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: bool + then + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize result + count + (Hacspec_ml_kem.Parameters.impl_FieldElement__new (decoded.[ i ] <: u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + in + let count:usize = count +! mk_usize 1 in + count, result + <: + (usize & t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16)) + else + count, result + <: + (usize & t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16))) + in + result, count <: (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 16) & usize) + +let sample_ntt (v_N v_N8 v_N12 v_N96: usize) (bytes: t_Array u8 v_N12) + : Prims.Pure + (Core_models.Result.t_Result (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + t_BadRejectionSamplingRandomnessError) + (requires + v_N <=. (Hacspec_ml_kem.Serialize.v_MAX_BYTES /! mk_usize 96 <: usize) && + v_N8 =. (v_N *! mk_usize 8 <: usize) && + v_N12 =. (v_N *! mk_usize 12 <: usize) && + v_N96 =. (v_N12 *! mk_usize 8 <: usize)) + (fun _ -> Prims.l_True) = + let decoded:t_Array u16 v_N8 = + Hacspec_ml_kem.Serialize.byte_decode_generic v_N v_N8 v_N12 v_N96 bytes (mk_usize 12) + in + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.repeat (Hacspec_ml_kem.Parameters.impl_FieldElement__new (mk_u16 0) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + (mk_usize 256) + in + let (sampled_coefficients: usize):usize = mk_usize 0 in + let + (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)), + (sampled_coefficients: usize) = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + v_N8 + (fun temp_0_ temp_1_ -> + let + (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)), + (sampled_coefficients: usize) = + temp_0_ + in + let _:usize = temp_1_ in + true) + (result, sampled_coefficients + <: + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) & usize)) + (fun temp_0_ i -> + let + (result: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)), + (sampled_coefficients: usize) = + temp_0_ + in + let i:usize = i in + if + ((decoded.[ i ] <: u16) <. Hacspec_ml_kem.Parameters.v_FIELD_MODULUS <: bool) && + (sampled_coefficients <. mk_usize 256 <: bool) + then + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Rust_primitives.Hax.Monomorphized_update_at.update_at_usize result + sampled_coefficients + (Hacspec_ml_kem.Parameters.impl_FieldElement__new (decoded.[ i ] <: u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + in + result, sampled_coefficients +! mk_usize 1 + <: + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) & usize) + else + result, sampled_coefficients + <: + (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) & usize)) + in + if sampled_coefficients =. mk_usize 256 + then + Core_models.Result.Result_Ok result + <: + Core_models.Result.t_Result (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + t_BadRejectionSamplingRandomnessError + else + Core_models.Result.Result_Err + (BadRejectionSamplingRandomnessError <: t_BadRejectionSamplingRandomnessError) + <: + Core_models.Result.t_Result (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + t_BadRejectionSamplingRandomnessError + +let sum_coins (eta: usize) (coins: t_Slice bool) + : Prims.Pure Hacspec_ml_kem.Parameters.t_FieldElement + (requires eta <=. mk_usize 4 && (Core_models.Slice.impl__len #bool coins <: usize) =. eta) + (ensures + fun r -> + let r:Hacspec_ml_kem.Parameters.t_FieldElement = r in + r.Hacspec_ml_kem.Parameters.f_val <=. (cast (eta <: usize) <: u16)) = + let _:Prims.unit = () <: Prims.unit in + let (sum: u16):u16 = mk_u16 0 in + let sum:u16 = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + eta + (fun sum i -> + let sum:u16 = sum in + let i:usize = i in + sum <=. (cast (i <: usize) <: u16) <: bool) + sum + (fun sum i -> + let sum:u16 = sum in + let i:usize = i in + let sum:u16 = sum +! (cast (coins.[ i ] <: bool) <: u16) in + sum) + in + Hacspec_ml_kem.Parameters.impl_FieldElement__new sum + +#push-options "--z3rlimit 150" + +/// Given a series of uniformly random bytes in `randomness`, sample +/// a ring element from a binomial distribution centered at 0 that uses two sets +/// of `eta` coin flips. If, for example, +/// `eta = ETA`, each ring coefficient is a value `v` such +/// such that `v ∈ {-ETA, -ETA + 1, ..., 0, ..., ETA + 1, ETA}` and: +/// ```plaintext +/// - If v < 0, Pr[v] = Pr[-v] +/// - If v >= 0, Pr[v] = BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) / 2 ^ (2 * ETA) +/// ``` +/// The values `v < 0` are mapped to the appropriate `KyberFieldElement`. +/// The expected value is: +/// ```plaintext +/// E[X] = (-ETA)Pr[-ETA] + (-(ETA - 1))Pr[-(ETA - 1)] + ... + (ETA - 1)Pr[ETA - 1] + (ETA)Pr[ETA] +/// = 0 since Pr[-v] = Pr[v] when v < 0. +/// ``` +/// And the variance is: +/// ```plaintext +/// Var(X) = E[(X - E[X])^2] +/// = E[X^2] +/// = sum_(v=-ETA to ETA)v^2 * (BINOMIAL_COEFFICIENT(2 * ETA; ETA - v) / 2^(2 * ETA)) +/// = ETA / 2 +/// ``` +/// This function implements Algorithm 7 of the NIST FIPS 203 standard, which is +/// reproduced below: +/// ```plaintext +/// Input: byte array B ∈ 𝔹^{64η}. +/// Output: array f ∈ ℤ₂₅₆. +/// b ← BytesToBits(B) +/// for (i ← 0; i < 256; i++) +/// x ← ∑(j=0 to η - 1) b[2iη + j] +/// y ← ∑(j=0 to η - 1) b[2iη + η + j] +/// f[i] ← x−y mod q +/// end for +/// return f +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let sample_poly_cbd (v_ETA64 v_ETA512 eta: usize) (bytes: t_Array u8 v_ETA64) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires + eta <=. mk_usize 4 && v_ETA64 =. (eta *! mk_usize 64 <: usize) && + v_ETA512 =. (eta *! mk_usize 512 <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let (bits: t_Array bool v_ETA512):t_Array bool v_ETA512 = + Hacspec_ml_kem.Serialize.bytes_to_bits v_ETA64 v_ETA512 bytes + in + Hacspec_ml_kem.Parameters.createi #Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256) + #(usize -> Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + let (x: Hacspec_ml_kem.Parameters.t_FieldElement):Hacspec_ml_kem.Parameters.t_FieldElement = + sum_coins eta + (bits.[ { + Core_models.Ops.Range.f_start = (mk_usize 2 *! i <: usize) *! eta <: usize; + Core_models.Ops.Range.f_end + = + ((mk_usize 2 *! i <: usize) *! eta <: usize) +! eta <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice bool) + in + let (y: Hacspec_ml_kem.Parameters.t_FieldElement):Hacspec_ml_kem.Parameters.t_FieldElement = + sum_coins eta + (bits.[ { + Core_models.Ops.Range.f_start + = + ((mk_usize 2 *! i <: usize) *! eta <: usize) +! eta <: usize; + Core_models.Ops.Range.f_end + = + ((mk_usize 2 *! i <: usize) *! eta <: usize) +! (mk_usize 2 *! eta <: usize) + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice bool) + in + Hacspec_ml_kem.Parameters.impl_FieldElement__new (((x.Hacspec_ml_kem.Parameters.f_val +! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16) -! + y.Hacspec_ml_kem.Parameters.f_val + <: + u16) %! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16)) + +#pop-options diff --git a/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Serialize.fst b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Serialize.fst new file mode 100644 index 0000000000..b84a67d424 --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Hacspec_ml_kem.Serialize.fst @@ -0,0 +1,949 @@ +module Hacspec_ml_kem.Serialize +#set-options "--fuel 0 --ifuel 1 --z3rlimit 15" +open FStar.Mul +open Core_models + +let v_MAX_BYTES: usize = mk_usize 16384 + +#push-options "--z3rlimit 150" + +/// Converts a set of bytes in `bytes` into a set of bits. +/// This function implements Algorithm 3 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// ```plaintext +/// Input: byte array B ∈ 𝔹ˡ. +/// Output: bit array b ∈ {0,1}⁸ˡ. +/// for (i ← 0; i < l; i++) +/// for(j ← 0; j < 8; j++) +/// b[8i + j] ← B[i] mod 2 +/// B[i] ← ⌊B[i]/2⌋ +/// end for +/// end for +/// return b +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let bytes_to_bits (v_N v_N8: usize) (bytes: t_Array u8 v_N) + : Prims.Pure (t_Array bool v_N8) + (requires v_N <. mk_usize 16384 && v_N8 =. (v_N *! mk_usize 8 <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + Hacspec_ml_kem.Parameters.createi #bool + v_N8 + #(usize -> bool) + (fun i -> + let i:usize = i in + (((bytes.[ i /! mk_usize 8 <: usize ] <: u8) >>! (i %! mk_usize 8 <: usize) <: u8) &. + mk_u8 1 + <: + u8) =. + mk_u8 1 + <: + bool) + +#pop-options + +#push-options "--z3rlimit 150" + +/// Converts a bit string `bits` into an array of bytes. This function asserts +/// that `bits.len()` is a multiple of 8. +/// This function implements Algorithm 2 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// ```plaintext +/// Input: bit array b ∈ {0,1}⁸ˡ. +/// Output: byte array B ∈ 𝔹ˡ. +/// B ← (0,...,0) +/// for (i ← 0; i < 8l; i++) +/// B[⌊i/8⌋] ← B[⌊i/8⌋] + b[i]·2^{i} mod 8 +/// end for +/// return B +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let bits_to_bytes (v_N v_N8: usize) (bv: t_Array bool v_N8) + : Prims.Pure (t_Array u8 v_N) + (requires v_N <. mk_usize 16384 && v_N8 =. (v_N *! mk_usize 8 <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + Hacspec_ml_kem.Parameters.createi #u8 + v_N + #(usize -> u8) + (fun i -> + let i:usize = i in + (((((((cast (bv.[ mk_usize 8 *! i <: usize ] <: bool) <: u8) |. + ((cast (bv.[ (mk_usize 8 *! i <: usize) +! mk_usize 1 <: usize ] <: bool) <: u8) <Algorithm 4 of the NIST FIPS 203 standard, which is +/// reproduced below: +/// ```plaintext +/// Input: integer array F ∈ ℤₘ²⁵⁶, where m = 2ᵈ if d < 12 and m = q if d = 12. +/// Output: byte array B ∈ 𝔹^{32d}. +/// for(i ← 0; i < 256; i++) +/// a ← F[i] +/// for(j ← 0; j < d; j++) +/// b[i·d + j] ← a mod 2 +/// a ← (a − b[i·d + j])/2 +/// end for +/// B ← BitsToBytes(b) +/// return B +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let bitvector_from_bounded_ints (v_N v_Nd: usize) (input: t_Array u16 v_N) (d: usize) + : Prims.Pure (t_Array bool v_Nd) + (requires + v_N <. mk_usize 16384 && d <=. Hacspec_ml_kem.Parameters.v_BITS_PER_COEFFICIENT && + v_Nd =. (v_N *! d <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + Hacspec_ml_kem.Parameters.createi #bool + v_Nd + #(usize -> bool) + (fun i -> + let i:usize = i in + (((input.[ i /! d <: usize ] <: u16) >>! (i %! d <: usize) <: u16) &. mk_u16 1 <: u16) =. + mk_u16 1 + <: + bool) + +#pop-options + +#push-options "--z3rlimit 150" + +let byte_encode + (v_D32 v_D256: usize) + (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (d: usize) + : Prims.Pure (t_Array u8 v_D32) + (requires + d <=. Hacspec_ml_kem.Parameters.v_BITS_PER_COEFFICIENT && + v_D32 =. (mk_usize 32 *! d <: usize) && + v_D256 =. (mk_usize 256 *! d <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let (p_raw: t_Array u16 (mk_usize 256)):t_Array u16 (mk_usize 256) = + Hacspec_ml_kem.Parameters.createi #u16 + (mk_usize 256) + #(usize -> u16) + (fun i -> + let i:usize = i in + (p.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement).Hacspec_ml_kem.Parameters.f_val) + in + let bv:t_Array bool v_D256 = bitvector_from_bounded_ints (mk_usize 256) v_D256 p_raw d in + bits_to_bytes v_D32 v_D256 bv + +#pop-options + +#push-options "--z3rlimit 300" + +/// Given a series of bytes representing a ring element in `re_bytes`, +/// first convert them into a vector of bits in little-endian order; i.e. +/// the least significant `bits_per_coefficient` of `re_bytes[0]` +/// are the first set of bits in the bitstream. +/// This vector is deserialized into a `Polynomial` structure. +/// The first `bits_per_coefficient` represent the first coefficient of +/// the ring element, the second `bits_per_coefficient` the second coefficient, +/// and so on. +/// N.B.: This function asserts that `bits_per_coefficient <= 12` +/// This function implements Algorithm 5 of the NIST FIPS 203 +/// standard, which is reproduced below: +/// ```plaintext +/// Input: byte array B ∈ 𝔹^{32d}. +/// Output: integer array F ∈ ℤₘ²⁵⁶, where m = 2ᵈ if d < 12 and m = q if d = 12. +/// b ← BytesToBits(B) +/// for (i ← 0; i < 256; i++) +/// F[i] ← ∑(j = 0 to d−1) b[i·d + j] · 2ʲ mod m +/// end for +/// return F +/// ``` +/// The NIST FIPS 203 standard can be found at +/// . +let bitvector_to_bounded_ints (v_N v_Nd: usize) (input: t_Array bool v_Nd) (d: usize) + : Prims.Pure (t_Array u16 v_N) + (requires + v_N <. mk_usize 16384 && d <=. Hacspec_ml_kem.Parameters.v_BITS_PER_COEFFICIENT && + v_Nd =. (v_N *! d <: usize)) + (ensures + fun result -> + let result:t_Array u16 v_N = result in + forall (i: usize). + b2t (i <. v_N <: bool) ==> + b2t ((result.[ i ] <: u16) <. (mk_u16 1 < u16) + (fun i -> + let i:usize = i in + let (coefficient: u16):u16 = mk_u16 0 in + let coefficient:u16 = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + d + (fun coefficient j -> + let coefficient:u16 = coefficient in + let j:usize = j in + coefficient <. (mk_u16 1 < + let coefficient:u16 = coefficient in + let j:usize = j in + if input.[ (i *! d <: usize) +! j <: usize ] <: bool + then + let coefficient:u16 = coefficient +! (mk_u16 1 <. mk_usize 0 && d <=. Hacspec_ml_kem.Parameters.v_BITS_PER_COEFFICIENT && + v_N <. (mk_usize 16384 /! d <: usize) && + v_N <. (mk_usize 16384 /! mk_usize 8 <: usize) && + v_N8 =. (v_N *! mk_usize 8 <: usize) && + v_Nd =. (v_N *! d <: usize) && + v_Nd8 =. (v_Nd *! mk_usize 8 <: usize)) + (ensures + fun result -> + let result:t_Array u16 v_N8 = result in + forall (i: usize). + b2t (i <. v_N8 <: bool) ==> + b2t ((result.[ i ] <: u16) <. (mk_u16 1 <. mk_usize 0 && d <=. Hacspec_ml_kem.Parameters.v_BITS_PER_COEFFICIENT && + (Core_models.Slice.impl__len #u8 (b <: t_Slice u8) <: usize) =. (mk_usize 32 *! d <: usize) && + v_D32 =. (mk_usize 32 *! d <: usize) && + v_D256 =. (mk_usize 256 *! d <: usize)) + (ensures + fun result -> + let result:t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = result in + forall (i: usize). + b2t (i <. mk_usize 256 <: bool) ==> + b2t + ((result.[ i ] <: Hacspec_ml_kem.Parameters.t_FieldElement) + .Hacspec_ml_kem.Parameters.f_val <. + (mk_u16 1 < Hacspec_ml_kem.Parameters.t_FieldElement) + (fun i -> + let i:usize = i in + Hacspec_ml_kem.Parameters.impl_FieldElement__new ((decoded.[ i ] <: u16) %! + Hacspec_ml_kem.Parameters.v_FIELD_MODULUS + <: + u16) + <: + Hacspec_ml_kem.Parameters.t_FieldElement) + +#pop-options + +#push-options "--z3rlimit 150" + +/// Encode each polynomial of an NTT vector at 12 bits per coefficient, +/// writing the result into `out`. Companion to value-returning +/// [`serialize_secret_key`]. Mirrors the existing +/// [`byte_encode_into`] convention: the `_into` form is the canonical +/// primitive; the value-returning form is a thin allocating wrapper. +let serialize_secret_key_into + (v_RANK: usize) + (vector: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (out: t_Slice u8) + : Prims.Pure (t_Slice u8) + (requires + v_RANK <=. mk_usize 4 && + (Core_models.Slice.impl__len #u8 out <: usize) =. + (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let out:t_Slice u8 = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + v_RANK + (fun out e_i -> + let out:t_Slice u8 = out in + let e_i:usize = e_i in + (Core_models.Slice.impl__len #u8 out <: usize) =. + (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) + <: + bool) + out + (fun out i -> + let out:t_Slice u8 = out in + let i:usize = i in + let encoded:t_Array u8 (mk_usize 384) = + byte_encode (mk_usize 384) + (mk_usize 3072) + (vector.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (mk_usize 12) + in + let out:t_Slice u8 = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range out + ({ + Core_models.Ops.Range.f_start + = + i *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize; + Core_models.Ops.Range.f_end + = + (i +! mk_usize 1 <: usize) *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize) + (Core_models.Slice.impl__copy_from_slice #u8 + (out.[ { + Core_models.Ops.Range.f_start + = + i *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize; + Core_models.Ops.Range.f_end + = + (i +! mk_usize 1 <: usize) *! + Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT + <: + usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + (encoded <: t_Slice u8) + <: + t_Slice u8) + in + out) + in + out + +#pop-options + +#push-options "--z3rlimit 150" + +let vector_decode_12_ (v_RANK: usize) (encoded: t_Slice u8) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + v_RANK <=. mk_usize 4 && + (Core_models.Slice.impl__len #u8 encoded <: usize) =. + (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + let start:usize = i *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT in + let (chunk: t_Array u8 (mk_usize 384)):t_Array u8 (mk_usize 384) = + Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 384)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 384)) + #FStar.Tactics.Typeclasses.solve + (encoded.[ { + Core_models.Ops.Range.f_start = start; + Core_models.Ops.Range.f_end = start +! mk_usize 384 <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 384)) + Core_models.Array.t_TryFromSliceError) + in + byte_decode (mk_usize 384) (mk_usize 3072) chunk (mk_usize 12)) + +#pop-options + +#push-options "--z3rlimit 150" + +let byte_encode_into + (p: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (d: usize) + (out: t_Slice u8) + : Prims.Pure (t_Slice u8) + (requires + (d =. mk_usize 1 || d =. mk_usize 4 || d =. mk_usize 5 || d =. mk_usize 10 || + d =. mk_usize 11 || + d =. mk_usize 12) && + (Core_models.Slice.impl__len #u8 out <: usize) =. (mk_usize 32 *! d <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + let out:t_Slice u8 = + match d <: usize with + | Rust_primitives.Integers.MkInt 1 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 32) (mk_usize 256) p (mk_usize 1) <: t_Slice u8) + | Rust_primitives.Integers.MkInt 4 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 128) (mk_usize 1024) p (mk_usize 4) <: t_Slice u8) + | Rust_primitives.Integers.MkInt 5 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 160) (mk_usize 1280) p (mk_usize 5) <: t_Slice u8) + | Rust_primitives.Integers.MkInt 10 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 320) (mk_usize 2560) p (mk_usize 10) <: t_Slice u8) + | Rust_primitives.Integers.MkInt 11 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 352) (mk_usize 2816) p (mk_usize 11) <: t_Slice u8) + | Rust_primitives.Integers.MkInt 12 -> + Core_models.Slice.impl__copy_from_slice #u8 + out + (byte_encode (mk_usize 384) (mk_usize 3072) p (mk_usize 12) <: t_Slice u8) + | _ -> + let args:usize = d <: usize in + let args:t_Array Core_models.Fmt.Rt.t_Argument (mk_usize 1) = + let list = [Core_models.Fmt.Rt.impl__new_display #usize args] in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 1); + Rust_primitives.Hax.array_of_list 1 list + in + out + in + out + +#pop-options + +#push-options "--z3rlimit 150" + +let byte_decode_dyn (b: t_Slice u8) (d: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires + (d =. mk_usize 1 || d =. mk_usize 4 || d =. mk_usize 5 || d =. mk_usize 10 || + d =. mk_usize 11 || + d =. mk_usize 12) && + (Core_models.Slice.impl__len #u8 b <: usize) =. (mk_usize 32 *! d <: usize)) + (fun _ -> Prims.l_True) = + let _:Prims.unit = () <: Prims.unit in + match d <: usize with + | Rust_primitives.Integers.MkInt 1 -> + byte_decode (mk_usize 32) + (mk_usize 256) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 32)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 32)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 32)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 32)) + (mk_usize 1) + | Rust_primitives.Integers.MkInt 4 -> + byte_decode (mk_usize 128) + (mk_usize 1024) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 128)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 128)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 128)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 128)) + (mk_usize 4) + | Rust_primitives.Integers.MkInt 5 -> + byte_decode (mk_usize 160) + (mk_usize 1280) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 160)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 160)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 160)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 160)) + (mk_usize 5) + | Rust_primitives.Integers.MkInt 10 -> + byte_decode (mk_usize 320) + (mk_usize 2560) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 320)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 320)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 320)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 320)) + (mk_usize 10) + | Rust_primitives.Integers.MkInt 11 -> + byte_decode (mk_usize 352) + (mk_usize 2816) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 352)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 352)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 352)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 352)) + (mk_usize 11) + | Rust_primitives.Integers.MkInt 12 -> + byte_decode (mk_usize 384) + (mk_usize 3072) + (Core_models.Result.impl__unwrap #(t_Array u8 (mk_usize 384)) + #Core_models.Array.t_TryFromSliceError + (Core_models.Convert.f_try_into #(t_Slice u8) + #(t_Array u8 (mk_usize 384)) + #FStar.Tactics.Typeclasses.solve + b + <: + Core_models.Result.t_Result (t_Array u8 (mk_usize 384)) + Core_models.Array.t_TryFromSliceError) + <: + t_Array u8 (mk_usize 384)) + (mk_usize 12) + | _ -> + let args:usize = d <: usize in + let args:t_Array Core_models.Fmt.Rt.t_Argument (mk_usize 1) = + let list = [Core_models.Fmt.Rt.impl__new_display #usize args] in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 1); + Rust_primitives.Hax.array_of_list 1 list + in + Rust_primitives.Hax.never_to_any (Core_models.Panicking.panic_fmt (Core_models.Fmt.Rt.impl_1__new_v1 + (mk_usize 1) + (mk_usize 1) + (let list = ["unsupported d="] in + FStar.Pervasives.assert_norm (Prims.eq2 (List.Tot.length list) 1); + Rust_primitives.Hax.array_of_list 1 list) + args + <: + Core_models.Fmt.t_Arguments) + <: + Rust_primitives.Hax.t_Never) + +#pop-options + +/// Compress a polynomial to 1 bit per coefficient, then serialize to bytes. +/// Corresponds to `compress_then_serialize_message` in the implementation. +/// Used for encoding/decoding the message in K-PKE. +let compress_then_serialize_message + (re: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array u8 (mk_usize 32) = + byte_encode (mk_usize 32) + (mk_usize 256) + (Hacspec_ml_kem.Compress.compress re (mk_usize 1) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (mk_usize 1) + +/// Deserialize bytes to a polynomial, then decompress from 1 bit per coefficient. +/// Corresponds to `deserialize_then_decompress_message` in the implementation. +let deserialize_then_decompress_message (serialized: t_Array u8 (mk_usize 32)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + Hacspec_ml_kem.Compress.decompress (byte_decode (mk_usize 32) + (mk_usize 256) + serialized + (mk_usize 1) + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (mk_usize 1) + +/// Serialize a polynomial with 12-bit coefficients (no compression). +/// Corresponds to `serialize_uncompressed_ring_element` in the implementation. +let serialize_uncompressed_ring_element + (re: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + : t_Array u8 (mk_usize 384) = byte_encode (mk_usize 384) (mk_usize 3072) re (mk_usize 12) + +/// Deserialize bytes to a polynomial with 12-bit coefficients (no decompression). +/// Corresponds to `deserialize_to_uncompressed_ring_element` in the implementation. +let deserialize_to_uncompressed_ring_element (serialized: t_Array u8 (mk_usize 384)) + : t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256) = + byte_decode (mk_usize 384) (mk_usize 3072) serialized (mk_usize 12) + +#push-options "--z3rlimit 150" + +/// Compress each polynomial in u to du bits, then serialize, writing +/// into `out`. Companion to value-returning [`compress_then_serialize_u`]. +/// Mirrors the [`byte_encode_into`] / [`serialize_secret_key_into`] +/// convention: the `_into` form is the canonical primitive. +let compress_then_serialize_u_into + (v_RANK: usize) + (u: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (du: usize) + (out: t_Slice u8) + : Prims.Pure (t_Slice u8) + (requires + v_RANK <=. mk_usize 4 && (du =. mk_usize 10 || du =. mk_usize 11) && + (Core_models.Slice.impl__len #u8 out <: usize) =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! du + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let du_poly_size:usize = + (Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! du <: usize) /! mk_usize 8 + in + let out:t_Slice u8 = + Rust_primitives.Hax.Folds.fold_range (mk_usize 0) + v_RANK + (fun out e_i -> + let out:t_Slice u8 = out in + let e_i:usize = e_i in + (Core_models.Slice.impl__len #u8 out <: usize) =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! du + <: + usize) /! + mk_usize 8 + <: + usize) + <: + bool) + out + (fun out i -> + let out:t_Slice u8 = out in + let i:usize = i in + let out:t_Slice u8 = + Rust_primitives.Hax.Monomorphized_update_at.update_at_range out + ({ + Core_models.Ops.Range.f_start = i *! du_poly_size <: usize; + Core_models.Ops.Range.f_end = (i +! mk_usize 1 <: usize) *! du_poly_size <: usize + } + <: + Core_models.Ops.Range.t_Range usize) + (byte_encode_into (Hacspec_ml_kem.Compress.compress (u.[ i ] + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + du + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + du + (out.[ { + Core_models.Ops.Range.f_start = i *! du_poly_size <: usize; + Core_models.Ops.Range.f_end + = + (i +! mk_usize 1 <: usize) *! du_poly_size <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + <: + t_Slice u8) + in + out) + in + out + +#pop-options + +#push-options "--z3rlimit 150" + +/// Compress each polynomial in u to du bits, then serialize. +/// Corresponds to `compress_then_serialize_ring_element_u` in the implementation. +/// Thin allocating wrapper around [`compress_then_serialize_u_into`]. +/// Note: The implementation dispatches on the compression factor (10 or 11). +/// In the spec we use the generic compress + byte_encode path. +let compress_then_serialize_u + (v_RANK v_U_SIZE: usize) + (u: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (du: usize) + : Prims.Pure (t_Array u8 v_U_SIZE) + (requires + v_RANK <=. mk_usize 4 && (du =. mk_usize 10 || du =. mk_usize 11) && + v_U_SIZE =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! du + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let out:t_Array u8 v_U_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_U_SIZE in + let out:t_Array u8 v_U_SIZE = compress_then_serialize_u_into v_RANK u du out in + out + +#pop-options + +#push-options "--z3rlimit 150" + +/// Compress v to dv bits, then serialize. +/// Corresponds to `compress_then_serialize_ring_element_v` in the implementation. +let compress_then_serialize_v + (v_V_SIZE: usize) + (v: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (dv: usize) + : Prims.Pure (t_Array u8 v_V_SIZE) + (requires + (dv =. mk_usize 4 || dv =. mk_usize 5) && + v_V_SIZE =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! dv <: usize) /! mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let out:t_Array u8 v_V_SIZE = Rust_primitives.Hax.repeat (mk_u8 0) v_V_SIZE in + let out:t_Array u8 v_V_SIZE = + byte_encode_into (Hacspec_ml_kem.Compress.compress v dv + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + dv + out + in + out + +#pop-options + +#push-options "--z3rlimit 150" + +/// Deserialize and decompress u from ciphertext bytes. +/// Corresponds to `deserialize_then_decompress_ring_element_u` in the implementation. +let deserialize_then_decompress_u (v_RANK: usize) (ciphertext: t_Slice u8) (du: usize) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + v_RANK <=. mk_usize 4 && (du =. mk_usize 10 || du =. mk_usize 11) && + (Core_models.Slice.impl__len #u8 ciphertext <: usize) =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! du + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + let du_poly_size:usize = + (Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! du <: usize) /! mk_usize 8 + in + Hacspec_ml_kem.Parameters.createi #(t_Array Hacspec_ml_kem.Parameters.t_FieldElement + (mk_usize 256)) + v_RANK + #(usize -> t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (fun i -> + let i:usize = i in + let start:usize = i *! du_poly_size in + Hacspec_ml_kem.Compress.decompress (byte_decode_dyn (ciphertext.[ { + Core_models.Ops.Range.f_start = start; + Core_models.Ops.Range.f_end = start +! du_poly_size <: usize + } + <: + Core_models.Ops.Range.t_Range usize ] + <: + t_Slice u8) + du + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + du) + +#pop-options + +#push-options "--z3rlimit 150" + +/// Deserialize, decompress, then NTT-transform u from ciphertext bytes. +/// Composes [`deserialize_then_decompress_u`] with [`vector_ntt`]. Used +/// by `ind_cpa::decrypt` and matches the libcrux-impl +/// `deserialize_then_decompress_u` function (which fuses the NTT into +/// the per-element decompress loop). +let deserialize_then_decompress_u_then_ntt (v_RANK: usize) (ciphertext: t_Slice u8) (du: usize) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + v_RANK <=. mk_usize 4 && (du =. mk_usize 10 || du =. mk_usize 11) && + (Core_models.Slice.impl__len #u8 ciphertext <: usize) =. + (((v_RANK *! Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT <: usize) *! du + <: + usize) /! + mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Ntt.vector_ntt v_RANK + (deserialize_then_decompress_u v_RANK ciphertext du + <: + t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + +#pop-options + +#push-options "--z3rlimit 150" + +/// Deserialize and decompress v from ciphertext bytes. +/// Corresponds to `deserialize_then_decompress_ring_element_v` in the implementation. +let deserialize_then_decompress_v (serialized: t_Slice u8) (dv: usize) + : Prims.Pure (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (requires + (dv =. mk_usize 4 || dv =. mk_usize 5) && + (Core_models.Slice.impl__len #u8 serialized <: usize) =. + ((Hacspec_ml_kem.Parameters.v_COEFFICIENTS_IN_RING_ELEMENT *! dv <: usize) /! mk_usize 8 + <: + usize)) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Compress.decompress (byte_decode_dyn serialized dv + <: + t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + dv + +#pop-options + +/// Deserialize ring elements from a byte slice, reducing mod q. +/// Corresponds to `deserialize_ring_elements_reduced` in the implementation. +/// This is equivalent to `vector_decode_12` but named to match the implementation. +let deserialize_ring_elements_reduced (v_RANK: usize) (encoded: t_Slice u8) + : Prims.Pure (t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (requires + v_RANK <=. mk_usize 4 && + (Core_models.Slice.impl__len #u8 encoded <: usize) =. + (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize)) + (fun _ -> Prims.l_True) = vector_decode_12_ v_RANK encoded + +/// Serialize a vector of polynomials with 12-bit coefficients. +/// Corresponds to `serialize_secret_key` / `serialize_vector` in the implementation. +let serialize_secret_key + (v_RANK v_T_SIZE: usize) + (vector: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + : Prims.Pure (t_Array u8 v_T_SIZE) + (requires + v_RANK <=. mk_usize 4 && + v_T_SIZE =. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize)) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #u8 + v_T_SIZE + #(usize -> u8) + (fun k -> + let k:usize = k in + let i:usize = k /! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT in + let j:usize = k %! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT in + let encoded:t_Array u8 (mk_usize 384) = + byte_encode (mk_usize 384) + (mk_usize 3072) + (vector.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (mk_usize 12) + in + encoded.[ j ]) + +/// Serialize a public key: encode the NTT vector t\u{302} concatenated with the seed ρ. +/// Corresponds to `serialize_public_key` in the implementation\'s `ind_cpa.rs`. +/// Layout: bytes [0, RANK*384) hold byte_encode(t_as_ntt[i], 12) per polynomial, +/// bytes [RANK*384, EK_SIZE) hold seed_for_A[0..32]. +let serialize_public_key + (v_RANK v_EK_SIZE: usize) + (tt_as_ntt: t_Array (t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) v_RANK) + (seed_for_A: t_Slice u8) + : Prims.Pure (t_Array u8 v_EK_SIZE) + (requires + v_RANK <=. mk_usize 4 && + v_EK_SIZE =. + ((v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) +! mk_usize 32 + <: + usize) && + (Core_models.Slice.impl__len #u8 seed_for_A <: usize) >=. mk_usize 32) + (fun _ -> Prims.l_True) = + Hacspec_ml_kem.Parameters.createi #u8 + v_EK_SIZE + #(usize -> u8) + (fun k -> + let k:usize = k in + if k <. (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) <: bool + then + let i:usize = k /! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT in + let j:usize = k %! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT in + let encoded:t_Array u8 (mk_usize 384) = + byte_encode (mk_usize 384) + (mk_usize 3072) + (tt_as_ntt.[ i ] <: t_Array Hacspec_ml_kem.Parameters.t_FieldElement (mk_usize 256)) + (mk_usize 12) + in + encoded.[ j ] + else + seed_for_A.[ k -! (v_RANK *! Hacspec_ml_kem.Parameters.v_BYTES_PER_RING_ELEMENT <: usize) + <: + usize ] + <: + u8) diff --git a/specs/ml-kem/proofs/fstar/extraction/Makefile b/specs/ml-kem/proofs/fstar/extraction/Makefile new file mode 100644 index 0000000000..b4ce70a38e --- /dev/null +++ b/specs/ml-kem/proofs/fstar/extraction/Makefile @@ -0,0 +1 @@ +include $(shell git rev-parse --show-toplevel)/fstar-helpers/Makefile.base From a4cfb1ebf26431b2ee81f0dc19383158aaf397b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 17 Jun 2026 09:14:02 +0000 Subject: [PATCH 4/8] [ml-kem] Add aeneas-Lean extraction of the spec --- specs/ml-kem/hax_aeneas.py | 102 + specs/ml-kem/proofs/aeneas-lean/.gitignore | 3 + .../proofs/aeneas-lean/HacspecMlKem.lean | 1 + .../HacspecMlKem/Extraction/Funs.lean | 6557 +++++++++++++++++ .../aeneas-lean/HacspecMlKem/Missing.lean | 171 + .../proofs/aeneas-lean/lake-manifest.json | 123 + specs/ml-kem/proofs/aeneas-lean/lakefile.toml | 15 + .../ml-kem/proofs/aeneas-lean/lean-toolchain | 1 + specs/sha3/hax_aeneas.py | 2 +- .../proofs/aeneas-lean/lake-manifest.json | 4 +- specs/sha3/proofs/aeneas-lean/lakefile.toml | 2 +- 11 files changed, 6977 insertions(+), 4 deletions(-) create mode 100755 specs/ml-kem/hax_aeneas.py create mode 100644 specs/ml-kem/proofs/aeneas-lean/.gitignore create mode 100644 specs/ml-kem/proofs/aeneas-lean/HacspecMlKem.lean create mode 100644 specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Extraction/Funs.lean create mode 100644 specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Missing.lean create mode 100644 specs/ml-kem/proofs/aeneas-lean/lake-manifest.json create mode 100644 specs/ml-kem/proofs/aeneas-lean/lakefile.toml create mode 100644 specs/ml-kem/proofs/aeneas-lean/lean-toolchain diff --git a/specs/ml-kem/hax_aeneas.py b/specs/ml-kem/hax_aeneas.py new file mode 100755 index 0000000000..82452738b4 --- /dev/null +++ b/specs/ml-kem/hax_aeneas.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +"""Hax into aeneas-lean extraction script for hacspec ML-KEM. + +Extracts the entire spec crate and patches the generated +`HacspecMlKem/Extraction/Funs.lean` to import `HacspecMlKem.Missing` +and to apply known mis-extraction workarounds. +""" + +import os +import re +import shutil +import subprocess +import sys +from pathlib import Path + +HAX_VERSION = "ffdf432705d409b62ec025d253a340234b59766f" +AENEAS_VERSION = "8d2077c" + +def check_version(cmd: list[str], expected: str) -> None: + result = subprocess.run(cmd, capture_output=True, text=True) + output = result.stdout + result.stderr + if expected not in output: + if os.environ.get("SKIP_VERSION_CHECK") == "1": + print(f"warning: version mismatch for {cmd[0]} (expected {expected!r}); continuing because SKIP_VERSION_CHECK=1", file=sys.stderr) + return + print(f"Version mismatch for {cmd[0]}: expected {expected!r} in output:\n{output}", file=sys.stderr) + sys.exit(1) + +check_version(["cargo", "hax", "--version"], HAX_VERSION) +check_version(["aeneas", "-version"], AENEAS_VERSION) + +result = subprocess.run( + ["cargo", "hax", "into", "aeneas-lean", + "--aeneas-args=-core-models-lib"], + env={**os.environ, "RUSTFLAGS": "--cfg hax_backend_lean"} +) + +funs_lean = Path("proofs/aeneas-lean/HacspecMlKem/Extraction/Funs.lean") + +# Aeneas can return non-zero while still emitting a partial `Funs.lean` +# Apply the import patch whenever the file exists; only abort if extraction +# produced nothing at all. +if not funs_lean.exists(): + sys.exit(result.returncode if result.returncode != 0 else 1) +if result.returncode != 0: + print( + f"warning: hax/aeneas exited with code {result.returncode}; " + f"applying patches to partial {funs_lean}.", + file=sys.stderr, + ) + +content = funs_lean.read_text() + +# Pull in our hand-written stubs (`HacspecMlKem.Missing`). +content = re.sub( + r"import CoreModels", + "import CoreModels\nimport HacspecMlKem.Missing", + content, + count=1, +) + +# Increase recursion depth for `ntt.ZETAS`. +content = content.replace( + "/-- [hacspec_ml_kem::ntt::ZETAS]", + "set_option maxRecDepth 1000 in\n/-- [hacspec_ml_kem::ntt::ZETAS]", + 1, +) + +# Aeneas emits `fmt::rt::Argument::new_display` with two +# arguments (the `Display` instance + the value), but rust-core-models +# defines it with one. The blocks below appear only in panic-path +# formatting (`fail panic` follows them), so we block-comment them +# away. +PANIC_FMT_BLOCK_RX = re.compile( + r" let a ←\n" + r" core\.fmt\.rt\.Argument\.new_display core\.Usize\.Insts\.CoreFmtDisplay [a-zA-Z_0-9]+\n" + r" let _ ←\n" + r" core\.fmt\.Arguments\.new\n" + r" \(Array\.make [0-9]+#usize \[\n" + r"(?:[^\]]+\n)+" + r" \]\) \(Array\.make [0-9]+#usize \[ a \]\)" +) +def _comment_panic_fmt(m: 're.Match[str]') -> str: + return "/-\n" + m.group(0) + "\n-/" +content = PANIC_FMT_BLOCK_RX.sub(_comment_panic_fmt, content) + +# `cmp.PartialEq` has only `eq` — drop the synthesised `ne` field. +content = re.sub( + r"\n ne := [^\n]+(?=\n})", + "", + content, +) + +# `cmp.Eq` has only `PartialEqInst` — drop the synthesised +# `assert_fields_are_eq` field. +content = re.sub( + r"\n assert_fields_are_eq :=\n [^\n]+(?=\n})", + "", + content, +) + +funs_lean.write_text(content) diff --git a/specs/ml-kem/proofs/aeneas-lean/.gitignore b/specs/ml-kem/proofs/aeneas-lean/.gitignore new file mode 100644 index 0000000000..86aed81adf --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/.gitignore @@ -0,0 +1,3 @@ +.lake +*.llbc +aeneas-error.log diff --git a/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem.lean b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem.lean new file mode 100644 index 0000000000..0cd8ae1fae --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem.lean @@ -0,0 +1 @@ +import HacspecMlKem.Extraction.Funs diff --git a/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Extraction/Funs.lean b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Extraction/Funs.lean new file mode 100644 index 0000000000..dfeb2b6275 --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Extraction/Funs.lean @@ -0,0 +1,6557 @@ +-- THIS FILE WAS AUTOMATICALLY GENERATED BY AENEAS +-- [hacspec_ml_kem] +import Aeneas +import CoreModels +import HacspecMlKem.Missing +open CoreModels Aeneas +open Aeneas.Std hiding namespace core alloc +open Result ControlFlow Error +set_option linter.dupNamespace false +set_option linter.hashCommand false +set_option linter.unusedVariables false + +/- You can set the `maxHeartbeats` value with the `-max-heartbeats` CLI option -/ +set_option maxHeartbeats 1000000 + +/- You can set the `maxRecDepth` value with the `-max-recdepth` CLI option -/ +set_option maxRecDepth 2048 + +/- You can remove the following line by using the CLI option `-all-computable`: -/ +noncomputable section + +namespace hacspec_ml_kem + +/-- [hacspec_ml_kem::parameters::createi]: + Source: 'ml-kem/src/parameters.rs', lines 363:0-365:1 + Visibility: public -/ +def parameters.createi + {T : Type} {F : Type} (N : Std.Usize) (coreopsfunctionFnFTupleUsizeTInst : + core.ops.function.Fn F Std.Usize T) (f : F) : + Result (Array T N) + := do + core.array.from_fn N coreopsfunctionFnFTupleUsizeTInst.FnMutInst f + +/-- [hacspec_ml_kem::parameters::FieldElement] + Source: 'ml-kem/src/parameters.rs', lines 306:0-309:1 + Visibility: public -/ +structure parameters.FieldElement where + val : Std.U16 + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::new]: + Source: 'ml-kem/src/parameters.rs', lines 314:4-316:5 + Visibility: public -/ +def parameters.FieldElement.new + (val : Std.U16) : Result parameters.FieldElement := do + ok { val } + +/-- [hacspec_ml_kem::parameters::FIELD_MODULUS] + Source: 'ml-kem/src/parameters.rs', lines 2:0-2:36 + Visibility: public -/ +@[global_simps, irreducible] def parameters.FIELD_MODULUS : Std.U16 := 3329#u16 + +/-- [hacspec_ml_kem::compress::compress_d]: + Source: 'ml-kem/src/compress.rs', lines 52:0-60:1 -/ +def compress.compress_d + (fe : parameters.FieldElement) (to_bit_size : Std.Usize) : + Result parameters.FieldElement + := do + massert (to_bit_size < 12#usize) + let i ← lift (UScalar.cast .U32 to_bit_size) + let two_pow_bit_size ← core.num.U32.pow 2#u32 i + let i1 ← lift (UScalar.cast .U32 fe.val) + let i2 ← i1 * 2#u32 + let i3 ← i2 * two_pow_bit_size + let i4 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i5 ← i3 + i4 + let i6 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i7 ← 2#u32 * i6 + let compressed ← i5 / i7 + let i8 ← compressed % two_pow_bit_size + let i9 ← lift (UScalar.cast .U16 i8) + parameters.FieldElement.new i9 + +/-- [hacspec_ml_kem::compress::compress::closure] + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +def compress.compress.closure := + Array parameters.FieldElement 256#usize × Std.Usize + +/-- [hacspec_ml_kem::compress::compress::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +def + compress.compress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : compress.compress.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, i) := c + let fe ← Array.index_usize a tupled_args + compress.compress_d fe i + +/-- [hacspec_ml_kem::compress::compress::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +def + compress.compress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : compress.compress.closure) (args : Std.Usize) : + Result (parameters.FieldElement × compress.compress.closure) + := do + let fe ← + compress.compress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::compress::compress::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +def + compress.compress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : compress.compress.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + compress.compress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::compress::compress::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +@[reducible] +def compress.compress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce compress.compress.closure Std.Usize + parameters.FieldElement := { + call_once := + compress.compress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::compress::compress::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +@[reducible] +def compress.compress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut compress.compress.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + compress.compress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + compress.compress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::compress::compress::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::compress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 15:12-15:66 -/ +@[reducible] +def compress.compress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement : + core.ops.function.Fn compress.compress.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + compress.compress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + compress.compress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::compress::compress]: + Source: 'ml-kem/src/compress.rs', lines 14:0-16:1 + Visibility: public -/ +def compress.compress + (re : Array parameters.FieldElement 256#usize) + (bits_per_compressed_coefficient : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + compress.compress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (re, bits_per_compressed_coefficient) + +/-- [hacspec_ml_kem::compress::decompress_d]: + Source: 'ml-kem/src/compress.rs', lines 83:0-89:1 -/ +def compress.decompress_d + (fe : parameters.FieldElement) (to_bit_size : Std.Usize) : + Result parameters.FieldElement + := do + massert (to_bit_size < 12#usize) + let i ← 1#u16 <<< to_bit_size + massert (fe.val < i) + let i1 ← lift (UScalar.cast .U32 to_bit_size) + let two_pow_bit_size ← core.num.U32.pow 2#u32 i1 + let i2 ← lift (UScalar.cast .U32 fe.val) + let i3 ← 2#u32 * i2 + let i4 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i5 ← i3 * i4 + let numerator ← i5 + two_pow_bit_size + let i6 ← two_pow_bit_size * 2#u32 + let decompressed ← numerator / i6 + let i7 ← lift (UScalar.cast .U16 decompressed) + parameters.FieldElement.new i7 + +/-- [hacspec_ml_kem::compress::decompress::closure] + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +def compress.decompress.closure := + Array parameters.FieldElement 256#usize × Std.Usize + +/-- [hacspec_ml_kem::compress::decompress::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +def + compress.decompress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : compress.decompress.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, i) := c + let fe ← Array.index_usize a tupled_args + compress.decompress_d fe i + +/-- [hacspec_ml_kem::compress::decompress::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +def + compress.decompress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : compress.decompress.closure) (args : Std.Usize) : + Result (parameters.FieldElement × compress.decompress.closure) + := do + let fe ← + compress.decompress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::compress::decompress::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +def + compress.decompress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : compress.decompress.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + compress.decompress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::compress::decompress::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +@[reducible] +def + compress.decompress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce compress.decompress.closure Std.Usize + parameters.FieldElement := { + call_once := + compress.decompress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::compress::decompress::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +@[reducible] +def + compress.decompress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut compress.decompress.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + compress.decompress.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + compress.decompress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::compress::decompress::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::compress::decompress::closure<'_0, '_1>}] + Source: 'ml-kem/src/compress.rs', lines 28:12-28:68 -/ +@[reducible] +def compress.decompress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement : + core.ops.function.Fn compress.decompress.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + compress.decompress.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + compress.decompress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::compress::decompress]: + Source: 'ml-kem/src/compress.rs', lines 27:0-29:1 + Visibility: public -/ +def compress.decompress + (re : Array parameters.FieldElement 256#usize) + (bits_per_compressed_coefficient : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + compress.decompress.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (re, bits_per_compressed_coefficient) + +/-- [hacspec_ml_kem::sampling::BadRejectionSamplingRandomnessError] + Source: 'ml-kem/src/sampling.rs', lines 4:0-4:47 + Visibility: public -/ +@[reducible] +def sampling.BadRejectionSamplingRandomnessError := Unit + +/-- [hacspec_ml_kem::parameters::hash_functions::H]: + Source: 'ml-kem/src/parameters.rs', lines 279:4-281:5 + Visibility: public -/ +def parameters.hash_functions.H + (input : Slice Std.U8) : Result (Array Std.U8 32#usize) := do + hacspec_sha3.sha3.sha3_256 input + +/-- [hacspec_ml_kem::parameters::hash_functions::H_DIGEST_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 276:4-276:40 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.hash_functions.H_DIGEST_SIZE : Std.Usize := 32#usize + +/-- [hacspec_ml_kem::parameters::MlKemParams] + Source: 'ml-kem/src/parameters.rs', lines 26:0-37:1 + Visibility: public -/ +structure parameters.MlKemParams where + rank : Std.Usize + eta1 : Std.Usize + eta2 : Std.Usize + du : Std.Usize + dv : Std.Usize + +/-- [hacspec_ml_kem::parameters::COEFFICIENTS_IN_RING_ELEMENT] + Source: 'ml-kem/src/parameters.rs', lines 8:0-8:52 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.COEFFICIENTS_IN_RING_ELEMENT : Std.Usize := 256#usize + +/-- [hacspec_ml_kem::parameters::BITS_PER_RING_ELEMENT] + Source: 'ml-kem/src/parameters.rs', lines 11:0-11:75 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.BITS_PER_RING_ELEMENT : Result Std.Usize := + parameters.COEFFICIENTS_IN_RING_ELEMENT * 12#usize + +/-- [hacspec_ml_kem::parameters::BYTES_PER_RING_ELEMENT] + Source: 'ml-kem/src/parameters.rs', lines 14:0-14:68 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.BYTES_PER_RING_ELEMENT : Result Std.Usize := do + let i ← parameters.BITS_PER_RING_ELEMENT + i / 8#usize + +/-- [hacspec_ml_kem::serialize::serialize_secret_key::closure] + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +@[reducible] +def serialize.serialize_secret_key.closure (RANK : Std.Usize) (T_SIZE : + Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::serialize::byte_encode::closure] + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +@[reducible] +def serialize.byte_encode.closure (D32 : Std.Usize) (D256 : Std.Usize) := + Array parameters.FieldElement 256#usize + +/-- [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::Fn<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +def serialize.byte_encode.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call + {D32 : Std.Usize} {D256 : Std.Usize} + (c : serialize.byte_encode.closure D32 D256) (tupled_args : Std.Usize) : + Result Std.U16 + := do + let fe ← Array.index_usize c tupled_args + ok fe.val + +/-- [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::FnMut<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +def + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut + {D32 : Std.Usize} {D256 : Std.Usize} + (state : serialize.byte_encode.closure D32 D256) (args : Std.Usize) : + Result (Std.U16 × (serialize.byte_encode.closure D32 D256)) + := do + let i ← + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call + state args + ok (i, state) + +/-- [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::FnOnce<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +def + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16.call_once + {D32 : Std.Usize} {D256 : Std.Usize} + (c : serialize.byte_encode.closure D32 D256) (i : Std.Usize) : + Result Std.U16 + := do + let (i1, _) ← + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut + c i + ok i1 + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::FnOnce<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +@[reducible] +def serialize.byte_encode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16 (D32 + : Std.Usize) (D256 : Std.Usize) : core.ops.function.FnOnce + (serialize.byte_encode.closure D32 D256) Std.Usize Std.U16 := { + call_once := + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::FnMut<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +@[reducible] +def serialize.byte_encode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16 (D32 + : Std.Usize) (D256 : Std.Usize) : core.ops.function.FnMut + (serialize.byte_encode.closure D32 D256) Std.Usize Std.U16 := { + FnOnceInst := + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16 D32 + D256 + call_mut := + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_encode::{impl core::ops::function::Fn<(usize,), u16> for hacspec_ml_kem::serialize::byte_encode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 118:36-118:48 -/ +@[reducible] +def serialize.byte_encode.closure.Insts.CoreOpsFunctionFnTupleUsizeU16 (D32 : + Std.Usize) (D256 : Std.Usize) : core.ops.function.Fn + (serialize.byte_encode.closure D32 D256) Std.Usize Std.U16 := { + FnMutInst := + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16 D32 + D256 + call := + serialize.byte_encode.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call +} + +/-- [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure] + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +def serialize.bitvector_from_bounded_ints.closure (N : Std.Usize) (Nd : + Std.Usize) := + Array Std.U16 N × Std.Usize + +/-- [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::Fn<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call + {N : Std.Usize} {Nd : Std.Usize} + (c : serialize.bitvector_from_bounded_ints.closure N Nd) + (tupled_args : Std.Usize) : + Result Bool + := do + let (a, i) := c + let i1 ← tupled_args / i + let i2 ← Array.index_usize a i1 + let i3 ← tupled_args % i + let i4 ← i2 >>> i3 + let i5 ← lift (i4 &&& 1#u16) + ok (i5 = 1#u16) + +/-- [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::FnMut<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut + {N : Std.Usize} {Nd : Std.Usize} + (state : serialize.bitvector_from_bounded_ints.closure N Nd) + (args : Std.Usize) : + Result (Bool × (serialize.bitvector_from_bounded_ints.closure N Nd)) + := do + let b ← + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call + state args + ok (b, state) + +/-- [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::FnOnce<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool.call_once + {N : Std.Usize} {Nd : Std.Usize} + (c : serialize.bitvector_from_bounded_ints.closure N Nd) (i : Std.Usize) : + Result Bool + := do + let (b, _) ← + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut + c i + ok b + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::FnOnce<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +@[reducible] +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.FnOnce + (serialize.bitvector_from_bounded_ints.closure N Nd) Std.Usize Bool := { + call_once := + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::FnMut<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +@[reducible] +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.FnMut + (serialize.bitvector_from_bounded_ints.closure N Nd) Std.Usize Bool := { + FnOnceInst := + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool + N Nd + call_mut := + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_from_bounded_ints::{impl core::ops::function::Fn<(usize,), bool> for hacspec_ml_kem::serialize::bitvector_from_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 112:12-112:53 -/ +@[reducible] +def + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeBool + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.Fn + (serialize.bitvector_from_bounded_ints.closure N Nd) Std.Usize Bool := { + FnMutInst := + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool + N Nd + call := + serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call +} + +/-- [hacspec_ml_kem::serialize::bitvector_from_bounded_ints]: + Source: 'ml-kem/src/serialize.rs', lines 107:0-113:1 + Visibility: public -/ +def serialize.bitvector_from_bounded_ints + {N : Std.Usize} (Nd : Std.Usize) (input : Array Std.U16 N) (d : Std.Usize) : + Result (Array Bool Nd) + := do + let i ← N * d + massert (Nd = i) + parameters.createi Nd + (serialize.bitvector_from_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeBool + N Nd) (input, d) + +/-- [hacspec_ml_kem::serialize::bits_to_bytes::closure] + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +@[reducible] +def serialize.bits_to_bytes.closure (N : Std.Usize) (N8 : Std.Usize) := + Array Bool N8 + +/-- [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +def serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + {N : Std.Usize} {N8 : Std.Usize} (c : serialize.bits_to_bytes.closure N N8) + (tupled_args : Std.Usize) : + Result Std.U8 + := do + let i ← 8#usize * tupled_args + let b ← Array.index_usize c i + let i1 ← lift (UScalar.cast_fromBool .U8 b) + let i2 ← i + 1#usize + let b1 ← Array.index_usize c i2 + let i3 ← lift (UScalar.cast_fromBool .U8 b1) + let i4 ← i3 <<< 1#i32 + let i5 ← lift (i1 ||| i4) + let i6 ← i + 2#usize + let b2 ← Array.index_usize c i6 + let i7 ← lift (UScalar.cast_fromBool .U8 b2) + let i8 ← i7 <<< 2#i32 + let i9 ← lift (i5 ||| i8) + let i10 ← i + 3#usize + let b3 ← Array.index_usize c i10 + let i11 ← lift (UScalar.cast_fromBool .U8 b3) + let i12 ← i11 <<< 3#i32 + let i13 ← lift (i9 ||| i12) + let i14 ← i + 4#usize + let b4 ← Array.index_usize c i14 + let i15 ← lift (UScalar.cast_fromBool .U8 b4) + let i16 ← i15 <<< 4#i32 + let i17 ← lift (i13 ||| i16) + let i18 ← i + 5#usize + let b5 ← Array.index_usize c i18 + let i19 ← lift (UScalar.cast_fromBool .U8 b5) + let i20 ← i19 <<< 5#i32 + let i21 ← lift (i17 ||| i20) + let i22 ← i + 6#usize + let b6 ← Array.index_usize c i22 + let i23 ← lift (UScalar.cast_fromBool .U8 b6) + let i24 ← i23 <<< 6#i32 + let i25 ← lift (i21 ||| i24) + let i26 ← i + 7#usize + let b7 ← Array.index_usize c i26 + let i27 ← lift (UScalar.cast_fromBool .U8 b7) + let i28 ← i27 <<< 7#i32 + ok (i25 ||| i28) + +/-- [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +def + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + {N : Std.Usize} {N8 : Std.Usize} + (state : serialize.bits_to_bytes.closure N N8) (args : Std.Usize) : + Result (Std.U8 × (serialize.bits_to_bytes.closure N N8)) + := do + let i ← + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + state args + ok (i, state) + +/-- [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +def + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once + {N : Std.Usize} {N8 : Std.Usize} (c : serialize.bits_to_bytes.closure N N8) + (i : Std.Usize) : + Result Std.U8 + := do + let (i1, _) ← + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + c i + ok i1 + +/-- Trait implementation: [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +@[reducible] +def serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 (N + : Std.Usize) (N8 : Std.Usize) : core.ops.function.FnOnce + (serialize.bits_to_bytes.closure N N8) Std.Usize Std.U8 := { + call_once := + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +@[reducible] +def serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 (N : + Std.Usize) (N8 : Std.Usize) : core.ops.function.FnMut + (serialize.bits_to_bytes.closure N N8) Std.Usize Std.U8 := { + FnOnceInst := + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 N + N8 + call_mut := + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bits_to_bytes::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::bits_to_bytes::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 54:25-63:5 -/ +@[reducible] +def serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 (N : + Std.Usize) (N8 : Std.Usize) : core.ops.function.Fn + (serialize.bits_to_bytes.closure N N8) Std.Usize Std.U8 := { + FnMutInst := + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 N N8 + call := + serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call +} + +/-- [hacspec_ml_kem::serialize::bits_to_bytes]: + Source: 'ml-kem/src/serialize.rs', lines 52:0-65:1 + Visibility: public -/ +def serialize.bits_to_bytes + (N : Std.Usize) {N8 : Std.Usize} (bv : Array Bool N8) : + Result (Array Std.U8 N) + := do + let i ← N * 8#usize + massert (N8 = i) + parameters.createi N + (serialize.bits_to_bytes.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 N N8) + bv + +/-- [hacspec_ml_kem::parameters::BITS_PER_COEFFICIENT] + Source: 'ml-kem/src/parameters.rs', lines 5:0-5:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.BITS_PER_COEFFICIENT : Std.Usize := 12#usize + +/-- [hacspec_ml_kem::serialize::byte_encode]: + Source: 'ml-kem/src/serialize.rs', lines 116:0-121:1 + Visibility: public -/ +def serialize.byte_encode + (D32 : Std.Usize) (D256 : Std.Usize) + (p : Array parameters.FieldElement 256#usize) (d : Std.Usize) : + Result (Array Std.U8 D32) + := do + massert (d <= parameters.BITS_PER_COEFFICIENT) + let i ← 32#usize * d + massert (D32 = i) + let i1 ← 256#usize * d + massert (D256 = i1) + let p_raw ← + parameters.createi 256#usize + (serialize.byte_encode.closure.Insts.CoreOpsFunctionFnTupleUsizeU16 D32 + D256) p + let bv ← serialize.bitvector_from_bounded_ints D256 p_raw d + serialize.bits_to_bytes D32 bv + +/-- [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +def + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + {RANK : Std.Usize} {T_SIZE : Std.Usize} + (c : serialize.serialize_secret_key.closure RANK T_SIZE) + (tupled_args : Std.Usize) : + Result Std.U8 + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← tupled_args / i + let j ← tupled_args % i + let a ← Array.index_usize c i1 + let encoded ← serialize.byte_encode 384#usize 3072#usize a 12#usize + Array.index_usize encoded j + +/-- [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +def + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + {RANK : Std.Usize} {T_SIZE : Std.Usize} + (state : serialize.serialize_secret_key.closure RANK T_SIZE) + (args : Std.Usize) : + Result (Std.U8 × (serialize.serialize_secret_key.closure RANK T_SIZE)) + := do + let i ← + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + state args + ok (i, state) + +/-- [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +def + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once + {RANK : Std.Usize} {T_SIZE : Std.Usize} + (c : serialize.serialize_secret_key.closure RANK T_SIZE) (i : Std.Usize) : + Result Std.U8 + := do + let (i1, _) ← + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + c i + ok i1 + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +@[reducible] +def + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 + (RANK : Std.Usize) (T_SIZE : Std.Usize) : core.ops.function.FnOnce + (serialize.serialize_secret_key.closure RANK T_SIZE) Std.Usize Std.U8 := { + call_once := + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +@[reducible] +def + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 + (RANK : Std.Usize) (T_SIZE : Std.Usize) : core.ops.function.FnMut + (serialize.serialize_secret_key.closure RANK T_SIZE) Std.Usize Std.U8 := { + FnOnceInst := + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 + RANK T_SIZE + call_mut := + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_secret_key::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::serialize_secret_key::closure<'_0, RANK, T_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 404:12-409:5 -/ +@[reducible] +def serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 + (RANK : Std.Usize) (T_SIZE : Std.Usize) : core.ops.function.Fn + (serialize.serialize_secret_key.closure RANK T_SIZE) Std.Usize Std.U8 := { + FnMutInst := + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 + RANK T_SIZE + call := + serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call +} + +/-- [hacspec_ml_kem::serialize::serialize_secret_key]: + Source: 'ml-kem/src/serialize.rs', lines 401:0-410:1 + Visibility: public -/ +def serialize.serialize_secret_key + {RANK : Std.Usize} (T_SIZE : Std.Usize) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array Std.U8 T_SIZE) + := do + parameters.createi T_SIZE + (serialize.serialize_secret_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 + RANK T_SIZE) vector + +/-- [hacspec_ml_kem::parameters::hash_functions::G]: + Source: 'ml-kem/src/parameters.rs', lines 272:4-274:5 + Visibility: public -/ +def parameters.hash_functions.G + (input : Slice Std.U8) : Result (Array Std.U8 64#usize) := do + hacspec_sha3.sha3.sha3_512 input + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column::closure] + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +def matrix.multiply_matrix_by_column.closure (RANK : Std.Usize) := + Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK × Array + (Array parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n::closure] + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +def ntt.ntt_multiply_n.closure (N : Std.Usize) := + Array parameters.FieldElement N × Array parameters.FieldElement N × Slice + parameters.FieldElement + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::neg]: + Source: 'ml-kem/src/parameters.rs', lines 348:4-350:5 + Visibility: public -/ +def parameters.FieldElement.neg + (self : parameters.FieldElement) : Result parameters.FieldElement := do + let i ← parameters.FIELD_MODULUS - self.val + let i1 ← i % parameters.FIELD_MODULUS + parameters.FieldElement.new i1 + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::mul]: + Source: 'ml-kem/src/parameters.rs', lines 343:4-345:5 + Visibility: public -/ +def parameters.FieldElement.mul + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result parameters.FieldElement + := do + let i ← lift (UScalar.cast .U32 self.val) + let i1 ← lift (UScalar.cast .U32 other.val) + let i2 ← i * i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::add]: + Source: 'ml-kem/src/parameters.rs', lines 330:4-332:5 + Visibility: public -/ +def parameters.FieldElement.add + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result parameters.FieldElement + := do + let i ← lift (UScalar.cast .U32 self.val) + let i1 ← lift (UScalar.cast .U32 other.val) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::ntt::base_case_multiply_odd]: + Source: 'ml-kem/src/ntt.rs', lines 302:0-310:1 -/ +def ntt.base_case_multiply_odd + (a0 : parameters.FieldElement) (a1 : parameters.FieldElement) + (b0 : parameters.FieldElement) (b1 : parameters.FieldElement) : + Result parameters.FieldElement + := do + let fe ← parameters.FieldElement.mul a0 b1 + let fe1 ← parameters.FieldElement.mul a1 b0 + parameters.FieldElement.add fe fe1 + +/-- [hacspec_ml_kem::ntt::base_case_multiply_even]: + Source: 'ml-kem/src/ntt.rs', lines 291:0-300:1 -/ +def ntt.base_case_multiply_even + (a0 : parameters.FieldElement) (a1 : parameters.FieldElement) + (b0 : parameters.FieldElement) (b1 : parameters.FieldElement) + (zeta : parameters.FieldElement) : + Result parameters.FieldElement + := do + let fe ← parameters.FieldElement.mul a0 b0 + let fe1 ← parameters.FieldElement.mul a1 b1 + let fe2 ← parameters.FieldElement.mul fe1 zeta + parameters.FieldElement.add fe fe2 + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n_at]: + Source: 'ml-kem/src/ntt.rs', lines 349:0-366:1 -/ +def ntt.ntt_multiply_n_at + {N : Std.Usize} (p1 : Array parameters.FieldElement N) + (p2 : Array parameters.FieldElement N) + (zetas : Slice parameters.FieldElement) (i : Std.Usize) : + Result parameters.FieldElement + := do + let group ← i / 4#usize + let i1 ← i % 4#usize + let zeta ← + if i1 < 2#usize + then Slice.index_usize zetas group + else + do + let fe ← Slice.index_usize zetas group + parameters.FieldElement.neg fe + let i2 ← i % 2#usize + if i2 = 0#usize + then + let fe ← Array.index_usize p1 i + let i3 ← i + 1#usize + let fe1 ← Array.index_usize p1 i3 + let fe2 ← Array.index_usize p2 i + let fe3 ← Array.index_usize p2 i3 + ntt.base_case_multiply_even fe fe1 fe2 fe3 zeta + else + let i3 ← i - 1#usize + let fe ← Array.index_usize p1 i3 + let fe1 ← Array.index_usize p1 i + let fe2 ← Array.index_usize p2 i3 + let fe3 ← Array.index_usize p2 i + ntt.base_case_multiply_odd fe fe1 fe2 fe3 + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}::call]: + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +def + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + {N : Std.Usize} (c : ntt.ntt_multiply_n.closure N) (tupled_args : Std.Usize) + : + Result parameters.FieldElement + := do + let (a, a1, s) := c + ntt.ntt_multiply_n_at a a1 s tupled_args + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}::call_mut]: + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +def + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + {N : Std.Usize} (state : ntt.ntt_multiply_n.closure N) (args : Std.Usize) : + Result (parameters.FieldElement × (ntt.ntt_multiply_n.closure N)) + := do + let fe ← + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}::call_once]: + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +def + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + {N : Std.Usize} (c : ntt.ntt_multiply_n.closure N) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +@[reducible] +def + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.FnOnce (ntt.ntt_multiply_n.closure N) + Std.Usize parameters.FieldElement := { + call_once := + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +@[reducible] +def ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.FnMut (ntt.ntt_multiply_n.closure N) + Std.Usize parameters.FieldElement := { + FnOnceInst := + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + N + call_mut := + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_multiply_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_multiply_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 374:12-374:56 -/ +@[reducible] +def ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement (N + : Std.Usize) : core.ops.function.Fn (ntt.ntt_multiply_n.closure N) Std.Usize + parameters.FieldElement := { + FnMutInst := + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + N + call := + ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::ntt::ntt_multiply_n]: + Source: 'ml-kem/src/ntt.rs', lines 369:0-375:1 + Visibility: public -/ +def ntt.ntt_multiply_n + {N : Std.Usize} (p1 : Array parameters.FieldElement N) + (p2 : Array parameters.FieldElement N) + (zetas : Slice parameters.FieldElement) : + Result (Array parameters.FieldElement N) + := do + parameters.createi N + (ntt.ntt_multiply_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + N) (p1, p2, zetas) + +set_option maxRecDepth 1000 in +/-- [hacspec_ml_kem::ntt::ZETAS] + Source: 'ml-kem/src/ntt.rs', lines 63:0-192:2 + Visibility: public -/ +@[global_simps, irreducible] +def ntt.ZETAS : Result (Array parameters.FieldElement 128#usize) := do + let fe ← parameters.FieldElement.new 1#u16 + let fe1 ← parameters.FieldElement.new 1729#u16 + let fe2 ← parameters.FieldElement.new 2580#u16 + let fe3 ← parameters.FieldElement.new 3289#u16 + let fe4 ← parameters.FieldElement.new 2642#u16 + let fe5 ← parameters.FieldElement.new 630#u16 + let fe6 ← parameters.FieldElement.new 1897#u16 + let fe7 ← parameters.FieldElement.new 848#u16 + let fe8 ← parameters.FieldElement.new 1062#u16 + let fe9 ← parameters.FieldElement.new 1919#u16 + let fe10 ← parameters.FieldElement.new 193#u16 + let fe11 ← parameters.FieldElement.new 797#u16 + let fe12 ← parameters.FieldElement.new 2786#u16 + let fe13 ← parameters.FieldElement.new 3260#u16 + let fe14 ← parameters.FieldElement.new 569#u16 + let fe15 ← parameters.FieldElement.new 1746#u16 + let fe16 ← parameters.FieldElement.new 296#u16 + let fe17 ← parameters.FieldElement.new 2447#u16 + let fe18 ← parameters.FieldElement.new 1339#u16 + let fe19 ← parameters.FieldElement.new 1476#u16 + let fe20 ← parameters.FieldElement.new 3046#u16 + let fe21 ← parameters.FieldElement.new 56#u16 + let fe22 ← parameters.FieldElement.new 2240#u16 + let fe23 ← parameters.FieldElement.new 1333#u16 + let fe24 ← parameters.FieldElement.new 1426#u16 + let fe25 ← parameters.FieldElement.new 2094#u16 + let fe26 ← parameters.FieldElement.new 535#u16 + let fe27 ← parameters.FieldElement.new 2882#u16 + let fe28 ← parameters.FieldElement.new 2393#u16 + let fe29 ← parameters.FieldElement.new 2879#u16 + let fe30 ← parameters.FieldElement.new 1974#u16 + let fe31 ← parameters.FieldElement.new 821#u16 + let fe32 ← parameters.FieldElement.new 289#u16 + let fe33 ← parameters.FieldElement.new 331#u16 + let fe34 ← parameters.FieldElement.new 3253#u16 + let fe35 ← parameters.FieldElement.new 1756#u16 + let fe36 ← parameters.FieldElement.new 1197#u16 + let fe37 ← parameters.FieldElement.new 2304#u16 + let fe38 ← parameters.FieldElement.new 2277#u16 + let fe39 ← parameters.FieldElement.new 2055#u16 + let fe40 ← parameters.FieldElement.new 650#u16 + let fe41 ← parameters.FieldElement.new 1977#u16 + let fe42 ← parameters.FieldElement.new 2513#u16 + let fe43 ← parameters.FieldElement.new 632#u16 + let fe44 ← parameters.FieldElement.new 2865#u16 + let fe45 ← parameters.FieldElement.new 33#u16 + let fe46 ← parameters.FieldElement.new 1320#u16 + let fe47 ← parameters.FieldElement.new 1915#u16 + let fe48 ← parameters.FieldElement.new 2319#u16 + let fe49 ← parameters.FieldElement.new 1435#u16 + let fe50 ← parameters.FieldElement.new 807#u16 + let fe51 ← parameters.FieldElement.new 452#u16 + let fe52 ← parameters.FieldElement.new 1438#u16 + let fe53 ← parameters.FieldElement.new 2868#u16 + let fe54 ← parameters.FieldElement.new 1534#u16 + let fe55 ← parameters.FieldElement.new 2402#u16 + let fe56 ← parameters.FieldElement.new 2647#u16 + let fe57 ← parameters.FieldElement.new 2617#u16 + let fe58 ← parameters.FieldElement.new 1481#u16 + let fe59 ← parameters.FieldElement.new 648#u16 + let fe60 ← parameters.FieldElement.new 2474#u16 + let fe61 ← parameters.FieldElement.new 3110#u16 + let fe62 ← parameters.FieldElement.new 1227#u16 + let fe63 ← parameters.FieldElement.new 910#u16 + let fe64 ← parameters.FieldElement.new 17#u16 + let fe65 ← parameters.FieldElement.new 2761#u16 + let fe66 ← parameters.FieldElement.new 583#u16 + let fe67 ← parameters.FieldElement.new 2649#u16 + let fe68 ← parameters.FieldElement.new 1637#u16 + let fe69 ← parameters.FieldElement.new 723#u16 + let fe70 ← parameters.FieldElement.new 2288#u16 + let fe71 ← parameters.FieldElement.new 1100#u16 + let fe72 ← parameters.FieldElement.new 1409#u16 + let fe73 ← parameters.FieldElement.new 2662#u16 + let fe74 ← parameters.FieldElement.new 3281#u16 + let fe75 ← parameters.FieldElement.new 233#u16 + let fe76 ← parameters.FieldElement.new 756#u16 + let fe77 ← parameters.FieldElement.new 2156#u16 + let fe78 ← parameters.FieldElement.new 3015#u16 + let fe79 ← parameters.FieldElement.new 3050#u16 + let fe80 ← parameters.FieldElement.new 1703#u16 + let fe81 ← parameters.FieldElement.new 1651#u16 + let fe82 ← parameters.FieldElement.new 2789#u16 + let fe83 ← parameters.FieldElement.new 1789#u16 + let fe84 ← parameters.FieldElement.new 1847#u16 + let fe85 ← parameters.FieldElement.new 952#u16 + let fe86 ← parameters.FieldElement.new 1461#u16 + let fe87 ← parameters.FieldElement.new 2687#u16 + let fe88 ← parameters.FieldElement.new 939#u16 + let fe89 ← parameters.FieldElement.new 2308#u16 + let fe90 ← parameters.FieldElement.new 2437#u16 + let fe91 ← parameters.FieldElement.new 2388#u16 + let fe92 ← parameters.FieldElement.new 733#u16 + let fe93 ← parameters.FieldElement.new 2337#u16 + let fe94 ← parameters.FieldElement.new 268#u16 + let fe95 ← parameters.FieldElement.new 641#u16 + let fe96 ← parameters.FieldElement.new 1584#u16 + let fe97 ← parameters.FieldElement.new 2298#u16 + let fe98 ← parameters.FieldElement.new 2037#u16 + let fe99 ← parameters.FieldElement.new 3220#u16 + let fe100 ← parameters.FieldElement.new 375#u16 + let fe101 ← parameters.FieldElement.new 2549#u16 + let fe102 ← parameters.FieldElement.new 2090#u16 + let fe103 ← parameters.FieldElement.new 1645#u16 + let fe104 ← parameters.FieldElement.new 1063#u16 + let fe105 ← parameters.FieldElement.new 319#u16 + let fe106 ← parameters.FieldElement.new 2773#u16 + let fe107 ← parameters.FieldElement.new 757#u16 + let fe108 ← parameters.FieldElement.new 2099#u16 + let fe109 ← parameters.FieldElement.new 561#u16 + let fe110 ← parameters.FieldElement.new 2466#u16 + let fe111 ← parameters.FieldElement.new 2594#u16 + let fe112 ← parameters.FieldElement.new 2804#u16 + let fe113 ← parameters.FieldElement.new 1092#u16 + let fe114 ← parameters.FieldElement.new 403#u16 + let fe115 ← parameters.FieldElement.new 1026#u16 + let fe116 ← parameters.FieldElement.new 1143#u16 + let fe117 ← parameters.FieldElement.new 2150#u16 + let fe118 ← parameters.FieldElement.new 2775#u16 + let fe119 ← parameters.FieldElement.new 886#u16 + let fe120 ← parameters.FieldElement.new 1722#u16 + let fe121 ← parameters.FieldElement.new 1212#u16 + let fe122 ← parameters.FieldElement.new 1874#u16 + let fe123 ← parameters.FieldElement.new 1029#u16 + let fe124 ← parameters.FieldElement.new 2110#u16 + let fe125 ← parameters.FieldElement.new 2935#u16 + let fe126 ← parameters.FieldElement.new 885#u16 + let fe127 ← parameters.FieldElement.new 2154#u16 + ok + (Array.make 128#usize [ + fe, fe1, fe2, fe3, fe4, fe5, fe6, fe7, fe8, fe9, fe10, fe11, fe12, fe13, + fe14, fe15, fe16, fe17, fe18, fe19, fe20, fe21, fe22, fe23, fe24, fe25, + fe26, fe27, fe28, fe29, fe30, fe31, fe32, fe33, fe34, fe35, fe36, fe37, + fe38, fe39, fe40, fe41, fe42, fe43, fe44, fe45, fe46, fe47, fe48, fe49, + fe50, fe51, fe52, fe53, fe54, fe55, fe56, fe57, fe58, fe59, fe60, fe61, + fe62, fe63, fe64, fe65, fe66, fe67, fe68, fe69, fe70, fe71, fe72, fe73, + fe74, fe75, fe76, fe77, fe78, fe79, fe80, fe81, fe82, fe83, fe84, fe85, + fe86, fe87, fe88, fe89, fe90, fe91, fe92, fe93, fe94, fe95, fe96, fe97, + fe98, fe99, fe100, fe101, fe102, fe103, fe104, fe105, fe106, fe107, + fe108, fe109, fe110, fe111, fe112, fe113, fe114, fe115, fe116, fe117, + fe118, fe119, fe120, fe121, fe122, fe123, fe124, fe125, fe126, fe127 + ]) + +/-- [hacspec_ml_kem::ntt::multiply_ntts]: + Source: 'ml-kem/src/ntt.rs', lines 377:0-379:1 + Visibility: public -/ +def ntt.multiply_ntts + (p1 : Array parameters.FieldElement 256#usize) + (p2 : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← ntt.ZETAS + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + parameters.FieldElement)) a { start := 64#usize, «end» := 128#usize } + ntt.ntt_multiply_n p1 p2 s + +/-- [hacspec_ml_kem::matrix::add_polynomials::closure] + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +def matrix.add_polynomials.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : matrix.add_polynomials.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let fe1 ← Array.index_usize a1 tupled_args + let i1 ← lift (UScalar.cast .U32 fe1.val) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : matrix.add_polynomials.closure) (args : Std.Usize) : + Result (parameters.FieldElement × matrix.add_polynomials.closure) + := do + let fe ← + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : matrix.add_polynomials.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +@[reducible] +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce matrix.add_polynomials.closure Std.Usize + parameters.FieldElement := { + call_once := + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +@[reducible] +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut matrix.add_polynomials.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_polynomials::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::add_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 15:12-17:5 -/ +@[reducible] +def + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn matrix.add_polynomials.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::matrix::add_polynomials]: + Source: 'ml-kem/src/matrix.rs', lines 14:0-18:1 + Visibility: public -/ +def matrix.add_polynomials + (p1 : Array parameters.FieldElement 256#usize) + (p2 : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + matrix.add_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (p1, p2) + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column_at]: loop body 0: + Source: 'ml-kem/src/matrix.rs', lines 46:4-49:5 -/ +@[rust_loop_body] +def matrix.multiply_matrix_by_column_at_loop.body + {RANK : Std.Usize} + (m : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (i : Std.Usize) (iter : core.ops.range.Range Std.Usize) + (result : Array parameters.FieldElement 256#usize) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array + parameters.FieldElement 256#usize)) (Array parameters.FieldElement + 256#usize)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done result) + | core.option.Option.Some j => + let a ← Array.index_usize m j + let a1 ← Array.index_usize a i + let a2 ← Array.index_usize vector j + let product ← ntt.multiply_ntts a1 a2 + let result1 ← matrix.add_polynomials result product + ok (cont (iter1, result1)) + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column_at]: loop 0: + Source: 'ml-kem/src/matrix.rs', lines 46:4-49:5 -/ +@[rust_loop] +def matrix.multiply_matrix_by_column_at_loop + {RANK : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (m : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (i : Std.Usize) (result : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + loop + (fun (iter1, result1) => matrix.multiply_matrix_by_column_at_loop.body m + vector i iter1 result1) + (iter, result) + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column_at]: + Source: 'ml-kem/src/matrix.rs', lines 40:0-51:1 -/ +def matrix.multiply_matrix_by_column_at + {RANK : Std.Usize} + (m : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let fe ← parameters.FieldElement.new 0#u16 + let result := Array.repeat 256#usize fe + matrix.multiply_matrix_by_column_at_loop + { start := 0#usize, «end» := RANK } m vector i result + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : matrix.multiply_matrix_by_column.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, a1) := c + matrix.multiply_matrix_by_column_at a a1 tupled_args + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : matrix.multiply_matrix_by_column.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (matrix.multiply_matrix_by_column.closure RANK)) + := do + let a ← + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : matrix.multiply_matrix_by_column.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +@[reducible] +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (matrix.multiply_matrix_by_column.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +@[reducible] +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (matrix.multiply_matrix_by_column.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::multiply_matrix_by_column::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::multiply_matrix_by_column::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 57:12-57:66 -/ +@[reducible] +def + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn + (matrix.multiply_matrix_by_column.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnMutInst := + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::matrix::multiply_matrix_by_column]: + Source: 'ml-kem/src/matrix.rs', lines 53:0-58:1 + Visibility: public -/ +def matrix.multiply_matrix_by_column + {RANK : Std.Usize} + (m : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (matrix.multiply_matrix_by_column.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) (m, vector) + +/-- [hacspec_ml_kem::matrix::add_vectors::closure] + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +def matrix.add_vectors.closure (RANK : Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK × Array (Array + parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : matrix.add_vectors.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, a1) := c + let a2 ← Array.index_usize a tupled_args + let a3 ← Array.index_usize a1 tupled_args + matrix.add_polynomials a2 a3 + +/-- [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : matrix.add_vectors.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (matrix.add_vectors.closure RANK)) + := do + let a ← + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : matrix.add_vectors.closure RANK) (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +@[reducible] +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce (matrix.add_vectors.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + call_once := + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +@[reducible] +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut (matrix.add_vectors.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnOnceInst := + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::add_vectors::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::add_vectors::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 30:12-30:47 -/ +@[reducible] +def + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (matrix.add_vectors.closure RANK) + Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + matrix.add_vectors.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::matrix::add_vectors]: + Source: 'ml-kem/src/matrix.rs', lines 29:0-31:1 + Visibility: public -/ +def matrix.add_vectors + {RANK : Std.Usize} + (v1 : Array (Array parameters.FieldElement 256#usize) RANK) + (v2 : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (matrix.add_vectors.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) (v1, v2) + +/-- [hacspec_ml_kem::matrix::compute_As_plus_e]: + Source: 'ml-kem/src/matrix.rs', lines 176:0-183:1 + Visibility: public -/ +def matrix.compute_As_plus_e + {RANK : Std.Usize} + (a_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) (s_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (error_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let product ← matrix.multiply_matrix_by_column a_as_ntt s_as_ntt + matrix.add_vectors product error_as_ntt + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure] + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +def serialize.bitvector_to_bounded_ints.closure (N : Std.Usize) (Nd : + Std.Usize) := + Array Bool Nd × Std.Usize + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints_at]: loop body 0: + Source: 'ml-kem/src/serialize.rs', lines 161:4-171:5 -/ +@[rust_loop_body] +def serialize.bitvector_to_bounded_ints_at_loop.body + {Nd : Std.Usize} (input : Array Bool Nd) (d : Std.Usize) (i : Std.Usize) + (iter : core.ops.range.Range Std.Usize) (coefficient : Std.U16) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × Std.U16) Std.U16) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done coefficient) + | core.option.Option.Some j => + let i1 ← i * d + let i2 ← i1 + j + let b ← Array.index_usize input i2 + if b + then + let i3 ← 1#u16 <<< j + let coefficient1 ← coefficient + i3 + ok (cont (iter1, coefficient1)) + else ok (cont (iter1, coefficient)) + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints_at]: loop 0: + Source: 'ml-kem/src/serialize.rs', lines 161:4-171:5 -/ +@[rust_loop] +def serialize.bitvector_to_bounded_ints_at_loop + {Nd : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (input : Array Bool Nd) (d : Std.Usize) (i : Std.Usize) + (coefficient : Std.U16) : + Result Std.U16 + := do + loop + (fun (iter1, coefficient1) => + serialize.bitvector_to_bounded_ints_at_loop.body input d i iter1 + coefficient1) + (iter, coefficient) + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints_at]: + Source: 'ml-kem/src/serialize.rs', lines 155:0-173:1 -/ +@[reducible] +def serialize.bitvector_to_bounded_ints_at + {Nd : Std.Usize} (input : Array Bool Nd) (d : Std.Usize) (i : Std.Usize) : + Result Std.U16 + := do + serialize.bitvector_to_bounded_ints_at_loop + { start := 0#usize, «end» := d } input d i 0#u16 + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::Fn<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call + {N : Std.Usize} {Nd : Std.Usize} + (c : serialize.bitvector_to_bounded_ints.closure N Nd) + (tupled_args : Std.Usize) : + Result Std.U16 + := do + let (a, i) := c + serialize.bitvector_to_bounded_ints_at a i tupled_args + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::FnMut<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut + {N : Std.Usize} {Nd : Std.Usize} + (state : serialize.bitvector_to_bounded_ints.closure N Nd) (args : Std.Usize) + : + Result (Std.U16 × (serialize.bitvector_to_bounded_ints.closure N Nd)) + := do + let i ← + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call + state args + ok (i, state) + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::FnOnce<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16.call_once + {N : Std.Usize} {Nd : Std.Usize} + (c : serialize.bitvector_to_bounded_ints.closure N Nd) (i : Std.Usize) : + Result Std.U16 + := do + let (i1, _) ← + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut + c i + ok i1 + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::FnOnce<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +@[reducible] +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16 + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.FnOnce + (serialize.bitvector_to_bounded_ints.closure N Nd) Std.Usize Std.U16 := { + call_once := + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::FnMut<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +@[reducible] +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16 + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.FnMut + (serialize.bitvector_to_bounded_ints.closure N Nd) Std.Usize Std.U16 := { + FnOnceInst := + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU16 + N Nd + call_mut := + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bitvector_to_bounded_ints::{impl core::ops::function::Fn<(usize,), u16> for hacspec_ml_kem::serialize::bitvector_to_bounded_ints::closure<'_0, '_1, N, Nd>}] + Source: 'ml-kem/src/serialize.rs', lines 184:35-184:86 -/ +@[reducible] +def + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeU16 + (N : Std.Usize) (Nd : Std.Usize) : core.ops.function.Fn + (serialize.bitvector_to_bounded_ints.closure N Nd) Std.Usize Std.U16 := { + FnMutInst := + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU16 + N Nd + call := + serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeU16.call +} + +/-- [hacspec_ml_kem::serialize::bitvector_to_bounded_ints]: + Source: 'ml-kem/src/serialize.rs', lines 179:0-187:1 + Visibility: public -/ +def serialize.bitvector_to_bounded_ints + (N : Std.Usize) {Nd : Std.Usize} (input : Array Bool Nd) (d : Std.Usize) : + Result (Array Std.U16 N) + := do + let i ← N * d + massert (Nd = i) + let result ← + parameters.createi N + (serialize.bitvector_to_bounded_ints.closure.Insts.CoreOpsFunctionFnTupleUsizeU16 + N Nd) (input, d) + let a ← serialize.bitvector_from_bounded_ints Nd result d + let b ← + core.Array.Insts.CoreCmpPartialEqArray.eq + core.Bool.Insts.CoreCmpPartialEqBool input a + massert b + ok result + +/-- [hacspec_ml_kem::serialize::bytes_to_bits::closure] + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +@[reducible] +def serialize.bytes_to_bits.closure (N : Std.Usize) (N8 : Std.Usize) := + Array Std.U8 N + +/-- [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::Fn<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +def serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call + {N : Std.Usize} {N8 : Std.Usize} (c : serialize.bytes_to_bits.closure N N8) + (tupled_args : Std.Usize) : + Result Bool + := do + let i ← tupled_args / 8#usize + let i1 ← Array.index_usize c i + let i2 ← tupled_args % 8#usize + let i3 ← i1 >>> i2 + let i4 ← lift (i3 &&& 1#u8) + ok (i4 = 1#u8) + +/-- [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::FnMut<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +def + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut + {N : Std.Usize} {N8 : Std.Usize} + (state : serialize.bytes_to_bits.closure N N8) (args : Std.Usize) : + Result (Bool × (serialize.bytes_to_bits.closure N N8)) + := do + let b ← + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call + state args + ok (b, state) + +/-- [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::FnOnce<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +def + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool.call_once + {N : Std.Usize} {N8 : Std.Usize} (c : serialize.bytes_to_bits.closure N N8) + (i : Std.Usize) : + Result Bool + := do + let (b, _) ← + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut + c i + ok b + +/-- Trait implementation: [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::FnOnce<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +@[reducible] +def serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool + (N : Std.Usize) (N8 : Std.Usize) : core.ops.function.FnOnce + (serialize.bytes_to_bits.closure N N8) Std.Usize Bool := { + call_once := + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::FnMut<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +@[reducible] +def serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool (N + : Std.Usize) (N8 : Std.Usize) : core.ops.function.FnMut + (serialize.bytes_to_bits.closure N N8) Std.Usize Bool := { + FnOnceInst := + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeBool N + N8 + call_mut := + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::bytes_to_bits::{impl core::ops::function::Fn<(usize,), bool> for hacspec_ml_kem::serialize::bytes_to_bits::closure<'_0, N, N8>}] + Source: 'ml-kem/src/serialize.rs', lines 29:12-29:50 -/ +@[reducible] +def serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnTupleUsizeBool (N : + Std.Usize) (N8 : Std.Usize) : core.ops.function.Fn + (serialize.bytes_to_bits.closure N N8) Std.Usize Bool := { + FnMutInst := + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnMutTupleUsizeBool N + N8 + call := + serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnTupleUsizeBool.call +} + +/-- [hacspec_ml_kem::serialize::bytes_to_bits]: + Source: 'ml-kem/src/serialize.rs', lines 27:0-30:1 + Visibility: public -/ +def serialize.bytes_to_bits + {N : Std.Usize} (N8 : Std.Usize) (bytes : Array Std.U8 N) : + Result (Array Bool N8) + := do + let i ← N * 8#usize + massert (N8 = i) + parameters.createi N8 + (serialize.bytes_to_bits.closure.Insts.CoreOpsFunctionFnTupleUsizeBool N + N8) bytes + +/-- [hacspec_ml_kem::serialize::byte_decode_generic]: + Source: 'ml-kem/src/serialize.rs', lines 193:0-202:1 + Visibility: public -/ +def serialize.byte_decode_generic + (N : Std.Usize) (N8 : Std.Usize) {Nd : Std.Usize} (Nd8 : Std.Usize) + (b : Array Std.U8 Nd) (d : Std.Usize) : + Result (Array Std.U16 N8) + := do + massert (d <= parameters.BITS_PER_COEFFICIENT) + let i ← N * 8#usize + massert (N8 = i) + let i1 ← N * d + massert (Nd = i1) + let i2 ← Nd * 8#usize + massert (Nd8 = i2) + let bv ← serialize.bytes_to_bits Nd8 b + serialize.bitvector_to_bounded_ints N8 bv d + +/-- [hacspec_ml_kem::sampling::sample_ntt]: loop body 0: + Source: 'ml-kem/src/sampling.rs', lines 81:4-86:5 + Visibility: public -/ +@[rust_loop_body] +def sampling.sample_ntt_loop.body + {N8 : Std.Usize} (decoded : Array Std.U16 N8) + (iter : core.ops.range.Range Std.Usize) + (result : Array parameters.FieldElement 256#usize) + (sampled_coefficients : Std.Usize) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array + parameters.FieldElement 256#usize) × Std.Usize) ((Array + parameters.FieldElement 256#usize) × Std.Usize)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done (result, sampled_coefficients)) + | core.option.Option.Some i => + let i1 ← Array.index_usize decoded i + if i1 < parameters.FIELD_MODULUS + then + if sampled_coefficients < 256#usize + then + let fe ← parameters.FieldElement.new i1 + let a ← Array.update result sampled_coefficients fe + let sampled_coefficients1 ← sampled_coefficients + 1#usize + ok (cont (iter1, a, sampled_coefficients1)) + else ok (cont (iter1, result, sampled_coefficients)) + else ok (cont (iter1, result, sampled_coefficients)) + +/-- [hacspec_ml_kem::sampling::sample_ntt]: loop 0: + Source: 'ml-kem/src/sampling.rs', lines 81:4-86:5 + Visibility: public -/ +@[rust_loop] +def sampling.sample_ntt_loop + {N8 : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (decoded : Array Std.U16 N8) + (result : Array parameters.FieldElement 256#usize) + (sampled_coefficients : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × Std.Usize) + := do + loop + (fun (iter1, result1, sampled_coefficients1) => + sampling.sample_ntt_loop.body decoded iter1 result1 + sampled_coefficients1) + (iter, result, sampled_coefficients) + +/-- [hacspec_ml_kem::sampling::sample_ntt]: + Source: 'ml-kem/src/sampling.rs', lines 75:0-92:1 + Visibility: public -/ +def sampling.sample_ntt + (N : Std.Usize) (N8 : Std.Usize) {N12 : Std.Usize} (N96 : Std.Usize) + (bytes : Array Std.U8 N12) : + Result (core.result.Result (Array parameters.FieldElement 256#usize) + sampling.BadRejectionSamplingRandomnessError) + := do + let decoded ← serialize.byte_decode_generic N N8 N96 bytes 12#usize + let fe ← parameters.FieldElement.new 0#u16 + let result := Array.repeat 256#usize fe + let (result1, sampled_coefficients) ← + sampling.sample_ntt_loop { start := 0#usize, «end» := N8 } decoded result + 0#usize + if sampled_coefficients = 256#usize + then ok (core.result.Result.Ok result1) + else ok (core.result.Result.Err ()) + +/-- [hacspec_ml_kem::parameters::hash_functions::XOF]: + Source: 'ml-kem/src/parameters.rs', lines 289:4-291:5 + Visibility: public -/ +def parameters.hash_functions.XOF + (LEN : Std.Usize) (input : Slice Std.U8) : Result (Array Std.U8 LEN) := do + hacspec_sha3.sha3.shake128 LEN input + +/-- [hacspec_ml_kem::matrix::sample_matrix_A_row]: loop body 0: + Source: 'ml-kem/src/matrix.rs', lines 94:4-106:1 -/ +@[rust_loop_body] +def matrix.sample_matrix_A_row_loop.body + {RANK : Std.Usize} (i : Std.Usize) (iter : core.ops.range.Range Std.Usize) + (xof_input : Array Std.U8 34#usize) (transpose : Bool) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array Std.U8 + 34#usize) × Bool × (Array (Array (Array parameters.FieldElement + 256#usize) RANK) RANK)) ((core.result.Result Unit + sampling.BadRejectionSamplingRandomnessError) × (Array Std.U8 34#usize) × + (Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK))) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => + ok (done (core.result.Result.Ok (), xof_input, A_as_ntt)) + | core.option.Option.Some j => + let i1 ← lift (UScalar.cast .U8 i) + let xof_input1 ← Array.update xof_input 32#usize i1 + let i2 ← lift (UScalar.cast .U8 j) + let xof_input2 ← Array.update xof_input1 33#usize i2 + let s ← lift (Array.to_slice xof_input2) + let xof_bytes ← parameters.hash_functions.XOF 840#usize s + let r ← sampling.sample_ntt 70#usize 560#usize 6720#usize xof_bytes + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + if transpose + then + let (a, index_mut_back) ← Array.index_mut_usize A_as_ntt j + let a1 ← Array.update a i val + let a2 := index_mut_back a1 + ok (cont (iter1, xof_input2, true, a2)) + else + let (a, index_mut_back) ← Array.index_mut_usize A_as_ntt i + let a1 ← Array.update a j val + let a2 := index_mut_back a1 + ok (cont (iter1, xof_input2, false, a2)) + | core.ops.control_flow.ControlFlow.Break residual => + let r1 ← + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + Unit (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + ok (done (r1, xof_input2, A_as_ntt)) + +/-- [hacspec_ml_kem::matrix::sample_matrix_A_row]: loop 0: + Source: 'ml-kem/src/matrix.rs', lines 94:4-106:1 -/ +@[rust_loop] +def matrix.sample_matrix_A_row_loop + {RANK : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (xof_input : Array Std.U8 34#usize) (i : Std.Usize) (transpose : Bool) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) : + Result ((core.result.Result Unit + sampling.BadRejectionSamplingRandomnessError) × (Array Std.U8 34#usize) × + (Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK)) + := do + loop + (fun (iter1, xof_input1, transpose1, A_as_ntt1) => + matrix.sample_matrix_A_row_loop.body i iter1 xof_input1 transpose1 + A_as_ntt1) + (iter, xof_input, transpose, A_as_ntt) + +/-- [hacspec_ml_kem::matrix::sample_matrix_A_row]: + Source: 'ml-kem/src/matrix.rs', lines 88:0-106:1 -/ +@[reducible] +def matrix.sample_matrix_A_row + {RANK : Std.Usize} (xof_input : Array Std.U8 34#usize) (i : Std.Usize) + (transpose : Bool) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) : + Result ((core.result.Result Unit + sampling.BadRejectionSamplingRandomnessError) × (Array Std.U8 34#usize) × + (Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK)) + := do + matrix.sample_matrix_A_row_loop { start := 0#usize, «end» := RANK } + xof_input i transpose A_as_ntt + +/-- [hacspec_ml_kem::matrix::sample_matrix_A]: loop body 0: + Source: 'ml-kem/src/matrix.rs', lines 117:4-121:1 + Visibility: public -/ +@[rust_loop_body] +def matrix.sample_matrix_A_loop.body + {RANK : Std.Usize} (transpose : Bool) (iter : core.ops.range.Range Std.Usize) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) (xof_input : Array Std.U8 34#usize) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array (Array (Array + parameters.FieldElement 256#usize) RANK) RANK) × (Array Std.U8 34#usize)) + (core.result.Result (Array (Array (Array parameters.FieldElement 256#usize) + RANK) RANK) sampling.BadRejectionSamplingRandomnessError)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done (core.result.Result.Ok A_as_ntt)) + | core.option.Option.Some i => + let (r, xof_input1, A_as_ntt1) ← + matrix.sample_matrix_A_row xof_input i transpose A_as_ntt + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue _ => + ok (cont (iter1, A_as_ntt1, xof_input1)) + | core.ops.control_flow.ControlFlow.Break residual => + let r1 ← + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + (Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + ok (done r1) + +/-- [hacspec_ml_kem::matrix::sample_matrix_A]: loop 0: + Source: 'ml-kem/src/matrix.rs', lines 117:4-121:1 + Visibility: public -/ +@[rust_loop] +def matrix.sample_matrix_A_loop + {RANK : Std.Usize} (iter : core.ops.range.Range Std.Usize) (transpose : Bool) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) (xof_input : Array Std.U8 34#usize) : + Result (core.result.Result (Array (Array (Array parameters.FieldElement + 256#usize) RANK) RANK) sampling.BadRejectionSamplingRandomnessError) + := do + loop + (fun (iter1, A_as_ntt1, xof_input1) => matrix.sample_matrix_A_loop.body + transpose iter1 A_as_ntt1 xof_input1) + (iter, A_as_ntt, xof_input) + +/-- [hacspec_ml_kem::matrix::sample_matrix_A]: + Source: 'ml-kem/src/matrix.rs', lines 110:0-121:1 + Visibility: public -/ +def matrix.sample_matrix_A + (RANK : Std.Usize) (seed_for_A : Slice Std.U8) (transpose : Bool) : + Result (core.result.Result (Array (Array (Array parameters.FieldElement + 256#usize) RANK) RANK) sampling.BadRejectionSamplingRandomnessError) + := do + let fe ← parameters.FieldElement.new 0#u16 + let a := Array.repeat 256#usize fe + let a1 := Array.repeat RANK a + let A_as_ntt := Array.repeat RANK a1 + let xof_input := Array.repeat 34#usize 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) xof_input { «end» := 32#usize } + let s1 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s seed_for_A + let xof_input1 := index_mut_back s1 + matrix.sample_matrix_A_loop { start := 0#usize, «end» := RANK } transpose + A_as_ntt xof_input1 + +/-- [hacspec_ml_kem::ntt::vector_ntt::closure] + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +@[reducible] +def ntt.vector_ntt.closure (RANK : Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::ntt::ntt_layer_n::closure] + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +def ntt.ntt_layer_n.closure (N : Std.Usize) := + Array parameters.FieldElement N × Std.Usize × Slice parameters.FieldElement + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::sub]: + Source: 'ml-kem/src/parameters.rs', lines 335:4-340:5 + Visibility: public -/ +def parameters.FieldElement.sub + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result parameters.FieldElement + := do + let i ← lift (UScalar.cast .U32 self.val) + let i1 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 other.val) + let i4 ← i2 - i3 + let i5 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i6 ← i4 % i5 + let i7 ← lift (UScalar.cast .U16 i6) + parameters.FieldElement.new i7 + +/-- [hacspec_ml_kem::ntt::butterfly]: + Source: 'ml-kem/src/ntt.rs', lines 201:0-208:1 + Visibility: public -/ +def ntt.butterfly + (zeta : parameters.FieldElement) (a : parameters.FieldElement) + (b : parameters.FieldElement) : + Result (parameters.FieldElement × parameters.FieldElement) + := do + let t ← parameters.FieldElement.mul zeta b + let fe ← parameters.FieldElement.add a t + let fe1 ← parameters.FieldElement.sub a t + ok (fe, fe1) + +/-- [hacspec_ml_kem::ntt::ntt_layer_n_at]: + Source: 'ml-kem/src/ntt.rs', lines 223:0-236:1 -/ +def ntt.ntt_layer_n_at + {N : Std.Usize} (p : Array parameters.FieldElement N) (len : Std.Usize) + (zetas : Slice parameters.FieldElement) (i : Std.Usize) : + Result parameters.FieldElement + := do + let i1 ← 2#usize * len + let group ← i / i1 + let idx ← i % i1 + if idx < len + then + let fe ← Slice.index_usize zetas group + let fe1 ← Array.index_usize p i + let i2 ← i + len + let fe2 ← Array.index_usize p i2 + let (fe3, _) ← ntt.butterfly fe fe1 fe2 + ok fe3 + else + let fe ← Slice.index_usize zetas group + let i2 ← i - len + let fe1 ← Array.index_usize p i2 + let fe2 ← Array.index_usize p i + let (_, fe3) ← ntt.butterfly fe fe1 fe2 + ok fe3 + +/-- [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}::call]: + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +def ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + {N : Std.Usize} (c : ntt.ntt_layer_n.closure N) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, i, s) := c + ntt.ntt_layer_n_at a i s tupled_args + +/-- [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}::call_mut]: + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +def + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + {N : Std.Usize} (state : ntt.ntt_layer_n.closure N) (args : Std.Usize) : + Result (parameters.FieldElement × (ntt.ntt_layer_n.closure N)) + := do + let fe ← + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}::call_once]: + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +def + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + {N : Std.Usize} (c : ntt.ntt_layer_n.closure N) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +@[reducible] +def ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.FnOnce (ntt.ntt_layer_n.closure N) + Std.Usize parameters.FieldElement := { + call_once := + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +@[reducible] +def ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement (N + : Std.Usize) : core.ops.function.FnMut (ntt.ntt_layer_n.closure N) Std.Usize + parameters.FieldElement := { + FnOnceInst := + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement N + call_mut := + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::ntt_layer_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::ntt::ntt_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/ntt.rs', lines 246:12-246:54 -/ +@[reducible] +def ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement (N : + Std.Usize) : core.ops.function.Fn (ntt.ntt_layer_n.closure N) Std.Usize + parameters.FieldElement := { + FnMutInst := + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement N + call := + ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::ntt::ntt_layer_n]: + Source: 'ml-kem/src/ntt.rs', lines 241:0-247:1 + Visibility: public -/ +def ntt.ntt_layer_n + {N : Std.Usize} (p : Array parameters.FieldElement N) (len : Std.Usize) + (zetas : Slice parameters.FieldElement) : + Result (Array parameters.FieldElement N) + := do + parameters.createi N + (ntt.ntt_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement N) + (p, len, zetas) + +/-- [hacspec_ml_kem::ntt::ntt_layer]: + Source: 'ml-kem/src/ntt.rs', lines 256:0-260:1 -/ +def ntt.ntt_layer + (p : Array parameters.FieldElement 256#usize) (layer : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let len ← 1#usize <<< layer + let a ← ntt.ZETAS + let groups ← 128#usize / len + let i ← 2#usize * groups + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + parameters.FieldElement)) a { start := groups, «end» := i } + ntt.ntt_layer_n p len s + +/-- [hacspec_ml_kem::ntt::ntt]: + Source: 'ml-kem/src/ntt.rs', lines 262:0-271:1 -/ +def ntt.ntt + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let p1 ← ntt.ntt_layer p 7#usize + let p2 ← ntt.ntt_layer p1 6#usize + let p3 ← ntt.ntt_layer p2 5#usize + let p4 ← ntt.ntt_layer p3 4#usize + let p5 ← ntt.ntt_layer p4 3#usize + let p6 ← ntt.ntt_layer p5 2#usize + ntt.ntt_layer p6 1#usize + +/-- [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}::call]: + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : ntt.vector_ntt.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← Array.index_usize c tupled_args + ntt.ntt a + +/-- [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}::call_mut]: + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : ntt.vector_ntt.closure RANK) (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × (ntt.vector_ntt.closure + RANK)) + := do + let a ← + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}::call_once]: + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : ntt.vector_ntt.closure RANK) (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +@[reducible] +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce (ntt.vector_ntt.closure RANK) + Std.Usize (Array parameters.FieldElement 256#usize) := { + call_once := + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +@[reducible] +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut (ntt.vector_ntt.closure RANK) + Std.Usize (Array parameters.FieldElement 256#usize) := { + FnOnceInst := + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::ntt::vector_ntt::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ntt::vector_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/ntt.rs', lines 382:12-382:30 -/ +@[reducible] +def + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (ntt.vector_ntt.closure RANK) + Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::ntt::vector_ntt]: + Source: 'ml-kem/src/ntt.rs', lines 381:0-383:1 + Visibility: public -/ +def ntt.vector_ntt + {RANK : Std.Usize} + (vector : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (ntt.vector_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) vector + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure] + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +def ind_cpa.sample_vector_cbd.closure (RANK : Std.Usize) := + Slice Std.U8 × Std.U8 × Std.Usize + +/-- [hacspec_ml_kem::ind_cpa::concat_byte]: + Source: 'ml-kem/src/ind_cpa.rs', lines 33:0-38:1 -/ +def ind_cpa.concat_byte + {N : Std.Usize} (N1 : Std.Usize) (a : Array Std.U8 N) (b : Std.U8) : + Result (Array Std.U8 N1) + := do + let result := Array.repeat N1 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) result { «end» := N } + let s1 ← lift (Array.to_slice a) + let s2 ← core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s s1 + let result1 := index_mut_back s2 + Array.update result1 N b + +/-- [hacspec_ml_kem::sampling::sample_poly_cbd::closure] + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +def sampling.sample_poly_cbd.closure (ETA64 : Std.Usize) (ETA512 : Std.Usize) + := + Std.Usize × Array Bool ETA512 + +/-- [hacspec_ml_kem::sampling::sum_coins]: loop body 0: + Source: 'ml-kem/src/sampling.rs', lines 99:4-102:5 -/ +@[rust_loop_body] +def sampling.sum_coins_loop.body + (coins : Slice Bool) (iter : core.ops.range.Range Std.Usize) (sum : Std.U16) + : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × Std.U16) Std.U16) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done sum) + | core.option.Option.Some i => + let b ← Slice.index_usize coins i + let i1 ← lift (UScalar.cast_fromBool .U16 b) + let sum1 ← sum + i1 + ok (cont (iter1, sum1)) + +/-- [hacspec_ml_kem::sampling::sum_coins]: loop 0: + Source: 'ml-kem/src/sampling.rs', lines 99:4-102:5 -/ +@[rust_loop] +def sampling.sum_coins_loop + (iter : core.ops.range.Range Std.Usize) (coins : Slice Bool) (sum : Std.U16) + : + Result Std.U16 + := do + loop + (fun (iter1, sum1) => sampling.sum_coins_loop.body coins iter1 sum1) + (iter, sum) + +/-- [hacspec_ml_kem::sampling::sum_coins]: + Source: 'ml-kem/src/sampling.rs', lines 96:0-104:1 -/ +def sampling.sum_coins + (eta : Std.Usize) (coins : Slice Bool) : Result parameters.FieldElement := do + massert (eta <= 4#usize) + let i ← core.slice.Slice.len coins + massert (i = eta) + let sum ← + sampling.sum_coins_loop { start := 0#usize, «end» := eta } coins 0#u16 + parameters.FieldElement.new sum + +/-- [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}::call]: + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + {ETA64 : Std.Usize} {ETA512 : Std.Usize} + (c : sampling.sample_poly_cbd.closure ETA64 ETA512) (tupled_args : Std.Usize) + : + Result parameters.FieldElement + := do + let (i, a) := c + let i1 ← 2#usize * tupled_args + let i2 ← i1 * i + let i3 ← i1 * i + let i4 ← i3 + i + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Bool)) a { start := i2, «end» := i4 } + let x ← sampling.sum_coins i s + let i5 ← i1 * i + let i6 ← i5 + i + let i7 ← i1 * i + let i8 ← 2#usize * i + let i9 ← i7 + i8 + let s1 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Bool)) a { start := i6, «end» := i9 } + let y ← sampling.sum_coins i s1 + let i10 ← x.val + parameters.FIELD_MODULUS + let i11 ← i10 - y.val + let i12 ← i11 % parameters.FIELD_MODULUS + parameters.FieldElement.new i12 + +/-- [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}::call_mut]: + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + {ETA64 : Std.Usize} {ETA512 : Std.Usize} + (state : sampling.sample_poly_cbd.closure ETA64 ETA512) (args : Std.Usize) : + Result (parameters.FieldElement × (sampling.sample_poly_cbd.closure ETA64 + ETA512)) + := do + let fe ← + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}::call_once]: + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + {ETA64 : Std.Usize} {ETA512 : Std.Usize} + (c : sampling.sample_poly_cbd.closure ETA64 ETA512) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}] + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +@[reducible] +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + (ETA64 : Std.Usize) (ETA512 : Std.Usize) : core.ops.function.FnOnce + (sampling.sample_poly_cbd.closure ETA64 ETA512) Std.Usize + parameters.FieldElement := { + call_once := + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}] + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +@[reducible] +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + (ETA64 : Std.Usize) (ETA512 : Std.Usize) : core.ops.function.FnMut + (sampling.sample_poly_cbd.closure ETA64 ETA512) Std.Usize + parameters.FieldElement := { + FnOnceInst := + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + ETA64 ETA512 + call_mut := + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::sampling::sample_poly_cbd::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::sampling::sample_poly_cbd::closure<'_0, '_1, ETA64, ETA512>}] + Source: 'ml-kem/src/sampling.rs', lines 160:12-164:5 -/ +@[reducible] +def + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (ETA64 : Std.Usize) (ETA512 : Std.Usize) : core.ops.function.Fn + (sampling.sample_poly_cbd.closure ETA64 ETA512) Std.Usize + parameters.FieldElement := { + FnMutInst := + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + ETA64 ETA512 + call := + sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::sampling::sample_poly_cbd]: + Source: 'ml-kem/src/sampling.rs', lines 154:0-165:1 + Visibility: public -/ +def sampling.sample_poly_cbd + {ETA64 : Std.Usize} (ETA512 : Std.Usize) (eta : Std.Usize) + (bytes : Array Std.U8 ETA64) : + Result (Array parameters.FieldElement 256#usize) + := do + let i ← eta * 64#usize + massert (ETA64 = i) + let i1 ← eta * 512#usize + massert (ETA512 = i1) + let bits ← serialize.bytes_to_bits ETA512 bytes + parameters.createi 256#usize + (sampling.sample_poly_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + ETA64 ETA512) (eta, bits) + +/-- [hacspec_ml_kem::parameters::hash_functions::PRF]: + Source: 'ml-kem/src/parameters.rs', lines 284:4-286:5 + Visibility: public -/ +def parameters.hash_functions.PRF + (LEN : Std.Usize) (input : Slice Std.U8) : Result (Array Std.U8 LEN) := do + hacspec_sha3.sha3.shake256 LEN input + +/-- [hacspec_ml_kem::ind_cpa::sample_secret]: + Source: 'ml-kem/src/ind_cpa.rs', lines 18:0-30:1 -/ +def ind_cpa.sample_secret + (eta : Std.Usize) (prf_input : Array Std.U8 33#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + match eta.val with + | 2 => + let s ← lift (Array.to_slice prf_input) + let out ← parameters.hash_functions.PRF 128#usize s + sampling.sample_poly_cbd 1024#usize 2#usize out + | 3 => + let s ← lift (Array.to_slice prf_input) + let out ← parameters.hash_functions.PRF 192#usize s + sampling.sample_poly_cbd 1536#usize 3#usize out + | _ => +/- + let a ← + core.fmt.rt.Argument.new_display core.Usize.Insts.CoreFmtDisplay eta + let _ ← + core.fmt.Arguments.new + (Array.make 19#usize [ + 16#u8, 117#u8, 110#u8, 115#u8, 117#u8, 112#u8, 112#u8, 111#u8, + 114#u8, 116#u8, 101#u8, 100#u8, 32#u8, 101#u8, 116#u8, 97#u8, 61#u8, + 192#u8, 0#u8 + ]) (Array.make 1#usize [ a ]) +-/ + fail panic + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}::call]: + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : ind_cpa.sample_vector_cbd.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (s, i, i1) := c + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize s + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + let i2 ← lift (UScalar.cast .U8 tupled_args) + let i3 ← i + i2 + let prf_input ← ind_cpa.concat_byte 33#usize a i3 + ind_cpa.sample_secret i1 prf_input + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}::call_mut]: + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : ind_cpa.sample_vector_cbd.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (ind_cpa.sample_vector_cbd.closure RANK)) + := do + let a ← + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}::call_once]: + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : ind_cpa.sample_vector_cbd.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +@[reducible] +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (ind_cpa.sample_vector_cbd.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +@[reducible] +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (ind_cpa.sample_vector_cbd.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::ind_cpa::sample_vector_cbd::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::ind_cpa::sample_vector_cbd::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/ind_cpa.rs', lines 57:12-63:5 -/ +@[reducible] +def + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (ind_cpa.sample_vector_cbd.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd]: + Source: 'ml-kem/src/ind_cpa.rs', lines 52:0-64:1 + Visibility: public -/ +def ind_cpa.sample_vector_cbd + (RANK : Std.Usize) (eta : Std.Usize) (seed : Slice Std.U8) + (domain_separator : Std.U8) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (ind_cpa.sample_vector_cbd.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) (seed, domain_separator, eta) + +/-- [hacspec_ml_kem::ind_cpa::sample_vector_cbd_then_ntt]: + Source: 'ml-kem/src/ind_cpa.rs', lines 74:0-80:1 + Visibility: public -/ +def ind_cpa.sample_vector_cbd_then_ntt + (RANK : Std.Usize) (eta : Std.Usize) (seed : Slice Std.U8) + (domain_separator : Std.U8) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let a ← ind_cpa.sample_vector_cbd RANK eta seed domain_separator + ntt.vector_ntt a + +/-- [hacspec_ml_kem::ind_cpa::generate_keypair_unpacked]: + Source: 'ml-kem/src/ind_cpa.rs', lines 102:0-133:1 + Visibility: public -/ +def ind_cpa.generate_keypair_unpacked + (RANK : Std.Usize) (params : parameters.MlKemParams) + (key_generation_seed : Slice Std.U8) : + Result (core.result.Result ((Array (Array parameters.FieldElement 256#usize) + RANK) × (Array (Array parameters.FieldElement 256#usize) RANK) × (Array + (Array (Array parameters.FieldElement 256#usize) RANK) RANK) × (Array + Std.U8 32#usize)) sampling.BadRejectionSamplingRandomnessError) + := do + let i ← core.slice.Slice.len key_generation_seed + massert (i = 32#usize) + let g_input := Array.repeat 33#usize 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) g_input { «end» := 32#usize } + let s1 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s + key_generation_seed + let g_input1 := index_mut_back s1 + let i1 ← lift (UScalar.cast .U8 RANK) + let g_input2 ← Array.update g_input1 32#usize i1 + let s2 ← lift (Array.to_slice g_input2) + let hashed ← parameters.hash_functions.G s2 + let s3 ← lift (Array.to_slice hashed) + let (seed_for_A_slice, seed_for_secret_and_error) ← + core.slice.Slice.split_at s3 32#usize + let r ← matrix.sample_matrix_A RANK seed_for_A_slice false + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let secret_as_ntt ← + ind_cpa.sample_vector_cbd_then_ntt RANK params.eta1 + seed_for_secret_and_error 0#u8 + let i2 ← lift (UScalar.cast .U8 RANK) + let error_as_ntt ← + ind_cpa.sample_vector_cbd_then_ntt RANK params.eta1 + seed_for_secret_and_error i2 + let t_as_ntt ← matrix.compute_As_plus_e val secret_as_ntt error_as_ntt + let seed_for_A := Array.repeat 32#usize 0#u8 + let (s4, to_slice_mut_back) ← lift (Array.to_slice_mut seed_for_A) + let s5 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s4 + seed_for_A_slice + let seed_for_A1 := to_slice_mut_back s5 + ok (core.result.Result.Ok (secret_as_ntt, t_as_ntt, val, seed_for_A1)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array (Array parameters.FieldElement 256#usize) RANK) × (Array (Array + parameters.FieldElement 256#usize) RANK) × (Array (Array (Array + parameters.FieldElement 256#usize) RANK) RANK) × (Array Std.U8 + 32#usize)) (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + +/-- [hacspec_ml_kem::ind_cpa::generate_keypair]: + Source: 'ml-kem/src/ind_cpa.rs', lines 173:0-196:1 + Visibility: public -/ +def ind_cpa.generate_keypair + (RANK : Std.Usize) (EK_SIZE : Std.Usize) (DK_PKE_SIZE : Std.Usize) + (params : parameters.MlKemParams) (key_generation_seed : Slice Std.U8) : + Result (core.result.Result ((Array Std.U8 EK_SIZE) × (Array Std.U8 + DK_PKE_SIZE)) sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * i + massert (DK_PKE_SIZE = i3) + let i4 ← core.slice.Slice.len key_generation_seed + massert (i4 = 32#usize) + let r ← ind_cpa.generate_keypair_unpacked RANK params key_generation_seed + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let (secret_as_ntt, t_as_ntt, _, seed_for_A) := val + let t_encoded ← serialize.serialize_secret_key DK_PKE_SIZE t_as_ntt + let ek := Array.repeat EK_SIZE 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) ek { «end» := DK_PKE_SIZE } + let s1 ← lift (Array.to_slice t_encoded) + let s2 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s s1 + let ek1 := index_mut_back s2 + let (s3, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) ek1 { start := DK_PKE_SIZE } + let s4 ← lift (Array.to_slice seed_for_A) + let s5 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s3 s4 + let dk ← serialize.serialize_secret_key DK_PKE_SIZE secret_as_ntt + let ek2 := index_mut_back1 s5 + ok (core.result.Result.Ok (ek2, dk)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array Std.U8 EK_SIZE) × (Array Std.U8 DK_PKE_SIZE)) + (core.convert.From.Blanket sampling.BadRejectionSamplingRandomnessError) + residual + +/-- [hacspec_ml_kem::ind_cca::keygen_internal]: + Source: 'ml-kem/src/ind_cca.rs', lines 27:0-52:1 -/ +def ind_cca.keygen_internal + (RANK : Std.Usize) (EK_SIZE : Std.Usize) (DK_PKE_SIZE : Std.Usize) (DK_SIZE : + Std.Usize) (params : parameters.MlKemParams) (d : Array Std.U8 32#usize) + (z : Array Std.U8 32#usize) : + Result (core.result.Result ((Array Std.U8 EK_SIZE) × (Array Std.U8 DK_SIZE)) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * i + massert (DK_PKE_SIZE = i3) + let i4 ← DK_PKE_SIZE + EK_SIZE + let i5 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let i6 ← i5 + 32#usize + massert (DK_SIZE = i6) + let s ← lift (Array.to_slice d) + let r ← ind_cpa.generate_keypair RANK EK_SIZE DK_PKE_SIZE params s + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let (ek, dk_pke) := val + let dk := Array.repeat DK_SIZE 0#u8 + let (s1, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk { «end» := DK_PKE_SIZE } + let s2 ← lift (Array.to_slice dk_pke) + let s3 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s1 s2 + let dk1 := index_mut_back s3 + let (s4, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk1 { start := DK_PKE_SIZE, «end» := i4 } + let s5 ← lift (Array.to_slice ek) + let s6 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s4 s5 + let dk2 := index_mut_back1 s6 + let i7 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let (s7, index_mut_back2) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk2 { start := i4, «end» := i7 } + let s8 ← lift (Array.to_slice ek) + let a ← parameters.hash_functions.H s8 + let s9 ← lift (Array.to_slice a) + let s10 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s7 s9 + let dk3 := index_mut_back2 s10 + let i8 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let (s11, index_mut_back3) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk3 { start := i8 } + let s12 ← lift (Array.to_slice z) + let s13 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s11 s12 + let dk4 := index_mut_back3 s13 + ok (core.result.Result.Ok (ek, dk4)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array Std.U8 EK_SIZE) × (Array Std.U8 DK_SIZE)) + (core.convert.From.Blanket sampling.BadRejectionSamplingRandomnessError) + residual + +/-- [hacspec_ml_kem::serialize::vector_decode_12::closure] + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +@[reducible] +def serialize.vector_decode_12.closure (RANK : Std.Usize) := Slice Std.U8 + +/-- [hacspec_ml_kem::serialize::byte_decode::closure] + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +@[reducible] +def serialize.byte_decode.closure (D32 : Std.Usize) (D256 : Std.Usize) := + Array Std.U16 256#usize + +/-- [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +def + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + {D32 : Std.Usize} {D256 : Std.Usize} + (c : serialize.byte_decode.closure D32 D256) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let i ← Array.index_usize c tupled_args + let i1 ← i % parameters.FIELD_MODULUS + parameters.FieldElement.new i1 + +/-- [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +def + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + {D32 : Std.Usize} {D256 : Std.Usize} + (state : serialize.byte_decode.closure D32 D256) (args : Std.Usize) : + Result (parameters.FieldElement × (serialize.byte_decode.closure D32 D256)) + := do + let fe ← + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +def + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + {D32 : Std.Usize} {D256 : Std.Usize} + (c : serialize.byte_decode.closure D32 D256) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +@[reducible] +def + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + (D32 : Std.Usize) (D256 : Std.Usize) : core.ops.function.FnOnce + (serialize.byte_decode.closure D32 D256) Std.Usize parameters.FieldElement + := { + call_once := + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +@[reducible] +def + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + (D32 : Std.Usize) (D256 : Std.Usize) : core.ops.function.FnMut + (serialize.byte_decode.closure D32 D256) Std.Usize parameters.FieldElement + := { + FnOnceInst := + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + D32 D256 + call_mut := + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::byte_decode::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::serialize::byte_decode::closure<'_0, D32, D256>}] + Source: 'ml-kem/src/serialize.rs', lines 211:12-211:61 -/ +@[reducible] +def serialize.byte_decode.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (D32 : Std.Usize) (D256 : Std.Usize) : core.ops.function.Fn + (serialize.byte_decode.closure D32 D256) Std.Usize parameters.FieldElement + := { + FnMutInst := + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + D32 D256 + call := + serialize.byte_decode.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::serialize::byte_decode]: + Source: 'ml-kem/src/serialize.rs', lines 206:0-212:1 + Visibility: public -/ +def serialize.byte_decode + {D32 : Std.Usize} (D256 : Std.Usize) (b : Array Std.U8 D32) (d : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + massert (d <= parameters.BITS_PER_COEFFICIENT) + let s ← lift (Array.to_slice b) + let i ← core.slice.Slice.len s + let i1 ← 32#usize * d + massert (i = i1) + massert (D32 = i1) + let i2 ← 256#usize * d + massert (D256 = i2) + let decoded ← serialize.byte_decode_generic 32#usize 256#usize D256 b d + parameters.createi 256#usize + (serialize.byte_decode.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + D32 D256) decoded + +/-- [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : serialize.vector_decode_12.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let start ← tupled_args * i + let i1 ← start + 384#usize + let s ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) c { start, «end» := i1 } + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 384#usize s + let chunk ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + serialize.byte_decode 3072#usize chunk 12#usize + +/-- [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : serialize.vector_decode_12.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (serialize.vector_decode_12.closure RANK)) + := do + let a ← + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : serialize.vector_decode_12.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +@[reducible] +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (serialize.vector_decode_12.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +@[reducible] +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (serialize.vector_decode_12.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::vector_decode_12::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::vector_decode_12::closure<'_0, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 233:12-237:5 -/ +@[reducible] +def + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (serialize.vector_decode_12.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::serialize::vector_decode_12]: + Source: 'ml-kem/src/serialize.rs', lines 231:0-238:1 + Visibility: public -/ +def serialize.vector_decode_12 + (RANK : Std.Usize) (encoded : Slice Std.U8) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let i ← core.slice.Slice.len encoded + let i1 ← parameters.BYTES_PER_RING_ELEMENT + let i2 ← RANK * i1 + massert (i = i2) + parameters.createi RANK + (serialize.vector_decode_12.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) encoded + +/-- [hacspec_ml_kem::serialize::deserialize_ring_elements_reduced]: + Source: 'ml-kem/src/serialize.rs', lines 394:0-396:1 + Visibility: public -/ +def serialize.deserialize_ring_elements_reduced + (RANK : Std.Usize) (encoded : Slice Std.U8) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + serialize.vector_decode_12 RANK encoded + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::t_as_ntt_encoded_size]: + Source: 'ml-kem/src/parameters.rs', lines 40:4-42:5 + Visibility: public -/ +def parameters.MlKemParams.t_as_ntt_encoded_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + self.rank * i + +/-- [hacspec_ml_kem::serialize::byte_encode_into]: + Source: 'ml-kem/src/serialize.rs', lines 241:0-252:1 + Visibility: public -/ +def serialize.byte_encode_into + (p : Array parameters.FieldElement 256#usize) (d : Std.Usize) + (out : Slice Std.U8) : + Result (Slice Std.U8) + := do + massert (d <= parameters.BITS_PER_COEFFICIENT) + let i ← core.slice.Slice.len out + let i1 ← 32#usize * d + massert (i = i1) + match d.val with + | 1 => + let a ← serialize.byte_encode 32#usize 256#usize p 1#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | 4 => + let a ← serialize.byte_encode 128#usize 1024#usize p 4#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | 5 => + let a ← serialize.byte_encode 160#usize 1280#usize p 5#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | 10 => + let a ← serialize.byte_encode 320#usize 2560#usize p 10#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | 11 => + let a ← serialize.byte_encode 352#usize 2816#usize p 11#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | 12 => + let a ← serialize.byte_encode 384#usize 3072#usize p 12#usize + let s ← lift (Array.to_slice a) + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy out s + | _ => +/- + let a ← + core.fmt.rt.Argument.new_display core.Usize.Insts.CoreFmtDisplay d + let _ ← + core.fmt.Arguments.new + (Array.make 17#usize [ + 14#u8, 117#u8, 110#u8, 115#u8, 117#u8, 112#u8, 112#u8, 111#u8, + 114#u8, 116#u8, 101#u8, 100#u8, 32#u8, 100#u8, 61#u8, 192#u8, 0#u8 + ]) (Array.make 1#usize [ a ]) +-/ + fail panic + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_v]: + Source: 'ml-kem/src/serialize.rs', lines 346:0-350:1 + Visibility: public -/ +def serialize.compress_then_serialize_v + (V_SIZE : Std.Usize) (v : Array parameters.FieldElement 256#usize) + (dv : Std.Usize) : + Result (Array Std.U8 V_SIZE) + := do + let out := Array.repeat V_SIZE 0#u8 + let a ← compress.compress v dv + let (s, to_slice_mut_back) ← lift (Array.to_slice_mut out) + let s1 ← serialize.byte_encode_into a dv s + ok (to_slice_mut_back s1) + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_u_into]: loop body 0: + Source: 'ml-kem/src/serialize.rs', lines 315:4-324:5 + Visibility: public -/ +@[rust_loop_body] +def serialize.compress_then_serialize_u_into_loop.body + {RANK : Std.Usize} (u : Array (Array parameters.FieldElement 256#usize) RANK) + (du : Std.Usize) (du_poly_size : Std.Usize) + (iter : core.ops.range.Range Std.Usize) (out : Slice Std.U8) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Slice Std.U8)) + (Slice Std.U8)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done out) + | core.option.Option.Some i => + let a ← Array.index_usize u i + let a1 ← compress.compress a du + let i1 ← i * du_poly_size + let i2 ← i + 1#usize + let i3 ← i2 * du_poly_size + let (s, index_mut_back) ← + core.Slice.Insts.CoreOpsIndexIndexMut.index_mut + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) out { start := i1, «end» := i3 } + let s1 ← serialize.byte_encode_into a1 du s + let out1 := index_mut_back s1 + ok (cont (iter1, out1)) + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_u_into]: loop 0: + Source: 'ml-kem/src/serialize.rs', lines 315:4-324:5 + Visibility: public -/ +@[rust_loop] +def serialize.compress_then_serialize_u_into_loop + {RANK : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (u : Array (Array parameters.FieldElement 256#usize) RANK) (du : Std.Usize) + (out : Slice Std.U8) (du_poly_size : Std.Usize) : + Result (Slice Std.U8) + := do + loop + (fun (iter1, out1) => serialize.compress_then_serialize_u_into_loop.body u + du du_poly_size iter1 out1) + (iter, out) + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_u_into]: + Source: 'ml-kem/src/serialize.rs', lines 309:0-325:1 + Visibility: public -/ +def serialize.compress_then_serialize_u_into + {RANK : Std.Usize} (u : Array (Array parameters.FieldElement 256#usize) RANK) + (du : Std.Usize) (out : Slice Std.U8) : + Result (Slice Std.U8) + := do + let i ← parameters.COEFFICIENTS_IN_RING_ELEMENT * du + let du_poly_size ← i / 8#usize + serialize.compress_then_serialize_u_into_loop + { start := 0#usize, «end» := RANK } u du out du_poly_size + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_u]: + Source: 'ml-kem/src/serialize.rs', lines 334:0-341:1 + Visibility: public -/ +def serialize.compress_then_serialize_u + {RANK : Std.Usize} (U_SIZE : Std.Usize) + (u : Array (Array parameters.FieldElement 256#usize) RANK) (du : Std.Usize) : + Result (Array Std.U8 U_SIZE) + := do + let out := Array.repeat U_SIZE 0#u8 + let (s, to_slice_mut_back) ← lift (Array.to_slice_mut out) + let s1 ← serialize.compress_then_serialize_u_into u du s + ok (to_slice_mut_back s1) + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_message]: + Source: 'ml-kem/src/serialize.rs', lines 282:0-284:1 + Visibility: public -/ +def serialize.deserialize_then_decompress_message + (serialized : Array Std.U8 32#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← serialize.byte_decode 256#usize serialized 1#usize + compress.decompress a 1#usize + +/-- [hacspec_ml_kem::matrix::compute_vector_u::closure] + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +@[reducible] +def matrix.compute_vector_u.closure (RANK : Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure] + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +@[reducible] +def invert_ntt.ntt_inverse_layer.closure := Std.Usize + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_zeta]: + Source: 'ml-kem/src/invert_ntt.rs', lines 89:0-95:1 -/ +def invert_ntt.ntt_inverse_layer_zeta + (groups : Std.Usize) (round : Std.Usize) : + Result parameters.FieldElement + := do + if round < groups + then + let a ← ntt.ZETAS + let i ← 2#usize * groups + let i1 ← i - 1#usize + let i2 ← i1 - round + Array.index_usize a i2 + else parameters.FieldElement.new 0#u16 + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}::call]: + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : invert_ntt.ntt_inverse_layer.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + invert_ntt.ntt_inverse_layer_zeta c tupled_args + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}::call_mut]: + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : invert_ntt.ntt_inverse_layer.closure) (args : Std.Usize) : + Result (parameters.FieldElement × invert_ntt.ntt_inverse_layer.closure) + := do + let fe ← + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}::call_once]: + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : invert_ntt.ntt_inverse_layer.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce invert_ntt.ntt_inverse_layer.closure Std.Usize + parameters.FieldElement := { + call_once := + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut invert_ntt.ntt_inverse_layer.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 107:45-107:90 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn invert_ntt.ntt_inverse_layer.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure] + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +def invert_ntt.ntt_inverse_layer_n.closure (N : Std.Usize) := + Array parameters.FieldElement N × Std.Usize × Slice parameters.FieldElement + +/-- [hacspec_ml_kem::invert_ntt::inv_butterfly]: + Source: 'ml-kem/src/invert_ntt.rs', lines 39:0-45:1 + Visibility: public -/ +def invert_ntt.inv_butterfly + (zeta : parameters.FieldElement) (a : parameters.FieldElement) + (b : parameters.FieldElement) : + Result (parameters.FieldElement × parameters.FieldElement) + := do + let fe ← parameters.FieldElement.add a b + let fe1 ← parameters.FieldElement.sub b a + let fe2 ← parameters.FieldElement.mul zeta fe1 + ok (fe, fe2) + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n_at]: + Source: 'ml-kem/src/invert_ntt.rs', lines 57:0-70:1 -/ +def invert_ntt.ntt_inverse_layer_n_at + {N : Std.Usize} (p : Array parameters.FieldElement N) (len : Std.Usize) + (zetas : Slice parameters.FieldElement) (i : Std.Usize) : + Result parameters.FieldElement + := do + let i1 ← 2#usize * len + let group ← i / i1 + let idx ← i % i1 + if idx < len + then + let fe ← Slice.index_usize zetas group + let fe1 ← Array.index_usize p i + let i2 ← i + len + let fe2 ← Array.index_usize p i2 + let (fe3, _) ← invert_ntt.inv_butterfly fe fe1 fe2 + ok fe3 + else + let fe ← Slice.index_usize zetas group + let i2 ← i - len + let fe1 ← Array.index_usize p i2 + let fe2 ← Array.index_usize p i + let (_, fe3) ← invert_ntt.inv_butterfly fe fe1 fe2 + ok fe3 + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}::call]: + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + {N : Std.Usize} (c : invert_ntt.ntt_inverse_layer_n.closure N) + (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, i, s) := c + invert_ntt.ntt_inverse_layer_n_at a i s tupled_args + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}::call_mut]: + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + {N : Std.Usize} (state : invert_ntt.ntt_inverse_layer_n.closure N) + (args : Std.Usize) : + Result (parameters.FieldElement × (invert_ntt.ntt_inverse_layer_n.closure + N)) + := do + let fe ← + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}::call_once]: + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + {N : Std.Usize} (c : invert_ntt.ntt_inverse_layer_n.closure N) + (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.FnOnce + (invert_ntt.ntt_inverse_layer_n.closure N) Std.Usize parameters.FieldElement + := { + call_once := + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.FnMut + (invert_ntt.ntt_inverse_layer_n.closure N) Std.Usize parameters.FieldElement + := { + FnOnceInst := + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + N + call_mut := + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n::closure<'_0, '_1, '_2, N>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 80:12-80:62 -/ +@[reducible] +def + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (N : Std.Usize) : core.ops.function.Fn + (invert_ntt.ntt_inverse_layer_n.closure N) Std.Usize parameters.FieldElement + := { + FnMutInst := + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + N + call := + invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer_n]: + Source: 'ml-kem/src/invert_ntt.rs', lines 75:0-81:1 + Visibility: public -/ +def invert_ntt.ntt_inverse_layer_n + {N : Std.Usize} (p : Array parameters.FieldElement N) (len : Std.Usize) + (zetas : Slice parameters.FieldElement) : + Result (Array parameters.FieldElement N) + := do + parameters.createi N + (invert_ntt.ntt_inverse_layer_n.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + N) (p, len, zetas) + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_layer]: + Source: 'ml-kem/src/invert_ntt.rs', lines 103:0-109:1 -/ +def invert_ntt.ntt_inverse_layer + (p : Array parameters.FieldElement 256#usize) (layer : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let len ← 1#usize <<< layer + let groups ← 128#usize / len + let zetas ← + parameters.createi 128#usize + invert_ntt.ntt_inverse_layer.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + groups + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + parameters.FieldElement)) zetas { start := 0#usize, «end» := groups } + invert_ntt.ntt_inverse_layer_n p len s + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse_butterflies]: + Source: 'ml-kem/src/invert_ntt.rs', lines 129:0-138:1 + Visibility: public -/ +def invert_ntt.ntt_inverse_butterflies + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let p1 ← invert_ntt.ntt_inverse_layer p 1#usize + let p2 ← invert_ntt.ntt_inverse_layer p1 2#usize + let p3 ← invert_ntt.ntt_inverse_layer p2 3#usize + let p4 ← invert_ntt.ntt_inverse_layer p3 4#usize + let p5 ← invert_ntt.ntt_inverse_layer p4 5#usize + let p6 ← invert_ntt.ntt_inverse_layer p5 6#usize + invert_ntt.ntt_inverse_layer p6 7#usize + +/-- [hacspec_ml_kem::invert_ntt::reduce_polynomial::closure] + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +@[reducible] +def invert_ntt.reduce_polynomial.closure := + Array parameters.FieldElement 256#usize + +/-- [hacspec_ml_kem::invert_ntt::INVERSE_OF_128] + Source: 'ml-kem/src/invert_ntt.rs', lines 4:0-4:61 -/ +@[global_simps, irreducible] +def invert_ntt.INVERSE_OF_128 : Result parameters.FieldElement := + parameters.FieldElement.new 3303#u16 + +/-- [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}::call]: + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : invert_ntt.reduce_polynomial.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let fe ← Array.index_usize c tupled_args + let fe1 ← invert_ntt.INVERSE_OF_128 + parameters.FieldElement.mul fe fe1 + +/-- [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}::call_mut]: + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : invert_ntt.reduce_polynomial.closure) (args : Std.Usize) : + Result (parameters.FieldElement × invert_ntt.reduce_polynomial.closure) + := do + let fe ← + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}::call_once]: + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : invert_ntt.reduce_polynomial.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +@[reducible] +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce invert_ntt.reduce_polynomial.closure Std.Usize + parameters.FieldElement := { + call_once := + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +@[reducible] +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut invert_ntt.reduce_polynomial.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::reduce_polynomial::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::reduce_polynomial::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 112:12-112:40 -/ +@[reducible] +def + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn invert_ntt.reduce_polynomial.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::invert_ntt::reduce_polynomial]: + Source: 'ml-kem/src/invert_ntt.rs', lines 111:0-113:1 + Visibility: public -/ +def invert_ntt.reduce_polynomial + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + invert_ntt.reduce_polynomial.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + p + +/-- [hacspec_ml_kem::invert_ntt::ntt_inverse]: + Source: 'ml-kem/src/invert_ntt.rs', lines 140:0-142:1 + Visibility: public -/ +def invert_ntt.ntt_inverse + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← invert_ntt.ntt_inverse_butterflies p + invert_ntt.reduce_polynomial a + +/-- [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : matrix.compute_vector_u.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← Array.index_usize c tupled_args + invert_ntt.ntt_inverse a + +/-- [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : matrix.compute_vector_u.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (matrix.compute_vector_u.closure RANK)) + := do + let a ← + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : matrix.compute_vector_u.closure RANK) (i : Std.Usize) + : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +@[reducible] +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (matrix.compute_vector_u.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +@[reducible] +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut (matrix.compute_vector_u.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnOnceInst := + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::compute_vector_u::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::compute_vector_u::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 166:44-166:71 -/ +@[reducible] +def + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (matrix.compute_vector_u.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::matrix::transpose::closure::closure] + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +def matrix.transpose.closure.closure (RANK : Std.Usize) := + Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK × + Std.Usize + +/-- [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : matrix.transpose.closure.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, i) := c + let a1 ← Array.index_usize a tupled_args + Array.index_usize a1 i + +/-- [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : matrix.transpose.closure.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (matrix.transpose.closure.closure RANK)) + := do + let a ← + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : matrix.transpose.closure.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +@[reducible] +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (matrix.transpose.closure.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +@[reducible] +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (matrix.transpose.closure.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::closure::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::matrix::transpose::closure::closure<'_0, '_1, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:24-73:35 -/ +@[reducible] +def + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn (matrix.transpose.closure.closure + RANK) Std.Usize (Array parameters.FieldElement 256#usize) := { + FnMutInst := + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::matrix::transpose::closure] + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +@[reducible] +def matrix.transpose.closure (RANK : Std.Usize) := + Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK + +/-- [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::Fn<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayArrayFieldElement256RANK.call + {RANK : Std.Usize} (c : matrix.transpose.closure RANK) + (tupled_args : Std.Usize) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (matrix.transpose.closure.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) (c, tupled_args) + +/-- [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::FnMut<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayArrayFieldElement256RANK.call_mut + {RANK : Std.Usize} (state : matrix.transpose.closure RANK) (args : Std.Usize) + : + Result ((Array (Array parameters.FieldElement 256#usize) RANK) × + (matrix.transpose.closure RANK)) + := do + let a ← + matrix.transpose.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayArrayFieldElement256RANK.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::FnOnce<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayArrayFieldElement256RANK.call_once + {RANK : Std.Usize} (c : matrix.transpose.closure RANK) (i : Std.Usize) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let (a, _) ← + matrix.transpose.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayArrayFieldElement256RANK.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::FnOnce<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +@[reducible] +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayArrayFieldElement256RANK + (RANK : Std.Usize) : core.ops.function.FnOnce (matrix.transpose.closure RANK) + Std.Usize (Array (Array parameters.FieldElement 256#usize) RANK) := { + call_once := + matrix.transpose.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayArrayFieldElement256RANK.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::FnMut<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +@[reducible] +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayArrayFieldElement256RANK + (RANK : Std.Usize) : core.ops.function.FnMut (matrix.transpose.closure RANK) + Std.Usize (Array (Array parameters.FieldElement 256#usize) RANK) := { + FnOnceInst := + matrix.transpose.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayArrayFieldElement256RANK + RANK + call_mut := + matrix.transpose.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayArrayFieldElement256RANK.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::transpose::{impl core::ops::function::Fn<(usize,), [[hacspec_ml_kem::parameters::FieldElement; 256usize]; RANK]> for hacspec_ml_kem::matrix::transpose::closure<'_0, RANK>}] + Source: 'ml-kem/src/matrix.rs', lines 73:12-73:36 -/ +@[reducible] +def + matrix.transpose.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayArrayFieldElement256RANK + (RANK : Std.Usize) : core.ops.function.Fn (matrix.transpose.closure RANK) + Std.Usize (Array (Array parameters.FieldElement 256#usize) RANK) := { + FnMutInst := + matrix.transpose.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayArrayFieldElement256RANK + RANK + call := + matrix.transpose.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayArrayFieldElement256RANK.call +} + +/-- [hacspec_ml_kem::matrix::transpose]: + Source: 'ml-kem/src/matrix.rs', lines 72:0-74:1 + Visibility: public -/ +def matrix.transpose + {RANK : Std.Usize} + (m : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) : + Result (Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + := do + parameters.createi RANK + (matrix.transpose.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayArrayFieldElement256RANK + RANK) m + +/-- [hacspec_ml_kem::matrix::compute_vector_u]: + Source: 'ml-kem/src/matrix.rs', lines 159:0-168:1 + Visibility: public -/ +def matrix.compute_vector_u + {RANK : Std.Usize} + (a_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) (r_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (error_1 : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let a_transpose ← matrix.transpose a_as_ntt + let product ← matrix.multiply_matrix_by_column a_transpose r_as_ntt + let product_inv ← + parameters.createi RANK + (matrix.compute_vector_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) product + matrix.add_vectors product_inv error_1 + +/-- [hacspec_ml_kem::matrix::multiply_vectors]: loop body 0: + Source: 'ml-kem/src/matrix.rs', lines 62:4-65:5 + Visibility: public -/ +@[rust_loop_body] +def matrix.multiply_vectors_loop.body + {RANK : Std.Usize} + (v1 : Array (Array parameters.FieldElement 256#usize) RANK) + (v2 : Array (Array parameters.FieldElement 256#usize) RANK) + (iter : core.ops.range.Range Std.Usize) + (result : Array parameters.FieldElement 256#usize) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array + parameters.FieldElement 256#usize)) (Array parameters.FieldElement + 256#usize)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done result) + | core.option.Option.Some j => + let a ← Array.index_usize v1 j + let a1 ← Array.index_usize v2 j + let product ← ntt.multiply_ntts a a1 + let result1 ← matrix.add_polynomials result product + ok (cont (iter1, result1)) + +/-- [hacspec_ml_kem::matrix::multiply_vectors]: loop 0: + Source: 'ml-kem/src/matrix.rs', lines 62:4-65:5 + Visibility: public -/ +@[rust_loop] +def matrix.multiply_vectors_loop + {RANK : Std.Usize} (iter : core.ops.range.Range Std.Usize) + (v1 : Array (Array parameters.FieldElement 256#usize) RANK) + (v2 : Array (Array parameters.FieldElement 256#usize) RANK) + (result : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + loop + (fun (iter1, result1) => matrix.multiply_vectors_loop.body v1 v2 iter1 + result1) + (iter, result) + +/-- [hacspec_ml_kem::matrix::multiply_vectors]: + Source: 'ml-kem/src/matrix.rs', lines 60:0-67:1 + Visibility: public -/ +def matrix.multiply_vectors + {RANK : Std.Usize} + (v1 : Array (Array parameters.FieldElement 256#usize) RANK) + (v2 : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array parameters.FieldElement 256#usize) + := do + let fe ← parameters.FieldElement.new 0#u16 + let result := Array.repeat 256#usize fe + matrix.multiply_vectors_loop { start := 0#usize, «end» := RANK } v1 v2 + result + +/-- [hacspec_ml_kem::matrix::compute_ring_element_v]: + Source: 'ml-kem/src/matrix.rs', lines 143:0-152:1 + Visibility: public -/ +def matrix.compute_ring_element_v + {RANK : Std.Usize} + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (r_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (error_2 : Array parameters.FieldElement 256#usize) + (message : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let inner_product ← matrix.multiply_vectors t_as_ntt r_as_ntt + let inner_product_inv ← invert_ntt.ntt_inverse inner_product + let a ← matrix.add_polynomials inner_product_inv error_2 + matrix.add_polynomials a message + +/-- [hacspec_ml_kem::ind_cpa::encrypt_unpacked]: + Source: 'ml-kem/src/ind_cpa.rs', lines 297:0-349:1 + Visibility: public -/ +def ind_cpa.encrypt_unpacked + {RANK : Std.Usize} (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) (CT_SIZE : + Std.Usize) (params : parameters.MlKemParams) + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (A_as_ntt : Array (Array (Array parameters.FieldElement 256#usize) RANK) + RANK) (message : Array Std.U8 32#usize) (randomness : Slice Std.U8) : + Result (core.result.Result (Array Std.U8 CT_SIZE) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i1 ← i * params.du + let i2 ← i1 / 8#usize + massert (U_SIZE = i2) + let i3 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i4 ← i3 / 8#usize + massert (V_SIZE = i4) + let i5 ← U_SIZE + V_SIZE + massert (CT_SIZE = i5) + let i6 ← core.slice.Slice.len randomness + massert (i6 = 32#usize) + let r_as_ntt ← + ind_cpa.sample_vector_cbd_then_ntt RANK params.eta1 randomness 0#u8 + let i7 ← lift (UScalar.cast .U8 RANK) + let error_1 ← ind_cpa.sample_vector_cbd RANK params.eta2 randomness i7 + let prf_input := Array.repeat 33#usize 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) prf_input { «end» := 32#usize } + let s1 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s randomness + let i8 ← RANK * 2#usize + let prf_input1 := index_mut_back s1 + let i9 ← lift (UScalar.cast .U8 i8) + let prf_input2 ← Array.update prf_input1 32#usize i9 + let error_2 ← ind_cpa.sample_secret params.eta2 prf_input2 + let u ← matrix.compute_vector_u A_as_ntt r_as_ntt error_1 + let message_as_ring_element ← + serialize.deserialize_then_decompress_message message + let v ← + matrix.compute_ring_element_v t_as_ntt r_as_ntt error_2 + message_as_ring_element + let c1 ← serialize.compress_then_serialize_u U_SIZE u params.du + let c2 ← serialize.compress_then_serialize_v V_SIZE v params.dv + let c := Array.repeat CT_SIZE 0#u8 + let (s2, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) c { «end» := U_SIZE } + let s3 ← lift (Array.to_slice c1) + let s4 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s2 s3 + let c3 := index_mut_back1 s4 + let (s5, index_mut_back2) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) c3 { start := U_SIZE } + let s6 ← lift (Array.to_slice c2) + let s7 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s5 s6 + let c4 := index_mut_back2 s7 + ok (core.result.Result.Ok c4) + +/-- [hacspec_ml_kem::ind_cpa::encrypt]: + Source: 'ml-kem/src/ind_cpa.rs', lines 244:0-281:1 + Visibility: public -/ +def ind_cpa.encrypt + (RANK : Std.Usize) (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) (CT_SIZE : + Std.Usize) (params : parameters.MlKemParams) (ek : Slice Std.U8) + (message : Array Std.U8 32#usize) (randomness : Slice Std.U8) : + Result (core.result.Result (Array Std.U8 CT_SIZE) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i1 ← i * params.du + let i2 ← i1 / 8#usize + massert (U_SIZE = i2) + let i3 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i4 ← i3 / 8#usize + massert (V_SIZE = i4) + let i5 ← U_SIZE + V_SIZE + massert (CT_SIZE = i5) + let i6 ← core.slice.Slice.len ek + let i7 ← parameters.BYTES_PER_RING_ELEMENT + let i8 ← RANK * i7 + let i9 ← i8 + 32#usize + massert (i6 = i9) + let i10 ← core.slice.Slice.len randomness + massert (i10 = 32#usize) + let t_encoded_size ← parameters.MlKemParams.t_as_ntt_encoded_size params + let s ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) ek { «end» := t_encoded_size } + let t_as_ntt ← serialize.deserialize_ring_elements_reduced RANK s + let seed_for_A ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) ek { start := t_encoded_size } + let r ← matrix.sample_matrix_A RANK seed_for_A false + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + ind_cpa.encrypt_unpacked U_SIZE V_SIZE CT_SIZE params t_as_ntt val message + randomness + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + (Array Std.U8 CT_SIZE) (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + +/-- [hacspec_ml_kem::ind_cca::encaps_internal]: + Source: 'ml-kem/src/ind_cca.rs', lines 75:0-106:1 -/ +def ind_cca.encaps_internal + (RANK : Std.Usize) (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) (CT_SIZE : + Std.Usize) (params : parameters.MlKemParams) (ek : Slice Std.U8) + (m : Array Std.U8 32#usize) : + Result (core.result.Result ((Array Std.U8 32#usize) × (Array Std.U8 + CT_SIZE)) sampling.BadRejectionSamplingRandomnessError) + := do + let i ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i1 ← i * params.du + let i2 ← i1 / 8#usize + massert (U_SIZE = i2) + let i3 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i4 ← i3 / 8#usize + massert (V_SIZE = i4) + let i5 ← U_SIZE + V_SIZE + massert (CT_SIZE = i5) + let i6 ← core.slice.Slice.len ek + let i7 ← parameters.BYTES_PER_RING_ELEMENT + let i8 ← RANK * i7 + let i9 ← i8 + 32#usize + massert (i6 = i9) + let to_hash := Array.repeat 64#usize 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash { «end» := 32#usize } + let s1 ← lift (Array.to_slice m) + let s2 ← core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s s1 + let to_hash1 := index_mut_back s2 + let (s3, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash1 { start := 32#usize } + let a ← parameters.hash_functions.H ek + let s4 ← lift (Array.to_slice a) + let s5 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s3 s4 + let to_hash2 := index_mut_back1 s5 + let s6 ← lift (Array.to_slice to_hash2) + let hashed ← parameters.hash_functions.G s6 + let s7 ← lift (Array.to_slice hashed) + let (shared_secret, pseudorandomness) ← + core.slice.Slice.split_at s7 32#usize + let s8 ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) pseudorandomness { «end» := 32#usize } + let r ← + core.Array.Insts.CoreConvertTryFromShared0SliceTryFromSliceError.try_from + 32#usize core.U8.Insts.CoreMarkerCopy s8 + let r1 ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + let s9 ← lift (Array.to_slice r1) + let r2 ← ind_cpa.encrypt RANK U_SIZE V_SIZE CT_SIZE params ek m s9 + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r2 + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let k := Array.repeat 32#usize 0#u8 + let (s10, to_slice_mut_back) ← lift (Array.to_slice_mut k) + let s11 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s10 + shared_secret + let k1 := to_slice_mut_back s11 + ok (core.result.Result.Ok (k1, val)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array Std.U8 32#usize) × (Array Std.U8 CT_SIZE)) + (core.convert.From.Blanket sampling.BadRejectionSamplingRandomnessError) + residual + +/-- [hacspec_ml_kem::parameters::hash_functions::J]: + Source: 'ml-kem/src/parameters.rs', lines 294:4-296:5 + Visibility: public -/ +def parameters.hash_functions.J + (LEN : Std.Usize) (input : Slice Std.U8) : Result (Array Std.U8 LEN) := do + hacspec_sha3.sha3.shake256 LEN input + +/-- [hacspec_ml_kem::serialize::byte_decode_dyn]: + Source: 'ml-kem/src/serialize.rs', lines 255:0-266:1 + Visibility: public -/ +def serialize.byte_decode_dyn + (b : Slice Std.U8) (d : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + massert (d <= parameters.BITS_PER_COEFFICIENT) + let i ← core.slice.Slice.len b + let i1 ← 32#usize * d + massert (i = i1) + match d.val with + | 1 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 256#usize a 1#usize + | 4 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 128#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 1024#usize a 4#usize + | 5 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 160#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 1280#usize a 5#usize + | 10 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 320#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 2560#usize a 10#usize + | 11 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 352#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 2816#usize a 11#usize + | 12 => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 384#usize b + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + serialize.byte_decode 3072#usize a 12#usize + | _ => +/- + let a ← + core.fmt.rt.Argument.new_display core.Usize.Insts.CoreFmtDisplay d + let _ ← + core.fmt.Arguments.new + (Array.make 17#usize [ + 14#u8, 117#u8, 110#u8, 115#u8, 117#u8, 112#u8, 112#u8, 111#u8, + 114#u8, 116#u8, 101#u8, 100#u8, 32#u8, 100#u8, 61#u8, 192#u8, 0#u8 + ]) (Array.make 1#usize [ a ]) +-/ + fail panic + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_v]: + Source: 'ml-kem/src/serialize.rs', lines 385:0-387:1 + Visibility: public -/ +def serialize.deserialize_then_decompress_v + (serialized : Slice Std.U8) (dv : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← serialize.byte_decode_dyn serialized dv + compress.decompress a dv + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure] + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +def serialize.deserialize_then_decompress_u.closure (RANK : Std.Usize) := + Std.Usize × Slice Std.U8 × Std.Usize + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : serialize.deserialize_then_decompress_u.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (i, s, i1) := c + let start ← tupled_args * i + let i2 ← start + i + let s1 ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) s { start, «end» := i2 } + let a ← serialize.byte_decode_dyn s1 i1 + compress.decompress a i1 + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} + (state : serialize.deserialize_then_decompress_u.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (serialize.deserialize_then_decompress_u.closure RANK)) + := do + let a ← + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : serialize.deserialize_then_decompress_u.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +@[reducible] +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (serialize.deserialize_then_decompress_u.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +@[reducible] +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (serialize.deserialize_then_decompress_u.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::deserialize_then_decompress_u::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::serialize::deserialize_then_decompress_u::closure<'_0, '_1, '_2, RANK>}] + Source: 'ml-kem/src/serialize.rs', lines 360:12-366:5 -/ +@[reducible] +def + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn + (serialize.deserialize_then_decompress_u.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnMutInst := + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u]: + Source: 'ml-kem/src/serialize.rs', lines 355:0-367:1 + Visibility: public -/ +def serialize.deserialize_then_decompress_u + (RANK : Std.Usize) (ciphertext : Slice Std.U8) (du : Std.Usize) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let i ← parameters.COEFFICIENTS_IN_RING_ELEMENT * du + let du_poly_size ← i / 8#usize + parameters.createi RANK + (serialize.deserialize_then_decompress_u.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) (du_poly_size, ciphertext, du) + +/-- [hacspec_ml_kem::serialize::compress_then_serialize_message]: + Source: 'ml-kem/src/serialize.rs', lines 276:0-278:1 + Visibility: public -/ +def serialize.compress_then_serialize_message + (re : Array parameters.FieldElement 256#usize) : + Result (Array Std.U8 32#usize) + := do + let a ← compress.compress re 1#usize + serialize.byte_encode 32#usize 256#usize a 1#usize + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::u_encoded_size]: + Source: 'ml-kem/src/parameters.rs', lines 52:4-54:5 + Visibility: public -/ +def parameters.MlKemParams.u_encoded_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← self.rank * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i1 ← i * self.du + i1 / 8#usize + +/-- [hacspec_ml_kem::matrix::sub_polynomials::closure] + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +def matrix.sub_polynomials.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : matrix.sub_polynomials.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let i1 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i2 ← i + i1 + let fe1 ← Array.index_usize a1 tupled_args + let i3 ← lift (UScalar.cast .U32 fe1.val) + let i4 ← i2 - i3 + let i5 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i6 ← i4 % i5 + let i7 ← lift (UScalar.cast .U16 i6) + parameters.FieldElement.new i7 + +/-- [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : matrix.sub_polynomials.closure) (args : Std.Usize) : + Result (parameters.FieldElement × matrix.sub_polynomials.closure) + := do + let fe ← + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : matrix.sub_polynomials.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +@[reducible] +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce matrix.sub_polynomials.closure Std.Usize + parameters.FieldElement := { + call_once := + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +@[reducible] +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut matrix.sub_polynomials.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::matrix::sub_polynomials::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::matrix::sub_polynomials::closure<'_0, '_1>}] + Source: 'ml-kem/src/matrix.rs', lines 21:12-26:5 -/ +@[reducible] +def + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn matrix.sub_polynomials.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::matrix::sub_polynomials]: + Source: 'ml-kem/src/matrix.rs', lines 20:0-27:1 + Visibility: public -/ +def matrix.sub_polynomials + (p1 : Array parameters.FieldElement 256#usize) + (p2 : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + matrix.sub_polynomials.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (p1, p2) + +/-- [hacspec_ml_kem::matrix::compute_message]: + Source: 'ml-kem/src/matrix.rs', lines 128:0-136:1 + Visibility: public -/ +def matrix.compute_message + {RANK : Std.Usize} (v : Array parameters.FieldElement 256#usize) + (secret_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (u_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array parameters.FieldElement 256#usize) + := do + let inner_product ← matrix.multiply_vectors secret_as_ntt u_as_ntt + let inner_product_inv ← invert_ntt.ntt_inverse inner_product + matrix.sub_polynomials v inner_product_inv + +/-- [hacspec_ml_kem::ind_cpa::decrypt_unpacked]: + Source: 'ml-kem/src/ind_cpa.rs', lines 399:0-425:1 + Visibility: public -/ +def ind_cpa.decrypt_unpacked + {RANK : Std.Usize} (params : parameters.MlKemParams) + (secret_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (ciphertext : Slice Std.U8) : + Result (Array Std.U8 32#usize) + := do + let i ← core.slice.Slice.len ciphertext + let i1 ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i2 ← i1 * params.du + let i3 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i4 ← i2 + i3 + let i5 ← i4 / 8#usize + massert (i = i5) + let u_encoded_size ← parameters.MlKemParams.u_encoded_size params + let s ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) ciphertext { start := 0#usize, «end» := u_encoded_size } + let u ← serialize.deserialize_then_decompress_u RANK s params.du + let s1 ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) ciphertext { start := u_encoded_size } + let v ← serialize.deserialize_then_decompress_v s1 params.dv + let u_as_ntt ← ntt.vector_ntt u + let w ← matrix.compute_message v secret_as_ntt u_as_ntt + serialize.compress_then_serialize_message w + +/-- [hacspec_ml_kem::ind_cpa::decrypt]: + Source: 'ml-kem/src/ind_cpa.rs', lines 375:0-388:1 + Visibility: public -/ +def ind_cpa.decrypt + (RANK : Std.Usize) (params : parameters.MlKemParams) (dk : Slice Std.U8) + (ciphertext : Slice Std.U8) : + Result (Array Std.U8 32#usize) + := do + let i ← core.slice.Slice.len dk + let i1 ← parameters.BYTES_PER_RING_ELEMENT + let i2 ← RANK * i1 + massert (i = i2) + let i3 ← core.slice.Slice.len ciphertext + let i4 ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i5 ← i4 * params.du + let i6 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i7 ← i5 + i6 + let i8 ← i7 / 8#usize + massert (i3 = i8) + let secret_as_ntt ← serialize.deserialize_ring_elements_reduced RANK dk + ind_cpa.decrypt_unpacked params secret_as_ntt ciphertext + +/-- [hacspec_ml_kem::ind_cca::decaps_internal]: + Source: 'ml-kem/src/ind_cca.rs', lines 140:0-205:1 -/ +def ind_cca.decaps_internal + (RANK : Std.Usize) (EK_SIZE : Std.Usize) {DK_SIZE : Std.Usize} (DK_PKE_SIZE : + Std.Usize) (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) {CT_SIZE : Std.Usize} + (J_INPUT_SIZE : Std.Usize) (params : parameters.MlKemParams) + (dk : Array Std.U8 DK_SIZE) (c : Array Std.U8 CT_SIZE) : + Result (core.result.Result (Array Std.U8 32#usize) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * i + massert (DK_PKE_SIZE = i3) + let i4 ← DK_PKE_SIZE + EK_SIZE + let i5 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let i6 ← i5 + 32#usize + massert (DK_SIZE = i6) + let i7 ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i8 ← i7 * params.du + let i9 ← i8 / 8#usize + massert (U_SIZE = i9) + let i10 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i11 ← i10 / 8#usize + massert (V_SIZE = i11) + let i12 ← U_SIZE + V_SIZE + massert (CT_SIZE = i12) + let i13 ← 32#usize + CT_SIZE + massert (J_INPUT_SIZE = i13) + let dk_pke ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk { «end» := DK_PKE_SIZE } + let ek ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk { start := DK_PKE_SIZE, «end» := i4 } + let i14 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let h ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk { start := i4, «end» := i14 } + let i15 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let z ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) dk { start := i15 } + let s ← lift (Array.to_slice c) + let m_prime ← ind_cpa.decrypt RANK params dk_pke s + let to_hash := Array.repeat 64#usize 0#u8 + let (s1, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash { «end» := 32#usize } + let s2 ← lift (Array.to_slice m_prime) + let s3 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s1 s2 + let to_hash1 := index_mut_back s3 + let (s4, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash1 { start := 32#usize } + let s5 ← core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s4 h + let to_hash2 := index_mut_back1 s5 + let s6 ← lift (Array.to_slice to_hash2) + let hashed ← parameters.hash_functions.G s6 + let s7 ← lift (Array.to_slice hashed) + let (success_shared_secret, pseudorandomness) ← + core.slice.Slice.split_at s7 32#usize + let s8 ← + core.Slice.Insts.CoreOpsIndexIndex.index + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) pseudorandomness { «end» := 32#usize } + let r ← + core.Array.Insts.CoreConvertTryFromShared0SliceTryFromSliceError.try_from + 32#usize core.U8.Insts.CoreMarkerCopy s8 + let r_prime ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + let j_input := Array.repeat J_INPUT_SIZE 0#u8 + let (s9, index_mut_back2) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) j_input { «end» := 32#usize } + let s10 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s9 z + let j_input1 := index_mut_back2 s10 + let (s11, index_mut_back3) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) j_input1 { start := 32#usize } + let s12 ← lift (Array.to_slice c) + let s13 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s11 s12 + let j_input2 := index_mut_back3 s13 + let s14 ← lift (Array.to_slice j_input2) + let rejection_shared_secret ← parameters.hash_functions.J 32#usize s14 + let s15 ← lift (Array.to_slice r_prime) + let r1 ← ind_cpa.encrypt RANK U_SIZE V_SIZE CT_SIZE params ek m_prime s15 + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r1 + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let s16 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFull.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) c () + let s17 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFull.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) val () + let b ← + core.Slice.Insts.CoreCmpPartialEqSlice.eq + core.U8.Insts.CoreCmpPartialEqU8 s16 s17 + if b + then + let k := Array.repeat 32#usize 0#u8 + let (s18, to_slice_mut_back) ← lift (Array.to_slice_mut k) + let s19 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s18 + success_shared_secret + let k1 := to_slice_mut_back s19 + ok (core.result.Result.Ok k1) + else ok (core.result.Result.Ok rejection_shared_secret) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + (Array Std.U8 32#usize) (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + +/-- [hacspec_ml_kem::ind_cca::generate_keypair]: + Source: 'ml-kem/src/ind_cca.rs', lines 217:0-234:1 + Visibility: public -/ +def ind_cca.generate_keypair + (RANK : Std.Usize) (EK_SIZE : Std.Usize) (DK_SIZE : Std.Usize) (DK_PKE_SIZE : + Std.Usize) (params : parameters.MlKemParams) + (randomness : Array Std.U8 64#usize) : + Result (core.result.Result ((Array Std.U8 EK_SIZE) × (Array Std.U8 DK_SIZE)) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * i + massert (DK_PKE_SIZE = i3) + let i4 ← DK_PKE_SIZE + EK_SIZE + let i5 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let i6 ← i5 + 32#usize + massert (DK_SIZE = i6) + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) randomness { «end» := 32#usize } + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize s + let d ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + let s1 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) randomness { start := 32#usize } + let r1 ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize s1 + let z ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r1 + ind_cca.keygen_internal RANK EK_SIZE DK_PKE_SIZE DK_SIZE params d z + +/-- [hacspec_ml_kem::ind_cca::public_key_modulus_check]: loop body 0: + Source: 'ml-kem/src/ind_cca.rs', lines 251:4-258:5 + Visibility: public -/ +@[rust_loop_body] +def ind_cca.public_key_modulus_check_loop.body + (iter : core.slice.iter.ChunksExact Std.U8) (valid : Bool) : + Result (ControlFlow ((core.slice.iter.ChunksExact Std.U8) × Bool) Bool) + := do + let (o, iter1) ← + core.slice.iter.ChunksExact.Insts.CoreIterTraitsIteratorIteratorSharedASlice.next + iter + match o with + | core.option.Option.None => ok (done valid) + | core.option.Option.Some chunk => + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 384#usize chunk + let a ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r + let decoded ← serialize.byte_decode 3072#usize a 12#usize + let re_encoded ← + serialize.byte_encode 384#usize 3072#usize decoded 12#usize + let s ← core.array.Array.as_slice re_encoded + let b ← + core.Shared1A.Insts.CoreCmpPartialEqShared0B.ne + (core.Slice.Insts.CoreCmpPartialEqSlice + core.U8.Insts.CoreCmpPartialEqU8) chunk s + if b + then ok (cont (iter1, false)) + else ok (cont (iter1, valid)) + +/-- [hacspec_ml_kem::ind_cca::public_key_modulus_check]: loop 0: + Source: 'ml-kem/src/ind_cca.rs', lines 251:4-258:5 + Visibility: public -/ +@[rust_loop] +def ind_cca.public_key_modulus_check_loop + (iter : core.slice.iter.ChunksExact Std.U8) (valid : Bool) : + Result Bool + := do + loop + (fun (iter1, valid1) => ind_cca.public_key_modulus_check_loop.body iter1 + valid1) + (iter, valid) + +/-- [hacspec_ml_kem::ind_cca::public_key_modulus_check]: + Source: 'ml-kem/src/ind_cca.rs', lines 243:0-260:1 + Visibility: public -/ +def ind_cca.public_key_modulus_check + {EK_SIZE : Std.Usize} (params : parameters.MlKemParams) + (ek : Array Std.U8 EK_SIZE) : + Result Bool + := do + let t_size ← parameters.MlKemParams.t_as_ntt_encoded_size params + let encoded_ring_elements ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) ek { «end» := t_size } + let i ← parameters.BYTES_PER_RING_ELEMENT + let iter ← core.slice.Slice.chunks_exact encoded_ring_elements i + ind_cca.public_key_modulus_check_loop iter true + +/-- [hacspec_ml_kem::ind_cca::encapsulate]: + Source: 'ml-kem/src/ind_cca.rs', lines 275:0-299:1 + Visibility: public -/ +def ind_cca.encapsulate + (RANK : Std.Usize) {EK_SIZE : Std.Usize} (U_SIZE : Std.Usize) (V_SIZE : + Std.Usize) (CT_SIZE : Std.Usize) (params : parameters.MlKemParams) + (ek : Array Std.U8 EK_SIZE) (m : Array Std.U8 32#usize) : + Result (core.result.Result ((Array Std.U8 32#usize) × (Array Std.U8 + CT_SIZE)) sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i4 ← i3 * params.du + let i5 ← i4 / 8#usize + massert (U_SIZE = i5) + let i6 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i7 ← i6 / 8#usize + massert (V_SIZE = i7) + let i8 ← U_SIZE + V_SIZE + massert (CT_SIZE = i8) + let b ← ind_cca.public_key_modulus_check params ek + massert b + let s ← lift (Array.to_slice ek) + ind_cca.encaps_internal RANK U_SIZE V_SIZE CT_SIZE params s m + +/-- [hacspec_ml_kem::ind_cca::decapsulate]: + Source: 'ml-kem/src/ind_cca.rs', lines 316:0-342:1 + Visibility: public -/ +def ind_cca.decapsulate + (RANK : Std.Usize) (EK_SIZE : Std.Usize) {DK_SIZE : Std.Usize} (DK_PKE_SIZE : + Std.Usize) (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) {CT_SIZE : Std.Usize} + (J_INPUT_SIZE : Std.Usize) (params : parameters.MlKemParams) + (dk : Array Std.U8 DK_SIZE) (c : Array Std.U8 CT_SIZE) : + Result (core.result.Result (Array Std.U8 32#usize) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let i3 ← RANK * i + massert (DK_PKE_SIZE = i3) + let i4 ← DK_PKE_SIZE + EK_SIZE + let i5 ← i4 + parameters.hash_functions.H_DIGEST_SIZE + let i6 ← i5 + 32#usize + massert (DK_SIZE = i6) + let i7 ← RANK * parameters.COEFFICIENTS_IN_RING_ELEMENT + let i8 ← i7 * params.du + let i9 ← i8 / 8#usize + massert (U_SIZE = i9) + let i10 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * params.dv + let i11 ← i10 / 8#usize + massert (V_SIZE = i11) + let i12 ← U_SIZE + V_SIZE + massert (CT_SIZE = i12) + let i13 ← 32#usize + CT_SIZE + massert (J_INPUT_SIZE = i13) + ind_cca.decaps_internal RANK EK_SIZE DK_PKE_SIZE U_SIZE V_SIZE J_INPUT_SIZE + params dk c + +/-- [hacspec_ml_kem::serialize::serialize_public_key::closure] + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +def serialize.serialize_public_key.closure (RANK : Std.Usize) (EK_SIZE : + Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK × Slice Std.U8 + +/-- [hacspec_ml_kem::serialize::serialize_public_key_at]: + Source: 'ml-kem/src/serialize.rs', lines 421:0-434:1 -/ +def serialize.serialize_public_key_at + {RANK : Std.Usize} + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (seed_for_A : Slice Std.U8) (k : Std.Usize) : + Result Std.U8 + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + if k < i1 + then + let i2 ← k / i + let j ← k % i + let a ← Array.index_usize t_as_ntt i2 + let encoded ← serialize.byte_encode 384#usize 3072#usize a 12#usize + Array.index_usize encoded j + else let i2 ← RANK * i + let i3 ← k - i2 + Slice.index_usize seed_for_A i3 + +/-- [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}::call]: + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +def + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + {RANK : Std.Usize} {EK_SIZE : Std.Usize} + (c : serialize.serialize_public_key.closure RANK EK_SIZE) + (tupled_args : Std.Usize) : + Result Std.U8 + := do + let (a, s) := c + serialize.serialize_public_key_at a s tupled_args + +/-- [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}::call_mut]: + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +def + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + {RANK : Std.Usize} {EK_SIZE : Std.Usize} + (state : serialize.serialize_public_key.closure RANK EK_SIZE) + (args : Std.Usize) : + Result (Std.U8 × (serialize.serialize_public_key.closure RANK EK_SIZE)) + := do + let i ← + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call + state args + ok (i, state) + +/-- [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}::call_once]: + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +def + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once + {RANK : Std.Usize} {EK_SIZE : Std.Usize} + (c : serialize.serialize_public_key.closure RANK EK_SIZE) (i : Std.Usize) : + Result Std.U8 + := do + let (i1, _) ← + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut + c i + ok i1 + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::FnOnce<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +@[reducible] +def + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 + (RANK : Std.Usize) (EK_SIZE : Std.Usize) : core.ops.function.FnOnce + (serialize.serialize_public_key.closure RANK EK_SIZE) Std.Usize Std.U8 := { + call_once := + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::FnMut<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +@[reducible] +def + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 + (RANK : Std.Usize) (EK_SIZE : Std.Usize) : core.ops.function.FnMut + (serialize.serialize_public_key.closure RANK EK_SIZE) Std.Usize Std.U8 := { + FnOnceInst := + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeU8 + RANK EK_SIZE + call_mut := + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::serialize::serialize_public_key::{impl core::ops::function::Fn<(usize,), u8> for hacspec_ml_kem::serialize::serialize_public_key::closure<'_0, '_1, RANK, EK_SIZE>}] + Source: 'ml-kem/src/serialize.rs', lines 442:12-442:72 -/ +@[reducible] +def serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 + (RANK : Std.Usize) (EK_SIZE : Std.Usize) : core.ops.function.Fn + (serialize.serialize_public_key.closure RANK EK_SIZE) Std.Usize Std.U8 := { + FnMutInst := + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnMutTupleUsizeU8 + RANK EK_SIZE + call := + serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8.call +} + +/-- [hacspec_ml_kem::serialize::serialize_public_key]: + Source: 'ml-kem/src/serialize.rs', lines 438:0-443:1 + Visibility: public -/ +def serialize.serialize_public_key + {RANK : Std.Usize} (EK_SIZE : Std.Usize) + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (seed_for_A : Slice Std.U8) : + Result (Array Std.U8 EK_SIZE) + := do + parameters.createi EK_SIZE + (serialize.serialize_public_key.closure.Insts.CoreOpsFunctionFnTupleUsizeU8 + RANK EK_SIZE) (t_as_ntt, seed_for_A) + +/-- [hacspec_ml_kem::ind_cca::ind_cca_unpack_generate_keypair]: + Source: 'ml-kem/src/ind_cca.rs', lines 373:0-403:1 + Visibility: public -/ +def ind_cca.ind_cca_unpack_generate_keypair + (RANK : Std.Usize) (EK_SIZE : Std.Usize) (params : parameters.MlKemParams) + (randomness : Array Std.U8 64#usize) : + Result (core.result.Result ((Array (Array parameters.FieldElement 256#usize) + RANK) × (Array (Array parameters.FieldElement 256#usize) RANK) × (Array + (Array (Array parameters.FieldElement 256#usize) RANK) RANK) × (Array + Std.U8 32#usize) × (Array Std.U8 32#usize) × (Array Std.U8 32#usize)) + sampling.BadRejectionSamplingRandomnessError) + := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← RANK * i + let i2 ← i1 + 32#usize + massert (EK_SIZE = i2) + let s ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) randomness { «end» := 32#usize } + let r ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize s + let d ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug r + let s1 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) randomness { start := 32#usize } + let r1 ← + core.SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + 32#usize s1 + let z ← + core.result.Result.unwrap core.array.TryFromSliceError.Insts.CoreFmtDebug + r1 + let s2 ← lift (Array.to_slice d) + let r2 ← ind_cpa.generate_keypair_unpacked RANK params s2 + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r2 + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let (secret_as_ntt, t_as_ntt, A_as_ntt, seed_for_A) := val + let m_A ← matrix.transpose A_as_ntt + let s3 ← lift (Array.to_slice seed_for_A) + let ek ← serialize.serialize_public_key EK_SIZE t_as_ntt s3 + let s4 ← lift (Array.to_slice ek) + let public_key_hash ← parameters.hash_functions.H s4 + let implicit_rejection_value := Array.repeat 32#usize 0#u8 + let (s5, to_slice_mut_back) ← + lift (Array.to_slice_mut implicit_rejection_value) + let s6 ← lift (Array.to_slice z) + let s7 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s5 s6 + let implicit_rejection_value1 := to_slice_mut_back s7 + ok (core.result.Result.Ok (secret_as_ntt, t_as_ntt, m_A, seed_for_A, + public_key_hash, implicit_rejection_value1)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array (Array parameters.FieldElement 256#usize) RANK) × (Array (Array + parameters.FieldElement 256#usize) RANK) × (Array (Array (Array + parameters.FieldElement 256#usize) RANK) RANK) × (Array Std.U8 32#usize) + × (Array Std.U8 32#usize) × (Array Std.U8 32#usize)) + (core.convert.From.Blanket sampling.BadRejectionSamplingRandomnessError) + residual + +/-- [hacspec_ml_kem::ind_cca::ind_cca_unpack_encapsulate]: + Source: 'ml-kem/src/ind_cca.rs', lines 418:0-451:1 + Visibility: public -/ +def ind_cca.ind_cca_unpack_encapsulate + {RANK : Std.Usize} (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) (CT_SIZE : + Std.Usize) (params : parameters.MlKemParams) + (public_key_hash : Array Std.U8 32#usize) + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (m_A : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) + (randomness : Array Std.U8 32#usize) : + Result (core.result.Result ((Array Std.U8 32#usize) × (Array Std.U8 + CT_SIZE)) sampling.BadRejectionSamplingRandomnessError) + := do + let to_hash := Array.repeat 64#usize 0#u8 + let (s, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash { «end» := 32#usize } + let s1 ← lift (Array.to_slice randomness) + let s2 ← core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s s1 + let to_hash1 := index_mut_back s2 + let (s3, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash1 { start := 32#usize } + let s4 ← lift (Array.to_slice public_key_hash) + let s5 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s3 s4 + let to_hash2 := index_mut_back1 s5 + let s6 ← lift (Array.to_slice to_hash2) + let hashed ← parameters.hash_functions.G s6 + let s7 ← lift (Array.to_slice hashed) + let (shared_secret, pseudorandomness) ← + core.slice.Slice.split_at s7 32#usize + let A_as_ntt ← matrix.transpose m_A + let r ← + ind_cpa.encrypt_unpacked U_SIZE V_SIZE CT_SIZE params t_as_ntt A_as_ntt + randomness pseudorandomness + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let k := Array.repeat 32#usize 0#u8 + let (s8, to_slice_mut_back) ← lift (Array.to_slice_mut k) + let s9 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s8 + shared_secret + let k1 := to_slice_mut_back s9 + ok (core.result.Result.Ok (k1, val)) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + ((Array Std.U8 32#usize) × (Array Std.U8 CT_SIZE)) + (core.convert.From.Blanket sampling.BadRejectionSamplingRandomnessError) + residual + +/-- [hacspec_ml_kem::ind_cca::ind_cca_unpack_decapsulate]: + Source: 'ml-kem/src/ind_cca.rs', lines 468:0-517:1 + Visibility: public -/ +def ind_cca.ind_cca_unpack_decapsulate + {RANK : Std.Usize} (U_SIZE : Std.Usize) (V_SIZE : Std.Usize) {CT_SIZE : + Std.Usize} (J_INPUT_SIZE : Std.Usize) (params : parameters.MlKemParams) + (public_key_hash : Array Std.U8 32#usize) + (implicit_rejection_value : Array Std.U8 32#usize) + (ciphertext : Array Std.U8 CT_SIZE) + (secret_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (t_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) + (m_A : Array (Array (Array parameters.FieldElement 256#usize) RANK) RANK) : + Result (core.result.Result (Array Std.U8 32#usize) + sampling.BadRejectionSamplingRandomnessError) + := do + let s ← lift (Array.to_slice ciphertext) + let m_prime ← ind_cpa.decrypt_unpacked params secret_as_ntt s + let to_hash := Array.repeat 64#usize 0#u8 + let (s1, index_mut_back) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash { «end» := 32#usize } + let s2 ← lift (Array.to_slice m_prime) + let s3 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s1 s2 + let to_hash1 := index_mut_back s3 + let (s4, index_mut_back1) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) to_hash1 { start := 32#usize } + let s5 ← lift (Array.to_slice public_key_hash) + let s6 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s4 s5 + let to_hash2 := index_mut_back1 s6 + let s7 ← lift (Array.to_slice to_hash2) + let hashed ← parameters.hash_functions.G s7 + let s8 ← lift (Array.to_slice hashed) + let (success_shared_secret, pseudorandomness) ← + core.slice.Slice.split_at s8 32#usize + let j_input := Array.repeat J_INPUT_SIZE 0#u8 + let (s9, index_mut_back2) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) j_input { «end» := 32#usize } + let s10 ← lift (Array.to_slice implicit_rejection_value) + let s11 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s9 s10 + let j_input1 := index_mut_back2 s11 + let (s12, index_mut_back3) ← + core.Array.Insts.CoreOpsIndexIndexMut.index_mut + (core.Slice.Insts.CoreOpsIndexIndexMut + (core.ops.range.RangeFromUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) j_input1 { start := 32#usize } + let s13 ← lift (Array.to_slice ciphertext) + let s14 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s12 s13 + let j_input2 := index_mut_back3 s14 + let s15 ← lift (Array.to_slice j_input2) + let rejection_shared_secret ← parameters.hash_functions.J 32#usize s15 + let A_as_ntt ← matrix.transpose m_A + let r ← + ind_cpa.encrypt_unpacked U_SIZE V_SIZE CT_SIZE params t_as_ntt A_as_ntt + m_prime pseudorandomness + let cf ← core.result.Result.Insts.CoreOpsTry_traitTry.branch r + match cf with + | core.ops.control_flow.ControlFlow.Continue val => + let s16 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFull.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) ciphertext () + let s17 ← + core.Array.Insts.CoreOpsIndexIndex.index + (core.Slice.Insts.CoreOpsIndexIndex + (core.ops.range.RangeFull.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8)) val () + let b ← + core.Slice.Insts.CoreCmpPartialEqSlice.eq + core.U8.Insts.CoreCmpPartialEqU8 s16 s17 + if b + then + let k := Array.repeat 32#usize 0#u8 + let (s18, to_slice_mut_back) ← lift (Array.to_slice_mut k) + let s19 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s18 + success_shared_secret + let k1 := to_slice_mut_back s19 + ok (core.result.Result.Ok k1) + else ok (core.result.Result.Ok rejection_shared_secret) + | core.ops.control_flow.ControlFlow.Break residual => + core.result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + (Array Std.U8 32#usize) (core.convert.From.Blanket + sampling.BadRejectionSamplingRandomnessError) residual + +/-- [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure] + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +@[reducible] +def invert_ntt.vector_inverse_ntt.closure (RANK : Std.Usize) := + Array (Array parameters.FieldElement 256#usize) RANK + +/-- [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}::call]: + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + {RANK : Std.Usize} (c : invert_ntt.vector_inverse_ntt.closure RANK) + (tupled_args : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let a ← Array.index_usize c tupled_args + invert_ntt.ntt_inverse a + +/-- [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}::call_mut]: + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + {RANK : Std.Usize} (state : invert_ntt.vector_inverse_ntt.closure RANK) + (args : Std.Usize) : + Result ((Array parameters.FieldElement 256#usize) × + (invert_ntt.vector_inverse_ntt.closure RANK)) + := do + let a ← + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call + state args + ok (a, state) + +/-- [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}::call_once]: + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once + {RANK : Std.Usize} (c : invert_ntt.vector_inverse_ntt.closure RANK) + (i : Std.Usize) : + Result (Array parameters.FieldElement 256#usize) + := do + let (a, _) ← + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut + c i + ok a + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::FnOnce<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +@[reducible] +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnOnce + (invert_ntt.vector_inverse_ntt.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + call_once := + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::FnMut<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +@[reducible] +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.FnMut + (invert_ntt.vector_inverse_ntt.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnOnceInst := + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeArrayFieldElement256 + RANK + call_mut := + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::vector_inverse_ntt::{impl core::ops::function::Fn<(usize,), [hacspec_ml_kem::parameters::FieldElement; 256usize]> for hacspec_ml_kem::invert_ntt::vector_inverse_ntt::closure<'_0, RANK>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 146:12-146:45 -/ +@[reducible] +def + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + (RANK : Std.Usize) : core.ops.function.Fn + (invert_ntt.vector_inverse_ntt.closure RANK) Std.Usize (Array + parameters.FieldElement 256#usize) := { + FnMutInst := + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnMutTupleUsizeArrayFieldElement256 + RANK + call := + invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256.call +} + +/-- [hacspec_ml_kem::invert_ntt::vector_inverse_ntt]: + Source: 'ml-kem/src/invert_ntt.rs', lines 145:0-147:1 + Visibility: public -/ +def invert_ntt.vector_inverse_ntt + {RANK : Std.Usize} + (vector_as_ntt : Array (Array parameters.FieldElement 256#usize) RANK) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + parameters.createi RANK + (invert_ntt.vector_inverse_ntt.closure.Insts.CoreOpsFunctionFnTupleUsizeArrayFieldElement256 + RANK) vector_as_ntt + +/-- [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure] + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +@[reducible] +def invert_ntt.poly_barrett_reduce.closure := + Array parameters.FieldElement 256#usize + +/-- [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}::call]: + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : invert_ntt.poly_barrett_reduce.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let fe ← Array.index_usize c tupled_args + let i ← fe.val % parameters.FIELD_MODULUS + parameters.FieldElement.new i + +/-- [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}::call_mut]: + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : invert_ntt.poly_barrett_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × invert_ntt.poly_barrett_reduce.closure) + := do + let fe ← + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}::call_once]: + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : invert_ntt.poly_barrett_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +@[reducible] +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce invert_ntt.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + call_once := + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +@[reducible] +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut invert_ntt.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::invert_ntt::poly_barrett_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::invert_ntt::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/invert_ntt.rs', lines 152:12-152:59 -/ +@[reducible] +def + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn invert_ntt.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::invert_ntt::poly_barrett_reduce]: + Source: 'ml-kem/src/invert_ntt.rs', lines 151:0-153:1 + Visibility: public -/ +def invert_ntt.poly_barrett_reduce + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + invert_ntt.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + p + +/-- [hacspec_ml_kem::ntt::ZETA] + Source: 'ml-kem/src/ntt.rs', lines 6:0-6:49 -/ +@[global_simps, irreducible] +def ntt.ZETA : Result parameters.FieldElement := + parameters.FieldElement.new 17#u16 + +/-- [hacspec_ml_kem::ntt::to_unsigned_field_modulus]: + Source: 'ml-kem/src/ntt.rs', lines 14:0-16:1 + Visibility: public -/ +def ntt.to_unsigned_field_modulus + (a : parameters.FieldElement) : Result parameters.FieldElement := do + let i ← a.val % parameters.FIELD_MODULUS + parameters.FieldElement.new i + +/-- [hacspec_ml_kem::ntt::bit_rev_7]: loop body 0: + Source: 'ml-kem/src/ntt.rs', lines 21:4-25:5 -/ +@[rust_loop_body] +def ntt.bit_rev_7_loop.body + (x : Std.Usize) (iter : core.ops.range.Range Std.I32) (result : Std.Usize) : + Result (ControlFlow ((core.ops.range.Range Std.I32) × Std.Usize) Std.Usize) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.I32.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done result) + | core.option.Option.Some i => + let i1 ← x >>> i + let i2 ← lift (i1 &&& 1#usize) + if i2 = 1#usize + then + let i3 ← 6#i32 - i + let i4 ← 1#usize <<< i3 + let result1 ← lift (result ||| i4) + ok (cont (iter1, result1)) + else ok (cont (iter1, result)) + +/-- [hacspec_ml_kem::ntt::bit_rev_7]: loop 0: + Source: 'ml-kem/src/ntt.rs', lines 21:4-25:5 -/ +@[rust_loop] +def ntt.bit_rev_7_loop + (iter : core.ops.range.Range Std.I32) (x : Std.Usize) (result : Std.Usize) : + Result Std.Usize + := do + loop + (fun (iter1, result1) => ntt.bit_rev_7_loop.body x iter1 result1) + (iter, result) + +/-- [hacspec_ml_kem::ntt::bit_rev_7]: + Source: 'ml-kem/src/ntt.rs', lines 19:0-27:1 -/ +@[reducible] +def ntt.bit_rev_7 (x : Std.Usize) : Result Std.Usize := do + ntt.bit_rev_7_loop { start := 0#i32, «end» := 7#i32 } x 0#usize + +/-- [hacspec_ml_kem::ntt::get_zeta]: + Source: 'ml-kem/src/ntt.rs', lines 195:0-197:1 + Visibility: public -/ +def ntt.get_zeta (i : Std.Usize) : Result parameters.FieldElement := do + let a ← ntt.ZETAS + Array.index_usize a i + +/-- [hacspec_ml_kem::parameters::REJECTION_SAMPLING_SEED_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 20:0-20:56 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.REJECTION_SAMPLING_SEED_SIZE : Result Std.Usize := + 168#usize * 5#usize + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::ek_size]: + Source: 'ml-kem/src/parameters.rs', lines 43:4-45:5 + Visibility: public -/ +def parameters.MlKemParams.ek_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.MlKemParams.t_as_ntt_encoded_size self + i + 32#usize + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::dk_pke_size]: + Source: 'ml-kem/src/parameters.rs', lines 46:4-48:5 + Visibility: public -/ +def parameters.MlKemParams.dk_pke_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + self.rank * i + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::dk_size]: + Source: 'ml-kem/src/parameters.rs', lines 49:4-51:5 + Visibility: public -/ +def parameters.MlKemParams.dk_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.MlKemParams.dk_pke_size self + let i1 ← parameters.MlKemParams.ek_size self + let i2 ← i + i1 + let i3 ← i2 + parameters.hash_functions.H_DIGEST_SIZE + i3 + 32#usize + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::v_encoded_size]: + Source: 'ml-kem/src/parameters.rs', lines 55:4-57:5 + Visibility: public -/ +def parameters.MlKemParams.v_encoded_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.COEFFICIENTS_IN_RING_ELEMENT * self.dv + i / 8#usize + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::MlKemParams}::ciphertext_size]: + Source: 'ml-kem/src/parameters.rs', lines 58:4-60:5 + Visibility: public -/ +def parameters.MlKemParams.ciphertext_size + (self : parameters.MlKemParams) : Result Std.Usize := do + let i ← parameters.MlKemParams.u_encoded_size self + let i1 ← parameters.MlKemParams.v_encoded_size self + i + i1 + +/-- [hacspec_ml_kem::parameters::ML_KEM_512] + Source: 'ml-kem/src/parameters.rs', lines 63:0-69:2 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512 : parameters.MlKemParams := + { + rank := 2#usize, + eta1 := 3#usize, + eta2 := 2#usize, + du := 10#usize, + dv := 4#usize + } + +/-- [hacspec_ml_kem::parameters::ML_KEM_768] + Source: 'ml-kem/src/parameters.rs', lines 70:0-76:2 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768 : parameters.MlKemParams := + { + rank := 3#usize, + eta1 := 2#usize, + eta2 := 2#usize, + du := 10#usize, + dv := 4#usize + } + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024] + Source: 'ml-kem/src/parameters.rs', lines 77:0-83:2 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024 : parameters.MlKemParams := + { + rank := 4#usize, + eta1 := 2#usize, + eta2 := 2#usize, + du := 11#usize, + dv := 5#usize + } + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_EK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 86:0-86:42 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_EK_SIZE : Std.Usize := 800#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_DK_PKE_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 87:0-87:46 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_DK_PKE_SIZE : Std.Usize := 768#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_DK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 88:0-88:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_DK_SIZE : Std.Usize := 1632#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_U_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 89:0-89:41 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_U_SIZE : Std.Usize := 640#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_V_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 90:0-90:41 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_V_SIZE : Std.Usize := 128#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_CT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 91:0-91:42 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_CT_SIZE : Std.Usize := 768#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_512_J_INPUT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 92:0-92:47 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_512_J_INPUT_SIZE : Std.Usize := 800#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_EK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 95:0-95:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_EK_SIZE : Std.Usize := 1184#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_DK_PKE_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 96:0-96:47 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_DK_PKE_SIZE : Std.Usize := 1152#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_DK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 97:0-97:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_DK_SIZE : Std.Usize := 2400#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_U_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 98:0-98:41 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_U_SIZE : Std.Usize := 960#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_V_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 99:0-99:41 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_V_SIZE : Std.Usize := 128#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_CT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 100:0-100:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_CT_SIZE : Std.Usize := 1088#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_768_J_INPUT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 101:0-101:48 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_768_J_INPUT_SIZE : Std.Usize := 1120#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_EK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 104:0-104:44 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_EK_SIZE : Std.Usize := 1568#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_DK_PKE_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 105:0-105:48 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_DK_PKE_SIZE : Std.Usize := 1536#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_DK_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 106:0-106:44 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_DK_SIZE : Std.Usize := 3168#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_U_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 107:0-107:43 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_U_SIZE : Std.Usize := 1408#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_V_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 108:0-108:42 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_V_SIZE : Std.Usize := 160#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_CT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 109:0-109:44 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_CT_SIZE : Std.Usize := 1568#usize + +/-- [hacspec_ml_kem::parameters::ML_KEM_1024_J_INPUT_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 110:0-110:49 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.ML_KEM_1024_J_INPUT_SIZE : Std.Usize := 1600#usize + +/-- [hacspec_ml_kem::parameters::cpa_ciphertext_size]: + Source: 'ml-kem/src/parameters.rs', lines 120:0-128:1 + Visibility: public -/ +def parameters.cpa_ciphertext_size (rank : Std.Usize) : Result Std.Usize := do + if rank = 2#usize + then ok parameters.ML_KEM_512_CT_SIZE + else + if rank = 3#usize + then ok parameters.ML_KEM_768_CT_SIZE + else ok parameters.ML_KEM_1024_CT_SIZE + +/-- [hacspec_ml_kem::parameters::rank_to_params]: + Source: 'ml-kem/src/parameters.rs', lines 137:0-145:1 + Visibility: public -/ +def parameters.rank_to_params + (rank : Std.Usize) : Result parameters.MlKemParams := do + if rank = 2#usize + then ok parameters.ML_KEM_512 + else + if rank = 3#usize + then ok parameters.ML_KEM_768 + else ok parameters.ML_KEM_1024 + +/-- [hacspec_ml_kem::parameters::is_rank]: + Source: 'ml-kem/src/parameters.rs', lines 149:0-151:1 + Visibility: public -/ +def parameters.is_rank (rank : Std.Usize) : Result Bool := do + if rank = 2#usize + then ok true + else if rank = 3#usize + then ok true + else ok (rank = 4#usize) + +/-- [hacspec_ml_kem::parameters::SHARED_SECRET_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 158:0-158:41 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.SHARED_SECRET_SIZE : Std.Usize := 32#usize + +/-- [hacspec_ml_kem::parameters::CPA_KEY_GENERATION_SEED_SIZE] + Source: 'ml-kem/src/parameters.rs', lines 161:0-161:51 + Visibility: public -/ +@[global_simps, irreducible] +def parameters.CPA_KEY_GENERATION_SEED_SIZE : Std.Usize := 32#usize + +/-- [hacspec_ml_kem::parameters::t_as_ntt_encoded_size]: + Source: 'ml-kem/src/parameters.rs', lines 167:0-169:1 + Visibility: public -/ +def parameters.t_as_ntt_encoded_size + (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + rank * i + +/-- [hacspec_ml_kem::parameters::ranked_bytes_per_ring_element]: + Source: 'ml-kem/src/parameters.rs', lines 174:0-176:1 + Visibility: public -/ +def parameters.ranked_bytes_per_ring_element + (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + rank * i + +/-- [hacspec_ml_kem::parameters::cpa_public_key_size]: + Source: 'ml-kem/src/parameters.rs', lines 180:0-182:1 + Visibility: public -/ +def parameters.cpa_public_key_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + let i1 ← rank * i + i1 + 32#usize + +/-- [hacspec_ml_kem::parameters::cpa_private_key_size]: + Source: 'ml-kem/src/parameters.rs', lines 186:0-188:1 + Visibility: public -/ +def parameters.cpa_private_key_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.BYTES_PER_RING_ELEMENT + rank * i + +/-- [hacspec_ml_kem::parameters::cca_private_key_size]: + Source: 'ml-kem/src/parameters.rs', lines 192:0-194:1 + Visibility: public -/ +def parameters.cca_private_key_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.cpa_private_key_size rank + let i1 ← parameters.cpa_public_key_size rank + let i2 ← i + i1 + let i3 ← i2 + parameters.hash_functions.H_DIGEST_SIZE + i3 + 32#usize + +/-- [hacspec_ml_kem::parameters::vector_u_compression_factor]: + Source: 'ml-kem/src/parameters.rs', lines 198:0-204:1 + Visibility: public -/ +def parameters.vector_u_compression_factor + (rank : Std.Usize) : Result Std.Usize := do + if rank = 4#usize + then ok 11#usize + else ok 10#usize + +/-- [hacspec_ml_kem::parameters::vector_v_compression_factor]: + Source: 'ml-kem/src/parameters.rs', lines 208:0-214:1 + Visibility: public -/ +def parameters.vector_v_compression_factor + (rank : Std.Usize) : Result Std.Usize := do + if rank = 4#usize + then ok 5#usize + else ok 4#usize + +/-- [hacspec_ml_kem::parameters::c1_block_size]: + Source: 'ml-kem/src/parameters.rs', lines 218:0-220:1 + Visibility: public -/ +def parameters.c1_block_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.vector_u_compression_factor rank + let i1 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * i + i1 / 8#usize + +/-- [hacspec_ml_kem::parameters::c1_size]: + Source: 'ml-kem/src/parameters.rs', lines 224:0-226:1 + Visibility: public -/ +def parameters.c1_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.c1_block_size rank + rank * i + +/-- [hacspec_ml_kem::parameters::c2_size]: + Source: 'ml-kem/src/parameters.rs', lines 230:0-232:1 + Visibility: public -/ +def parameters.c2_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.vector_v_compression_factor rank + let i1 ← parameters.COEFFICIENTS_IN_RING_ELEMENT * i + i1 / 8#usize + +/-- [hacspec_ml_kem::parameters::eta1]: + Source: 'ml-kem/src/parameters.rs', lines 236:0-242:1 + Visibility: public -/ +def parameters.eta1 (rank : Std.Usize) : Result Std.Usize := do + if rank = 2#usize + then ok 3#usize + else ok 2#usize + +/-- [hacspec_ml_kem::parameters::eta2]: + Source: 'ml-kem/src/parameters.rs', lines 246:0-249:1 + Visibility: public -/ +def parameters.eta2 (rank : Std.Usize) : Result Std.Usize := do + ok 2#usize + +/-- [hacspec_ml_kem::parameters::eta1_randomness_size]: + Source: 'ml-kem/src/parameters.rs', lines 253:0-255:1 + Visibility: public -/ +def parameters.eta1_randomness_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.eta1 rank + 64#usize * i + +/-- [hacspec_ml_kem::parameters::eta2_randomness_size]: + Source: 'ml-kem/src/parameters.rs', lines 259:0-261:1 + Visibility: public -/ +def parameters.eta2_randomness_size (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.eta2 rank + 64#usize * i + +/-- [hacspec_ml_kem::parameters::implicit_rejection_hash_input_size]: + Source: 'ml-kem/src/parameters.rs', lines 265:0-267:1 + Visibility: public -/ +def parameters.implicit_rejection_hash_input_size + (rank : Std.Usize) : Result Std.Usize := do + let i ← parameters.cpa_ciphertext_size rank + 32#usize + i + +/-- [hacspec_ml_kem::parameters::{impl core::clone::Clone for hacspec_ml_kem::parameters::FieldElement}::clone]: + Source: 'ml-kem/src/parameters.rs', lines 303:9-303:14 + Visibility: public -/ +def parameters.FieldElement.Insts.CoreCloneClone.clone + (self : parameters.FieldElement) : Result parameters.FieldElement := do + ok self + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::clone::Clone for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:9-303:14 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreCloneClone : core.clone.Clone + parameters.FieldElement := { + clone := parameters.FieldElement.Insts.CoreCloneClone.clone +} + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::marker::Copy for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:16-303:20 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreMarkerCopy : core.marker.Copy + parameters.FieldElement := { + cloneCloneInst := parameters.FieldElement.Insts.CoreCloneClone +} + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::marker::StructuralPartialEq for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:22-303:31 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreMarkerStructuralPartialEq : + core.marker.StructuralPartialEq parameters.FieldElement := { +} + +/-- [hacspec_ml_kem::parameters::{impl core::cmp::PartialEq for hacspec_ml_kem::parameters::FieldElement}::ne]: + Source: 'ml-kem/src/parameters.rs', lines 303:22-303:31 + Visibility: public -/ +axiom parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement.ne + : parameters.FieldElement → parameters.FieldElement → Result Bool + +/-- [hacspec_ml_kem::parameters::{impl core::cmp::PartialEq for hacspec_ml_kem::parameters::FieldElement}::eq]: + Source: 'ml-kem/src/parameters.rs', lines 303:22-303:31 + Visibility: public -/ +def parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement.eq + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result Bool + := do + ok (self.val = other.val) + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::cmp::PartialEq for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:22-303:31 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement : + core.cmp.PartialEq parameters.FieldElement parameters.FieldElement := { + eq := parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement.eq +} + +/-- [hacspec_ml_kem::parameters::{impl core::cmp::Eq for hacspec_ml_kem::parameters::FieldElement}::assert_fields_are_eq]: + Source: 'ml-kem/src/parameters.rs', lines 303:33-303:35 + Visibility: public -/ +def parameters.FieldElement.Insts.CoreCmpEq.assert_fields_are_eq + (self : parameters.FieldElement) : Result Unit := do + ok () + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::cmp::Eq for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:33-303:35 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreCmpEq : core.cmp.Eq + parameters.FieldElement := { + PartialEqInst := parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement +} + +/-- [hacspec_ml_kem::parameters::{impl core::cmp::Ord for hacspec_ml_kem::parameters::FieldElement}::cmp]: + Source: 'ml-kem/src/parameters.rs', lines 303:49-303:52 + Visibility: public -/ +def parameters.FieldElement.Insts.CoreCmpOrd.cmp + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result core.cmp.Ordering + := do + core.U16.Insts.CoreCmpOrd.cmp self.val other.val + +/-- [hacspec_ml_kem::parameters::{impl core::cmp::PartialOrd for hacspec_ml_kem::parameters::FieldElement}::partial_cmp]: + Source: 'ml-kem/src/parameters.rs', lines 303:37-303:47 + Visibility: public -/ +def parameters.FieldElement.Insts.CoreCmpPartialOrdFieldElement.partial_cmp + (self : parameters.FieldElement) (other : parameters.FieldElement) : + Result (core.option.Option core.cmp.Ordering) + := do + let o ← parameters.FieldElement.Insts.CoreCmpOrd.cmp self other + ok (core.option.Option.Some o) + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::cmp::PartialOrd for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:37-303:47 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreCmpPartialOrdFieldElement : + core.cmp.PartialOrd parameters.FieldElement parameters.FieldElement := { + PartialEqInst := parameters.FieldElement.Insts.CoreCmpPartialEqFieldElement + partial_cmp := + parameters.FieldElement.Insts.CoreCmpPartialOrdFieldElement.partial_cmp +} + +/-- Trait implementation: [hacspec_ml_kem::parameters::{impl core::cmp::Ord for hacspec_ml_kem::parameters::FieldElement}] + Source: 'ml-kem/src/parameters.rs', lines 303:49-303:52 -/ +@[reducible] +def parameters.FieldElement.Insts.CoreCmpOrd : core.cmp.Ord + parameters.FieldElement := { + EqInst := parameters.FieldElement.Insts.CoreCmpEq + PartialOrdInst := parameters.FieldElement.Insts.CoreCmpPartialOrdFieldElement + cmp := parameters.FieldElement.Insts.CoreCmpOrd.cmp +} + +/-- [hacspec_ml_kem::parameters::{hacspec_ml_kem::parameters::FieldElement}::from_i16]: + Source: 'ml-kem/src/parameters.rs', lines 323:4-327:5 + Visibility: public -/ +def parameters.FieldElement.from_i16 + (v : Std.I16) : Result parameters.FieldElement := do + let q ← lift (UScalar.hcast .I32 parameters.FIELD_MODULUS) + let i ← lift (IScalar.cast .I32 v) + let i1 ← i % q + let i2 ← i1 + q + let i3 ← i2 % q + let r ← lift (IScalar.hcast .U16 i3) + parameters.FieldElement.new r + +/-- [hacspec_ml_kem::polynomial::poly_zero]: + Source: 'ml-kem/src/polynomial.rs', lines 11:0-13:1 + Visibility: public -/ +def polynomial.poly_zero + : Result (Array parameters.FieldElement 256#usize) := do + let fe ← parameters.FieldElement.new 0#u16 + ok (Array.repeat 256#usize fe) + +/-- [hacspec_ml_kem::polynomial::add_to_ring_element::closure] + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +def polynomial.add_to_ring_element.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.add_to_ring_element.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let fe1 ← Array.index_usize a1 tupled_args + let i1 ← lift (UScalar.cast .U32 fe1.val) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.add_to_ring_element.closure) (args : Std.Usize) : + Result (parameters.FieldElement × polynomial.add_to_ring_element.closure) + := do + let fe ← + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.add_to_ring_element.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +@[reducible] +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.add_to_ring_element.closure Std.Usize + parameters.FieldElement := { + call_once := + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +@[reducible] +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.add_to_ring_element.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_to_ring_element::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_to_ring_element::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 20:12-22:5 -/ +@[reducible] +def + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.add_to_ring_element.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::add_to_ring_element]: + Source: 'ml-kem/src/polynomial.rs', lines 19:0-23:1 + Visibility: public -/ +def polynomial.add_to_ring_element + (lhs : Array parameters.FieldElement 256#usize) + (rhs : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.add_to_ring_element.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (lhs, rhs) + +/-- [hacspec_ml_kem::polynomial::poly_barrett_reduce::closure] + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +@[reducible] +def polynomial.poly_barrett_reduce.closure := + Array parameters.FieldElement 256#usize + +/-- [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.poly_barrett_reduce.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let fe ← Array.index_usize c tupled_args + let i ← fe.val % parameters.FIELD_MODULUS + parameters.FieldElement.new i + +/-- [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.poly_barrett_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × polynomial.poly_barrett_reduce.closure) + := do + let fe ← + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.poly_barrett_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +@[reducible] +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + call_once := + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +@[reducible] +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::poly_barrett_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::poly_barrett_reduce::closure<'_0>}] + Source: 'ml-kem/src/polynomial.rs', lines 30:12-30:59 -/ +@[reducible] +def + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.poly_barrett_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::poly_barrett_reduce]: + Source: 'ml-kem/src/polynomial.rs', lines 29:0-31:1 + Visibility: public -/ +def polynomial.poly_barrett_reduce + (p : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.poly_barrett_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + p + +/-- [hacspec_ml_kem::polynomial::subtract_reduce::closure] + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +def polynomial.subtract_reduce.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.subtract_reduce.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let i1 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i2 ← i + i1 + let fe1 ← Array.index_usize a1 tupled_args + let i3 ← lift (UScalar.cast .U32 fe1.val) + let i4 ← i2 - i3 + let i5 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i6 ← i4 % i5 + let i7 ← lift (UScalar.cast .U16 i6) + parameters.FieldElement.new i7 + +/-- [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.subtract_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × polynomial.subtract_reduce.closure) + := do + let fe ← + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.subtract_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +@[reducible] +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.subtract_reduce.closure Std.Usize + parameters.FieldElement := { + call_once := + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +@[reducible] +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.subtract_reduce.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::subtract_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::subtract_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 35:12-40:5 -/ +@[reducible] +def + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.subtract_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::subtract_reduce]: + Source: 'ml-kem/src/polynomial.rs', lines 34:0-41:1 + Visibility: public -/ +def polynomial.subtract_reduce + (a : Array parameters.FieldElement 256#usize) + (b : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.subtract_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (a, b) + +/-- [hacspec_ml_kem::polynomial::ntt_multiply]: + Source: 'ml-kem/src/polynomial.rs', lines 46:0-48:1 + Visibility: public -/ +def polynomial.ntt_multiply + (a : Array parameters.FieldElement 256#usize) + (b : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + ntt.multiply_ntts a b + +/-- [hacspec_ml_kem::polynomial::add_message_error_reduce::closure] + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +def polynomial.add_message_error_reduce.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize × Array parameters.FieldElement 256#usize + +/-- [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.add_message_error_reduce.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1, a2) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let fe1 ← Array.index_usize a1 tupled_args + let i1 ← lift (UScalar.cast .U32 fe1.val) + let i2 ← i + i1 + let fe2 ← Array.index_usize a2 tupled_args + let i3 ← lift (UScalar.cast .U32 fe2.val) + let i4 ← i2 + i3 + let i5 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i6 ← i4 % i5 + let i7 ← lift (UScalar.cast .U16 i6) + parameters.FieldElement.new i7 + +/-- [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.add_message_error_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × + polynomial.add_message_error_reduce.closure) + := do + let fe ← + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.add_message_error_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}] + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +@[reducible] +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.add_message_error_reduce.closure + Std.Usize parameters.FieldElement := { + call_once := + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}] + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +@[reducible] +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.add_message_error_reduce.closure + Std.Usize parameters.FieldElement := { + FnOnceInst := + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_message_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_message_error_reduce::closure<'_0, '_1, '_2>}] + Source: 'ml-kem/src/polynomial.rs', lines 63:12-68:5 -/ +@[reducible] +def + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.add_message_error_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::add_message_error_reduce]: + Source: 'ml-kem/src/polynomial.rs', lines 58:0-69:1 + Visibility: public -/ +def polynomial.add_message_error_reduce + (error_2 : Array parameters.FieldElement 256#usize) + (message : Array parameters.FieldElement 256#usize) + (ntt_product : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.add_message_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (error_2, message, ntt_product) + +/-- [hacspec_ml_kem::polynomial::add_error_reduce::closure] + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +def polynomial.add_error_reduce.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.add_error_reduce.closure) (tupled_args : Std.Usize) : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let fe1 ← Array.index_usize a1 tupled_args + let i1 ← lift (UScalar.cast .U32 fe1.val) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.add_error_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × polynomial.add_error_reduce.closure) + := do + let fe ← + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.add_error_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +@[reducible] +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.add_error_reduce.closure Std.Usize + parameters.FieldElement := { + call_once := + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +@[reducible] +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.add_error_reduce.closure Std.Usize + parameters.FieldElement := { + FnOnceInst := + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 75:12-79:5 -/ +@[reducible] +def + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.add_error_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::add_error_reduce]: + Source: 'ml-kem/src/polynomial.rs', lines 74:0-80:1 + Visibility: public -/ +def polynomial.add_error_reduce + (ntt_product : Array parameters.FieldElement 256#usize) + (error : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.add_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (ntt_product, error) + +/-- [hacspec_ml_kem::polynomial::add_standard_error_reduce::closure] + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +def polynomial.add_standard_error_reduce.closure := + Array parameters.FieldElement 256#usize × Array parameters.FieldElement + 256#usize + +/-- [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}::call]: + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + (c : polynomial.add_standard_error_reduce.closure) (tupled_args : Std.Usize) + : + Result parameters.FieldElement + := do + let (a, a1) := c + let fe ← Array.index_usize a tupled_args + let i ← lift (UScalar.cast .U32 fe.val) + let fe1 ← Array.index_usize a1 tupled_args + let i1 ← lift (UScalar.cast .U32 fe1.val) + let i2 ← i + i1 + let i3 ← lift (UScalar.cast .U32 parameters.FIELD_MODULUS) + let i4 ← i2 % i3 + let i5 ← lift (UScalar.cast .U16 i4) + parameters.FieldElement.new i5 + +/-- [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}::call_mut]: + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + (state : polynomial.add_standard_error_reduce.closure) (args : Std.Usize) : + Result (parameters.FieldElement × + polynomial.add_standard_error_reduce.closure) + := do + let fe ← + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call + state args + ok (fe, state) + +/-- [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}::call_once]: + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once + (c : polynomial.add_standard_error_reduce.closure) (i : Std.Usize) : + Result parameters.FieldElement + := do + let (fe, _) ← + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut + c i + ok fe + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::FnOnce<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +@[reducible] +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + : core.ops.function.FnOnce polynomial.add_standard_error_reduce.closure + Std.Usize parameters.FieldElement := { + call_once := + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement.call_once +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::FnMut<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +@[reducible] +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + : core.ops.function.FnMut polynomial.add_standard_error_reduce.closure + Std.Usize parameters.FieldElement := { + FnOnceInst := + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnOnceTupleUsizeFieldElement + call_mut := + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement.call_mut +} + +/-- Trait implementation: [hacspec_ml_kem::polynomial::add_standard_error_reduce::{impl core::ops::function::Fn<(usize,), hacspec_ml_kem::parameters::FieldElement> for hacspec_ml_kem::polynomial::add_standard_error_reduce::closure<'_0, '_1>}] + Source: 'ml-kem/src/polynomial.rs', lines 86:12-90:5 -/ +@[reducible] +def + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + : core.ops.function.Fn polynomial.add_standard_error_reduce.closure Std.Usize + parameters.FieldElement := { + FnMutInst := + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnMutTupleUsizeFieldElement + call := + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement.call +} + +/-- [hacspec_ml_kem::polynomial::add_standard_error_reduce]: + Source: 'ml-kem/src/polynomial.rs', lines 85:0-91:1 + Visibility: public -/ +def polynomial.add_standard_error_reduce + (ntt_product : Array parameters.FieldElement 256#usize) + (error_ntt : Array parameters.FieldElement 256#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + parameters.createi 256#usize + polynomial.add_standard_error_reduce.closure.Insts.CoreOpsFunctionFnTupleUsizeFieldElement + (ntt_product, error_ntt) + +/-- [hacspec_ml_kem::sampling::rej_sample_step]: loop body 0: + Source: 'ml-kem/src/sampling.rs', lines 59:4-65:5 + Visibility: public -/ +@[rust_loop_body] +def sampling.rej_sample_step_loop.body + (decoded : Array Std.U16 16#usize) (iter : core.ops.range.Range Std.Usize) + (result : Array parameters.FieldElement 16#usize) (count : Std.Usize) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Array + parameters.FieldElement 16#usize) × Std.Usize) ((Array + parameters.FieldElement 16#usize) × Std.Usize)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done (result, count)) + | core.option.Option.Some i => + let i1 ← Array.index_usize decoded i + if i1 < parameters.FIELD_MODULUS + then + let fe ← parameters.FieldElement.new i1 + let a ← Array.update result count fe + let count1 ← count + 1#usize + ok (cont (iter1, a, count1)) + else ok (cont (iter1, result, count)) + +/-- [hacspec_ml_kem::sampling::rej_sample_step]: loop 0: + Source: 'ml-kem/src/sampling.rs', lines 59:4-65:5 + Visibility: public -/ +@[rust_loop] +def sampling.rej_sample_step_loop + (iter : core.ops.range.Range Std.Usize) (decoded : Array Std.U16 16#usize) + (result : Array parameters.FieldElement 16#usize) (count : Std.Usize) : + Result ((Array parameters.FieldElement 16#usize) × Std.Usize) + := do + loop + (fun (iter1, result1, count1) => sampling.rej_sample_step_loop.body decoded + iter1 result1 count1) + (iter, result, count) + +/-- [hacspec_ml_kem::sampling::rej_sample_step]: + Source: 'ml-kem/src/sampling.rs', lines 55:0-67:1 + Visibility: public -/ +def sampling.rej_sample_step + (bytes : Array Std.U8 24#usize) : + Result ((Array parameters.FieldElement 16#usize) × Std.Usize) + := do + let decoded ← + serialize.byte_decode_generic 2#usize 16#usize 192#usize bytes 12#usize + let fe ← parameters.FieldElement.new 0#u16 + let result := Array.repeat 16#usize fe + sampling.rej_sample_step_loop { start := 0#usize, «end» := 16#usize } + decoded result 0#usize + +/-- [hacspec_ml_kem::serialize::MAX_BYTES] + Source: 'ml-kem/src/serialize.rs', lines 5:0-5:35 + Visibility: public -/ +@[global_simps, irreducible] def serialize.MAX_BYTES : Std.Usize := 16384#usize + +/-- [hacspec_ml_kem::serialize::serialize_secret_key_into]: loop body 0: + Source: 'ml-kem/src/serialize.rs', lines 222:4-227:5 + Visibility: public -/ +@[rust_loop_body] +def serialize.serialize_secret_key_into_loop.body + {RANK : Std.Usize} (i : Std.Usize) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (iter : core.ops.range.Range Std.Usize) (out : Slice Std.U8) : + Result (ControlFlow ((core.ops.range.Range Std.Usize) × (Slice Std.U8)) + (Slice Std.U8)) + := do + let (o, iter1) ← + core.ops.range.Range.Insts.CoreIterTraitsIteratorIterator.next + core.Usize.Insts.CoreIterRangeStep iter + match o with + | core.option.Option.None => ok (done out) + | core.option.Option.Some i1 => + let a ← Array.index_usize vector i1 + let encoded ← serialize.byte_encode 384#usize 3072#usize a 12#usize + let i2 ← i1 * i + let i3 ← i1 + 1#usize + let i4 ← i3 * i + let (s, index_mut_back) ← + core.Slice.Insts.CoreOpsIndexIndexMut.index_mut + (core.ops.range.RangeUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + Std.U8) out { start := i2, «end» := i4 } + let s1 ← lift (Array.to_slice encoded) + let s2 ← + core.slice.Slice.copy_from_slice core.U8.Insts.CoreMarkerCopy s s1 + let out1 := index_mut_back s2 + ok (cont (iter1, out1)) + +/-- [hacspec_ml_kem::serialize::serialize_secret_key_into]: loop 0: + Source: 'ml-kem/src/serialize.rs', lines 222:4-227:5 + Visibility: public -/ +@[rust_loop] +def serialize.serialize_secret_key_into_loop + {RANK : Std.Usize} (i : Std.Usize) (iter : core.ops.range.Range Std.Usize) + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (out : Slice Std.U8) : + Result (Slice Std.U8) + := do + loop + (fun (iter1, out1) => serialize.serialize_secret_key_into_loop.body i + vector iter1 out1) + (iter, out) + +/-- [hacspec_ml_kem::serialize::serialize_secret_key_into]: + Source: 'ml-kem/src/serialize.rs', lines 220:0-228:1 + Visibility: public -/ +def serialize.serialize_secret_key_into + {RANK : Std.Usize} + (vector : Array (Array parameters.FieldElement 256#usize) RANK) + (out : Slice Std.U8) : + Result (Slice Std.U8) + := do + let i ← core.slice.Slice.len out + let i1 ← parameters.BYTES_PER_RING_ELEMENT + let i2 ← RANK * i1 + massert (i = i2) + serialize.serialize_secret_key_into_loop i1 + { start := 0#usize, «end» := RANK } vector out + +/-- [hacspec_ml_kem::serialize::serialize_uncompressed_ring_element]: + Source: 'ml-kem/src/serialize.rs', lines 288:0-290:1 + Visibility: public -/ +def serialize.serialize_uncompressed_ring_element + (re : Array parameters.FieldElement 256#usize) : + Result (Array Std.U8 384#usize) + := do + serialize.byte_encode 384#usize 3072#usize re 12#usize + +/-- [hacspec_ml_kem::serialize::deserialize_to_uncompressed_ring_element]: + Source: 'ml-kem/src/serialize.rs', lines 294:0-298:1 + Visibility: public -/ +def serialize.deserialize_to_uncompressed_ring_element + (serialized : Array Std.U8 384#usize) : + Result (Array parameters.FieldElement 256#usize) + := do + serialize.byte_decode 3072#usize serialized 12#usize + +/-- [hacspec_ml_kem::serialize::deserialize_then_decompress_u_then_ntt]: + Source: 'ml-kem/src/serialize.rs', lines 375:0-380:1 + Visibility: public -/ +def serialize.deserialize_then_decompress_u_then_ntt + (RANK : Std.Usize) (ciphertext : Slice Std.U8) (du : Std.Usize) : + Result (Array (Array parameters.FieldElement 256#usize) RANK) + := do + let a ← serialize.deserialize_then_decompress_u RANK ciphertext du + ntt.vector_ntt a + +end hacspec_ml_kem diff --git a/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Missing.lean b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Missing.lean new file mode 100644 index 0000000000..06be5c4228 --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/HacspecMlKem/Missing.lean @@ -0,0 +1,171 @@ +/- # Hand-written stubs for symbols the current rust-core-models doesn't define -/ +import Aeneas +import CoreModels +import HacspecSha3 + +open CoreModels Aeneas +open Aeneas.Std hiding namespace core alloc +open Result + +noncomputable section + +namespace CoreModels.core + +/-- `RangeTo Usize → RangeTo Usize` identity bridge. -/ +def cmRangeToUsizeToAeneas (r : ops.range.RangeTo Aeneas.Std.Usize) : + Aeneas.Std.core.ops.range.RangeTo Aeneas.Std.Usize := + { «end» := r.«end» } + +/-- `SliceIndex` for `RangeTo Usize` (`s[..end]`). Forwards to + Aeneas's `SliceIndexRangeToUsizeSlice.*`. -/ +@[reducible] def ops.range.RangeToUsize.Insts.CoreSliceIndexSliceIndexSliceSlice + (T : Type) : Aeneas.Std.core.slice.index.SliceIndex + (ops.range.RangeTo Aeneas.Std.Usize) (Aeneas.Std.Slice T) (Aeneas.Std.Slice T) := + { sealedInst := {} + get := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeToUsizeSlice.get (cmRangeToUsizeToAeneas r) s + get_mut := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeToUsizeSlice.get_mut (cmRangeToUsizeToAeneas r) s + get_unchecked := fun _ _ => Aeneas.Std.Result.fail Aeneas.Std.Error.undef + get_unchecked_mut := fun _ _ => Aeneas.Std.Result.fail Aeneas.Std.Error.undef + index := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeToUsizeSlice.index (cmRangeToUsizeToAeneas r) s + index_mut := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeToUsizeSlice.index_mut (cmRangeToUsizeToAeneas r) s } + +/-- `SliceIndex` for `RangeFull` (`s[..]`). The full slice is just `s`. -/ +def cmRangeFullToAeneas {T : Type} (_r : ops.range.RangeFull) + (s : Aeneas.Std.Slice T) : + Aeneas.Std.core.ops.range.Range Aeneas.Std.Usize := + { start := 0#usize, «end» := Aeneas.Std.Slice.len s } + +@[reducible] def ops.range.RangeFull.Insts.CoreSliceIndexSliceIndexSliceSlice + (T : Type) : Aeneas.Std.core.slice.index.SliceIndex + ops.range.RangeFull (Aeneas.Std.Slice T) (Aeneas.Std.Slice T) := + { sealedInst := {} + get := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeUsizeSlice.get (cmRangeFullToAeneas r s) s + get_mut := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeUsizeSlice.get_mut (cmRangeFullToAeneas r s) s + get_unchecked := fun _ _ => Aeneas.Std.Result.fail Aeneas.Std.Error.undef + get_unchecked_mut := fun _ _ => Aeneas.Std.Result.fail Aeneas.Std.Error.undef + index := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeUsizeSlice.index (cmRangeFullToAeneas r s) s + index_mut := fun r s => + Aeneas.Std.core.slice.index.SliceIndexRangeUsizeSlice.index_mut (cmRangeFullToAeneas r s) s } + +/-- `TryFrom<&[T]>` for `[T; N]`. -/ +def SharedAArray.Insts.CoreConvertTryFromSharedASliceTryFromSliceError.try_from + {T : Type} (N : Aeneas.Std.Usize) (s : Aeneas.Std.Slice T) : + Aeneas.Std.Result (result.Result (Aeneas.Std.Array T N) array.TryFromSliceError) := + if h: s.len = N then + Aeneas.Std.Result.ok (result.Result.Ok ⟨s.val, by scalar_tac⟩) + else + Aeneas.Std.Result.ok (result.Result.Err ()) + +/-- `Try::branch` for `Result` (residual = `Result`). + The `?`-operator desugar — branch is the "is this Ok or Err" split. -/ +def result.Result.Insts.CoreOpsTry_traitTry.branch + {T E : Type} (r : result.Result T E) : + Aeneas.Std.Result (ops.control_flow.ControlFlow (result.Result convert.Infallible E) T) := + match r with + | .Ok x => Aeneas.Std.Result.ok (.Continue x) + | .Err e => Aeneas.Std.Result.ok (.Break (.Err e)) + +/-- `FromResidual::from_residual` for `Result` from residual + `Result` via `F: From`. The other half of the + `?`-operator desugar — lifts a Residual back into the carrier + monad through the `From` instance. -/ +def result.Result.Insts.CoreOpsTry_traitFromResidualResultInfallibleE.from_residual + {E F : Type} (T : Type) (FromInst : convert.From F E) + (residual : result.Result convert.Infallible E) : + Aeneas.Std.Result (result.Result T F) := + match residual with + | .Err e => do + let f ← FromInst.«from» e + Aeneas.Std.Result.ok (.Err f) + | .Ok _ => Aeneas.Std.Result.fail Aeneas.Std.Error.panic -- Infallible has no inhabitants + +/-- `Slice::split_at` — routes to Aeneas's `core.slice.Slice.split_at`. -/ +def slice.Slice.split_at {T : Type} (s : Aeneas.Std.Slice T) (mid : Aeneas.Std.Usize) : + Aeneas.Std.Result (Aeneas.Std.Slice T × Aeneas.Std.Slice T) := + Aeneas.Std.core.slice.Slice.split_at s mid + +/-- `PartialEq` for `Bool`. -/ +instance Bool.Insts.CoreCmpPartialEqBool : cmp.PartialEq Bool Bool := + { eq := fun x y => Aeneas.Std.Result.ok (x == y) } + +/-- `Ord` for `U16`. -/ +instance U16.Insts.CoreCmpOrd : cmp.Ord Aeneas.Std.U16 := + { EqInst := { PartialEqInst := U16.Insts.CoreCmpPartialEqU16 } + PartialOrdInst := U16.Insts.CoreCmpPartialOrdU16 + cmp := fun x y => Aeneas.Std.Result.ok + (match compare x.val y.val with + | .lt => cmp.Ordering.Less + | .eq => cmp.Ordering.Equal + | .gt => cmp.Ordering.Greater) } + +/-- `PartialEq` not-equal for `&A` vs `&B`. Forwards to the underlying + `PartialEq A B` instance. -/ +def Shared1A.Insts.CoreCmpPartialEqShared0B.ne + {A B : Type} (inst : cmp.PartialEq A B) (a : A) (b : B) : + Aeneas.Std.Result Bool := do + let eq ← inst.eq a b + Aeneas.Std.Result.ok (!eq) + +/-- `PartialEq Slice Slice` from elementwise `PartialEq T T`. -/ +def Slice.Insts.CoreCmpPartialEqSlice + {T : Type} (inst : cmp.PartialEq T T) : + cmp.PartialEq (Aeneas.Std.Slice T) (Aeneas.Std.Slice T) := + { eq := fun a0 a1 => + if a0.length = a1.length then + List.allM (fun (x, y) => inst.eq x y) (List.zip a0.val a1.val) + else .ok false } + +/-- `Array::as_slice`. Routes to Aeneas's `Array.to_slice`. -/ +def array.Array.as_slice {T : Type} {N : Aeneas.Std.Usize} + (a : Aeneas.Std.Array T N) : Aeneas.Std.Result (Aeneas.Std.Slice T) := + Aeneas.Std.Result.ok (Aeneas.Std.Array.to_slice a) + +/-- `Formatter.write_str` taking `Aeneas.Std.Str`. No-op body; the + formatter state is `Unit` so writes carry no information. -/ +def fmt.Formatter.write_str + (f : fmt.Formatter) (_ : Aeneas.Std.Str) : + Aeneas.Std.Result (result.Result Unit fmt.Error × fmt.Formatter) := + Aeneas.Std.Result.ok (.Ok (), f) + +/-- `Formatter::debug_struct_field1_finish`. No-op stub returning Ok. -/ +def fmt.Formatter.debug_struct_field1_finish + {T : Type} (f : fmt.Formatter) + (_name : Aeneas.Std.Slice Aeneas.Std.U8) + (_field : Aeneas.Std.Slice Aeneas.Std.U8) + (_value : T) : + Aeneas.Std.Result ((result.Result Unit fmt.Error) × fmt.Formatter) := + Aeneas.Std.Result.ok (result.Result.Ok (), f) + +/-- `slice.Slice.chunks_exact`. Builds the + ChunksExact iterator state from a slice and chunk size. -/ +def slice.Slice.chunks_exact {T : Type} (s : Aeneas.Std.Slice T) + (chunk_size : Aeneas.Std.Usize) : + Aeneas.Std.Result (slice.iter.ChunksExact T) := + Aeneas.Std.Result.ok { cs := chunk_size, elements := s } + +/-- `slice.iter.ChunksExact.…SharedASlice.next`. + Yields the first `cs` bytes paired with the rest, when at least `cs` + bytes remain; otherwise returns `none` (and leaves iterator state). -/ +def slice.iter.ChunksExact.Insts.CoreIterTraitsIteratorIteratorSharedASlice.next + {T : Type} (it : slice.iter.ChunksExact T) : + Aeneas.Std.Result ((option.Option (Aeneas.Std.Slice T)) × + (slice.iter.ChunksExact T)) := do + let s := it.elements + let cs := it.cs + if cs.val ≤ s.val.length then + let (s0, s1) ← Aeneas.Std.core.slice.Slice.split_at s cs + Aeneas.Std.Result.ok (option.Option.Some s0, + { cs := cs, elements := s1 }) + else + Aeneas.Std.Result.ok (option.Option.None, it) + +end CoreModels.core + +end diff --git a/specs/ml-kem/proofs/aeneas-lean/lake-manifest.json b/specs/ml-kem/proofs/aeneas-lean/lake-manifest.json new file mode 100644 index 0000000000..07c1441099 --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/lake-manifest.json @@ -0,0 +1,123 @@ +{"version": "1.2.0", + "packagesDir": ".lake/packages", + "packages": + [{"type": "path", + "scope": "", + "name": "HacspecSha3", + "manifestFile": "lake-manifest.json", + "inherited": false, + "dir": "../../../sha3/proofs/aeneas-lean", + "configFile": "lakefile.toml"}, + {"url": "https://github.com/cryspen/hax-evit", + "type": "git", + "subDir": "hax-lib/proof-libs/aeneas-lean", + "scope": "", + "rev": "ffdf432705d409b62ec025d253a340234b59766f", + "name": "Hax", + "manifestFile": "lake-manifest.json", + "inputRev": "ffdf432705d409b62ec025d253a340234b59766f", + "inherited": false, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/cryspen/aeneas", + "type": "git", + "subDir": "backends/lean", + "scope": "", + "rev": "8d2077ce4946b52d2d199fe9d2f2d520a4bf8184", + "name": "aeneas", + "manifestFile": "lake-manifest.json", + "inputRev": "8d2077ce4946b52d2d199fe9d2f2d520a4bf8184", + "inherited": true, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/mathlib4.git", + "type": "git", + "subDir": null, + "scope": "", + "rev": "5450b53e5ddc75d46418fabb605edbf36bd0beb6", + "name": "mathlib", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.30.0-rc2", + "inherited": true, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/plausible", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "86210d4ad1b08b086d0bd638637a75246523dbb8", + "name": "plausible", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/LeanSearchClient", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "c5d5b8fe6e5158def25cd28eb94e4141ad97c843", + "name": "LeanSearchClient", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/import-graph", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "cdab3938ccabbdb044be6896e251b5814bec932e", + "name": "importGraph", + "manifestFile": "lake-manifest.json", + "inputRev": "main", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/ProofWidgets4", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "2db6054a44326f8c0230ee0570e2ddb894816511", + "name": "proofwidgets", + "manifestFile": "lake-manifest.json", + "inputRev": "v0.0.98", + "inherited": true, + "configFile": "lakefile.lean"}, + {"url": "https://github.com/leanprover-community/aesop", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "f0c6e183ea26531e82773feb4b73ab6595ca17a5", + "name": "aesop", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.30.0-rc2", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/quote4", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "1cc7e819b9b9bc1e87c9edcccb62e0269e00a809", + "name": "Qq", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.30.0-rc2", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover-community/batteries", + "type": "git", + "subDir": null, + "scope": "leanprover-community", + "rev": "5c57f3857ba81924a88b2cdf4f062e34ec04ff11", + "name": "batteries", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.30.0-rc2", + "inherited": true, + "configFile": "lakefile.toml"}, + {"url": "https://github.com/leanprover/lean4-cli", + "type": "git", + "subDir": null, + "scope": "leanprover", + "rev": "13567aed1ac4f12aea9484178e07e51f8c9f7658", + "name": "Cli", + "manifestFile": "lake-manifest.json", + "inputRev": "v4.30.0-rc2", + "inherited": true, + "configFile": "lakefile.toml"}], + "name": "HacspecMlKem", + "lakeDir": ".lake", + "fixedToolchain": false} diff --git a/specs/ml-kem/proofs/aeneas-lean/lakefile.toml b/specs/ml-kem/proofs/aeneas-lean/lakefile.toml new file mode 100644 index 0000000000..b1d8232b64 --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/lakefile.toml @@ -0,0 +1,15 @@ +name = "HacspecMlKem" +version = "0.1.0" +defaultTargets = ["HacspecMlKem"] + +[[lean_lib]] +name = "HacspecMlKem" + +[[require]] +name = "Hax" +git = { url = "https://github.com/cryspen/hax-evit", subDir = "hax-lib/proof-libs/aeneas-lean" } +rev = "ffdf432705d409b62ec025d253a340234b59766f" + +[[require]] +name = "HacspecSha3" +path = "../../../sha3/proofs/aeneas-lean" diff --git a/specs/ml-kem/proofs/aeneas-lean/lean-toolchain b/specs/ml-kem/proofs/aeneas-lean/lean-toolchain new file mode 100644 index 0000000000..635bb9534e --- /dev/null +++ b/specs/ml-kem/proofs/aeneas-lean/lean-toolchain @@ -0,0 +1 @@ +leanprover/lean4:v4.30.0-rc2 \ No newline at end of file diff --git a/specs/sha3/hax_aeneas.py b/specs/sha3/hax_aeneas.py index 49caa742cb..c262d1a14c 100755 --- a/specs/sha3/hax_aeneas.py +++ b/specs/sha3/hax_aeneas.py @@ -7,7 +7,7 @@ import os -HAX_VERSION = "1f85fc13b9967080cc657863e2000ba5d4aa8647" +HAX_VERSION = "ffdf432705d409b62ec025d253a340234b59766f" AENEAS_VERSION = "8d2077c" diff --git a/specs/sha3/proofs/aeneas-lean/lake-manifest.json b/specs/sha3/proofs/aeneas-lean/lake-manifest.json index 068e9c905b..f9b7030985 100644 --- a/specs/sha3/proofs/aeneas-lean/lake-manifest.json +++ b/specs/sha3/proofs/aeneas-lean/lake-manifest.json @@ -5,10 +5,10 @@ "type": "git", "subDir": "hax-lib/proof-libs/aeneas-lean", "scope": "", - "rev": "1f85fc13b9967080cc657863e2000ba5d4aa8647", + "rev": "ffdf432705d409b62ec025d253a340234b59766f", "name": "Hax", "manifestFile": "lake-manifest.json", - "inputRev": "1f85fc13b9967080cc657863e2000ba5d4aa8647", + "inputRev": "ffdf432705d409b62ec025d253a340234b59766f", "inherited": false, "configFile": "lakefile.toml"}, {"url": "https://github.com/cryspen/rust-core-models", diff --git a/specs/sha3/proofs/aeneas-lean/lakefile.toml b/specs/sha3/proofs/aeneas-lean/lakefile.toml index cbd8b2436d..75e5d8a31f 100644 --- a/specs/sha3/proofs/aeneas-lean/lakefile.toml +++ b/specs/sha3/proofs/aeneas-lean/lakefile.toml @@ -8,4 +8,4 @@ name = "HacspecSha3" [[require]] name = "Hax" git = { url = "https://github.com/cryspen/hax-evit", subDir = "hax-lib/proof-libs/aeneas-lean" } -rev = "1f85fc13b9967080cc657863e2000ba5d4aa8647" +rev = "ffdf432705d409b62ec025d253a340234b59766f" From c6bcf1abe59cb22f3192e41d588e5b589c694775 Mon Sep 17 00:00:00 2001 From: Alexander Bentkamp Date: Tue, 23 Jun 2026 16:55:01 +0200 Subject: [PATCH 5/8] [mlkem] remove changelog entries --- CHANGELOG.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 09d5f90a84..c8b078bdb0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added - -- [#1399](https://github.com/cryspen/libcrux/pull/1399): Add a Rust spec for SHA-3 -- [#1480](https://github.com/cryspen/libcrux/pull/1480): Add a Rust spec for ML-KEM ### Fixed - (libcrux-ml-dsa) [#1398](https://github.com/cryspen/libcrux/pull/1398): Fix incorrect AVX2 use_hint implementation From 8fcb983f8a80d9c23c61a6750f33f5f4df098b67 Mon Sep 17 00:00:00 2001 From: Alexander Bentkamp Date: Tue, 23 Jun 2026 16:57:39 +0200 Subject: [PATCH 6/8] [sha3] update Readme to new hax-evit version --- specs/sha3/Readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/specs/sha3/Readme.md b/specs/sha3/Readme.md index 0d3497d812..6d115033e6 100644 --- a/specs/sha3/Readme.md +++ b/specs/sha3/Readme.md @@ -19,7 +19,7 @@ Run `hax_fstar.sh extract` to produce the F* files, and `hax_fstar.sh prove` to Prerequisites: * Hax from `hax-evit`, commit `1f85fc1` - https://github.com/cryspen/hax-evit/tree/1f85fc13b9967080cc657863e2000ba5d4aa8647 + https://github.com/cryspen/hax-evit/tree/ffdf432705d409b62ec025d253a340234b59766f (This is a not publicly available yet.) * Aeneas `8d2077c` (https://github.com/cryspen/aeneas/releases/tag/nightly-2026.06.04) From 7c822196c3f83a7590225d0a585d309b204e7bde Mon Sep 17 00:00:00 2001 From: Alexander Bentkamp Date: Tue, 23 Jun 2026 17:16:04 +0200 Subject: [PATCH 7/8] [mlkem] Add ML-KEM Readme --- specs/ml-kem/Readme.md | 24 ++++++++++++++++++++++++ specs/sha3/Readme.md | 7 ++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 specs/ml-kem/Readme.md diff --git a/specs/ml-kem/Readme.md b/specs/ml-kem/Readme.md new file mode 100644 index 0000000000..fb14d59da0 --- /dev/null +++ b/specs/ml-kem/Readme.md @@ -0,0 +1,24 @@ +# Hacspec-style ML-KEM specification + +This is a hacspec-style Rust implementation of ML-KEM, closely following FIPS 203. Its purpose +is to serve as a reference implementation for verifying functional correctness of more efficient +implementations. + +**Do not use this implementation for other applications! Besides being slow, it may be vulnerable to side-channel attacks.** + +## Extraction via HAX + +### Lean + +Prerequisites: +* Hax from `hax-evit`, commit `1f85fc1` + https://github.com/cryspen/hax-evit/tree/ffdf432705d409b62ec025d253a340234b59766f + (This is a not publicly available yet.) +* Aeneas `8d2077c` + (https://github.com/cryspen/aeneas/releases/tag/nightly-2026.06.04) + +Run `hax_aeneas.py` to extract. +Depending on the aeneas binary you have, you may have to run +`SKIP_VERSION_CHECK=1 hax_aeneas.py` instead. +Run `cd proofs/aeneas-lean && lake update && lake build` to +type-check. \ No newline at end of file diff --git a/specs/sha3/Readme.md b/specs/sha3/Readme.md index 6d115033e6..acb9742118 100644 --- a/specs/sha3/Readme.md +++ b/specs/sha3/Readme.md @@ -4,6 +4,8 @@ This is a hacspec-style Rust implementation of SHA-3, closely following FIPS-202 is to serve as a reference implementation for verifying functional correctness of more efficient implementations. +**Do not use this implementation for other applications! Besides being slow, it may be vulnerable to side-channel attacks.** + ## Extraction via HAX ### F* @@ -24,5 +26,8 @@ Prerequisites: * Aeneas `8d2077c` (https://github.com/cryspen/aeneas/releases/tag/nightly-2026.06.04) -Run `hax_aeneas.py` to extract. Run `cd proofs/aeneas-lean && lake update && lake build` to +Run `hax_aeneas.py` to extract. +Depending on the aeneas binary you have, you may have to run +`SKIP_VERSION_CHECK=1 hax_aeneas.py` instead. +Run `cd proofs/aeneas-lean && lake update && lake build` to type-check. \ No newline at end of file From 6330a8cfdf229064d1f6d840579d25c3be80f8b3 Mon Sep 17 00:00:00 2001 From: Alexander Bentkamp Date: Tue, 23 Jun 2026 17:22:07 +0200 Subject: [PATCH 8/8] [mlkem] fix cargo fmt --- crates/sys/platform/src/x86.rs | 4 ++-- libcrux-ml-kem/tests/cross_spec.rs | 31 ++++++++++++++++++------------ specs/ml-kem/src/ind_cpa.rs | 12 +++--------- specs/ml-kem/src/serialize.rs | 10 ++-------- 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/crates/sys/platform/src/x86.rs b/crates/sys/platform/src/x86.rs index 0e5a0d2f5c..2af526d306 100644 --- a/crates/sys/platform/src/x86.rs +++ b/crates/sys/platform/src/x86.rs @@ -3,9 +3,9 @@ #![allow(non_upper_case_globals)] #[cfg(target_arch = "x86")] -use core::arch::x86::{__cpuid, __cpuid_count, CpuidResult}; +use core::arch::x86::{CpuidResult, __cpuid, __cpuid_count}; #[cfg(target_arch = "x86_64")] -use core::arch::x86_64::{__cpuid, __cpuid_count, CpuidResult}; +use core::arch::x86_64::{CpuidResult, __cpuid, __cpuid_count}; use core::sync::atomic::{AtomicBool, Ordering}; #[allow(non_camel_case_types)] diff --git a/libcrux-ml-kem/tests/cross_spec.rs b/libcrux-ml-kem/tests/cross_spec.rs index e2081b425a..9e582f0d55 100644 --- a/libcrux-ml-kem/tests/cross_spec.rs +++ b/libcrux-ml-kem/tests/cross_spec.rs @@ -187,18 +187,20 @@ macro_rules! cross_spec_tests { spec::generate_keypair::<$k, $ek, $dk, $dk_pke>(&$params, randomness) .unwrap_or_else(|_| { panic!( - concat!(stringify!($mod_name), " spec keygen failed for seed idx {}"), + concat!( + stringify!($mod_name), + " spec keygen failed for seed idx {}" + ), i ) }); let (spec_ss, spec_ct) = spec::encapsulate::<$k, $ek, $u, $v, $ct>(&$params, &spec_ek, &encaps_rand) .expect("spec encaps failed"); - let spec_ss_d = - spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( - &$params, &spec_dk, &spec_ct, - ) - .expect("spec decaps failed"); + let spec_ss_d = spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( + &$params, &spec_dk, &spec_ct, + ) + .expect("spec decaps failed"); let kp = impl_mod::generate_key_pair(*randomness); let (impl_ct, impl_ss_e) = impl_mod::encapsulate(kp.public_key(), encaps_rand); @@ -207,7 +209,11 @@ macro_rules! cross_spec_tests { assert_eq!(impl_ct.as_ref(), &spec_ct[..], "ct (seed idx={})", i); assert_eq!(&impl_ss_e[..], &spec_ss[..], "encaps ss"); assert_eq!(&impl_ss_d[..], &spec_ss_d[..], "decaps ss"); - assert_eq!(&impl_ss_e[..], &impl_ss_d[..], "impl encaps/decaps ss differ"); + assert_eq!( + &impl_ss_e[..], + &impl_ss_d[..], + "impl encaps/decaps ss differ" + ); } } @@ -232,11 +238,12 @@ macro_rules! cross_spec_tests { let mut tampered_ct: [u8; $ct] = spec_ct; tampered_ct[7] ^= 0xA5; - let spec_ss_rej = - spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( - &$params, &spec_dk, &tampered_ct, - ) - .expect("spec decaps (rejection branch) failed"); + let spec_ss_rej = spec::decapsulate::<$k, $ek, $dk, $dk_pke, $u, $v, $ct, $j>( + &$params, + &spec_dk, + &tampered_ct, + ) + .expect("spec decaps (rejection branch) failed"); let impl_tampered_ct = tampered_ct.into(); let impl_ss_rej = impl_mod::decapsulate(kp.private_key(), &impl_tampered_ct); diff --git a/specs/ml-kem/src/ind_cpa.rs b/specs/ml-kem/src/ind_cpa.rs index 6640dc2f20..20265b82da 100644 --- a/specs/ml-kem/src/ind_cpa.rs +++ b/specs/ml-kem/src/ind_cpa.rs @@ -55,10 +55,8 @@ pub fn sample_vector_cbd( domain_separator: u8, ) -> Vector { createi(|i| { - let prf_input: [u8; 33] = concat_byte::<32, 33>( - seed.try_into().unwrap(), - domain_separator + i as u8, - ); + let prf_input: [u8; 33] = + concat_byte::<32, 33>(seed.try_into().unwrap(), domain_separator + i as u8); sample_secret(eta, &prf_input) }) } @@ -272,11 +270,7 @@ pub fn encrypt< let A_as_ntt: Matrix = sample_matrix_A(seed_for_A, false)?; encrypt_unpacked::( - params, - &t_as_ntt, - &A_as_ntt, - message, - randomness, + params, &t_as_ntt, &A_as_ntt, message, randomness, ) } diff --git a/specs/ml-kem/src/serialize.rs b/specs/ml-kem/src/serialize.rs index a769af6c07..34fa940abc 100644 --- a/specs/ml-kem/src/serialize.rs +++ b/specs/ml-kem/src/serialize.rs @@ -152,11 +152,7 @@ pub fn byte_encode(p: Polynomial, d: usize) /// hoisted to dodge Aeneas issue /// https://github.com/AeneasVerif/aeneas/issues/924. #[allow(non_upper_case_globals)] -fn bitvector_to_bounded_ints_at( - input: &BitVector, - d: usize, - i: usize, -) -> u16 { +fn bitvector_to_bounded_ints_at(input: &BitVector, d: usize, i: usize) -> u16 { let mut coefficient: u16 = 0; for j in 0..d { // Loop invariant: coefficient holds the value assembled @@ -222,8 +218,7 @@ pub fn serialize_secret_key_into(vector: &Vector, out: for i in 0..RANK { hax_lib::loop_invariant!(|_i: usize| out.len() == RANK * BYTES_PER_RING_ELEMENT); let encoded = byte_encode::<{ 32 * 12 }, { 256 * 12 }>(vector[i], 12); - out[i * BYTES_PER_RING_ELEMENT..(i + 1) * BYTES_PER_RING_ELEMENT] - .copy_from_slice(&encoded); + out[i * BYTES_PER_RING_ELEMENT..(i + 1) * BYTES_PER_RING_ELEMENT].copy_from_slice(&encoded); } } @@ -442,7 +437,6 @@ pub fn serialize_public_key( createi(|k| serialize_public_key_at::(t_as_ntt, seed_for_A, k)) } - #[cfg(test)] mod tests { use proptest::prelude::*;