1use constant_time_eq::constant_time_eq;
2
3use super::{
4 curve25519::{FieldElement, U256},
5 ed25519,
6};
7use crate::{EllipticCurveError, Hasher, sha2::Sha512};
8
9pub const KEY_SIZE: usize = 32;
10pub const SHARED_SECRET_SIZE: usize = 32;
11
12const A24: FieldElement = FieldElement(U256::from_u64(121665));
13
14const BASEPOINT_U: [u8; 32] = {
15 let mut u = [0u8; 32];
16 u[0] = 9;
17 u
18};
19
20#[derive(Clone, PartialEq, Eq)]
75#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
76pub struct SecretKey {
77 bytes: [u8; KEY_SIZE],
78 public_key: FieldElement,
79}
80
81impl SecretKey {
82 #[cfg(feature = "random")]
87 pub fn generate() -> Result<SecretKey, EllipticCurveError> {
88 let bytes: [u8; KEY_SIZE] = crate::random::bytes()?;
89 Ok(SecretKey::from_bytes(&bytes))
90 }
91
92 pub fn from_bytes(bytes: &[u8; KEY_SIZE]) -> SecretKey {
93 let public_key = x25519_inner(bytes, FieldElement::from_relaxed_bytes(&BASEPOINT_U));
94 SecretKey {
95 bytes: *bytes,
96 public_key,
97 }
98 }
99
100 #[inline]
101 pub fn to_bytes(&self) -> [u8; KEY_SIZE] {
102 self.bytes
103 }
104
105 #[inline]
106 pub fn public_key(&self) -> PublicKey {
107 PublicKey {
108 u: self.public_key,
109 }
110 }
111
112 pub fn ecdh(&self, peer: &PublicKey) -> Result<[u8; SHARED_SECRET_SIZE], EllipticCurveError> {
125 let result = x25519_inner(&self.bytes, peer.u);
126 if result.is_zero() {
127 return Err(EllipticCurveError::InvalidSharedSecret);
128 }
129 Ok(result.to_bytes())
130 }
131}
132
133impl From<&ed25519::SecretKey> for SecretKey {
134 fn from(ed25519_key: &ed25519::SecretKey) -> Self {
135 let digest = Sha512::hash(&ed25519_key.to_bytes());
136 let mut expanded = [0u8; 64];
137 expanded.copy_from_slice(digest.as_ref());
138 expanded[0] &= 248;
139 expanded[31] &= 127;
140 expanded[31] |= 64;
141 let mut bytes = [0u8; KEY_SIZE];
142 bytes.copy_from_slice(&expanded[..32]);
143
144 let public_key = x25519_inner(&bytes, FieldElement::from_relaxed_bytes(&BASEPOINT_U));
145
146 SecretKey {
147 bytes,
148 public_key,
149 }
150 }
151}
152
153impl From<&[u8; KEY_SIZE]> for SecretKey {
154 fn from(bytes: &[u8; KEY_SIZE]) -> Self {
155 Self::from_bytes(bytes)
156 }
157}
158
159impl TryFrom<&[u8]> for SecretKey {
160 type Error = EllipticCurveError;
161
162 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
163 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
164 }
165}
166
167#[derive(Clone, Debug, PartialEq, Eq)]
183pub struct PublicKey {
184 u: FieldElement,
185}
186
187const LOW_ORDER_U: [[u8; KEY_SIZE]; 5] = [
195 [0u8; KEY_SIZE],
197 {
199 let mut u = [0u8; KEY_SIZE];
200 u[0] = 1;
201 u
202 },
203 [
205 0xec, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
206 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
207 ],
208 [
210 0xe0, 0xeb, 0x7a, 0x7c, 0x3b, 0x41, 0xb8, 0xae, 0x16, 0x56, 0xe3, 0xfa, 0xf1, 0x9f, 0xc4, 0x6a, 0xda, 0x09,
211 0x8d, 0xeb, 0x9c, 0x32, 0xb1, 0xfd, 0x86, 0x62, 0x05, 0x16, 0x5f, 0x49, 0xb8, 0x00,
212 ],
213 [
215 0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0, 0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44,
216 0x5c, 0xc4, 0x58, 0x1c, 0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0x57,
217 ],
218];
219
220impl PublicKey {
221 pub fn from_bytes(bytes: &[u8; KEY_SIZE]) -> Self {
222 let u = FieldElement::from_relaxed_bytes(bytes);
223 PublicKey {
224 u,
225 }
226 }
227
228 #[inline]
229 pub fn to_bytes(&self) -> [u8; KEY_SIZE] {
230 self.u.to_bytes()
231 }
232
233 pub fn is_low_order(&self) -> bool {
240 let bytes = self.u.to_bytes();
241 let mut found = 0u8;
242 for candidate in &LOW_ORDER_U {
243 found |= constant_time_eq(&bytes, candidate) as u8;
244 }
245 found != 0
246 }
247}
248
249impl From<&[u8; KEY_SIZE]> for PublicKey {
250 fn from(bytes: &[u8; KEY_SIZE]) -> Self {
251 Self::from_bytes(bytes)
252 }
253}
254
255impl TryFrom<&[u8]> for PublicKey {
256 type Error = EllipticCurveError;
257
258 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
259 Ok(Self::from_bytes(bytes.try_into().map_err(|_| EllipticCurveError::InvalidKey)?))
260 }
261}
262
263impl TryFrom<&ed25519::PublicKey> for PublicKey {
264 type Error = EllipticCurveError;
265
266 fn try_from(ed25519_public_key: &ed25519::PublicKey) -> Result<Self, Self::Error> {
267 let mut y_bytes = ed25519_public_key.to_bytes();
268 y_bytes[31] &= 0x7f;
269 let y = FieldElement::from_canonical_bytes(&y_bytes).ok_or(EllipticCurveError::InvalidKey)?;
270 let one = FieldElement::ONE;
271 let u = (one.add(y)).mul((one.sub(y)).invert().ok_or(EllipticCurveError::InvalidKey)?);
272 Ok(PublicKey {
273 u,
274 })
275 }
276}
277
278#[inline]
279fn clamp_scalar(mut scalar: [u8; 32]) -> [u8; 32] {
280 scalar[0] &= 248;
281 scalar[31] &= 127;
282 scalar[31] |= 64;
283 scalar
284}
285
286#[inline]
287fn cswap(swap: bool, a: &mut FieldElement, b: &mut FieldElement) {
288 let tmp = FieldElement::select(b, a, swap);
289 *b = FieldElement::select(a, b, swap);
290 *a = tmp;
291}
292
293fn x25519_inner(scalar: &[u8; 32], u: FieldElement) -> FieldElement {
294 let clamped = clamp_scalar(*scalar);
295 let x_1 = u;
296 let mut x_2 = FieldElement::ONE;
297 let mut z_2 = FieldElement::ZERO;
298 let mut x_3 = u;
299 let mut z_3 = FieldElement::ONE;
300 let mut swap = false;
301
302 let mut t: isize = 254;
303 while t >= 0 {
304 let k_t = ((clamped[(t as usize) / 8] >> ((t as usize) % 8)) & 1) != 0;
305
306 swap ^= k_t;
307 cswap(swap, &mut x_2, &mut x_3);
308 cswap(swap, &mut z_2, &mut z_3);
309 swap = k_t;
310
311 let a = x_2.add(z_2);
312 let aa = a.square();
313 let b = x_2.sub(z_2);
314 let bb = b.square();
315 let e = aa.sub(bb);
316 let c = x_3.add(z_3);
317 let d = x_3.sub(z_3);
318 let da = d.mul(a);
319 let cb = c.mul(b);
320 x_3 = da.add(cb).square();
321 z_3 = x_1.mul(da.sub(cb).square());
322 x_2 = aa.mul(bb);
323 z_2 = e.mul(aa.add(A24.mul(e)));
324
325 t -= 1;
326 }
327
328 cswap(swap, &mut x_2, &mut x_3);
329 cswap(swap, &mut z_2, &mut z_3);
330
331 if z_2.is_zero() {
332 return FieldElement::ZERO;
333 }
334
335 x_2.mul(z_2.invert().expect("z_2 must be non-zero"))
336}
337
338#[cfg(test)]
339mod tests {
340 use super::*;
341 use crate::curve25519::curve25519::{P, U256};
342
343 #[cfg(feature = "zeroize")]
344 #[test]
345 fn secret_key_zeroize_clears_bytes() {
346 use zeroize::Zeroize;
347
348 let mut key = SecretKey::from_bytes(&[1u8; KEY_SIZE]);
349 assert_ne!(key.to_bytes(), [0u8; KEY_SIZE]);
350 key.zeroize();
351 assert_eq!(key.to_bytes(), [0u8; KEY_SIZE]);
352 }
353
354 fn x25519(private_key: &[u8; KEY_SIZE], public_key: &[u8; KEY_SIZE]) -> [u8; SHARED_SECRET_SIZE] {
355 let priv_key = SecretKey::from_bytes(private_key);
356 let pub_key = PublicKey::from_bytes(public_key);
357 priv_key
358 .ecdh(&pub_key)
359 .expect("test vector must have a non-zero shared secret")
360 }
361
362 fn x25519_checked(
363 private_key: &[u8; KEY_SIZE],
364 public_key: &[u8; KEY_SIZE],
365 ) -> Result<[u8; SHARED_SECRET_SIZE], EllipticCurveError> {
366 SecretKey::from_bytes(private_key).ecdh(&PublicKey::from_bytes(public_key))
367 }
368
369 fn decode_hex<const N: usize>(hex_str: &str) -> [u8; N] {
370 let bytes = hex::decode(hex_str).unwrap();
371 assert_eq!(bytes.len(), N, "hex string must decode to exactly {N} bytes");
372 let mut out = [0u8; N];
373 out.copy_from_slice(&bytes);
374 out
375 }
376
377 struct DhTestVector {
378 alice_private: &'static str,
379 alice_public: &'static str,
380 bob_private: &'static str,
381 bob_public: &'static str,
382 shared_secret: &'static str,
383 }
384
385 #[test]
386 fn key_exchange() {
387 let alice_priv = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
388 let bob_priv = decode_hex::<32>("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb");
389 let expected_alice_pub = decode_hex::<32>("8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a");
390 let expected_bob_pub = decode_hex::<32>("de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f");
391 let expected_shared = decode_hex::<32>("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742");
392
393 let alice = SecretKey::from_bytes(&alice_priv);
394 let bob = SecretKey::from_bytes(&bob_priv);
395
396 assert_eq!(alice.public_key().to_bytes(), expected_alice_pub);
397 assert_eq!(bob.public_key().to_bytes(), expected_bob_pub);
398
399 let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
400 let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
401
402 assert_eq!(alice_shared, expected_shared);
403 assert_eq!(bob_shared, expected_shared);
404 }
405
406 #[test]
407 fn generate_produces_valid_keys() {
408 let alice = SecretKey::generate().unwrap();
409 let bob = SecretKey::generate().unwrap();
410
411 let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
412 let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
413 assert_eq!(alice_shared, bob_shared);
414 assert_eq!(alice_shared.len(), 32);
415 }
416
417 #[test]
418 fn public_key_bytes_roundtrip() {
419 let key = SecretKey::generate().unwrap();
420 let pub_key = key.public_key();
421 let bytes = pub_key.to_bytes();
422 let restored = PublicKey::from_bytes(&bytes);
423 assert_eq!(bytes, restored.to_bytes());
424 }
425
426 #[test]
427 fn private_key_bytes_roundtrip() {
428 let orig = SecretKey::generate().unwrap();
429 let bytes = orig.to_bytes();
430 let restored = SecretKey::from_bytes(&bytes);
431 assert_eq!(bytes, restored.to_bytes());
432 assert_eq!(orig.public_key().to_bytes(), restored.public_key().to_bytes());
433 }
434
435 #[test]
436 fn from_ed25519_public_key() {
437 let ed_seed = decode_hex::<32>("9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60");
438 let ed_priv = ed25519::SecretKey::from_bytes(&ed_seed);
439 let ed_pub = ed_priv.public_key();
440 let x_pub = PublicKey::try_from(&ed_pub).unwrap();
441
442 assert_ne!(x_pub.to_bytes(), [0u8; 32]);
443 }
444
445 #[test]
446 fn from_ed25519_ecdh_roundtrip() {
447 let ed_alice = ed25519::SecretKey::generate().unwrap();
448 let ed_bob = ed25519::SecretKey::generate().unwrap();
449
450 let x_alice = SecretKey::from(&ed_alice);
451 let x_bob = SecretKey::from(&ed_bob);
452
453 let x_alice_pub = PublicKey::try_from(&ed_alice.public_key()).unwrap();
454 let x_bob_pub = PublicKey::try_from(&ed_bob.public_key()).unwrap();
455
456 let alice_shared = x_alice.ecdh(&x_bob_pub).unwrap();
457 let bob_shared = x_bob.ecdh(&x_alice_pub).unwrap();
458 assert_eq!(alice_shared, bob_shared);
459 }
460
461 #[test]
462 fn rfc7748_section_5_2_vector_1() {
463 let scalar = decode_hex::<32>("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4");
464 let u = decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
465 let expected = decode_hex::<32>("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552");
466
467 let output = x25519(&scalar, &u);
468 assert_eq!(output, expected);
469 }
470
471 #[test]
472 fn rfc7748_section_5_2_vector_2() {
473 let scalar = decode_hex::<32>("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d");
474 let u = decode_hex::<32>("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493");
475 let expected = decode_hex::<32>("95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957");
476
477 let output = x25519(&scalar, &u);
478 assert_eq!(output, expected);
479 }
480
481 #[test]
482 fn rfc7748_section_5_2_iterative_1() {
483 let k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
484 let u = k;
485 let expected = decode_hex::<32>("422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079");
486
487 let output = x25519(&k, &u);
488 assert_eq!(output, expected);
489 }
490
491 #[test]
492 fn rfc7748_section_5_2_iterative_1000() {
493 let mut k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
494 let mut u = k;
495 for _ in 0..1000 {
496 let out = x25519(&k, &u);
497 u = k;
498 k = out;
499 }
500 let expected = decode_hex::<32>("684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51");
501 assert_eq!(k, expected);
502 }
503
504 #[test]
505 #[ignore = "takes about 1 minute; run with -- --ignored"]
506 fn rfc7748_section_5_2_iterative_1000000() {
507 let mut k = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
508 let mut u = k;
509 for _ in 0..1000000 {
510 let out = x25519(&k, &u);
511 u = k;
512 k = out;
513 }
514 let expected = decode_hex::<32>("7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424");
515 assert_eq!(k, expected);
516 }
517
518 #[test]
519 fn rfc7748_section_5_2_dh_exchange() {
520 let vectors: [DhTestVector; 1] = [DhTestVector {
521 alice_private: "77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
522 alice_public: "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
523 bob_private: "5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb",
524 bob_public: "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f",
525 shared_secret: "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742",
526 }];
527
528 for v in &vectors {
529 let alice_private = decode_hex::<32>(v.alice_private);
530 let alice_public = decode_hex::<32>(v.alice_public);
531 let bob_private = decode_hex::<32>(v.bob_private);
532 let bob_public = decode_hex::<32>(v.bob_public);
533 let expected = decode_hex::<32>(v.shared_secret);
534
535 let alice_computed_public = SecretKey::from_bytes(&alice_private).public_key().to_bytes();
536 assert_eq!(alice_computed_public, alice_public, "Alice public key mismatch");
537
538 let bob_computed_public = SecretKey::from_bytes(&bob_private).public_key().to_bytes();
539 assert_eq!(bob_computed_public, bob_public, "Bob public key mismatch");
540
541 let alice_shared = x25519(&alice_private, &bob_public);
542 assert_eq!(alice_shared, expected, "Alice shared secret mismatch");
543
544 let bob_shared = x25519(&bob_private, &alice_public);
545 assert_eq!(bob_shared, expected, "Bob shared secret mismatch");
546 }
547 }
548
549 #[test]
550 fn basepoint_multiplication_identity_pattern() {
551 let key = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
552 let public = SecretKey::from_bytes(&key).public_key().to_bytes();
553 let basepoint = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
554 let direct = x25519(&key, &basepoint);
555 assert_eq!(public, direct);
556 }
557
558 #[test]
559 fn low_order_point_zero() {
560 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
561 let all_zero = [0u8; 32];
562 assert_eq!(
563 x25519_checked(&scalar, &all_zero),
564 Err(EllipticCurveError::InvalidSharedSecret),
565 "X25519 with u=0 must be rejected"
566 );
567 }
568
569 #[test]
570 fn low_order_point_u_one() {
571 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
572 let mut u_one = [0u8; 32];
573 u_one[0] = 1;
574 assert_eq!(
575 x25519_checked(&scalar, &u_one),
576 Err(EllipticCurveError::InvalidSharedSecret),
577 "X25519 with u=1 must be rejected"
578 );
579 }
580
581 #[test]
582 fn low_order_points_are_rejected() {
583 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
584 for u in LOW_ORDER_U {
585 assert!(
586 PublicKey::from_bytes(&u).is_low_order(),
587 "u={} must be low order",
588 hex::encode(u)
589 );
590 assert_eq!(
591 x25519_checked(&scalar, &u),
592 Err(EllipticCurveError::InvalidSharedSecret),
593 "low-order u={} must be rejected",
594 hex::encode(u)
595 );
596 }
597 }
598
599 #[test]
600 fn non_canonical_low_order_points_are_rejected() {
601 let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
602 let non_canonical = [
604 decode_hex::<32>("edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"),
605 decode_hex::<32>("eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"),
606 ];
607 for u in non_canonical {
608 assert!(
609 PublicKey::from_bytes(&u).is_low_order(),
610 "u={} must be low order",
611 hex::encode(u)
612 );
613 assert_eq!(
614 x25519_checked(&scalar, &u),
615 Err(EllipticCurveError::InvalidSharedSecret),
616 "non-canonical low-order u={} must be rejected",
617 hex::encode(u)
618 );
619 }
620 }
621
622 #[test]
623 fn is_low_order_false_for_honest_keys() {
624 let basepoint = decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000");
625 assert!(!PublicKey::from_bytes(&basepoint).is_low_order());
626 for _ in 0..16 {
627 assert!(!SecretKey::generate().unwrap().public_key().is_low_order());
628 }
629 }
630
631 #[test]
632 fn all_zero_private_key() {
633 let key = [0u8; 32];
634 let u = decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c");
635 let output = x25519(&key, &u);
636 let expected = x25519(
637 &decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000040"),
638 &u,
639 );
640 assert_eq!(output, expected);
641 }
642
643 #[test]
652 fn wycheproof_valid_vectors() {
653 let shared = x25519(
654 &decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"),
655 &decode_hex::<32>("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"),
656 );
657 assert_eq!(
658 shared,
659 decode_hex::<32>("436a2c040cf45fea9b29a0cb81b1f41458f863d0d61b453d0a982720d6d61320")
660 );
661
662 let shared = x25519(
663 &decode_hex::<32>("a8386f7f16c50731d64f82e6a170b142a4e34f31fd7768fcb8902925e7d1e25a"),
664 &decode_hex::<32>("0400000000000000000000000000000000000000000000000000000000000000"),
665 );
666 assert_eq!(
667 shared,
668 decode_hex::<32>("34b7e4fa53264420d9f943d15513902342b386b172a0b0b7c8b8f2dd3d669f59")
669 );
670
671 let shared = x25519(
672 &decode_hex::<32>("a046e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449a44"),
673 &decode_hex::<32>("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c"),
674 );
675 assert_eq!(
676 shared,
677 decode_hex::<32>("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552")
678 );
679 }
680
681 #[test]
682 fn wycheproof_low_order_and_zero_shared() {
683 let private = decode_hex::<32>("786a33a4f7af297a20e7642925932bf509e7070fa1bc36986af1eb13f4f50b55");
684
685 for u in [
686 "0000000000000000000000000000000000000000000000000000000000000000",
687 "0100000000000000000000000000000000000000000000000000000000000000",
688 "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
689 ] {
690 assert_eq!(
691 x25519_checked(&private, &decode_hex::<32>(u)),
692 Err(EllipticCurveError::InvalidSharedSecret),
693 "low-order u={u} must be rejected"
694 );
695 }
696 }
697
698 #[test]
699 fn wycheproof_non_canonical_public_keys() {
700 let shared = x25519(
701 &decode_hex::<32>("0016b62af5cabde8c40938ebf2108e05d27fa0533ed85d70015ad4ad39762d54"),
702 &decode_hex::<32>("efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f"),
703 );
704 assert_eq!(
705 shared,
706 decode_hex::<32>("b4d10e832714972f96bd3382e4d082a21a8333a16315b3ffb536061d2482360d")
707 );
708 }
709
710 #[test]
711 fn golang_crypto_vectors() {
712 struct GoVector {
713 scalar: [u8; 32],
714 base: [u8; 32],
715 expected: [u8; 32],
716 }
717
718 let vectors = [
719 GoVector {
720 scalar: decode_hex::<32>("668fb9f76ad971c81ac900071a1560bce2ca00cac7e67af99348913761434014"),
721 base: decode_hex::<32>("db5f32b7f841e7a1a00968effded12735fc47a3eb13b579aacadeae80939a7dd"),
722 expected: decode_hex::<32>("090d85e599ea8e2beeb61304d37be10ec5c905f9927d32f42a9a0afb3e0b4074"),
723 },
724 GoVector {
725 scalar: decode_hex::<32>("636695e34f75b9a279c8706fad1289f2c0b1e22e16f8b8861729c10a582958af"),
726 base: decode_hex::<32>("090d0701f8fde28f70043b83f2346225419b18a7f27e9e3d2bfd04e10f3d213e"),
727 expected: decode_hex::<32>("bf26ec7ec413061733d44070ea67cab02a85dc1be8cfe1ff73d541cc08325506"),
728 },
729 ];
730
731 for (i, v) in vectors.iter().enumerate() {
732 let result = x25519(&v.scalar, &v.base);
733 assert_eq!(result, v.expected, "Go vector {} failed", i);
734 }
735 }
736
737 #[test]
738 fn additional_boringssl_vectors() {
739 struct Vector {
740 scalar: [u8; 32],
741 base: [u8; 32],
742 expected: [u8; 32],
743 }
744
745 let vectors = [
746 Vector {
747 scalar: decode_hex::<32>("203161c3159a876a2beaec29d2427fb0c7c30d382cd013d27cc3d393db0daf6f"),
748 base: decode_hex::<32>("6ab95d1abe68c09b005c3db9042cc91ac849f7e94a2a4a9b893678970b7b95bf"),
749 expected: decode_hex::<32>("11edaedc95ff78f563a1c8f15591c071dea092b4d7ecaac8e0387b5a160c4e5d"),
750 },
751 Vector {
752 scalar: decode_hex::<32>("13d65491fe75f203a008b4415abc60d532e695dbd2f1e803accb34b2b72c3d70"),
753 base: decode_hex::<32>("2e784e04ca0073336256a839255ed2f7d4796a64cdc37f1eb0e5c4c8d1d1e0f5"),
754 expected: decode_hex::<32>("563e8c9adaa7d73101b0f2ead3cae1ea5d8fcd5cd36080bb8e6ec03d61450917"),
755 },
756 ];
757
758 for (i, v) in vectors.iter().enumerate() {
759 let result = x25519(&v.scalar, &v.base);
760 assert_eq!(result, v.expected, "BoringSSL vector {} failed", i);
761 }
762 }
763
764 #[test]
765 fn wycheproof_twist_vectors() {
766 let shared = x25519(
767 &decode_hex::<32>("d85d8c061a50804ac488ad774ac716c3f5ba714b2712e048491379a500211958"),
768 &decode_hex::<32>("63aa40c6e38346c5caf23a6df0a5e6c80889a08647e551b3563449befcfc9733"),
769 );
770 assert_eq!(
771 shared,
772 decode_hex::<32>("279df67a7c4611db4708a0e8282b195e5ac0ed6f4b2f292c6fbd0acac30d1332")
773 );
774
775 let shared = x25519(
776 &decode_hex::<32>("d03edde9f3e7b799045f9ac3793d4a9277dadeadc41bec0290f81f744f73775f"),
777 &decode_hex::<32>("0200000000000000000000000000000000000000000000000000000000000000"),
778 );
779 assert_eq!(
780 shared,
781 decode_hex::<32>("b87a1722cc6c1e2feecb54e97abd5a22acc27616f78f6e315fd2b73d9f221e57")
782 );
783 }
784
785 #[test]
786 fn more_boringssl_vectors() {
787 struct Vector {
788 scalar: [u8; 32],
789 base: [u8; 32],
790 expected: [u8; 32],
791 }
792
793 let vectors = [Vector {
794 scalar: decode_hex::<32>("203161c3159a876a2beaec29d2427fb0c7c30d382cd013d27cc3d393db0daf6f"),
795 base: decode_hex::<32>("6ab95d1abe68c09b005c3db9042cc91ac849f7e94a2a4a9b893678970b7b95bf"),
796 expected: decode_hex::<32>("11edaedc95ff78f563a1c8f15591c071dea092b4d7ecaac8e0387b5a160c4e5d"),
797 }];
798
799 for (i, v) in vectors.iter().enumerate() {
800 let result = x25519(&v.scalar, &v.base);
801 assert_eq!(result, v.expected, "BoringSSL vector {} failed", i);
802 }
803 }
804
805 #[test]
806 fn clamped_zero_scalar_produces_known_public_key() {
807 let scalar = [0u8; 32];
808 let expected = decode_hex::<32>("2fe57da347cd62431528daac5fbb290730fff684afc4cfc2ed90995f58cb3b74");
809 let pub_key = SecretKey::from_bytes(&scalar).public_key().to_bytes();
810 assert_eq!(pub_key, expected, "clamped zero scalar must produce deterministic public key");
811 }
812
813 #[test]
814 fn max_scalar_x25519_against_basepoint() {
815 let scalar = [0xffu8; 32];
816 let pub_key = SecretKey::from_bytes(&scalar).public_key().to_bytes();
817 let direct = x25519(
818 &scalar,
819 &decode_hex::<32>("0900000000000000000000000000000000000000000000000000000000000000"),
820 );
821 assert_eq!(pub_key, direct);
822 }
823
824 #[test]
825 fn x25519_self_ecdh_consistency() {
826 let alice = SecretKey::generate().unwrap();
827 let alice_pub = alice.public_key();
828 let alice_shared = alice.ecdh(&alice_pub).unwrap();
829 assert_eq!(alice_shared.len(), 32);
830 }
831
832 #[test]
833 fn non_canonical_above_p_reduces_correctly() {
834 let scalar = decode_hex::<32>("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475");
835 let (sum, _) = P.add_raw(&U256::from_u64(2));
836 let mut p_plus_2_bytes = sum.to_le_bytes_fixed::<32>();
837 p_plus_2_bytes[31] &= 0x7f;
838 let result = x25519(&scalar, &p_plus_2_bytes);
839
840 let u_2 = decode_hex::<32>("0200000000000000000000000000000000000000000000000000000000000000");
841 let expected = x25519(&scalar, &u_2);
842 assert_eq!(result, expected, "X25519(scalar, p+2) must equal X25519(scalar, 2)");
843 }
844
845 #[test]
848 fn wycheproof_x25519() {
849 let data: serde_json::Value =
850 serde_json::from_str(include_str!("../../testdata/wycheproof/testvectors_v1/x25519_test.json")).unwrap();
851 let mut valid_tested = 0u64;
852 let mut acceptable_tested = 0u64;
853 for group in data["testGroups"].as_array().unwrap() {
854 if group["curve"].as_str() != Some("curve25519") {
855 continue;
856 }
857 for test in group["tests"].as_array().unwrap() {
858 let public_hex = test["public"].as_str().unwrap();
859 let private_hex = test["private"].as_str().unwrap();
860 let expected_shared_hex = test["shared"].as_str().unwrap();
861 let result = test["result"].as_str().unwrap();
862
863 let public_key = decode_hex::<32>(public_hex);
864 let private_key = decode_hex::<32>(private_hex);
865
866 let shared = x25519_checked(&private_key, &public_key);
867
868 if result == "valid" {
869 let shared = shared.expect("valid wycheproof x25519 vector must not error");
870 assert_eq!(
871 hex::encode(shared),
872 expected_shared_hex,
873 "wycheproof x25519 tcId={} shared secret mismatch",
874 test["tcId"]
875 );
876 valid_tested += 1;
877 } else {
878 if let Ok(shared) = shared {
881 assert_eq!(
882 hex::encode(shared),
883 expected_shared_hex,
884 "wycheproof x25519 tcId={} acceptable shared secret mismatch",
885 test["tcId"]
886 );
887 }
888 acceptable_tested += 1;
889 }
890 }
891 }
892 assert!(valid_tested > 0, "no valid wycheproof x25519 tests were run");
893 assert!(acceptable_tested > 0, "no acceptable wycheproof x25519 tests were run");
894 }
895}