Coverage Report

Created: 2026-07-25 06:31

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/fips203/src/lib.rs
Line
Count
Source
1
#![no_std]
2
#![deny(clippy::pedantic, warnings, missing_docs, unsafe_code)]
3
// Most of the 'allow' category...
4
#![deny(absolute_paths_not_starting_with_crate, dead_code)]
5
#![deny(elided_lifetimes_in_paths, explicit_outlives_requirements, keyword_idents)]
6
#![deny(let_underscore_drop, macro_use_extern_crate, meta_variable_misuse, missing_abi)]
7
#![deny(non_ascii_idents, rust_2021_incompatible_closure_captures)]
8
#![deny(rust_2021_incompatible_or_patterns, rust_2021_prefixes_incompatible_syntax)]
9
#![deny(rust_2021_prelude_collisions, single_use_lifetimes, trivial_casts)]
10
#![deny(trivial_numeric_casts, unreachable_pub, unsafe_op_in_unsafe_fn, unstable_features)]
11
#![deny(unused_extern_crates, unused_import_braces, unused_lifetimes, unused_macro_rules)]
12
#![deny(unused_qualifications, unused_results, variant_size_differences)]
13
//
14
#![doc = include_str!("../README.md")]
15
16
// Implements FIPS 203 Module-Lattice-based Key-Encapsulation Mechanism Standard.
17
// See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf>
18
19
// TODO Roadmap
20
//   1. Expand test coverage, looping test w/ check, add report badge
21
//   2. Perf: optimize/minimize modular reductions, minimize u16 arith, consider avx2/aarch64
22
//      (currently, code is 'optimized' for safety and change-support, with reasonable perf)
23
//   3. Slightly more intelligent fuzzing (e.g., as dk contains h(ek))
24
25
// Functionality map per FIPS 203
26
//
27
// Algorithm 1 ForExample()                                 --> example only  (byte_fns.rs)
28
// Algorithm 2 SHAKE128example(str1, …, strπ‘š, 𝑏_1, …, 𝑏_β„“)  --> example only  (byte_fns.rs)
29
// Algorithm 3 BitsToBytes(b) on page 20                    --> optimized out (byte_fns.rs)
30
// Algorithm 4 BytesToBits(B) on page 20                    --> optimized out (byte_fns.rs)
31
// Algorithm 5 ByteEncode_d(F) on page 22                   --> byte_fns.rs
32
// Algorithm 6 ByteDecode_d(B) on page 22                   --> byte_fns.rs
33
// Algorithm 7 SampleNTT(B) on page 23                      --> sampling.rs
34
// Algorithm 8 SamplePolyCBD_Ξ·(B) on page 23                --> sampling.rs
35
// Algorithm 9 NTT(f) on page 26                            --> ntt.rs
36
// Algorithm 10 NTTβˆ’1(fΛ†) on page 26                        --> ntt.rs
37
// Algorithm 11 MultiplyNTTs(fΛ†,ĝ) on page 27               --> ntt.rs
38
// Algorithm 12 BaseCaseMultiply(a0,a1,b0,b1,Ξ³) on page 27  --> ntt.rs
39
// Algorithm 13 K-PKE.KeyGen() on page 29                   --> k_pke.rs
40
// Algorithm 14 K-PKE.Encrypt(ek_PKE,m,r) on page 30        --> k_pke.rs
41
// Algorithm 15 K-PKE.Decrypt(dk_PKE,c) on page 31          --> k_pke.rs
42
// Algorithm 16 ML-KEM.KeyGen_internal(d,z) on page 32      --> ml_kem.rs
43
// Algorithm 17 ML-KEM.Encaps_internal(ek,m) on page 33     --> ml_kem.rs
44
// Algorithm 18 ML-KEM.Decaps_internal(dk,c) on page 34     --> ml_kem.rs
45
// Algorithm 19 ML-KEM.KeyGen() on page 35                  --> ml_kem.rs
46
// Algorithm 20 ML-KEM.Encaps(ek) on page 37                --> ml_kem.rs
47
// Algorithm 21 ML-KEM.Decaps(dk,c) on page 38              --> ml_kem.rs
48
// PRF and XOF on page 18/19                                --> helpers.rs
49
// Three hash functions: G, H, J on page 18/19              --> helpers.rs
50
// Compress and Decompress on page 21                       --> helpers.rs
51
//
52
// The three security parameter sets are modules in this file with injected macro code that
53
// connects them into the functionality in ml_kem.rs. Some of the 'obtuse' coding style is
54
// driven by `clippy pedantic`. This code has been confirmed as constant-time (outside of
55
// rho) via manual inspection,  ./fips203/dudect and ./fips203/ct_cm4 functionality (other
56
// than the `validate_keypair_vartime()` functions).
57
//
58
// Note that the use of generics has been constrained to storage allocation purposes,
59
// only e.g. `[0u8; EK_LEN];` (where arithmetic expressions are not allowed), while the
60
// remainder of the security parameters are generally passed as normal function parameters.
61
//
62
// The ensure!() instances are for validation purposes and cannot be turned off. The
63
// debug_assert!() instances are (effectively) targeted by the fuzzer in /fips203/fuzz and
64
// will support quicker future changes/fixes from any FIPS 203 specification update.
65
66
67
/// These `rand_core` types are re-exported so that users of fips203 do not
68
/// have to worry about using the exactly correct version of `rand_core`.
69
pub use rand_core::{CryptoRng, Error as RngError, RngCore};
70
71
use crate::traits::SerDes;
72
use subtle::ConstantTimeEq;
73
use zeroize::{Zeroize, ZeroizeOnDrop};
74
75
mod byte_fns;
76
mod helpers;
77
mod k_pke;
78
mod ml_kem;
79
mod ntt;
80
mod sampling;
81
mod types;
82
83
/// All functionality is covered by traits, such that consumers can utilize trait objects if desired.
84
pub mod traits;
85
86
// Relevant to all parameter sets
87
const Q: u16 = 3329;
88
const ZETA: u16 = 17;
89
90
91
/// Shared Secret Key length for all ML-KEM variants (in bytes)
92
pub const SSK_LEN: usize = 32;
93
94
/// The (opaque) secret key that can be de/serialized by each party.
95
#[derive(Clone, Debug, Zeroize, ZeroizeOnDrop)]
96
pub struct SharedSecretKey([u8; SSK_LEN]);
97
98
99
impl SerDes for SharedSecretKey {
100
    type ByteArray = [u8; SSK_LEN];
101
102
843
    fn into_bytes(self) -> Self::ByteArray { self.0 }
103
104
    // While this function never fails for `SharedSecretKey`, it includes the `try_` prefix
105
    // to maintain alignment with the SerDes trait (alongside all the other objects) and to
106
    // retains the opportunity for future validation.
107
843
    fn try_from_bytes(ssk: Self::ByteArray) -> Result<Self, &'static str> {
108
843
        Ok(SharedSecretKey(ssk))
109
843
    }
