rustls/crypto/aws_lc_rs/pq/
mlkem.rs

1use alloc::boxed::Box;
2use alloc::vec::Vec;
3
4use aws_lc_rs::kem;
5
6use super::INVALID_KEY_SHARE;
7use crate::crypto::{ActiveKeyExchange, CompletedKeyExchange, SharedSecret, SupportedKxGroup};
8use crate::ffdhe_groups::FfdheGroup;
9use crate::{Error, NamedGroup, ProtocolVersion};
10
11#[derive(Debug)]
12pub(crate) struct MlKem {
13    pub(crate) alg: &'static kem::Algorithm<kem::AlgorithmId>,
14    pub(crate) group: NamedGroup,
15}
16
17impl SupportedKxGroup for MlKem {
18    fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
19        let decaps_key = kem::DecapsulationKey::generate(self.alg)
20            .map_err(|_| Error::General("key generation failed".into()))?;
21
22        let pub_key_bytes = decaps_key
23            .encapsulation_key()
24            .and_then(|encaps_key| encaps_key.key_bytes())
25            .map_err(|_| Error::General("encaps failed".into()))?;
26
27        Ok(Box::new(Active {
28            decaps_key: Box::new(decaps_key),
29            encaps_key_bytes: Vec::from(pub_key_bytes.as_ref()),
30            group: self.group,
31        }))
32    }
33
34    fn start_and_complete(&self, client_share: &[u8]) -> Result<CompletedKeyExchange, Error> {
35        let encaps_key =
36            kem::EncapsulationKey::new(self.alg, client_share).map_err(|_| INVALID_KEY_SHARE)?;
37
38        let (ciphertext, shared_secret) = encaps_key
39            .encapsulate()
40            .map_err(|_| INVALID_KEY_SHARE)?;
41
42        Ok(CompletedKeyExchange {
43            group: self.name(),
44            pub_key: Vec::from(ciphertext.as_ref()),
45            secret: SharedSecret::from(shared_secret.as_ref()),
46        })
47    }
48
49    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
50        None
51    }
52
53    fn name(&self) -> NamedGroup {
54        self.group
55    }
56
57    fn fips(&self) -> bool {
58        // AUDITORS:
59        // At the time of writing, the ML-KEM implementation in AWS-LC-FIPS module 3.0
60        // is FIPS-pending.  Some regulatory regimes (eg, FedRAMP rev 5 SC-13) allow
61        // use of implementations in this state, as if they are already approved.
62        //
63        // We follow this liberal interpretation, and say MlKem768 is FIPS-compliant
64        // if the underlying library is in FIPS mode.
65        //
66        // TODO: adjust the `fips()` function return type to allow more policies to
67        // be expressed, perhaps following something like
68        // <https://github.com/golang/go/issues/70200#issuecomment-2490017956> --
69        // see <https://github.com/rustls/rustls/issues/2309>
70        super::super::fips()
71    }
72
73    fn usable_for_version(&self, version: ProtocolVersion) -> bool {
74        version == ProtocolVersion::TLSv1_3
75    }
76}
77
78struct Active {
79    decaps_key: Box<kem::DecapsulationKey<kem::AlgorithmId>>,
80    encaps_key_bytes: Vec<u8>,
81    group: NamedGroup,
82}
83
84impl ActiveKeyExchange for Active {
85    // The received 'peer_pub_key' is actually the ML-KEM ciphertext,
86    // which when decapsulated with our `decaps_key` produces the shared
87    // secret.
88    fn complete(self: Box<Self>, peer_pub_key: &[u8]) -> Result<SharedSecret, Error> {
89        let shared_secret = self
90            .decaps_key
91            .decapsulate(peer_pub_key.into())
92            .map_err(|_| INVALID_KEY_SHARE)?;
93
94        Ok(SharedSecret::from(shared_secret.as_ref()))
95    }
96
97    fn pub_key(&self) -> &[u8] {
98        &self.encaps_key_bytes
99    }
100
101    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
102        None
103    }
104
105    fn group(&self) -> NamedGroup {
106        self.group
107    }
108}