Skip to main content

crypto/curve25519/
x25519.rs

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/// X25519 Diffie-Hellman key exchange private key (RFC 7748).
21///
22/// # Generating a key
23///
24/// ```ignore
25/// use crypto::curve25519::x25519::SecretKey;
26///
27/// let alice = SecretKey::generate().unwrap();
28/// let bob = SecretKey::generate().unwrap();
29/// ```
30///
31/// # Diffie-Hellman key exchange
32///
33/// ```ignore
34/// use crypto::curve25519::x25519::SecretKey;
35///
36/// let alice = SecretKey::generate().unwrap();
37/// let bob = SecretKey::generate().unwrap();
38/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
39/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
40/// assert_eq!(alice_shared, bob_shared);
41/// ```
42///
43/// [`ecdh`](Self::ecdh) returns [`EllipticCurveError::InvalidSharedSecret`]
44/// when the computed shared secret is all zeros. This happens whenever the
45/// peer public key is a low-order point (`u = 0`, `u = 1`, `u = p - 1`, or
46/// one of the two order-8 points), because the clamped scalar is a multiple
47/// of the curve's cofactor and therefore annihilates such a point.
48///
49/// **Security impact:** an all-zero shared secret is not secret at all. Every
50/// peer that sends a low-order public key — including a malicious one — knows
51/// the value, so any key derived from it (even through a KDF) is known to the
52/// attacker. This yields a complete confidentiality and authentication break
53/// for protocols that do not independently detect the degenerate output.
54/// Callers should therefore treat the error as a fatal protocol failure and
55/// abort the handshake.
56///
57/// If the caller's protocol already binds both public keys into the derived
58/// key (so that a non-contributory secret cannot be exploited), the error can
59/// be deliberately ignored by substituting the all-zero secret:
60///
61/// ```ignore
62/// let shared = alice.ecdh(&bob.public_key()).unwrap_or_default();
63/// ```
64///
65/// # Conversion from Ed25519
66///
67/// An Ed25519 [`SecretKey`](crate::curve25519::ed25519::SecretKey) can be
68/// converted to an X25519 key via [`From<&ed25519::SecretKey>`](From).
69///
70/// # Security
71///
72/// The shared secret produced by [`ecdh`](Self::ecdh) **must not** be used
73/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
74#[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    /// Generates a fresh random secret key.
83    ///
84    /// Returns [`EllipticCurveError::Random`] when the operating system's
85    /// random number generator is unavailable or fails.
86    #[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    /// Perform a Diffie-Hellman key exchange to derive a shared secret.
113    /// The shared secret **IS NOT SAFE** to use directly as an encryption key and an additional
114    /// key derivation operation must be applied to it.
115    ///
116    /// # Errors
117    ///
118    /// Returns [`EllipticCurveError::InvalidSharedSecret`] if the computed
119    /// shared secret is all zeros, which occurs when `peer` is a low-order
120    /// public key. Such a secret is publicly known and must not be used to
121    /// derive keys unless the protocol independently binds the peer public
122    /// key into the derivation. See the [type-level documentation](SecretKey)
123    /// for details.
124    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/// X25519 Diffie-Hellman key exchange public key (RFC 7748).
168///
169/// # Deserializing from bytes
170///
171/// ```ignore
172/// use crypto::curve25519::x25519::PublicKey;
173///
174/// let bytes = [0u8; 32]; // replace with a peer's public key
175/// let pub_key = PublicKey::from_bytes(&bytes);
176/// ```
177///
178/// # Conversion from Ed25519
179///
180/// An Ed25519 [`PublicKey`](crate::curve25519::ed25519::PublicKey) can be
181/// converted to an X25519 public key via [`TryFrom<&ed25519::PublicKey>`](TryFrom).
182#[derive(Clone, Debug, PartialEq, Eq)]
183pub struct PublicKey {
184    u: FieldElement,
185}
186
187/// Canonical u-coordinates of the low-order points on Curve25519.
188///
189/// These are the points whose order divides the curve cofactor (8). Because
190/// X25519 clamps the scalar to a multiple of 8, multiplying any of them by a
191/// valid scalar yields the point at infinity, encoded as `u = 0`. Non-canonical
192/// encodings (`u >= p`) are reduced by [`FieldElement::from_relaxed_bytes`]
193/// before this comparison, so they are covered too.
194const LOW_ORDER_U: [[u8; KEY_SIZE]; 5] = [
195    // u = 0
196    [0u8; KEY_SIZE],
197    // u = 1
198    {
199        let mut u = [0u8; KEY_SIZE];
200        u[0] = 1;
201        u
202    },
203    // u = p - 1
204    [
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    // order-8 point
209    [
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    // order-8 point
214    [
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    /// Returns `true` if this public key is a low-order point on Curve25519.
234    ///
235    /// Low-order public keys are never produced by honest key generation.
236    /// Passing one to [`SecretKey::ecdh`] produces an all-zero, publicly known
237    /// shared secret, so it must be rejected or otherwise neutralized. The
238    /// comparison is constant-time with respect to the key bytes.
239    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        // `u = p` reduces to 0 and `u = p + 1` reduces to 1; both are low order.
603        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]
644    // fn public_key_exceeds_prime_is_reduced() {
645    //     let scalar = decode_hex::<32>("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a");
646    //     let p_bytes = P.to_le_bytes_fixed::<32>();
647    //     let result = x25519(&scalar, &p_bytes);
648    //     assert!(result.is_ok(), "Non-canonical values must be accepted per RFC 7748");
649    // }
650
651    #[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    // --- Wycheproof test vectors ---
846
847    #[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                    // Non-valid vectors (typically low-order public keys) may be
879                    // rejected or, if accepted, must match the expected secret.
880                    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}