110
}
111
112
113
// Conservative constant-time support
114
impl PartialEq for SharedSecretKey {
115
732
    fn eq(&self, other: &Self) -> bool { bool::from(self.0.ct_eq(&other.0)) }
116
}
117
118
119
// This common functionality is injected into each parameter set module
120
macro_rules! functionality {
121
    () => {
122
        use crate::byte_fns::byte_decode;
123
        use crate::helpers::{ensure, h};
124
        use crate::ml_kem::{
125
            ml_kem_decaps, ml_kem_encaps, ml_kem_key_gen, ml_kem_key_gen_internal,
126
        };
127
        use crate::traits::{Decaps, Encaps, KeyGen, SerDes};
128
        use crate::SharedSecretKey;
129
        use rand_core::CryptoRngCore;
130
131
132
        /// Correctly sized encapsulation key specific to the target security parameter set.
133
        pub type EncapsKey = crate::types::EncapsKey<EK_LEN>;
134
135
        /// Correctly sized decapsulation key specific to the target security parameter set.
136
        pub type DecapsKey = crate::types::DecapsKey<DK_LEN>;
137
138
        /// Correctly sized ciphertext specific to the target security parameter set.
139
        pub type CipherText = crate::types::CipherText<CT_LEN>;
140
141
        /// Supports the `KeyGen` trait, allowing for keypair generation
142
        pub struct KG();
143
144
145
        impl KeyGen for KG {
146
            type DecapsByteArray = [u8; DK_LEN];
147
            type DecapsKey = DecapsKey;
148
            type EncapsByteArray = [u8; EK_LEN];
149
            type EncapsKey = EncapsKey;
150
151
732
            fn try_keygen_with_rng(
152
732
                rng: &mut impl CryptoRngCore,
153
732
            ) -> Result<(EncapsKey, DecapsKey), &'static str> {
154
732
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
155
732
                ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?;
156
732
                Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk }))
157
732
            }
<fips203::ml_kem_512::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
151
244
            fn try_keygen_with_rng(
152
244
                rng: &mut impl CryptoRngCore,
153
244
            ) -> Result<(EncapsKey, DecapsKey), &'static str> {
