1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
use ml_kem::array::typenum::marker_traits::Unsigned;
use ml_kem::kem::Decapsulate;
use ml_kem::{Ciphertext, EncodedSizeUser, KemCore, MlKem1024, MlKem1024Params};
/// Use the strongest variant of ML-KEM. It is fast and the keys are small, so there is no practical
/// benefit of going with anything lower. The servers also only supports the strongest variant.
const ALGORITHM_NAME: &str = "ML-KEM-1024";
/// The number of bytes in an ML-KEM 1024 ciphertext.
const CIPHERTEXT_LEN: usize = <MlKem1024 as KemCore>::CiphertextSize::USIZE;
pub struct Keypair {
encapsulation_key: ml_kem::kem::EncapsulationKey<MlKem1024Params>,
decapsulation_key: ml_kem::kem::DecapsulationKey<MlKem1024Params>,
}
impl Keypair {
/// Returns the encapsulation key. This is sometimes called the public key.
///
/// This is the key to send to the peer you want to negotiate a shared secret with.
pub fn encapsulation_key(&self) -> Vec<u8> {
self.encapsulation_key.as_bytes().as_slice().to_vec()
}
pub fn algorithm_name(&self) -> &'static str {
ALGORITHM_NAME
}
/// Decapsulates a shared secret that was encapsulated to our encapsulation key.
///
// Always inline in order to try to avoid potential copies of `shared_secret` to multiple places
// on the stack. This is almost pointless as with optimization all bets are off regarding where
// the shared secrets will end up in memory. In the future we can try to do better, by
// cleaning the stack. But this is not trivial. Please see:
// https://github.com/RustCrypto/KEMs/issues/70
#[inline(always)]
pub fn decapsulate(&self, ciphertext_slice: &[u8]) -> Result<[u8; 32], super::Error> {
// Convert the ciphertext byte slice into the appropriate Array<u8, ...> type.
// This involves validating the length of the ciphertext.
let ciphertext_array =
<Ciphertext<MlKem1024>>::try_from(ciphertext_slice).map_err(|_| {
super::Error::InvalidCiphertextLength {
algorithm: self.algorithm_name(),
actual: ciphertext_slice.len(),
expected: CIPHERTEXT_LEN,
}
})?;
// Decapsulate the shared secret. This is an infallible operation but
// must due to the signature of the trait it is implemented via return a
// Result that we must unwrap... For now. Please see:
// https://github.com/RustCrypto/KEMs/pull/59
let shared_secret = self
.decapsulation_key
.decapsulate(&ciphertext_array)
.unwrap();
Ok(shared_secret.0)
}
}
/// Generates and returns an ML-KEM keypair.
pub fn keypair() -> Keypair {
let (decapsulation_key, encapsulation_key) =
ml_kem::MlKem1024::generate(&mut rand::thread_rng());
Keypair {
encapsulation_key,
decapsulation_key,
}
}
|