/rust/registry/src/index.crates.io-1949cf8c6b5b557f/aws-lc-rs-1.12.4/src/kem.rs
Line | Count | Source |
1 | | // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. |
2 | | // SPDX-License-Identifier: Apache-2.0 OR ISC |
3 | | |
4 | | //! Key-Encapsulation Mechanisms (KEMs), including support for Kyber Round 3 Submission. |
5 | | //! |
6 | | //! # Example |
7 | | //! |
8 | | //! Note that this example uses the Kyber-512 Round 3 algorithm, but other algorithms can be used |
9 | | //! in the exact same way by substituting |
10 | | //! `kem::<desired_algorithm_here>` for `kem::KYBER512_R3`. |
11 | | //! |
12 | | //! ```rust |
13 | | //! use aws_lc_rs::{ |
14 | | //! kem::{Ciphertext, DecapsulationKey, EncapsulationKey}, |
15 | | //! kem::{ML_KEM_512} |
16 | | //! }; |
17 | | //! |
18 | | //! // Alice generates their (private) decapsulation key. |
19 | | //! let decapsulation_key = DecapsulationKey::generate(&ML_KEM_512)?; |
20 | | //! |
21 | | //! // Alices computes the (public) encapsulation key. |
22 | | //! let encapsulation_key = decapsulation_key.encapsulation_key()?; |
23 | | //! |
24 | | //! let encapsulation_key_bytes = encapsulation_key.key_bytes()?; |
25 | | //! |
26 | | //! // Alice sends the encapsulation key bytes to bob through some |
27 | | //! // protocol message. |
28 | | //! let encapsulation_key_bytes = encapsulation_key_bytes.as_ref(); |
29 | | //! |
30 | | //! // Bob constructs the (public) encapsulation key from the key bytes provided by Alice. |
31 | | //! let retrieved_encapsulation_key = EncapsulationKey::new(&ML_KEM_512, encapsulation_key_bytes)?; |
32 | | //! |
33 | | //! // Bob executes the encapsulation algorithm to to produce their copy of the secret, and associated ciphertext. |
34 | | //! let (ciphertext, bob_secret) = retrieved_encapsulation_key.encapsulate()?; |
35 | | //! |
36 | | //! // Alice receives ciphertext bytes from bob |
37 | | //! let ciphertext_bytes = ciphertext.as_ref(); |
38 | | //! |
39 | | //! // Bob sends Alice the ciphertext computed from the encapsulation algorithm, Alice runs decapsulation to derive their |
40 | | //! // copy of the secret. |
41 | | //! let alice_secret = decapsulation_key.decapsulate(Ciphertext::from(ciphertext_bytes))?; |
42 | | //! |
43 | | //! // Alice and Bob have now arrived to the same secret |
44 | | //! assert_eq!(alice_secret.as_ref(), bob_secret.as_ref()); |
45 | | //! |
46 | | //! # Ok::<(), aws_lc_rs::error::Unspecified>(()) |
47 | | //! ``` |
48 | | use crate::aws_lc::{ |
49 | | EVP_PKEY_CTX_kem_set_params, EVP_PKEY_decapsulate, EVP_PKEY_encapsulate, |
50 | | EVP_PKEY_kem_new_raw_public_key, EVP_PKEY, EVP_PKEY_KEM, |
51 | | }; |
52 | | use crate::buffer::Buffer; |
53 | | use crate::encoding::generated_encodings; |
54 | | use crate::error::{KeyRejected, Unspecified}; |
55 | | use crate::ptr::LcPtr; |
56 | | use alloc::borrow::Cow; |
57 | | use core::cmp::Ordering; |
58 | | use zeroize::Zeroize; |
59 | | |
60 | | const ML_KEM_512_SHARED_SECRET_LENGTH: usize = 32; |
61 | | const ML_KEM_512_PUBLIC_KEY_LENGTH: usize = 800; |
62 | | const ML_KEM_512_SECRET_KEY_LENGTH: usize = 1632; |
63 | | const ML_KEM_512_CIPHERTEXT_LENGTH: usize = 768; |
64 | | |
65 | | const ML_KEM_768_SHARED_SECRET_LENGTH: usize = 32; |
66 | | const ML_KEM_768_PUBLIC_KEY_LENGTH: usize = 1184; |
67 | | const ML_KEM_768_SECRET_KEY_LENGTH: usize = 2400; |
68 | | const ML_KEM_768_CIPHERTEXT_LENGTH: usize = 1088; |
69 | | |
70 | | const ML_KEM_1024_SHARED_SECRET_LENGTH: usize = 32; |
71 | | const ML_KEM_1024_PUBLIC_KEY_LENGTH: usize = 1568; |
72 | | const ML_KEM_1024_SECRET_KEY_LENGTH: usize = 3168; |
73 | | const ML_KEM_1024_CIPHERTEXT_LENGTH: usize = 1568; |
74 | | |
75 | | /// NIST FIPS 203 ML-KEM-512 algorithm. |
76 | | pub const ML_KEM_512: Algorithm<AlgorithmId> = Algorithm { |
77 | | id: AlgorithmId::MlKem512, |
78 | | decapsulate_key_size: ML_KEM_512_SECRET_KEY_LENGTH, |
79 | | encapsulate_key_size: ML_KEM_512_PUBLIC_KEY_LENGTH, |
80 | | ciphertext_size: ML_KEM_512_CIPHERTEXT_LENGTH, |
81 | | shared_secret_size: ML_KEM_512_SHARED_SECRET_LENGTH, |
82 | | }; |
83 | | |
84 | | /// NIST FIPS 203 ML-KEM-768 algorithm. |
85 | | pub const ML_KEM_768: Algorithm<AlgorithmId> = Algorithm { |
86 | | id: AlgorithmId::MlKem768, |
87 | | decapsulate_key_size: ML_KEM_768_SECRET_KEY_LENGTH, |
88 | | encapsulate_key_size: ML_KEM_768_PUBLIC_KEY_LENGTH, |
89 | | ciphertext_size: ML_KEM_768_CIPHERTEXT_LENGTH, |
90 | | shared_secret_size: ML_KEM_768_SHARED_SECRET_LENGTH, |
91 | | }; |
92 | | |
93 | | /// NIST FIPS 203 ML-KEM-1024 algorithm. |
94 | | pub const ML_KEM_1024: Algorithm<AlgorithmId> = Algorithm { |
95 | | id: AlgorithmId::MlKem1024, |
96 | | decapsulate_key_size: ML_KEM_1024_SECRET_KEY_LENGTH, |
97 | | encapsulate_key_size: ML_KEM_1024_PUBLIC_KEY_LENGTH, |
98 | | ciphertext_size: ML_KEM_1024_CIPHERTEXT_LENGTH, |
99 | | shared_secret_size: ML_KEM_1024_SHARED_SECRET_LENGTH, |
100 | | }; |
101 | | |
102 | | use crate::aws_lc::{NID_MLKEM1024, NID_MLKEM512, NID_MLKEM768}; |
103 | | |
104 | | /// An identifier for a KEM algorithm. |
105 | | pub trait AlgorithmIdentifier: |
106 | | Copy + Clone + Debug + PartialEq + crate::sealed::Sealed + 'static |
107 | | { |
108 | | /// Returns the algorithm's associated AWS-LC nid. |
109 | | fn nid(self) -> i32; |
110 | | } |
111 | | |
112 | | /// A KEM algorithm |
113 | | #[derive(PartialEq)] |
114 | | pub struct Algorithm<Id = AlgorithmId> |
115 | | where |
116 | | Id: AlgorithmIdentifier, |
117 | | { |
118 | | pub(crate) id: Id, |
119 | | pub(crate) decapsulate_key_size: usize, |
120 | | pub(crate) encapsulate_key_size: usize, |
121 | | pub(crate) ciphertext_size: usize, |
122 | | pub(crate) shared_secret_size: usize, |
123 | | } |
124 | | |
125 | | impl<Id> Algorithm<Id> |
126 | | where |
127 | | Id: AlgorithmIdentifier, |
128 | | { |
129 | | /// Returns the identifier for this algorithm. |
130 | | #[must_use] |
131 | 0 | pub fn id(&self) -> Id { |
132 | 0 | self.id |
133 | 0 | } |
134 | | |
135 | | #[inline] |
136 | | #[allow(dead_code)] |
137 | 0 | pub(crate) fn decapsulate_key_size(&self) -> usize { |
138 | 0 | self.decapsulate_key_size |
139 | 0 | } |
140 | | |
141 | | #[inline] |
142 | 0 | pub(crate) fn encapsulate_key_size(&self) -> usize { |
143 | 0 | self.encapsulate_key_size |
144 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::Algorithm>::encapsulate_key_size Unexecuted instantiation: <aws_lc_rs::kem::Algorithm<_>>::encapsulate_key_size |
145 | | |
146 | | #[inline] |
147 | 0 | pub(crate) fn ciphertext_size(&self) -> usize { |
148 | 0 | self.ciphertext_size |
149 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::Algorithm>::ciphertext_size Unexecuted instantiation: <aws_lc_rs::kem::Algorithm<_>>::ciphertext_size |
150 | | |
151 | | #[inline] |
152 | 0 | pub(crate) fn shared_secret_size(&self) -> usize { |
153 | 0 | self.shared_secret_size |
154 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::Algorithm>::shared_secret_size Unexecuted instantiation: <aws_lc_rs::kem::Algorithm<_>>::shared_secret_size |
155 | | } |
156 | | |
157 | | impl<Id> Debug for Algorithm<Id> |
158 | | where |
159 | | Id: AlgorithmIdentifier, |
160 | | { |
161 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
162 | 0 | Debug::fmt(&self.id, f) |
163 | 0 | } |
164 | | } |
165 | | |
166 | | /// A serializable decapulsation key usable with KEMs. This can be randomly generated with `DecapsulationKey::generate`. |
167 | | pub struct DecapsulationKey<Id = AlgorithmId> |
168 | | where |
169 | | Id: AlgorithmIdentifier, |
170 | | { |
171 | | algorithm: &'static Algorithm<Id>, |
172 | | evp_pkey: LcPtr<EVP_PKEY>, |
173 | | } |
174 | | |
175 | | /// Identifier for a KEM algorithm. |
176 | | #[non_exhaustive] |
177 | | #[derive(Clone, Copy, Debug, PartialEq)] |
178 | | pub enum AlgorithmId { |
179 | | /// NIST FIPS 203 ML-KEM-512 algorithm. |
180 | | MlKem512, |
181 | | |
182 | | /// NIST FIPS 203 ML-KEM-768 algorithm. |
183 | | MlKem768, |
184 | | |
185 | | /// NIST FIPS 203 ML-KEM-1024 algorithm. |
186 | | MlKem1024, |
187 | | } |
188 | | |
189 | | impl AlgorithmIdentifier for AlgorithmId { |
190 | 0 | fn nid(self) -> i32 { |
191 | 0 | match self { |
192 | 0 | AlgorithmId::MlKem512 => NID_MLKEM512, |
193 | 0 | AlgorithmId::MlKem768 => NID_MLKEM768, |
194 | 0 | AlgorithmId::MlKem1024 => NID_MLKEM1024, |
195 | | } |
196 | 0 | } |
197 | | } |
198 | | |
199 | | impl crate::sealed::Sealed for AlgorithmId {} |
200 | | |
201 | | impl<Id> DecapsulationKey<Id> |
202 | | where |
203 | | Id: AlgorithmIdentifier, |
204 | | { |
205 | | /// Generate a new KEM decapsulation key for the given algorithm. |
206 | | /// |
207 | | /// # Errors |
208 | | /// `error::Unspecified` when operation fails due to internal error. |
209 | 0 | pub fn generate(alg: &'static Algorithm<Id>) -> Result<Self, Unspecified> { |
210 | 0 | let kyber_key = kem_key_generate(alg.id.nid())?; |
211 | 0 | Ok(DecapsulationKey { |
212 | 0 | algorithm: alg, |
213 | 0 | evp_pkey: kyber_key, |
214 | 0 | }) |
215 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey>::generate Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey<_>>::generate |
216 | | |
217 | | /// Return the algorithm associated with the given KEM decapsulation key. |
218 | | #[must_use] |
219 | 0 | pub fn algorithm(&self) -> &'static Algorithm<Id> { |
220 | 0 | self.algorithm |
221 | 0 | } |
222 | | |
223 | | /// Computes the KEM encapsulation key from the KEM decapsulation key. |
224 | | /// |
225 | | /// # Errors |
226 | | /// `error::Unspecified` when operation fails due to internal error. |
227 | | #[allow(clippy::missing_panics_doc)] |
228 | 0 | pub fn encapsulation_key(&self) -> Result<EncapsulationKey<Id>, Unspecified> { |
229 | 0 | let evp_pkey = self.evp_pkey.clone(); |
230 | | |
231 | 0 | Ok(EncapsulationKey { |
232 | 0 | algorithm: self.algorithm, |
233 | 0 | evp_pkey, |
234 | 0 | }) |
235 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey>::encapsulation_key Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey<_>>::encapsulation_key |
236 | | |
237 | | /// Performs the decapsulate operation using this KEM decapsulation key on the given ciphertext. |
238 | | /// |
239 | | /// `ciphertext` is the ciphertext generated by the encapsulate operation using the KEM encapsulation key |
240 | | /// associated with this KEM decapsulation key. |
241 | | /// |
242 | | /// # Errors |
243 | | /// `Unspecified` when operation fails due to internal error. |
244 | | #[allow(clippy::needless_pass_by_value)] |
245 | 0 | pub fn decapsulate(&self, ciphertext: Ciphertext<'_>) -> Result<SharedSecret, Unspecified> { |
246 | 0 | let mut shared_secret_len = self.algorithm.shared_secret_size(); |
247 | 0 | let mut shared_secret: Vec<u8> = vec![0u8; shared_secret_len]; |
248 | | |
249 | 0 | let mut ctx = self.evp_pkey.create_EVP_PKEY_CTX()?; |
250 | | |
251 | 0 | let ciphertext = ciphertext.as_ref(); |
252 | | |
253 | 0 | if 1 != unsafe { |
254 | 0 | EVP_PKEY_decapsulate( |
255 | 0 | *ctx.as_mut(), |
256 | 0 | shared_secret.as_mut_ptr(), |
257 | 0 | &mut shared_secret_len, |
258 | 0 | // AWS-LC incorrectly has this as an unqualified `uint8_t *`, it should be qualified with const |
259 | 0 | ciphertext.as_ptr() as *mut u8, |
260 | 0 | ciphertext.len(), |
261 | 0 | ) |
262 | 0 | } { |
263 | 0 | return Err(Unspecified); |
264 | 0 | } |
265 | | |
266 | | // This is currently pedantic but done for safety in-case the shared_secret buffer |
267 | | // size changes in the future. `EVP_PKEY_decapsulate` updates `shared_secret_len` with |
268 | | // the length of the shared secret in the event the buffer provided was larger then the secret. |
269 | | // This truncates the buffer to the proper length to match the shared secret written. |
270 | 0 | debug_assert_eq!(shared_secret_len, shared_secret.len()); |
271 | 0 | shared_secret.truncate(shared_secret_len); |
272 | | |
273 | 0 | Ok(SharedSecret(shared_secret.into_boxed_slice())) |
274 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey>::decapsulate Unexecuted instantiation: <aws_lc_rs::kem::DecapsulationKey<_>>::decapsulate |
275 | | } |
276 | | |
277 | | unsafe impl<Id> Send for DecapsulationKey<Id> where Id: AlgorithmIdentifier {} |
278 | | |
279 | | unsafe impl<Id> Sync for DecapsulationKey<Id> where Id: AlgorithmIdentifier {} |
280 | | |
281 | | impl<Id> Debug for DecapsulationKey<Id> |
282 | | where |
283 | | Id: AlgorithmIdentifier, |
284 | | { |
285 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
286 | 0 | f.debug_struct("DecapsulationKey") |
287 | 0 | .field("algorithm", &self.algorithm) |
288 | 0 | .finish_non_exhaustive() |
289 | 0 | } |
290 | | } |
291 | | |
292 | | use paste::paste; |
293 | | |
294 | | generated_encodings!(EncapsulationKeyBytes); |
295 | | |
296 | | /// A serializable encapsulation key usable with KEM algorithms. Constructed |
297 | | /// from either a `DecapsulationKey` or raw bytes. |
298 | | pub struct EncapsulationKey<Id = AlgorithmId> |
299 | | where |
300 | | Id: AlgorithmIdentifier, |
301 | | { |
302 | | algorithm: &'static Algorithm<Id>, |
303 | | evp_pkey: LcPtr<EVP_PKEY>, |
304 | | } |
305 | | |
306 | | impl<Id> EncapsulationKey<Id> |
307 | | where |
308 | | Id: AlgorithmIdentifier, |
309 | | { |
310 | | /// Return the algorithm associated with the given KEM encapsulation key. |
311 | | #[must_use] |
312 | 0 | pub fn algorithm(&self) -> &'static Algorithm<Id> { |
313 | 0 | self.algorithm |
314 | 0 | } |
315 | | |
316 | | /// Performs the encapsulate operation using this KEM encapsulation key, generating a ciphertext |
317 | | /// and associated shared secret. |
318 | | /// |
319 | | /// # Errors |
320 | | /// `error::Unspecified` when operation fails due to internal error. |
321 | 0 | pub fn encapsulate(&self) -> Result<(Ciphertext<'static>, SharedSecret), Unspecified> { |
322 | 0 | let mut ciphertext_len = self.algorithm.ciphertext_size(); |
323 | 0 | let mut shared_secret_len = self.algorithm.shared_secret_size(); |
324 | 0 | let mut ciphertext: Vec<u8> = vec![0u8; ciphertext_len]; |
325 | 0 | let mut shared_secret: Vec<u8> = vec![0u8; shared_secret_len]; |
326 | | |
327 | 0 | let mut ctx = self.evp_pkey.create_EVP_PKEY_CTX()?; |
328 | | |
329 | 0 | if 1 != unsafe { |
330 | 0 | EVP_PKEY_encapsulate( |
331 | 0 | *ctx.as_mut(), |
332 | 0 | ciphertext.as_mut_ptr(), |
333 | 0 | &mut ciphertext_len, |
334 | 0 | shared_secret.as_mut_ptr(), |
335 | 0 | &mut shared_secret_len, |
336 | 0 | ) |
337 | 0 | } { |
338 | 0 | return Err(Unspecified); |
339 | 0 | } |
340 | | |
341 | | // The following two steps are currently pedantic but done for safety in-case the buffer allocation |
342 | | // sizes change in the future. `EVP_PKEY_encapsulate` updates `ciphertext_len` and `shared_secret_len` with |
343 | | // the length of the ciphertext and shared secret respectivly in the event the buffer provided for each was |
344 | | // larger then the actual values. Thus these two steps truncate the buffers to the proper length to match the |
345 | | // value lengths written. |
346 | 0 | debug_assert_eq!(ciphertext_len, ciphertext.len()); |
347 | 0 | ciphertext.truncate(ciphertext_len); |
348 | 0 | debug_assert_eq!(shared_secret_len, shared_secret.len()); |
349 | 0 | shared_secret.truncate(shared_secret_len); |
350 | | |
351 | 0 | Ok(( |
352 | 0 | Ciphertext::new(ciphertext), |
353 | 0 | SharedSecret::new(shared_secret.into_boxed_slice()), |
354 | 0 | )) |
355 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey>::encapsulate Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey<_>>::encapsulate |
356 | | |
357 | | /// Returns the `EnscapsulationKey` bytes. |
358 | | /// |
359 | | /// # Errors |
360 | | /// * `Unspecified`: Any failure to retrieve the `EnscapsulationKey` bytes. |
361 | 0 | pub fn key_bytes(&self) -> Result<EncapsulationKeyBytes<'static>, Unspecified> { |
362 | 0 | let mut encapsulate_bytes = vec![0u8; self.algorithm.encapsulate_key_size()]; |
363 | 0 | let encapsulate_key_size = self |
364 | 0 | .evp_pkey |
365 | 0 | .marshal_raw_public_to_buffer(&mut encapsulate_bytes)?; |
366 | | |
367 | 0 | debug_assert_eq!(encapsulate_key_size, encapsulate_bytes.len()); |
368 | 0 | encapsulate_bytes.truncate(encapsulate_key_size); |
369 | | |
370 | 0 | Ok(EncapsulationKeyBytes::new(encapsulate_bytes)) |
371 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey>::key_bytes Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey<_>>::key_bytes |
372 | | |
373 | | /// Creates a new KEM encapsulation key from raw bytes. This method MUST NOT be used to generate |
374 | | /// a new encapsulation key, rather it MUST be used to construct `EncapsulationKey` previously serialized |
375 | | /// to raw bytes. |
376 | | /// |
377 | | /// `alg` is the [`Algorithm`] to be associated with the generated `EncapsulationKey`. |
378 | | /// |
379 | | /// `bytes` is a slice of raw bytes representing a `EncapsulationKey`. |
380 | | /// |
381 | | /// # Errors |
382 | | /// `error::KeyRejected` when operation fails during key creation. |
383 | 0 | pub fn new(alg: &'static Algorithm<Id>, bytes: &[u8]) -> Result<Self, KeyRejected> { |
384 | 0 | match bytes.len().cmp(&alg.encapsulate_key_size()) { |
385 | 0 | Ordering::Less => Err(KeyRejected::too_small()), |
386 | 0 | Ordering::Greater => Err(KeyRejected::too_large()), |
387 | 0 | Ordering::Equal => Ok(()), |
388 | 0 | }?; |
389 | 0 | let pubkey = LcPtr::new(unsafe { |
390 | 0 | EVP_PKEY_kem_new_raw_public_key(alg.id.nid(), bytes.as_ptr(), bytes.len()) |
391 | 0 | })?; |
392 | 0 | Ok(EncapsulationKey { |
393 | 0 | algorithm: alg, |
394 | 0 | evp_pkey: pubkey, |
395 | 0 | }) |
396 | 0 | } Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey>::new Unexecuted instantiation: <aws_lc_rs::kem::EncapsulationKey<_>>::new |
397 | | } |
398 | | |
399 | | unsafe impl<Id> Send for EncapsulationKey<Id> where Id: AlgorithmIdentifier {} |
400 | | |
401 | | unsafe impl<Id> Sync for EncapsulationKey<Id> where Id: AlgorithmIdentifier {} |
402 | | |
403 | | impl<Id> Debug for EncapsulationKey<Id> |
404 | | where |
405 | | Id: AlgorithmIdentifier, |
406 | | { |
407 | 0 | fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result { |
408 | 0 | f.debug_struct("EncapsulationKey") |
409 | 0 | .field("algorithm", &self.algorithm) |
410 | 0 | .finish_non_exhaustive() |
411 | 0 | } |
412 | | } |
413 | | |
414 | | /// A set of encrypted bytes produced by [`EncapsulationKey::encapsulate`], |
415 | | /// and used as an input to [`DecapsulationKey::decapsulate`]. |
416 | | pub struct Ciphertext<'a>(Cow<'a, [u8]>); |
417 | | |
418 | | impl<'a> Ciphertext<'a> { |
419 | 0 | fn new(value: Vec<u8>) -> Ciphertext<'a> { |
420 | 0 | Self(Cow::Owned(value)) |
421 | 0 | } |
422 | | } |
423 | | |
424 | | impl Drop for Ciphertext<'_> { |
425 | 0 | fn drop(&mut self) { |
426 | 0 | if let Cow::Owned(ref mut v) = self.0 { |
427 | 0 | v.zeroize(); |
428 | 0 | } |
429 | 0 | } |
430 | | } |
431 | | |
432 | | impl AsRef<[u8]> for Ciphertext<'_> { |
433 | 0 | fn as_ref(&self) -> &[u8] { |
434 | 0 | match self.0 { |
435 | 0 | Cow::Borrowed(v) => v, |
436 | 0 | Cow::Owned(ref v) => v.as_ref(), |
437 | | } |
438 | 0 | } |
439 | | } |
440 | | |
441 | | impl<'a> From<&'a [u8]> for Ciphertext<'a> { |
442 | 0 | fn from(value: &'a [u8]) -> Self { |
443 | 0 | Self(Cow::Borrowed(value)) |
444 | 0 | } |
445 | | } |
446 | | |
447 | | /// The cryptographic shared secret output from the KEM encapsulate / decapsulate process. |
448 | | pub struct SharedSecret(Box<[u8]>); |
449 | | |
450 | | impl SharedSecret { |
451 | 0 | fn new(value: Box<[u8]>) -> Self { |
452 | 0 | Self(value) |
453 | 0 | } |
454 | | } |
455 | | |
456 | | impl Drop for SharedSecret { |
457 | 0 | fn drop(&mut self) { |
458 | 0 | self.0.zeroize(); |
459 | 0 | } |
460 | | } |
461 | | |
462 | | impl AsRef<[u8]> for SharedSecret { |
463 | 0 | fn as_ref(&self) -> &[u8] { |
464 | 0 | self.0.as_ref() |
465 | 0 | } |
466 | | } |
467 | | |
468 | | // Returns an LcPtr to an EVP_PKEY |
469 | | #[inline] |
470 | 0 | fn kem_key_generate(nid: i32) -> Result<LcPtr<EVP_PKEY>, Unspecified> { |
471 | 0 | let params_fn = |ctx| { |
472 | 0 | if 1 == unsafe { EVP_PKEY_CTX_kem_set_params(ctx, nid) } { |
473 | 0 | Ok(()) |
474 | | } else { |
475 | 0 | Err(()) |
476 | | } |
477 | 0 | }; Unexecuted instantiation: aws_lc_rs::kem::kem_key_generate::{closure#0}Unexecuted instantiation: aws_lc_rs::kem::kem_key_generate::{closure#0} |
478 | | |
479 | 0 | LcPtr::<EVP_PKEY>::generate(EVP_PKEY_KEM, Some(params_fn)) |
480 | 0 | } Unexecuted instantiation: aws_lc_rs::kem::kem_key_generate Unexecuted instantiation: aws_lc_rs::kem::kem_key_generate |
481 | | |
482 | | #[cfg(test)] |
483 | | mod tests { |
484 | | use super::{Ciphertext, DecapsulationKey, EncapsulationKey, SharedSecret}; |
485 | | use crate::error::KeyRejected; |
486 | | |
487 | | use crate::kem::{ML_KEM_1024, ML_KEM_512, ML_KEM_768}; |
488 | | |
489 | | #[test] |
490 | | fn ciphertext() { |
491 | | let ciphertext_bytes = vec![42u8; 4]; |
492 | | let ciphertext = Ciphertext::from(ciphertext_bytes.as_ref()); |
493 | | assert_eq!(ciphertext.as_ref(), &[42, 42, 42, 42]); |
494 | | drop(ciphertext); |
495 | | |
496 | | let ciphertext_bytes = vec![42u8; 4]; |
497 | | let ciphertext = Ciphertext::<'static>::new(ciphertext_bytes); |
498 | | assert_eq!(ciphertext.as_ref(), &[42, 42, 42, 42]); |
499 | | } |
500 | | |
501 | | #[test] |
502 | | fn shared_secret() { |
503 | | let secret_bytes = vec![42u8; 4]; |
504 | | let shared_secret = SharedSecret::new(secret_bytes.into_boxed_slice()); |
505 | | assert_eq!(shared_secret.as_ref(), &[42, 42, 42, 42]); |
506 | | } |
507 | | |
508 | | #[test] |
509 | | fn test_kem_serialize() { |
510 | | for algorithm in [&ML_KEM_512, &ML_KEM_768, &ML_KEM_1024] { |
511 | | let priv_key = DecapsulationKey::generate(algorithm).unwrap(); |
512 | | assert_eq!(priv_key.algorithm(), algorithm); |
513 | | |
514 | | let pub_key = priv_key.encapsulation_key().unwrap(); |
515 | | let pubkey_raw_bytes = pub_key.key_bytes().unwrap(); |
516 | | let pub_key_from_bytes = |
517 | | EncapsulationKey::new(algorithm, pubkey_raw_bytes.as_ref()).unwrap(); |
518 | | |
519 | | assert_eq!( |
520 | | pub_key.key_bytes().unwrap().as_ref(), |
521 | | pub_key_from_bytes.key_bytes().unwrap().as_ref() |
522 | | ); |
523 | | assert_eq!(pub_key.algorithm(), pub_key_from_bytes.algorithm()); |
524 | | } |
525 | | } |
526 | | |
527 | | #[test] |
528 | | fn test_kem_wrong_sizes() { |
529 | | for algorithm in [&ML_KEM_512, &ML_KEM_768, &ML_KEM_1024] { |
530 | | let too_long_bytes = vec![0u8; algorithm.encapsulate_key_size() + 1]; |
531 | | let long_pub_key_from_bytes = EncapsulationKey::new(algorithm, &too_long_bytes); |
532 | | assert_eq!( |
533 | | long_pub_key_from_bytes.err(), |
534 | | Some(KeyRejected::too_large()) |
535 | | ); |
536 | | |
537 | | let too_short_bytes = vec![0u8; algorithm.encapsulate_key_size() - 1]; |
538 | | let short_pub_key_from_bytes = EncapsulationKey::new(algorithm, &too_short_bytes); |
539 | | assert_eq!( |
540 | | short_pub_key_from_bytes.err(), |
541 | | Some(KeyRejected::too_small()) |
542 | | ); |
543 | | } |
544 | | } |
545 | | |
546 | | #[test] |
547 | | fn test_kem_e2e() { |
548 | | for algorithm in [&ML_KEM_512, &ML_KEM_768, &ML_KEM_1024] { |
549 | | let priv_key = DecapsulationKey::generate(algorithm).unwrap(); |
550 | | assert_eq!(priv_key.algorithm(), algorithm); |
551 | | |
552 | | let pub_key = priv_key.encapsulation_key().unwrap(); |
553 | | |
554 | | let (alice_ciphertext, alice_secret) = |
555 | | pub_key.encapsulate().expect("encapsulate successful"); |
556 | | |
557 | | let bob_secret = priv_key |
558 | | .decapsulate(alice_ciphertext) |
559 | | .expect("decapsulate successful"); |
560 | | |
561 | | assert_eq!(alice_secret.as_ref(), bob_secret.as_ref()); |
562 | | } |
563 | | } |
564 | | |
565 | | #[test] |
566 | | fn test_serialized_kem_e2e() { |
567 | | for algorithm in [&ML_KEM_512, &ML_KEM_768, &ML_KEM_1024] { |
568 | | let priv_key = DecapsulationKey::generate(algorithm).unwrap(); |
569 | | assert_eq!(priv_key.algorithm(), algorithm); |
570 | | |
571 | | let pub_key = priv_key.encapsulation_key().unwrap(); |
572 | | |
573 | | // Generate public key bytes to send to bob |
574 | | let pub_key_bytes = pub_key.key_bytes().unwrap(); |
575 | | |
576 | | // Test that priv_key's EVP_PKEY isn't entirely freed since we remove this pub_key's reference. |
577 | | drop(pub_key); |
578 | | |
579 | | let retrieved_pub_key = |
580 | | EncapsulationKey::new(algorithm, pub_key_bytes.as_ref()).unwrap(); |
581 | | let (ciphertext, bob_secret) = retrieved_pub_key |
582 | | .encapsulate() |
583 | | .expect("encapsulate successful"); |
584 | | |
585 | | let alice_secret = priv_key |
586 | | .decapsulate(ciphertext) |
587 | | .expect("decapsulate successful"); |
588 | | |
589 | | assert_eq!(alice_secret.as_ref(), bob_secret.as_ref()); |
590 | | } |
591 | | } |
592 | | |
593 | | #[test] |
594 | | fn test_debug_fmt() { |
595 | | let private = DecapsulationKey::generate(&ML_KEM_512).expect("successful generation"); |
596 | | assert_eq!( |
597 | | format!("{private:?}"), |
598 | | "DecapsulationKey { algorithm: MlKem512, .. }" |
599 | | ); |
600 | | assert_eq!( |
601 | | format!( |
602 | | "{:?}", |
603 | | private.encapsulation_key().expect("public key retrievable") |
604 | | ), |
605 | | "EncapsulationKey { algorithm: MlKem512, .. }" |
606 | | ); |
607 | | } |
608 | | } |