154
244
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
155
244
                ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?;
156
244
                Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk }))
157
244
            }
<fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<rand_core::os::OsRng>
Line
Count
Source
151
244
            fn try_keygen_with_rng(
152
244
                rng: &mut impl CryptoRngCore,
153
244
            ) -> Result<(EncapsKey, DecapsKey), &'static str> {
154
244
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
155
244
                ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?;
156
244
                Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk }))
157
244
            }
<fips203::ml_kem_768::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
151
244
            fn try_keygen_with_rng(
152
244
                rng: &mut impl CryptoRngCore,
153
244
            ) -> Result<(EncapsKey, DecapsKey), &'static str> {
154
244
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
155
244
                ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?;
156
244
                Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk }))
157
244
            }
Unexecuted instantiation: <fips203::ml_kem_512::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<_>
Unexecuted instantiation: <fips203::ml_kem_768::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<_>
Unexecuted instantiation: <fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<_>
158
159
1.41k
            fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) {
160
1.41k
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
161
1.41k
                ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk);
162
1.41k
                (EncapsKey { 0: ek }, DecapsKey { 0: dk })
163
1.41k
            }
<fips203::ml_kem_512::KG as fips203::traits::KeyGen>::keygen_from_seed
Line
Count
Source
159
471
            fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) {
160
471
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
161
471
                ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk);
162
471
                (EncapsKey { 0: ek }, DecapsKey { 0: dk })
163
471
            }
<fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::keygen_from_seed
Line
Count
Source
159
471
            fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) {
160
471
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
161
471
                ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk);
162
471
                (EncapsKey { 0: ek }, DecapsKey { 0: dk })
163
471
            }
<fips203::ml_kem_768::KG as fips203::traits::KeyGen>::keygen_from_seed
Line
Count
Source
159
471
            fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) {
160
471
                let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]);
161
471
                ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk);
162
471
                (EncapsKey { 0: ek }, DecapsKey { 0: dk })
163
471
            }
164
165
732
            fn validate_keypair_with_rng_vartime(
166
732
                rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray,
167
732
                dk: &Self::DecapsByteArray,
168
732
            ) -> bool {
169
                // Note that size is checked by only accepting a ref to a correctly sized byte array
170
732
                let len_ek_pke = 384 * K + 32;
171
732
                let len_dk_pke = 384 * K;
172
                // 1. dk should contain ek
173
732
                if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) {
174
0
                    return false;
175
732
                };
176
                // 2. dk should contain hash of ek
177
732
                if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) {
178
0
                    return false;
179
732
                };
180
                // 3. ek and dk should deserialize ok
181
732
                let ek = EncapsKey::try_from_bytes(*ek);
182
732
                let dk = DecapsKey::try_from_bytes(*dk);
183
732
                if ek.is_err() || dk.is_err() {
184
0
                    return false;
185
732
                };
186
                // 4. encaps should run without a problem
187
732
                let ek_res = ek.unwrap().try_encaps_with_rng(rng);
188
732
                if ek_res.is_err() {
189
0
                    return false;
190
732
                };
191
                // 5. decaps should run without a problem
192
732
                let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1);
193
732
                if dk_res.is_err() {
194
0
                    return false;
195
732
                };
196
                // 6. encaps and decaps should produce the same shared secret
197
732
                return ek_res.unwrap().0 == dk_res.unwrap();
198
732
            }
<fips203::ml_kem_512::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
165
244
            fn validate_keypair_with_rng_vartime(
166
244
                rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray,
167
244
                dk: &Self::DecapsByteArray,
168
244
            ) -> bool {
169
                // Note that size is checked by only accepting a ref to a correctly sized byte array
170
244
                let len_ek_pke = 384 * K + 32;
171
244
                let len_dk_pke = 384 * K;
172
                // 1. dk should contain ek
173
244
                if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) {
174
0
                    return false;
175
244
                };
176
                // 2. dk should contain hash of ek
177
244
                if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) {
178
0
                    return false;
179
244
                };
180
                // 3. ek and dk should deserialize ok
181
244
                let ek = EncapsKey::try_from_bytes(*ek);
182
244
                let dk = DecapsKey::try_from_bytes(*dk);
183
244
                if ek.is_err() || dk.is_err() {
184
0
                    return false;
185
244
                };
186
                // 4. encaps should run without a problem
187
244
                let ek_res = ek.unwrap().try_encaps_with_rng(rng);
188
244
                if ek_res.is_err() {
189
0
                    return false;
190
244
                };
191
                // 5. decaps should run without a problem
