1use super::{
2 curve25519::{FieldElement, U256},
3 ed25519,
4};
5use crate::{EllipticCurveError, Hasher, sha2::Sha512};
6
7pub const KEY_SIZE: usize = 32;
8pub const SHARED_SECRET_SIZE: usize = 32;
9
10const A24: FieldElement = FieldElement(U256::from_u64(121665));
11
12const BASEPOINT_U: [u8; 32] = {
13 let mut u = [0u8; 32];
14 u[0] = 9;
15 u
16};
17
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct SecretKey {
20 bytes: [u8; KEY_SIZE],
21 public_key: FieldElement,
22}
23
24impl SecretKey {
25 #[cfg(feature = "std")]
26 pub fn generate() -> SecretKey {
27 let bytes: [u8; KEY_SIZE] = rand::random();
28 SecretKey::from_bytes(&bytes)
29 }
30
31 pub fn from_bytes(bytes: &[u8; KEY_SIZE]) -> SecretKey {
32 let public_key = x25519_inner(bytes, FieldElement::from_relaxed_bytes(&BASEPOINT_U));
33 SecretKey {
34 bytes: *bytes,
35 public_key,
36 }
37 }
38
39 #[inline]
40 pub fn to_bytes(&self) -> [u8; KEY_SIZE] {
41 self.bytes
42 }
43
44 #[inline]
45 pub fn public_key(&self) -> PublicKey {
46 PublicKey {
47 u: self.public_key,
48 }
49 }
50
51 pub fn ecdh(&self, peer: &PublicKey) -> [u8; SHARED_SECRET_SIZE] {
55 let result = x25519_inner(&self.bytes, peer.u);
56 result.to_bytes()
57 }
58}
59
60impl From<&ed25519::SecretKey> for SecretKey {
61 fn from(ed25519_key: &ed25519::SecretKey) -> Self {
62 let digest = Sha512::hash(&ed25519_key.to_bytes());
63 let mut expanded = [0u8; 64];
64 expanded.copy_from_slice(digest.as_ref());
65 expanded[0] &= 248;
66 expanded[31] &= 127;
67 expanded[31] |= 64;
68 let mut bytes = [0u8; KEY_SIZE];
69 bytes.copy_from_slice(&expanded[..32]);
70
71 let public_key = x25519_inner(&bytes, FieldElement::from_relaxed_bytes(&BASEPOINT_U));
72
73 SecretKey {
74 bytes,
75 public_key,
76 }
77 }
78}
79
80impl From<&[u8; KEY_SIZE]> for SecretKey {
81 fn from(bytes: &[u8; KEY_SIZE]) -> Self {
82 Self::from_bytes(bytes)
83 }
84}
85
86impl TryFrom<&[u8]> for SecretKey {
87 type Error = EllipticCurveError;
88
89 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
90 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
91 }
92}
93
94#[derive(Clone, Debug, PartialEq, Eq)]
95pub struct PublicKey {
96 u: FieldElement,
97}
98
99impl PublicKey {
100 pub fn from_bytes(bytes: &[u8; KEY_SIZE]) -> Self {
101 let u = FieldElement::from_relaxed_bytes(bytes);
102 PublicKey {
103 u,
104 }
105 }
106
107 #[inline]
108 pub fn to_bytes(&self) -> [u8; KEY_SIZE] {
109 self.u.to_bytes()
110 }
111}
112
113impl From<&[u8; KEY_SIZE]> for PublicKey {
114 fn from(bytes: &[u8; KEY_SIZE]) -> Self {
115 Self::from_bytes(bytes)
116 }
117}
118
119impl TryFrom<&[u8]> for PublicKey {
120 type Error = EllipticCurveError;
121
122 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
123 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
124 }
125}
126
127impl TryFrom<&ed25519::PublicKey> for PublicKey {
128 type Error = EllipticCurveError;
129
130 fn try_from(ed25519_public_key: &ed25519::PublicKey) -> Result<Self, Self::Error> {
131 let mut y_bytes = ed25519_public_key.to_bytes();
132 y_bytes[31] &= 0x7f;
133 let y = FieldElement::from_canonical_bytes(&y_bytes).ok_or(EllipticCurveError::InvalidKey)?;
134 let one = FieldElement::ONE;
135 let u = (one.add(y)).mul((one.sub(y)).invert().ok_or(EllipticCurveError::InvalidKey)?);
136 Ok(PublicKey {
137 u,
138 })
139 }
140}
141
142#[inline]
143fn clamp_scalar(mut scalar: [u8; 32]) -> [u8; 32] {
144 scalar[0] &= 248;
145 scalar[31] &= 127;
146 scalar[31] |= 64;
147 scalar
148}
149
150#[inline]
151fn cswap(swap: bool, a: &mut FieldElement, b: &mut FieldElement) {
152 let tmp = FieldElement::select(b, a, swap);
153 *b = FieldElement::select(a, b, swap);
154 *a = tmp;
155}
156
157fn x25519_inner(scalar: &[u8; 32], u: FieldElement) -> FieldElement {
158 let clamped = clamp_scalar(*scalar);
159 let x_1 = u;
160 let mut x_2 = FieldElement::ONE;
161 let mut z_2 = FieldElement::ZERO;
162 let mut x_3 = u;
163 let mut z_3 = FieldElement::ONE;
164 let mut swap = false;
165
166 let mut t: isize = 254;
167 while t >= 0 {
168 let k_t = ((clamped[(t as usize) / 8] >> ((t as usize) % 8)) & 1) != 0;
169
170 swap ^= k_t;
171 cswap(swap, &mut x_2, &mut x_3);
172 cswap(swap, &mut z_2, &mut z_3);
173 swap = k_t;
174
175 let a = x_2.add(z_2);
176 let aa = a.square();
177 let b = x_2.sub(z_2);
178 let bb = b.square();
179 let e = aa.sub(bb);
180 let c = x_3.add(z_3);
181 let d = x_3.sub(z_3);
182 let da = d.mul(a);
183 let cb = c.mul(b);
184 x_3 = da.add(cb).square();
185 z_3 = x_1.mul(da.sub(cb).square());
186 x_2 = aa.mul(bb);
187 z_2 = e.mul(aa.add(A24.mul(e)));
188
189 t -= 1;
190 }
191
192 cswap(swap, &mut x_2, &mut x_3);
193 cswap(swap, &mut z_2, &mut z_3);
194
195 if z_2.is_zero() {
196 return FieldElement::ZERO;
197 }
198
199 x_2.mul(z_2.invert().expect("z_2 must be non-zero"))
200}
201
202#[cfg(test)]
203mod tests {
204 use super::*;
205 use crate::curve25519::curve25519::{P, U256};
206
207 fn x25519(private_key: &[u8; KEY_SIZE], public_key: &[u8; KEY_SIZE]) -> [u8; SHARED_SECRET_SIZE] {
208 let priv_key = SecretKey::from_bytes(private_key);
209 let pub_key = PublicKey::from_bytes(public_key);
210 priv_key.ecdh(&pub_key)
211 }
212
213 fn decode_hex<const N: usize>(hex_str: &str) -> [u8; N] {
214 let bytes = hex::decode(hex_str).unwrap();
215 assert_eq!(bytes.len(), N, "hex string must decode to exactly {N} bytes");
216 let mut out = [0u8; N];
217 out.copy_from_slice(&bytes);
218 out
219 }
220
221 struct DhTestVector {
222 alice_private: &'static str,
223 alice_public: &'static str,
224 bob_private: &'static str,
225 bob_public: &'static str,
226 shared_secret: &'static str,
227 }
228
229 #[test]
230 fn key_exchange() {
231 let alice_priv = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
232 let bob_priv = decode_hex::<32>("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
233 let expected_alice_pub = decode_hex::<32>("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
234 let expected_bob_pub = decode_hex::<32>("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
235 let expected_shared = decode_hex::<32>("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
236
237 let alice = SecretKey::from_bytes(&alice_priv);
238 let bob = SecretKey::from_bytes(&bob_priv);
239
240 assert_eq!(alice.public_key().to_bytes(), expected_alice_pub);
241 assert_eq!(bob.public_key().to_bytes(), expected_bob_pub);
242
243 let alice_shared = alice.ecdh(&bob.public_key());
244 let bob_shared = bob.ecdh(&alice.public_key());
245
246 assert_eq!(alice_shared, expected_shared);
247 assert_eq!(bob_shared, expected_shared);
248 }
249
250 #[test]
251 fn generate_produces_valid_keys() {
252 let alice = SecretKey::generate();
253 let bob = SecretKey::generate();
254
255 let alice_shared = alice.ecdh(&bob.public_key());
256 let bob_shared = bob.ecdh(&alice.public_key());
257 assert_eq!(alice_shared, bob_shared);
258 assert_eq!(alice_shared.len(), 32);
259 }
260
261 #[test]
262 fn public_key_bytes_roundtrip() {
263 let key = SecretKey::generate();
264 let pub_key = key.public_key();
265 let bytes = pub_key.to_bytes();
266 let restored = PublicKey::from_bytes(&bytes);
267 assert_eq!(bytes, restored.to_bytes());
268 }
269
270 #[test]
271 fn private_key_bytes_roundtrip() {
272 let orig = SecretKey::generate();
273 let bytes = orig.to_bytes();
274 let restored = SecretKey::from_bytes(&bytes);
275 assert_eq!(bytes, restored.to_bytes());
276 assert_eq!(orig.public_key().to_bytes(), restored.public_key().to_bytes());
277 }
278
279 #[test]
280 fn from_ed25519_public_key() {
281 let ed_seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
282 let ed_priv = ed25519::SecretKey::from_bytes(&ed_seed);
283 let ed_pub = ed_priv.public_key();
284 let x_pub = PublicKey::try_from(&ed_pub).unwrap();
285
286 assert_ne!(x_pub.to_bytes(), [0u8; 32]);
287 }
288
289 #[test]
290 fn from_ed25519_ecdh_roundtrip() {
291 let ed_alice = ed25519::SecretKey::generate();
292 let ed_bob = ed25519::SecretKey::generate();
293
294 let x_alice = SecretKey::from(&ed_alice);
295 let x_bob = SecretKey::from(&ed_bob);
296
297 let x_alice_pub = PublicKey::try_from(&ed_alice.public_key()).unwrap();
298 let x_bob_pub = PublicKey::try_from(&ed_bob.public_key()).unwrap();
299
300 let alice_shared = x_alice.ecdh(&x_bob_pub);
301 let bob_shared = x_bob.ecdh(&x_alice_pub);
302 assert_eq!(alice_shared, bob_shared);
303 }
304
305 #[test]
306 fn rfc7748_section_5_2_vector_1() {
307 let scalar = decode_hex::<32>("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4");
308 let u = decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
309 let expected = decode_hex::<32>("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
310
311 let output = x25519(&scalar, &u);
312 assert_eq!(output, expected);
313 }
314
315 #[test]
316 fn rfc7748_section_5_2_vector_2() {
317 let scalar = decode_hex::<32>("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d");
318 let u = decode_hex::<32>("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493");
319 let expected = decode_hex::<32>("95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957");
320
321 let output = x25519(&scalar, &u);
322 assert_eq!(output, expected);
323 }
324
325 #[test]
326 fn rfc7748_section_5_2_iterative_1() {
327 let k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
328 let u = k;
329 let expected = decode_hex::<32>("422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
330
331 let output = x25519(&k, &u);
332 assert_eq!(output, expected);
333 }
334
335 #[test]
336 fn rfc7748_section_5_2_iterative_1000() {
337 let mut k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
338 let mut u = k;
339 for _ in 0..1000 {
340 let out = x25519(&k, &u);
341 u = k;
342 k = out;
343 }
344 let expected = decode_hex::<32>("684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51");
345 assert_eq!(k, expected);
346 }
347
348 #[test]
349 #[ignore = "takes about 1 minute; run with -- --ignored"]
350 fn rfc7748_section_5_2_iterative_1000000() {
351 let mut k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
352 let mut u = k;
353 for _ in 0..1000000 {
354 let out = x25519(&k, &u);
355 u = k;
356 k = out;
357 }
358 let expected = decode_hex::<32>("7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424");
359 assert_eq!(k, expected);
360 }
361
362 #[test]
363 fn rfc7748_section_5_2_dh_exchange() {
364 let vectors: [DhTestVector; 1] = [DhTestVector {
365 alice_private: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
366 alice_public: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
367 bob_private: "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
368 bob_public: "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
369 shared_secret: "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
370 }];
371
372 for v in &vectors {
373 let alice_private = decode_hex::<32>(v.alice_private);
374 let alice_public = decode_hex::<32>(v.alice_public);
375 let bob_private = decode_hex::<32>(v.bob_private);
376 let bob_public = decode_hex::<32>(v.bob_public);
377 let expected = decode_hex::<32>(v.shared_secret);
378
379 let alice_computed_public = SecretKey::from_bytes(&alice_private).public_key().to_bytes();
380 assert_eq!(alice_computed_public, alice_public, "Alice public key mismatch");
381
382 let bob_computed_public = SecretKey::from_bytes(&bob_private).public_key().to_bytes();
383 assert_eq!(bob_computed_public, bob_public, "Bob public key mismatch");
384
385 let alice_shared = x25519(&alice_private, &bob_public);
386 assert_eq!(alice_shared, expected, "Alice shared secret mismatch");
387
388 let bob_shared = x25519(&bob_private, &alice_public);
389 assert_eq!(bob_shared, expected, "Bob shared secret mismatch");
390 }
391 }
392
393 #[test]
394 fn basepoint_multiplication_identity_pattern() {
395 let key = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
396 let public = SecretKey::from_bytes(&key).public_key().to_bytes();
397 let basepoint = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
398 let direct = x25519(&key, &basepoint);
399 assert_eq!(public, direct);
400 }
401
402 #[test]
403 fn low_order_point_zero() {
404 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
405 let all_zero = [0u8; 32];
406 let output = x25519(&scalar, &all_zero);
407 assert_eq!(output, [0u8; 32], "X25519 with u=0 must produce all-zero output");
408 }
409
410 #[test]
411 fn low_order_point_u_one() {
412 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
413 let mut u_one = [0u8; 32];
414 u_one[0] = 1;
415 let output = x25519(&scalar, &u_one);
416 let _ = output;
417 }
418
419 #[test]
420 fn all_zero_private_key() {
421 let key = [0u8; 32];
422 let u = decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
423 let output = x25519(&key, &u);
424 let expected = x25519(
425 &decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000040"),
426 &u,
427 );
428 assert_eq!(output, expected);
429 }
430
431 #[test]
440 fn wycheproof_valid_vectors() {
441 let shared = x25519(
442 &decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"),
443 &decode_hex::<32>("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"),
444 );
445 assert_eq!(
446 shared,
447 decode_hex::<32>("436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320")
448 );
449
450 let shared = x25519(
451 &decode_hex::<32>("a8386f7f16c50731d64f82e6a170b142a4e34f31fd7768fcb8902925e7d1e25a"),
452 &decode_hex::<32>("0400000000000000000000000000000000000000000000000000000000000000"),
453 );
454 assert_eq!(
455 shared,
456 decode_hex::<32>("34b7e4fa53264420d9f943d15513902342b386b172a0b0b7c8b8f2dd3d669f59")
457 );
458
459 let shared = x25519(
460 &decode_hex::<32>("a046e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449a44"),
461 &decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c"),
462 );
463 assert_eq!(
464 shared,
465 decode_hex::<32>("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552")
466 );
467 }
468
469 #[test]
470 fn wycheproof_low_order_and_zero_shared() {
471 let private = decode_hex::<32>("786a33a4f7af297a20e7642925932bf509e7070fa1bc36986af1eb13f4f50b55");
472
473 let shared = x25519(
474 &private,
475 &decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000000"),
476 );
477 assert_eq!(shared, [0u8; 32]);
478
479 let shared = x25519(
480 &private,
481 &decode_hex::<32>("0100000000000000000000000000000000000000000000000000000000000000"),
482 );
483 assert_eq!(shared, [0u8; 32]);
484
485 let shared = x25519(
486 &private,
487 &decode_hex::<32>("ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"),
488 );
489 assert_eq!(shared, [0u8; 32]);
490 }
491
492 #[test]
493 fn wycheproof_non_canonical_public_keys() {
494 let shared = x25519(
495 &decode_hex::<32>("0016b62af5cabde8c40938ebf2108e05d27fa0533ed85d70015ad4ad39762d54"),
496 &decode_hex::<32>("efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"),
497 );
498 assert_eq!(
499 shared,
500 decode_hex::<32>("b4d10e832714972f96bd3382e4d082a21a8333a16315b3ffb536061d2482360d")
501 );
502 }
503
504 #[test]
505 fn golang_crypto_vectors() {
506 struct GoVector {
507 scalar: [u8; 32],
508 base: [u8; 32],
509 expected: [u8; 32],
510 }
511
512 let vectors = [
513 GoVector {
514 scalar: decode_hex::<32>("668fb9f76ad971c81ac900071a1560bce2ca00cac7e67af99348913761434014"),
515 base: decode_hex::<32>("db5f32b7f841e7a1a00968effded12735fc47a3eb13b579aacadeae80939a7dd"),
516 expected: decode_hex::<32>("090d85e599ea8e2beeb61304d37be10ec5c905f9927d32f42a9a0afb3e0b4074"),
517 },
518 GoVector {
519 scalar: decode_hex::<32>("636695e34f75b9a279c8706fad1289f2c0b1e22e16f8b8861729c10a582958af"),
520 base: decode_hex::<32>("090d0701f8fde28f70043b83f2346225419b18a7f27e9e3d2bfd04e10f3d213e"),
521 expected: decode_hex::<32>("bf26ec7ec413061733d44070ea67cab02a85dc1be8cfe1ff73d541cc08325506"),
522 },
523 ];
524
525 for (i, v) in vectors.iter().enumerate() {
526 let result = x25519(&v.scalar, &v.base);
527 assert_eq!(result, v.expected, "Go vector {} failed", i);
528 }
529 }
530
531 #[test]
532 fn additional_boringssl_vectors() {
533 struct Vector {
534 scalar: [u8; 32],
535 base: [u8; 32],
536 expected: [u8; 32],
537 }
538
539 let vectors = [
540 Vector {
541 scalar: decode_hex::<32>("203161c3159a876a2beaec29d2427fb0c7c30d382cd013d27cc3d393db0daf6f"),
542 base: decode_hex::<32>("6ab95d1abe68c09b005c3db9042cc91ac849f7e94a2a4a9b893678970b7b95bf"),
543 expected: decode_hex::<32>("11edaedc95ff78f563a1c8f15591c071dea092b4d7ecaac8e0387b5a160c4e5d"),
544 },
545 Vector {
546 scalar: decode_hex::<32>("13d65491fe75f203a008b4415abc60d532e695dbd2f1e803accb34b2b72c3d70"),
547 base: decode_hex::<32>("2e784e04ca0073336256a839255ed2f7d4796a64cdc37f1eb0e5c4c8d1d1e0f5"),
548 expected: decode_hex::<32>("563e8c9adaa7d73101b0f2ead3cae1ea5d8fcd5cd36080bb8e6ec03d61450917"),
549 },
550 ];
551
552 for (i, v) in vectors.iter().enumerate() {
553 let result = x25519(&v.scalar, &v.base);
554 assert_eq!(result, v.expected, "BoringSSL vector {} failed", i);
555 }
556 }
557
558 #[test]
559 fn wycheproof_twist_vectors() {
560 let shared = x25519(
561 &decode_hex::<32>("d85d8c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211958"),
562 &decode_hex::<32>("63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733"),
563 );
564 assert_eq!(
565 shared,
566 decode_hex::<32>("279df67a7c4611db4708a0e8282b195e5ac0ed6f4b2f292c6fbd0acac30d1332")
567 );
568
569 let shared = x25519(
570 &decode_hex::<32>("d03edde9f3e7b799045f9ac3793d4a9277dadeadc41bec0290f81f744f73775f"),
571 &decode_hex::<32>("0200000000000000000000000000000000000000000000000000000000000000"),
572 );
573 assert_eq!(
574 shared,
575 decode_hex::<32>("b87a1722cc6c1e2feecb54e97abd5a22acc27616f78f6e315fd2b73d9f221e57")
576 );
577 }
578
579 #[test]
580 fn more_boringssl_vectors() {
581 struct Vector {
582 scalar: [u8; 32],
583 base: [u8; 32],
584 expected: [u8; 32],
585 }
586
587 let vectors = [Vector {
588 scalar: decode_hex::<32>("203161c3159a876a2beaec29d2427fb0c7c30d382cd013d27cc3d393db0daf6f"),
589 base: decode_hex::<32>("6ab95d1abe68c09b005c3db9042cc91ac849f7e94a2a4a9b893678970b7b95bf"),
590 expected: decode_hex::<32>("11edaedc95ff78f563a1c8f15591c071dea092b4d7ecaac8e0387b5a160c4e5d"),
591 }];
592
593 for (i, v) in vectors.iter().enumerate() {
594 let result = x25519(&v.scalar, &v.base);
595 assert_eq!(result, v.expected, "BoringSSL vector {} failed", i);
596 }
597 }
598
599 #[test]
600 fn clamped_zero_scalar_produces_known_public_key() {
601 let scalar = [0u8; 32];
602 let expected = decode_hex::<32>("2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74");
603 let pub_key = SecretKey::from_bytes(&scalar).public_key().to_bytes();
604 assert_eq!(pub_key, expected, "clamped zero scalar must produce deterministic public key");
605 }
606
607 #[test]
608 fn max_scalar_x25519_against_basepoint() {
609 let scalar = [0xffu8; 32];
610 let pub_key = SecretKey::from_bytes(&scalar).public_key().to_bytes();
611 let direct = x25519(
612 &scalar,
613 &decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000"),
614 );
615 assert_eq!(pub_key, direct);
616 }
617
618 #[test]
619 fn x25519_self_ecdh_consistency() {
620 let alice = SecretKey::generate();
621 let alice_pub = alice.public_key();
622 let alice_shared = alice.ecdh(&alice_pub);
623 assert_eq!(alice_shared.len(), 32);
624 }
625
626 #[test]
627 fn non_canonical_above_p_reduces_correctly() {
628 let scalar = decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475");
629 let (sum, _) = P.add_raw(&U256::from_u64(2));
630 let mut p_plus_2_bytes = sum.to_le_bytes_fixed::<32>();
631 p_plus_2_bytes[31] &= 0x7f;
632 let result = x25519(&scalar, &p_plus_2_bytes);
633
634 let u_2 = decode_hex::<32>("0200000000000000000000000000000000000000000000000000000000000000");
635 let expected = x25519(&scalar, &u_2);
636 assert_eq!(result, expected, "X25519(scalar, p+2) must equal X25519(scalar, 2)");
637 }
638
639 #[test]
642 fn wycheproof_x25519() {
643 let data: serde_json::Value =
644 serde_json::from_str(include_str!("../../testdata/wycheproof/testvectors_v1/x25519_test.json")).unwrap();
645 let mut valid_tested = 0u64;
646 let mut acceptable_tested = 0u64;
647 for group in data["testGroups"].as_array().unwrap() {
648 if group["curve"].as_str() != Some("curve25519") {
649 continue;
650 }
651 for test in group["tests"].as_array().unwrap() {
652 let public_hex = test["public"].as_str().unwrap();
653 let private_hex = test["private"].as_str().unwrap();
654 let expected_shared_hex = test["shared"].as_str().unwrap();
655 let result = test["result"].as_str().unwrap();
656
657 let public_key = decode_hex::<32>(public_hex);
658 let private_key = decode_hex::<32>(private_hex);
659
660 let shared = x25519(&private_key, &public_key);
661 let shared_hex = hex::encode(shared);
664
665 if result == "valid" {
666 assert_eq!(
667 shared_hex, expected_shared_hex,
668 "wycheproof x25519 tcId={} shared secret mismatch",
669 test["tcId"]
670 );
671 valid_tested += 1;
672 } else {
673 acceptable_tested += 1;
674 }
675 }
676 }
677 assert!(valid_tested > 0, "no valid wycheproof x25519 tests were run");
678 assert!(acceptable_tested > 0, "no acceptable wycheproof x25519 tests were run");
679 }
680}