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, 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 name(&self) -> NamedGroup {
62 self.name
63 }
64
65 fn fips(&self) -> bool {
66 self.fips_allowed && super::fips()
67 }
68}
69
70impl fmt::Debug for KxGroup {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 self.name.fmt(f)
73 }
74}
75
76pub static X25519: &dyn SupportedKxGroup = &KxGroup {
78 name: NamedGroup::X25519,
79 agreement_algorithm: &agreement::X25519,
80
81 fips_allowed: false,
87
88 pub_key_validator: |point: &[u8]| point.len() == 32,
89};
90
91pub static SECP256R1: &dyn SupportedKxGroup = &KxGroup {
93 name: NamedGroup::secp256r1,
94 agreement_algorithm: &agreement::ECDH_P256,
95 fips_allowed: true,
96 pub_key_validator: uncompressed_point,
97};
98
99pub static SECP384R1: &dyn SupportedKxGroup = &KxGroup {
101 name: NamedGroup::secp384r1,
102 agreement_algorithm: &agreement::ECDH_P384,
103 fips_allowed: true,
104 pub_key_validator: uncompressed_point,
105};
106
107fn uncompressed_point(point: &[u8]) -> bool {
108 matches!(point.first(), Some(0x04))
112}
113
114struct KeyExchange {
117 name: NamedGroup,
118 agreement_algorithm: &'static agreement::Algorithm,
119 priv_key: agreement::EphemeralPrivateKey,
120 pub_key: agreement::PublicKey,
121 pub_key_validator: fn(&[u8]) -> bool,
122}
123
124impl ActiveKeyExchange for KeyExchange {
125 fn complete(self: Box<Self>, peer: &[u8]) -> Result<SharedSecret, Error> {
127 if !(self.pub_key_validator)(peer) {
128 return Err(PeerMisbehaved::InvalidKeyShare.into());
129 }
130 let peer_key = agreement::UnparsedPublicKey::new(self.agreement_algorithm, peer);
131 super::ring_shim::agree_ephemeral(self.priv_key, &peer_key)
132 .map_err(|_| PeerMisbehaved::InvalidKeyShare.into())
133 }
134
135 fn group(&self) -> NamedGroup {
137 self.name
138 }
139
140 fn pub_key(&self) -> &[u8] {
142 self.pub_key.as_ref()
143 }
144}
145
146#[cfg(test)]
147mod tests {
148 use std::format;
149
150 #[test]
151 fn kxgroup_fmt_yields_name() {
152 assert_eq!("X25519", format!("{:?}", super::X25519));
153 }
154}
155
156#[cfg(bench)]
157mod benchmarks {
158 #[bench]
159 fn bench_x25519(b: &mut test::Bencher) {
160 bench_any(b, super::X25519);
161 }
162
163 #[bench]
164 fn bench_ecdh_p256(b: &mut test::Bencher) {
165 bench_any(b, super::SECP256R1);
166 }
167
168 #[bench]
169 fn bench_ecdh_p384(b: &mut test::Bencher) {
170 bench_any(b, super::SECP384R1);
171 }
172
173 fn bench_any(b: &mut test::Bencher, kxg: &dyn super::SupportedKxGroup) {
174 b.iter(|| {
175 let akx = kxg.start().unwrap();
176 let pub_key = akx.pub_key().to_vec();
177 test::black_box(akx.complete(&pub_key).unwrap());
178 });
179 }
180}