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