1use rustls::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup};
11use rustls::{Error as RustlsError, NamedGroup, PeerMisbehaved};
12
13use ferritls_core::ecdh::{p256, p384, x25519};
14use ferritls_core::mlkem;
15
16fn map_dh_err(_: ferritls_core::Error) -> RustlsError {
18 RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare)
19}
20
21#[derive(Debug)]
23pub struct X25519;
24
25#[derive(Debug)]
27pub struct SecP256R1;
28
29#[derive(Debug)]
31pub struct SecP384R1;
32
33impl SupportedKxGroup for X25519 {
34 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
35 let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
36 let pk = sk.public_key();
37 Ok(Box::new(ActiveX25519 { sk, pk }))
38 }
39
40 fn name(&self) -> NamedGroup {
41 NamedGroup::X25519
42 }
43
44 fn fips(&self) -> bool {
45 false
47 }
48}
49
50impl SupportedKxGroup for SecP256R1 {
51 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
52 let sk = p256::SecretKey::generate().map_err(map_dh_err)?;
53 let pk = sk.public_key();
54 Ok(Box::new(ActiveSecP256R1 { sk, pk }))
55 }
56
57 fn name(&self) -> NamedGroup {
58 NamedGroup::secp256r1
59 }
60
61 fn fips(&self) -> bool {
62 false
64 }
65}
66
67impl SupportedKxGroup for SecP384R1 {
68 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
69 let sk = p384::SecretKey::generate().map_err(map_dh_err)?;
70 let pk = sk.public_key();
71 Ok(Box::new(ActiveSecP384R1 { sk, pk }))
72 }
73
74 fn name(&self) -> NamedGroup {
75 NamedGroup::secp384r1
76 }
77
78 fn fips(&self) -> bool {
79 false
80 }
81}
82
83pub(crate) struct ActiveX25519 {
85 sk: x25519::SecretKey,
86 pk: [u8; 32],
87}
88
89pub(crate) struct ActiveSecP256R1 {
91 sk: p256::SecretKey,
92 pk: [u8; p256::PUBLIC_KEY_LEN],
93}
94
95pub(crate) struct ActiveSecP384R1 {
97 sk: p384::SecretKey,
98 pk: [u8; p384::PUBLIC_KEY_LEN],
99}
100
101impl std::fmt::Debug for ActiveX25519 {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 f.write_str("ActiveX25519")
104 }
105}
106
107impl std::fmt::Debug for ActiveSecP256R1 {
108 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109 f.write_str("ActiveSecP256R1")
110 }
111}
112
113impl std::fmt::Debug for ActiveSecP384R1 {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 f.write_str("ActiveSecP384R1")
116 }
117}
118
119impl ActiveKeyExchange for ActiveX25519 {
120 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
121 let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
122 Ok(SharedSecret::from(ss.as_bytes().to_vec()))
123 }
124
125 fn pub_key(&self) -> &[u8] {
126 &self.pk
127 }
128
129 fn group(&self) -> NamedGroup {
130 NamedGroup::X25519
131 }
132}
133
134impl ActiveKeyExchange for ActiveSecP256R1 {
135 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
136 let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
137 Ok(SharedSecret::from(ss.as_bytes().to_vec()))
138 }
139
140 fn pub_key(&self) -> &[u8] {
141 &self.pk
142 }
143
144 fn group(&self) -> NamedGroup {
145 NamedGroup::secp256r1
146 }
147}
148
149impl ActiveKeyExchange for ActiveSecP384R1 {
150 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
151 let ss = self.sk.diffie_hellman(peer_pub_key).map_err(map_dh_err)?;
152 Ok(SharedSecret::from(ss.as_bytes().to_vec()))
153 }
154
155 fn pub_key(&self) -> &[u8] {
156 &self.pk
157 }
158
159 fn group(&self) -> NamedGroup {
160 NamedGroup::secp384r1
161 }
162}
163
164#[derive(Debug)]
178pub struct X25519Mlkem768;
179
180pub(crate) struct ActiveX25519Mlkem768 {
182 dk: mlkem::Mlkem768DecapsKey,
183 sk: x25519::SecretKey,
184 share: Vec<u8>,
185}
186
187impl SupportedKxGroup for X25519Mlkem768 {
188 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, RustlsError> {
189 let (ek, dk) = mlkem::generate_keypair().map_err(map_dh_err)?;
190 let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
191 let mut share = Vec::with_capacity(mlkem::EK_BYTES + 32);
192 share.extend_from_slice(ek.as_bytes());
193 share.extend_from_slice(&sk.public_key());
194 Ok(Box::new(ActiveX25519Mlkem768 { dk, sk, share }))
195 }
196
197 fn start_and_complete(&self, peer_pub_key: &[u8]) -> Result<CompletedKeyExchange, RustlsError> {
198 let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
200 if peer_pub_key.len() != mlkem::EK_BYTES + 32 {
201 return Err(invalid());
202 }
203 let ek = mlkem::Mlkem768EncapsKey::from_bytes(&peer_pub_key[..mlkem::EK_BYTES])
205 .map_err(|_| invalid())?;
206 let (ct, ss_m) = mlkem::encapsulate(&ek).map_err(map_dh_err)?;
207 let sk = x25519::SecretKey::generate().map_err(map_dh_err)?;
208 let ss_x = self_x25519(&sk, &peer_pub_key[mlkem::EK_BYTES..])?;
209
210 let mut secret = Vec::with_capacity(mlkem::SS_BYTES + 32);
211 secret.extend_from_slice(ss_m.expose_bytes());
212 secret.extend_from_slice(ss_x.as_bytes());
213 let mut pub_key = Vec::with_capacity(mlkem::CT_BYTES + 32);
214 pub_key.extend_from_slice(ct.as_bytes());
215 pub_key.extend_from_slice(&sk.public_key());
216 Ok(CompletedKeyExchange {
217 group: NamedGroup::X25519MLKEM768,
218 pub_key,
219 secret: SharedSecret::from(secret),
220 })
221 }
222
223 fn name(&self) -> NamedGroup {
224 NamedGroup::X25519MLKEM768
225 }
226
227 fn fips(&self) -> bool {
228 false
230 }
231}
232
233impl ActiveKeyExchange for ActiveX25519Mlkem768 {
234 fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
235 let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
238 if peer_pub_key.len() != mlkem::CT_BYTES + 32 {
239 return Err(invalid());
240 }
241 let ct = mlkem::Mlkem768Ciphertext::from_bytes(&peer_pub_key[..mlkem::CT_BYTES])
242 .map_err(|_| invalid())?;
243 let ss_m = mlkem::decapsulate(&self.dk, &ct);
244 let ss_x = self_x25519(&self.sk, &peer_pub_key[mlkem::CT_BYTES..])?;
245
246 let mut secret = Vec::with_capacity(mlkem::SS_BYTES + 32);
247 secret.extend_from_slice(ss_m.expose_bytes());
248 secret.extend_from_slice(ss_x.as_bytes());
249 Ok(SharedSecret::from(secret))
250 }
251
252 fn pub_key(&self) -> &[u8] {
253 &self.share
254 }
255
256 fn group(&self) -> NamedGroup {
257 NamedGroup::X25519MLKEM768
258 }
259}
260
261fn self_x25519(sk: &x25519::SecretKey, peer: &[u8]) -> Result<x25519::SharedSecret, RustlsError> {
262 sk.diffie_hellman(peer).map_err(map_dh_err)
263}
264
265pub static X25519_GROUP: &dyn SupportedKxGroup = &X25519;
267pub static SECP256R1_GROUP: &dyn SupportedKxGroup = &SecP256R1;
269pub static SECP384R1_GROUP: &dyn SupportedKxGroup = &SecP384R1;
271
272pub static X25519MLKEM768_GROUP: &dyn SupportedKxGroup = &X25519Mlkem768;
275
276pub static ALL_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
279 &[&X25519Mlkem768, &X25519, &SecP256R1, &SecP384R1];
280
281pub static FIPS_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
284 &[&X25519Mlkem768, &SecP256R1, &SecP384R1];