Skip to main content

ferritls_rustls/
cipher.rs

1//! TLS 1.3 密码套件装配(M6;QUIC 接线 M8.5)。
2//!
3//! HKDF 复用 rustls 内建 [`rustls::crypto::tls13::HkdfUsingHmac`],包装
4//! 本 crate 实现的 [`rustls::crypto::hmac::Hmac`](不在边界内重写密钥
5//! 调度);AEAD 为 ferritls-core 的 GCM/CCM/ChaCha20-Poly1305 适配。
6//!
7//! QUIC:AES-128-GCM / AES-256-GCM / ChaCha20-Poly1305 三套件经
8//! [`crate::quic`] 的 `quic::Algorithm` 参与 QUIC 包保护;CCM(含
9//! CCM_8)保持 `None`(RFC 9001 §5.1 以 AES-GCM 为强制基准,
10//! `ConnectionTrafficSecrets` 亦无 CCM 变体)。
11//!
12//! 套件面(5):批准模式 3 套件(GCM×2 + CCM,无 ChaCha/CCM_8);
13//! 默认模式另含 ChaCha20-Poly1305 与 `TLS_AES_128_CCM_8_SHA256`
14//! (8 字节标签不在 SP 800-52r2 TLS 批准套件面,见 [`ferritls_core::policy`])。
15
16use rustls::crypto::CipherSuiteCommon;
17use rustls::crypto::cipher::{
18    AeadKey, InboundOpaqueMessage, InboundPlainMessage, Iv, MessageDecrypter, MessageEncrypter,
19    Nonce, PrefixedPayload, Tls13AeadAlgorithm, UnsupportedOperationError, make_tls13_aad,
20};
21use rustls::crypto::hash::{Context, Hash, HashAlgorithm, Output};
22use rustls::crypto::hmac::{self, Hmac, Key};
23use rustls::crypto::tls13::HkdfUsingHmac;
24use rustls::{
25    ConnectionTrafficSecrets, ContentType, Error, ProtocolVersion, SupportedCipherSuite,
26    Tls13CipherSuite,
27};
28
29use ferritls_core::chacha20poly1305::ChaCha20Poly1305;
30use ferritls_core::gcm::{Aes128Gcm, Aes256Gcm};
31
32use ferritls_core::ccm::Aes128CcmAny;
33
34/// 套件清单(顺序即偏好)。同时被 `tests/api.rs` 断言,防止清单与
35/// 文档漂移。CCM_8 仅默认模式(批准模式清单见 `fips_tls13_suites`)。
36pub const TLS13_SUITE_NAMES: &[&str] = &[
37    "TLS_AES_128_GCM_SHA256",
38    "TLS_AES_256_GCM_SHA384",
39    "TLS_CHACHA20_POLY1305_SHA256",
40    "TLS_AES_128_CCM_SHA256",
41    "TLS_AES_128_CCM_8_SHA256",
42];
43
44// ---------------------------------------------------------------------------
45// Hash 适配(SHA-256 / SHA-384)
46// ---------------------------------------------------------------------------
47
48#[derive(Debug)]
49struct Sha256Hash;
50
51struct Sha256Ctx(ferritls_core::sha2::Sha256);
52
53impl Context for Sha256Ctx {
54    fn fork_finish(&self) -> Output {
55        Output::new(&self.0.clone().finalize())
56    }
57
58    fn fork(&self) -> Box<dyn Context> {
59        Box::new(Self(self.0.clone()))
60    }
61
62    fn finish(self: Box<Self>) -> Output {
63        Output::new(&self.0.finalize())
64    }
65
66    fn update(&mut self, data: &[u8]) {
67        self.0.update(data);
68    }
69}
70
71impl Hash for Sha256Hash {
72    fn start(&self) -> Box<dyn Context> {
73        Box::new(Sha256Ctx(ferritls_core::sha2::Sha256::new()))
74    }
75
76    fn hash(&self, data: &[u8]) -> Output {
77        Output::new(&ferritls_core::sha2::Sha256::one_shot(data))
78    }
79
80    fn output_len(&self) -> usize {
81        32
82    }
83
84    fn algorithm(&self) -> HashAlgorithm {
85        HashAlgorithm::SHA256
86    }
87}
88
89#[derive(Debug)]
90struct Sha384Hash;
91
92struct Sha384Ctx(ferritls_core::sha2::Sha384);
93
94impl Context for Sha384Ctx {
95    fn fork_finish(&self) -> Output {
96        Output::new(&self.0.clone().finalize())
97    }
98
99    fn fork(&self) -> Box<dyn Context> {
100        Box::new(Self(self.0.clone()))
101    }
102
103    fn finish(self: Box<Self>) -> Output {
104        Output::new(&self.0.finalize())
105    }
106
107    fn update(&mut self, data: &[u8]) {
108        self.0.update(data);
109    }
110}
111
112impl Hash for Sha384Hash {
113    fn start(&self) -> Box<dyn Context> {
114        Box::new(Sha384Ctx(ferritls_core::sha2::Sha384::new()))
115    }
116
117    fn hash(&self, data: &[u8]) -> Output {
118        Output::new(&ferritls_core::sha2::Sha384::one_shot(data))
119    }
120
121    fn output_len(&self) -> usize {
122        48
123    }
124
125    fn algorithm(&self) -> HashAlgorithm {
126        HashAlgorithm::SHA384
127    }
128}
129
130pub(crate) static SHA256_HASH: &dyn Hash = &Sha256Hash;
131pub(crate) static SHA384_HASH: &dyn Hash = &Sha384Hash;
132
133// ---------------------------------------------------------------------------
134// Hmac 适配(rustls::crypto::hmac;供 HkdfUsingHmac 复用)
135// ---------------------------------------------------------------------------
136
137#[derive(Debug)]
138struct HmacSha256Impl;
139
140#[derive(Debug)]
141struct HmacSha256Key(Vec<u8>);
142
143impl Key for HmacSha256Key {
144    fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> hmac::Tag {
145        let mut buf = Vec::with_capacity(first.len() + 16 * middle.len() + last.len());
146        buf.extend_from_slice(first);
147        for m in middle {
148            buf.extend_from_slice(m);
149        }
150        buf.extend_from_slice(last);
151        hmac::Tag::new(&ferritls_core::hmac::HmacSha256::one_shot(&self.0, &buf))
152    }
153
154    fn tag_len(&self) -> usize {
155        32
156    }
157}
158
159impl Hmac for HmacSha256Impl {
160    fn with_key(&self, key: &[u8]) -> Box<dyn Key> {
161        Box::new(HmacSha256Key(key.to_vec()))
162    }
163
164    fn hash_output_len(&self) -> usize {
165        32
166    }
167}
168
169#[derive(Debug)]
170struct HmacSha384Impl;
171
172#[derive(Debug)]
173struct HmacSha384Key(Vec<u8>);
174
175impl Key for HmacSha384Key {
176    fn sign_concat(&self, first: &[u8], middle: &[&[u8]], last: &[u8]) -> hmac::Tag {
177        let mut buf = Vec::with_capacity(first.len() + 16 * middle.len() + last.len());
178        buf.extend_from_slice(first);
179        for m in middle {
180            buf.extend_from_slice(m);
181        }
182        buf.extend_from_slice(last);
183        hmac::Tag::new(&ferritls_core::hmac::HmacSha384::one_shot(&self.0, &buf))
184    }
185
186    fn tag_len(&self) -> usize {
187        48
188    }
189}
190
191impl Hmac for HmacSha384Impl {
192    fn with_key(&self, key: &[u8]) -> Box<dyn Key> {
193        Box::new(HmacSha384Key(key.to_vec()))
194    }
195
196    fn hash_output_len(&self) -> usize {
197        48
198    }
199}
200
201static HMAC_SHA256_IMPL: HmacSha256Impl = HmacSha256Impl;
202static HMAC_SHA384_IMPL: HmacSha384Impl = HmacSha384Impl;
203static HKDF_USING_HMAC_SHA256: HkdfUsingHmac<'static> = HkdfUsingHmac(&HMAC_SHA256_IMPL);
204static HKDF_USING_HMAC_SHA384: HkdfUsingHmac<'static> = HkdfUsingHmac(&HMAC_SHA384_IMPL);
205
206pub(crate) static HKDF_SHA256_PROVIDER: &dyn rustls::crypto::tls13::Hkdf = &HKDF_USING_HMAC_SHA256;
207pub(crate) static HKDF_SHA384_PROVIDER: &dyn rustls::crypto::tls13::Hkdf = &HKDF_USING_HMAC_SHA384;
208
209// ---------------------------------------------------------------------------
210// AEAD 适配:GCM
211// ---------------------------------------------------------------------------
212
213const TAG_LEN: usize = 16;
214
215/// 从 `AeadKey` 提取定长密钥字节。
216pub(crate) fn key_bytes<const N: usize>(key: &AeadKey) -> [u8; N] {
217    let mut out = [0u8; N];
218    out.copy_from_slice(key.as_ref());
219    out
220}
221
222/// GCM 实例(按密钥长度选择 AES-128/256)。
223enum GcmInstance {
224    Aes128(Aes128Gcm),
225    Aes256(Aes256Gcm),
226}
227
228impl GcmInstance {
229    fn seal(&self, nonce: &Nonce, aad: &[u8], plaintext: &[u8]) -> Vec<u8> {
230        match self {
231            GcmInstance::Aes128(g) => g.seal(&nonce.0, aad, plaintext),
232            GcmInstance::Aes256(g) => g.seal(&nonce.0, aad, plaintext),
233        }
234    }
235
236    fn open(&self, nonce: &Nonce, aad: &[u8], ct_tag: &[u8]) -> Result<Vec<u8>, Error> {
237        match self {
238            GcmInstance::Aes128(g) => g.open(&nonce.0, aad, ct_tag),
239            GcmInstance::Aes256(g) => g.open(&nonce.0, aad, ct_tag),
240        }
241        .map_err(|_| Error::DecryptError)
242    }
243}
244
245fn gcm_of<const N: usize>(key: &AeadKey) -> GcmInstance {
246    if N == 32 {
247        GcmInstance::Aes256(Aes256Gcm::new(&key_bytes::<32>(key)))
248    } else {
249        GcmInstance::Aes128(Aes128Gcm::new(&key_bytes::<16>(key)))
250    }
251}
252
253struct GcmEncrypter<const N: usize> {
254    gcm: GcmInstance,
255    iv: Iv,
256    _n: std::marker::PhantomData<[u8; N]>,
257}
258
259struct GcmDecrypter<const N: usize> {
260    gcm: GcmInstance,
261    iv: Iv,
262    _n: std::marker::PhantomData<[u8; N]>,
263}
264
265impl<const N: usize> GcmEncrypter<N> {
266    fn seal(&self, nonce: &Nonce, aad: &[u8], plaintext: &[u8]) -> Vec<u8> {
267        self.gcm.seal(nonce, aad, plaintext)
268    }
269}
270
271impl<const N: usize> GcmDecrypter<N> {
272    fn open(&self, nonce: &Nonce, aad: &[u8], ct_tag: &[u8]) -> Result<Vec<u8>, Error> {
273        self.gcm.open(nonce, aad, ct_tag)
274    }
275}
276
277impl<const N: usize> MessageEncrypter for GcmEncrypter<N> {
278    fn encrypt(
279        &mut self,
280        msg: rustls::crypto::cipher::OutboundPlainMessage<'_>,
281        seq: u64,
282    ) -> Result<rustls::crypto::cipher::OutboundOpaqueMessage, Error> {
283        let total_len = self.encrypted_payload_len(msg.payload.len());
284        let nonce = Nonce::new(&self.iv, seq);
285        let aad = make_tls13_aad(total_len);
286
287        let mut plain = msg.payload.to_vec();
288        plain.push(msg.typ.to_array()[0]);
289        let sealed = self.seal(&nonce, &aad, &plain);
290
291        let mut payload = PrefixedPayload::with_capacity(total_len);
292        payload.extend_from_slice(&sealed);
293        Ok(rustls::crypto::cipher::OutboundOpaqueMessage::new(
294            ContentType::ApplicationData,
295            // RFC 8446 §5.1:TLS 1.3 应用数据记录沿用 legacy 版本 0x0303
296            ProtocolVersion::TLSv1_2,
297            payload,
298        ))
299    }
300
301    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
302        payload_len + 1 + TAG_LEN
303    }
304}
305
306impl<const N: usize> MessageDecrypter for GcmDecrypter<N> {
307    fn decrypt<'a>(
308        &mut self,
309        mut msg: InboundOpaqueMessage<'a>,
310        seq: u64,
311    ) -> Result<InboundPlainMessage<'a>, Error> {
312        let payload = &mut msg.payload;
313        if payload.len() < TAG_LEN + 1 {
314            return Err(Error::DecryptError);
315        }
316        let nonce = Nonce::new(&self.iv, seq);
317        let aad = make_tls13_aad(payload.len());
318        let plain = self
319            .open(&nonce, &aad, payload)
320            .map_err(|_| Error::DecryptError)?;
321        let plain_len = plain.len();
322        payload[..plain_len].copy_from_slice(&plain);
323        payload.truncate(plain_len);
324        msg.into_tls13_unpadded_message()
325    }
326}
327
328macro_rules! gcm_aead {
329    ($name:ident, $key_len:expr, $secret:ident, $doc:expr) => {
330        #[doc = $doc]
331        #[derive(Debug)]
332        pub struct $name;
333
334        impl Tls13AeadAlgorithm for $name {
335            fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> {
336                Box::new(GcmEncrypter::<$key_len> {
337                    gcm: gcm_of::<$key_len>(&key),
338                    iv,
339                    _n: std::marker::PhantomData,
340                })
341            }
342
343            fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> {
344                Box::new(GcmDecrypter::<$key_len> {
345                    gcm: gcm_of::<$key_len>(&key),
346                    iv,
347                    _n: std::marker::PhantomData,
348                })
349            }
350
351            fn key_len(&self) -> usize {
352                $key_len
353            }
354
355            fn extract_keys(
356                &self,
357                key: AeadKey,
358                iv: Iv,
359            ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
360                Ok(ConnectionTrafficSecrets::$secret { key, iv })
361            }
362        }
363    };
364}
365
366gcm_aead!(Gcm128Aead, 16, Aes128Gcm, "AES-128-GCM AEAD 适配(批准)。");
367gcm_aead!(Gcm256Aead, 32, Aes256Gcm, "AES-256-GCM AEAD 适配(批准)。");
368
369// ---------------------------------------------------------------------------
370// AEAD 适配:ChaCha20-Poly1305(非批准,仅默认模式)
371// ---------------------------------------------------------------------------
372
373#[derive(Debug)]
374pub struct Chacha20Poly1305Aead;
375
376struct ChachaEncrypter {
377    aead: ChaCha20Poly1305,
378    iv: Iv,
379}
380
381struct ChachaDecrypter {
382    aead: ChaCha20Poly1305,
383    iv: Iv,
384}
385
386impl MessageEncrypter for ChachaEncrypter {
387    fn encrypt(
388        &mut self,
389        msg: rustls::crypto::cipher::OutboundPlainMessage<'_>,
390        seq: u64,
391    ) -> Result<rustls::crypto::cipher::OutboundOpaqueMessage, Error> {
392        let total_len = self.encrypted_payload_len(msg.payload.len());
393        let nonce = Nonce::new(&self.iv, seq);
394        let aad = make_tls13_aad(total_len);
395
396        let mut plain = msg.payload.to_vec();
397        plain.push(msg.typ.to_array()[0]);
398        let sealed = self.aead.seal(&nonce.0, &aad, &plain);
399
400        let mut payload = PrefixedPayload::with_capacity(total_len);
401        payload.extend_from_slice(&sealed);
402        Ok(rustls::crypto::cipher::OutboundOpaqueMessage::new(
403            ContentType::ApplicationData,
404            ProtocolVersion::TLSv1_2,
405            payload,
406        ))
407    }
408
409    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
410        payload_len + 1 + TAG_LEN
411    }
412}
413
414impl MessageDecrypter for ChachaDecrypter {
415    fn decrypt<'a>(
416        &mut self,
417        mut msg: InboundOpaqueMessage<'a>,
418        seq: u64,
419    ) -> Result<InboundPlainMessage<'a>, Error> {
420        let payload = &mut msg.payload;
421        if payload.len() < TAG_LEN + 1 {
422            return Err(Error::DecryptError);
423        }
424        let nonce = Nonce::new(&self.iv, seq);
425        let aad = make_tls13_aad(payload.len());
426        let plain = self
427            .aead
428            .open(&nonce.0, &aad, payload)
429            .map_err(|_| Error::DecryptError)?;
430        let plain_len = plain.len();
431        payload[..plain_len].copy_from_slice(&plain);
432        payload.truncate(plain_len);
433        msg.into_tls13_unpadded_message()
434    }
435}
436
437impl Tls13AeadAlgorithm for Chacha20Poly1305Aead {
438    fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> {
439        Box::new(ChachaEncrypter {
440            aead: ChaCha20Poly1305::new(&key_bytes::<32>(&key)),
441            iv,
442        })
443    }
444
445    fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> {
446        Box::new(ChachaDecrypter {
447            aead: ChaCha20Poly1305::new(&key_bytes::<32>(&key)),
448            iv,
449        })
450    }
451
452    fn key_len(&self) -> usize {
453        32
454    }
455
456    fn extract_keys(
457        &self,
458        key: AeadKey,
459        iv: Iv,
460    ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
461        Ok(ConnectionTrafficSecrets::Chacha20Poly1305 { key, iv })
462    }
463}
464
465// ---------------------------------------------------------------------------
466// AEAD 适配:AES-128-CCM(TLS 1.3 两档标签长度:M=16 / M=8,
467// nonce 12 / L=3;标签长度为编译期常量、内核为 core 的运行时引擎)
468// ---------------------------------------------------------------------------
469
470/// CCM AEAD 适配(M=16,`TLS_AES_128_CCM_SHA256`,批准)。
471#[derive(Debug)]
472pub struct Ccm128Aead;
473
474/// CCM AEAD 适配(M=8,`TLS_AES_128_CCM_8_SHA256`;8 字节标签不在
475/// SP 800-52r2 TLS 批准套件面,仅默认模式装配)。
476#[derive(Debug)]
477pub struct Ccm8TlsAead;
478
479fn ccm_engine<const M: usize>(key: &AeadKey) -> Aes128CcmAny {
480    // M 为编译期常量,构造必不失败
481    Aes128CcmAny::new(&key_bytes::<16>(key), M).expect("fixed tag length")
482}
483
484struct CcmEncrypter<const M: usize> {
485    ccm: Aes128CcmAny,
486    iv: Iv,
487}
488
489struct CcmDecrypter<const M: usize> {
490    ccm: Aes128CcmAny,
491    iv: Iv,
492}
493
494impl<const M: usize> MessageEncrypter for CcmEncrypter<M> {
495    fn encrypt(
496        &mut self,
497        msg: rustls::crypto::cipher::OutboundPlainMessage<'_>,
498        seq: u64,
499    ) -> Result<rustls::crypto::cipher::OutboundOpaqueMessage, Error> {
500        let total_len = self.encrypted_payload_len(msg.payload.len());
501        let nonce = Nonce::new(&self.iv, seq);
502        let aad = make_tls13_aad(total_len);
503
504        let mut plain = msg.payload.to_vec();
505        plain.push(msg.typ.to_array()[0]);
506        let sealed = self
507            .ccm
508            .seal(&nonce.0, &aad, &plain)
509            .map_err(|_| Error::EncryptError)?;
510
511        let mut payload = PrefixedPayload::with_capacity(total_len);
512        payload.extend_from_slice(&sealed);
513        Ok(rustls::crypto::cipher::OutboundOpaqueMessage::new(
514            ContentType::ApplicationData,
515            ProtocolVersion::TLSv1_2,
516            payload,
517        ))
518    }
519
520    fn encrypted_payload_len(&self, payload_len: usize) -> usize {
521        payload_len + 1 + M
522    }
523}
524
525impl<const M: usize> MessageDecrypter for CcmDecrypter<M> {
526    fn decrypt<'a>(
527        &mut self,
528        mut msg: InboundOpaqueMessage<'a>,
529        seq: u64,
530    ) -> Result<InboundPlainMessage<'a>, Error> {
531        let payload = &mut msg.payload;
532        if payload.len() < M + 1 {
533            return Err(Error::DecryptError);
534        }
535        let nonce = Nonce::new(&self.iv, seq);
536        let aad = make_tls13_aad(payload.len());
537        let plain = self
538            .ccm
539            .open(&nonce.0, &aad, payload)
540            .map_err(|_| Error::DecryptError)?;
541        let plain_len = plain.len();
542        payload[..plain_len].copy_from_slice(&plain);
543        payload.truncate(plain_len);
544        msg.into_tls13_unpadded_message()
545    }
546}
547
548/// 两档标签长度的 `Tls13AeadAlgorithm` 完全同构,仅 M 常量不同。
549macro_rules! ccm_aead_impl {
550    ($name:ident, $tag_len:expr) => {
551        impl Tls13AeadAlgorithm for $name {
552            fn encrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageEncrypter> {
553                Box::new(CcmEncrypter::<$tag_len> {
554                    ccm: ccm_engine::<$tag_len>(&key),
555                    iv,
556                })
557            }
558
559            fn decrypter(&self, key: AeadKey, iv: Iv) -> Box<dyn MessageDecrypter> {
560                Box::new(CcmDecrypter::<$tag_len> {
561                    ccm: ccm_engine::<$tag_len>(&key),
562                    iv,
563                })
564            }
565
566            fn key_len(&self) -> usize {
567                16
568            }
569
570            fn extract_keys(
571                &self,
572                _key: AeadKey,
573                _iv: Iv,
574            ) -> Result<ConnectionTrafficSecrets, UnsupportedOperationError> {
575                // ConnectionTrafficSecrets 无 CCM 变体:导出 traffic secrets(key
576                // exporter / key log)对 CCM 套件不可用——与 AGENTS.md §4 记载一致
577                Err(UnsupportedOperationError)
578            }
579        }
580    };
581}
582
583ccm_aead_impl!(Ccm128Aead, 16);
584ccm_aead_impl!(Ccm8TlsAead, 8);
585
586// ---------------------------------------------------------------------------
587// 套件静态表与装配
588// ---------------------------------------------------------------------------
589
590pub(crate) static GCM128_AEAD: Gcm128Aead = Gcm128Aead;
591pub(crate) static GCM256_AEAD: Gcm256Aead = Gcm256Aead;
592pub(crate) static CHACHA_AEAD: Chacha20Poly1305Aead = Chacha20Poly1305Aead;
593static CCM128_AEAD: Ccm128Aead = Ccm128Aead;
594static CCM8_AEAD: Ccm8TlsAead = Ccm8TlsAead;
595
596pub(crate) static TLS13_AES_128_GCM_SHA256: Tls13CipherSuite = Tls13CipherSuite {
597    common: CipherSuiteCommon {
598        suite: rustls::CipherSuite::TLS13_AES_128_GCM_SHA256,
599        hash_provider: SHA256_HASH,
600        // draft-irtf-aead-limits-08 §5.1.1(与 rustls ring provider 一致)
601        confidentiality_limit: 1 << 24,
602    },
603    hkdf_provider: HKDF_SHA256_PROVIDER,
604    aead_alg: &GCM128_AEAD,
605    quic: Some(&crate::quic::QUIC_AES_128_GCM),
606};
607
608pub(crate) static TLS13_AES_256_GCM_SHA384: Tls13CipherSuite = Tls13CipherSuite {
609    common: CipherSuiteCommon {
610        suite: rustls::CipherSuite::TLS13_AES_256_GCM_SHA384,
611        hash_provider: SHA384_HASH,
612        confidentiality_limit: 1 << 24,
613    },
614    hkdf_provider: HKDF_SHA384_PROVIDER,
615    aead_alg: &GCM256_AEAD,
616    quic: Some(&crate::quic::QUIC_AES_256_GCM),
617};
618
619pub(crate) static TLS13_CHACHA20_POLY1305_SHA256: Tls13CipherSuite = Tls13CipherSuite {
620    common: CipherSuiteCommon {
621        suite: rustls::CipherSuite::TLS13_CHACHA20_POLY1305_SHA256,
622        hash_provider: SHA256_HASH,
623        // draft-irtf-aead-limits-08 §5.2.1
624        confidentiality_limit: u64::MAX,
625    },
626    hkdf_provider: HKDF_SHA256_PROVIDER,
627    aead_alg: &CHACHA_AEAD,
628    quic: Some(&crate::quic::QUIC_CHACHA20_POLY1305),
629};
630
631static TLS13_AES_128_CCM_SHA256: Tls13CipherSuite = Tls13CipherSuite {
632    common: CipherSuiteCommon {
633        suite: rustls::CipherSuite::TLS13_AES_128_CCM_SHA256,
634        hash_provider: SHA256_HASH,
635        confidentiality_limit: 1 << 23,
636    },
637    hkdf_provider: HKDF_SHA256_PROVIDER,
638    aead_alg: &CCM128_AEAD,
639    // CCM 不参与 QUIC:RFC 9001 §5.1 以 AES-GCM 为强制基准(ring 同),
640    // ConnectionTrafficSecrets 亦无 CCM 变体。
641    quic: None,
642};
643
644static TLS13_AES_128_CCM_8_SHA256: Tls13CipherSuite = Tls13CipherSuite {
645    common: CipherSuiteCommon {
646        suite: rustls::CipherSuite::TLS13_AES_128_CCM_8_SHA256,
647        hash_provider: SHA256_HASH,
648        // 与 AES_128_CCM 同源(draft-irtf-aead-limits-08 §5.3)
649        confidentiality_limit: 1 << 23,
650    },
651    hkdf_provider: HKDF_SHA256_PROVIDER,
652    aead_alg: &CCM8_AEAD,
653    // CCM(含 CCM_8)不参与 QUIC,理由同上。
654    quic: None,
655};
656
657/// `TLS_AES_128_GCM_SHA256`(批准)。
658pub fn tls13_aes_128_gcm_sha256() -> SupportedCipherSuite {
659    SupportedCipherSuite::Tls13(&TLS13_AES_128_GCM_SHA256)
660}
661
662/// `TLS_AES_256_GCM_SHA384`(批准)。
663pub fn tls13_aes_256_gcm_sha384() -> SupportedCipherSuite {
664    SupportedCipherSuite::Tls13(&TLS13_AES_256_GCM_SHA384)
665}
666
667/// `TLS_CHACHA20_POLY1305_SHA256`(非批准,仅默认模式)。
668pub fn tls13_chacha20_poly1305_sha256() -> SupportedCipherSuite {
669    SupportedCipherSuite::Tls13(&TLS13_CHACHA20_POLY1305_SHA256)
670}
671
672/// `TLS_AES_128_CCM_SHA256`(批准,SP 800-52r2 面向受限环境)。
673pub fn tls13_aes_128_ccm_sha256() -> SupportedCipherSuite {
674    SupportedCipherSuite::Tls13(&TLS13_AES_128_CCM_SHA256)
675}
676
677/// `TLS_AES_128_CCM_8_SHA256`(非批准 TLS 套件面:8 字节标签不在
678/// SP 800-52r2 批准清单,仅默认模式装配)。
679pub fn tls13_aes_128_ccm_8_sha256() -> SupportedCipherSuite {
680    SupportedCipherSuite::Tls13(&TLS13_AES_128_CCM_8_SHA256)
681}
682
683/// 默认模式全部套件(偏好序)。
684pub fn all_tls13_suites() -> Vec<SupportedCipherSuite> {
685    vec![
686        tls13_aes_128_gcm_sha256(),
687        tls13_aes_256_gcm_sha384(),
688        tls13_chacha20_poly1305_sha256(),
689        tls13_aes_128_ccm_sha256(),
690        tls13_aes_128_ccm_8_sha256(),
691    ]
692}
693
694/// 批准模式套件(无 ChaCha20-Poly1305)。
695pub fn fips_tls13_suites() -> Vec<SupportedCipherSuite> {
696    vec![
697        tls13_aes_128_gcm_sha256(),
698        tls13_aes_256_gcm_sha384(),
699        tls13_aes_128_ccm_sha256(),
700    ]
701}