192
244
                let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1);
193
244
                if dk_res.is_err() {
194
0
                    return false;
195
244
                };
196
                // 6. encaps and decaps should produce the same shared secret
197
244
                return ek_res.unwrap().0 == dk_res.unwrap();
198
244
            }
<fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
165
244
            fn validate_keypair_with_rng_vartime(
166
244
                rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray,
167
244
                dk: &Self::DecapsByteArray,
168
244
            ) -> bool {
169
                // Note that size is checked by only accepting a ref to a correctly sized byte array
170
244
                let len_ek_pke = 384 * K + 32;
171
244
                let len_dk_pke = 384 * K;
172
                // 1. dk should contain ek
173
244
                if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) {
174
0
                    return false;
175
244
                };
176
                // 2. dk should contain hash of ek
177
244
                if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) {
178
0
                    return false;
179
244
                };
180
                // 3. ek and dk should deserialize ok
181
244
                let ek = EncapsKey::try_from_bytes(*ek);
182
244
                let dk = DecapsKey::try_from_bytes(*dk);
183
244
                if ek.is_err() || dk.is_err() {
184
0
                    return false;
185
244
                };
186
                // 4. encaps should run without a problem
187
244
                let ek_res = ek.unwrap().try_encaps_with_rng(rng);
188
244
                if ek_res.is_err() {
189
0
                    return false;
190
244
                };
191
                // 5. decaps should run without a problem
192
244
                let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1);
193
244
                if dk_res.is_err() {
194
0
                    return false;
195
244
                };
196
                // 6. encaps and decaps should produce the same shared secret
197
244
                return ek_res.unwrap().0 == dk_res.unwrap();
198
244
            }
<fips203::ml_kem_768::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
165
244
            fn validate_keypair_with_rng_vartime(
166
244
                rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray,
167
244
                dk: &Self::DecapsByteArray,
168
244
            ) -> bool {
169
                // Note that size is checked by only accepting a ref to a correctly sized byte array
170
244
                let len_ek_pke = 384 * K + 32;
171
244
                let len_dk_pke = 384 * K;
172
                // 1. dk should contain ek
173
244
                if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) {
174
0
                    return false;
175
244
                };
176
                // 2. dk should contain hash of ek
177
244
                if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) {
178
0
                    return false;
179
244
                };
180
                // 3. ek and dk should deserialize ok
181
244
                let ek = EncapsKey::try_from_bytes(*ek);
182
244
                let dk = DecapsKey::try_from_bytes(*dk);
183
244
                if ek.is_err() || dk.is_err() {
184
0
                    return false;
185
244
                };
186
                // 4. encaps should run without a problem
187
244
                let ek_res = ek.unwrap().try_encaps_with_rng(rng);
188
244
                if ek_res.is_err() {
189
0
                    return false;
190
244
                };
191
                // 5. decaps should run without a problem
192
244
                let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1);
193
244
                if dk_res.is_err() {
194
0
                    return false;
195
244
                };
196
                // 6. encaps and decaps should produce the same shared secret
197
244
                return ek_res.unwrap().0 == dk_res.unwrap();
198
244
            }
Unexecuted instantiation: <fips203::ml_kem_512::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<_>
Unexecuted instantiation: <fips203::ml_kem_768::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<_>
Unexecuted instantiation: <fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::validate_keypair_with_rng_vartime::<_>
199
        }
