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 {
18 name: NamedGroup,
20
21 agreement_algorithm: &'static agreement::Algorithm,
23
24 fips_allowed: bool,
29
30 pub_key_validator: fn(&[u8]) -> bool,
43}
44
45impl SupportedKxGroup for KxGroup {
46 fn start(&self) -> Result<Box<dyn ActiveKeyExchange>, Error> {
47 let rng = SystemRandom::new();
48 let priv_key = agreement::EphemeralPrivateKey::generate(self.agreement_algorithm, &rng)
49 .map_err(|_| GetRandomFailed)?;
50
51 let pub_key = priv_key
52 .compute_public_key()
53 .map_err(|_| GetRandomFailed)?;
54
55 Ok(Box::new(KeyExchange {
56 name: self.name,
57 agreement_algorithm: self.agreement_algorithm,
58 priv_key,
59 pub_key,
60 pub_key_validator: self.pub_key_validator,
61 }))
62 }
63
64 fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
65 None
66 }
67
68 fn name(&self) -> NamedGroup {
69 self.name
70 }
71
72 fn fips(&self) -> bool {
73 self.fips_allowed && super::fips()
74 }
75}
76
77impl fmt::Debug for KxGroup {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 self.name.fmt(f)
80 }
81}
82
83pub static X25519: &dyn SupportedKxGroup = &KxGroup {
85 name: NamedGroup::X25519,
86 agreement_algorithm: &agreement::X25519,
87
88 fips_allowed: false,
94
95 pub_key_validator: |point: &[u8]| point.len() == 32,
96};
97
98pub static SECP256R1: &dyn SupportedKxGroup = &KxGroup {
100 name: NamedGroup::secp256r1,
101 agreement_algorithm: &agreement::ECDH_P256,
102 fips_allowed: true,
103 pub_key_validator: uncompressed_point,
104};
105
106pub static SECP384R1: &dyn SupportedKxGroup = &KxGroup {
108 name: NamedGroup::secp384r1,
109 agreement_algorithm: &agreement::ECDH_P384,
110 fips_allowed: true,
111 pub_key_validator: uncompressed_point,
112};
113
114fn uncompressed_point(point: &[u8]) -> bool {
115 matches!(point.first(), Some(0x04))
119}
120
121pub static ALL_KX_GROUPS: &[&dyn SupportedKxGroup] = &[X25519, SECP256R1, SECP384R1];
123
124struct KeyExchange {
127 name: NamedGroup,
128 agreement_algorithm: &'static agreement::Algorithm,
129 priv_key: agreement::EphemeralPrivateKey,
130 pub_key: agreement::PublicKey,
131 pub_key_validator: fn(&[u8]) -> bool,
132}
133
134impl ActiveKeyExchange for KeyExchange {
135 fn complete(self: Box<Self>, peer: &[u8]) -> Result<SharedSecret, Error> {
137 if !(self.pub_key_validator)(peer) {
138 return Err(PeerMisbehaved::InvalidKeyShare.into());
139 }
140 let peer_key = agreement::UnparsedPublicKey::new(self.agreement_algorithm, peer);
141 super::ring_shim::agree_ephemeral(self.priv_key, &peer_key)
142 .map_err(|_| PeerMisbehaved::InvalidKeyShare.into())
143 }
144
145 fn ffdhe_group(&self) -> Option<FfdheGroup<'static>> {
146 None
147 }
148
149 fn group(&self) -> NamedGroup {
151 self.name
152 }
153
154 fn pub_key(&self) -> &[u8] {
156 self.pub_key.as_ref()
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use std::format;
163
164 #[test]
165 fn kxgroup_fmt_yields_name() {
166 assert_eq!("X25519", format!("{:?}", super::X25519));
167 }
168}
169
170#[cfg(bench)]
171mod benchmarks {
172 #[bench]
173 fn bench_x25519(b: &mut test::Bencher) {
174 bench_any(b, super::X25519);
175 }
176
177 #[bench]
178 fn bench_ecdh_p256(b: &mut test::Bencher) {
179 bench_any(b, super::SECP256R1);
180 }
181
182 #[bench]
183 fn bench_ecdh_p384(b: &mut test::Bencher) {
184 bench_any(b, super::SECP384R1);
185 }
186
187 fn bench_any(b: &mut test::Bencher, kxg: &dyn super::SupportedKxGroup) {
188 b.iter(|| {
189 let akx = kxg.start().unwrap();
190 let pub_key = akx.pub_key().to_vec();
191 test::black_box(akx.complete(&pub_key).unwrap());
192 });
193 }
194}