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/// X25519 Diffie-Hellman key exchange private key (RFC 7748).
19///
20/// # Generating a key
21///
22/// ```ignore
23/// use crypto::curve25519::x25519::SecretKey;
24///
25/// let alice = SecretKey::generate();
26/// let bob = SecretKey::generate();
27/// ```
28///
29/// # Diffie-Hellman key exchange
30///
31/// ```ignore
32/// use crypto::curve25519::x25519::SecretKey;
33///
34/// let alice = SecretKey::generate();
35/// let bob = SecretKey::generate();
36/// let alice_shared = alice.ecdh(&bob.public_key());
37/// let bob_shared = bob.ecdh(&alice.public_key());
38/// assert_eq!(alice_shared, bob_shared);
39/// ```
40///
41/// # Conversion from Ed25519
42///
43/// An Ed25519 [`SecretKey`](crate::curve25519::ed25519::SecretKey) can be
44/// converted to an X25519 key via [`From<&ed25519::SecretKey>`](From).
45///
46/// # Security
47///
48/// The shared secret produced by [`ecdh`](Self::ecdh) **must not** be used
49/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
50#[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    /// Perform a Diffie-Hellman key exchange to derive a shared secret.
84    /// The shared secret **IS NOT SAFE** to use directly as an encryption key and an additional
85    /// key derivation operation must be applied to it.
86    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/// X25519 Diffie-Hellman key exchange public key (RFC 7748).
127///
128/// # Deserializing from bytes
129///
130/// ```ignore
131/// use crypto::curve25519::x25519::PublicKey;
132///
133/// let bytes = [0u8; 32]; // replace with a peer's public key
134/// let pub_key = PublicKey::from_bytes(&bytes);
135/// ```
136///
137/// # Conversion from Ed25519
138///
139/// An Ed25519 [`PublicKey`](crate::curve25519::ed25519::PublicKey) can be
140/// converted to an X25519 public key via [`TryFrom<&ed25519::PublicKey>`](TryFrom).
141#[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]
479    // fn public_key_exceeds_prime_is_reduced() {
480    //     let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
481    //     let p_bytes = P.to_le_bytes_fixed::<32>();
482    //     let result = x25519(&scalar, &p_bytes);
483    //     assert!(result.is_ok(), "Non-canonical values must be accepted per RFC 7748");
484    // }
485
486    #[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    // --- Wycheproof test vectors ---
687
688    #[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                // assert!(shared.is_ok(), "wycheproof x25519 tcId={} returned error", test["tcId"]);
709                // let shared = shared.unwrap();
710                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}