Skip to main content

crypto/curve25519/
x25519.rs

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    /// Perform a Diffie-Hellman key exchange to derive a shared secret.
52    /// The shared secret **IS NOT SAFE** to use directly as an encryption key and an additional
53    /// key derivation operation must be applied to it.
54    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]
432    // fn public_key_exceeds_prime_is_reduced() {
433    //     let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
434    //     let p_bytes = P.to_le_bytes_fixed::<32>();
435    //     let result = x25519(&scalar, &p_bytes);
436    //     assert!(result.is_ok(), "Non-canonical values must be accepted per RFC 7748");
437    // }
438
439    #[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    // --- Wycheproof test vectors ---
640
641    #[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                // assert!(shared.is_ok(), "wycheproof x25519 tcId={} returned error", test["tcId"]);
662                // let shared = shared.unwrap();
663                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}