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