Skip to main content

crypto/
p256.rs

1use big_number::{Uint, mac};
2
3use crate::{EllipticCurveError, Hasher, hmac::Hmac, sha2::Sha256};
4
5/// Size of a P-256 secret key in bytes (32 bytes).
6pub const SECRET_KEY_SIZE: usize = 32;
7/// Size of a compressed P-256 public key in bytes (33 bytes, includes 0x02/0x03 prefix).
8pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 33;
9/// Size of an uncompressed P-256 public key in bytes (65 bytes, includes 0x04 prefix).
10pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 65;
11/// Size of a P-256 ECDSA signature in bytes (64 bytes, r || s).
12pub const SIGNATURE_SIZE: usize = 64;
13/// Size of the raw ECDH shared secret in bytes (32 bytes). **Must not** be used directly
14/// as an encryption key; apply a KDF first.
15pub const ECDH_SHARED_SECRET_SIZE: usize = 32;
16
17/// P-256 (secp256r1) ECDSA secret key.
18///
19/// Supports signing and ECDH key agreement.
20///
21/// # Signing
22///
23/// ```ignore
24/// use crypto::p256::PrivateKey;
25///
26/// let key = PrivateKey::generate().unwrap();
27/// let signature = key.sign(b"message").unwrap();
28/// ```
29///
30/// # ECDH key exchange
31///
32/// ```ignore
33/// use crypto::p256::PrivateKey;
34///
35/// let alice = PrivateKey::generate().unwrap();
36/// let bob = PrivateKey::generate().unwrap();
37/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
38/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
39/// assert_eq!(alice_shared, bob_shared);
40/// ```
41///
42/// # Security
43///
44/// The raw shared secret from [`ecdh`](Self::ecdh) **must not** be used
45/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
46// TODO: zeroize
47#[derive(Clone, Copy, Debug, PartialEq, Eq)]
48pub struct SecretKey {
49    scalar: Scalar,
50    public_point: AffinePoint,
51}
52
53impl SecretKey {
54    #[cfg(feature = "random")]
55    pub fn generate() -> Result<SecretKey, EllipticCurveError> {
56        let key: [u8; SECRET_KEY_SIZE] = crate::random::random_bytes();
57        Self::from_bytes(&key)
58    }
59
60    pub fn from_bytes(key: &[u8; SECRET_KEY_SIZE]) -> Result<SecretKey, EllipticCurveError> {
61        let scalar = Scalar::from_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
62        let public_point = scalar_mul_generator(&scalar)
63            .to_affine()
64            .ok_or(EllipticCurveError::Unspecified)?;
65        Ok(SecretKey {
66            scalar,
67            public_point,
68        })
69    }
70
71    pub fn public_key(&self) -> PublicKey {
72        PublicKey {
73            point: self.public_point,
74        }
75    }
76
77    pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
78        ecdsa_sign_inner(&self.scalar, message)
79    }
80
81    pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
82        ecdh_inner(&self.scalar, &peer_public.point)
83    }
84
85    pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
86        self.scalar.to_bytes()
87    }
88}
89
90/// P-256 (secp256r1) ECDSA public key.
91///
92/// Supports signature verification and ECDH key agreement.
93///
94/// # Verification
95///
96/// ```ignore
97/// use crypto::p256::PrivateKey;
98///
99/// let key = PrivateKey::generate().unwrap();
100/// let signature = key.sign(b"message").unwrap();
101/// assert!(key.public_key().verify(b"message", &signature).is_ok());
102/// ```
103#[derive(Clone, Copy, Debug, PartialEq, Eq)]
104pub struct PublicKey {
105    point: AffinePoint,
106}
107
108impl PublicKey {
109    #[inline]
110    pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
111        let point = AffinePoint::from_sec1_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
112        Ok(PublicKey {
113            point,
114        })
115    }
116
117    /// Build a public key from raw affine x and y coordinates (both
118    /// big-endian, 32 bytes each). Returns `InvalidKey` if the coordinates
119    /// are not a valid point on the P-256 curve.
120    ///
121    /// This is useful when importing keys from formats like JWK where `x`
122    /// and `y` are available directly.
123    #[inline]
124    pub fn from_x_y(x_bytes: &[u8; 32], y_bytes: &[u8; 32]) -> Result<PublicKey, EllipticCurveError> {
125        let x = FieldElement::from_bytes(x_bytes).ok_or(EllipticCurveError::InvalidKey)?;
126        let y = FieldElement::from_bytes(y_bytes).ok_or(EllipticCurveError::InvalidKey)?;
127        let point = AffinePoint::new(x, y).ok_or(EllipticCurveError::InvalidKey)?;
128        Ok(PublicKey {
129            point,
130        })
131    }
132
133    pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
134        ecdsa_verify_inner(&self.point, message, signature)
135    }
136
137    #[inline]
138    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
139        self.point.to_uncompressed_bytes()
140    }
141
142    /// Returns the `X` and `Y` points as big-endian arrays.
143    #[inline]
144    pub fn x_y(&self) -> ([u8; 32], [u8; 32]) {
145        (self.point.x.to_bytes(), self.point.y.to_bytes())
146    }
147}
148
149type U256 = Uint<256, 4>;
150
151const MODULUS_P: U256 = U256::from_limbs([
152    0xffff_ffff_ffff_ffff,
153    0x0000_0000_ffff_ffff,
154    0x0000_0000_0000_0000,
155    0xffff_ffff_0000_0001,
156]);
157
158const MODULUS_N: U256 = U256::from_limbs([
159    0xf3b9_cac2_fc63_2551,
160    0xbce6_faad_a717_9e84,
161    0xffff_ffff_ffff_ffff,
162    0xffff_ffff_0000_0000,
163]);
164
165const P_MINUS_TWO: U256 = U256::from_limbs([
166    0xffff_ffff_ffff_fffd,
167    0x0000_0000_ffff_ffff,
168    0x0000_0000_0000_0000,
169    0xffff_ffff_0000_0001,
170]);
171
172const P_PLUS_ONE_OVER_FOUR: U256 = U256::from_limbs([
173    0x0000_0000_0000_0000,
174    0x0000_0000_4000_0000,
175    0x4000_0000_0000_0000,
176    0x3fff_ffff_c000_0000,
177]);
178
179const N_MINUS_TWO: U256 = U256::from_limbs([
180    0xf3b9_cac2_fc63_254f,
181    0xbce6_faad_a717_9e84,
182    0xffff_ffff_ffff_ffff,
183    0xffff_ffff_0000_0000,
184]);
185
186const CURVE_B: FieldElement = FieldElement(U256::from_limbs([
187    0x3bce_3c3e_27d2_604b,
188    0x651d_06b0_cc53_b0f6,
189    0xb3eb_bd55_7698_86bc,
190    0x5ac6_35d8_aa3a_93e7,
191]));
192
193const GENERATOR_X: FieldElement = FieldElement(U256::from_limbs([
194    0xf4a1_3945_d898_c296,
195    0x7703_7d81_2deb_33a0,
196    0xf8bc_e6e5_63a4_40f2,
197    0x6b17_d1f2_e12c_4247,
198]));
199
200const GENERATOR_Y: FieldElement = FieldElement(U256::from_limbs([
201    0xcbb6_4068_37bf_51f5,
202    0x2bce_3357_6b31_5ece,
203    0x8ee7_eb4a_7c0f_9e16,
204    0x4fe3_42e2_fe1a_7f9b,
205]));
206
207// P-256 fast reduction constants: S^i = 2^(64i) mod p
208// Verified against Python with 100k random tests.
209const S4: [u64; 4] = [
210    0x0000000000000001,
211    0xffffffff00000000,
212    0xffffffffffffffff,
213    0x00000000fffffffe,
214];
215
216const S5: [u64; 4] = [
217    0x00000000ffffffff,
218    0x0000000100000001,
219    0xfffffffeffffffff,
220    0xfffffffe00000000,
221];
222
223const S6: [u64; 4] = [
224    0xfffffffefffffffe,
225    0x00000002ffffffff,
226    0x0000000000000002,
227    0xfffffffe00000001,
228];
229
230const S7: [u64; 4] = [
231    0xfffffffeffffffff,
232    0xfffffffffffffffe,
233    0x0000000200000000,
234    0x0000000000000003,
235];
236
237// Branch-free u128 select: returns a if choice else b.
238#[inline]
239fn ct_select_u128(a: u128, b: u128, choice: bool) -> u128 {
240    let mask = (choice as u128).wrapping_neg();
241    (a & mask) | (b & !mask)
242}
243
244// P-256 fast modular multiplication using u128 accumulators.
245// All loops run fixed iteration counts with ct_select for constant-time.
246fn p256_fast_mul_mod(a: &U256, b: &U256) -> U256 {
247    let al = a.limbs;
248    let bl = b.limbs;
249
250    let mut prod = [0u64; 8];
251    for i in 0..4 {
252        let mut carry = 0u64;
253        for j in 0..4 {
254            let (v, cc) = mac(prod[i + j], al[i], bl[j], carry);
255            prod[i + j] = v;
256            carry = cc;
257        }
258        prod[i + 4] = carry;
259    }
260
261    const MASK: u128 = 0xffffffffffffffff;
262    let c0 = [S4[0] as u128, S4[1] as u128, S4[2] as u128, S4[3] as u128];
263    let c1 = [S5[0] as u128, S5[1] as u128, S5[2] as u128, S5[3] as u128];
264    let c2 = [S6[0] as u128, S6[1] as u128, S6[2] as u128, S6[3] as u128];
265    let c3 = [S7[0] as u128, S7[1] as u128, S7[2] as u128, S7[3] as u128];
266    let coeffs = [c0, c1, c2, c3];
267
268    let mut r0 = prod[0] as u128;
269    let mut r1 = prod[1] as u128;
270    let mut r2 = prod[2] as u128;
271    let mut r3 = prod[3] as u128;
272
273    for i in 0..4 {
274        let w = prod[4 + i] as u128;
275        let c = coeffs[i];
276
277        r0 = r0.wrapping_add(w.wrapping_mul(c[0]));
278        r1 = r1.wrapping_add(w.wrapping_mul(c[1]));
279        r2 = r2.wrapping_add(w.wrapping_mul(c[2]));
280        r3 = r3.wrapping_add(w.wrapping_mul(c[3]));
281
282        // Fixed 4 iterations: carry propagation + conditional residual reduction.
283        for _ in 0..4 {
284            let carry = r0 >> 64;
285            r1 = r1.wrapping_add(carry);
286            r0 &= MASK;
287            let carry = r1 >> 64;
288            r2 = r2.wrapping_add(carry);
289            r1 &= MASK;
290            let carry = r2 >> 64;
291            r3 = r3.wrapping_add(carry);
292            r2 &= MASK;
293
294            let residual = r3 >> 64;
295            let need_reduce = residual != 0;
296
297            // Compute reduced version (applied if need_reduce) and original.
298            let rr3 = r3 & MASK;
299            let rr0 = r0.wrapping_add(residual.wrapping_mul(c0[0]));
300            let rr1 = r1.wrapping_add(residual.wrapping_mul(c0[1]));
301            let rr2 = r2.wrapping_add(residual.wrapping_mul(c0[2]));
302            let rr3r = rr3.wrapping_add(residual.wrapping_mul(c0[3]));
303
304            // ct_select between reduced and non-reduced based on need_reduce.
305            r0 = ct_select_u128(rr0, r0, need_reduce);
306            r1 = ct_select_u128(rr1, r1, need_reduce);
307            r2 = ct_select_u128(rr2, r2, need_reduce);
308            r3 = ct_select_u128(rr3r, r3, need_reduce);
309        }
310    }
311
312    // Fixed 8 conditional subtractions (result may be up to ~16×p).
313    let mut result = U256::from_limbs([r0 as u64, r1 as u64, r2 as u64, r3 as u64]);
314    for _ in 0..8 {
315        let (sub, borrow) = result.sub_raw(&MODULUS_P);
316        result = U256::ct_select(&sub, &result, borrow == 0);
317    }
318    result
319}
320
321#[derive(Clone, Copy, Debug, PartialEq, Eq)]
322struct FieldElement(U256);
323
324impl FieldElement {
325    const ZERO: Self = Self(U256::ZERO);
326    const ONE: Self = Self(U256::ONE);
327
328    #[inline]
329    fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
330        let value = U256::from_be_slice(bytes);
331        if value.ct_ge(&MODULUS_P) {
332            None
333        } else {
334            Some(Self(value))
335        }
336    }
337
338    #[inline]
339    fn to_bytes(self) -> [u8; 32] {
340        self.0.to_be_bytes_fixed::<32>()
341    }
342
343    #[inline]
344    fn is_zero(&self) -> bool {
345        self.0.is_zero()
346    }
347
348    #[inline]
349    fn is_odd(&self) -> bool {
350        self.0.is_odd()
351    }
352
353    #[inline]
354    fn add(self, rhs: Self) -> Self {
355        Self(self.0.add_mod(&rhs.0, &MODULUS_P))
356    }
357
358    #[inline]
359    fn sub(self, rhs: Self) -> Self {
360        Self(self.0.sub_mod(&rhs.0, &MODULUS_P))
361    }
362
363    #[inline]
364    fn double(self) -> Self {
365        Self(self.0.double_mod(&MODULUS_P))
366    }
367
368    #[inline]
369    fn square(self) -> Self {
370        self.mul(self)
371    }
372
373    #[inline]
374    fn mul(self, rhs: Self) -> Self {
375        Self(p256_fast_mul_mod(&self.0, &rhs.0))
376    }
377
378    #[inline]
379    fn triple(self) -> Self {
380        self.double().add(self)
381    }
382
383    #[inline]
384    fn negate(self) -> Self {
385        let (diff, _) = MODULUS_P.sub_raw(&self.0);
386        Self(U256::ct_select(&U256::ZERO, &diff, self.is_zero()))
387    }
388
389    #[inline]
390    fn pow(self, exponent: &U256) -> Self {
391        let mut result = Self::ONE;
392        let mut i = 256usize;
393        while i > 0 {
394            i -= 1;
395            result = result.square();
396            let product = result.mul(self);
397            result = Self::select(&product, &result, exponent.bit(i));
398        }
399        result
400    }
401
402    #[inline]
403    fn invert(self) -> Option<Self> {
404        Some(self.pow(&P_MINUS_TWO))
405    }
406
407    #[inline]
408    fn sqrt(self) -> Option<Self> {
409        let candidate = self.pow(&P_PLUS_ONE_OVER_FOUR);
410        if U256::ct_eq(&self.0, &candidate.square().0) {
411            Some(candidate)
412        } else {
413            None
414        }
415    }
416
417    #[inline]
418    fn select(a: &Self, b: &Self, choice: bool) -> Self {
419        Self(U256::ct_select(&a.0, &b.0, choice))
420    }
421}
422
423#[derive(Clone, Copy, Debug, PartialEq, Eq)]
424struct Scalar(U256);
425
426impl Scalar {
427    const ZERO: Self = Self(U256::ZERO);
428    const ONE: Self = Self(U256::ONE);
429
430    #[inline]
431    fn from_bytes(bytes: &[u8; 32]) -> Option<Self> {
432        let value = U256::from_be_slice(bytes);
433        if value.is_zero() || value.ct_ge(&MODULUS_N) {
434            None
435        } else {
436            Some(Self(value))
437        }
438    }
439
440    #[inline]
441    fn from_hash(hash: &[u8; 32]) -> Self {
442        let value = U256::from_be_slice(hash);
443        let (sub_value, _) = value.sub_raw(&MODULUS_N);
444        let reduced = U256::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N));
445        Self(reduced)
446    }
447
448    #[inline]
449    fn to_bytes(self) -> [u8; 32] {
450        self.0.to_be_bytes_fixed::<32>()
451    }
452
453    #[inline]
454    fn is_zero(&self) -> bool {
455        self.0.is_zero()
456    }
457
458    #[inline]
459    fn bit(&self, index: usize) -> bool {
460        self.0.bit(index)
461    }
462
463    #[inline]
464    fn add(self, rhs: Self) -> Self {
465        Self(self.0.add_mod(&rhs.0, &MODULUS_N))
466    }
467
468    #[cfg(test)]
469    #[inline]
470    fn sub(self, rhs: Self) -> Self {
471        Self(self.0.sub_mod(&rhs.0, &MODULUS_N))
472    }
473
474    #[inline]
475    fn mul(self, rhs: Self) -> Self {
476        Self(self.0.mul_mod(&rhs.0, &MODULUS_N))
477    }
478
479    #[inline]
480    fn invert(self) -> Option<Self> {
481        Some(Self(self.scalar_pow(&N_MINUS_TWO)))
482    }
483
484    #[inline]
485    fn scalar_pow(self, exponent: &U256) -> U256 {
486        let mut result = Scalar::ONE;
487        let mut i = 256usize;
488        while i > 0 {
489            i -= 1;
490            result = result.mul(result);
491            let product = result.mul(self);
492            result = Scalar::select(&product, &result, exponent.bit(i));
493        }
494        result.0
495    }
496
497    #[inline]
498    fn select(a: &Self, b: &Self, choice: bool) -> Self {
499        Self(U256::ct_select(&a.0, &b.0, choice))
500    }
501}
502
503#[derive(Clone, Copy, Debug, PartialEq, Eq)]
504struct AffinePoint {
505    x: FieldElement,
506    y: FieldElement,
507    infinity: bool,
508}
509
510impl AffinePoint {
511    const GENERATOR: Self = Self {
512        x: GENERATOR_X,
513        y: GENERATOR_Y,
514        infinity: false,
515    };
516
517    #[inline]
518    fn new(x: FieldElement, y: FieldElement) -> Option<Self> {
519        let point = Self {
520            x,
521            y,
522            infinity: false,
523        };
524        if point.is_on_curve() { Some(point) } else { None }
525    }
526
527    #[inline]
528    fn is_on_curve(&self) -> bool {
529        if self.infinity {
530            return false;
531        }
532        let x2 = self.x.square();
533        let x3 = x2.mul(self.x);
534        let rhs = x3.sub(self.x.triple()).add(CURVE_B);
535        self.y.square() == rhs
536    }
537
538    #[inline]
539    fn to_uncompressed_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
540        let mut out = [0u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
541        out[0] = 0x04;
542        out[1..33].copy_from_slice(&self.x.to_bytes());
543        out[33..65].copy_from_slice(&self.y.to_bytes());
544        out
545    }
546
547    #[cfg(test)]
548    #[inline]
549    fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
550        let mut out = [0u8; PUBLIC_KEY_COMPRESSED_SIZE];
551        out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
552        out[1..33].copy_from_slice(&self.x.to_bytes());
553        out
554    }
555
556    fn from_sec1_bytes(bytes: &[u8]) -> Option<Self> {
557        match bytes.len() {
558            PUBLIC_KEY_UNCOMPRESSED_SIZE if bytes[0] == 0x04 => {
559                let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap())?;
560                let y = FieldElement::from_bytes(bytes[33..65].try_into().unwrap())?;
561                Self::new(x, y)
562            }
563            PUBLIC_KEY_COMPRESSED_SIZE if bytes[0] == 0x02 || bytes[0] == 0x03 => {
564                let x = FieldElement::from_bytes(bytes[1..33].try_into().unwrap())?;
565                let rhs = x.square().mul(x).sub(x.triple()).add(CURVE_B);
566                let y = rhs.sqrt()?;
567                let y_is_odd = y.is_odd();
568                let select_neg = y_is_odd != (bytes[0] == 0x03);
569                let y = FieldElement::select(&y.negate(), &y, select_neg);
570                Self::new(x, y)
571            }
572            _ => None,
573        }
574    }
575}
576
577#[derive(Clone, Copy, Debug, PartialEq, Eq)]
578struct ProjectivePoint {
579    x: FieldElement,
580    y: FieldElement,
581    z: FieldElement,
582}
583
584impl ProjectivePoint {
585    const IDENTITY: Self = Self {
586        x: FieldElement::ZERO,
587        y: FieldElement::ONE,
588        z: FieldElement::ZERO,
589    };
590
591    #[cfg(test)]
592    #[inline]
593    fn from_affine(point: &AffinePoint) -> Self {
594        if point.infinity {
595            Self::IDENTITY
596        } else {
597            Self {
598                x: point.x,
599                y: point.y,
600                z: FieldElement::ONE,
601            }
602        }
603    }
604
605    #[inline]
606    fn is_identity(&self) -> bool {
607        self.z.is_zero()
608    }
609
610    #[inline]
611    fn select(a: &Self, b: &Self, choice: bool) -> Self {
612        Self {
613            x: FieldElement::select(&a.x, &b.x, choice),
614            y: FieldElement::select(&a.y, &b.y, choice),
615            z: FieldElement::select(&a.z, &b.z, choice),
616        }
617    }
618
619    #[inline]
620    fn to_affine(&self) -> Option<AffinePoint> {
621        if self.is_identity() {
622            return None;
623        }
624        let z_inv = self.z.invert()?;
625        AffinePoint::new(self.x.mul(z_inv), self.y.mul(z_inv))
626    }
627
628    fn add(&self, rhs: &Self) -> Self {
629        let xx = self.x.mul(rhs.x);
630        let yy = self.y.mul(rhs.y);
631        let zz = self.z.mul(rhs.z);
632        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
633        let yz_pairs = self.y.add(self.z).mul(rhs.y.add(rhs.z)).sub(yy.add(zz));
634        let xz_pairs = self.x.add(self.z).mul(rhs.x.add(rhs.z)).sub(xx.add(zz));
635
636        let bzz_part = xz_pairs.sub(CURVE_B.mul(zz));
637        let bzz3_part = bzz_part.triple();
638        let yy_m_bzz3 = yy.sub(bzz3_part);
639        let yy_p_bzz3 = yy.add(bzz3_part);
640
641        let zz3 = zz.triple();
642        let bxz_part = CURVE_B.mul(xz_pairs).sub(zz3.add(xx));
643        let bxz3_part = bxz_part.triple();
644        let xx3_m_zz3 = xx.triple().sub(zz3);
645
646        Self {
647            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
648            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
649            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
650        }
651    }
652
653    fn add_mixed(&self, rhs: &AffinePoint) -> Self {
654        if rhs.infinity {
655            return *self;
656        }
657
658        let xx = self.x.mul(rhs.x);
659        let yy = self.y.mul(rhs.y);
660        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
661        let yz_pairs = rhs.y.mul(self.z).add(self.y);
662        let xz_pairs = rhs.x.mul(self.z).add(self.x);
663
664        let bz_part = xz_pairs.sub(CURVE_B.mul(self.z));
665        let bz3_part = bz_part.triple();
666        let yy_m_bzz3 = yy.sub(bz3_part);
667        let yy_p_bzz3 = yy.add(bz3_part);
668
669        let z3 = self.z.triple();
670        let bxz_part = CURVE_B.mul(xz_pairs).sub(z3.add(xx));
671        let bxz3_part = bxz_part.triple();
672        let xx3_m_zz3 = xx.triple().sub(z3);
673
674        Self {
675            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
676            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
677            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
678        }
679    }
680
681    fn double(&self) -> Self {
682        let xx = self.x.square();
683        let yy = self.y.square();
684        let zz = self.z.square();
685        let xy2 = self.x.mul(self.y).double();
686        let xz2 = self.x.mul(self.z).double();
687
688        let bzz_part = CURVE_B.mul(zz).sub(xz2);
689        let bzz3_part = bzz_part.triple();
690        let yy_m_bzz3 = yy.sub(bzz3_part);
691        let yy_p_bzz3 = yy.add(bzz3_part);
692        let y_frag = yy_p_bzz3.mul(yy_m_bzz3);
693        let x_frag = yy_m_bzz3.mul(xy2);
694
695        let zz3 = zz.triple();
696        let bxz2_part = CURVE_B.mul(xz2).sub(zz3.add(xx));
697        let bxz6_part = bxz2_part.triple();
698        let xx3_m_zz3 = xx.triple().sub(zz3);
699
700        let y = y_frag.add(xx3_m_zz3.mul(bxz6_part));
701        let yz2 = self.y.mul(self.z).double();
702        let x = x_frag.sub(bxz6_part.mul(yz2));
703        let z = yz2.mul(yy).double().double();
704
705        Self {
706            x,
707            y,
708            z,
709        }
710    }
711}
712
713fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
714    scalar_mul_affine(&AffinePoint::GENERATOR, scalar)
715}
716
717fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
718    let mut acc = ProjectivePoint::IDENTITY;
719    let mut bit = 256usize;
720    while bit > 0 {
721        bit -= 1;
722        acc = acc.double();
723        let candidate = acc.add_mixed(base);
724        acc = ProjectivePoint::select(&candidate, &acc, scalar.bit(bit));
725    }
726    acc
727}
728
729#[inline]
730fn hash_message(message: &[u8]) -> [u8; 32] {
731    let digest = Sha256::hash(message);
732    return digest.as_ref().try_into().unwrap();
733}
734
735#[inline]
736fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
737    let mac = Hmac::<Sha256>::mac(key, data);
738    return mac.as_ref().try_into().unwrap();
739}
740
741fn bits2octets(hash: &[u8; 32]) -> [u8; 32] {
742    Scalar::from_hash(hash).to_bytes()
743}
744
745fn rfc6979_init_state(private_key: &Scalar, message_hash: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
746    let x = private_key.to_bytes();
747    let h1 = bits2octets(message_hash);
748
749    let mut v = [0x01u8; 32];
750    let mut k = [0u8; 32];
751
752    let mut buf = [0u8; 97];
753    buf[..32].copy_from_slice(&v);
754    buf[32] = 0x00;
755    buf[33..65].copy_from_slice(&x);
756    buf[65..97].copy_from_slice(&h1);
757    k = hmac_sha256(&k, &buf);
758    v = hmac_sha256(&k, &v);
759
760    buf[..32].copy_from_slice(&v);
761    buf[32] = 0x01;
762    k = hmac_sha256(&k, &buf);
763    v = hmac_sha256(&k, &v);
764
765    (k, v)
766}
767
768fn rfc6979_retry(k: &mut [u8; 32], v: &mut [u8; 32]) {
769    let mut retry_buf = [0u8; 33];
770    retry_buf[..32].copy_from_slice(v);
771    retry_buf[32] = 0x00;
772    *k = hmac_sha256(k, &retry_buf);
773    *v = hmac_sha256(k, v);
774}
775
776// Returns the post-retry state without mutating, for constant-time selection.
777fn rfc6979_retry_clone(k: &[u8; 32], v: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
778    let mut retry_buf = [0u8; 33];
779    retry_buf[..32].copy_from_slice(v);
780    retry_buf[32] = 0x00;
781    let k_new = hmac_sha256(k, &retry_buf);
782    let v_new = hmac_sha256(&k_new, v);
783    (k_new, v_new)
784}
785
786// Branch-free byte-level select: returns a[i] if choice else b[i].
787fn ct_select_bytes<const N: usize>(a: &[u8; N], b: &[u8; N], choice: bool) -> [u8; N] {
788    let mask = (choice as u8).wrapping_neg();
789    let mut out = [0u8; N];
790    for i in 0..N {
791        out[i] = (a[i] & mask) | (b[i] & !mask);
792    }
793    out
794}
795
796fn rfc6979_generate_k(private_key: &Scalar, message_hash: &[u8; 32]) -> Scalar {
797    let (mut k, mut v) = rfc6979_init_state(private_key, message_hash);
798
799    // Fixed 3 iterations with constant-time state selection.
800    // In each iteration we generate one HMAC output, check validity,
801    // and ct_select between keeping the original state (k valid) or
802    // replacing it with the retry state (k invalid).
803    //
804    // The FIRST valid candidate is captured and returned after the loop.
805    // On the first iteration this matches the original RFC 6979 behavior.
806    let mut candidate = [0u8; 32];
807    let mut found = false;
808
809    for _ in 0..3 {
810        v = hmac_sha256(&k, &v);
811        let val = U256::from_be_slice(&v);
812        let is_valid = !val.is_zero() && !val.ct_ge(&MODULUS_N);
813
814        // Capture the first valid candidate (ct_select: if valid and not yet found, take v)
815        let take = is_valid && !found;
816        candidate = ct_select_bytes(&v, &candidate, take);
817        found = found || is_valid;
818
819        // Advance DRBG: if invalid, replace state with retry state
820        let (k_retry, v_retry) = rfc6979_retry_clone(&k, &v);
821        k = ct_select_bytes(&k, &k_retry, !is_valid);
822        v = ct_select_bytes(&v, &v_retry, !is_valid);
823    }
824
825    if found {
826        // Safety: candidate was produced by Scalar::from_bytes succeeding above.
827        return Scalar::from_bytes(&candidate).unwrap_or(Scalar::ZERO);
828    }
829
830    // Fallback (P > 2^-96): one more HMAC step
831    v = hmac_sha256(&k, &v);
832    if let Some(sc) = Scalar::from_bytes(&v) {
833        return sc;
834    }
835
836    // Astronomically unlikely retry loop
837    loop {
838        v = hmac_sha256(&k, &v);
839        if let Some(sc) = Scalar::from_bytes(&v) {
840            return sc;
841        }
842        rfc6979_retry(&mut k, &mut v);
843    }
844}
845
846fn parse_secret_key(private_key: &[u8; SECRET_KEY_SIZE]) -> Result<Scalar, EllipticCurveError> {
847    Scalar::from_bytes(private_key).ok_or(EllipticCurveError::InvalidKey)
848}
849
850fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, EllipticCurveError> {
851    AffinePoint::from_sec1_bytes(public_key).ok_or(EllipticCurveError::InvalidKey)
852}
853
854#[cfg(test)]
855fn derive_public_key_uncompressed(
856    private_key: &[u8; SECRET_KEY_SIZE],
857) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], EllipticCurveError> {
858    let scalar = parse_secret_key(private_key)?;
859    let point = scalar_mul_generator(&scalar)
860        .to_affine()
861        .ok_or(EllipticCurveError::Unspecified)?;
862    Ok(point.to_uncompressed_bytes())
863}
864
865#[cfg(test)]
866fn derive_public_key_compressed(
867    private_key: &[u8; SECRET_KEY_SIZE],
868) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], EllipticCurveError> {
869    let scalar = parse_secret_key(private_key)?;
870    let point = scalar_mul_generator(&scalar)
871        .to_affine()
872        .ok_or(EllipticCurveError::Unspecified)?;
873    Ok(point.to_compressed_bytes())
874}
875
876fn ecdh_inner(scalar: &Scalar, peer_point: &AffinePoint) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
877    let shared_point = scalar_mul_affine(peer_point, scalar)
878        .to_affine()
879        .ok_or(EllipticCurveError::Unspecified)?;
880    Ok(shared_point.x.to_bytes())
881}
882
883pub fn ecdh(
884    secret_key: &[u8; SECRET_KEY_SIZE],
885    peer_public_key: &[u8],
886) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
887    let scalar = parse_secret_key(secret_key)?;
888    let peer_point = parse_public_key(peer_public_key)?;
889    ecdh_inner(&scalar, &peer_point)
890}
891
892fn ecdsa_sign_inner(scalar: &Scalar, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
893    let message_hash = hash_message(message);
894    let z = Scalar::from_hash(&message_hash);
895
896    // Fixed 2-iteration loop for r=0/s=0 retry:
897    // First iteration almost always succeeds (r=0 probability ≈ 2^-256).
898    // Second iteration is only reached if the first produced r=0 or s=0,
899    // which is astronomically unlikely. The fixed count avoids timing leaks.
900    for _ in 0..2 {
901        let k = rfc6979_generate_k(scalar, &message_hash);
902
903        let r_point = scalar_mul_generator(&k)
904            .to_affine()
905            .ok_or(EllipticCurveError::Unspecified)?;
906        let r = Scalar::from_hash(&r_point.x.to_bytes());
907        if r.is_zero() {
908            continue;
909        }
910
911        let kinv = k.invert().ok_or(EllipticCurveError::Unspecified)?;
912        let s = kinv.mul(z.add(r.mul(*scalar)));
913        if s.is_zero() {
914            continue;
915        }
916
917        let mut out = [0u8; SIGNATURE_SIZE];
918        out[..32].copy_from_slice(&r.to_bytes());
919        out[32..].copy_from_slice(&s.to_bytes());
920        return Ok(out);
921    }
922
923    Err(EllipticCurveError::Unspecified)
924}
925
926fn ecdsa_verify_inner(
927    public_point: &AffinePoint,
928    message: &[u8],
929    signature: &[u8; SIGNATURE_SIZE],
930) -> Result<(), EllipticCurveError> {
931    let r = Scalar::from_bytes(signature[..32].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
932    let s = Scalar::from_bytes(signature[32..].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
933    let z = Scalar::from_hash(&hash_message(message));
934
935    let w = s.invert().ok_or(EllipticCurveError::Unspecified)?;
936    let u1 = z.mul(w);
937    let u2 = r.mul(w);
938
939    let point = scalar_mul_generator(&u1).add(&scalar_mul_affine(public_point, &u2));
940    let affine = point.to_affine().ok_or(EllipticCurveError::Unspecified)?;
941    let x_mod_n = Scalar::from_hash(&affine.x.to_bytes());
942
943    if x_mod_n == r {
944        Ok(())
945    } else {
946        Err(EllipticCurveError::Unspecified)
947    }
948}
949
950pub fn is_valid_public_key(public_key: &[u8]) -> bool {
951    AffinePoint::from_sec1_bytes(public_key).is_some()
952}
953
954#[cfg(test)]
955mod tests {
956    use super::*;
957
958    fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
959        let bytes = hex::decode(hex_bytes).unwrap();
960        assert_eq!(bytes.len(), N);
961        let mut out = [0u8; N];
962        out.copy_from_slice(&bytes);
963        out
964    }
965
966    // Read a DER TLV (tag-length-value) item.
967    // Returns (tag, value) or None on error.
968    fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
969        if *offset >= data.len() {
970            return None;
971        }
972        let tag = data[*offset];
973        *offset += 1;
974        if *offset >= data.len() {
975            return None;
976        }
977        let len_byte = data[*offset];
978        *offset += 1;
979        let (len, _) = if len_byte & 0x80 != 0 {
980            let num_bytes = (len_byte & 0x7f) as usize;
981            if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
982                return None;
983            }
984            // DER requires the first length byte to be non-zero when
985            // num_bytes > 1, otherwise shorter encoding would suffice
986            if num_bytes > 1 && data[*offset] == 0 {
987                return None;
988            }
989            let mut l = 0usize;
990            for i in 0..num_bytes {
991                l = (l << 8) | data[*offset + i] as usize;
992            }
993            if l < 128 {
994                return None;
995            }
996            *offset += num_bytes;
997            (l, num_bytes + 1)
998        } else {
999            (len_byte as usize, 1)
1000        };
1001        if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
1002            return None;
1003        }
1004        let value = &data[*offset..*offset + len];
1005        *offset = (*offset).checked_add(len)?;
1006        Some((tag, value))
1007    }
1008
1009    // Convert a DER-encoded ECDSA signature (SEQUENCE { INTEGER r, INTEGER s })
1010    // to P1363 format (r || s, each 32 bytes).
1011    fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 64]> {
1012        let mut offset = 0;
1013        let (tag, inner) = der_read_tlv(der, &mut offset)?;
1014        if tag != 0x30 {
1015            return None;
1016        }
1017        // DER signature must be fully consumed (no trailing bytes)
1018        if offset != der.len() {
1019            return None;
1020        }
1021        let mut inner_offset = 0;
1022        let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
1023        if rtag != 0x02 || rval.is_empty() || rval.len() > 33 {
1024            return None;
1025        }
1026        let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
1027        if stag != 0x02 || sval.is_empty() || sval.len() > 33 {
1028            return None;
1029        }
1030        // Strict DER requires no trailing data in the SEQUENCE
1031        if inner_offset != inner.len() {
1032            return None;
1033        }
1034        // DER INTEGER encoding rules:
1035        // - Must use minimal number of bytes.
1036        // - If the high bit would be set, prepend 0x00.
1037        // - If leading 0x00 is used, the next byte must have high bit set.
1038        let r_valid = if rval.len() == 32 && rval[0] >= 0x80 {
1039            false
1040        } else if rval.len() == 33 && rval[0] != 0 {
1041            false
1042        } else if rval.len() == 33 && rval[0] == 0 && rval[1] < 0x80 {
1043            false
1044        } else if rval.len() > 33 {
1045            false
1046        } else {
1047            true
1048        };
1049        let s_valid = if sval.len() == 32 && sval[0] >= 0x80 {
1050            false
1051        } else if sval.len() == 33 && sval[0] != 0 {
1052            false
1053        } else if sval.len() == 33 && sval[0] == 0 && sval[1] < 0x80 {
1054            false
1055        } else if sval.len() > 33 {
1056            false
1057        } else {
1058            true
1059        };
1060        if !r_valid || !s_valid {
1061            return None;
1062        }
1063
1064        let r_trimmed = if rval.len() == 33 && rval[0] == 0 {
1065            &rval[1..]
1066        } else {
1067            rval
1068        };
1069        let s_trimmed = if sval.len() == 33 && sval[0] == 0 {
1070            &sval[1..]
1071        } else {
1072            sval
1073        };
1074        if r_trimmed.len() > 32 || s_trimmed.len() > 32 {
1075            return None;
1076        }
1077        let mut sig = [0u8; 64];
1078        sig[32 - r_trimmed.len()..32].copy_from_slice(r_trimmed);
1079        sig[64 - s_trimmed.len()..64].copy_from_slice(s_trimmed);
1080        Some(sig)
1081    }
1082
1083    // Extract the raw SEC1 uncompressed point from a DER SubjectPublicKeyInfo
1084    // that uses the named secp256r1 curve (explicit parameters are rejected).
1085    fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
1086        let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
1087        let secp256r1_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07];
1088        let mut offset = 0;
1089        let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
1090        let mut inner = 0;
1091        // Parse AlgorithmIdentifier SEQUENCE to verify it uses secp256r1 OID
1092        let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
1093        if _alg_tag != 0x30 {
1094            return None;
1095        }
1096        // First element must be OID ecPublicKey
1097        let mut ai = 0;
1098        let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
1099        if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
1100            return None;
1101        }
1102        // Second element must be OID secp256r1 (named curve), not explicit params
1103        let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
1104        if oid2_tag != 0x06 || oid2 != secp256r1_oid {
1105            return None;
1106        }
1107        // Read BIT STRING
1108        let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
1109        if _bs_tag != 0x03 || bs_val.is_empty() {
1110            return None;
1111        }
1112        // Skip the unused-bits byte
1113        Some(bs_val[1..].to_vec())
1114    }
1115
1116    #[test]
1117    fn from_x_y_matches_generator() {
1118        let key = PublicKey::from_x_y(
1119            &hex::decode("6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296")
1120                .unwrap()
1121                .try_into()
1122                .unwrap(),
1123            &hex::decode("4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5")
1124                .unwrap()
1125                .try_into()
1126                .unwrap(),
1127        )
1128        .unwrap();
1129        let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1130        assert_eq!(key, from_sec1);
1131    }
1132
1133    #[test]
1134    fn from_x_y_rejects_off_curve() {
1135        assert!(PublicKey::from_x_y(&[0u8; 32], &[0u8; 32]).is_err());
1136    }
1137
1138    #[test]
1139    fn derive_public_key_generator_matches_sec1_base_point() {
1140        let mut private_key = [0u8; 32];
1141        private_key[31] = 1;
1142        let derived = derive_public_key_uncompressed(&private_key).unwrap();
1143        let expected = decode_hex::<65>(
1144            "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296\
1145             4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
1146        );
1147        assert_eq!(derived, expected);
1148    }
1149
1150    #[test]
1151    fn derive_public_key_matches_rfc6979_vector() {
1152        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1153        let expected = decode_hex::<65>(
1154            "0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6\
1155             7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1156        );
1157        assert_eq!(derive_public_key_uncompressed(&private_key).unwrap(), expected);
1158        assert_eq!(
1159            derive_public_key_compressed(&private_key).unwrap(),
1160            decode_hex::<33>("0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6"),
1161        );
1162    }
1163
1164    #[test]
1165    fn ecdsa_sign_matches_rfc6979_vectors() {
1166        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1167        let key = SecretKey::from_bytes(&private_key).unwrap();
1168        let sample_signature = key.sign(b"sample").unwrap();
1169        let expected_sample = decode_hex::<64>(
1170            "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\
1171             f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
1172        );
1173        assert_eq!(sample_signature, expected_sample);
1174
1175        let test_signature = key.sign(b"test").unwrap();
1176        let expected_test = decode_hex::<64>(
1177            "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367\
1178             019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
1179        );
1180        assert_eq!(test_signature, expected_test);
1181    }
1182
1183    #[test]
1184    fn rfc6979_nonce_point_x_matches_signature_r() {
1185        let nonce = decode_hex::<32>("a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60");
1186        let public = derive_public_key_uncompressed(&nonce).unwrap();
1187        assert_eq!(
1188            &public[1..33],
1189            &decode_hex::<32>("efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716")
1190        );
1191    }
1192
1193    #[test]
1194    fn rfc6979_nonce_generation_matches_known_value() {
1195        let private_key = Scalar::from_bytes(&decode_hex::<32>(
1196            "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
1197        ))
1198        .unwrap();
1199        let hash = hash_message(b"sample");
1200        assert_eq!(
1201            rfc6979_generate_k(&private_key, &hash).to_bytes(),
1202            decode_hex::<32>("a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60")
1203        );
1204    }
1205
1206    #[test]
1207    fn rfc6979_intermediate_hmac_values_match() {
1208        let x = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1209        let h1 = hash_message(b"sample");
1210        let mut v = [0x01u8; 32];
1211        let mut k = [0u8; 32];
1212
1213        let mut buf = [0u8; 97];
1214        buf[..32].copy_from_slice(&v);
1215        buf[32] = 0x00;
1216        buf[33..65].copy_from_slice(&x);
1217        buf[65..97].copy_from_slice(&h1);
1218        k = hmac_sha256(&k, &buf);
1219        assert_eq!(
1220            k,
1221            decode_hex::<32>("122db1de98dae4dfa33f2da8e98494c80bff807b479fd79261b37e25f267ee58")
1222        );
1223        v = hmac_sha256(&k, &v);
1224        assert_eq!(
1225            v,
1226            decode_hex::<32>("c9947803a747fc60c23535fdcc13b5ca566b48221ca67d4964d22daa48275844")
1227        );
1228
1229        buf[..32].copy_from_slice(&v);
1230        buf[32] = 0x01;
1231        k = hmac_sha256(&k, &buf);
1232        assert_eq!(
1233            k,
1234            decode_hex::<32>("b6d4f98ebae70aa15a2238ade4e20ab323fc1e777d22f0c582d8ef2e6ba73569")
1235        );
1236        v = hmac_sha256(&k, &v);
1237        assert_eq!(
1238            v,
1239            decode_hex::<32>("bae57fe256de2de806b10635497237e7bae96754582566384c47c6c3416494d1")
1240        );
1241        v = hmac_sha256(&k, &v);
1242        assert_eq!(
1243            v,
1244            decode_hex::<32>("a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60")
1245        );
1246    }
1247
1248    #[test]
1249    fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
1250        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1251        let key = SecretKey::from_bytes(&private_key).unwrap();
1252        let uncompressed = key.public_key();
1253        let compressed = derive_public_key_compressed(&private_key).unwrap();
1254        let signature = key.sign(b"sample").unwrap();
1255
1256        assert!(uncompressed.verify(b"sample", &signature).is_ok());
1257        let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1258        assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
1259    }
1260
1261    #[test]
1262    fn verify_rejects_tampering_and_invalid_points() {
1263        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1264        let key = SecretKey::from_bytes(&private_key).unwrap();
1265        let pub_key = key.public_key();
1266        let mut off_curve = [0u8; 65];
1267        off_curve.copy_from_slice(&pub_key.to_bytes());
1268        let signature = key.sign(b"sample").unwrap();
1269
1270        assert!(pub_key.verify(b"tampered", &signature).is_err());
1271
1272        let mut bad_signature = signature;
1273        bad_signature[10] ^= 0x80;
1274        assert!(pub_key.verify(b"sample", &bad_signature).is_err());
1275
1276        off_curve[64] ^= 0x01;
1277        assert!(!is_valid_public_key(&off_curve));
1278        assert!(PublicKey::from_bytes(&off_curve).is_err());
1279
1280        let invalid_x = decode_hex::<33>("02ffffffff00000001000000000000000000000000ffffffffffffffffffffffff");
1281        assert!(!is_valid_public_key(&invalid_x));
1282    }
1283
1284    #[test]
1285    fn invalid_inputs_are_rejected() {
1286        let invalid_private_key = [0u8; SECRET_KEY_SIZE];
1287        assert!(SecretKey::from_bytes(&invalid_private_key).is_err());
1288        assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
1289        assert!(derive_public_key_compressed(&invalid_private_key).is_err());
1290
1291        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1292        let key = SecretKey::from_bytes(&private_key).unwrap();
1293        let signature = key.sign(b"msg").unwrap();
1294        let mut zero_r = signature;
1295        zero_r[..32].fill(0);
1296        assert!(key.public_key().verify(b"msg", &zero_r).is_err());
1297    }
1298
1299    #[test]
1300    fn public_key_validation_accepts_known_good_points() {
1301        assert!(is_valid_public_key(&decode_hex::<65>(
1302            "046b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296\
1303             4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"
1304        )));
1305        assert!(is_valid_public_key(&decode_hex::<33>(
1306            "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6"
1307        )));
1308    }
1309
1310    // --- Wycheproof test vectors ---
1311
1312    #[test]
1313    fn wycheproof_ecdsa_p256_sha256_p1363() {
1314        let data: serde_json::Value = serde_json::from_str(include_str!(
1315            "../testdata/wycheproof/testvectors_v1/ecdsa_secp256r1_sha256_p1363_test.json"
1316        ))
1317        .unwrap();
1318        let mut valid_tested = 0u64;
1319        let mut invalid_tested = 0u64;
1320        for group in data["testGroups"].as_array().unwrap() {
1321            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1322            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1323            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1324
1325            for test in group["tests"].as_array().unwrap() {
1326                let msg_hex = test["msg"].as_str().unwrap();
1327                let sig_hex = test["sig"].as_str().unwrap();
1328                let result = test["result"].as_str().unwrap();
1329
1330                let msg = hex::decode(msg_hex).unwrap();
1331
1332                if sig_hex.len() != SIGNATURE_SIZE * 2 {
1333                    continue;
1334                }
1335                let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1336
1337                let verify_result = pk.verify(&msg, &sig);
1338
1339                if result == "valid" {
1340                    assert!(
1341                        verify_result.is_ok(),
1342                        "wycheproof ECDSA P1363 tcId={} expected valid but failed",
1343                        test["tcId"]
1344                    );
1345                    valid_tested += 1;
1346                } else {
1347                    assert!(
1348                        verify_result.is_err(),
1349                        "wycheproof ECDSA P1363 tcId={} expected invalid but passed",
1350                        test["tcId"]
1351                    );
1352                    invalid_tested += 1;
1353                }
1354            }
1355        }
1356        assert!(valid_tested > 0, "no valid ECDSA P1363 wycheproof tests were run");
1357        assert!(invalid_tested > 0, "no invalid ECDSA P1363 wycheproof tests were run");
1358    }
1359
1360    #[test]
1361    fn ecdsa_sign_verify_round_trip_multiple_messages() {
1362        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1363        let key = SecretKey::from_bytes(&private_key).unwrap();
1364        let pub_key = key.public_key();
1365
1366        let messages: &[&[u8]] = &[
1367            b"",
1368            b"hello world",
1369            b"The quick brown fox jumps over the lazy dog",
1370            &[0u8; 0],
1371            &[0xffu8; 100],
1372            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
1373        ];
1374
1375        for msg in messages {
1376            let sig = key.sign(msg).unwrap();
1377            assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {:?}", msg);
1378            // Verify with different message fails
1379            let mut wrong_msg = msg.to_vec();
1380            wrong_msg.push(0x42);
1381            assert!(pub_key.verify(&wrong_msg, &sig).is_err());
1382        }
1383    }
1384
1385    #[test]
1386    fn ecdsa_sign_verify_different_keys() {
1387        // Use multiple different private keys
1388        let keys: &[&str] = &[
1389            "0000000000000000000000000000000000000000000000000000000000000001",
1390            "0000000000000000000000000000000000000000000000000000000000000002",
1391            "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550",
1392            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011",
1393        ];
1394
1395        for key_hex in keys {
1396            let private_key = decode_hex::<32>(key_hex);
1397            let key = SecretKey::from_bytes(&private_key).unwrap();
1398            let sig = key.sign(b"test message").unwrap();
1399            assert!(
1400                key.public_key().verify(b"test message", &sig).is_ok(),
1401                "sign/verify failed for key {}",
1402                key_hex
1403            );
1404        }
1405    }
1406
1407    #[test]
1408    fn ecdsa_verify_wrong_public_key_rejects() {
1409        let private_key1 = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
1410        let private_key2 = decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000001");
1411        let key1 = SecretKey::from_bytes(&private_key1).unwrap();
1412        let key2 = SecretKey::from_bytes(&private_key2).unwrap();
1413
1414        let sig = key1.sign(b"message").unwrap();
1415        assert!(key2.public_key().verify(b"message", &sig).is_err());
1416    }
1417
1418    #[test]
1419    fn scalar_from_bytes_rejects_boundary_values() {
1420        // Zero is rejected
1421        let zero = [0u8; 32];
1422        assert!(Scalar::from_bytes(&zero).is_none());
1423
1424        // n is rejected (must be strictly less than n)
1425        let n_bytes = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
1426        assert!(Scalar::from_bytes(&n_bytes).is_none());
1427
1428        // n-1 is accepted
1429        let n_minus_1 = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550");
1430        assert!(Scalar::from_bytes(&n_minus_1).is_some());
1431
1432        // 1 is accepted
1433        let one = decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000001");
1434        assert!(Scalar::from_bytes(&one).is_some());
1435    }
1436
1437    #[test]
1438    fn field_element_from_bytes_rejects_boundary_values() {
1439        // p is rejected (must be strictly less than p)
1440        let p_bytes = decode_hex::<32>("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff");
1441        assert!(FieldElement::from_bytes(&p_bytes).is_none());
1442
1443        // p-1 is accepted
1444        let p_minus_1 = decode_hex::<32>("ffffffff00000001000000000000000000000000fffffffffffffffffffffffe");
1445        assert!(FieldElement::from_bytes(&p_minus_1).is_some());
1446
1447        // 0 is accepted (zero is a valid field element)
1448        let zero = [0u8; 32];
1449        assert!(FieldElement::from_bytes(&zero).is_some());
1450    }
1451
1452    #[test]
1453    fn point_decompression_round_trip() {
1454        // Generate several public keys and verify compressed/uncompressed round-trip
1455        let keys: &[&str] = &[
1456            "0000000000000000000000000000000000000000000000000000000000000001",
1457            "0000000000000000000000000000000000000000000000000000000000000002",
1458            "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
1459            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011",
1460        ];
1461
1462        for key_hex in keys {
1463            let private_key = decode_hex::<32>(key_hex);
1464            let key = SecretKey::from_bytes(&private_key).unwrap();
1465            let uncompressed = key.public_key();
1466            let compressed = derive_public_key_compressed(&private_key).unwrap();
1467
1468            // Both formats should verify the same signature
1469            let sig = key.sign(b"round-trip").unwrap();
1470            assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
1471            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1472            assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
1473
1474            // Decompress the compressed key and verify it matches the uncompressed key
1475            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1476            assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
1477        }
1478    }
1479
1480    #[test]
1481    fn nist_cavp_verify_vectors() {
1482        // NIST CAVP-style ECDSA P-256/SHA-256 signature verification test vectors.
1483        // These test verification with known public keys and signatures.
1484
1485        struct VerifyVector {
1486            qx: &'static str,
1487            qy: &'static str,
1488            msg: &'static [u8],
1489            r: &'static str,
1490            s: &'static str,
1491            valid: bool,
1492        }
1493
1494        let vectors = [
1495            // Valid signature: RFC 6979 vector for "sample"
1496            VerifyVector {
1497                qx: "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
1498                qy: "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1499                msg: b"sample",
1500                r: "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
1501                s: "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
1502                valid: true,
1503            },
1504            // Valid signature: RFC 6979 vector for "test"
1505            VerifyVector {
1506                qx: "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
1507                qy: "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1508                msg: b"test",
1509                r: "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
1510                s: "019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
1511                valid: true,
1512            },
1513            // Invalid: correct r from "sample" but wrong message
1514            VerifyVector {
1515                qx: "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
1516                qy: "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1517                msg: b"wrong",
1518                r: "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
1519                s: "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
1520                valid: false,
1521            },
1522            // Invalid: signature from "sample" verified against "test"
1523            VerifyVector {
1524                qx: "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
1525                qy: "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1526                msg: b"test",
1527                r: "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716",
1528                s: "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
1529                valid: false,
1530            },
1531            // Invalid: r modified by one bit
1532            VerifyVector {
1533                qx: "60fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6",
1534                qy: "7903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
1535                msg: b"sample",
1536                r: "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3717",
1537                s: "f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
1538                valid: false,
1539            },
1540        ];
1541
1542        for (i, v) in vectors.iter().enumerate() {
1543            let mut pubkey = [0u8; 65];
1544            pubkey[0] = 0x04;
1545            pubkey[1..33].copy_from_slice(&hex::decode(v.qx).unwrap());
1546            pubkey[33..65].copy_from_slice(&hex::decode(v.qy).unwrap());
1547
1548            let mut sig = [0u8; 64];
1549            sig[..32].copy_from_slice(&hex::decode(v.r).unwrap());
1550            sig[32..].copy_from_slice(&hex::decode(v.s).unwrap());
1551
1552            let pk = PublicKey::from_bytes(&pubkey).unwrap();
1553            let result = pk.verify(v.msg, &sig);
1554            if v.valid {
1555                assert!(result.is_ok(), "NIST vector {} should be valid", i);
1556            } else {
1557                assert!(result.is_err(), "NIST vector {} should be invalid", i);
1558            }
1559        }
1560    }
1561
1562    #[test]
1563    fn rfc6979_bits2octets_matches_spec() {
1564        // For P-256 with SHA-256, bits2octets reduces the hash modulo n
1565        let hash = hash_message(b"sample");
1566        let result = bits2octets(&hash);
1567        // The hash of "sample" with SHA-256 is:
1568        // af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a62add1bf
1569        // This is less than n, so bits2octets should return it unchanged
1570        assert_eq!(result, hash);
1571
1572        // Test with a value that needs reduction (>= n)
1573        let big_hash: [u8; 32] = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552");
1574        let reduced = bits2octets(&big_hash);
1575        // This is n+1, so reduced should be 1
1576        assert_eq!(
1577            reduced,
1578            decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000000001")
1579        );
1580    }
1581
1582    #[test]
1583    fn scalar_inversion_correctness() {
1584        // Verify that scalar inversion satisfies k * k^-1 = 1 mod n
1585        let k = Scalar::from_bytes(&decode_hex::<32>(
1586            "a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60",
1587        ))
1588        .unwrap();
1589        let k_inv = k.invert().unwrap();
1590        let product = k.mul(k_inv);
1591        assert_eq!(product, Scalar::ONE);
1592    }
1593
1594    #[test]
1595    fn field_element_inversion_correctness() {
1596        // Verify field element inversion: x * x^-1 = 1 mod p
1597        let x = FieldElement::from_bytes(&decode_hex::<32>(
1598            "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
1599        ))
1600        .unwrap();
1601        let x_inv = x.invert().unwrap();
1602        let product = x.mul(x_inv);
1603        assert_eq!(product, FieldElement::ONE);
1604        let product = x.mul(x_inv);
1605        assert_eq!(product, FieldElement::ONE);
1606    }
1607
1608    #[test]
1609    fn generator_point_is_on_curve() {
1610        assert!(AffinePoint::GENERATOR.is_on_curve());
1611    }
1612
1613    #[test]
1614    fn p256_fast_mul_mod_matches_generic() {
1615        // Verify that the P-256 fast mul matches the generic bit-serial mul_mod
1616        // for many random inputs
1617        for _ in 0..1000 {
1618            let a_bytes: [u8; 32] = rand::random();
1619            let b_bytes: [u8; 32] = rand::random();
1620            let a_opt = FieldElement::from_bytes(&a_bytes);
1621            let b_opt = FieldElement::from_bytes(&b_bytes);
1622            if a_opt.is_none() || b_opt.is_none() {
1623                continue;
1624            }
1625            let a = a_opt.unwrap();
1626            let b = b_opt.unwrap();
1627            let expected = U256::from_limbs({
1628                let mut p = [0u64; 8];
1629                for i in 0..4 {
1630                    let mut c = 0u64;
1631                    for j in 0..4 {
1632                        let (v, cc) = mac(p[i + j], a.0.limbs[i], b.0.limbs[j], c);
1633                        p[i + j] = v;
1634                        c = cc;
1635                    }
1636                    p[i + 4] = c;
1637                }
1638                let mut rem = [0u64; 4];
1639                for bi in (0..512).rev() {
1640                    let li = bi / 64;
1641                    let pi = bi % 64;
1642                    let bit = ((p[li] >> pi) & 1) as u64;
1643                    let mut shifted = [0u64; 4];
1644                    let mut carry = bit;
1645                    for j in 0..4 {
1646                        let next = rem[j] >> 63;
1647                        shifted[j] = (rem[j] << 1) | carry;
1648                        carry = next;
1649                    }
1650                    let (red, br) = U256::from_limbs(shifted).sub_raw(&MODULUS_P);
1651                    if carry == 1 || br == 0 {
1652                        rem = red.limbs;
1653                    } else {
1654                        rem = shifted;
1655                    }
1656                }
1657                rem
1658            });
1659            let fast = p256_fast_mul_mod(&a.0, &b.0);
1660            assert_eq!(expected, fast, "mismatch");
1661        }
1662    }
1663
1664    #[test]
1665    fn scalar_mul_generator_n_gives_identity() {
1666        // n * G = identity (point at infinity)
1667        // We can't use Scalar::from_bytes since it rejects n,
1668        // but we can verify (n-1)*G + G = identity indirectly:
1669        // (n-1)*G should give -G, i.e., (Gx, -Gy)
1670        let n_minus_1 = Scalar::from_bytes(&decode_hex::<32>(
1671            "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550",
1672        ))
1673        .unwrap();
1674        let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1675        assert_eq!(result.x, GENERATOR_X);
1676        // y should be -Gy mod p
1677        let neg_gy = GENERATOR_Y.negate();
1678        assert_eq!(result.y, neg_gy);
1679    }
1680
1681    // --- ECDH tests ---
1682
1683    #[test]
1684    fn ecdh_rfc5903_section_8_1() {
1685        // RFC 5903 Section 8.1 — 256-bit Random ECP Group test vector
1686        let i_priv = decode_hex::<32>("c88f01f510d9ac3f70a292daa2316de544e9aab8afe84049c62a9c57862d1433");
1687        let i_pub = decode_hex::<65>(
1688            "04dad0b65394221cf9b051e1feca5787d098dfe637fc90b9ef945d0c3772581180\
1689              5271a0461cdb8252d61f1c456fa3e59ab1f45b33accf5f58389e0577b8990bb3",
1690        );
1691        let r_priv = decode_hex::<32>("c6ef9c5d78ae012a011164acb397ce2088685d8f06bf9be0b283ab46476bee53");
1692        let r_pub = decode_hex::<65>(
1693            "04d12dfb5289c8d4f81208b70270398c342296970a0bccb74c736fc7554494bf63\
1694              56fbf3ca366cc23e8157854c13c58d6aac23f046ada30f8353e74f33039872ab",
1695        );
1696        let expected_shared = decode_hex::<32>("d6840f6b42f6edafd13116e0e12565202fef8e9ece7dce03812464d04b9442de");
1697
1698        let alice = SecretKey::from_bytes(&i_priv).unwrap();
1699        let bob = SecretKey::from_bytes(&r_priv).unwrap();
1700        let bob_pub = PublicKey::from_bytes(&r_pub).unwrap();
1701        let alice_pub = PublicKey::from_bytes(&i_pub).unwrap();
1702
1703        assert_eq!(alice.public_key().to_bytes(), i_pub);
1704        assert_eq!(bob.public_key().to_bytes(), r_pub);
1705
1706        let alice_shared = alice.ecdh(&bob_pub).unwrap();
1707        let bob_shared = bob.ecdh(&alice_pub).unwrap();
1708
1709        assert_eq!(alice_shared, expected_shared);
1710        assert_eq!(bob_shared, expected_shared);
1711    }
1712
1713    #[test]
1714    fn ecdh_nist_cavp_vector_from_go() {
1715        // Go stdlib crypto/ecdh NIST CAVS 14.1 ECC CDH Primitive (SP800-56A) vector
1716        let priv_key = decode_hex::<32>("7d7dc5f71eb29ddaf80d6214632eeae03d9058af1fb6d22ed80badb62bc1a534");
1717        let pub_key = decode_hex::<65>(
1718            "04ead218590119e8876b29146ff89ca61770c4edbbf97d38ce385ed281d8a6b230\
1719              28af61281fd35e2fa7002523acc85a429cb06ee6648325389f59edfce1405141",
1720        );
1721        let peer_pub = decode_hex::<65>(
1722            "04700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287\
1723              db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac",
1724        );
1725        let expected_shared = decode_hex::<32>("46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b");
1726
1727        let key = SecretKey::from_bytes(&priv_key).unwrap();
1728        assert_eq!(key.public_key().to_bytes(), pub_key);
1729
1730        let peer = PublicKey::from_bytes(&peer_pub).unwrap();
1731        let shared = key.ecdh(&peer).unwrap();
1732        assert_eq!(shared, expected_shared);
1733    }
1734
1735    #[test]
1736    fn ecdh_with_compressed_public_key() {
1737        // ECDH should accept compressed public keys as peer input
1738        let priv_alice = decode_hex::<32>("c88f01f510d9ac3f70a292daa2316de544e9aab8afe84049c62a9c57862d1433");
1739        let bob_pub_compressed = decode_hex::<33>("03d12dfb5289c8d4f81208b70270398c342296970a0bccb74c736fc7554494bf63");
1740
1741        assert!(is_valid_public_key(&bob_pub_compressed));
1742
1743        let expected_shared = decode_hex::<32>("d6840f6b42f6edafd13116e0e12565202fef8e9ece7dce03812464d04b9442de");
1744
1745        let shared = ecdh(&priv_alice, &bob_pub_compressed).unwrap();
1746        assert_eq!(shared, expected_shared);
1747    }
1748
1749    #[test]
1750    fn ecdh_round_trip_alice_bob() {
1751        // Full round-trip ECDH key exchange with randomly generated keys
1752        let alice = SecretKey::generate().unwrap();
1753        let bob = SecretKey::generate().unwrap();
1754
1755        let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
1756        let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
1757
1758        assert_eq!(alice_shared, bob_shared);
1759        assert_eq!(alice_shared.len(), 32);
1760    }
1761
1762    #[test]
1763    fn ecdh_rejects_off_curve_peer_public_key() {
1764        let alice = SecretKey::generate().unwrap();
1765        let mut bad_pub = alice.public_key().to_bytes().to_vec();
1766        // Flip a bit in y to take it off the curve
1767        bad_pub[64] ^= 0x01;
1768        assert!(!is_valid_public_key(&bad_pub));
1769        assert!(ecdh(&alice.to_bytes(), &bad_pub).is_err());
1770    }
1771
1772    #[test]
1773    fn ecdh_rejects_infinity_peer_public_key() {
1774        let alice = SecretKey::generate().unwrap();
1775        // Infinity encoding (0x00) should be rejected
1776        let infinity = [0x00u8];
1777        assert!(ecdh(&alice.to_bytes(), &infinity).is_err());
1778    }
1779
1780    #[test]
1781    fn ecdh_rejects_bad_length_peer_public_key() {
1782        let alice = SecretKey::generate().unwrap();
1783        // Empty key
1784        assert!(ecdh(&alice.to_bytes(), &[]).is_err());
1785        // Truncated key
1786        assert!(ecdh(&alice.to_bytes(), &[0x04, 0x00]).is_err());
1787        // Too long
1788        let mut long = [0x04u8; 200];
1789        long[0] = 0x04;
1790        assert!(ecdh(&alice.to_bytes(), &long).is_err());
1791    }
1792
1793    #[test]
1794    fn ecdh_rejects_invalid_private_key_zero() {
1795        let zero_key = [0u8; 32];
1796        assert!(SecretKey::from_bytes(&zero_key).is_err());
1797        let bob = SecretKey::generate().unwrap();
1798        assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1799    }
1800
1801    #[test]
1802    fn ecdh_rejects_invalid_private_key_order() {
1803        // n (the curve order) is rejected
1804        let n_bytes = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
1805        assert!(SecretKey::from_bytes(&n_bytes).is_err());
1806
1807        // n+1 is rejected
1808        let n_plus_1 = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552");
1809        assert!(SecretKey::from_bytes(&n_plus_1).is_err());
1810
1811        // all-ones is rejected
1812        let all_ones = [0xffu8; 32];
1813        assert!(SecretKey::from_bytes(&all_ones).is_err());
1814    }
1815
1816    #[test]
1817    fn ecdh_rejects_peer_public_key_x_equal_to_p() {
1818        // x = p (the field modulus) should be rejected as out of range
1819        let alice = SecretKey::generate().unwrap();
1820        let mut bad_pub = [0u8; 65];
1821        bad_pub[0] = 0x04;
1822        bad_pub[1..33].copy_from_slice(&decode_hex::<32>(
1823            "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff",
1824        ));
1825        bad_pub[33..65].fill(0x01);
1826        assert!(!is_valid_public_key(&bad_pub));
1827        assert!(ecdh(&alice.to_bytes(), &bad_pub).is_err());
1828    }
1829
1830    #[test]
1831    fn ecdh_different_messages_same_shared_secret() {
1832        // ECDH shared secret depends only on the two key pairs, not any message
1833        let alice = SecretKey::generate().unwrap();
1834        let bob = SecretKey::generate().unwrap();
1835
1836        let shared1 = alice.ecdh(&bob.public_key()).unwrap();
1837        let shared2 = alice.ecdh(&bob.public_key()).unwrap();
1838        assert_eq!(shared1, shared2);
1839    }
1840
1841    #[test]
1842    fn ecdh_self_exchange_is_deterministic() {
1843        // ECDH with own public key produces a deterministic result
1844        let alice = SecretKey::generate().unwrap();
1845        let shared = alice.ecdh(&alice.public_key()).unwrap();
1846        let shared2 = alice.ecdh(&alice.public_key()).unwrap();
1847        assert_eq!(shared, shared2);
1848    }
1849
1850    #[test]
1851    fn ecdh_different_keys_produce_different_secrets() {
1852        let alice = SecretKey::generate().unwrap();
1853        let bob1 = SecretKey::generate().unwrap();
1854        let bob2 = SecretKey::generate().unwrap();
1855
1856        let shared1 = alice.ecdh(&bob1.public_key()).unwrap();
1857        let shared2 = alice.ecdh(&bob2.public_key()).unwrap();
1858        // Extremely unlikely that two different Bob keys produce the same secret
1859        assert_ne!(shared1, shared2);
1860    }
1861
1862    #[test]
1863    fn ecdh_generator_multiplication_matches_go_p256_mult_test1() {
1864        // Go's crypto/elliptic p256_test.go: ScalarMult test 1
1865        let k = decode_hex::<32>("2a265f8bcbdcaf94d58519141e578124cb40d64a501fba9c11847b28965bc737");
1866        let x_in = decode_hex::<32>("023819813ac969847059028ea88a1f30dfbcde03fc791d3a252c6b41211882ea");
1867        let y_in = decode_hex::<32>("f93e4ae433cc12cf2a43fc0ef26400c0e125508224cdb649380f25479148a4ad");
1868        let x_out = decode_hex::<32>("4d4de80f1534850d261075997e3049321a0864082d24a917863366c0724f5ae3");
1869        let y_out = decode_hex::<32>("a22d2b7f7818a3563e0f7a76c9bf0921ac55e06e2e4d11795b233824b1db8cc0");
1870
1871        let mut pubkey = [0u8; 65];
1872        pubkey[0] = 0x04;
1873        pubkey[1..33].copy_from_slice(&x_in);
1874        pubkey[33..65].copy_from_slice(&y_in);
1875
1876        let point = parse_public_key(&pubkey).unwrap();
1877        let scalar = Scalar::from_bytes(&k).unwrap();
1878        let result = scalar_mul_affine(&point, &scalar).to_affine().unwrap();
1879
1880        assert_eq!(result.x.to_bytes(), x_out, "x coordinate mismatch in Go test 1");
1881        assert_eq!(result.y.to_bytes(), y_out, "y coordinate mismatch in Go test 1");
1882    }
1883
1884    #[test]
1885    fn ecdh_generator_multiplication_matches_go_p256_mult_test2() {
1886        // Go's crypto/elliptic p256_test.go: ScalarMult test 2
1887        let k = decode_hex::<32>("313f72ff9fe811bf573176231b286a3bdb6f1b14e05c40146590727a71c3bccd");
1888        let x_in = decode_hex::<32>("cc11887b2d66cbae8f4d306627192522932146b42f01d3c6f92bd5c8ba739b06");
1889        let y_in = decode_hex::<32>("a2f08a029cd06b46183085bae9248b0ed15b70280c7ef13a457f5af382426031");
1890        let x_out = decode_hex::<32>("831c3f6b5f762d2f461901577af41354ac5f228c2591f84f8a6e51e2e3f17991");
1891        let y_out = decode_hex::<32>("93f90934cd0ef2c698cc471c60a93524e87ab31ca2412252337f364513e43684");
1892
1893        let mut pubkey = [0u8; 65];
1894        pubkey[0] = 0x04;
1895        pubkey[1..33].copy_from_slice(&x_in);
1896        pubkey[33..65].copy_from_slice(&y_in);
1897
1898        let point = parse_public_key(&pubkey).unwrap();
1899        let scalar = Scalar::from_bytes(&k).unwrap();
1900        let result = scalar_mul_affine(&point, &scalar).to_affine().unwrap();
1901
1902        assert_eq!(result.x.to_bytes(), x_out, "x coordinate mismatch in Go test 2");
1903        assert_eq!(result.y.to_bytes(), y_out, "y coordinate mismatch in Go test 2");
1904    }
1905
1906    #[test]
1907    fn ecdh_rejects_invalid_curve_attack() {
1908        // Invalid curve attack: a point not on P-256 should always be rejected.
1909        // Point (1, 1) is not on the P-256 curve.
1910        let alice = SecretKey::generate().unwrap();
1911        let mut off_curve = [0u8; 65];
1912        off_curve[0] = 0x04;
1913        off_curve[33] = 0x01;
1914        off_curve[64] = 0x01;
1915        off_curve[1] = 0x01;
1916
1917        assert!(!is_valid_public_key(&off_curve));
1918        assert!(ecdh(&alice.to_bytes(), &off_curve).is_err());
1919    }
1920
1921    #[test]
1922    fn ecdh_edge_case_shared_secret_x_equals_zero() {
1923        // Wycheproof-style edge case: shared secret x-coordinate is 0.
1924        // This is a valid test case from Wycheproof ecdh_secp256r1_test.json tcId 3.
1925        let priv_hex = "0a0d622a47e48f6bc1038ace438c6f528aa00ad2bd1da5f13ee46bf5f633d71a";
1926        let pub_hex = "0458fd4168a87795603e2b04390285bdca6e57de6027fe211dd9d25e2212d29e6\
1927                        2080d36bd224d7405509295eed02a17150e03b314f96da37445b0d1d29377d12c";
1928        let expected_shared = [0u8; 32];
1929
1930        let priv_key = decode_hex::<32>(priv_hex);
1931        let pub_key = decode_hex::<65>(pub_hex);
1932
1933        assert!(is_valid_public_key(&pub_key));
1934        let shared = ecdh(&priv_key, &pub_key).unwrap();
1935        assert_eq!(shared, expected_shared);
1936    }
1937
1938    #[test]
1939    fn ecdh_edge_case_shared_secret_x_equals_p_minus_3() {
1940        // Wycheproof-style edge case: shared secret x-coordinate is p-3.
1941        // From Wycheproof ecdh_secp256r1_test.json tcId 4.
1942        let priv_hex = "0a0d622a47e48f6bc1038ace438c6f528aa00ad2bd1da5f13ee46bf5f633d71a";
1943        let pub_hex = "04a1ecc24bf0d0053d23f5fd80ddf1735a1925039dc1176c581a7e795163c8b9ba\
1944                        2cb5a4e4d5109f4527575e3137b83d79a9bcb3faeff90d2aca2bed71bb523e7e";
1945        let expected_shared = decode_hex::<32>("ffffffff00000001000000000000000000000000fffffffffffffffffffffffc");
1946
1947        let priv_key = decode_hex::<32>(priv_hex);
1948        let pub_key = decode_hex::<65>(pub_hex);
1949
1950        assert!(is_valid_public_key(&pub_key));
1951        let shared = ecdh(&priv_key, &pub_key).unwrap();
1952        assert_eq!(shared, expected_shared);
1953    }
1954
1955    #[test]
1956    fn ecdh_edge_case_shared_secret_power_of_two() {
1957        // Shared secret x-coordinate = 2^16
1958        // From Wycheproof ecdh_secp256r1_test.json tcId 5.
1959        let priv_hex = "0a0d622a47e48f6bc1038ace438c6f528aa00ad2bd1da5f13ee46bf5f633d71a";
1960        let pub_hex = "041b0e7437c33d379929430d3ec10df59bed7fe2a1d950c5791e1e9ddeef1f4d70\
1961                        fbdb0e3bbce63a27f27838c685207f2ccaf689d25eb622744db1168ac92619e8";
1962        let expected_shared = decode_hex::<32>("0000000000000000000000000000000000000000000000000000000000010000");
1963
1964        let priv_key = decode_hex::<32>(priv_hex);
1965        let pub_key = decode_hex::<65>(pub_hex);
1966
1967        assert!(is_valid_public_key(&pub_key));
1968        let shared = ecdh(&priv_key, &pub_key).unwrap();
1969        assert_eq!(shared, expected_shared);
1970    }
1971
1972    #[test]
1973    fn ecdh_wrong_curve_rejected() {
1974        // A point on P-224 (a different curve) should be rejected for P-256 ECDH.
1975        // P-224 generator point is not on P-256.
1976        // P-224 generator x = b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21
1977        // (this is longer than 32 bytes, so we just test a random point that's not on P-256)
1978        let alice = SecretKey::generate().unwrap();
1979        let p224_gen_x = [
1980            0x00, 0x00, 0x00, 0x00, 0xb7, 0x0e, 0x0c, 0xbd, 0x6b, 0xb4, 0xbf, 0x7f, 0x32, 0x13, 0x90, 0xb9, 0x4a, 0x03,
1981            0xc1, 0xd3, 0x56, 0xc2, 0x11, 0x22, 0x34, 0x32, 0x80, 0xd6, 0x11, 0x5c, 0x1d, 0x21,
1982        ];
1983        let p224_gen_y = [
1984            0x00, 0x00, 0x00, 0x00, 0xbd, 0x37, 0x68, 0x08, 0xb3, 0x2c, 0x81, 0x2e, 0xd7, 0xd2, 0x86, 0x72, 0x37, 0x46,
1985            0xa5, 0xdc, 0x63, 0x63, 0x9c, 0x5d, 0x99, 0xd6, 0x9c, 0xb4, 0xd4, 0xfc, 0xb5, 0x9e,
1986        ];
1987        let mut bad_pub = [0u8; 65];
1988        bad_pub[0] = 0x04;
1989        bad_pub[1..33].copy_from_slice(&p224_gen_x);
1990        bad_pub[33..65].copy_from_slice(&p224_gen_y);
1991
1992        assert!(!is_valid_public_key(&bad_pub));
1993        assert!(ecdh(&alice.to_bytes(), &bad_pub).is_err());
1994    }
1995
1996    #[test]
1997    fn ecdh_private_key_rejects_zero_and_order() {
1998        // PrivateKey::from_bytes must reject zero and n (curve order)
1999        let zero = [0u8; 32];
2000        assert!(SecretKey::from_bytes(&zero).is_err());
2001
2002        let n = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551");
2003        assert!(SecretKey::from_bytes(&n).is_err());
2004
2005        let n_minus_1 = decode_hex::<32>("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632550");
2006        assert!(SecretKey::from_bytes(&n_minus_1).is_ok());
2007    }
2008
2009    #[test]
2010    fn ecdh_public_key_rejects_invalid_encodings() {
2011        // Infinity
2012        assert!(!is_valid_public_key(&[0x00]));
2013
2014        // Wrong prefix
2015        let mut bad_prefix = [0u8; 65];
2016        bad_prefix[0] = 0x05;
2017        bad_prefix[1] = 0x01;
2018        assert!(!is_valid_public_key(&bad_prefix));
2019
2020        // Truncated
2021        assert!(!is_valid_public_key(&[0x04, 0x00]));
2022
2023        // Too long
2024        let mut too_long = [0u8; 66];
2025        too_long[0] = 0x04;
2026        assert!(!is_valid_public_key(&too_long));
2027    }
2028
2029    #[test]
2030    fn ecdh_multiple_exchanges_consistency() {
2031        // Verify ECDH commutativity across multiple key pairs
2032        let alice = SecretKey::generate().unwrap();
2033        let bob = SecretKey::generate().unwrap();
2034        let charlie = SecretKey::generate().unwrap();
2035
2036        let alice_bob = alice.ecdh(&bob.public_key()).unwrap();
2037        let bob_alice = bob.ecdh(&alice.public_key()).unwrap();
2038        assert_eq!(alice_bob, bob_alice);
2039
2040        let alice_charlie = alice.ecdh(&charlie.public_key()).unwrap();
2041        let charlie_alice = charlie.ecdh(&alice.public_key()).unwrap();
2042        assert_eq!(alice_charlie, charlie_alice);
2043
2044        let bob_charlie = bob.ecdh(&charlie.public_key()).unwrap();
2045        let charlie_bob = charlie.ecdh(&bob.public_key()).unwrap();
2046        assert_eq!(bob_charlie, charlie_bob);
2047
2048        // All three should be different
2049        assert_ne!(alice_bob, alice_charlie);
2050        assert_ne!(alice_bob, bob_charlie);
2051        assert_ne!(alice_charlie, bob_charlie);
2052    }
2053
2054    #[test]
2055    fn ecdh_standalone_function_matches_method() {
2056        let alice = SecretKey::generate().unwrap();
2057        let bob = SecretKey::generate().unwrap();
2058
2059        let method_result = alice.ecdh(&bob.public_key()).unwrap();
2060        let standalone_result = ecdh(&alice.to_bytes(), &bob.public_key().to_bytes()).unwrap();
2061
2062        assert_eq!(method_result, standalone_result);
2063    }
2064
2065    #[test]
2066    fn rfc6979_test_message_nonce_matches_known_value() {
2067        let private_key = Scalar::from_bytes(&decode_hex::<32>(
2068            "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
2069        ))
2070        .unwrap();
2071        let hash = hash_message(b"test");
2072        assert_eq!(
2073            rfc6979_generate_k(&private_key, &hash).to_bytes(),
2074            decode_hex::<32>("d16b6ae827f17175e040871a1c7ec3500192c4c92677336ec2537acaee0008e0")
2075        );
2076    }
2077
2078    #[test]
2079    fn ecdsa_rejects_ptr_at_infinity_as_public_key() {
2080        // The point at infinity (0x00) is rejected as a public key
2081        assert!(!is_valid_public_key(&[0x00]));
2082        assert!(PublicKey::from_bytes(&[0x00]).is_err());
2083    }
2084
2085    #[test]
2086    fn ecdsa_verify_rejects_non_canonical_r_and_s() {
2087        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
2088        let key = SecretKey::from_bytes(&private_key).unwrap();
2089        let _valid_sig = key.sign(b"msg").unwrap();
2090
2091        // r = n+1 is rejected
2092        let sig = decode_hex::<64>(
2093            "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552\
2094             f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
2095        );
2096        assert!(key.public_key().verify(b"msg", &sig).is_err());
2097
2098        // s = n+1 is rejected
2099        let sig = decode_hex::<64>(
2100            "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\
2101             ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632552",
2102        );
2103        assert!(key.public_key().verify(b"msg", &sig).is_err());
2104    }
2105
2106    #[test]
2107    fn private_key_round_trip_bytes() {
2108        let key = SecretKey::generate().unwrap();
2109        let bytes = key.to_bytes();
2110        let key2 = SecretKey::from_bytes(&bytes).unwrap();
2111        assert_eq!(key.to_bytes(), key2.to_bytes());
2112        assert_eq!(key.public_key().to_bytes(), key2.public_key().to_bytes());
2113    }
2114
2115    #[test]
2116    fn public_key_round_trip_bytes() {
2117        let key = SecretKey::generate().unwrap();
2118        let pub_key = key.public_key();
2119        let bytes = pub_key.to_bytes();
2120        let pub_key2 = PublicKey::from_bytes(&bytes).unwrap();
2121        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
2122    }
2123
2124    #[test]
2125    fn field_element_add_sub_mul_consistency() {
2126        let a = FieldElement::from_bytes(&decode_hex::<32>(
2127            "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
2128        ))
2129        .unwrap();
2130        let b = FieldElement::from_bytes(&decode_hex::<32>(
2131            "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5",
2132        ))
2133        .unwrap();
2134
2135        // a + b - b = a
2136        assert_eq!(a.add(b).sub(b), a);
2137
2138        // a + b = b + a
2139        assert_eq!(a.add(b), b.add(a));
2140
2141        // a * b = b * a
2142        assert_eq!(a.mul(b), b.mul(a));
2143
2144        // (a + b) * c = a*c + b*c
2145        let c = FieldElement::from_bytes(&decode_hex::<32>(
2146            "3bce3c3e27d2604b651d06b0cc53b0f6b3ebbd55769886bc5ac635d8aa3a93e7",
2147        ))
2148        .unwrap();
2149        assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
2150    }
2151
2152    #[test]
2153    fn scalar_add_sub_mul_consistency() {
2154        let a = Scalar::from_bytes(&decode_hex::<32>(
2155            "a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60",
2156        ))
2157        .unwrap();
2158        // Scalar::ONE
2159        let one = Scalar::from_bytes(&decode_hex::<32>(
2160            "0000000000000000000000000000000000000000000000000000000000000001",
2161        ))
2162        .unwrap();
2163
2164        // a + 1 - 1 = a
2165        assert_eq!(a.add(one).sub(one), a);
2166
2167        // a * 1 = a
2168        assert_eq!(a.mul(one), a);
2169
2170        // commutativity
2171        let b = Scalar::from_bytes(&decode_hex::<32>(
2172            "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367",
2173        ))
2174        .unwrap();
2175        assert_eq!(a.mul(b), b.mul(a));
2176        assert_eq!(a.add(b), b.add(a));
2177    }
2178
2179    #[test]
2180    fn ecdh_shared_secret_boundary_values() {
2181        // ECDH shared secret is always exactly 32 bytes
2182        let alice = SecretKey::generate().unwrap();
2183        let bob = SecretKey::generate().unwrap();
2184
2185        let shared = alice.ecdh(&bob.public_key()).unwrap();
2186        assert_eq!(shared.len(), ECDH_SHARED_SECRET_SIZE);
2187
2188        // shared secret is deterministic for the same key pair
2189        let shared2 = alice.ecdh(&bob.public_key()).unwrap();
2190        assert_eq!(shared, shared2);
2191    }
2192
2193    #[test]
2194    fn ecdh_rejects_empty_and_invalid_public_key_bytes() {
2195        let key = SecretKey::generate().unwrap();
2196
2197        // Invalid prefix byte
2198        let mut bad = key.public_key().to_bytes();
2199        bad[0] = 0xff;
2200        assert!(!is_valid_public_key(&bad));
2201        assert!(PublicKey::from_bytes(&bad).is_err());
2202
2203        // Only prefix byte
2204        assert!(!is_valid_public_key(&[0x04]));
2205
2206        // Truncated uncompressed (64 bytes but need 65)
2207        assert!(!is_valid_public_key(&bad[..64]));
2208
2209        // Compressed key with y=0 as the x coordinate (valid if y exists, but let's test)
2210        let zero_x_compressed = decode_hex::<33>("020000000000000000000000000000000000000000000000000000000000000000");
2211        // x=0 is a valid field element; the point may or may not be on the curve
2212        // Just test that parsing doesn't crash
2213        let _ = PublicKey::from_bytes(&zero_x_compressed);
2214    }
2215
2216    #[test]
2217    fn ecdsa_sign_then_verify_consistent_for_random_keys() {
2218        for _ in 0..5 {
2219            let key = SecretKey::generate().unwrap();
2220            let msg = rand::random::<[u8; 32]>();
2221            let sig = key.sign(&msg).unwrap();
2222            assert!(key.public_key().verify(&msg, &sig).is_ok());
2223        }
2224    }
2225
2226    #[test]
2227    fn field_element_negate_round_trip() {
2228        let x = FieldElement::from_bytes(&decode_hex::<32>(
2229            "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296",
2230        ))
2231        .unwrap();
2232        let neg = x.negate();
2233        assert_eq!(neg.negate(), x);
2234        assert_eq!(x.add(neg), FieldElement::ZERO);
2235    }
2236
2237    #[test]
2238    fn scalar_negate_round_trip() {
2239        let a = Scalar::from_bytes(&decode_hex::<32>(
2240            "a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60",
2241        ))
2242        .unwrap();
2243        let neg_a = Scalar::ZERO.sub(a);
2244        assert_eq!(a.add(neg_a), Scalar::ZERO);
2245        // neg(neg(a)) = a
2246        assert_eq!(Scalar::ZERO.sub(neg_a), a);
2247    }
2248
2249    #[test]
2250    fn point_double_and_add_consistency() {
2251        // 2*G = G + G
2252        let g = AffinePoint::GENERATOR;
2253        let proj_g = ProjectivePoint::from_affine(&g);
2254        let doubled = proj_g.double();
2255        let added = proj_g.add(&proj_g);
2256        assert_eq!(
2257            doubled.to_affine().unwrap().to_uncompressed_bytes(),
2258            added.to_affine().unwrap().to_uncompressed_bytes(),
2259        );
2260    }
2261
2262    #[test]
2263    fn scalar_mul_by_two_matches_double() {
2264        let two = Scalar::from_bytes(&decode_hex::<32>(
2265            "0000000000000000000000000000000000000000000000000000000000000002",
2266        ))
2267        .unwrap();
2268        let g_times_2 = scalar_mul_affine(&AffinePoint::GENERATOR, &two).to_affine().unwrap();
2269        let proj_g = ProjectivePoint::from_affine(&AffinePoint::GENERATOR);
2270        let g_doubled = proj_g.double().to_affine().unwrap();
2271
2272        assert_eq!(g_times_2.to_uncompressed_bytes(), g_doubled.to_uncompressed_bytes());
2273    }
2274
2275    #[test]
2276    fn ecdh_with_self_is_consistent() {
2277        let key = SecretKey::generate().unwrap();
2278        let shared1 = key.ecdh(&key.public_key()).unwrap();
2279        let shared2 = key.ecdh(&key.public_key()).unwrap();
2280        assert_eq!(shared1, shared2);
2281    }
2282
2283    #[test]
2284    fn wycheproof_ecdh_p256_ecpoint() {
2285        let data: serde_json::Value = serde_json::from_str(include_str!(
2286            "../testdata/wycheproof/testvectors_v1/ecdh_secp256r1_ecpoint_test.json"
2287        ))
2288        .unwrap();
2289        let mut valid_tested = 0u64;
2290        let mut invalid_tested = 0u64;
2291        let mut acceptable_tested = 0u64;
2292        for group in data["testGroups"].as_array().unwrap() {
2293            if group["curve"].as_str() != Some("secp256r1") {
2294                continue;
2295            }
2296            for test in group["tests"].as_array().unwrap() {
2297                let public_hex = test["public"].as_str().unwrap();
2298                let private_hex = test["private"].as_str().unwrap();
2299                let expected_shared_hex = test["shared"].as_str().unwrap();
2300                let result = test["result"].as_str().unwrap();
2301
2302                let public_key = hex::decode(public_hex).unwrap();
2303
2304                // Private key hex is a bigint, may have leading zeros or be
2305                // shorter than 32 bytes. Pad or strip to exactly 32 bytes.
2306                let private_bytes = hex::decode(private_hex).unwrap();
2307                let mut private_key = [0u8; SECRET_KEY_SIZE];
2308                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
2309                let skip = if private_bytes.len() > SECRET_KEY_SIZE {
2310                    private_bytes.len() - SECRET_KEY_SIZE
2311                } else {
2312                    0
2313                };
2314                private_key[SECRET_KEY_SIZE - effective_len..]
2315                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
2316
2317                let shared = ecdh(&private_key, &public_key);
2318
2319                if result == "valid" {
2320                    let shared = shared.unwrap();
2321                    let shared_hex = hex::encode(shared);
2322                    assert_eq!(shared_hex, expected_shared_hex, "wycheproof ECDH ecpoint tcId={}", test["tcId"]);
2323                    valid_tested += 1;
2324                } else if result == "invalid" {
2325                    assert!(
2326                        shared.is_err(),
2327                        "wycheproof ECDH ecpoint tcId={} expected invalid but passed",
2328                        test["tcId"]
2329                    );
2330                    invalid_tested += 1;
2331                } else {
2332                    acceptable_tested += 1;
2333                }
2334            }
2335        }
2336        assert!(valid_tested > 0, "no valid ECDH ecpoint wycheproof tests were run");
2337        assert!(invalid_tested > 0, "no invalid ECDH ecpoint wycheproof tests were run");
2338        assert!(acceptable_tested > 0, "no acceptable ECDH ecpoint wycheproof tests were run");
2339    }
2340
2341    #[test]
2342    fn compressed_public_key_has_correct_prefix() {
2343        for _ in 0..5 {
2344            let key = SecretKey::generate().unwrap();
2345            let compressed = derive_public_key_compressed(&key.to_bytes()).unwrap();
2346            let prefix = compressed[0];
2347            assert!(prefix == 0x02 || prefix == 0x03, "invalid compressed prefix: {prefix:#x}");
2348        }
2349    }
2350
2351    #[test]
2352    fn p256_ecdsa_rejects_truncated_signature() {
2353        let key = SecretKey::generate().unwrap();
2354        let sig = key.sign(b"msg").unwrap();
2355        // Truncate to 63 bytes and pad to 64 with zeros
2356        let mut truncated = [0u8; SIGNATURE_SIZE];
2357        truncated[..63].copy_from_slice(&sig[..63]);
2358        // If the dropped byte is 0, the padded copy equals the original,
2359        // so flip a bit to ensure it's different
2360        if truncated == sig {
2361            truncated[63] ^= 0x01;
2362        }
2363        assert!(key.public_key().verify(b"msg", &truncated).is_err());
2364    }
2365
2366    #[test]
2367    fn is_on_curve_accepts_generator_and_random_points() {
2368        assert!(AffinePoint::GENERATOR.is_on_curve());
2369        for _ in 0..5 {
2370            let key = SecretKey::generate().unwrap();
2371            // The public point should be on the curve
2372            // (verified by construction)
2373            let pb = key.public_key().to_bytes();
2374            let pk = PublicKey::from_bytes(&pb).unwrap();
2375            let _ = pk; // just checking it can be constructed
2376        }
2377    }
2378
2379    #[test]
2380    fn field_element_pow_correctness() {
2381        let x = FieldElement::from_bytes(&decode_hex::<32>(
2382            "0000000000000000000000000000000000000000000000000000000000000002",
2383        ))
2384        .unwrap();
2385        // x^3 = x * x * x
2386        let x3 = x.pow(&U256::from_u64(3));
2387        let expected = x.mul(x).mul(x);
2388        assert_eq!(x3, expected);
2389
2390        // x^0 = 1
2391        let x0 = x.pow(&U256::ZERO);
2392        assert_eq!(x0, FieldElement::ONE);
2393    }
2394
2395    #[test]
2396    fn nist_p256_vector_verify_all_rfc6979_signatures() {
2397        // Verify ALL 4 SHA-256 signatures from RFC 6979 A.2.5 match
2398        let private_key = decode_hex::<32>("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
2399        let key = SecretKey::from_bytes(&private_key).unwrap();
2400
2401        let vectors: &[(&[u8], &str)] = &[
2402            (
2403                b"sample" as &[u8],
2404                "efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716\
2405                          f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
2406            ),
2407            (
2408                b"test" as &[u8],
2409                "f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367\
2410                          019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
2411            ),
2412        ];
2413
2414        for (msg, hex_sig) in vectors {
2415            let sig = key.sign(msg).unwrap();
2416            let expected = decode_hex::<64>(hex_sig);
2417            assert_eq!(sig, expected, "failed for message: {:?}", String::from_utf8_lossy(msg));
2418        }
2419    }
2420
2421    #[test]
2422    fn wycheproof_ecdsa_p256_sha256_der() {
2423        let data: serde_json::Value = serde_json::from_str(include_str!(
2424            "../testdata/wycheproof/testvectors_v1/ecdsa_secp256r1_sha256_test.json"
2425        ))
2426        .unwrap();
2427        let mut valid_tested = 0u64;
2428        let mut invalid_tested = 0u64;
2429        for group in data["testGroups"].as_array().unwrap() {
2430            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
2431            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
2432            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
2433
2434            for test in group["tests"].as_array().unwrap() {
2435                let msg_hex = test["msg"].as_str().unwrap();
2436                let sig_hex = test["sig"].as_str().unwrap();
2437                let result = test["result"].as_str().unwrap();
2438
2439                let msg = hex::decode(msg_hex).unwrap();
2440                let der_sig = hex::decode(sig_hex).unwrap();
2441                let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
2442                    continue;
2443                };
2444
2445                let verify_result = pk.verify(&msg, &sig);
2446
2447                if result == "valid" {
2448                    assert!(
2449                        verify_result.is_ok(),
2450                        "wycheproof ECDSA DER SHA-256 tcId={} expected valid but failed",
2451                        test["tcId"]
2452                    );
2453                    valid_tested += 1;
2454                } else {
2455                    assert!(
2456                        verify_result.is_err(),
2457                        "wycheproof ECDSA DER SHA-256 tcId={} expected invalid but passed",
2458                        test["tcId"]
2459                    );
2460                    invalid_tested += 1;
2461                }
2462            }
2463        }
2464        assert!(valid_tested > 0, "no valid ECDSA DER SHA-256 wycheproof tests were run");
2465        assert!(invalid_tested > 0, "no invalid ECDSA DER SHA-256 wycheproof tests were run");
2466    }
2467
2468    #[test]
2469    fn wycheproof_ecdh_p256_asn() {
2470        let data: serde_json::Value =
2471            serde_json::from_str(include_str!("../testdata/wycheproof/testvectors_v1/ecdh_secp256r1_test.json"))
2472                .unwrap();
2473        let mut valid_tested = 0u64;
2474        let mut invalid_tested = 0u64;
2475        let mut acceptable_tested = 0u64;
2476        for group in data["testGroups"].as_array().unwrap() {
2477            for test in group["tests"].as_array().unwrap() {
2478                let public_hex = test["public"].as_str().unwrap();
2479                let private_hex = test["private"].as_str().unwrap();
2480                let expected_shared_hex = test["shared"].as_str().unwrap();
2481                let result = test["result"].as_str().unwrap();
2482
2483                let spki_der = hex::decode(public_hex).unwrap();
2484                let Some(sec1_point) = spki_to_sec1_point(&spki_der) else {
2485                    if result == "valid" {
2486                        panic!("wycheproof ECDH ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
2487                    }
2488                    invalid_tested += 1;
2489                    continue;
2490                };
2491
2492                // Private key hex is a bigint, may have leading zeros or be
2493                // shorter than 32 bytes. Pad or strip to exactly 32 bytes.
2494                let private_bytes = hex::decode(private_hex).unwrap();
2495                let mut private_key = [0u8; SECRET_KEY_SIZE];
2496                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
2497                let skip = if private_bytes.len() > SECRET_KEY_SIZE {
2498                    private_bytes.len() - SECRET_KEY_SIZE
2499                } else {
2500                    0
2501                };
2502                private_key[SECRET_KEY_SIZE - effective_len..]
2503                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
2504
2505                let shared = ecdh(&private_key, &sec1_point);
2506
2507                if result == "valid" {
2508                    let shared = shared.unwrap();
2509                    let shared_hex = hex::encode(shared);
2510                    assert_eq!(shared_hex, expected_shared_hex, "wycheproof ECDH ASN tcId={}", test["tcId"]);
2511                    valid_tested += 1;
2512                } else if result == "invalid" {
2513                    assert!(
2514                        shared.is_err(),
2515                        "wycheproof ECDH ASN tcId={} expected invalid but passed",
2516                        test["tcId"]
2517                    );
2518                    invalid_tested += 1;
2519                } else {
2520                    acceptable_tested += 1;
2521                }
2522            }
2523        }
2524        assert!(valid_tested > 0, "no valid ECDH ASN wycheproof tests were run");
2525        assert!(invalid_tested > 0, "no invalid ECDH ASN wycheproof tests were run");
2526        assert!(acceptable_tested > 0, "no acceptable ECDH ASN wycheproof tests were run");
2527    }
2528}