Skip to main content

ferritls_rustls/
cipher.rs

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