Skip to main content

crypto/p_curves/
p224.rs

1//! P-224 (secp224r1) ECDSA and ECDH.
2//!
3//! Messages are hashed with SHA-256, truncated to the leftmost 224 bits as
4//! specified by FIPS 186-4. See [`SecretKey`] and [`PublicKey`] for the
5//! signing, verification and key agreement APIs.
6
7use big_number::Uint;
8
9use super::p_curves::{self, Curve, UintOps, field_pow};
10use crate::{EllipticCurveError, Hasher, RandomError, hmac::Hmac, sha2::Sha256};
11
12/// Size of a P-224 secret key in bytes (28 bytes).
13pub const SECRET_KEY_SIZE: usize = 28;
14/// Size of a compressed P-224 public key in bytes (29 bytes, includes 0x02/0x03 prefix).
15pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 29;
16/// Size of an uncompressed P-224 public key in bytes (57 bytes, includes 0x04 prefix).
17pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 57;
18/// Size of a P-224 ECDSA signature in bytes (56 bytes, r || s).
19pub const SIGNATURE_SIZE: usize = 56;
20/// Size of the raw ECDH shared secret in bytes (28 bytes). **Must not** be used directly
21/// as an encryption key; apply a KDF first.
22pub const ECDH_SHARED_SECRET_SIZE: usize = 28;
23
24/// P-224 (secp224r1) ECDSA secret key.
25///
26/// Supports signing and ECDH key agreement. Messages are hashed with SHA-256,
27/// truncated to the leftmost 224 bits as specified by FIPS 186-4.
28///
29/// # Signing
30///
31/// ```ignore
32/// use crypto::p224::SecretKey;
33///
34/// let key = SecretKey::generate().unwrap();
35/// let signature = key.sign(b"message").unwrap();
36/// ```
37///
38/// # ECDH key exchange
39///
40/// ```ignore
41/// use crypto::p224::SecretKey;
42///
43/// let alice = SecretKey::generate().unwrap();
44/// let bob = SecretKey::generate().unwrap();
45/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
46/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
47/// assert_eq!(alice_shared, bob_shared);
48/// ```
49///
50/// # Security
51///
52/// The raw shared secret from [`ecdh`](Self::ecdh) **must not** be used
53/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
54#[derive(Clone, PartialEq, Eq)]
55#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
56pub struct SecretKey(p_curves::SecretKey<P224>);
57
58impl SecretKey {
59    /// Generates a fresh random secret key.
60    #[cfg(feature = "random")]
61    pub fn generate() -> Result<SecretKey, EllipticCurveError> {
62        Ok(SecretKey(p_curves::SecretKey::generate()?))
63    }
64
65    /// Builds a secret key from its 28-byte big-endian scalar.
66    ///
67    /// Returns [`EllipticCurveError::InvalidKey`] when the scalar is zero or
68    /// greater than or equal to the group order.
69    pub fn from_bytes(key: &[u8; SECRET_KEY_SIZE]) -> Result<SecretKey, EllipticCurveError> {
70        Ok(SecretKey(p_curves::SecretKey::from_bytes(key)?))
71    }
72
73    /// Returns the corresponding public key.
74    pub fn public_key(&self) -> PublicKey {
75        PublicKey(self.0.public_key())
76    }
77
78    /// Signs `message` with ECDSA using SHA-256 (truncated to the leftmost 224
79    /// bits) and deterministic (RFC 6979) nonces.
80    ///
81    /// The emitted signature is always in canonical low-s form (`s <= n / 2`),
82    /// so a given key and message always produce the exact same signature
83    /// bytes. This prevents signature malleability for systems that hash the
84    /// signature bytes (e.g. transaction ids, replay caches).
85    ///
86    /// Verification remains permissive by default (both `s` and `n - s` are
87    /// accepted); use [`PublicKey::verify_strict`] if high-s signatures must be
88    /// rejected.
89    pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
90        self.0.sign(message)
91    }
92
93    /// Computes the ECDH shared secret with `peer_public`.
94    ///
95    /// # Security
96    ///
97    /// The raw shared secret **must not** be used directly as an encryption
98    /// key. Apply a KDF (e.g. HKDF) first.
99    pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
100        self.0.ecdh(&peer_public.0)
101    }
102
103    /// Returns the secret scalar as 28 big-endian bytes.
104    pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
105        self.0.to_bytes()
106    }
107}
108
109/// P-224 (secp224r1) ECDSA public key.
110///
111/// Supports signature verification and ECDH key agreement. Both compressed
112/// (29-byte) and uncompressed (57-byte) SEC1 encodings are accepted on input;
113/// use [`to_compressed_bytes`](Self::to_compressed_bytes) to export compressed.
114///
115/// # Verification
116///
117/// ```ignore
118/// use crypto::p224::SecretKey;
119///
120/// let key = SecretKey::generate().unwrap();
121/// let signature = key.sign(b"message").unwrap();
122/// assert!(key.public_key().verify(b"message", &signature).is_ok());
123/// ```
124#[derive(Clone, Copy, Debug, PartialEq, Eq)]
125pub struct PublicKey(p_curves::PublicKey<P224>);
126
127impl PublicKey {
128    /// Parses a compressed (29-byte) or uncompressed (57-byte) SEC1 encoding.
129    ///
130    /// Returns [`EllipticCurveError::InvalidKey`] when the encoding is not a
131    /// valid point on the P-224 curve.
132    #[inline]
133    pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
134        Ok(PublicKey(p_curves::PublicKey::from_bytes(key)?))
135    }
136
137    /// Builds a public key from raw affine x and y coordinates (both
138    /// big-endian, 28 bytes each). Returns `InvalidKey` if the coordinates
139    /// are not a valid point on the P-224 curve.
140    ///
141    /// This is useful when importing keys from formats like JWK where `x`
142    /// and `y` are available directly.
143    #[inline]
144    pub fn from_x_y(x_bytes: &[u8; 28], y_bytes: &[u8; 28]) -> Result<PublicKey, EllipticCurveError> {
145        Ok(PublicKey(p_curves::PublicKey::from_x_y(x_bytes, y_bytes)?))
146    }
147
148    /// Verifies an ECDSA signature over `message` using SHA-256 (truncated to
149    /// the leftmost 224 bits).
150    ///
151    /// Both canonical low-s and non-canonical high-s signatures are accepted,
152    /// matching typical ECDSA interoperability. Use [`Self::verify_strict`] to
153    /// additionally reject high-s signatures.
154    pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
155        self.0.verify(message, signature)
156    }
157
158    /// Verifies an ECDSA signature over `message` using SHA-256, additionally
159    /// rejecting non-canonical high-s signatures (`s > n / 2`).
160    ///
161    /// Use this when signature malleability must be excluded (Bitcoin/EIP-2
162    /// style). Note that some third-party signers emit high-s signatures, which
163    /// this method will reject; [`Self::verify`] accepts both forms.
164    pub fn verify_strict(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
165        self.0.verify_strict(message, signature)
166    }
167
168    /// Returns the 57-byte uncompressed SEC1 encoding (`0x04 || x || y`).
169    #[inline]
170    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
171        self.0.to_bytes()
172    }
173
174    /// Returns the 29-byte compressed SEC1 encoding (`0x02`/`0x03 || x`).
175    #[inline]
176    pub fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
177        self.0.to_compressed_bytes()
178    }
179
180    /// Returns the `X` and `Y` points as big-endian arrays.
181    #[inline]
182    pub fn x_y(&self) -> ([u8; 28], [u8; 28]) {
183        self.0.x_y()
184    }
185}
186
187/// Marker type carrying the P-224 curve parameters.
188#[derive(Clone, Copy, Debug, PartialEq, Eq)]
189struct P224;
190
191type U224 = Uint<224, 4>;
192type CurveUint = U224;
193
194const MODULUS_P: CurveUint = CurveUint::from_limbs([
195    0x0000_0000_0000_0001,
196    0xffff_ffff_0000_0000,
197    0xffff_ffff_ffff_ffff,
198    0x0000_0000_ffff_ffff,
199]);
200
201const MODULUS_N: CurveUint = CurveUint::from_limbs([
202    0x13dd_2945_5c5c_2a3d,
203    0xffff_16a2_e0b8_f03e,
204    0xffff_ffff_ffff_ffff,
205    0x0000_0000_ffff_ffff,
206]);
207
208// floor(n / 2). A signature scalar `s` is "high" when `s > N_HALF`, in which
209// case it is normalized to `n - s` so that signatures are non-malleable.
210const N_HALF: CurveUint = CurveUint::from_limbs([
211    0x09ee_94a2_ae2e_151e,
212    0xffff_8b51_705c_781f,
213    0xffff_ffff_ffff_ffff,
214    0x0000_0000_7fff_ffff,
215]);
216
217const P_MINUS_TWO: CurveUint = CurveUint::from_limbs([
218    0xffff_ffff_ffff_ffff,
219    0xffff_fffe_ffff_ffff,
220    0xffff_ffff_ffff_ffff,
221    0x0000_0000_ffff_ffff,
222]);
223
224const N_MINUS_TWO: CurveUint = CurveUint::from_limbs([
225    0x13dd_2945_5c5c_2a3b,
226    0xffff_16a2_e0b8_f03e,
227    0xffff_ffff_ffff_ffff,
228    0x0000_0000_ffff_ffff,
229]);
230
231// P-224 has p = 2^224 - 2^96 + 1, so p - 1 = 2^S * Q with S = 96 and
232// Q = 2^128 - 1 odd. These constants drive the Tonelli-Shanks square-root
233// algorithm (see `p224_field_sqrt`).
234const TONELLI_S: usize = 96;
235// (Q - 1) / 2.
236const TONELLI_Q_MINUS_ONE_OVER_TWO: CurveUint = CurveUint::from_limbs([
237    0xffff_ffff_ffff_ffff,
238    0x7fff_ffff_ffff_ffff,
239    0x0000_0000_0000_0000,
240    0x0000_0000_0000_0000,
241]);
242// Generator of the order-2^S subgroup: `11^Q`, where 11 is the smallest
243// quadratic non-residue modulo p (verified: 11^((p-1)/2) = -1). This is the
244// fixed `z` used by the constant-time Tonelli-Shanks variant.
245const TONELLI_ROOT_OF_UNITY: CurveUint = CurveUint::from_limbs([
246    0xf3fb_3632_dc69_1b74,
247    0x0b2d_6ffb_bea3_d8ce,
248    0x8598_a792_0c55_b2d4,
249    0x0000_0000_6a0f_ec67,
250]);
251
252// Barrett reduction constants: mu = floor(2^(2 * 4 * 64) / modulus), as
253// computed by big_number's `compute_mu_for_barrett`.
254const P_MU: [u64; 5] = [
255    0x0000_0000_ffff_ffff,
256    0xffff_ffff_ffff_ffff,
257    0x0000_0000_ffff_ffff,
258    0x0000_0000_0000_0000,
259    0x0000_0001_0000_0000,
260];
261
262const N_MU: [u64; 5] = [
263    0xd4ba_a4cf_1822_bc47,
264    0xec22_d6ba_a3a3_d5c3,
265    0x0000_e95d_1f47_0fc1,
266    0x0000_0000_0000_0000,
267    0x0000_0001_0000_0000,
268];
269
270const CURVE_B: CurveUint = CurveUint::from_limbs([
271    0x270b_3943_2355_ffb4,
272    0x5044_b0b7_d7bf_d8ba,
273    0x0c04_b3ab_f541_3256,
274    0x0000_0000_b405_0a85,
275]);
276
277const GENERATOR_X: CurveUint = CurveUint::from_limbs([
278    0x3432_80d6_115c_1d21,
279    0x4a03_c1d3_56c2_1122,
280    0x6bb4_bf7f_3213_90b9,
281    0x0000_0000_b70e_0cbd,
282]);
283
284const GENERATOR_Y: CurveUint = CurveUint::from_limbs([
285    0x44d5_8199_8500_7e34,
286    0xcd43_75a0_5a07_4764,
287    0xb5f7_23fb_4c22_dfe6,
288    0x0000_0000_bd37_6388,
289]);
290
291// Reduce a big-endian value modulo n, accepting zero and values >= n.
292#[inline]
293fn reduce_mod(value: CurveUint) -> CurveUint {
294    let (sub_value, _) = value.sub_raw(&MODULUS_N);
295    CurveUint::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N))
296}
297
298/// Square root modulo p. Returns `None` when `a` is a quadratic non-residue.
299///
300/// P-224 has p ≡ 1 (mod 4), so the simple `a^((p+1)/4)` formula used by
301/// P-256/P-384 is unavailable. Since `p - 1 = 2^96 * Q`, this uses the
302/// constant-time Tonelli-Shanks variant from "Square root computation over even
303/// extension fields" (eprint 2012/685, Algorithm 5): every loop has a fixed
304/// iteration count and all state transitions are constant-time selects, so the
305/// running time does not depend on `a`. (The returned `Option` still reveals
306/// whether `a` is a quadratic residue, as the signature implies.)
307fn p224_field_sqrt(a: CurveUint) -> Option<CurveUint> {
308    // w = a^((Q-1)/2); then x = a*w and b = x*w = a^Q.
309    let w = field_pow::<P224>(a, &TONELLI_Q_MINUS_ONE_OVER_TWO);
310    let mut v = TONELLI_S;
311    let mut x = curve_field_mul(&w, &a);
312    let mut b = curve_field_mul(&x, &w);
313
314    // z starts as the generator of the order-2^S subgroup.
315    let mut z = TONELLI_ROOT_OF_UNITY;
316
317    for max_v in (1..=TONELLI_S).rev() {
318        let mut k = 1usize;
319        let mut b2k = curve_field_mul(&b, &b);
320        let mut j_less_than_v = true;
321
322        for j in 2..max_v {
323            let b2k_is_one = b2k.ct_eq(&CurveUint::ONE);
324            // Square `z` when `b^(2^k) == 1`, otherwise keep squaring `b2k`.
325            let squared = curve_field_mul(
326                &CurveUint::ct_select(&z, &b2k, b2k_is_one),
327                &CurveUint::ct_select(&z, &b2k, b2k_is_one),
328            );
329            b2k = CurveUint::ct_select(&b2k, &squared, b2k_is_one);
330            let new_z = CurveUint::ct_select(&squared, &z, b2k_is_one);
331            j_less_than_v &= j != v;
332            k = ct_select_usize(k, j, b2k_is_one);
333            z = CurveUint::ct_select(&new_z, &z, j_less_than_v);
334        }
335
336        let result = curve_field_mul(&x, &z);
337        x = CurveUint::ct_select(&x, &result, b.ct_eq(&CurveUint::ONE));
338        z = curve_field_mul(&z, &z);
339        b = curve_field_mul(&b, &z);
340        v = k;
341    }
342
343    if curve_field_mul(&x, &x).ct_eq(&a) {
344        Some(x)
345    } else {
346        None
347    }
348}
349
350/// Branch-free select for `usize` indices: returns `a` if `choice` else `b`.
351#[inline]
352fn ct_select_usize(a: usize, b: usize, choice: bool) -> usize {
353    let mask = (choice as usize).wrapping_neg();
354    (a & mask) | (b & !mask)
355}
356
357#[inline]
358fn curve_field_mul(a: &CurveUint, b: &CurveUint) -> CurveUint {
359    a.mul_mod_barrett_w::<10>(b, &MODULUS_P, &P_MU)
360}
361
362impl Curve for P224 {
363    type U = CurveUint;
364    type FieldBytes = [u8; SECRET_KEY_SIZE];
365    type DigestBytes = [u8; 32];
366    type CompressedBytes = [u8; PUBLIC_KEY_COMPRESSED_SIZE];
367    type UncompressedBytes = [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
368    type SignatureBytes = [u8; SIGNATURE_SIZE];
369
370    const FIELD_BITS: usize = 224;
371    const FIELD_BYTES: usize = SECRET_KEY_SIZE;
372    const DIGEST_BYTES: usize = 32;
373    const COMPRESSED_BYTES: usize = PUBLIC_KEY_COMPRESSED_SIZE;
374    const UNCOMPRESSED_BYTES: usize = PUBLIC_KEY_UNCOMPRESSED_SIZE;
375
376    const MODULUS_P: CurveUint = MODULUS_P;
377    const MODULUS_N: CurveUint = MODULUS_N;
378    const N_HALF: CurveUint = N_HALF;
379    const P_MINUS_TWO: CurveUint = P_MINUS_TWO;
380    const N_MINUS_TWO: CurveUint = N_MINUS_TWO;
381    const CURVE_B: CurveUint = CURVE_B;
382    const GENERATOR_X: CurveUint = GENERATOR_X;
383    const GENERATOR_Y: CurveUint = GENERATOR_Y;
384
385    #[inline]
386    fn field_mul(a: &Self::U, b: &Self::U) -> Self::U {
387        a.mul_mod_barrett_w::<10>(b, &MODULUS_P, &P_MU)
388    }
389
390    #[inline]
391    fn scalar_mul(a: &Self::U, b: &Self::U) -> Self::U {
392        a.mul_mod_barrett_w::<10>(b, &MODULUS_N, &N_MU)
393    }
394
395    #[inline]
396    fn field_sqrt(a: Self::U) -> Option<Self::U> {
397        p224_field_sqrt(a)
398    }
399
400    #[inline]
401    fn hash(data: &[u8]) -> Self::DigestBytes {
402        Sha256::hash(data).as_ref().try_into().unwrap()
403    }
404
405    #[inline]
406    fn hmac(key: &[u8], data: &[u8]) -> Self::DigestBytes {
407        Hmac::<Sha256>::mac(key, data).as_ref().try_into().unwrap()
408    }
409
410    /// Reduce a SHA-256 digest to a scalar. FIPS 186-4 truncates the digest to
411    /// the leftmost 224 bits (the first 28 bytes) before reduction.
412    #[inline]
413    fn scalar_from_digest(digest: &Self::DigestBytes) -> Self::U {
414        let truncated: [u8; SECRET_KEY_SIZE] = digest.as_ref()[..SECRET_KEY_SIZE].try_into().unwrap();
415        reduce_mod(CurveUint::read_be(&truncated))
416    }
417
418    #[inline]
419    fn scalar_from_field_bytes(bytes: &Self::FieldBytes) -> Self::U {
420        reduce_mod(CurveUint::read_be(bytes.as_ref()))
421    }
422
423    #[cfg(feature = "random")]
424    #[inline]
425    fn random_secret_key() -> Result<Self::FieldBytes, RandomError> {
426        crate::random::bytes()
427    }
428}
429
430/// Returns `true` when `public_key` is a valid SEC1 encoding of a point on the
431/// P-224 curve.
432pub fn is_valid_public_key(public_key: &[u8]) -> bool {
433    p_curves::is_valid_public_key::<P224>(public_key)
434}
435
436#[cfg(test)]
437mod tests {
438    #![allow(dead_code)]
439    use super::*;
440    use crate::p_curves::p_curves;
441
442    type FieldElement = p_curves::FieldElement<P224>;
443    type Scalar = p_curves::Scalar<P224>;
444    type AffinePoint = p_curves::AffinePoint<P224>;
445    type ProjectivePoint = p_curves::ProjectivePoint<P224>;
446    type Rfc6979 = p_curves::Rfc6979<P224>;
447
448    // Concrete wrappers around the generic core helpers.
449    fn hash_message(message: &[u8]) -> [u8; 32] {
450        p_curves::hash_message::<P224>(message)
451    }
452
453    fn hmac_digest(key: &[u8], data: &[u8]) -> [u8; 32] {
454        p_curves::hmac_digest::<P224>(key, data)
455    }
456
457    fn bits2octets(hash: &[u8; 32]) -> [u8; SECRET_KEY_SIZE] {
458        p_curves::bits2octets::<P224>(hash)
459    }
460
461    fn derive_public_key_uncompressed(
462        private_key: &[u8; SECRET_KEY_SIZE],
463    ) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], crate::EllipticCurveError> {
464        p_curves::derive_public_key_uncompressed::<P224>(private_key)
465    }
466
467    fn derive_public_key_compressed(
468        private_key: &[u8; SECRET_KEY_SIZE],
469    ) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], crate::EllipticCurveError> {
470        p_curves::derive_public_key_compressed::<P224>(private_key)
471    }
472
473    fn ecdsa_sign_inner_impl(
474        scalar: &Scalar,
475        message: &[u8],
476        force_first_retry: bool,
477    ) -> Result<[u8; SIGNATURE_SIZE], crate::EllipticCurveError> {
478        p_curves::ecdsa_sign_inner_impl::<P224>(scalar, message, force_first_retry)
479    }
480
481    fn ecdsa_verify_inner(
482        public_point: &AffinePoint,
483        message: &[u8],
484        signature: &[u8; SIGNATURE_SIZE],
485    ) -> Result<(), crate::EllipticCurveError> {
486        p_curves::ecdsa_verify_inner::<P224>(public_point, message, signature)
487    }
488
489    fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, crate::EllipticCurveError> {
490        p_curves::parse_public_key::<P224>(public_key)
491    }
492
493    fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
494        p_curves::scalar_mul_generator::<P224>(scalar)
495    }
496
497    fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
498        p_curves::scalar_mul_affine::<P224>(base, scalar)
499    }
500
501    // Raw-byte ECDH built from the public method, for the vector tests.
502    fn ecdh(
503        secret_key: &[u8; SECRET_KEY_SIZE],
504        peer_public_key: &[u8],
505    ) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], crate::EllipticCurveError> {
506        SecretKey::from_bytes(secret_key)?.ecdh(&PublicKey::from_bytes(peer_public_key)?)
507    }
508
509    #[cfg(feature = "zeroize")]
510    #[test]
511    fn secret_key_zeroize_clears_scalar() {
512        use zeroize::Zeroize;
513
514        let mut key = SecretKey::from_bytes(&[1u8; SECRET_KEY_SIZE]).unwrap();
515        assert_ne!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
516        key.zeroize();
517        assert_eq!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
518    }
519
520    fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
521        let bytes = hex::decode(hex_bytes).unwrap();
522        assert_eq!(bytes.len(), N);
523        let mut out = [0u8; N];
524        out.copy_from_slice(&bytes);
525        out
526    }
527
528    // Read a DER TLV (tag-length-value) item.
529    fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
530        if *offset >= data.len() {
531            return None;
532        }
533        let tag = data[*offset];
534        *offset += 1;
535        if *offset >= data.len() {
536            return None;
537        }
538        let len_byte = data[*offset];
539        *offset += 1;
540        let (len, _) = if len_byte & 0x80 != 0 {
541            let num_bytes = (len_byte & 0x7f) as usize;
542            if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
543                return None;
544            }
545            if num_bytes > 1 && data[*offset] == 0 {
546                return None;
547            }
548            let mut l = 0usize;
549            for i in 0..num_bytes {
550                l = (l << 8) | data[*offset + i] as usize;
551            }
552            if l < 128 {
553                return None;
554            }
555            *offset += num_bytes;
556            (l, num_bytes + 1)
557        } else {
558            (len_byte as usize, 1)
559        };
560        if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
561            return None;
562        }
563        let value = &data[*offset..*offset + len];
564        *offset = (*offset).checked_add(len)?;
565        Some((tag, value))
566    }
567
568    // Convert a DER-encoded ECDSA signature (SEQUENCE { INTEGER r, INTEGER s })
569    // to P1363 format (r || s, each 28 bytes).
570    fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 56]> {
571        let mut offset = 0;
572        let (tag, inner) = der_read_tlv(der, &mut offset)?;
573        if tag != 0x30 {
574            return None;
575        }
576        if offset != der.len() {
577            return None;
578        }
579        let mut inner_offset = 0;
580        let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
581        if rtag != 0x02 || rval.is_empty() || rval.len() > 29 {
582            return None;
583        }
584        let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
585        if stag != 0x02 || sval.is_empty() || sval.len() > 29 {
586            return None;
587        }
588        if inner_offset != inner.len() {
589            return None;
590        }
591        let r_valid = if rval.len() == 28 && rval[0] >= 0x80 {
592            false
593        } else if rval.len() == 29 && rval[0] != 0 {
594            false
595        } else if rval.len() == 29 && rval[0] == 0 && rval[1] < 0x80 {
596            false
597        } else {
598            rval.len() <= 29
599        };
600        let s_valid = if sval.len() == 28 && sval[0] >= 0x80 {
601            false
602        } else if sval.len() == 29 && sval[0] != 0 {
603            false
604        } else if sval.len() == 29 && sval[0] == 0 && sval[1] < 0x80 {
605            false
606        } else {
607            sval.len() <= 29
608        };
609        if !r_valid || !s_valid {
610            return None;
611        }
612
613        let r_trimmed = if rval.len() == 29 && rval[0] == 0 {
614            &rval[1..]
615        } else {
616            rval
617        };
618        let s_trimmed = if sval.len() == 29 && sval[0] == 0 {
619            &sval[1..]
620        } else {
621            sval
622        };
623        if r_trimmed.len() > 28 || s_trimmed.len() > 28 {
624            return None;
625        }
626        let mut sig = [0u8; 56];
627        sig[28 - r_trimmed.len()..28].copy_from_slice(r_trimmed);
628        sig[56 - s_trimmed.len()..56].copy_from_slice(s_trimmed);
629        Some(sig)
630    }
631
632    // Extract the raw SEC1 point from a DER SubjectPublicKeyInfo using the
633    // named secp224r1 curve (explicit parameters are rejected).
634    fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
635        let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
636        let secp224r1_oid: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x21];
637        let mut offset = 0;
638        let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
639        let mut inner = 0;
640        let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
641        if _alg_tag != 0x30 {
642            return None;
643        }
644        let mut ai = 0;
645        let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
646        if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
647            return None;
648        }
649        let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
650        if oid2_tag != 0x06 || oid2 != secp224r1_oid {
651            return None;
652        }
653        let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
654        if _bs_tag != 0x03 || bs_val.is_empty() {
655            return None;
656        }
657        Some(bs_val[1..].to_vec())
658    }
659
660    // RFC 6979 A.2.4: P-224 test key pair.
661    const RFC6979_PRIVATE_KEY: &str = "f220266e1105bfe3083e03ec7a3a654651f45e37167e88600bf257c1";
662    const RFC6979_PUBLIC_X: &str = "00cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c";
663    const RFC6979_PUBLIC_Y: &str = "eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a";
664
665    #[test]
666    fn derive_public_key_generator_matches_sec1_base_point() {
667        let mut private_key = [0u8; SECRET_KEY_SIZE];
668        private_key[27] = 1;
669        let derived = derive_public_key_uncompressed(&private_key).unwrap();
670        let expected = decode_hex::<57>(
671            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
672             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
673        );
674        assert_eq!(derived, expected);
675    }
676
677    #[test]
678    fn derive_public_key_matches_rfc6979_vector() {
679        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
680        let expected = decode_hex::<57>(
681            "0400cf08da5ad719e42707fa431292dea11244d64fc51610d94b130d6c\
682             eeab6f3debe455e3dbf85416f7030cbd94f34f2d6f232c69f3c1385a",
683        );
684        assert_eq!(derive_public_key_uncompressed(&private_key).unwrap(), expected);
685        assert_eq!(
686            PublicKey::from_x_y(&decode_hex::<28>(RFC6979_PUBLIC_X), &decode_hex::<28>(RFC6979_PUBLIC_Y),)
687                .unwrap()
688                .to_bytes(),
689            expected,
690        );
691
692        // Compressed form derives from the same point; prefix encodes y parity.
693        let compressed = derive_public_key_compressed(&private_key).unwrap();
694        assert_eq!(&compressed[1..], &decode_hex::<28>(RFC6979_PUBLIC_X));
695    }
696
697    #[test]
698    fn ecdsa_sign_matches_rfc6979_vectors() {
699        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
700        let key = SecretKey::from_bytes(&private_key).unwrap();
701
702        // The raw RFC 6979 "sample" signature has high s (bc81…0101); the
703        // expected value below is its canonical low-s form (n - s).
704        let sample_signature = key.sign(b"sample").unwrap();
705        let expected_sample = decode_hex::<56>(
706            "61aa3da010e8e8406c656bc477a7a7189895e7e840cdfe8ff42307ba\
707             437ebfaf254a2dc88f786b6b061e7022049df927fa4b6b5ec9ab293c",
708        );
709        assert_eq!(sample_signature, expected_sample);
710
711        let test_signature = key.sign(b"test").unwrap();
712        let expected_test = decode_hex::<56>(
713            "ad04dde87b84747a243a631ea47a1ba6d1faa059149ad2440de6fba6\
714             178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd",
715        );
716        assert_eq!(test_signature, expected_test);
717    }
718
719    #[test]
720    fn ecdsa_sign_emits_canonical_low_s() {
721        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
722        let key = SecretKey::from_bytes(&private_key).unwrap();
723        for msg in [&b"sample"[..], b"test", b"", b"another message"] {
724            let sig = key.sign(msg).unwrap();
725            let s = Scalar::from_bytes(sig[28..].try_into().unwrap()).unwrap();
726            assert!(!s.is_high(), "sign produced a high-s signature for {:?}", msg);
727        }
728    }
729
730    #[test]
731    fn ecdsa_verify_strict_rejects_high_s() {
732        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
733        let key = SecretKey::from_bytes(&private_key).unwrap();
734        let public_key = key.public_key();
735        let signature = key.sign(b"sample").unwrap();
736
737        // Canonical low-s signature passes both permissive and strict verification.
738        assert!(public_key.verify(b"sample", &signature).is_ok());
739        assert!(public_key.verify_strict(b"sample", &signature).is_ok());
740
741        // The malleable counterpart (r, n - s) is rejected only by strict verify.
742        let r = Scalar::from_bytes(signature[..28].try_into().unwrap()).unwrap();
743        let s = Scalar::from_bytes(signature[28..].try_into().unwrap()).unwrap();
744        let high_s = Scalar::ZERO.sub(s);
745        assert!(high_s.is_high());
746
747        let mut malleable = [0u8; SIGNATURE_SIZE];
748        malleable[..28].copy_from_slice(&r.to_bytes());
749        malleable[28..].copy_from_slice(&high_s.to_bytes());
750
751        assert!(public_key.verify(b"sample", &malleable).is_ok());
752        assert!(public_key.verify_strict(b"sample", &malleable).is_err());
753    }
754
755    #[test]
756    fn rfc6979_nonce_generation_matches_known_value() {
757        let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
758        let hash = hash_message(b"sample");
759        assert_eq!(
760            Rfc6979::new(&private_key, &hash).generate().to_bytes(),
761            decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")
762        );
763    }
764
765    #[test]
766    fn rfc6979_test_message_nonce_matches_known_value() {
767        let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
768        let hash = hash_message(b"test");
769        assert_eq!(
770            Rfc6979::new(&private_key, &hash).generate().to_bytes(),
771            decode_hex::<28>("ff86f57924da248d6e44e8154eb69f0ae2aebaee9931d0b5a969f904")
772        );
773    }
774
775    #[test]
776    fn rfc6979_retry_advances_drbg() {
777        let private_key = Scalar::from_bytes(&decode_hex::<28>(RFC6979_PRIVATE_KEY)).unwrap();
778        let hash = hash_message(b"sample");
779
780        let mut drbg = Rfc6979::new(&private_key, &hash);
781        let k1 = drbg.generate();
782        assert_eq!(
783            k1.to_bytes(),
784            decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")
785        );
786
787        // RFC 6979 §3.2 h.3: an r=0/s=0 retry must continue the DRBG, not
788        // reproduce the same nonce.
789        drbg.retry();
790        let k2 = drbg.generate();
791        assert_ne!(k1.to_bytes(), k2.to_bytes());
792        assert_eq!(
793            k2.to_bytes(),
794            decode_hex::<28>("e651536d86136a3b2a48606e067796dd9b8586698a271d594aeb0255")
795        );
796    }
797
798    #[test]
799    fn ecdsa_forced_retry_uses_fresh_nonce() {
800        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
801        let key = SecretKey::from_bytes(&private_key).unwrap();
802        let scalar = Scalar::from_bytes(&private_key).unwrap();
803
804        let normal = key.sign(b"sample").unwrap();
805        // Force the r=0/s=0 retry path. The signature must be produced with the
806        // next DRBG nonce; repeating the first nonce would yield `normal`.
807        let retried = ecdsa_sign_inner_impl(&scalar, b"sample", true).unwrap();
808
809        assert_ne!(normal, retried);
810        assert!(key.public_key().verify(b"sample", &retried).is_ok());
811    }
812
813    #[test]
814    fn rfc6979_bits2octets_truncates_and_reduces() {
815        // SHA-256("sample") truncated to the leftmost 224 bits.
816        let hash = hash_message(b"sample");
817        let expected = decode_hex::<28>("af2bdbe1aa9b6ec1e2ade1d694f41fc71a831d0268e9891562113d8a");
818        assert_eq!(bits2octets(&hash), expected);
819
820        // A value exactly equal to n maps to zero; n+1 maps to one.
821        // n in the top 224 bits maps to zero; n+1 maps to one.
822        let n_times = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d00000000");
823        assert_eq!(bits2octets(&n_times), [0u8; 28]);
824        let n_plus_one = decode_hex::<32>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e00000000");
825        assert_eq!(
826            bits2octets(&n_plus_one),
827            decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")
828        );
829    }
830
831    #[test]
832    fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
833        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
834        let key = SecretKey::from_bytes(&private_key).unwrap();
835        let uncompressed = key.public_key();
836        let compressed = derive_public_key_compressed(&private_key).unwrap();
837        let signature = key.sign(b"sample").unwrap();
838
839        assert!(uncompressed.verify(b"sample", &signature).is_ok());
840        let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
841        assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
842    }
843
844    #[test]
845    fn verify_rejects_tampering_and_invalid_points() {
846        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
847        let key = SecretKey::from_bytes(&private_key).unwrap();
848        let pub_key = key.public_key();
849        let mut off_curve = [0u8; 57];
850        off_curve.copy_from_slice(&pub_key.to_bytes());
851        let signature = key.sign(b"sample").unwrap();
852
853        assert!(pub_key.verify(b"tampered", &signature).is_err());
854
855        let mut bad_signature = signature;
856        bad_signature[10] ^= 0x80;
857        assert!(pub_key.verify(b"sample", &bad_signature).is_err());
858
859        off_curve[56] ^= 0x01;
860        assert!(!is_valid_public_key(&off_curve));
861        assert!(PublicKey::from_bytes(&off_curve).is_err());
862    }
863
864    #[test]
865    fn invalid_inputs_are_rejected() {
866        let invalid_private_key = [0u8; SECRET_KEY_SIZE];
867        assert!(SecretKey::from_bytes(&invalid_private_key).is_err());
868        assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
869        assert!(derive_public_key_compressed(&invalid_private_key).is_err());
870
871        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
872        let key = SecretKey::from_bytes(&private_key).unwrap();
873        let signature = key.sign(b"msg").unwrap();
874        let mut zero_r = signature;
875        zero_r[..28].fill(0);
876        assert!(key.public_key().verify(b"msg", &zero_r).is_err());
877    }
878
879    #[test]
880    fn public_key_validation_accepts_known_good_points() {
881        assert!(is_valid_public_key(&decode_hex::<57>(
882            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
883             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"
884        )));
885    }
886
887    #[test]
888    fn scalar_from_bytes_rejects_boundary_values() {
889        assert!(Scalar::from_bytes(&[0u8; 28]).is_none());
890
891        // n is rejected (must be strictly less than n)
892        let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
893        assert!(Scalar::from_bytes(&n_bytes).is_none());
894
895        // n-1 is accepted
896        let n_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c");
897        assert!(Scalar::from_bytes(&n_minus_1).is_some());
898
899        let one = decode_hex::<28>("00000000000000000000000000000000000000000000000000000001");
900        assert!(Scalar::from_bytes(&one).is_some());
901    }
902
903    #[test]
904    fn field_element_from_bytes_rejects_boundary_values() {
905        // p is rejected (must be strictly less than p)
906        let p_bytes = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000001");
907        assert!(FieldElement::from_bytes(&p_bytes).is_none());
908
909        // p-1 is accepted
910        let p_minus_1 = decode_hex::<28>("ffffffffffffffffffffffffffffffff000000000000000000000000");
911        assert!(FieldElement::from_bytes(&p_minus_1).is_some());
912
913        assert!(FieldElement::from_bytes(&[0u8; 28]).is_some());
914    }
915
916    #[test]
917    fn point_decompression_round_trip() {
918        let keys: &[&str] = &[
919            "00000000000000000000000000000000000000000000000000000001",
920            "00000000000000000000000000000000000000000000000000000002",
921            RFC6979_PRIVATE_KEY,
922            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7",
923        ];
924
925        for key_hex in keys {
926            let private_key = decode_hex::<28>(key_hex);
927            let key = SecretKey::from_bytes(&private_key).unwrap();
928            let uncompressed = key.public_key();
929            let compressed = derive_public_key_compressed(&private_key).unwrap();
930
931            let sig = key.sign(b"round-trip").unwrap();
932            assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
933            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
934            assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
935
936            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
937            assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
938        }
939    }
940
941    #[test]
942    fn public_key_compressed_round_trip() {
943        for _ in 0..5 {
944            let key = SecretKey::generate().unwrap();
945            let pub_key = key.public_key();
946            let compressed = pub_key.to_compressed_bytes();
947            assert_eq!(compressed.len(), PUBLIC_KEY_COMPRESSED_SIZE);
948
949            let decoded = PublicKey::from_bytes(&compressed).unwrap();
950            assert_eq!(decoded, pub_key);
951            assert_eq!(decoded.to_bytes(), pub_key.to_bytes());
952        }
953    }
954
955    #[test]
956    fn sqrt_matches_squares_and_rejects_non_residues() {
957        // Squares always have a root that squares back to the input, and that
958        // root is one of the two square roots (±x).
959        for _ in 0..500 {
960            let bytes: [u8; 28] = rand::random();
961            let Some(x) = FieldElement::from_bytes(&bytes) else {
962                continue;
963            };
964            let square = x.square();
965            let root = square.sqrt().expect("square should have a square root");
966            assert_eq!(root.square(), square);
967            assert!(root == x || root == x.negate());
968
969            // The computation is deterministic.
970            assert_eq!(root, square.sqrt().unwrap());
971        }
972
973        // Zero is its own square root.
974        let zero = FieldElement::from_uint(U224::from_u64(0));
975        assert_eq!(zero.sqrt().unwrap(), zero);
976
977        // 11 is the smallest quadratic non-residue modulo p.
978        let non_residue = FieldElement::from_uint(U224::from_u64(11));
979        assert!(non_residue.sqrt().is_none());
980    }
981
982    #[test]
983    fn scalar_inversion_correctness() {
984        let k =
985            Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
986        let k_inv = k.invert().unwrap();
987        assert_eq!(k.mul(k_inv), Scalar::ONE);
988    }
989
990    #[test]
991    fn field_element_inversion_correctness() {
992        let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
993            .unwrap();
994        let x_inv = x.invert().unwrap();
995        assert_eq!(x.mul(x_inv), FieldElement::ONE);
996    }
997
998    #[test]
999    fn barrett_mul_matches_generic() {
1000        // Verify the Barrett-based field and scalar multiplications match the
1001        // generic (bit-serial) Uint::mul_mod.
1002        for _ in 0..1000 {
1003            let a_bytes: [u8; 28] = rand::random();
1004            let b_bytes: [u8; 28] = rand::random();
1005            let (Some(a), Some(b)) = (FieldElement::from_bytes(&a_bytes), FieldElement::from_bytes(&b_bytes)) else {
1006                continue;
1007            };
1008            assert_eq!(a.mul(b).0, a.0.mul_mod(&b.0, &MODULUS_P), "field mul mismatch");
1009
1010            let (Some(c), Some(d)) = (Scalar::from_bytes(&a_bytes), Scalar::from_bytes(&b_bytes)) else {
1011                continue;
1012            };
1013            assert_eq!(c.mul(d).0, c.0.mul_mod(&d.0, &MODULUS_N), "scalar mul mismatch");
1014        }
1015    }
1016
1017    #[test]
1018    fn scalar_mul_generator_n_gives_identity() {
1019        // (n-1)*G = -G, so the x coordinate matches and y is negated.
1020        let n_minus_1 =
1021            Scalar::from_bytes(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3c")).unwrap();
1022        let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1023        assert_eq!(result.x, FieldElement::from_uint(GENERATOR_X));
1024        assert_eq!(result.y, FieldElement::from_uint(GENERATOR_Y).negate());
1025    }
1026
1027    #[test]
1028    fn ecdh_deterministic_vector_against_generator() {
1029        // ECDH between the RFC 6979 private key and the generator: the shared
1030        // secret is the x-coordinate of the derived public key.
1031        let private_key = decode_hex::<28>(RFC6979_PRIVATE_KEY);
1032        let generator = decode_hex::<57>(
1033            "04b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21\
1034             bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34",
1035        );
1036
1037        let key = SecretKey::from_bytes(&private_key).unwrap();
1038        let shared = ecdh(&private_key, &generator).unwrap();
1039        assert_eq!(shared, key.public_key().x_y().0);
1040        assert_eq!(shared, decode_hex::<28>(RFC6979_PUBLIC_X));
1041    }
1042
1043    #[test]
1044    fn ecdh_with_compressed_public_key() {
1045        let alice = SecretKey::generate().unwrap();
1046        let bob = SecretKey::generate().unwrap();
1047        let bob_compressed = derive_public_key_compressed(&bob.to_bytes()).unwrap();
1048
1049        assert!(is_valid_public_key(&bob_compressed));
1050        let shared = alice.ecdh(&PublicKey::from_bytes(&bob_compressed).unwrap()).unwrap();
1051        let expected = bob.ecdh(&alice.public_key()).unwrap();
1052        assert_eq!(shared, expected);
1053    }
1054
1055    #[test]
1056    fn ecdh_rejects_invalid_private_key_zero_and_order() {
1057        let zero_key = [0u8; 28];
1058        assert!(SecretKey::from_bytes(&zero_key).is_err());
1059        let bob = SecretKey::generate().unwrap();
1060        assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1061
1062        let n_bytes = decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d");
1063        assert!(SecretKey::from_bytes(&n_bytes).is_err());
1064    }
1065
1066    #[test]
1067    fn ecdh_standalone_function_matches_method() {
1068        let alice = SecretKey::generate().unwrap();
1069        let bob = SecretKey::generate().unwrap();
1070
1071        let method_result = alice.ecdh(&bob.public_key()).unwrap();
1072        let standalone_result = ecdh(&alice.to_bytes(), &bob.public_key().to_bytes()).unwrap();
1073        assert_eq!(method_result, standalone_result);
1074    }
1075
1076    #[test]
1077    fn ecdsa_sign_verify_round_trip_multiple_messages() {
1078        let key = SecretKey::generate().unwrap();
1079        let pub_key = key.public_key();
1080
1081        let messages: &[&[u8]] = &[
1082            b"",
1083            b"hello world",
1084            b"The quick brown fox jumps over the lazy dog",
1085            &[0xffu8; 100],
1086            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
1087        ];
1088
1089        for msg in messages {
1090            let sig = key.sign(msg).unwrap();
1091            assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {msg:?}");
1092            let mut wrong_msg = msg.to_vec();
1093            wrong_msg.push(0x42);
1094            assert!(pub_key.verify(&wrong_msg, &sig).is_err());
1095        }
1096    }
1097
1098    #[test]
1099    fn ecdsa_verify_wrong_public_key_rejects() {
1100        let key1 = SecretKey::generate().unwrap();
1101        let key2 = SecretKey::generate().unwrap();
1102
1103        let sig = key1.sign(b"message").unwrap();
1104        assert!(key2.public_key().verify(b"message", &sig).is_err());
1105    }
1106
1107    #[test]
1108    fn ecdsa_rejects_non_canonical_r_and_s() {
1109        let key = SecretKey::generate().unwrap();
1110        let mut bad_r = key.sign(b"msg").unwrap();
1111        bad_r[..28].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1112        assert!(key.public_key().verify(b"msg", &bad_r).is_err());
1113
1114        let mut bad_s = key.sign(b"msg").unwrap();
1115        bad_s[28..].copy_from_slice(&decode_hex::<28>("ffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3e"));
1116        assert!(key.public_key().verify(b"msg", &bad_s).is_err());
1117    }
1118
1119    #[test]
1120    fn from_x_y_matches_generator() {
1121        let key = PublicKey::from_x_y(
1122            &decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"),
1123            &decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"),
1124        )
1125        .unwrap();
1126        let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1127        assert_eq!(key, from_sec1);
1128    }
1129
1130    #[test]
1131    fn x_y_round_trip() {
1132        let key = SecretKey::generate().unwrap();
1133        let pub_key = key.public_key();
1134        let (x, y) = pub_key.x_y();
1135        let pub_key2 = PublicKey::from_x_y(&x, &y).unwrap();
1136        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1137    }
1138
1139    #[test]
1140    fn field_element_add_sub_mul_consistency() {
1141        let a = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1142            .unwrap();
1143        let b = FieldElement::from_bytes(&decode_hex::<28>("bd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34"))
1144            .unwrap();
1145
1146        assert_eq!(a.add(b).sub(b), a);
1147        assert_eq!(a.add(b), b.add(a));
1148        assert_eq!(a.mul(b), b.mul(a));
1149
1150        let c = FieldElement::from_bytes(&decode_hex::<28>("270b39432355ffb45044b0b7d7bfd8ba0c04b3abf5413256b4050a85"))
1151            .unwrap();
1152        assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
1153    }
1154
1155    #[test]
1156    fn scalar_add_sub_mul_consistency() {
1157        let a =
1158            Scalar::from_bytes(&decode_hex::<28>("ad3029e0278f80643de33917ce6908c70a8ff50a411f06e41dedfcdc")).unwrap();
1159        let one =
1160            Scalar::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000001")).unwrap();
1161
1162        assert_eq!(a.add(one).sub(one), a);
1163        assert_eq!(a.mul(one), a);
1164
1165        let b =
1166            Scalar::from_bytes(&decode_hex::<28>("178d49b1ae90e3d8b629be3db5683915f4e8c99fdf6e666cf37adcfd")).unwrap();
1167        assert_eq!(a.mul(b), b.mul(a));
1168        assert_eq!(a.add(b), b.add(a));
1169    }
1170
1171    #[test]
1172    fn field_element_negate_round_trip() {
1173        let x = FieldElement::from_bytes(&decode_hex::<28>("b70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21"))
1174            .unwrap();
1175        let neg = x.negate();
1176        assert_eq!(neg.negate(), x);
1177        assert_eq!(x.add(neg), FieldElement::ZERO);
1178    }
1179
1180    #[test]
1181    fn field_element_pow_correctness() {
1182        let x = FieldElement::from_bytes(&decode_hex::<28>("00000000000000000000000000000000000000000000000000000002"))
1183            .unwrap();
1184        assert_eq!(x.pow(&U224::from_u64(3)), x.mul(x).mul(x));
1185        assert_eq!(x.pow(&U224::ZERO), FieldElement::ONE);
1186    }
1187
1188    #[test]
1189    fn wycheproof_ecdsa_p224_sha256_p1363() {
1190        let data: serde_json::Value = serde_json::from_str(include_str!(
1191            "../../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_p1363_test.json"
1192        ))
1193        .unwrap();
1194        let mut valid_tested = 0u64;
1195        let mut invalid_tested = 0u64;
1196        for group in data["testGroups"].as_array().unwrap() {
1197            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1198            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1199            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1200
1201            for test in group["tests"].as_array().unwrap() {
1202                let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1203                let sig_hex = test["sig"].as_str().unwrap();
1204                let result = test["result"].as_str().unwrap();
1205
1206                if sig_hex.len() != SIGNATURE_SIZE * 2 {
1207                    continue;
1208                }
1209                let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1210                let verify_result = pk.verify(&msg, &sig);
1211
1212                if result == "valid" {
1213                    assert!(verify_result.is_ok(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1214                    valid_tested += 1;
1215                } else {
1216                    assert!(verify_result.is_err(), "wycheproof ECDSA P1363 tcId={}", test["tcId"]);
1217                    invalid_tested += 1;
1218                }
1219            }
1220        }
1221        assert!(valid_tested > 0, "no valid ECDSA P1363 wycheproof tests were run");
1222        assert!(invalid_tested > 0, "no invalid ECDSA P1363 wycheproof tests were run");
1223    }
1224
1225    #[test]
1226    fn wycheproof_ecdsa_p224_sha256_der() {
1227        let data: serde_json::Value = serde_json::from_str(include_str!(
1228            "../../testdata/wycheproof/testvectors_v1/ecdsa_secp224r1_sha256_test.json"
1229        ))
1230        .unwrap();
1231        let mut valid_tested = 0u64;
1232        let mut invalid_tested = 0u64;
1233        for group in data["testGroups"].as_array().unwrap() {
1234            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1235            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1236            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1237
1238            for test in group["tests"].as_array().unwrap() {
1239                let msg = hex::decode(test["msg"].as_str().unwrap()).unwrap();
1240                let der_sig = hex::decode(test["sig"].as_str().unwrap()).unwrap();
1241                let result = test["result"].as_str().unwrap();
1242
1243                let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
1244                    continue;
1245                };
1246
1247                let verify_result = pk.verify(&msg, &sig);
1248                if result == "valid" {
1249                    assert!(
1250                        verify_result.is_ok(),
1251                        "wycheproof ECDSA DER tcId={} expected valid but failed",
1252                        test["tcId"]
1253                    );
1254                    valid_tested += 1;
1255                } else {
1256                    assert!(
1257                        verify_result.is_err(),
1258                        "wycheproof ECDSA DER tcId={} expected invalid but passed",
1259                        test["tcId"]
1260                    );
1261                    invalid_tested += 1;
1262                }
1263            }
1264        }
1265        assert!(valid_tested > 0, "no valid ECDSA DER wycheproof tests were run");
1266        assert!(invalid_tested > 0, "no invalid ECDSA DER wycheproof tests were run");
1267    }
1268
1269    fn wycheproof_ecdh_case(file: &str, asn: bool) {
1270        let data: serde_json::Value = serde_json::from_str(file).unwrap();
1271        let mut valid_tested = 0u64;
1272        let mut invalid_tested = 0u64;
1273        for group in data["testGroups"].as_array().unwrap() {
1274            for test in group["tests"].as_array().unwrap() {
1275                let public_hex = test["public"].as_str().unwrap();
1276                let private_hex = test["private"].as_str().unwrap();
1277                let expected_shared_hex = test["shared"].as_str().unwrap();
1278                let result = test["result"].as_str().unwrap();
1279
1280                let public_bytes = hex::decode(public_hex).unwrap();
1281                let public_key = if asn {
1282                    match spki_to_sec1_point(&public_bytes) {
1283                        Some(point) => point,
1284                        None => {
1285                            if result == "valid" {
1286                                panic!("wycheproof ECDH ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
1287                            }
1288                            invalid_tested += 1;
1289                            continue;
1290                        }
1291                    }
1292                } else {
1293                    public_bytes
1294                };
1295
1296                // Private key hex is a bigint and may be shorter than 28 bytes.
1297                let private_bytes = hex::decode(private_hex).unwrap();
1298                let mut private_key = [0u8; SECRET_KEY_SIZE];
1299                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
1300                let skip = private_bytes.len().saturating_sub(SECRET_KEY_SIZE);
1301                private_key[SECRET_KEY_SIZE - effective_len..]
1302                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1303
1304                let shared = ecdh(&private_key, &public_key);
1305
1306                if result == "valid" {
1307                    let shared = shared.unwrap();
1308                    assert_eq!(
1309                        hex::encode(shared),
1310                        expected_shared_hex,
1311                        "wycheproof ECDH tcId={}",
1312                        test["tcId"]
1313                    );
1314                    valid_tested += 1;
1315                } else if result == "invalid" {
1316                    assert!(
1317                        shared.is_err(),
1318                        "wycheproof ECDH tcId={} expected invalid but passed",
1319                        test["tcId"]
1320                    );
1321                    invalid_tested += 1;
1322                }
1323            }
1324        }
1325        assert!(valid_tested > 0, "no valid ECDH wycheproof tests were run");
1326        assert!(invalid_tested > 0, "no invalid ECDH wycheproof tests were run");
1327    }
1328
1329    #[test]
1330    fn wycheproof_ecdh_p224_ecpoint() {
1331        wycheproof_ecdh_case(
1332            include_str!("../../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_ecpoint_test.json"),
1333            false,
1334        );
1335    }
1336
1337    #[test]
1338    fn wycheproof_ecdh_p224_asn() {
1339        wycheproof_ecdh_case(
1340            include_str!("../../testdata/wycheproof/testvectors_v1/ecdh_secp224r1_test.json"),
1341            true,
1342        );
1343    }
1344
1345    // Shared curve-independent tests, instantiated for P224.
1346    #[test]
1347    fn generator_point_is_on_curve() {
1348        p_curves::test_support::generator_point_is_on_curve::<P224>();
1349    }
1350
1351    #[test]
1352    fn from_x_y_rejects_off_curve() {
1353        p_curves::test_support::from_x_y_rejects_off_curve::<P224>();
1354    }
1355
1356    #[test]
1357    fn ecdh_round_trip_alice_bob() {
1358        p_curves::test_support::ecdh_round_trip_alice_bob::<P224>();
1359    }
1360
1361    #[test]
1362    fn ecdh_rejects_off_curve_peer_public_key() {
1363        p_curves::test_support::ecdh_rejects_off_curve_peer_public_key::<P224>();
1364    }
1365
1366    #[test]
1367    fn ecdh_rejects_infinity_peer_public_key() {
1368        p_curves::test_support::ecdh_rejects_infinity_peer_public_key::<P224>();
1369    }
1370
1371    #[test]
1372    fn ecdh_rejects_bad_length_peer_public_key() {
1373        p_curves::test_support::ecdh_rejects_bad_length_peer_public_key::<P224>();
1374    }
1375
1376    #[test]
1377    fn ecdh_multiple_exchanges_consistency() {
1378        p_curves::test_support::ecdh_multiple_exchanges_consistency::<P224>();
1379    }
1380
1381    #[test]
1382    fn private_key_round_trip_bytes() {
1383        p_curves::test_support::private_key_round_trip_bytes::<P224>();
1384    }
1385
1386    #[test]
1387    fn public_key_round_trip_bytes() {
1388        p_curves::test_support::public_key_round_trip_bytes::<P224>();
1389    }
1390
1391    #[test]
1392    fn point_double_and_add_consistency() {
1393        p_curves::test_support::point_double_and_add_consistency::<P224>();
1394    }
1395
1396    #[test]
1397    fn compressed_public_key_has_correct_prefix() {
1398        p_curves::test_support::compressed_public_key_has_correct_prefix::<P224>();
1399    }
1400
1401    #[cfg(feature = "zeroize")]
1402    #[test]
1403    fn rfc6979_state_zeroize_clears_drbg() {
1404        p_curves::test_support::rfc6979_state_zeroize_clears_drbg::<P224>();
1405    }
1406}