1#![allow(clippy::duplicate_mod)]
2
3use alloc::boxed::Box;
4use core::fmt;
5
6use super::ring_like::agreement;
7use super::ring_like::rand::SystemRandom;
8use crate::crypto::{ActiveKeyExchange, FfdheGroup, SharedSecret, SupportedKxGroup};
9use crate::error::{Error, PeerMisbehaved};
10use crate::msgs::enums::NamedGroup;
11use crate::rand::GetRandomFailed;
12
13struct KxGroup {
15    name: NamedGroup,
17
18    agreement_algorithm: &'static agreement::Algorithm,
20
21    fips_allowed: bool,
26
27    pub_key_validator: fn(&[u8]) -> bool,
40}
41
42impl SupportedKxGroup for KxGroup {
43    fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
44        let rng = SystemRandom::new();
45        let priv_key = agreement::EphemeralPrivateKey::generate(self.agreement_algorithm, &rng)
46            .map_err(|_| GetRandomFailed)?;
47
48        let pub_key = priv_key
49            .compute_public_key()
50            .map_err(|_| GetRandomFailed)?;
51
52        Ok(Box::new(KeyExchange {
53            name: self.name,
54            agreement_algorithm: self.agreement_algorithm,
55            priv_key,
56            pub_key,
57            pub_key_validator: self.pub_key_validator,
58        }))
59    }
60
61    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
62        None
63    }
64
65    fn name(&self) -> NamedGroup {
66        self.name
67    }
68
69    fn fips(&self) -> bool {
70        self.fips_allowed && super::fips()
71    }
72}
73
74impl fmt::Debug for KxGroup {
75    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76        self.name.fmt(f)
77    }
78}
79
80pub static X25519: &dyn SupportedKxGroup = &KxGroup {
82    name: NamedGroup::X25519,
83    agreement_algorithm: &agreement::X25519,
84
85    fips_allowed: false,
91
92    pub_key_validator: |point: &[u8]| point.len() == 32,
93};
94
95pub static SECP256R1: &dyn SupportedKxGroup = &KxGroup {
97    name: NamedGroup::secp256r1,
98    agreement_algorithm: &agreement::ECDH_P256,
99    fips_allowed: true,
100    pub_key_validator: uncompressed_point,
101};
102
103pub static SECP384R1: &dyn SupportedKxGroup = &KxGroup {
105    name: NamedGroup::secp384r1,
106    agreement_algorithm: &agreement::ECDH_P384,
107    fips_allowed: true,
108    pub_key_validator: uncompressed_point,
109};
110
111fn uncompressed_point(point: &[u8]) -> bool {
112    matches!(point.first(), Some(0x04))
116}
117
118struct KeyExchange {
121    name: NamedGroup,
122    agreement_algorithm: &'static agreement::Algorithm,
123    priv_key: agreement::EphemeralPrivateKey,
124    pub_key: agreement::PublicKey,
125    pub_key_validator: fn(&[u8]) -> bool,
126}
127
128impl ActiveKeyExchange for KeyExchange {
129    fn complete(self: Box<Self>, peer: &[u8]) -> Result<SharedSecret, Error> {
131        if !(self.pub_key_validator)(peer) {
132            return Err(PeerMisbehaved::InvalidKeyShare.into());
133        }
134        let peer_key = agreement::UnparsedPublicKey::new(self.agreement_algorithm, peer);
135        super::ring_shim::agree_ephemeral(self.priv_key, &peer_key)
136            .map_err(|_| PeerMisbehaved::InvalidKeyShare.into())
137    }
138
139    fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
140        None
141    }
142
143    fn group(&self) -> NamedGroup {
145        self.name
146    }
147
148    fn pub_key(&self) -> &[u8] {
150        self.pub_key.as_ref()
151    }
152}
153
154#[cfg(test)]
155mod tests {
156    use std::format;
157
158    #[test]
159    fn kxgroup_fmt_yields_name() {
160        assert_eq!("X25519", format!("{:?}", super::X25519));
161    }
162}
163
164#[cfg(bench)]
165mod benchmarks {
166    #[bench]
167    fn bench_x25519(b: &mut test::Bencher) {
168        bench_any(b, super::X25519);
169    }
170
171    #[bench]
172    fn bench_ecdh_p256(b: &mut test::Bencher) {
173        bench_any(b, super::SECP256R1);
174    }
175
176    #[bench]
177    fn bench_ecdh_p384(b: &mut test::Bencher) {
178        bench_any(b, super::SECP384R1);
179    }
180
181    fn bench_any(b: &mut test::Bencher, kxg: &dyn super::SupportedKxGroup) {
182        b.iter(|| {
183            let akx = kxg.start().unwrap();
184            let pub_key = akx.pub_key().to_vec();
185            test::black_box(akx.complete(&pub_key).unwrap());
186        });
187    }
188}