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 | 837 | 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 | 837 | fn try_from_bytes(ssk: Self::ByteArray) -> Result<Self, &'static str> { |
108 | 837 | Ok(SharedSecretKey(ssk)) |
109 | 837 | } |
110 | | } |
111 | | |
112 | | |
113 | | // Conservative constant-time support |
114 | | impl PartialEq for SharedSecretKey { |
115 | 693 | 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 | 693 | fn try_keygen_with_rng( |
152 | 693 | rng: &mut impl CryptoRngCore, |
153 | 693 | ) -> Result<(EncapsKey, DecapsKey), &'static str> { |
154 | 693 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); |
155 | 693 | ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?; |
156 | 693 | Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk })) |
157 | 693 | } <fips203::ml_kem_512::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng> Line | Count | Source | 151 | 231 | fn try_keygen_with_rng( | 152 | 231 | rng: &mut impl CryptoRngCore, | 153 | 231 | ) -> Result<(EncapsKey, DecapsKey), &'static str> { | 154 | 231 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 155 | 231 | ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?; | 156 | 231 | Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk })) | 157 | 231 | } |
<fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<rand_core::os::OsRng> Line | Count | Source | 151 | 231 | fn try_keygen_with_rng( | 152 | 231 | rng: &mut impl CryptoRngCore, | 153 | 231 | ) -> Result<(EncapsKey, DecapsKey), &'static str> { | 154 | 231 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 155 | 231 | ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?; | 156 | 231 | Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk })) | 157 | 231 | } |
<fips203::ml_kem_768::KG as fips203::traits::KeyGen>::try_keygen_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng> Line | Count | Source | 151 | 231 | fn try_keygen_with_rng( | 152 | 231 | rng: &mut impl CryptoRngCore, | 153 | 231 | ) -> Result<(EncapsKey, DecapsKey), &'static str> { | 154 | 231 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 155 | 231 | ml_kem_key_gen::<K, { ETA1 as usize * 64 }>(rng, &mut ek, &mut dk)?; | 156 | 231 | Ok((EncapsKey { 0: ek }, DecapsKey { 0: dk })) | 157 | 231 | } |
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 | 470 | fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) { | 160 | 470 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 161 | 470 | ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk); | 162 | 470 | (EncapsKey { 0: ek }, DecapsKey { 0: dk }) | 163 | 470 | } |
<fips203::ml_kem_1024::KG as fips203::traits::KeyGen>::keygen_from_seed Line | Count | Source | 159 | 470 | fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) { | 160 | 470 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 161 | 470 | ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk); | 162 | 470 | (EncapsKey { 0: ek }, DecapsKey { 0: dk }) | 163 | 470 | } |
<fips203::ml_kem_768::KG as fips203::traits::KeyGen>::keygen_from_seed Line | Count | Source | 159 | 470 | fn keygen_from_seed(d: [u8; 32], z: [u8; 32]) -> (EncapsKey, DecapsKey) { | 160 | 470 | let (mut ek, mut dk) = ([0u8; EK_LEN], [0u8; DK_LEN]); | 161 | 470 | ml_kem_key_gen_internal::<K, { ETA1 as usize * 64 }>(d, z, &mut ek, &mut dk); | 162 | 470 | (EncapsKey { 0: ek }, DecapsKey { 0: dk }) | 163 | 470 | } |
|
164 | | |
165 | 693 | fn validate_keypair_with_rng_vartime( |
166 | 693 | rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray, |
167 | 693 | dk: &Self::DecapsByteArray, |
168 | 693 | ) -> bool { |
169 | | // Note that size is checked by only accepting a ref to a correctly sized byte array |
170 | 693 | let len_ek_pke = 384 * K + 32; |
171 | 693 | let len_dk_pke = 384 * K; |
172 | | // 1. dk should contain ek |
173 | 693 | if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) { |
174 | 0 | return false; |
175 | 693 | }; |
176 | | // 2. dk should contain hash of ek |
177 | 693 | if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) { |
178 | 0 | return false; |
179 | 693 | }; |
180 | | // 3. ek and dk should deserialize ok |
181 | 693 | let ek = EncapsKey::try_from_bytes(*ek); |
182 | 693 | let dk = DecapsKey::try_from_bytes(*dk); |
183 | 693 | if ek.is_err() || dk.is_err() { |
184 | 0 | return false; |
185 | 693 | }; |
186 | | // 4. encaps should run without a problem |
187 | 693 | let ek_res = ek.unwrap().try_encaps_with_rng(rng); |
188 | 693 | if ek_res.is_err() { |
189 | 0 | return false; |
190 | 693 | }; |
191 | | // 5. decaps should run without a problem |
192 | 693 | let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1); |
193 | 693 | if dk_res.is_err() { |
194 | 0 | return false; |
195 | 693 | }; |
196 | | // 6. encaps and decaps should produce the same shared secret |
197 | 693 | return ek_res.unwrap().0 == dk_res.unwrap(); |
198 | 693 | } <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 | 231 | fn validate_keypair_with_rng_vartime( | 166 | 231 | rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray, | 167 | 231 | dk: &Self::DecapsByteArray, | 168 | 231 | ) -> bool { | 169 | | // Note that size is checked by only accepting a ref to a correctly sized byte array | 170 | 231 | let len_ek_pke = 384 * K + 32; | 171 | 231 | let len_dk_pke = 384 * K; | 172 | | // 1. dk should contain ek | 173 | 231 | if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) { | 174 | 0 | return false; | 175 | 231 | }; | 176 | | // 2. dk should contain hash of ek | 177 | 231 | if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) { | 178 | 0 | return false; | 179 | 231 | }; | 180 | | // 3. ek and dk should deserialize ok | 181 | 231 | let ek = EncapsKey::try_from_bytes(*ek); | 182 | 231 | let dk = DecapsKey::try_from_bytes(*dk); | 183 | 231 | if ek.is_err() || dk.is_err() { | 184 | 0 | return false; | 185 | 231 | }; | 186 | | // 4. encaps should run without a problem | 187 | 231 | let ek_res = ek.unwrap().try_encaps_with_rng(rng); | 188 | 231 | if ek_res.is_err() { | 189 | 0 | return false; | 190 | 231 | }; | 191 | | // 5. decaps should run without a problem | 192 | 231 | let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1); | 193 | 231 | if dk_res.is_err() { | 194 | 0 | return false; | 195 | 231 | }; | 196 | | // 6. encaps and decaps should produce the same shared secret | 197 | 231 | return ek_res.unwrap().0 == dk_res.unwrap(); | 198 | 231 | } |
<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 | 231 | fn validate_keypair_with_rng_vartime( | 166 | 231 | rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray, | 167 | 231 | dk: &Self::DecapsByteArray, | 168 | 231 | ) -> bool { | 169 | | // Note that size is checked by only accepting a ref to a correctly sized byte array | 170 | 231 | let len_ek_pke = 384 * K + 32; | 171 | 231 | let len_dk_pke = 384 * K; | 172 | | // 1. dk should contain ek | 173 | 231 | if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) { | 174 | 0 | return false; | 175 | 231 | }; | 176 | | // 2. dk should contain hash of ek | 177 | 231 | if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) { | 178 | 0 | return false; | 179 | 231 | }; | 180 | | // 3. ek and dk should deserialize ok | 181 | 231 | let ek = EncapsKey::try_from_bytes(*ek); | 182 | 231 | let dk = DecapsKey::try_from_bytes(*dk); | 183 | 231 | if ek.is_err() || dk.is_err() { | 184 | 0 | return false; | 185 | 231 | }; | 186 | | // 4. encaps should run without a problem | 187 | 231 | let ek_res = ek.unwrap().try_encaps_with_rng(rng); | 188 | 231 | if ek_res.is_err() { | 189 | 0 | return false; | 190 | 231 | }; | 191 | | // 5. decaps should run without a problem | 192 | 231 | let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1); | 193 | 231 | if dk_res.is_err() { | 194 | 0 | return false; | 195 | 231 | }; | 196 | | // 6. encaps and decaps should produce the same shared secret | 197 | 231 | return ek_res.unwrap().0 == dk_res.unwrap(); | 198 | 231 | } |
<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 | 231 | fn validate_keypair_with_rng_vartime( | 166 | 231 | rng: &mut impl CryptoRngCore, ek: &Self::EncapsByteArray, | 167 | 231 | dk: &Self::DecapsByteArray, | 168 | 231 | ) -> bool { | 169 | | // Note that size is checked by only accepting a ref to a correctly sized byte array | 170 | 231 | let len_ek_pke = 384 * K + 32; | 171 | 231 | let len_dk_pke = 384 * K; | 172 | | // 1. dk should contain ek | 173 | 231 | if !(*ek == dk[len_dk_pke..(len_dk_pke + len_ek_pke)]) { | 174 | 0 | return false; | 175 | 231 | }; | 176 | | // 2. dk should contain hash of ek | 177 | 231 | if !(h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)]) { | 178 | 0 | return false; | 179 | 231 | }; | 180 | | // 3. ek and dk should deserialize ok | 181 | 231 | let ek = EncapsKey::try_from_bytes(*ek); | 182 | 231 | let dk = DecapsKey::try_from_bytes(*dk); | 183 | 231 | if ek.is_err() || dk.is_err() { | 184 | 0 | return false; | 185 | 231 | }; | 186 | | // 4. encaps should run without a problem | 187 | 231 | let ek_res = ek.unwrap().try_encaps_with_rng(rng); | 188 | 231 | if ek_res.is_err() { | 189 | 0 | return false; | 190 | 231 | }; | 191 | | // 5. decaps should run without a problem | 192 | 231 | let dk_res = dk.unwrap().try_decaps(&ek_res.as_ref().unwrap().1); | 193 | 231 | if dk_res.is_err() { | 194 | 0 | return false; | 195 | 231 | }; | 196 | | // 6. encaps and decaps should produce the same shared secret | 197 | 231 | return ek_res.unwrap().0 == dk_res.unwrap(); | 198 | 231 | } |
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.76k | fn try_encaps_with_rng( |
207 | 1.76k | &self, rng: &mut impl CryptoRngCore, |
208 | 1.76k | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { |
209 | 1.76k | let mut ct = [0u8; CT_LEN]; |
210 | 1.76k | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( |
211 | 1.76k | rng, DU, DV, &self.0, &mut ct, |
212 | 0 | )?; |
213 | 1.76k | Ok((ssk, CipherText { 0: ct })) |
214 | 1.76k | } <fips203::types::EncapsKey<800> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng> Line | Count | Source | 206 | 279 | fn try_encaps_with_rng( | 207 | 279 | &self, rng: &mut impl CryptoRngCore, | 208 | 279 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 279 | let mut ct = [0u8; CT_LEN]; | 210 | 279 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 279 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 279 | Ok((ssk, CipherText { 0: ct })) | 214 | 279 | } |
<fips203::types::EncapsKey<800> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng> Line | Count | Source | 206 | 231 | fn try_encaps_with_rng( | 207 | 231 | &self, rng: &mut impl CryptoRngCore, | 208 | 231 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 231 | let mut ct = [0u8; CT_LEN]; | 210 | 231 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 231 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 231 | Ok((ssk, CipherText { 0: ct })) | 214 | 231 | } |
<fips203::types::EncapsKey<1184> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng> Line | Count | Source | 206 | 279 | fn try_encaps_with_rng( | 207 | 279 | &self, rng: &mut impl CryptoRngCore, | 208 | 279 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 279 | let mut ct = [0u8; CT_LEN]; | 210 | 279 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 279 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 279 | Ok((ssk, CipherText { 0: ct })) | 214 | 279 | } |
<fips203::types::EncapsKey<1184> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng> Line | Count | Source | 206 | 231 | fn try_encaps_with_rng( | 207 | 231 | &self, rng: &mut impl CryptoRngCore, | 208 | 231 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 231 | let mut ct = [0u8; CT_LEN]; | 210 | 231 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 231 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 231 | Ok((ssk, CipherText { 0: ct })) | 214 | 231 | } |
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<fips203::traits::DummyRng> Line | Count | Source | 206 | 279 | fn try_encaps_with_rng( | 207 | 279 | &self, rng: &mut impl CryptoRngCore, | 208 | 279 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 279 | let mut ct = [0u8; CT_LEN]; | 210 | 279 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 279 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 279 | Ok((ssk, CipherText { 0: ct })) | 214 | 279 | } |
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<rand_core::os::OsRng> Line | Count | Source | 206 | 231 | fn try_encaps_with_rng( | 207 | 231 | &self, rng: &mut impl CryptoRngCore, | 208 | 231 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 231 | let mut ct = [0u8; CT_LEN]; | 210 | 231 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 231 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 231 | Ok((ssk, CipherText { 0: ct })) | 214 | 231 | } |
<fips203::types::EncapsKey<1568> as fips203::traits::Encaps>::try_encaps_with_rng::<ml_kem_fuzz::_::__libfuzzer_sys_run::TestRng> Line | Count | Source | 206 | 231 | fn try_encaps_with_rng( | 207 | 231 | &self, rng: &mut impl CryptoRngCore, | 208 | 231 | ) -> Result<(Self::SharedSecretKey, Self::CipherText), &'static str> { | 209 | 231 | let mut ct = [0u8; CT_LEN]; | 210 | 231 | let ssk = ml_kem_encaps::<K, { ETA1 as usize * 64 }, { ETA2 as usize * 64 }>( | 211 | 231 | rng, DU, DV, &self.0, &mut ct, | 212 | 0 | )?; | 213 | 231 | Ok((ssk, CipherText { 0: ct })) | 214 | 231 | } |
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.48k | fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> { |
223 | 1.48k | let ssk = ml_kem_decaps::< |
224 | 1.48k | K, |
225 | 1.48k | { ETA1 as usize * 64 }, |
226 | 1.48k | { ETA2 as usize * 64 }, |
227 | 1.48k | { 32 + 32 * (DU as usize * K + DV as usize) }, |
228 | 1.48k | CT_LEN, |
229 | 1.48k | >(DU, DV, &self.0, &ct.0); |
230 | 1.48k | ssk |
231 | 1.48k | } <fips203::types::DecapsKey<1632> as fips203::traits::Decaps>::try_decaps Line | Count | Source | 222 | 510 | fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> { | 223 | 510 | let ssk = ml_kem_decaps::< | 224 | 510 | K, | 225 | 510 | { ETA1 as usize * 64 }, | 226 | 510 | { ETA2 as usize * 64 }, | 227 | 510 | { 32 + 32 * (DU as usize * K + DV as usize) }, | 228 | 510 | CT_LEN, | 229 | 510 | >(DU, DV, &self.0, &ct.0); | 230 | 510 | ssk | 231 | 510 | } |
<fips203::types::DecapsKey<2400> as fips203::traits::Decaps>::try_decaps Line | Count | Source | 222 | 496 | fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> { | 223 | 496 | let ssk = ml_kem_decaps::< | 224 | 496 | K, | 225 | 496 | { ETA1 as usize * 64 }, | 226 | 496 | { ETA2 as usize * 64 }, | 227 | 496 | { 32 + 32 * (DU as usize * K + DV as usize) }, | 228 | 496 | CT_LEN, | 229 | 496 | >(DU, DV, &self.0, &ct.0); | 230 | 496 | ssk | 231 | 496 | } |
<fips203::types::DecapsKey<3168> as fips203::traits::Decaps>::try_decaps Line | Count | Source | 222 | 477 | fn try_decaps(&self, ct: &CipherText) -> Result<SharedSecretKey, &'static str> { | 223 | 477 | let ssk = ml_kem_decaps::< | 224 | 477 | K, | 225 | 477 | { ETA1 as usize * 64 }, | 226 | 477 | { ETA2 as usize * 64 }, | 227 | 477 | { 32 + 32 * (DU as usize * K + DV as usize) }, | 228 | 477 | CT_LEN, | 229 | 477 | >(DU, DV, &self.0, &ct.0); | 230 | 477 | ssk | 231 | 477 | } |
|
232 | | } |
233 | | |
234 | | |
235 | | impl SerDes for EncapsKey { |
236 | | type ByteArray = [u8; EK_LEN]; |
237 | | |
238 | 2.05k | fn into_bytes(self) -> Self::ByteArray { self.0 }<fips203::types::EncapsKey<800> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 238 | 701 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::EncapsKey<1184> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 238 | 683 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::EncapsKey<1568> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 238 | 673 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
|
239 | | |
240 | 3.84k | 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.1k | for i in 0..K { |
246 | 11.3k | let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?; |
247 | | } |
248 | 3.80k | Ok(EncapsKey { 0: ek }) |
249 | 3.84k | } <fips203::types::EncapsKey<800> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 240 | 1.36k | 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.06k | for i in 0..K { | 246 | 2.71k | let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?; | 247 | | } | 248 | 1.34k | Ok(EncapsKey { 0: ek }) | 249 | 1.36k | } |
<fips203::types::EncapsKey<1184> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 240 | 1.26k | 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.01k | for i in 0..K { | 246 | 3.76k | let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?; | 247 | | } | 248 | 1.24k | Ok(EncapsKey { 0: ek }) | 249 | 1.26k | } |
<fips203::types::EncapsKey<1568> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 240 | 1.21k | 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.07k | for i in 0..K { | 246 | 4.86k | let _ek_hat = byte_decode(12, &ek[384 * i..384 * (i + 1)])?; | 247 | | } | 248 | 1.21k | Ok(EncapsKey { 0: ek }) | 249 | 1.21k | } |
|
250 | | } |
251 | | |
252 | | |
253 | | impl SerDes for DecapsKey { |
254 | | type ByteArray = [u8; DK_LEN]; |
255 | | |
256 | 1.78k | fn into_bytes(self) -> Self::ByteArray { self.0 }<fips203::types::DecapsKey<1632> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 256 | 668 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::DecapsKey<2400> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 256 | 577 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::DecapsKey<3168> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 256 | 544 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
|
257 | | |
258 | 1.78k | 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.78k | let len_ek_pke = 384 * K + 32; |
263 | 1.78k | let len_dk_pke = 384 * K; |
264 | 1.78k | let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN]; |
265 | 1.77k | let _res = |
266 | 1.78k | EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?; |
267 | 1.77k | ensure!( |
268 | 1.77k | h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)], |
269 | 147 | "Encaps hash wrong" |
270 | | ); |
271 | 1.63k | Ok(DecapsKey { 0: dk }) |
272 | 1.78k | } <fips203::types::DecapsKey<1632> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 258 | 668 | 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 | 668 | let len_ek_pke = 384 * K + 32; | 263 | 668 | let len_dk_pke = 384 * K; | 264 | 668 | let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN]; | 265 | 660 | let _res = | 266 | 668 | EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?; | 267 | 660 | ensure!( | 268 | 660 | h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)], | 269 | 83 | "Encaps hash wrong" | 270 | | ); | 271 | 577 | Ok(DecapsKey { 0: dk }) | 272 | 668 | } |
<fips203::types::DecapsKey<2400> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 258 | 577 | 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 | 577 | let len_ek_pke = 384 * K + 32; | 263 | 577 | let len_dk_pke = 384 * K; | 264 | 577 | let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN]; | 265 | 575 | let _res = | 266 | 577 | EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?; | 267 | 575 | ensure!( | 268 | 575 | h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)], | 269 | 31 | "Encaps hash wrong" | 270 | | ); | 271 | 544 | Ok(DecapsKey { 0: dk }) | 272 | 577 | } |
<fips203::types::DecapsKey<3168> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 258 | 544 | 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 | 544 | let len_ek_pke = 384 * K + 32; | 263 | 544 | let len_dk_pke = 384 * K; | 264 | 544 | let ek = &dk[len_dk_pke..len_dk_pke + EK_LEN]; | 265 | 543 | let _res = | 266 | 544 | EncapsKey::try_from_bytes(ek.try_into().map_err(|_| "Malformed encaps key")?)?; | 267 | 543 | ensure!( | 268 | 543 | h(ek) == dk[(len_dk_pke + len_ek_pke)..(len_dk_pke + len_ek_pke + 32)], | 269 | 33 | "Encaps hash wrong" | 270 | | ); | 271 | 510 | Ok(DecapsKey { 0: dk }) | 272 | 544 | } |
|
273 | | } |
274 | | |
275 | | |
276 | | impl SerDes for CipherText { |
277 | | type ByteArray = [u8; CT_LEN]; |
278 | | |
279 | 837 | fn into_bytes(self) -> Self::ByteArray { self.0 }<fips203::types::CipherText<768> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 279 | 279 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::CipherText<1088> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 279 | 279 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
<fips203::types::CipherText<1568> as fips203::traits::SerDes>::into_bytes Line | Count | Source | 279 | 279 | fn into_bytes(self) -> Self::ByteArray { self.0 } |
|
280 | | |
281 | 837 | 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 | 837 | Ok(CipherText { 0: ct }) |
286 | 837 | } <fips203::types::CipherText<768> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 281 | 279 | 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 | 279 | Ok(CipherText { 0: ct }) | 286 | 279 | } |
<fips203::types::CipherText<1088> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 281 | 279 | 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 | 279 | Ok(CipherText { 0: ct }) | 286 | 279 | } |
<fips203::types::CipherText<1568> as fips203::traits::SerDes>::try_from_bytes Line | Count | Source | 281 | 279 | 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 | 279 | Ok(CipherText { 0: ct }) | 286 | 279 | } |
|
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 | | } |