Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}