200
201
202
        impl Encaps for EncapsKey {
203
            type CipherText = CipherText;
204
            type SharedSecretKey = SharedSecretKey;
205
206
1.81k
            fn try_encaps_with_rng(
207
1.81k
                &self, rng: &mut impl CryptoRngCore,
208
1.81k
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
1.81k
                let mut ct = [0u8; CT_LEN];
210
1.81k
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
1.81k
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
1.81k
                Ok((ssk, CipherText { 0: ct }))
214
1.81k
            }
<fips203::types::EncapsKey<800> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng>
Line
Count
Source
206
281
            fn try_encaps_with_rng(
207
281
                &self, rng: &mut impl CryptoRngCore,
208
281
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
281
                let mut ct = [0u8; CT_LEN];
210
281
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
281
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
281
                Ok((ssk, CipherText { 0: ct }))
214
281
            }
<fips203::types::EncapsKey<800> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
206
244
            fn try_encaps_with_rng(
207
244
                &self, rng: &mut impl CryptoRngCore,
208
244
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
244
                let mut ct = [0u8; CT_LEN];
210
244
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
244
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
244
                Ok((ssk, CipherText { 0: ct }))
214
244
            }
<fips203::types::EncapsKey<1184> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng>
Line
Count
Source
206
281
            fn try_encaps_with_rng(
207
281
                &self, rng: &mut impl CryptoRngCore,
208
281
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
281
                let mut ct = [0u8; CT_LEN];
210
281
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
281
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
281
                Ok((ssk, CipherText { 0: ct }))
214
281
            }
<fips203::types::EncapsKey<1184> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
206
244
            fn try_encaps_with_rng(
207
244
                &self, rng: &mut impl CryptoRngCore,
208
244
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
244
                let mut ct = [0u8; CT_LEN];
210
244
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
244
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
244
                Ok((ssk, CipherText { 0: ct }))
214
244
            }
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng>
Line
Count
Source
206
281
            fn try_encaps_with_rng(
207
281
                &self, rng: &mut impl CryptoRngCore,
208
281
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
281
                let mut ct = [0u8; CT_LEN];
210
281
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
281
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
281
                Ok((ssk, CipherText { 0: ct }))
214
281
            }
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<rand_core::os::OsRng>
Line
Count
Source
206
244
            fn try_encaps_with_rng(
207
244
                &self, rng: &mut impl CryptoRngCore,
208
244
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
244
                let mut ct = [0u8; CT_LEN];
210
244
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
244
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
244
                Ok((ssk, CipherText { 0: ct }))
214
244
            }
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng>
Line
Count
Source
206
244
            fn try_encaps_with_rng(
207
244
                &self, rng: &mut impl CryptoRngCore,
208
244
            ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> {
209
244
                let mut ct = [0u8; CT_LEN];
210
244
                let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>(
211
244
                    rng, DU, DV, &self.0, &mut ct,
212
0
                )?;
213
244
                Ok((ssk, CipherText { 0: ct }))
214
244
            }
Unexecuted instantiation: <fips203::types::EncapsKey<800> as fips203::traits::Encaps>::try_encaps_with_rng::<_>
Unexecuted instantiation: <fips203::types::EncapsKey<1184> as fips203::traits::Encaps>::try_encaps_with_rng::<_>
Unexecuted instantiation: <fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<_>
215
        }
216
217
218
        impl Decaps for DecapsKey {
219
            type CipherText = CipherText;
220
            type SharedSecretKey = SharedSecretKey;
221
222
1.53k
            fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
223
1.53k
                let ssk = ml_kem_decaps::<
224
1.53k
                    K,
225
1.53k
                    { ETA1 as usize * 64 },
226
1.53k
                    { ETA2 as usize * 64 },
227
1.53k
                    { 32 + 32 * (DU as usize * K + DV as usize) },
228
1.53k
                    CT_LEN,
229
1.53k
                >(DU, DV, &self.0, &ct.0);
230
1.53k
                ssk
231
1.53k
            }
<fips203::types::DecapsKey<1632> as fips203::traits::Decaps>::try_decaps
Line
Count
Source
222
525
            fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
223
525
                let ssk = ml_kem_decaps::<
224
525
                    K,
225
525
                    { ETA1 as usize * 64 },
226
525
                    { ETA2 as usize * 64 },
227
525
                    { 32 + 32 * (DU as usize * K + DV as usize) },
228
525
                    CT_LEN,
229
525
                >(DU, DV, &self.0, &ct.0);
230
525
                ssk
231
525
            }
<fips203::types::DecapsKey<2400> as fips203::traits::Decaps>::try_decaps
Line
Count
Source
222
514
            fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
223
514
                let ssk = ml_kem_decaps::<
224
514
                    K,
225
514
                    { ETA1 as usize * 64 },
226
514
                    { ETA2 as usize * 64 },
227
514
                    { 32 + 32 * (DU as usize * K + DV as usize) },
228
514
                    CT_LEN,
229
514
                >(DU, DV, &self.0, &ct.0);
230
514
                ssk
231
514
            }
<fips203::types::DecapsKey<3168> as fips203::traits::Decaps>::try_decaps
Line
Count
Source
222
500
            fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> {
223
500
                let ssk = ml_kem_decaps::<
224
500
                    K,
225
500
                    { ETA1 as usize * 64 },
226
500
                    { ETA2 as usize * 64 },
227
500
                    { 32 + 32 * (DU as usize * K + DV as usize) },
228
500
                    CT_LEN,
229
500
                >(DU, DV, &self.0, &ct.0);
230
500
                ssk
231
500
            }
232
        }
