mls_rs_crypto_awslc/
lib.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// Copyright by contributors to this project.
3// SPDX-License-Identifier: (Apache-2.0 OR MIT)
4
5mod aead;
6mod ec;
7mod ecdsa;
8mod hmac;
9mod kdf;
10mod kem;
11
12pub mod x509;
13
14#[cfg(feature = "fips")]
15use aws_lc_fips_sys as aws_lc_sys_impl;
16
17#[cfg(not(feature = "fips"))]
18use aws_lc_sys as aws_lc_sys_impl;
19pub use hmac::AwsLcHmac;
20
21use std::{ffi::c_int, mem::MaybeUninit, num::TryFromIntError};
22
23pub use aead::AwsLcAead;
24use aws_lc_rs::error::{KeyRejected, Unspecified};
25
26use crate::aws_lc_sys_impl::SHA256;
27use mls_rs_core::{
28    crypto::{
29        CipherSuite, CipherSuiteProvider, CryptoProvider, HpkeCiphertext, HpkePublicKey,
30        HpkeSecretKey, SignaturePublicKey, SignatureSecretKey,
31    },
32    error::{AnyError, IntoAnyError},
33};
34
35pub use ecdsa::AwsLcEcdsa;
36pub use kdf::AwsLcHkdf;
37pub use kem::ecdh::Ecdh;
38use mls_rs_crypto_hpke::{
39    context::{ContextR, ContextS},
40    dhkem::DhKem,
41    hpke::{Hpke, HpkeError},
42};
43use mls_rs_crypto_traits::{AeadId, AeadType, Curve, Hash, KdfId, KdfType, KemId};
44use thiserror::Error;
45use zeroize::Zeroizing;
46
47#[cfg(feature = "post-quantum")]
48use self::kdf::shake::AwsLcShake128;
49
50#[cfg(feature = "post-quantum")]
51use mls_rs_crypto_hpke::kem_combiner::xwing::{CombinedKem, XWingSharedSecretHashInput};
52
53#[cfg(feature = "post-quantum")]
54pub use self::kem::ml_kem::{MlKem, MlKemKem};
55
56#[cfg(feature = "post-quantum")]
57pub use self::kdf::Sha3;
58
59pub use self::kdf::AwsLcHash;
60
61#[derive(Clone)]
62pub struct AwsLcCipherSuite {
63    cipher_suite: CipherSuite,
64    signing: AwsLcEcdsa,
65    aead: AwsLcAead,
66    kdf: AwsLcHkdf,
67    hpke: AwsLcHpke,
68    hmac: AwsLcHmac,
69    hash: AwsLcHash,
70}
71
72pub type EcdhKem = DhKem<Ecdh, AwsLcHkdf>;
73
74#[cfg(feature = "post-quantum")]
75pub type CombinedEcdhMlKemKem =
76    CombinedKem<MlKemKem, EcdhKem, AwsLcHash, AwsLcShake128, XWingSharedSecretHashInput>;
77
78#[derive(Clone)]
79#[non_exhaustive]
80enum AwsLcHpke {
81    Classical(Hpke<EcdhKem, AwsLcHkdf, AwsLcAead>),
82    #[cfg(feature = "post-quantum")]
83    PostQuantum(Hpke<MlKemKem, AwsLcHkdf, AwsLcAead>),
84    #[cfg(feature = "post-quantum")]
85    Combined(Hpke<CombinedEcdhMlKemKem, AwsLcHkdf, AwsLcAead>),
86}
87
88impl AwsLcCipherSuite {
89    pub fn import_ec_der_private_key(
90        &self,
91        bytes: &[u8],
92    ) -> Result<SignatureSecretKey, AwsLcCryptoError> {
93        self.signing.import_ec_der_private_key(bytes)
94    }
95
96    pub fn import_ec_der_public_key(
97        &self,
98        bytes: &[u8],
99    ) -> Result<SignaturePublicKey, AwsLcCryptoError> {
100        self.signing.import_ec_der_public_key(bytes)
101    }
102}
103
104#[derive(Clone, Debug)]
105pub struct AwsLcCryptoProvider {
106    pub enabled_cipher_suites: Vec<CipherSuite>,
107}
108
109impl Default for AwsLcCryptoProvider {
110    fn default() -> Self {
111        Self::new()
112    }
113}
114
115impl AwsLcCryptoProvider {
116    pub fn new() -> Self {
117        Self {
118            enabled_cipher_suites: Self::all_supported_cipher_suites(),
119        }
120    }
121
122    pub fn all_supported_cipher_suites() -> Vec<CipherSuite> {
123        [
124            Self::supported_classical_cipher_suites(),
125            #[cfg(feature = "post-quantum")]
126            Self::supported_pq_cipher_suites(),
127        ]
128        .concat()
129    }
130
131    pub fn supported_classical_cipher_suites() -> Vec<CipherSuite> {
132        vec![
133            CipherSuite::CURVE25519_AES128,
134            CipherSuite::CURVE25519_CHACHA,
135            CipherSuite::P256_AES128,
136            CipherSuite::P384_AES256,
137            CipherSuite::P521_AES256,
138        ]
139    }
140
141    #[cfg(feature = "post-quantum")]
142    pub fn supported_pq_cipher_suites() -> Vec<CipherSuite> {
143        vec![
144            CipherSuite::ML_KEM_512,
145            CipherSuite::ML_KEM_768,
146            CipherSuite::ML_KEM_1024,
147            CipherSuite::ML_KEM_768_X25519,
148        ]
149    }
150}
151
152impl AwsLcCryptoProvider {
153    pub fn with_enabled_cipher_suites(enabled_cipher_suites: Vec<CipherSuite>) -> Self {
154        Self {
155            enabled_cipher_suites,
156        }
157    }
158}
159
160#[derive(Clone, Default)]
161pub struct AwsLcCipherSuiteBuilder {
162    signing: Option<AwsLcEcdsa>,
163    aead: Option<AwsLcAead>,
164    kdf: Option<AwsLcHkdf>,
165    hpke: Option<AwsLcHpke>,
166    hmac: Option<AwsLcHmac>,
167    hash: Option<AwsLcHash>,
168    fallback_cipher_suite: Option<CipherSuite>,
169}
170
171impl AwsLcCipherSuiteBuilder {
172    pub fn new() -> Self {
173        Self::default()
174    }
175
176    pub fn signing(self, signing: Curve) -> Self {
177        Self {
178            signing: Some(AwsLcEcdsa(signing)),
179            ..self
180        }
181    }
182
183    pub fn aead(self, aead: AeadId) -> Self {
184        Self {
185            aead: Some(AwsLcAead(aead)),
186            ..self
187        }
188    }
189
190    pub fn kdf(self, kdf: KdfId) -> Self {
191        Self {
192            kdf: Some(AwsLcHkdf(kdf)),
193            ..self
194        }
195    }
196
197    pub fn hmac(self, hmac: AwsLcHmac) -> Self {
198        Self {
199            hmac: Some(hmac),
200            ..self
201        }
202    }
203
204    pub fn hash(self, hash: AwsLcHash) -> Self {
205        Self {
206            hash: Some(hash),
207            ..self
208        }
209    }
210
211    pub fn hpke(self, cipher_suite: CipherSuite) -> Self {
212        Self {
213            hpke: classical_hpke(cipher_suite),
214            ..self
215        }
216    }
217
218    pub fn fallback_cipher_suite(self, cipher_suite: CipherSuite) -> Self {
219        Self {
220            fallback_cipher_suite: Some(cipher_suite),
221            ..self
222        }
223    }
224
225    #[cfg(feature = "post-quantum")]
226    pub fn pq_hpke(self, ml_kem: MlKem, kdf: KdfId, aead: AeadId) -> Self {
227        let ml_kem = MlKemKem {
228            ml_kem,
229            kdf: AwsLcHkdf(kdf),
230        };
231
232        Self {
233            hpke: Some(AwsLcHpke::PostQuantum(Hpke::new(
234                ml_kem,
235                AwsLcHkdf(kdf),
236                Some(AwsLcAead(aead)),
237            ))),
238            ..self
239        }
240    }
241
242    #[cfg(feature = "post-quantum")]
243    pub fn combined_hpke(
244        self,
245        classical_cipher_suite: CipherSuite,
246        ml_kem: MlKem,
247        kdf: KdfId,
248        aead: AeadId,
249        hash: AwsLcHash,
250    ) -> Self {
251        let ml_kem = MlKemKem {
252            ml_kem,
253            kdf: AwsLcHkdf(kdf),
254        };
255
256        let ecdh = dhkem(classical_cipher_suite);
257
258        let hpke = ecdh.map(|ecdh| {
259            let kem = CombinedKem::new_xwing(ml_kem, ecdh, hash, AwsLcShake128);
260
261            AwsLcHpke::Combined(Hpke::new(kem, AwsLcHkdf(kdf), Some(AwsLcAead(aead))))
262        });
263
264        Self { hpke, ..self }
265    }
266
267    #[cfg(feature = "post-quantum")]
268    pub fn ghp_combined_hpke(
269        self,
270        classical_cipher_suite: CipherSuite,
271        ml_kem: MlKem,
272        kdf: KdfId,
273        aead: AeadId,
274        hash: AwsLcHash,
275    ) -> Self {
276        let ml_kem = MlKemKem {
277            ml_kem,
278            kdf: AwsLcHkdf(kdf),
279        };
280
281        let ecdh = dhkem(classical_cipher_suite);
282
283        let hpke = ecdh.map(|ecdh| {
284            let kem = CombinedKem::new_xwing(ml_kem, ecdh, hash, AwsLcShake128);
285
286            AwsLcHpke::Combined(Hpke::new(kem, AwsLcHkdf(kdf), Some(AwsLcAead(aead))))
287        });
288
289        Self { hpke, ..self }
290    }
291
292    pub fn build(self, cipher_suite: CipherSuite) -> Option<AwsLcCipherSuite> {
293        let fallback_cs = self.fallback_cipher_suite.unwrap_or(cipher_suite);
294        let hpke = self.hpke.or_else(|| classical_hpke(fallback_cs))?;
295        let kdf = self.kdf.or_else(|| AwsLcHkdf::new(fallback_cs))?;
296        let aead = self.aead.or_else(|| AwsLcAead::new(fallback_cs))?;
297        let signing = self.signing.or_else(|| AwsLcEcdsa::new(fallback_cs))?;
298        let hmac = self.hmac.or_else(|| AwsLcHmac::new(fallback_cs))?;
299        let hash = self.hash.or_else(|| AwsLcHash::new(fallback_cs))?;
300
301        Some(AwsLcCipherSuite {
302            cipher_suite,
303            hpke,
304            aead,
305            kdf,
306            signing,
307            hmac,
308            hash,
309        })
310    }
311}
312
313fn classical_hpke(cipher_suite: CipherSuite) -> Option<AwsLcHpke> {
314    Some(AwsLcHpke::Classical(Hpke::new(
315        dhkem(cipher_suite)?,
316        AwsLcHkdf::new(cipher_suite)?,
317        Some(AwsLcAead::new(cipher_suite)?),
318    )))
319}
320
321impl CryptoProvider for AwsLcCryptoProvider {
322    type CipherSuiteProvider = AwsLcCipherSuite;
323
324    fn supported_cipher_suites(&self) -> Vec<CipherSuite> {
325        self.enabled_cipher_suites.clone()
326    }
327
328    fn cipher_suite_provider(
329        &self,
330        cipher_suite: CipherSuite,
331    ) -> Option<Self::CipherSuiteProvider> {
332        let classical_cs = match cipher_suite {
333            #[cfg(feature = "post-quantum")]
334            CipherSuite::ML_KEM_1024 => CipherSuite::P384_AES256,
335            #[cfg(feature = "post-quantum")]
336            CipherSuite::ML_KEM_512 | CipherSuite::ML_KEM_768 | CipherSuite::ML_KEM_768_X25519 => {
337                CipherSuite::CURVE25519_AES128
338            }
339            _ => cipher_suite,
340        };
341
342        let kdf = AwsLcHkdf::new(classical_cs)?;
343        let aead = AwsLcAead::new(classical_cs)?;
344        let hmac = AwsLcHmac::new(classical_cs)?;
345
346        let hpke = match cipher_suite {
347            #[cfg(feature = "post-quantum")]
348            CipherSuite::ML_KEM_512 | CipherSuite::ML_KEM_768 | CipherSuite::ML_KEM_1024 => {
349                AwsLcHpke::PostQuantum(Hpke::new(MlKemKem::new(cipher_suite)?, kdf, Some(aead)))
350            }
351            #[cfg(feature = "post-quantum")]
352            CipherSuite::ML_KEM_768_X25519 => {
353                let kem = CombinedKem::new_xwing(
354                    MlKemKem::new(CipherSuite::ML_KEM_768)?,
355                    dhkem(classical_cs)?,
356                    AwsLcHash::new_sha3(Sha3::SHA3_256)?,
357                    AwsLcShake128,
358                );
359
360                AwsLcHpke::Combined(Hpke::new(kem, kdf, Some(aead)))
361            }
362            _ => AwsLcHpke::Classical(Hpke::new(dhkem(cipher_suite)?, kdf, Some(aead))),
363        };
364
365        Some(AwsLcCipherSuite {
366            cipher_suite,
367            hpke,
368            aead,
369            kdf,
370            signing: AwsLcEcdsa::new(classical_cs)?,
371            hmac,
372            hash: AwsLcHash::new(classical_cs)?,
373        })
374    }
375}
376
377pub fn dhkem(cipher_suite: CipherSuite) -> Option<DhKem<Ecdh, AwsLcHkdf>> {
378    let kem_id = KemId::new(cipher_suite)?;
379    let dh = Ecdh::new(cipher_suite)?;
380    let kdf = AwsLcHkdf::new(cipher_suite)?;
381
382    Some(DhKem::new(dh, kdf, kem_id as u16, kem_id.n_secret()))
383}
384
385#[derive(Debug, Error)]
386pub enum AwsLcCryptoError {
387    #[error("Invalid key data")]
388    InvalidKeyData,
389    #[error("Underlying crypto error")]
390    CryptoError,
391    #[error("Invalid signature")]
392    InvalidSignature,
393    #[error(transparent)]
394    HpkeError(#[from] HpkeError),
395    #[error("Unsupported ciphersuite")]
396    UnsupportedCipherSuite,
397    #[error("Cert validation error: {0}")]
398    CertValidationFailure(String),
399    #[error(transparent)]
400    TryFromIntError(#[from] TryFromIntError),
401    #[error(transparent)]
402    KeyRejected(#[from] KeyRejected),
403    #[error(transparent)]
404    CombinedKemError(AnyError),
405    #[error(transparent)]
406    MlsCodecError(#[from] mls_rs_core::mls_rs_codec::Error),
407}
408
409impl From<Unspecified> for AwsLcCryptoError {
410    fn from(_value: Unspecified) -> Self {
411        AwsLcCryptoError::CryptoError
412    }
413}
414
415impl IntoAnyError for AwsLcCryptoError {}
416
417#[cfg_attr(not(mls_build_async), maybe_async::must_be_sync)]
418#[cfg_attr(all(target_arch = "wasm32", mls_build_async), maybe_async::must_be_async(?Send))]
419#[cfg_attr(
420    all(not(target_arch = "wasm32"), mls_build_async),
421    maybe_async::must_be_async
422)]
423impl CipherSuiteProvider for AwsLcCipherSuite {
424    type Error = AwsLcCryptoError;
425
426    type HpkeContextS = ContextS<AwsLcHkdf, AwsLcAead>;
427    type HpkeContextR = ContextR<AwsLcHkdf, AwsLcAead>;
428
429    fn cipher_suite(&self) -> mls_rs_core::crypto::CipherSuite {
430        self.cipher_suite
431    }
432
433    async fn hash(&self, data: &[u8]) -> Result<Vec<u8>, Self::Error> {
434        self.hash.hash(data)
435    }
436
437    async fn mac(&self, key: &[u8], data: &[u8]) -> Result<Vec<u8>, Self::Error> {
438        self.hmac.hmac(key, data)
439    }
440
441    async fn aead_seal(
442        &self,
443        key: &[u8],
444        data: &[u8],
445        aad: Option<&[u8]>,
446        nonce: &[u8],
447    ) -> Result<Vec<u8>, Self::Error> {
448        self.aead.seal(key, data, aad, nonce).await
449    }
450
451    async fn aead_open(
452        &self,
453        key: &[u8],
454        ciphertext: &[u8],
455        aad: Option<&[u8]>,
456        nonce: &[u8],
457    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
458        self.aead
459            .open(key, ciphertext, aad, nonce)
460            .await
461            .map(Into::into)
462    }
463
464    fn aead_key_size(&self) -> usize {
465        self.aead.key_size()
466    }
467
468    fn aead_nonce_size(&self) -> usize {
469        self.aead.nonce_size()
470    }
471
472    async fn kdf_extract(
473        &self,
474        salt: &[u8],
475        ikm: &[u8],
476    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
477        self.kdf.extract(salt, ikm).await.map(Into::into)
478    }
479
480    async fn kdf_expand(
481        &self,
482        prk: &[u8],
483        info: &[u8],
484        len: usize,
485    ) -> Result<Zeroizing<Vec<u8>>, Self::Error> {
486        self.kdf.expand(prk, info, len).await.map(Into::into)
487    }
488
489    fn kdf_extract_size(&self) -> usize {
490        self.kdf.extract_size()
491    }
492
493    async fn hpke_seal(
494        &self,
495        remote_key: &HpkePublicKey,
496        info: &[u8],
497        aad: Option<&[u8]>,
498        pt: &[u8],
499    ) -> Result<HpkeCiphertext, Self::Error> {
500        match &self.hpke {
501            AwsLcHpke::Classical(hpke) => hpke.seal(remote_key, info, None, aad, pt),
502            #[cfg(feature = "post-quantum")]
503            AwsLcHpke::PostQuantum(hpke) => hpke.seal(remote_key, info, None, aad, pt),
504            #[cfg(feature = "post-quantum")]
505            AwsLcHpke::Combined(hpke) => hpke.seal(remote_key, info, None, aad, pt),
506        }
507        .await
508        .map_err(Into::into)
509    }
510
511    async fn hpke_open(
512        &self,
513        ciphertext: &HpkeCiphertext,
514        local_secret: &HpkeSecretKey,
515        local_public: &HpkePublicKey,
516        info: &[u8],
517        aad: Option<&[u8]>,
518    ) -> Result<Vec<u8>, Self::Error> {
519        match &self.hpke {
520            AwsLcHpke::Classical(hpke) => {
521                hpke.open(ciphertext, local_secret, local_public, info, None, aad)
522            }
523            #[cfg(feature = "post-quantum")]
524            AwsLcHpke::PostQuantum(hpke) => {
525                hpke.open(ciphertext, local_secret, local_public, info, None, aad)
526            }
527            #[cfg(feature = "post-quantum")]
528            AwsLcHpke::Combined(hpke) => {
529                hpke.open(ciphertext, local_secret, local_public, info, None, aad)
530            }
531        }
532        .await
533        .map_err(Into::into)
534    }
535
536    async fn hpke_setup_s(
537        &self,
538        remote_key: &HpkePublicKey,
539        info: &[u8],
540    ) -> Result<(Vec<u8>, Self::HpkeContextS), Self::Error> {
541        match &self.hpke {
542            AwsLcHpke::Classical(hpke) => hpke.setup_sender(remote_key, info, None),
543            #[cfg(feature = "post-quantum")]
544            AwsLcHpke::PostQuantum(hpke) => hpke.setup_sender(remote_key, info, None),
545            #[cfg(feature = "post-quantum")]
546            AwsLcHpke::Combined(hpke) => hpke.setup_sender(remote_key, info, None),
547        }
548        .await
549        .map_err(Into::into)
550    }
551
552    async fn hpke_setup_r(
553        &self,
554        kem_output: &[u8],
555        local_secret: &HpkeSecretKey,
556        local_public: &HpkePublicKey,
557        info: &[u8],
558    ) -> Result<Self::HpkeContextR, Self::Error> {
559        match &self.hpke {
560            AwsLcHpke::Classical(hpke) => {
561                hpke.setup_receiver(kem_output, local_secret, local_public, info, None)
562            }
563            #[cfg(feature = "post-quantum")]
564            AwsLcHpke::PostQuantum(hpke) => {
565                hpke.setup_receiver(kem_output, local_secret, local_public, info, None)
566            }
567            #[cfg(feature = "post-quantum")]
568            AwsLcHpke::Combined(hpke) => {
569                hpke.setup_receiver(kem_output, local_secret, local_public, info, None)
570            }
571        }
572        .await
573        .map_err(Into::into)
574    }
575
576    async fn kem_derive(&self, ikm: &[u8]) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
577        match &self.hpke {
578            AwsLcHpke::Classical(hpke) => hpke.derive(ikm),
579            #[cfg(feature = "post-quantum")]
580            AwsLcHpke::PostQuantum(hpke) => hpke.derive(ikm),
581            #[cfg(feature = "post-quantum")]
582            AwsLcHpke::Combined(hpke) => hpke.derive(ikm),
583        }
584        .await
585        .map_err(Into::into)
586    }
587
588    async fn kem_generate(&self) -> Result<(HpkeSecretKey, HpkePublicKey), Self::Error> {
589        match &self.hpke {
590            AwsLcHpke::Classical(hpke) => hpke.generate(),
591            #[cfg(feature = "post-quantum")]
592            AwsLcHpke::PostQuantum(hpke) => hpke.generate(),
593            #[cfg(feature = "post-quantum")]
594            AwsLcHpke::Combined(hpke) => hpke.generate(),
595        }
596        .await
597        .map_err(Into::into)
598    }
599
600    fn kem_public_key_validate(&self, key: &HpkePublicKey) -> Result<(), Self::Error> {
601        match &self.hpke {
602            AwsLcHpke::Classical(hpke) => hpke.public_key_validate(key),
603            #[cfg(feature = "post-quantum")]
604            AwsLcHpke::PostQuantum(hpke) => hpke.public_key_validate(key),
605            #[cfg(feature = "post-quantum")]
606            AwsLcHpke::Combined(hpke) => hpke.public_key_validate(key),
607        }
608        .map_err(Into::into)
609    }
610
611    fn random_bytes(&self, out: &mut [u8]) -> Result<(), Self::Error> {
612        Ok(aws_lc_rs::rand::fill(out)?)
613    }
614
615    async fn signature_key_generate(
616        &self,
617    ) -> Result<(SignatureSecretKey, SignaturePublicKey), Self::Error> {
618        self.signing.signature_key_generate()
619    }
620
621    async fn signature_key_derive_public(
622        &self,
623        secret_key: &SignatureSecretKey,
624    ) -> Result<SignaturePublicKey, Self::Error> {
625        self.signing.signature_key_derive_public(secret_key)
626    }
627
628    async fn sign(
629        &self,
630        secret_key: &SignatureSecretKey,
631        data: &[u8],
632    ) -> Result<Vec<u8>, Self::Error> {
633        self.signing.sign(secret_key, data)
634    }
635
636    async fn verify(
637        &self,
638        public_key: &SignaturePublicKey,
639        signature: &[u8],
640        data: &[u8],
641    ) -> Result<(), Self::Error> {
642        self.signing.verify(public_key, signature, data)
643    }
644}
645
646pub fn sha256(data: &[u8]) -> [u8; 32] {
647    unsafe {
648        let mut out = MaybeUninit::<[u8; 32]>::uninit();
649        SHA256(data.as_ptr(), data.len(), out.as_mut_ptr() as *mut u8);
650        out.assume_init()
651    }
652}
653
654fn check_res(r: c_int) -> Result<(), AwsLcCryptoError> {
655    check_int_return(r).map(|_| ())
656}
657
658fn check_int_return(r: c_int) -> Result<c_int, AwsLcCryptoError> {
659    if r <= 0 {
660        Err(AwsLcCryptoError::CryptoError)
661    } else {
662        Ok(r)
663    }
664}
665
666fn check_non_null<T>(r: *mut T) -> Result<*mut T, AwsLcCryptoError> {
667    if r.is_null() {
668        return Err(AwsLcCryptoError::CryptoError);
669    }
670
671    Ok(r)
672}
673
674fn check_non_null_const<T>(r: *const T) -> Result<*const T, AwsLcCryptoError> {
675    if r.is_null() {
676        return Err(AwsLcCryptoError::CryptoError);
677    }
678
679    Ok(r)
680}
681
682#[cfg(not(mls_build_async))]
683#[test]
684fn mls_core_tests() {
685    mls_rs_core::crypto::test_suite::verify_tests(&AwsLcCryptoProvider::new(), true);
686
687    for cs in AwsLcCryptoProvider::supported_classical_cipher_suites() {
688        let mut hpke = Hpke::new(
689            dhkem(cs).unwrap(),
690            AwsLcHkdf::new(cs).unwrap(),
691            AwsLcAead::new(cs),
692        );
693
694        mls_rs_core::crypto::test_suite::verify_hpke_context_tests(&hpke, cs);
695        mls_rs_core::crypto::test_suite::verify_hpke_encap_tests(&mut hpke, cs);
696    }
697}
698
699#[cfg(all(not(mls_build_async), feature = "post-quantum", not(feature = "fips")))]
700#[test]
701fn pq_cipher_suite_test() {
702    for cs in AwsLcCryptoProvider::supported_pq_cipher_suites() {
703        let cs = AwsLcCryptoProvider::new()
704            .cipher_suite_provider(cs)
705            .unwrap();
706
707        let (sk, pk) = cs.kem_derive(&[0u8; 64]).unwrap();
708        let ct = cs.hpke_seal(&pk, b"info", None, b"very secret").unwrap();
709        let pt = cs.hpke_open(&ct, &sk, &pk, b"info", None).unwrap();
710        assert_eq!(pt, b"very secret");
711    }
712}