Skip to main content

ferritls_rustls/
kx.rs

1//! 密钥交换组:`SupportedKxGroup` / `ActiveKeyExchange` 适配。
2//!
3//! X25519 = 非批准;secp256r1/secp384r1 = 批准(但认证前 `fips()` 恒
4//! `false`,见 lib.rs“fips() 语义”)。
5//!
6//! 私钥由 ferritls-core 的 OS 熵直读生成(批准模式的 DRBG 路径由
7//! `CryptoProvider::secure_random` 与密钥生成入口的自检守卫覆盖;
8//! ECDH 私钥生成熵需求为曲线阶长,风险敞口极小)。
9
10use 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
16/// 把 core 的共享秘密错误映射为 rustls 的 InvalidKeyShare。
17fn map_dh_err(_: ferritls_core::Error) -> RustlsError {
18    RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare)
19}
20
21/// X25519(RFC 7748)密钥交换组。
22#[derive(Debug)]
23pub struct X25519;
24
25/// secp256r1(NIST P-256)密钥交换组。
26#[derive(Debug)]
27pub struct SecP256R1;
28
29/// secp384r1(NIST P-384)密钥交换组。
30#[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        // X25519 独立使用为非批准算法;即便认证后也不会在批准模式提供。
46        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        // 认证(阶段 C)落地前恒 false。
63        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
83/// 进行中的 X25519 密钥交换。
84pub(crate) struct ActiveX25519 {
85    sk: x25519::SecretKey,
86    pk: [u8; 32],
87}
88
89/// 进行中的 P-256 密钥交换。
90pub(crate) struct ActiveSecP256R1 {
91    sk: p256::SecretKey,
92    pk: [u8; p256::PUBLIC_KEY_LEN],
93}
94
95/// 进行中的 P-384 密钥交换。
96pub(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/// X25519MLKEM768(draft-ietf-tls-ecdhe-mlkem,codepoint 0x11EC)混合
165/// 密钥交换组(M8.3)。
166///
167/// 角色与线格式(TLS 1.3,密钥交换数据依赖 ⇒ KEM 角色**不对称**):
168/// - 客户端 = ML-KEM **解封装方**:`start()` 生成 (ek, dk) + X25519
169///   ephemeral,share = ek(1184)‖X25519 pk(32) = 1216 B;
170/// - 服务端 = ML-KEM **封装方**:走 [`SupportedKxGroup::start_and_complete`]
171///   覆写——对客户端 ek 先做 FIPS 203 §7.2 封装密钥检查再封装,
172///   share = ct(1088)‖X25519 pk(32) = 1120 B;
173/// - 共享秘密 = ML-KEM ss(32) ‖ X25519 ss(32) = 64 B。
174///
175/// X25519 独立使用为非批准算法;本混合组是其进入批准模式配置的
176/// 通道(SP 800-52r2 口径)。CMVP 认证前 `fips()` 恒 `false`。
177#[derive(Debug)]
178pub struct X25519Mlkem768;
179
180/// 进行中的 X25519MLKEM768 客户端侧密钥交换(持有解封装密钥)。
181pub(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        // 服务端:peer share = ek(1184) ‖ X25519 pk(32)
199        let invalid = || RustlsError::PeerMisbehaved(PeerMisbehaved::InvalidKeyShare);
200        if peer_pub_key.len() != mlkem::EK_BYTES + 32 {
201            return Err(invalid());
202        }
203        // FIPS 203 §7.2 封装密钥检查(含模校验)在此发生
204        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        // 认证(阶段 C)落地前恒 false(AGENTS.md 规则 3)。
229        false
230    }
231}
232
233impl ActiveKeyExchange for ActiveX25519Mlkem768 {
234    fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, RustlsError> {
235        // 客户端:服务端 share = ct(1088) ‖ X25519 pk(32);密文长度
236        // 不符必须中止(draft-ietf-tls-ecdhe-mlkem §3)。
237        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
265/// X25519 组单例。
266pub static X25519_GROUP: &dyn SupportedKxGroup = &X25519;
267/// P-256 组单例。
268pub static SECP256R1_GROUP: &dyn SupportedKxGroup = &SecP256R1;
269/// P-384 组单例。
270pub static SECP384R1_GROUP: &dyn SupportedKxGroup = &SecP384R1;
271
272/// X25519MLKEM768 混合组单例(默认列表首项:PQ 优先,同主流
273/// provider/浏览器实践;1216 B ClientHello share)。
274pub static X25519MLKEM768_GROUP: &dyn SupportedKxGroup = &X25519Mlkem768;
275
276/// 默认(非批准模式)密钥交换组清单;顺序即偏好,首项为 TLS 1.3
277/// 默认 key share(X25519MLKEM768 混合优先,其后经典 X25519)。
278pub static ALL_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
279    &[&X25519Mlkem768, &X25519, &SecP256R1, &SecP384R1];
280
281/// 批准模式密钥交换组清单:混合组(X25519 进批准模式的通道)+
282/// 经典 P-256/P-384;无独立 X25519(SP 800-52r2 口径)。
283pub static FIPS_KX_GROUPS: &[&'static dyn SupportedKxGroup] =
284    &[&X25519Mlkem768, &SecP256R1, &SecP384R1];