233
234
235
        impl SerDes for EncapsKey {
236
            type ByteArray = [u8; EK_LEN];
237
238
2.08k
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::EncapsKey<800> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
238
715
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::EncapsKey<1184> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
238
690
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::EncapsKey<1568> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
238
682
            fn into_bytes(self) -> Self::ByteArray { self.0 }
239
240
3.94k
            fn try_from_bytes(ek: Self::ByteArray) -> Result<Self, &'static str> {
241
                // Validation per pg 36 #2 "This check ensures that the integers encoded
242
                // in the public key are in the valid range [0, π‘ž βˆ’ 1]". Note that
243
                // accepting a byte array of fixed size, rather than a slice of varied
244
                // size, addresses check #1.
245
15.5k
                for i in 0..K {
246
11.6k
                    let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?;
247
                }
248
3.90k
                Ok(EncapsKey { 0: ek })
249
3.94k
            }
<fips203::types::EncapsKey<800> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
240
1.39k
            fn try_from_bytes(ek: Self::ByteArray) -> Result<Self, &'static str> {
241
                // Validation per pg 36 #2 "This check ensures that the integers encoded
242
                // in the public key are in the valid range [0, π‘ž βˆ’ 1]". Note that
243
                // accepting a byte array of fixed size, rather than a slice of varied
244
                // size, addresses check #1.
245
4.11k
                for i in 0..K {
246
2.75k
                    let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?;
247
                }
248
1.36k
                Ok(EncapsKey { 0: ek })
249
1.39k
            }
<fips203::types::EncapsKey<1184> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
240
1.30k
            fn try_from_bytes(ek: Self::ByteArray) -> Result<Self, &'static str> {
241
                // Validation per pg 36 #2 "This check ensures that the integers encoded
242
                // in the public key are in the valid range [0, π‘ž βˆ’ 1]". Note that
243
                // accepting a byte array of fixed size, rather than a slice of varied
244
                // size, addresses check #1.
245
5.21k
                for i in 0..K {
246
3.91k
                    let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?;
247
                }
248
1.29k
                Ok(EncapsKey { 0: ek })
249
1.30k
            }
<fips203::types::EncapsKey<1568> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
240
1.24k
            fn try_from_bytes(ek: Self::ByteArray) -> Result<Self, &'static str> {
241
                // Validation per pg 36 #2 "This check ensures that the integers encoded
242
                // in the public key are in the valid range [0, π‘ž βˆ’ 1]". Note that
243
                // accepting a byte array of fixed size, rather than a slice of varied
244
                // size, addresses check #1.
245
6.22k
                for i in 0..K {
246
4.98k
                    let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?;
247
                }
248
1.24k
                Ok(EncapsKey { 0: ek })
249
1.24k
            }
250
        }
251
252
253
        impl SerDes for DecapsKey {
254
            type ByteArray = [u8; DK_LEN];
255
256
1.86k
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::DecapsKey<1632> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
256
675
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::DecapsKey<2400> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
256
618
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::DecapsKey<3168> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
256
567
            fn into_bytes(self) -> Self::ByteArray { self.0 }
257
258
1.86k
            fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
259
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
260
                // functions take only byte arrays of correct size. Nonetheless, we take the
261
                // opportunity to validate the ek and h(ek).
262
1.86k
                let len_ek_pke = 384 * K + 32;
263
1.86k
                let len_dk_pke = 384 * K;
264
1.86k
                let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN];
265
1.85k
                let _res =
266
1.86k
                    EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?;
267
1.85k
                ensure!(
268
1.85k
                    h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)],
269
143
                    "Encaps hash wrong"
270
                );
271
1.71k
                Ok(DecapsKey { 0: dk })
272
1.86k
            }
<fips203::types::DecapsKey<1632> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
258
675
            fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
259
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
260
                // functions take only byte arrays of correct size. Nonetheless, we take the
261
                // opportunity to validate the ek and h(ek).
262
675
                let len_ek_pke = 384 * K + 32;
263
675
                let len_dk_pke = 384 * K;
264
675
                let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN];
265
671
                let _res =
266
675
                    EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?;
267
671
                ensure!(
268
671
                    h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)],
269
53
                    "Encaps hash wrong"
270
                );
271
618
                Ok(DecapsKey { 0: dk })
272
675
            }
<fips203::types::DecapsKey<2400> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
258
618
            fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
259
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
260
                // functions take only byte arrays of correct size. Nonetheless, we take the
261
                // opportunity to validate the ek and h(ek).
262
618
                let len_ek_pke = 384 * K + 32;
263
618
                let len_dk_pke = 384 * K;
264
618
                let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN];
265
617
                let _res =
