1use super::{ops, scalar::SCALAR_LEN};
18use crate::{agreement, bb, cpu, ec, error, rand};
19use core::ffi::c_int;
20
21static CURVE25519: ec::Curve = ec::Curve {
22 public_key_len: PUBLIC_KEY_LEN,
23 elem_scalar_seed_len: ELEM_AND_SCALAR_LEN,
24 id: ec::CurveID::Curve25519,
25 check_private_key_bytes: x25519_check_private_key_bytes,
26 generate_private_key: x25519_generate_private_key,
27 public_from_private: x25519_public_from_private,
28};
29
30pub static X25519: agreement::Algorithm = agreement::Algorithm {
39 curve: &CURVE25519,
40 ecdh: x25519_ecdh,
41};
42
43#[allow(clippy::unnecessary_wraps)]
44fn x25519_check_private_key_bytes(
45 bytes: &[u8],
46 _: cpu::Features,
47) -> Result<(), error::Unspecified> {
48 debug_assert_eq!(bytes.len(), PRIVATE_KEY_LEN);
49 Ok(())
50}
51
52fn x25519_generate_private_key(
53 rng: &dyn rand::SecureRandom,
54 out: &mut [u8],
55 _: cpu::Features,
56) -> Result<(), error::Unspecified> {
57 rng.fill(out)
58}
59
60fn x25519_public_from_private(
61 public_out: &mut [u8],
62 private_key: &ec::Seed,
63 cpu_features: cpu::Features,
64) -> Result<(), error::Unspecified> {
65 let public_out = public_out.try_into()?;
66
67 let private_key: &[u8; SCALAR_LEN] = private_key.bytes_less_safe().try_into()?;
68 let private_key = ops::MaskedScalar::from_bytes_masked(*private_key);
69
70 #[cfg(all(
71 all(target_arch = "arm", target_endian = "little"),
72 any(target_os = "android", target_os = "linux")
73 ))]
74 if let Some(cpu) = <cpu::Features as cpu::GetFeature<_>>::get_feature(&cpu_features) {
75 static MONTGOMERY_BASE_POINT: [u8; 32] = [
76 9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
77 0, 0, 0,
78 ];
79 x25519_neon(public_out, &private_key, &MONTGOMERY_BASE_POINT, cpu);
80 return Ok(());
81 }
82
83 prefixed_extern! {
84 fn x25519_public_from_private_generic_masked(
85 public_key_out: &mut PublicKey,
86 private_key: &PrivateKey,
87 use_adx: c_int,
88 );
89 }
90 unsafe {
91 x25519_public_from_private_generic_masked(
92 public_out,
93 &private_key,
94 ops::has_fe25519_adx(cpu_features).into(),
95 );
96 }
97
98 Ok(())
99}
100
101fn x25519_ecdh(
102 out: &mut [u8],
103 my_private_key: &ec::Seed,
104 peer_public_key: untrusted::Input,
105 cpu_features: cpu::Features,
106) -> Result<(), error::Unspecified> {
107 let my_private_key: &[u8; SCALAR_LEN] = my_private_key.bytes_less_safe().try_into()?;
108 let my_private_key = ops::MaskedScalar::from_bytes_masked(*my_private_key);
109 let peer_public_key: &[u8; PUBLIC_KEY_LEN] = peer_public_key.as_slice_less_safe().try_into()?;
110
111 fn scalar_mult(
112 out: &mut ops::EncodedPoint,
113 scalar: &ops::MaskedScalar,
114 point: &ops::EncodedPoint,
115 #[allow(unused_variables)] cpu_features: cpu::Features,
116 ) {
117 #[cfg(all(
118 all(target_arch = "arm", target_endian = "little"),
119 any(target_os = "android", target_os = "linux")
120 ))]
121 if let Some(cpu) = <cpu::Features as cpu::GetFeature<_>>::get_feature(&cpu_features) {
122 return x25519_neon(out, scalar, point, cpu);
123 }
124
125 #[cfg(all(target_arch = "x86_64", not(target_os = "windows")))]
126 {
127 if ops::has_fe25519_adx(cpu_features) {
128 prefixed_extern! {
129 fn x25519_scalar_mult_adx(
130 out: &mut ops::EncodedPoint,
131 scalar: &ops::MaskedScalar,
132 point: &ops::EncodedPoint,
133 );
134 }
135 return unsafe { x25519_scalar_mult_adx(out, scalar, point) };
136 }
137 }
138
139 prefixed_extern! {
140 fn x25519_scalar_mult_generic_masked(
141 out: &mut ops::EncodedPoint,
142 scalar: &ops::MaskedScalar,
143 point: &ops::EncodedPoint,
144 );
145 }
146 unsafe {
147 x25519_scalar_mult_generic_masked(out, scalar, point);
148 }
149 }
150
151 scalar_mult(
152 out.try_into()?,
153 &my_private_key,
154 peer_public_key,
155 cpu_features,
156 );
157
158 let zeros: SharedSecret = [0; SHARED_SECRET_LEN];
159 if bb::verify_slices_are_equal(out, &zeros).is_ok() {
160 return Err(error::Unspecified);
162 }
163
164 Ok(())
165}
166
167#[cfg(all(
169 all(target_arch = "arm", target_endian = "little"),
170 any(target_os = "android", target_os = "linux")
171))]
172fn x25519_neon(
173 out: &mut ops::EncodedPoint,
174 scalar: &ops::MaskedScalar,
175 point: &ops::EncodedPoint,
176 _cpu: cpu::arm::Neon,
177) {
178 prefixed_extern! {
179 fn x25519_NEON(
180 out: &mut ops::EncodedPoint,
181 scalar: &ops::MaskedScalar,
182 point: &ops::EncodedPoint,
183 );
184 }
185 unsafe { x25519_NEON(out, scalar, point) }
186}
187
188const ELEM_AND_SCALAR_LEN: usize = ops::ELEM_LEN;
189
190type PrivateKey = ops::MaskedScalar;
191const PRIVATE_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
192
193type PublicKey = [u8; PUBLIC_KEY_LEN];
195const PUBLIC_KEY_LEN: usize = ELEM_AND_SCALAR_LEN;
196
197type SharedSecret = [u8; SHARED_SECRET_LEN];
199const SHARED_SECRET_LEN: usize = ELEM_AND_SCALAR_LEN;
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204 use crate::ec;
205 use untrusted::Input;
206
207 #[test]
208 fn test_x25519_public_from_private() {
209 struct TestVector {
210 private: [u8; 32],
211 public: [u8; 32],
212 }
213 static TEST_CASES: &[TestVector] = &[
214 TestVector {
215 private: [
216 0x77, 0x07, 0x6d, 0x0a, 0x73, 0x18, 0xa5, 0x7d, 0x3c, 0x16, 0xc1, 0x72, 0x51,
217 0xb2, 0x66, 0x45, 0xdf, 0x4c, 0x2f, 0x87, 0xeb, 0xc0, 0x99, 0x2a, 0xb1, 0x77,
218 0xfb, 0xa5, 0x1d, 0xb9, 0x2c, 0x2a,
219 ],
220 public: [
221 0x85, 0x20, 0xf0, 0x09, 0x89, 0x30, 0xa7, 0x54, 0x74, 0x8b, 0x7d, 0xdc, 0xb4,
222 0x3e, 0xf7, 0x5a, 0x0d, 0xbf, 0x3a, 0x0d, 0x26, 0x38, 0x1a, 0xf4, 0xeb, 0xa4,
223 0xa9, 0x8e, 0xaa, 0x9b, 0x4e, 0x6a,
224 ],
225 },
226 TestVector {
227 private: [
228 0x5d, 0xab, 0x08, 0x7e, 0x62, 0x4a, 0x8a, 0x4b, 0x79, 0xe1, 0x7f, 0x8b, 0x83,
229 0x80, 0x0e, 0xe6, 0x6f, 0x3b, 0xb1, 0x29, 0x26, 0x18, 0xb6, 0xfd, 0x1c, 0x2f,
230 0x8b, 0x27, 0xff, 0x88, 0xe0, 0xeb,
231 ],
232 public: [
233 0xde, 0x9e, 0xdb, 0x7d, 0x7b, 0x7d, 0xc1, 0xb4, 0xd3, 0x5b, 0x61, 0xc2, 0xec,
234 0xe4, 0x35, 0x37, 0x3f, 0x83, 0x43, 0xc8, 0x5b, 0x78, 0x67, 0x4d, 0xad, 0xfc,
235 0x7e, 0x14, 0x6f, 0x88, 0x2b, 0x4f,
236 ],
237 },
238 ];
239 let cpu_features = cpu::features();
240 for test_case in TEST_CASES {
241 let seed =
242 ec::Seed::from_bytes(&CURVE25519, Input::from(&test_case.private), cpu_features)
243 .unwrap();
244 let mut output = [0u8; 32];
245 x25519_public_from_private(&mut output, &seed, cpu_features).unwrap();
246 assert_eq!(output, test_case.public);
247 }
248 }
249}