securedrop_protocol_minimal/primitives/
mlkem.rs1use crate::primitives::provider::kem::{PrivateKey, PublicKey};
2use crate::primitives::provider::mlkem::{KEY_GENERATION_SEED_SIZE, mlkem768};
3use rand_core::{CryptoRng, RngCore};
4
5pub const MLKEM768_PUBLIC_KEY_LEN: usize = 1184;
10pub const MLKEM768_PRIVATE_KEY_LEN: usize = 2400;
11
12pub(crate) const LEN_MLKEM_SHAREDSECRET_ENCAPS: usize = 1088;
13
14#[derive(Debug, Clone)]
16pub(crate) struct MLKEM768PublicKey([u8; MLKEM768_PUBLIC_KEY_LEN]);
17
18#[derive(Debug, Clone)]
20pub(crate) struct MLKEM768PrivateKey([u8; MLKEM768_PRIVATE_KEY_LEN]);
21
22impl MLKEM768PublicKey {
23 pub(crate) fn as_bytes(&self) -> &[u8; MLKEM768_PUBLIC_KEY_LEN] {
24 &self.0
25 }
26
27 pub(crate) fn from_bytes(bytes: [u8; MLKEM768_PUBLIC_KEY_LEN]) -> Self {
28 Self(bytes)
29 }
30}
31
32impl MLKEM768PrivateKey {
33 pub(crate) fn as_bytes(&self) -> &[u8; MLKEM768_PRIVATE_KEY_LEN] {
34 &self.0
35 }
36
37 pub(crate) fn from_bytes(bytes: [u8; MLKEM768_PRIVATE_KEY_LEN]) -> Self {
38 Self(bytes)
39 }
40}
41
42pub(crate) fn from_bytes(
44 seed: [u8; KEY_GENERATION_SEED_SIZE],
45) -> Result<(MLKEM768PrivateKey, MLKEM768PublicKey), anyhow::Error> {
46 let (sk, pk) = mlkem768::generate_key_pair(seed).into_parts();
47 let mlkem_encaps = MLKEM768PublicKey::from_bytes(*pk.as_slice());
48 let mlkem_decaps = MLKEM768PrivateKey::from_bytes(*sk.as_slice());
49
50 Ok((mlkem_decaps, mlkem_encaps))
51}
52
53pub(crate) fn deterministic_keygen(
56 randomness: [u8; KEY_GENERATION_SEED_SIZE],
57) -> Result<(MLKEM768PrivateKey, MLKEM768PublicKey), anyhow::Error> {
58 use crate::primitives::provider::kem::{Algorithm, key_gen_derand};
59
60 let (sk, pk) = key_gen_derand(Algorithm::MlKem768, &randomness)
62 .map_err(|e| anyhow::anyhow!("MLKEM-768 deterministic key generation failed: {:?}", e))?;
63
64 typed(sk, pk)
65}
66
67#[cfg_attr(hax, hax_lib::fstar::verification_status(lax))]
68fn typed(
70 sk: PrivateKey,
71 pk: PublicKey,
72) -> Result<(MLKEM768PrivateKey, MLKEM768PublicKey), anyhow::Error> {
73 let private_key_bytes = sk.encode();
75 let public_key_bytes = pk.encode();
76
77 if private_key_bytes.len() != MLKEM768_PRIVATE_KEY_LEN
79 || public_key_bytes.len() != MLKEM768_PUBLIC_KEY_LEN
80 {
81 return Err(anyhow::anyhow!(
92 "Unexpected MLKEM-768 key sizes: private={}, public={}",
93 private_key_bytes.len(),
94 public_key_bytes.len()
95 ));
96 }
97
98 let private_key = MLKEM768PrivateKey::from_bytes(
99 private_key_bytes
100 .as_slice()
101 .try_into()
102 .map_err(|_| anyhow::anyhow!("Failed to convert private key bytes"))?,
103 );
104 let public_key = MLKEM768PublicKey::from_bytes(
105 public_key_bytes
106 .as_slice()
107 .try_into()
108 .map_err(|_| anyhow::anyhow!("Failed to convert public key bytes"))?,
109 );
110
111 Ok((private_key, public_key))
112}
113
114pub(crate) fn generate_mlkem768_keypair<R: RngCore + CryptoRng>(
116 rng: &mut R,
117) -> Result<(MLKEM768PrivateKey, MLKEM768PublicKey), anyhow::Error> {
118 use crate::primitives::provider::kem::{Algorithm, key_gen};
119
120 let (sk, pk) = key_gen(Algorithm::MlKem768, rng)
122 .map_err(|e| anyhow::anyhow!("MLKEM-768 key generation failed: {:?}", e))?;
123
124 typed(sk, pk)
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130 use rand_chacha::ChaCha20Rng;
131 use rand_core::SeedableRng;
132
133 #[test]
134 fn test_mlkem768_key_generation() {
135 let mut rng = ChaCha20Rng::seed_from_u64(42);
136
137 let (private_key, public_key) =
138 generate_mlkem768_keypair(&mut rng).expect("Should generate MLKEM-768 keypair");
139
140 assert_eq!(private_key.as_bytes().len(), MLKEM768_PRIVATE_KEY_LEN);
142 assert_eq!(public_key.as_bytes().len(), MLKEM768_PUBLIC_KEY_LEN);
143
144 assert_ne!(private_key.as_bytes().len(), public_key.as_bytes().len());
146 }
147
148 #[test]
149 fn test_mlkem768_key_serialization() {
150 let mut rng = ChaCha20Rng::seed_from_u64(42);
151
152 let (private_key, public_key) =
153 generate_mlkem768_keypair(&mut rng).expect("Should generate MLKEM-768 keypair");
154
155 let private_bytes = *private_key.as_bytes();
157 let public_bytes = *public_key.as_bytes();
158
159 let reconstructed_private = MLKEM768PrivateKey::from_bytes(private_bytes);
160 let reconstructed_public = MLKEM768PublicKey::from_bytes(public_bytes);
161
162 assert_eq!(private_key.as_bytes(), reconstructed_private.as_bytes());
163 assert_eq!(public_key.as_bytes(), reconstructed_public.as_bytes());
164 }
165}