266
618
                    EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?;
267
617
                ensure!(
268
617
                    h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)],
269
50
                    "Encaps hash wrong"
270
                );
271
567
                Ok(DecapsKey { 0: dk })
272
618
            }
<fips203::types::DecapsKey<3168> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
258
567
            fn try_from_bytes(dk: Self::ByteArray) -> Result<Self, &'static str> {
259
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
260
                // functions take only byte arrays of correct size. Nonetheless, we take the
261
                // opportunity to validate the ek and h(ek).
262
567
                let len_ek_pke = 384 * K + 32;
263
567
                let len_dk_pke = 384 * K;
264
567
                let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN];
265
565
                let _res =
266
567
                    EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?;
267
565
                ensure!(
268
565
                    h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)],
269
40
                    "Encaps hash wrong"
270
                );
271
525
                Ok(DecapsKey { 0: dk })
272
567
            }
273
        }
274
275
276
        impl SerDes for CipherText {
277
            type ByteArray = [u8; CT_LEN];
278
279
843
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::CipherText<768> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
279
281
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::CipherText<1088> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
279
281
            fn into_bytes(self) -> Self::ByteArray { self.0 }
<fips203::types::CipherText<1568> as fips203::traits::SerDes>::into_bytes
Line
Count
Source
279
281
            fn into_bytes(self) -> Self::ByteArray { self.0 }
280
281
843
            fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
282
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
283
                // functions take only byte arrays of correct size. Nonetheless, we use a Result
284
                // here in case future opportunities for further validation arise.
285
843
                Ok(CipherText { 0: ct })
286
843
            }
<fips203::types::CipherText<768> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
281
281
            fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
282
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
283
                // functions take only byte arrays of correct size. Nonetheless, we use a Result
284
                // here in case future opportunities for further validation arise.
285
281
                Ok(CipherText { 0: ct })
286
281
            }
<fips203::types::CipherText<1088> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
281
281
            fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
282
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
283
                // functions take only byte arrays of correct size. Nonetheless, we use a Result
284
                // here in case future opportunities for further validation arise.
285
281
                Ok(CipherText { 0: ct })
286
281
            }
<fips203::types::CipherText<1568> as fips203::traits::SerDes>::try_from_bytes
Line
Count
Source
281
281
            fn try_from_bytes(ct: Self::ByteArray) -> Result<Self, &'static str> {
282
                // Validation per pg 31. Note that the two checks specify fixed sizes, and these
283
                // functions take only byte arrays of correct size. Nonetheless, we use a Result
284
                // here in case future opportunities for further validation arise.
285
281
                Ok(CipherText { 0: ct })
286
281
            }
287
        }
288
289
290
        #[cfg(test)]
291
        mod tests {
292
            use super::*;
293
            use crate::types::EncapsKey;
294
            use rand_chacha::rand_core::SeedableRng;
295
296
            #[test]
297
            fn smoke_test() {
298
                let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(123);
299
                let (ek, dk) = KG::keygen_from_seed([1u8; 32], [2u8; 32]);
300
                let (ssk1, ct) = ek.encaps_from_seed(&[3u8; 32]);
301
                let ssk2 = dk.try_decaps(&ct).unwrap();
302
                assert_eq!(ssk1, ssk2);
303
                for _i in 0..100 {
304
                    let (ek, dk) = KG::try_keygen_with_rng(&mut rng).unwrap();
305
                    let (ssk1, ct) = ek.try_encaps_with_rng(&mut rng).unwrap();
306
                    let ssk2 = dk.try_decaps(&ct).unwrap();
307
                    assert!(KG::validate_keypair_with_rng_vartime(
308
                        &mut rng,
309
                        &ek.clone().into_bytes(),
310
                        &dk.clone().into_bytes()
311
                    ));
312
                    assert_eq!(ssk1, ssk2);
313
                    assert_eq!(ek.clone().0, EncapsKey::try_from_bytes(ek.into_bytes()).unwrap().0);
314
                    assert_eq!(dk.clone().0, DecapsKey::try_from_bytes(dk.into_bytes()).unwrap().0);
315
                }
316
            }
317
        }
318
    };
