Skip to main content

crypto/
p224.rs

1use big_number::Uint;
2
3use crate::{EllipticCurveError, Hasher, hmac::Hmac, sha2::Sha256};
4
5/// Size of a P-224 secret key in bytes (28 bytes).
6pub const SECRET_KEY_SIZE: usize = 28;
7/// Size of a compressed P-224 public key in bytes (29 bytes, includes 0x02/0x03 prefix).
8pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 29;
9/// Size of an uncompressed P-224 public key in bytes (57 bytes, includes 0x04 prefix).
10pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 57;
11/// Size of a P-224 ECDSA signature in bytes (56 bytes, r || s).
12pub const SIGNATURE_SIZE: usize = 56;
13/// Size of the raw ECDH shared secret in bytes (28 bytes). **Must not** be used directly
14/// as an encryption key; apply a KDF first.
15pub const ECDH_SHARED_SECRET_SIZE: usize = 28;
16
17/// P-224 (secp224r1) ECDSA secret key.
18///
19/// Supports signing and ECDH key agreement. Messages are hashed with SHA-256,
20/// truncated to the leftmost 224 bits as specified by FIPS 186-4.
21///
22/// # Signing
23///
24/// ```ignore
25/// use crypto::p224::SecretKey;
26///
27/// let key = SecretKey::generate().unwrap();
28/// let signature = key.sign(b"message").unwrap();
29/// ```
30///
31/// # ECDH key exchange
32///
33/// ```ignore
34/// use crypto::p224::SecretKey;
35///
36/// let alice = SecretKey::generate().unwrap();
37/// let bob = SecretKey::generate().unwrap();
38/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
39/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
40/// assert_eq!(alice_shared, bob_shared);
41/// ```
42///
43/// # Security
44///
45/// The raw shared secret from [`ecdh`](Self::ecdh) **must not** be used
46/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
47// TODO: zeroize
48#[derive(Clone, Copy, Debug, PartialEq, Eq)]
49pub struct SecretKey {
50    scalar: Scalar,
51    public_point: AffinePoint,
52}
53
54impl SecretKey {
55    #[cfg(feature = "random")]
56    pub fn generate() -> Result<SecretKey, EllipticCurveError> {
57        let key: [u8; SECRET_KEY_SIZE] = crate::random::random_bytes();
58        Self::from_bytes(&key)
59    }
60
61    pub fn from_bytes(key: &[u8; SECRET_KEY_SIZE]) -> Result<SecretKey, EllipticCurveError> {
62        let scalar = Scalar::from_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
63        let public_point = scalar_mul_generator(&scalar)
64            .to_affine()
65            .ok_or(EllipticCurveError::Unspecified)?;
66        Ok(SecretKey {
67            scalar,
68            public_point,
69        })
70    }
71
72    pub fn public_key(&self) -> PublicKey {
73        PublicKey {
74            point: self.public_point,
75        }
76    }
77
78    pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
79        ecdsa_sign_inner(&self.scalar, message)
80    }
81
82    pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
83        ecdh_inner(&self.scalar, &peer_public.point)
84    }
85
86    pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
87        self.scalar.to_bytes()
88    }
89}
90
91/// P-224 (secp224r1) ECDSA public key.
92///
93/// Supports signature verification and ECDH key agreement. Both compressed
94/// (29-byte) and uncompressed (57-byte) SEC1 encodings are accepted on input;
95/// use [`to_bytes`](Self::to_bytes) to export uncompressed, and
96/// [`to_compressed_bytes`](Self::to_compressed_bytes) to export compressed.
97///
98/// # Verification
99///
100/// ```ignore
101/// use crypto::p224::SecretKey;
102///
103/// let key = SecretKey::generate().unwrap();
104/// let signature = key.sign(b"message").unwrap();
105/// assert!(key.public_key().verify(b"message", &signature).is_ok());
106/// ```
107#[derive(Clone, Copy, Debug, PartialEq, Eq)]
108pub struct PublicKey {
109    point: AffinePoint,
110}
111
112impl PublicKey {
113    #[inline]
114    pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
115        let point = AffinePoint::from_sec1_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
116        Ok(PublicKey {
117            point,
118        })
119    }
120
121    /// Build a public key from raw affine x and y coordinates (both
122    /// big-endian, 28 bytes each). Returns `InvalidKey` if the coordinates
123    /// are not a valid point on the P-224 curve.
124    ///
125    /// This is useful when importing keys from formats like JWK where `x`
126    /// and `y` are available directly.
127    #[inline]
128    pub fn from_x_y(x_bytes: &[u8; 28], y_bytes: &[u8; 28]) -> Result<PublicKey, EllipticCurveError> {
129        let x = FieldElement::from_bytes(x_bytes).ok_or(EllipticCurveError::InvalidKey)?;
130        let y = FieldElement::from_bytes(y_bytes).ok_or(EllipticCurveError::InvalidKey)?;
131        let point = AffinePoint::new(x, y).ok_or(EllipticCurveError::InvalidKey)?;
132        Ok(PublicKey {
133            point,
134        })
135    }
136
137    pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
138        ecdsa_verify_inner(&self.point, message, signature)
139    }
140
141    #[inline]
142    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
143        self.point.to_uncompressed_bytes()
144    }
145
146    /// Returns the 29-byte compressed SEC1 encoding (`0x02`/`0x03 || x`).
147    ///
148    /// # Errors
149    ///
150    /// A valid `PublicKey` always has a compressed encoding, so this never
151    /// returns `Err`; the `Result` mirrors the other fallible key operations.
152    #[inline]
153    pub fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
154        self.point.to_compressed_bytes()
155    }
156
157    /// Returns the `X` and `Y` points as big-endian arrays.
158    #[inline]
159    pub fn x_y(&self) -> ([u8; 28], [u8; 28]) {
160        (self.point.x.to_bytes(), self.point.y.to_bytes())
161    }
162}
163
164type U224 = Uint<224, 4>;
165
166// P-224 values are 224 bits (28 bytes), so they do not fill an integral number
167// of 64-bit limbs. `Uint`'s slice helpers assume a whole number of limbs, so
168// use these local converters to pack/unpack the 28-byte big-endian encoding.
169#[inline]
170fn u224_from_be(bytes: &[u8; 28]) -> U224 {
171    U224::from_limbs([
172        u64::from_be_bytes(bytes[20..28].try_into().unwrap()),
173        u64::from_be_bytes(bytes[12..20].try_into().unwrap()),
174        u64::from_be_bytes(bytes[4..12].try_into().unwrap()),
175        u32::from_be_bytes(bytes[0..4].try_into().unwrap()) as u64,
176    ])
177}
178
179#[inline]
180fn u224_to_be(value: U224) -> [u8; 28] {
181    let mut out = [0u8; 28];
182    out[20..28].copy_from_slice(&value.limbs[0].to_be_bytes());
183    out[12..20].copy_from_slice(&value.limbs[1].to_be_bytes());
184    out[4..12].copy_from_slice(&value.limbs[2].to_be_bytes());
185    out[0..4].copy_from_slice(&(value.limbs[3] as u32).to_be_bytes());
186    out
187}
188
189const MODULUS_P: U224 = U224::from_limbs([
190    0x0000_0000_0000_0001,
191    0xffff_ffff_0000_0000,
192    0xffff_ffff_ffff_ffff,
193    0x0000_0000_ffff_ffff,
194]);
195
196const MODULUS_N: U224 = U224::from_limbs([
197    0x13dd_2945_5c5c_2a3d,
198    0xffff_16a2_e0b8_f03e,
199    0xffff_ffff_ffff_ffff,
200    0x0000_0000_ffff_ffff,
201]);
202
203const P_MINUS_TWO: U224 = U224::from_limbs([
204    0xffff_ffff_ffff_ffff,
205    0xffff_fffe_ffff_ffff,
206    0xffff_ffff_ffff_ffff,
207    0x0000_0000_ffff_ffff,
208]);
209
210const N_MINUS_TWO: U224 = U224::from_limbs([
211    0x13dd_2945_5c5c_2a3b,
212    0xffff_16a2_e0b8_f03e,
213    0xffff_ffff_ffff_ffff,
214    0x0000_0000_ffff_ffff,
215]);
216
217// (p - 1) / 2, used for the Legendre symbol during square-root computation.
218const P_MINUS_ONE_OVER_TWO: U224 = U224::from_limbs([
219    0x0000_0000_0000_0000,
220    0xffff_ffff_8000_0000,
221    0xffff_ffff_ffff_ffff,
222    0x0000_0000_7fff_ffff,
223]);
224
225// P-224 has p = 2^224 - 2^96 + 1, so p - 1 = 2^96 * d with d = 2^128 - 1.
226// These constants are used by the Tonelli-Shanks square-root algorithm.
227const TONELLI_S: usize = 96;
228const TONELLI_D: U224 = U224::from_limbs([
229    0xffff_ffff_ffff_ffff,
230    0xffff_ffff_ffff_ffff,
231    0x0000_0000_0000_0000,
232    0x0000_0000_0000_0000,
233]);
234const TONELLI_D_PLUS_ONE_OVER_TWO: U224 = U224::from_limbs([
235    0x0000_0000_0000_0000,
236    0x8000_0000_0000_0000,
237    0x0000_0000_0000_0000,
238    0x0000_0000_0000_0000,
239]);
240// Smallest quadratic non-residue modulo p (verified: 11^((p-1)/2) = -1).
241const TONELLI_NON_RESIDUE: FieldElement = FieldElement(U224::from_u64(11));
242
243// Barrett reduction constants: mu = floor(2^(2 * 4 * 64) / modulus), as
244// computed by big_number's `compute_mu_for_barrett`.
245const P_MU: [u64; 5] = [
246    0x0000_0000_ffff_ffff,
247    0xffff_ffff_ffff_ffff,
248    0x0000_0000_ffff_ffff,
249    0x0000_0000_0000_0000,
250    0x0000_0001_0000_0000,
251];
252
253const N_MU: [u64; 5] = [
254    0xd4ba_a4cf_1822_bc47,
255    0xec22_d6ba_a3a3_d5c3,
256    0x0000_e95d_1f47_0fc1,
257    0x0000_0000_0000_0000,
258    0x0000_0001_0000_0000,
259];
260
261const CURVE_B: FieldElement = FieldElement(U224::from_limbs([
262    0x270b_3943_2355_ffb4,
263    0x5044_b0b7_d7bf_d8ba,
264    0x0c04_b3ab_f541_3256,
265    0x0000_0000_b405_0a85,
266]));
267
268const GENERATOR_X: FieldElement = FieldElement(U224::from_limbs([
269    0x3432_80d6_115c_1d21,
270    0x4a03_c1d3_56c2_1122,
271    0x6bb4_bf7f_3213_90b9,
272    0x0000_0000_b70e_0cbd,
273]));
274
275const GENERATOR_Y: FieldElement = FieldElement(U224::from_limbs([
276    0x44d5_8199_8500_7e34,
277    0xcd43_75a0_5a07_4764,
278    0xb5f7_23fb_4c22_dfe6,
279    0x0000_0000_bd37_6388,
280]));
281
282#[derive(Clone, Copy, Debug, PartialEq, Eq)]
283struct FieldElement(U224);
284
285impl FieldElement {
286    const ZERO: Self = Self(U224::ZERO);
287    const ONE: Self = Self(U224::ONE);
288
289    #[inline]
290    fn from_bytes(bytes: &[u8; 28]) -> Option<Self> {
291        let value = u224_from_be(bytes);
292        if value.ct_ge(&MODULUS_P) {
293            None
294        } else {
295            Some(Self(value))
296        }
297    }
298
299    #[inline]
300    fn to_bytes(self) -> [u8; 28] {
301        u224_to_be(self.0)
302    }
303
304    #[inline]
305    fn is_zero(&self) -> bool {
306        self.0.is_zero()
307    }
308
309    #[inline]
310    fn is_odd(&self) -> bool {
311        self.0.is_odd()
312    }
313
314    #[inline]
315    fn add(self, rhs: Self) -> Self {
316        Self(self.0.add_mod(&rhs.0, &MODULUS_P))
317    }
318
319    #[inline]
320    fn sub(self, rhs: Self) -> Self {
321        Self(self.0.sub_mod(&rhs.0, &MODULUS_P))
322    }
323
324    #[inline]
325    fn double(self) -> Self {
326        Self(self.0.double_mod(&MODULUS_P))
327    }
328
329    #[inline]
330    fn square(self) -> Self {
331        self.mul(self)
332    }
333
334    #[inline]
335    fn mul(self, rhs: Self) -> Self {
336        Self(self.0.mul_mod_barrett(&rhs.0, &MODULUS_P, &P_MU))
337    }
338
339    #[inline]
340    fn triple(self) -> Self {
341        self.double().add(self)
342    }
343
344    #[inline]
345    fn negate(self) -> Self {
346        let (diff, _) = MODULUS_P.sub_raw(&self.0);
347        Self(U224::ct_select(&U224::ZERO, &diff, self.is_zero()))
348    }
349
350    #[inline]
351    fn pow(self, exponent: &U224) -> Self {
352        let mut result = Self::ONE;
353        let mut i = 224usize;
354        while i > 0 {
355            i -= 1;
356            result = result.square();
357            let product = result.mul(self);
358            result = Self::select(&product, &result, exponent.bit(i));
359        }
360        result
361    }
362
363    #[inline]
364    fn invert(self) -> Option<Self> {
365        Some(self.pow(&P_MINUS_TWO))
366    }
367
368    /// Square root modulo p. Returns `None` when `self` is a quadratic
369    /// non-residue.
370    ///
371    /// P-224 has p ≡ 1 (mod 4), so the simple `a^((p+1)/4)` formula used by
372    /// P-256/P-384 is unavailable; this uses Tonelli-Shanks instead. It is only
373    /// invoked on public data (SEC1 decompression), so its data-dependent
374    /// control flow does not leak secrets.
375    #[inline]
376    fn sqrt(self) -> Option<Self> {
377        if self.is_zero() {
378            return Some(Self::ZERO);
379        }
380        if self.pow(&P_MINUS_ONE_OVER_TWO) != Self::ONE {
381            return None;
382        }
383
384        let mut m = TONELLI_S;
385        let mut c = TONELLI_NON_RESIDUE.pow(&TONELLI_D);
386        let mut t = self.pow(&TONELLI_D);
387        let mut r = self.pow(&TONELLI_D_PLUS_ONE_OVER_TWO);
388
389        while t != Self::ONE {
390            // Find the least i (0 < i < m) such that t^(2^i) == 1.
391            let mut i = 0usize;
392            let mut probe = t;
393            while probe != Self::ONE {
394                probe = probe.square();
395                i += 1;
396                if i >= m {
397                    return None;
398                }
399            }
400
401            let mut b = c;
402            for _ in 0..(m - i - 1) {
403                b = b.square();
404            }
405
406            m = i;
407            c = b.square();
408            t = t.mul(c);
409            r = r.mul(b);
410        }
411
412        if r.square() == self { Some(r) } else { None }
413    }
414
415    #[inline]
416    fn select(a: &Self, b: &Self, choice: bool) -> Self {
417        Self(U224::ct_select(&a.0, &b.0, choice))
418    }
419}
420
421#[derive(Clone, Copy, Debug, PartialEq, Eq)]
422struct Scalar(U224);
423
424impl Scalar {
425    const ZERO: Self = Self(U224::ZERO);
426    const ONE: Self = Self(U224::ONE);
427
428    #[inline]
429    fn from_bytes(bytes: &[u8; 28]) -> Option<Self> {
430        let value = u224_from_be(bytes);
431        if value.is_zero() || value.ct_ge(&MODULUS_N) {
432            None
433        } else {
434            Some(Self(value))
435        }
436    }
437
438    /// Reduce a 224-bit big-endian value modulo n. Unlike [`Self::from_bytes`],
439    /// this accepts zero and values greater than or equal to n.
440    #[inline]
441    fn from_reduced_bytes(bytes: &[u8; 28]) -> Self {
442        let value = u224_from_be(bytes);
443        let (sub_value, _) = value.sub_raw(&MODULUS_N);
444        let reduced = U224::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N));
445        Self(reduced)
446    }
447
448    /// Reduce a SHA-256 digest to a scalar. FIPS 186-4 truncates the digest to
449    /// the leftmost 224 bits (the first 28 bytes) before reduction.
450    #[inline]
451    fn from_hash(hash: &[u8; 32]) -> Self {
452        Self::from_reduced_bytes(&hash[..28].try_into().unwrap())
453    }
454
455    #[inline]
456    fn to_bytes(self) -> [u8; 28] {
457        u224_to_be(self.0)
458    }
459
460    #[inline]
461    fn is_zero(&self) -> bool {
462        self.0.is_zero()
463    }
464
465    #[inline]
466    fn bit(&self, index: usize) -> bool {
467        self.0.bit(index)
468    }
469
470    #[inline]
471    fn add(self, rhs: Self) -> Self {
472        Self(self.0.add_mod(&rhs.0, &MODULUS_N))
473    }
474
475    #[cfg(test)]
476    #[inline]
477    fn sub(self, rhs: Self) -> Self {
478        Self(self.0.sub_mod(&rhs.0, &MODULUS_N))
479    }
480
481    #[inline]
482    fn mul(self, rhs: Self) -> Self {
483        Self(self.0.mul_mod_barrett(&rhs.0, &MODULUS_N, &N_MU))
484    }
485
486    #[inline]
487    fn invert(self) -> Option<Self> {
488        Some(Self(self.scalar_pow(&N_MINUS_TWO)))
489    }
490
491    #[inline]
492    fn scalar_pow(self, exponent: &U224) -> U224 {
493        let mut result = Scalar::ONE;
494        let mut i = 224usize;
495        while i > 0 {
496            i -= 1;
497            result = result.mul(result);
498            let product = result.mul(self);
499            result = Scalar::select(&product, &result, exponent.bit(i));
500        }
501        result.0
502    }
503
504    #[inline]
505    fn select(a: &Self, b: &Self, choice: bool) -> Self {
506        Self(U224::ct_select(&a.0, &b.0, choice))
507    }
508}
509
510#[derive(Clone, Copy, Debug, PartialEq, Eq)]
511struct AffinePoint {
512    x: FieldElement,
513    y: FieldElement,
514    infinity: bool,
515}
516
517impl AffinePoint {
518    const GENERATOR: Self = Self {
519        x: GENERATOR_X,
520        y: GENERATOR_Y,
521        infinity: false,
522    };
523
524    #[inline]
525    fn new(x: FieldElement, y: FieldElement) -> Option<Self> {
526        let point = Self {
527            x,
528            y,
529            infinity: false,
530        };
531        if point.is_on_curve() { Some(point) } else { None }
532    }
533
534    #[inline]
535    fn is_on_curve(&self) -> bool {
536        if self.infinity {
537            return false;
538        }
539        let x2 = self.x.square();
540        let x3 = x2.mul(self.x);
541        let rhs = x3.sub(self.x.triple()).add(CURVE_B);
542        self.y.square() == rhs
543    }
544
545    #[inline]
546    fn to_uncompressed_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
547        let mut out = [0u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
548        out[0] = 0x04;
549        out[1..29].copy_from_slice(&self.x.to_bytes());
550        out[29..57].copy_from_slice(&self.y.to_bytes());
551        out
552    }
553
554    /// SEC1 compressed encoding: `0x02`/`0x03 || x`, selecting the prefix from
555    /// the parity of `y`.
556    #[inline]
557    fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
558        let mut out = [0u8; PUBLIC_KEY_COMPRESSED_SIZE];
559        out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
560        out[1..29].copy_from_slice(&self.x.to_bytes());
561        out
562    }
563
564    fn from_sec1_bytes(bytes: &[u8]) -> Option<Self> {
565        match bytes.len() {
566            PUBLIC_KEY_UNCOMPRESSED_SIZE if bytes[0] == 0x04 => {
567                let x = FieldElement::from_bytes(bytes[1..29].try_into().unwrap())?;
568                let y = FieldElement::from_bytes(bytes[29..57].try_into().unwrap())?;
569                Self::new(x, y)
570            }
571            PUBLIC_KEY_COMPRESSED_SIZE if bytes[0] == 0x02 || bytes[0] == 0x03 => {
572                let x = FieldElement::from_bytes(bytes[1..29].try_into().unwrap())?;
573                let rhs = x.square().mul(x).sub(x.triple()).add(CURVE_B);
574                let y = rhs.sqrt()?;
575                let y_is_odd = y.is_odd();
576                let select_neg = y_is_odd != (bytes[0] == 0x03);
577                let y = FieldElement::select(&y.negate(), &y, select_neg);
578                Self::new(x, y)
579            }
580            _ => None,
581        }
582    }
583}
584
585#[derive(Clone, Copy, Debug, PartialEq, Eq)]
586struct ProjectivePoint {
587    x: FieldElement,
588    y: FieldElement,
589    z: FieldElement,
590}
591
592impl ProjectivePoint {
593    const IDENTITY: Self = Self {
594        x: FieldElement::ZERO,
595        y: FieldElement::ONE,
596        z: FieldElement::ZERO,
597    };
598
599    #[cfg(test)]
600    #[inline]
601    fn from_affine(point: &AffinePoint) -> Self {
602        if point.infinity {
603            Self::IDENTITY
604        } else {
605            Self {
606                x: point.x,
607                y: point.y,
608                z: FieldElement::ONE,
609            }
610        }
611    }
612
613    #[inline]
614    fn is_identity(&self) -> bool {
615        self.z.is_zero()
616    }
617
618    #[inline]
619    fn select(a: &Self, b: &Self, choice: bool) -> Self {
620        Self {
621            x: FieldElement::select(&a.x, &b.x, choice),
622            y: FieldElement::select(&a.y, &b.y, choice),
623            z: FieldElement::select(&a.z, &b.z, choice),
624        }
625    }
626
627    #[inline]
628    fn to_affine(&self) -> Option<AffinePoint> {
629        if self.is_identity() {
630            return None;
631        }
632        let z_inv = self.z.invert()?;
633        AffinePoint::new(self.x.mul(z_inv), self.y.mul(z_inv))
634    }
635
636    fn add(&self, rhs: &Self) -> Self {
637        let xx = self.x.mul(rhs.x);
638        let yy = self.y.mul(rhs.y);
639        let zz = self.z.mul(rhs.z);
640        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
641        let yz_pairs = self.y.add(self.z).mul(rhs.y.add(rhs.z)).sub(yy.add(zz));
642        let xz_pairs = self.x.add(self.z).mul(rhs.x.add(rhs.z)).sub(xx.add(zz));
643
644        let bzz_part = xz_pairs.sub(CURVE_B.mul(zz));
645        let bzz3_part = bzz_part.triple();
646        let yy_m_bzz3 = yy.sub(bzz3_part);
647        let yy_p_bzz3 = yy.add(bzz3_part);
648
649        let zz3 = zz.triple();
650        let bxz_part = CURVE_B.mul(xz_pairs).sub(zz3.add(xx));
651        let bxz3_part = bxz_part.triple();
652        let xx3_m_zz3 = xx.triple().sub(zz3);
653
654        Self {
655            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
656            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
657            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
658        }
659    }
660
661    fn add_mixed(&self, rhs: &AffinePoint) -> Self {
662        if rhs.infinity {
663            return *self;
664        }
665
666        let xx = self.x.mul(rhs.x);
667        let yy = self.y.mul(rhs.y);
668        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
669        let yz_pairs = rhs.y.mul(self.z).add(self.y);
670        let xz_pairs = rhs.x.mul(self.z).add(self.x);
671
672        let bz_part = xz_pairs.sub(CURVE_B.mul(self.z));
673        let bz3_part = bz_part.triple();
674        let yy_m_bzz3 = yy.sub(bz3_part);
675        let yy_p_bzz3 = yy.add(bz3_part);
676
677        let z3 = self.z.triple();
678        let bxz_part = CURVE_B.mul(xz_pairs).sub(z3.add(xx));
679        let bxz3_part = bxz_part.triple();
680        let xx3_m_zz3 = xx.triple().sub(z3);
681
682        Self {
683            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
684            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
685            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
686        }
687    }
688
689    fn double(&self) -> Self {
690        let xx = self.x.square();
691        let yy = self.y.square();
692        let zz = self.z.square();
693        let xy2 = self.x.mul(self.y).double();
694        let xz2 = self.x.mul(self.z).double();
695
696        let bzz_part = CURVE_B.mul(zz).sub(xz2);
697        let bzz3_part = bzz_part.triple();
698        let yy_m_bzz3 = yy.sub(bzz3_part);
699        let yy_p_bzz3 = yy.add(bzz3_part);
700        let y_frag = yy_p_bzz3.mul(yy_m_bzz3);
701        let x_frag = yy_m_bzz3.mul(xy2);
702
703        let zz3 = zz.triple();
704        let bxz2_part = CURVE_B.mul(xz2).sub(zz3.add(xx));
705        let bxz6_part = bxz2_part.triple();
706        let xx3_m_zz3 = xx.triple().sub(zz3);
707
708        let y = y_frag.add(xx3_m_zz3.mul(bxz6_part));
709        let yz2 = self.y.mul(self.z).double();
710        let x = x_frag.sub(bxz6_part.mul(yz2));
711        let z = yz2.mul(yy).double().double();
712
713        Self {
714            x,
715            y,
716            z,
717        }
718    }
719}
720
721fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
722    scalar_mul_affine(&AffinePoint::GENERATOR, scalar)
723}
724
725fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
726    let mut acc = ProjectivePoint::IDENTITY;
727    let mut bit = 224usize;
728    while bit > 0 {
729        bit -= 1;
730        acc = acc.double();
731        let candidate = acc.add_mixed(base);
732        acc = ProjectivePoint::select(&candidate, &acc, scalar.bit(bit));
733    }
734    acc
735}
736
737#[inline]
738fn hash_message(message: &[u8]) -> [u8; 32] {
739    let digest = Sha256::hash(message);
740    return digest.as_ref().try_into().unwrap();
741}
742
743#[inline]
744fn hmac_sha256(key: &[u8], data: &[u8]) -> [u8; 32] {
745    let mac = Hmac::<Sha256>::mac(key, data);
746    return mac.as_ref().try_into().unwrap();
747}
748
749// FIPS 186-4 truncates a SHA-256 digest to the leftmost 224 bits, then reduces
750// modulo n. The result is the 28-byte `bits2octets` value used by RFC 6979.
751fn bits2octets(hash: &[u8; 32]) -> [u8; 28] {
752    Scalar::from_hash(hash).to_bytes()
753}
754
755// RFC 6979 DRBG state uses the full 32-byte SHA-256 output for V and K, while
756// int2octets(x) and bits2octets(h1) are only 28 bytes (the size of the order n).
757fn rfc6979_init_state(private_key: &Scalar, message_hash: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
758    let x = private_key.to_bytes();
759    let h1 = bits2octets(message_hash);
760
761    let mut v = [0x01u8; 32];
762    let mut k = [0u8; 32];
763
764    let mut buf = [0u8; 89];
765    buf[..32].copy_from_slice(&v);
766    buf[32] = 0x00;
767    buf[33..61].copy_from_slice(&x);
768    buf[61..89].copy_from_slice(&h1);
769    k = hmac_sha256(&k, &buf);
770    v = hmac_sha256(&k, &v);
771
772    buf[..32].copy_from_slice(&v);
773    buf[32] = 0x01;
774    k = hmac_sha256(&k, &buf);
775    v = hmac_sha256(&k, &v);
776
777    (k, v)
778}
779
780fn rfc6979_retry(k: &mut [u8; 32], v: &mut [u8; 32]) {
781    let mut retry_buf = [0u8; 33];
782    retry_buf[..32].copy_from_slice(v);
783    retry_buf[32] = 0x00;
784    *k = hmac_sha256(k, &retry_buf);
785    *v = hmac_sha256(k, v);
786}
787
788fn rfc6979_retry_clone(k: &[u8; 32], v: &[u8; 32]) -> ([u8; 32], [u8; 32]) {
789    let mut retry_buf = [0u8; 33];
790    retry_buf[..32].copy_from_slice(v);
791    retry_buf[32] = 0x00;
792    let k_new = hmac_sha256(k, &retry_buf);
793    let v_new = hmac_sha256(&k_new, v);
794    (k_new, v_new)
795}
796
797// Branch-free byte-level select: returns a[i] if choice else b[i].
798fn ct_select_bytes<const N: usize>(a: &[u8; N], b: &[u8; N], choice: bool) -> [u8; N] {
799    let mask = (choice as u8).wrapping_neg();
800    let mut out = [0u8; N];
801    for i in 0..N {
802        out[i] = (a[i] & mask) | (b[i] & !mask);
803    }
804    out
805}
806
807fn rfc6979_generate_k(private_key: &Scalar, message_hash: &[u8; 32]) -> Scalar {
808    let (mut k, mut v) = rfc6979_init_state(private_key, message_hash);
809
810    // Fixed 3 iterations with constant-time state selection, mirroring the
811    // P-256/P-384 implementations. The first valid candidate is returned.
812    let mut candidate = [0u8; 28];
813    let mut found = false;
814
815    for _ in 0..3 {
816        v = hmac_sha256(&k, &v);
817        let candidate_bytes: [u8; 28] = v[..28].try_into().unwrap();
818        let val = u224_from_be(&candidate_bytes);
819        let is_valid = !val.is_zero() && !val.ct_ge(&MODULUS_N);
820
821        let take = is_valid && !found;
822        candidate = ct_select_bytes(&candidate_bytes, &candidate, take);
823        found = found || is_valid;
824
825        let (k_retry, v_retry) = rfc6979_retry_clone(&k, &v);
826        k = ct_select_bytes(&k, &k_retry, !is_valid);
827        v = ct_select_bytes(&v, &v_retry, !is_valid);
828    }
829
830    if found {
831        return Scalar::from_bytes(&candidate).unwrap_or(Scalar::ZERO);
832    }
833
834    // Fallback (probability < 2^-96): emit additional HMAC outputs.
835    v = hmac_sha256(&k, &v);
836    if let Some(sc) = Scalar::from_bytes(&v[..28].try_into().unwrap()) {
837        return sc;
838    }
839
840    loop {
841        v = hmac_sha256(&k, &v);
842        if let Some(sc) = Scalar::from_bytes(&v[..28].try_into().unwrap()) {
843            return sc;
844        }
845        rfc6979_retry(&mut k, &mut v);
846    }
847}
848
849fn parse_private_key(private_key: &[u8; SECRET_KEY_SIZE]) -> Result<Scalar, EllipticCurveError> {
850    Scalar::from_bytes(private_key).ok_or(EllipticCurveError::InvalidKey)
851}
852
853fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, EllipticCurveError> {
854    AffinePoint::from_sec1_bytes(public_key).ok_or(EllipticCurveError::InvalidKey)
855}
856
857#[cfg(test)]
858fn derive_public_key_uncompressed(
859    private_key: &[u8; SECRET_KEY_SIZE],
860) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], EllipticCurveError> {
861    let scalar = parse_private_key(private_key)?;
862    let point = scalar_mul_generator(&scalar)
863        .to_affine()
864        .ok_or(EllipticCurveError::Unspecified)?;
865    Ok(point.to_uncompressed_bytes())
866}
867
868#[cfg(test)]
869fn derive_public_key_compressed(
870    private_key: &[u8; SECRET_KEY_SIZE],
871) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], EllipticCurveError> {
872    let scalar = parse_private_key(private_key)?;
873    let point = scalar_mul_generator(&scalar)
874        .to_affine()
875        .ok_or(EllipticCurveError::Unspecified)?;
876    Ok(point.to_compressed_bytes())
877}
878
879fn ecdh_inner(scalar: &Scalar, peer_point: &AffinePoint) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
880    let shared_point = scalar_mul_affine(peer_point, scalar)
881        .to_affine()
882        .ok_or(EllipticCurveError::Unspecified)?;
883    Ok(shared_point.x.to_bytes())
884}
885
886pub fn ecdh(
887    secret_key: &[u8; SECRET_KEY_SIZE],
888    peer_public_key: &[u8],
889) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
890    let scalar = parse_private_key(secret_key)?;
891    let peer_point = parse_public_key(peer_public_key)?;
892    ecdh_inner(&scalar, &peer_point)
893}
894
895fn ecdsa_sign_inner(scalar: &Scalar, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
896    let message_hash = hash_message(message);
897    let z = Scalar::from_hash(&message_hash);
898
899    // Fixed 2-iteration loop for the astronomically unlikely r=0 / s=0 retry.
900    for _ in 0..2 {
901        let k = rfc6979_generate_k(scalar, &message_hash);
902
903        let r_point = scalar_mul_generator(&k)
904            .to_affine()
905            .ok_or(EllipticCurveError::Unspecified)?;
906        let r = Scalar::from_reduced_bytes(&r_point.x.to_bytes());
907        if r.is_zero() {
908            continue;
909        }
910
911        let kinv = k.invert().ok_or(EllipticCurveError::Unspecified)?;
912        let s = kinv.mul(z.add(r.mul(*scalar)));
913        if s.is_zero() {
914            continue;
915        }
916
917        let mut out = [0u8; SIGNATURE_SIZE];
918        out[..28].copy_from_slice(&r.to_bytes());
919        out[28..].copy_from_slice(&s.to_bytes());
920        return Ok(out);
921    }
922
923    Err(EllipticCurveError::Unspecified)
924}
925
926fn ecdsa_verify_inner(
927    public_point: &AffinePoint,
928    message: &[u8],
929    signature: &[u8; SIGNATURE_SIZE],
930) -> Result<(), EllipticCurveError> {
931    let r = Scalar::from_bytes(signature[..28].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
932    let s = Scalar::from_bytes(signature[28..].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
933    let z = Scalar::from_hash(&hash_message(message));
934
935    let w = s.invert().ok_or(EllipticCurveError::Unspecified)?;
936    let u1 = z.mul(w);
937    let u2 = r.mul(w);
938
939    let point = scalar_mul_generator(&u1).add(&scalar_mul_affine(public_point, &u2));
940    let affine = point.to_affine().ok_or(EllipticCurveError::Unspecified)?;
941    let x_mod_n = Scalar::from_reduced_bytes(&affine.x.to_bytes());
942
943    if x_mod_n == r {
944        Ok(())
945    } else {
946        Err(EllipticCurveError::Unspecified)
947    }
948}
949
950pub fn is_valid_public_key(public_key: &[u8]) -> bool {
951    AffinePoint::from_sec1_bytes(public_key).is_some()
952}
953
954#[cfg(test)]
955mod tests {
956    use super::*;
957
958    fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
959        let bytes = hex::decode(hex_bytes).unwrap();
960        assert_eq!(bytes.len(), N);
961        let mut out = [0u8; N];
962        out.copy_from_slice(&bytes);
963        out
964    }
965
966    // Read a DER TLV (tag-length-value) item.
967    fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
968        if *offset >= data.len() {
969            return None;
970        }
971        let tag = data[*offset];
972        *offset += 1;
973        if *offset >= data.len() {
974            return None;
975        }
976        let len_byte = data[*offset];
977        *offset += 1;
978        let (len, _) = if len_byte & 0x80 != 0 {
979            let num_bytes = (len_byte & 0x7f) as usize;
980            if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
981                return None;
982            }
983            if num_bytes > 1 && data[*offset] == 0 {
984                return None;
985            }
986            let mut l = 0usize;
987            for i in 0..num_bytes {
988                l = (l << 8) | data[*offset + i] as usize;
989            }
990            if l < 128 {
991                return None;
992            }
993            *offset += num_bytes;
994            (l, num_bytes + 1)
995        } else {
996            (len_byte as usize, 1)
997        };
998        if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
999            return None;
1000        }
1001        let value = &data[*offset..*offset + len];
1002        *offset = (*offset).checked_add(len)?;
1003        Some((tag, value))
1004    }
1005
1006    // Convert a DER-encoded ECDSA signature (SEQUENCE { INTEGER r, INTEGER s })
1007    // to P1363 format (r || s, each 28 bytes).
1008    fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 56]> {
1009        let mut offset = 0;
1010        let (tag, inner) = der_read_tlv(der, &mut offset)?;
1011        if tag != 0x30 {
1012            return None;
1013        }
1014        if offset != der.len() {
1015            return None;
1016        }
1017        let mut inner_offset = 0;
1018        let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
1019        if rtag != 0x02 || rval.is_empty() || rval.len() > 29 {
1020            return None;
1021        }
1022        let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
1023        if stag != 0x02 || sval.is_empty() || sval.len() > 29 {
1024            return None;
1025        }
1026        if inner_offset != inner.len() {
1027            return None;
1028        }
1029        let r_valid = if rval.len() == 28 && rval[0] >= 0x80 {
1030            false
1031        } else if rval.len() == 29 && rval[0] != 0 {
1032            false
1033        } else if rval.len() == 29 && rval[0] == 0 && rval[1] < 0x80 {
1034            false
1035        } else {
1036            rval.len() <= 29
1037        };
1038        let s_valid = if sval.len() == 28 && sval[0] >= 0x80 {
1039            false
1040        } else if sval.len() == 29 && sval[0] != 0 {
1041            false
1042        } else if sval.len() == 29 && sval[0] == 0 && sval[1] < 0x80 {
1043            false
1044        } else {
1045            sval.len() <= 29
1046        };
1047        if !r_valid || !s_valid {
1048            return None;
1049        }
1050
1051        let r_trimmed = if rval.len() == 29 && rval[0] == 0 {
1052            &rval[1..]
1053        } else {
1054            rval
1055        };
1056        let s_trimmed = if sval.len() == 29 && sval[0] == 0 {
1057            &sval[1..]
1058        } else {
1059            sval
1060        };
1061        if r_trimmed.len() > 28 || s_trimmed.len() > 28 {
1062            return None;
1063        }
1064        let mut sig = [0u8; 56];
1065        sig[28 - r_trimmed.len()..28].copy_from_slice(r_trimmed);
1066        sig[56 - s_trimmed.len()..56].copy_from_slice(s_trimmed);
1067        Some(sig)
1068    }
1069
1070    // Extract the raw SEC1 point from a DER SubjectPublicKeyInfo using the
1071    // named secp224r1 curve (explicit parameters are rejected).
1072    fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
1073        let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
1074        let secp224r1_oid: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x21];
1075        let mut offset = 0;
1076        let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
1077        let mut inner = 0;
1078        let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
1079        if _alg_tag != 0x30 {
1080            return None;
1081        }
1082        let mut ai = 0;
1083        let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
1084        if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
1085            return None;
1086        }
1087        let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
1088        if oid2_tag != 0x06 || oid2 != secp224r1_oid {
1089            return None;
1090        }
1091        let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
1092        if _bs_tag != 0x03 || bs_val.is_empty() {
1093            return None;
1094        }
1095        Some(bs_val[1..].to_vec())
1096    }
1097
1098    // RFC 6979 A.2.4: P-224 test key pair.
1099    const RFC6979_PRIVATE_KEY: &str = "f220266e1105bfe3083e03ec7a3a654651f45e37167e88600bf257c1";
1100    const RFC6979_PUBLIC_X: &str = "00cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c";
1101    const RFC6979_PUBLIC_Y: &str = "eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a";
1102
1103    #[test]
1104    fn derive_public_key_generator_matches_sec1_base_point() {
1105        let mut private_key = [0u8; SECRET_KEY_SIZE];
1106        private_key[27] = 1;
1107        let derived = derive_public_key_uncompressed(&private_key).unwrap();
1108        let expected = decode_hex::<57>(
1109            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
1110             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
1111        );
1112        assert_eq!(derived, expected);
1113    }
1114
1115    #[test]
1116    fn derive_public_key_matches_rfc6979_vector() {
1117        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1118        let expected = decode_hex::<57>(
1119            "0400cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c\
1120             eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a",
1121        );
1122        assert_eq!(derive_public_key_uncompressed(&private_key).unwrap(), expected);
1123        assert_eq!(
1124            PublicKey::from_x_y(&decode_hex::<28>(RFC6979_PUBLIC_X), &decode_hex::<28>(RFC6979_PUBLIC_Y),)
1125                .unwrap()
1126                .to_bytes(),
1127            expected,
1128        );
1129
1130        // Compressed form derives from the same point; prefix encodes y parity.
1131        let compressed = derive_public_key_compressed(&private_key).unwrap();
1132        assert_eq!(&compressed[1..], &decode_hex::<28>(RFC6979_PUBLIC_X));
1133    }
1134
1135    #[test]
1136    fn ecdsa_sign_matches_rfc6979_vectors() {
1137        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1138        let key = SecretKey::from_bytes(&private_key).unwrap();
1139
1140        let sample_signature = key.sign(b"sample").unwrap();
1141        let expected_sample = decode_hex::<56>(
1142            "61aa3da010e8e8406c656bc477a7a7189895e7e840cdfe8ff42307ba\
1143             bc814050dab5d23770879494f9e0a680dc1af7161991bde692b10101",
1144        );
1145        assert_eq!(sample_signature, expected_sample);
1146
1147        let test_signature = key.sign(b"test").unwrap();
1148        let expected_test = decode_hex::<56>(
1149            "ad04dde87b84747a243a631ea47a1ba6d1faa059149ad2440de6fba6\
1150             178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd",
1151        );
1152        assert_eq!(test_signature, expected_test);
1153    }
1154
1155    #[test]
1156    fn rfc6979_nonce_generation_matches_known_value() {
1157        let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
1158        let hash = hash_message(b"sample");
1159        assert_eq!(
1160            rfc6979_generate_k(&private_key, &hash).to_bytes(),
1161            decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")
1162        );
1163    }
1164
1165    #[test]
1166    fn rfc6979_test_message_nonce_matches_known_value() {
1167        let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
1168        let hash = hash_message(b"test");
1169        assert_eq!(
1170            rfc6979_generate_k(&private_key, &hash).to_bytes(),
1171            decode_hex::<28>("ff86f57924da248d6e44e8154eb69f0ae2aebaee9931d0b5a969f904")
1172        );
1173    }
1174
1175    #[test]
1176    fn rfc6979_bits2octets_truncates_and_reduces() {
1177        // SHA-256("sample") truncated to the leftmost 224 bits.
1178        let hash = hash_message(b"sample");
1179        let expected = decode_hex::<28>("af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a");
1180        assert_eq!(bits2octets(&hash), expected);
1181
1182        // A value exactly equal to n maps to zero; n+1 maps to one.
1183        // n in the top 224 bits maps to zero; n+1 maps to one.
1184        let n_times = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d00000000");
1185        assert_eq!(bits2octets(&n_times), [0u8; 28]);
1186        let n_plus_one = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e00000000");
1187        assert_eq!(
1188            bits2octets(&n_plus_one),
1189            decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")
1190        );
1191    }
1192
1193    #[test]
1194    fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
1195        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1196        let key = SecretKey::from_bytes(&private_key).unwrap();
1197        let uncompressed = key.public_key();
1198        let compressed = derive_public_key_compressed(&private_key).unwrap();
1199        let signature = key.sign(b"sample").unwrap();
1200
1201        assert!(uncompressed.verify(b"sample", &signature).is_ok());
1202        let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1203        assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
1204    }
1205
1206    #[test]
1207    fn verify_rejects_tampering_and_invalid_points() {
1208        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1209        let key = SecretKey::from_bytes(&private_key).unwrap();
1210        let pub_key = key.public_key();
1211        let mut off_curve = [0u8; 57];
1212        off_curve.copy_from_slice(&pub_key.to_bytes());
1213        let signature = key.sign(b"sample").unwrap();
1214
1215        assert!(pub_key.verify(b"tampered", &signature).is_err());
1216
1217        let mut bad_signature = signature;
1218        bad_signature[10] ^= 0x80;
1219        assert!(pub_key.verify(b"sample", &bad_signature).is_err());
1220
1221        off_curve[56] ^= 0x01;
1222        assert!(!is_valid_public_key(&off_curve));
1223        assert!(PublicKey::from_bytes(&off_curve).is_err());
1224    }
1225
1226    #[test]
1227    fn invalid_inputs_are_rejected() {
1228        let invalid_private_key = [0u8; SECRET_KEY_SIZE];
1229        assert!(SecretKey::from_bytes(&invalid_private_key).is_err());
1230        assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
1231        assert!(derive_public_key_compressed(&invalid_private_key).is_err());
1232
1233        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1234        let key = SecretKey::from_bytes(&private_key).unwrap();
1235        let signature = key.sign(b"msg").unwrap();
1236        let mut zero_r = signature;
1237        zero_r[..28].fill(0);
1238        assert!(key.public_key().verify(b"msg", &zero_r).is_err());
1239    }
1240
1241    #[test]
1242    fn public_key_validation_accepts_known_good_points() {
1243        assert!(is_valid_public_key(&decode_hex::<57>(
1244            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
1245             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
1246        )));
1247    }
1248
1249    #[test]
1250    fn scalar_from_bytes_rejects_boundary_values() {
1251        assert!(Scalar::from_bytes(&[0u8; 28]).is_none());
1252
1253        // n is rejected (must be strictly less than n)
1254        let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
1255        assert!(Scalar::from_bytes(&n_bytes).is_none());
1256
1257        // n-1 is accepted
1258        let n_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c");
1259        assert!(Scalar::from_bytes(&n_minus_1).is_some());
1260
1261        let one = decode_hex::<28>("00000000000000000000000000000000000000000000000000000001");
1262        assert!(Scalar::from_bytes(&one).is_some());
1263    }
1264
1265    #[test]
1266    fn field_element_from_bytes_rejects_boundary_values() {
1267        // p is rejected (must be strictly less than p)
1268        let p_bytes = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000001");
1269        assert!(FieldElement::from_bytes(&p_bytes).is_none());
1270
1271        // p-1 is accepted
1272        let p_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000000");
1273        assert!(FieldElement::from_bytes(&p_minus_1).is_some());
1274
1275        assert!(FieldElement::from_bytes(&[0u8; 28]).is_some());
1276    }
1277
1278    #[test]
1279    fn point_decompression_round_trip() {
1280        let keys: &[&str] = &[
1281            "00000000000000000000000000000000000000000000000000000001",
1282            "00000000000000000000000000000000000000000000000000000002",
1283            RFC6979_PRIVATE_KEY,
1284            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7",
1285        ];
1286
1287        for key_hex in keys {
1288            let private_key = decode_hex::<28>(key_hex);
1289            let key = SecretKey::from_bytes(&private_key).unwrap();
1290            let uncompressed = key.public_key();
1291            let compressed = derive_public_key_compressed(&private_key).unwrap();
1292
1293            let sig = key.sign(b"round-trip").unwrap();
1294            assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
1295            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1296            assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
1297
1298            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1299            assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
1300        }
1301    }
1302
1303    #[test]
1304    fn compressed_public_key_has_correct_prefix() {
1305        for _ in 0..5 {
1306            let key = SecretKey::generate().unwrap();
1307            let compressed = key.public_key().to_compressed_bytes();
1308            let prefix = compressed[0];
1309            assert!(prefix == 0x02 || prefix == 0x03, "invalid compressed prefix: {prefix:#x}");
1310
1311            // Prefix must encode the parity of y.
1312            let (_, y) = key.public_key().x_y();
1313            let expected_prefix = if y[27] & 1 == 1 { 0x03 } else { 0x02 };
1314            assert_eq!(prefix, expected_prefix);
1315
1316            // Public export matches the internal derivation helper.
1317            assert_eq!(compressed, derive_public_key_compressed(&key.to_bytes()).unwrap());
1318        }
1319    }
1320
1321    #[test]
1322    fn public_key_compressed_round_trip() {
1323        for _ in 0..5 {
1324            let key = SecretKey::generate().unwrap();
1325            let pub_key = key.public_key();
1326            let compressed = pub_key.to_compressed_bytes();
1327            assert_eq!(compressed.len(), PUBLIC_KEY_COMPRESSED_SIZE);
1328
1329            let decoded = PublicKey::from_bytes(&compressed).unwrap();
1330            assert_eq!(decoded, pub_key);
1331            assert_eq!(decoded.to_bytes(), pub_key.to_bytes());
1332        }
1333    }
1334
1335    #[test]
1336    fn sqrt_matches_squares_and_rejects_non_residues() {
1337        // Squares always have a root that squares back to the input.
1338        for _ in 0..200 {
1339            let bytes: [u8; 28] = rand::random();
1340            let Some(x) = FieldElement::from_bytes(&bytes) else {
1341                continue;
1342            };
1343            let square = x.square();
1344            let root = square.sqrt().expect("square should have a square root");
1345            assert_eq!(root.square(), square);
1346        }
1347
1348        // 11 is the smallest quadratic non-residue modulo p.
1349        let non_residue = FieldElement(U224::from_u64(11));
1350        assert!(non_residue.sqrt().is_none());
1351    }
1352
1353    #[test]
1354    fn scalar_inversion_correctness() {
1355        let k =
1356            Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
1357        let k_inv = k.invert().unwrap();
1358        assert_eq!(k.mul(k_inv), Scalar::ONE);
1359    }
1360
1361    #[test]
1362    fn field_element_inversion_correctness() {
1363        let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1364            .unwrap();
1365        let x_inv = x.invert().unwrap();
1366        assert_eq!(x.mul(x_inv), FieldElement::ONE);
1367    }
1368
1369    #[test]
1370    fn generator_point_is_on_curve() {
1371        assert!(AffinePoint::GENERATOR.is_on_curve());
1372    }
1373
1374    #[test]
1375    fn barrett_mul_matches_generic() {
1376        // Verify the Barrett-based field and scalar multiplications match the
1377        // generic (bit-serial) Uint::mul_mod.
1378        for _ in 0..1000 {
1379            let a_bytes: [u8; 28] = rand::random();
1380            let b_bytes: [u8; 28] = rand::random();
1381            let (Some(a), Some(b)) = (FieldElement::from_bytes(&a_bytes), FieldElement::from_bytes(&b_bytes)) else {
1382                continue;
1383            };
1384            assert_eq!(a.mul(b).0, a.0.mul_mod(&b.0, &MODULUS_P), "field mul mismatch");
1385
1386            let (Some(c), Some(d)) = (Scalar::from_bytes(&a_bytes), Scalar::from_bytes(&b_bytes)) else {
1387                continue;
1388            };
1389            assert_eq!(c.mul(d).0, c.0.mul_mod(&d.0, &MODULUS_N), "scalar mul mismatch");
1390        }
1391    }
1392
1393    #[test]
1394    fn scalar_mul_generator_n_gives_identity() {
1395        // (n-1)*G = -G, so the x coordinate matches and y is negated.
1396        let n_minus_1 =
1397            Scalar::from_bytes(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c")).unwrap();
1398        let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1399        assert_eq!(result.x, GENERATOR_X);
1400        assert_eq!(result.y, GENERATOR_Y.negate());
1401    }
1402
1403    #[test]
1404    fn ecdh_deterministic_vector_against_generator() {
1405        // ECDH between the RFC 6979 private key and the generator: the shared
1406        // secret is the x-coordinate of the derived public key.
1407        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1408        let generator = decode_hex::<57>(
1409            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
1410             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
1411        );
1412
1413        let key = SecretKey::from_bytes(&private_key).unwrap();
1414        let shared = ecdh(&private_key, &generator).unwrap();
1415        assert_eq!(shared, key.public_key().x_y().0);
1416        assert_eq!(shared, decode_hex::<28>(RFC6979_PUBLIC_X));
1417    }
1418
1419    #[test]
1420    fn ecdh_with_compressed_public_key() {
1421        let alice = SecretKey::generate().unwrap();
1422        let bob = SecretKey::generate().unwrap();
1423        let bob_compressed = derive_public_key_compressed(&bob.to_bytes()).unwrap();
1424
1425        assert!(is_valid_public_key(&bob_compressed));
1426        let shared = alice.ecdh(&PublicKey::from_bytes(&bob_compressed).unwrap()).unwrap();
1427        let expected = bob.ecdh(&alice.public_key()).unwrap();
1428        assert_eq!(shared, expected);
1429    }
1430
1431    #[test]
1432    fn ecdh_round_trip_alice_bob() {
1433        let alice = SecretKey::generate().unwrap();
1434        let bob = SecretKey::generate().unwrap();
1435
1436        let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
1437        let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
1438
1439        assert_eq!(alice_shared, bob_shared);
1440        assert_eq!(alice_shared.len(), ECDH_SHARED_SECRET_SIZE);
1441    }
1442
1443    #[test]
1444    fn ecdh_rejects_off_curve_peer_public_key() {
1445        let alice = SecretKey::generate().unwrap();
1446        let mut bad_pub = alice.public_key().to_bytes().to_vec();
1447        bad_pub[56] ^= 0x01;
1448        assert!(!is_valid_public_key(&bad_pub));
1449        assert!(ecdh(&alice.to_bytes(), &bad_pub).is_err());
1450    }
1451
1452    #[test]
1453    fn ecdh_rejects_infinity_peer_public_key() {
1454        let alice = SecretKey::generate().unwrap();
1455        assert!(ecdh(&alice.to_bytes(), &[0x00u8]).is_err());
1456    }
1457
1458    #[test]
1459    fn ecdh_rejects_bad_length_peer_public_key() {
1460        let alice = SecretKey::generate().unwrap();
1461        assert!(ecdh(&alice.to_bytes(), &[]).is_err());
1462        assert!(ecdh(&alice.to_bytes(), &[0x04, 0x00]).is_err());
1463        let long = [0x04u8; 200];
1464        assert!(ecdh(&alice.to_bytes(), &long).is_err());
1465    }
1466
1467    #[test]
1468    fn ecdh_rejects_invalid_private_key_zero_and_order() {
1469        let zero_key = [0u8; 28];
1470        assert!(SecretKey::from_bytes(&zero_key).is_err());
1471        let bob = SecretKey::generate().unwrap();
1472        assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1473
1474        let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
1475        assert!(SecretKey::from_bytes(&n_bytes).is_err());
1476    }
1477
1478    #[test]
1479    fn ecdh_multiple_exchanges_consistency() {
1480        let alice = SecretKey::generate().unwrap();
1481        let bob = SecretKey::generate().unwrap();
1482        let charlie = SecretKey::generate().unwrap();
1483
1484        let alice_bob = alice.ecdh(&bob.public_key()).unwrap();
1485        let bob_alice = bob.ecdh(&alice.public_key()).unwrap();
1486        assert_eq!(alice_bob, bob_alice);
1487
1488        let alice_charlie = alice.ecdh(&charlie.public_key()).unwrap();
1489        let charlie_alice = charlie.ecdh(&alice.public_key()).unwrap();
1490        assert_eq!(alice_charlie, charlie_alice);
1491
1492        assert_ne!(alice_bob, alice_charlie);
1493    }
1494
1495    #[test]
1496    fn ecdh_standalone_function_matches_method() {
1497        let alice = SecretKey::generate().unwrap();
1498        let bob = SecretKey::generate().unwrap();
1499
1500        let method_result = alice.ecdh(&bob.public_key()).unwrap();
1501        let standalone_result = ecdh(&alice.to_bytes(), &bob.public_key().to_bytes()).unwrap();
1502        assert_eq!(method_result, standalone_result);
1503    }
1504
1505    #[test]
1506    fn ecdsa_sign_verify_round_trip_multiple_messages() {
1507        let key = SecretKey::generate().unwrap();
1508        let pub_key = key.public_key();
1509
1510        let messages: &[&[u8]] = &[
1511            b"",
1512            b"hello world",
1513            b"The quick brown fox jumps over the lazy dog",
1514            &[0xffu8; 100],
1515            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
1516        ];
1517
1518        for msg in messages {
1519            let sig = key.sign(msg).unwrap();
1520            assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {msg:?}");
1521            let mut wrong_msg = msg.to_vec();
1522            wrong_msg.push(0x42);
1523            assert!(pub_key.verify(&wrong_msg, &sig).is_err());
1524        }
1525    }
1526
1527    #[test]
1528    fn ecdsa_verify_wrong_public_key_rejects() {
1529        let key1 = SecretKey::generate().unwrap();
1530        let key2 = SecretKey::generate().unwrap();
1531
1532        let sig = key1.sign(b"message").unwrap();
1533        assert!(key2.public_key().verify(b"message", &sig).is_err());
1534    }
1535
1536    #[test]
1537    fn ecdsa_rejects_non_canonical_r_and_s() {
1538        let key = SecretKey::generate().unwrap();
1539        let mut bad_r = key.sign(b"msg").unwrap();
1540        bad_r[..28].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1541        assert!(key.public_key().verify(b"msg", &bad_r).is_err());
1542
1543        let mut bad_s = key.sign(b"msg").unwrap();
1544        bad_s[28..].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1545        assert!(key.public_key().verify(b"msg", &bad_s).is_err());
1546    }
1547
1548    #[test]
1549    fn from_x_y_matches_generator() {
1550        let key = PublicKey::from_x_y(
1551            &decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"),
1552            &decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"),
1553        )
1554        .unwrap();
1555        let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1556        assert_eq!(key, from_sec1);
1557    }
1558
1559    #[test]
1560    fn from_x_y_rejects_off_curve() {
1561        assert!(PublicKey::from_x_y(&[0u8; 28], &[0u8; 28]).is_err());
1562    }
1563
1564    #[test]
1565    fn private_key_round_trip_bytes() {
1566        let key = SecretKey::generate().unwrap();
1567        let bytes = key.to_bytes();
1568        let key2 = SecretKey::from_bytes(&bytes).unwrap();
1569        assert_eq!(key.to_bytes(), key2.to_bytes());
1570        assert_eq!(key.public_key().to_bytes(), key2.public_key().to_bytes());
1571    }
1572
1573    #[test]
1574    fn public_key_round_trip_bytes() {
1575        let key = SecretKey::generate().unwrap();
1576        let pub_key = key.public_key();
1577        let pub_key2 = PublicKey::from_bytes(&pub_key.to_bytes()).unwrap();
1578        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1579    }
1580
1581    #[test]
1582    fn x_y_round_trip() {
1583        let key = SecretKey::generate().unwrap();
1584        let pub_key = key.public_key();
1585        let (x, y) = pub_key.x_y();
1586        let pub_key2 = PublicKey::from_x_y(&x, &y).unwrap();
1587        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1588    }
1589
1590    #[test]
1591    fn field_element_add_sub_mul_consistency() {
1592        let a = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1593            .unwrap();
1594        let b = FieldElement::from_bytes(&decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"))
1595            .unwrap();
1596
1597        assert_eq!(a.add(b).sub(b), a);
1598        assert_eq!(a.add(b), b.add(a));
1599        assert_eq!(a.mul(b), b.mul(a));
1600
1601        let c = FieldElement::from_bytes(&decode_hex::<28>("270b39432355ffb45044b0b7d7bfd8ba0c04b3abf5413256b4050a85"))
1602            .unwrap();
1603        assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
1604    }
1605
1606    #[test]
1607    fn scalar_add_sub_mul_consistency() {
1608        let a =
1609            Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
1610        let one =
1611            Scalar::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")).unwrap();
1612
1613        assert_eq!(a.add(one).sub(one), a);
1614        assert_eq!(a.mul(one), a);
1615
1616        let b =
1617            Scalar::from_bytes(&decode_hex::<28>("178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd")).unwrap();
1618        assert_eq!(a.mul(b), b.mul(a));
1619        assert_eq!(a.add(b), b.add(a));
1620    }
1621
1622    #[test]
1623    fn field_element_negate_round_trip() {
1624        let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1625            .unwrap();
1626        let neg = x.negate();
1627        assert_eq!(neg.negate(), x);
1628        assert_eq!(x.add(neg), FieldElement::ZERO);
1629    }
1630
1631    #[test]
1632    fn point_double_and_add_consistency() {
1633        let g = AffinePoint::GENERATOR;
1634        let proj_g = ProjectivePoint::from_affine(&g);
1635        assert_eq!(
1636            proj_g.double().to_affine().unwrap().to_uncompressed_bytes(),
1637            proj_g.add(&proj_g).to_affine().unwrap().to_uncompressed_bytes(),
1638        );
1639    }
1640
1641    #[test]
1642    fn field_element_pow_correctness() {
1643        let x = FieldElement::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000002"))
1644            .unwrap();
1645        assert_eq!(x.pow(&U224::from_u64(3)), x.mul(x).mul(x));
1646        assert_eq!(x.pow(&U224::ZERO), FieldElement::ONE);
1647    }
1648
1649    #[test]
1650    fn wycheproof_ecdsa_p224_sha256_p1363() {
1651        let data: serde_json::Value = serde_json::from_str(include_str!(
1652            "../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_p1363_test.json"
1653        ))
1654        .unwrap();
1655        let mut valid_tested = 0u64;
1656        let mut invalid_tested = 0u64;
1657        for group in data["testGroups"].as_array().unwrap() {
1658            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1659            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1660            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1661
1662            for test in group["tests"].as_array().unwrap() {
1663                let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1664                let sig_hex = test["sig"].as_str().unwrap();
1665                let result = test["result"].as_str().unwrap();
1666
1667                if sig_hex.len() != SIGNATURE_SIZE * 2 {
1668                    continue;
1669                }
1670                let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1671                let verify_result = pk.verify(&msg, &sig);
1672
1673                if result == "valid" {
1674                    assert!(verify_result.is_ok(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1675                    valid_tested += 1;
1676                } else {
1677                    assert!(verify_result.is_err(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1678                    invalid_tested += 1;
1679                }
1680            }
1681        }
1682        assert!(valid_tested > 0, "no valid ECDSA P1363 wycheproof tests were run");
1683        assert!(invalid_tested > 0, "no invalid ECDSA P1363 wycheproof tests were run");
1684    }
1685
1686    #[test]
1687    fn wycheproof_ecdsa_p224_sha256_der() {
1688        let data: serde_json::Value = serde_json::from_str(include_str!(
1689            "../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_test.json"
1690        ))
1691        .unwrap();
1692        let mut valid_tested = 0u64;
1693        let mut invalid_tested = 0u64;
1694        for group in data["testGroups"].as_array().unwrap() {
1695            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1696            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1697            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1698
1699            for test in group["tests"].as_array().unwrap() {
1700                let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1701                let der_sig = hex::decode(test["sig"].as_str().unwrap()).unwrap();
1702                let result = test["result"].as_str().unwrap();
1703
1704                let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
1705                    continue;
1706                };
1707
1708                let verify_result = pk.verify(&msg, &sig);
1709                if result == "valid" {
1710                    assert!(
1711                        verify_result.is_ok(),
1712                        "wycheproof ECDSA DER tcId={} expected valid but failed",
1713                        test["tcId"]
1714                    );
1715                    valid_tested += 1;
1716                } else {
1717                    assert!(
1718                        verify_result.is_err(),
1719                        "wycheproof ECDSA DER tcId={} expected invalid but passed",
1720                        test["tcId"]
1721                    );
1722                    invalid_tested += 1;
1723                }
1724            }
1725        }
1726        assert!(valid_tested > 0, "no valid ECDSA DER wycheproof tests were run");
1727        assert!(invalid_tested > 0, "no invalid ECDSA DER wycheproof tests were run");
1728    }
1729
1730    fn wycheproof_ecdh_case(file: &str, asn: bool) {
1731        let data: serde_json::Value = serde_json::from_str(file).unwrap();
1732        let mut valid_tested = 0u64;
1733        let mut invalid_tested = 0u64;
1734        for group in data["testGroups"].as_array().unwrap() {
1735            for test in group["tests"].as_array().unwrap() {
1736                let public_hex = test["public"].as_str().unwrap();
1737                let private_hex = test["private"].as_str().unwrap();
1738                let expected_shared_hex = test["shared"].as_str().unwrap();
1739                let result = test["result"].as_str().unwrap();
1740
1741                let public_bytes = hex::decode(public_hex).unwrap();
1742                let public_key = if asn {
1743                    match spki_to_sec1_point(&public_bytes) {
1744                        Some(point) => point,
1745                        None => {
1746                            if result == "valid" {
1747                                panic!("wycheproof ECDH ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
1748                            }
1749                            invalid_tested += 1;
1750                            continue;
1751                        }
1752                    }
1753                } else {
1754                    public_bytes
1755                };
1756
1757                // Private key hex is a bigint and may be shorter than 28 bytes.
1758                let private_bytes = hex::decode(private_hex).unwrap();
1759                let mut private_key = [0u8; SECRET_KEY_SIZE];
1760                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
1761                let skip = private_bytes.len().saturating_sub(SECRET_KEY_SIZE);
1762                private_key[SECRET_KEY_SIZE - effective_len..]
1763                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1764
1765                let shared = ecdh(&private_key, &public_key);
1766
1767                if result == "valid" {
1768                    let shared = shared.unwrap();
1769                    assert_eq!(
1770                        hex::encode(shared),
1771                        expected_shared_hex,
1772                        "wycheproof ECDH tcId={}",
1773                        test["tcId"]
1774                    );
1775                    valid_tested += 1;
1776                } else if result == "invalid" {
1777                    assert!(
1778                        shared.is_err(),
1779                        "wycheproof ECDH tcId={} expected invalid but passed",
1780                        test["tcId"]
1781                    );
1782                    invalid_tested += 1;
1783                }
1784            }
1785        }
1786        assert!(valid_tested > 0, "no valid ECDH wycheproof tests were run");
1787        assert!(invalid_tested > 0, "no invalid ECDH wycheproof tests were run");
1788    }
1789
1790    #[test]
1791    fn wycheproof_ecdh_p224_ecpoint() {
1792        wycheproof_ecdh_case(
1793            include_str!("../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_ecpoint_test.json"),
1794            false,
1795        );
1796    }
1797
1798    #[test]
1799    fn wycheproof_ecdh_p224_asn() {
1800        wycheproof_ecdh_case(
1801            include_str!("../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_test.json"),
1802            true,
1803        );
1804    }
1805}