319
}
320
321
322
#[cfg(feature = "ml-kem-512")]
323
pub mod ml_kem_512 {
324
    //! Functionality for the ML-KEM-512 security parameter set, which is claimed to be in security category 1, see
325
    //! table 2 & 3 on page 39 of spec.
326
    //!
327
    //! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf>
328
    //!
329
    //! Typical usage flow entails:
330
    //! 1. The originator runs `try_keygen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
331
    //! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
332
    //! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps()` to get the
333
    //!    shared secret key `ssk` and ciphertext `cipherText`.
334
    //! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
335
    //! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
336
    //!    runs `decapsKey.try_decaps(cipherText)` to the get shared secret ket `ssk`.
337
    //! 6. Both the originator and remote party now have the same shared secret key `ssk`.
338
    //!
339
    //! **--> See [`crate::traits`] for the keygen, encapsulation, decapsulation, and serialization/deserialization functionality.**
340
341
    const K: usize = 2;
342
    const ETA1: u32 = 3;
343
    const ETA2: u32 = 2;
344
    const DU: u32 = 10;
345
    const DV: u32 = 4;
346
347
    /// Serialized Encapsulation Key Length (in bytes)
348
    pub const EK_LEN: usize = 800;
349
    /// Serialized Decapsulation Key Length (in bytes)
350
    pub const DK_LEN: usize = 1632;
351
    /// Serialized Ciphertext Key Length (in bytes)
352
    pub const CT_LEN: usize = 768;
353
354
    functionality!();
355
}
356
357
358
#[cfg(feature = "ml-kem-768")]
359
pub mod ml_kem_768 {
360
    //! Functionality for the ML-KEM-768 security parameter set, which is claimed to be in security category 3, see
361
    //! table 2 & 3 on page 39 of spec.
362
    //!
363
    //! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf>
364
    //!
365
    //! Typical usage flow entails:
366
    //! 1. The originator runs `try_keygen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
367
    //! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
368
    //! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps()` to get the
369
    //!    shared secret key `ssk` and ciphertext `cipherText`.
370
    //! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
371
    //! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
372
    //!    runs `decapsKey.try_decaps(cipherText)` to the get shared secret ket `ssk`.
373
    //! 6. Both the originator and remote party now have the same shared secret key `ssk`.
374
    //!
375
    //! **--> See [`crate::traits`] for the keygen, encapsulation, decapsulation, and serialization/deserialization functionality.**
376
377
    const K: usize = 3;
378
    const ETA1: u32 = 2;
379
    const ETA2: u32 = 2;
380
    const DU: u32 = 10;
381
    const DV: u32 = 4;
382
383
    /// Serialized Encapsulation Key Length (in bytes)
384
    pub const EK_LEN: usize = 1184;
385
    /// Serialized Decapsulation Key Length (in bytes)
386
    pub const DK_LEN: usize = 2400;
387
    /// Serialized Ciphertext Key Length (in bytes)
388
    pub const CT_LEN: usize = 1088;
389
390
    functionality!();
391
}
392
393
#[cfg(feature = "ml-kem-1024")]
394
pub mod ml_kem_1024 {
395
    //! Functionality for the ML-KEM-1024 security parameter set, which is claimed to be in security category 5, see
396
    //! table 2 & 3 on page 39 of spec.
397
    //!
398
    //! See <https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.203.pdf>
399
    //!
400
    //! Typical usage flow entails:
401
    //! 1. The originator runs `try_keygen()` to get an encaps key `encapsKey` and decaps key `decapsKey`.
402
    //! 2. The originator serializes the encaps key via `encapsKey.into_bytes()` and sends to the remote party.
403
    //! 3. The remote party deserializes the bytes via `try_from_bytes(<bytes>)` and runs `try_encaps()` to get the
404
    //!    shared secret key `ssk` and ciphertext `cipherText`.
405
    //! 4. The remote party serializes the cipertext via `cipherText.into_bytes()` and sends to the originator.
406
    //! 5. The originator deserializes the ciphertext via `try_from_bytes(<bytes>)` then
407
    //!    runs `decapsKey.try_decaps(cipherText)` to the get shared secret ket `ssk`.
408
    //! 6. Both the originator and remote party now have the same shared secret key `ssk`.
409
    //!
410
    //! **--> See [`crate::traits`] for the keygen, encapsulation, decapsulation, and serialization/deserialization functionality.**
411
412
    const K: usize = 4;
413
    const ETA1: u32 = 2;
414
    const ETA2: u32 = 2;
415
    const DU: u32 = 11;
416
    const DV: u32 = 5;
417
418
    /// Serialized Encapsulation Key Length (in bytes)
419
    pub const EK_LEN: usize = 1568;
420
    /// Serialized Decapsulation Key Length (in bytes)
421
    pub const DK_LEN: usize = 3168;
422
    /// Serialized Ciphertext Key Length (in bytes)
423
    pub const CT_LEN: usize = 1568;
424
425
    functionality!();
426
}