Skip to main content

crypto/p_curves/
p384.rs

1//! P-384 (secp384r1) ECDSA and ECDH.
2//!
3//! See [`SecretKey`] and [`PublicKey`] for the signing, verification and key
4//! agreement APIs.
5
6use big_number::{Uint, mac};
7
8use super::p_curves::{self, Curve, UintOps, field_pow};
9use crate::{EllipticCurveError, Hasher, RandomError, hmac::Hmac, sha2::Sha384};
10
11/// Size of a P-384 secret key in bytes (48 bytes).
12pub const SECRET_KEY_SIZE: usize = 48;
13/// Size of a compressed P-384 public key in bytes (49 bytes, includes 0x02/0x03 prefix).
14pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 49;
15/// Size of an uncompressed P-384 public key in bytes (97 bytes, includes 0x04 prefix).
16pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 97;
17/// Size of a P-384 ECDSA signature in bytes (96 bytes, r || s).
18pub const SIGNATURE_SIZE: usize = 96;
19/// Size of the raw ECDH shared secret in bytes (48 bytes). **Must not** be used directly
20/// as an encryption key; apply a KDF first.
21pub const ECDH_SHARED_SECRET_SIZE: usize = 48;
22
23/// P-384 (secp384r1) ECDSA secret key.
24///
25/// Supports signing and ECDH key agreement.
26///
27/// # Signing
28///
29/// ```ignore
30/// use crypto::p384::SecretKey;
31///
32/// let key = SecretKey::generate().unwrap();
33/// let signature = key.sign(b"message").unwrap();
34/// ```
35///
36/// # ECDH key exchange
37///
38/// ```ignore
39/// use crypto::p384::SecretKey;
40///
41/// let alice = SecretKey::generate().unwrap();
42/// let bob = SecretKey::generate().unwrap();
43/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
44/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
45/// assert_eq!(alice_shared, bob_shared);
46/// ```
47///
48/// # Security
49///
50/// The raw shared secret from [`ecdh`](Self::ecdh) **must not** be used
51/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
52#[derive(Clone, PartialEq, Eq)]
53#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
54pub struct SecretKey(p_curves::SecretKey<P384>);
55
56impl SecretKey {
57    /// Generates a fresh random secret key.
58    #[cfg(feature = "random")]
59    pub fn generate() -> Result<SecretKey, EllipticCurveError> {
60        Ok(SecretKey(p_curves::SecretKey::generate()?))
61    }
62
63    /// Builds a secret key from its 48-byte big-endian scalar.
64    ///
65    /// Returns [`EllipticCurveError::InvalidKey`] when the scalar is zero or
66    /// greater than or equal to the group order.
67    pub fn from_bytes(key: &[u8; SECRET_KEY_SIZE]) -> Result<SecretKey, EllipticCurveError> {
68        Ok(SecretKey(p_curves::SecretKey::from_bytes(key)?))
69    }
70
71    /// Returns the corresponding public key.
72    pub fn public_key(&self) -> PublicKey {
73        PublicKey(self.0.public_key())
74    }
75
76    /// Signs `message` with ECDSA using SHA-384 and deterministic (RFC 6979)
77    /// nonces.
78    ///
79    /// The emitted signature is always in canonical low-s form (`s <= n / 2`),
80    /// so a given key and message always produce the exact same signature
81    /// bytes. This prevents signature malleability for systems that hash the
82    /// signature bytes (e.g. transaction ids, replay caches).
83    ///
84    /// Verification remains permissive by default (both `s` and `n - s` are
85    /// accepted); use [`PublicKey::verify_strict`] if high-s signatures must be
86    /// rejected.
87    pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
88        self.0.sign(message)
89    }
90
91    /// Computes the ECDH shared secret with `peer_public`.
92    ///
93    /// # Security
94    ///
95    /// The raw shared secret **must not** be used directly as an encryption
96    /// key. Apply a KDF (e.g. HKDF) first.
97    pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
98        self.0.ecdh(&peer_public.0)
99    }
100
101    /// Returns the secret scalar as 48 big-endian bytes.
102    pub fn to_bytes(&self) -> [u8; SECRET_KEY_SIZE] {
103        self.0.to_bytes()
104    }
105}
106
107/// P-384 (secp384r1) ECDSA public key.
108///
109/// Supports signature verification and ECDH key agreement.
110///
111/// # Verification
112///
113/// ```ignore
114/// use crypto::p384::SecretKey;
115///
116/// let key = SecretKey::generate().unwrap();
117/// let signature = key.sign(b"message").unwrap();
118/// assert!(key.public_key().verify(b"message", &signature).is_ok());
119/// ```
120#[derive(Clone, Copy, Debug, PartialEq, Eq)]
121pub struct PublicKey(p_curves::PublicKey<P384>);
122
123impl PublicKey {
124    /// Parses a compressed (49-byte) or uncompressed (97-byte) SEC1 encoding.
125    ///
126    /// Returns [`EllipticCurveError::InvalidKey`] when the encoding is not a
127    /// valid point on the P-384 curve.
128    #[inline]
129    pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
130        Ok(PublicKey(p_curves::PublicKey::from_bytes(key)?))
131    }
132
133    /// Builds a public key from raw affine x and y coordinates (both
134    /// big-endian, 48 bytes each). Returns `InvalidKey` if the coordinates
135    /// are not a valid point on the P-384 curve.
136    ///
137    /// This is useful when importing keys from formats like JWK where `x`
138    /// and `y` are available directly.
139    #[inline]
140    pub fn from_x_y(x_bytes: &[u8; 48], y_bytes: &[u8; 48]) -> Result<PublicKey, EllipticCurveError> {
141        Ok(PublicKey(p_curves::PublicKey::from_x_y(x_bytes, y_bytes)?))
142    }
143
144    /// Verifies an ECDSA signature over `message` using SHA-384.
145    ///
146    /// Both canonical low-s and non-canonical high-s signatures are accepted,
147    /// matching typical ECDSA interoperability. Use [`Self::verify_strict`] to
148    /// additionally reject high-s signatures.
149    pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
150        self.0.verify(message, signature)
151    }
152
153    /// Verifies an ECDSA signature over `message` using SHA-384, additionally
154    /// rejecting non-canonical high-s signatures (`s > n / 2`).
155    ///
156    /// Use this when signature malleability must be excluded (Bitcoin/EIP-2
157    /// style). Note that some third-party signers emit high-s signatures, which
158    /// this method will reject; [`Self::verify`] accepts both forms.
159    pub fn verify_strict(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
160        self.0.verify_strict(message, signature)
161    }
162
163    /// Returns the 97-byte uncompressed SEC1 encoding (`0x04 || x || y`).
164    #[inline]
165    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
166        self.0.to_bytes()
167    }
168
169    /// Returns the 49-byte compressed SEC1 encoding (`0x02`/`0x03 || x`).
170    #[inline]
171    pub fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
172        self.0.to_compressed_bytes()
173    }
174
175    /// Returns the `X` and `Y` points as big-endian arrays.
176    #[inline]
177    pub fn x_y(&self) -> ([u8; 48], [u8; 48]) {
178        self.0.x_y()
179    }
180}
181
182/// Marker type carrying the P-384 curve parameters.
183#[derive(Clone, Copy, Debug, PartialEq, Eq)]
184struct P384;
185
186type U384 = Uint<384, 6>;
187type CurveUint = U384;
188
189const MODULUS_P: CurveUint = CurveUint::from_limbs([
190    0x00000000ffffffff,
191    0xffffffff00000000,
192    0xfffffffffffffffe,
193    0xffffffffffffffff,
194    0xffffffffffffffff,
195    0xffffffffffffffff,
196]);
197
198const MODULUS_N: CurveUint = CurveUint::from_limbs([
199    0xecec196accc52973,
200    0x581a0db248b0a77a,
201    0xc7634d81f4372ddf,
202    0xffffffffffffffff,
203    0xffffffffffffffff,
204    0xffffffffffffffff,
205]);
206
207/// Barrett reciprocal `mu = floor(2^(2 * 6 * 64) / n)` for the group order,
208/// computed by `big_number`'s `compute_mu_for_barrett`.
209const N_MU: [u64; 7] = [
210    0x1313e695333ad68d,
211    0xa7e5f24db74f5885,
212    0x389cb27e0bc8d220,
213    0x0000000000000000,
214    0x0000000000000000,
215    0x0000000000000000,
216    0x0000000000000001,
217];
218
219// floor(n / 2). A signature scalar `s` is "high" when `s > N_HALF`, in which
220// case it is normalized to `n - s` so that signatures are non-malleable.
221const N_HALF: CurveUint = CurveUint::from_limbs([
222    0x76760cb5666294b9,
223    0xac0d06d9245853bd,
224    0xe3b1a6c0fa1b96ef,
225    0xffffffffffffffff,
226    0xffffffffffffffff,
227    0x7fffffffffffffff,
228]);
229
230const P_MINUS_TWO: CurveUint = CurveUint::from_limbs([
231    0x00000000fffffffd,
232    0xffffffff00000000,
233    0xfffffffffffffffe,
234    0xffffffffffffffff,
235    0xffffffffffffffff,
236    0xffffffffffffffff,
237]);
238
239const P_PLUS_ONE_OVER_FOUR: CurveUint = CurveUint::from_limbs([
240    0x0000000040000000,
241    0xbfffffffc0000000,
242    0xffffffffffffffff,
243    0xffffffffffffffff,
244    0xffffffffffffffff,
245    0x3fffffffffffffff,
246]);
247
248const N_MINUS_TWO: CurveUint = CurveUint::from_limbs([
249    0xecec196accc52971,
250    0x581a0db248b0a77a,
251    0xc7634d81f4372ddf,
252    0xffffffffffffffff,
253    0xffffffffffffffff,
254    0xffffffffffffffff,
255]);
256
257const CURVE_B: CurveUint = CurveUint::from_limbs([
258    0x2a85c8edd3ec2aef,
259    0xc656398d8a2ed19d,
260    0x0314088f5013875a,
261    0x181d9c6efe814112,
262    0x988e056be3f82d19,
263    0xb3312fa7e23ee7e4,
264]);
265
266const GENERATOR_X: CurveUint = CurveUint::from_limbs([
267    0x3a545e3872760ab7,
268    0x5502f25dbf55296c,
269    0x59f741e082542a38,
270    0x6e1d3b628ba79b98,
271    0x8eb1c71ef320ad74,
272    0xaa87ca22be8b0537,
273]);
274
275const GENERATOR_Y: CurveUint = CurveUint::from_limbs([
276    0x7a431d7c90ea0e5f,
277    0x0a60b1ce1d7e819d,
278    0xe9da3113b5f0b8c0,
279    0xf8f41dbd289a147c,
280    0x5d9e98bf9292dc29,
281    0x3617de4a96262c6f,
282]);
283
284// P-384 fast reduction constants: S_i = 2^(64i) mod p for i=6..11
285// Derived from p = 2^384 - 2^128 - 2^96 + 2^32 - 1
286// Each S_i is a 6-limb U384 value.
287const S6: [u64; 6] = [
288    0xffffffff00000001,
289    0x00000000ffffffff,
290    0x0000000000000001,
291    0x0000000000000000,
292    0x0000000000000000,
293    0x0000000000000000,
294];
295
296const S7: [u64; 6] = [
297    0x0000000000000000,
298    0xffffffff00000001,
299    0x00000000ffffffff,
300    0x0000000000000001,
301    0x0000000000000000,
302    0x0000000000000000,
303];
304
305const S8: [u64; 6] = [
306    0x0000000000000000,
307    0x0000000000000000,
308    0xffffffff00000001,
309    0x00000000ffffffff,
310    0x0000000000000001,
311    0x0000000000000000,
312];
313
314const S9: [u64; 6] = [
315    0x0000000000000000,
316    0x0000000000000000,
317    0x0000000000000000,
318    0xffffffff00000001,
319    0x00000000ffffffff,
320    0x0000000000000001,
321];
322
323const S10: [u64; 6] = [
324    0xffffffff00000001,
325    0x00000000ffffffff,
326    0x0000000000000001,
327    0x0000000000000000,
328    0xffffffff00000001,
329    0x00000000ffffffff,
330];
331
332const S11: [u64; 6] = [
333    0x00000001ffffffff,
334    0xfffffffe00000000,
335    0x00000001ffffffff,
336    0x0000000000000001,
337    0x0000000000000000,
338    0xffffffff00000001,
339];
340
341#[inline]
342fn ct_select_u128(a: u128, b: u128, choice: bool) -> u128 {
343    let mask = (choice as u128).wrapping_neg();
344    (a & mask) | (b & !mask)
345}
346
347// P-384 fast modular multiplication using u128 accumulators.
348// All loops run fixed iteration counts with ct_select for constant-time.
349fn p384_fast_mul_mod(a: &CurveUint, b: &CurveUint) -> CurveUint {
350    let al = a.limbs;
351    let bl = b.limbs;
352
353    let mut prod = [0u64; 12];
354    for i in 0..6 {
355        let mut carry = 0u64;
356        for j in 0..6 {
357            let (v, cc) = mac(prod[i + j], al[i], bl[j], carry);
358            prod[i + j] = v;
359            carry = cc;
360        }
361        prod[i + 6] = carry;
362    }
363
364    const MASK: u128 = 0xffffffffffffffff;
365    let c0 = [
366        S6[0] as u128,
367        S6[1] as u128,
368        S6[2] as u128,
369        S6[3] as u128,
370        S6[4] as u128,
371        S6[5] as u128,
372    ];
373    let c1 = [
374        S7[0] as u128,
375        S7[1] as u128,
376        S7[2] as u128,
377        S7[3] as u128,
378        S7[4] as u128,
379        S7[5] as u128,
380    ];
381    let c2 = [
382        S8[0] as u128,
383        S8[1] as u128,
384        S8[2] as u128,
385        S8[3] as u128,
386        S8[4] as u128,
387        S8[5] as u128,
388    ];
389    let c3 = [
390        S9[0] as u128,
391        S9[1] as u128,
392        S9[2] as u128,
393        S9[3] as u128,
394        S9[4] as u128,
395        S9[5] as u128,
396    ];
397    let c4 = [
398        S10[0] as u128,
399        S10[1] as u128,
400        S10[2] as u128,
401        S10[3] as u128,
402        S10[4] as u128,
403        S10[5] as u128,
404    ];
405    let c5 = [
406        S11[0] as u128,
407        S11[1] as u128,
408        S11[2] as u128,
409        S11[3] as u128,
410        S11[4] as u128,
411        S11[5] as u128,
412    ];
413    let coeffs: [&[u128; 6]; 6] = [&c0, &c1, &c2, &c3, &c4, &c5];
414
415    let mut r0 = prod[0] as u128;
416    let mut r1 = prod[1] as u128;
417    let mut r2 = prod[2] as u128;
418    let mut r3 = prod[3] as u128;
419    let mut r4 = prod[4] as u128;
420    let mut r5 = prod[5] as u128;
421
422    for i in 0..6 {
423        let w = prod[6 + i] as u128;
424        let c = coeffs[i];
425
426        r0 = r0.wrapping_add(w.wrapping_mul(c[0]));
427        r1 = r1.wrapping_add(w.wrapping_mul(c[1]));
428        r2 = r2.wrapping_add(w.wrapping_mul(c[2]));
429        r3 = r3.wrapping_add(w.wrapping_mul(c[3]));
430        r4 = r4.wrapping_add(w.wrapping_mul(c[4]));
431        r5 = r5.wrapping_add(w.wrapping_mul(c[5]));
432
433        // Fixed 4 iterations: carry propagation + conditional residual reduction.
434        for _ in 0..4 {
435            let carry = r0 >> 64;
436            r1 = r1.wrapping_add(carry);
437            r0 &= MASK;
438            let carry = r1 >> 64;
439            r2 = r2.wrapping_add(carry);
440            r1 &= MASK;
441            let carry = r2 >> 64;
442            r3 = r3.wrapping_add(carry);
443            r2 &= MASK;
444            let carry = r3 >> 64;
445            r4 = r4.wrapping_add(carry);
446            r3 &= MASK;
447            let carry = r4 >> 64;
448            r5 = r5.wrapping_add(carry);
449            r4 &= MASK;
450
451            let residual = r5 >> 64;
452            let need_reduce = residual != 0;
453
454            let rr5 = r5 & MASK;
455            let rr0 = r0.wrapping_add(residual.wrapping_mul(c0[0]));
456            let rr1 = r1.wrapping_add(residual.wrapping_mul(c0[1]));
457            let rr2 = r2.wrapping_add(residual.wrapping_mul(c0[2]));
458            let rr3 = r3.wrapping_add(residual.wrapping_mul(c0[3]));
459            let rr4 = r4.wrapping_add(residual.wrapping_mul(c0[4]));
460            let rr5r = rr5.wrapping_add(residual.wrapping_mul(c0[5]));
461
462            r0 = ct_select_u128(rr0, r0, need_reduce);
463            r1 = ct_select_u128(rr1, r1, need_reduce);
464            r2 = ct_select_u128(rr2, r2, need_reduce);
465            r3 = ct_select_u128(rr3, r3, need_reduce);
466            r4 = ct_select_u128(rr4, r4, need_reduce);
467            r5 = ct_select_u128(rr5r, r5, need_reduce);
468        }
469    }
470
471    // Fixed 8 conditional subtractions (result may be up to ~16×p).
472    let mut result = CurveUint::from_limbs([r0 as u64, r1 as u64, r2 as u64, r3 as u64, r4 as u64, r5 as u64]);
473    for _ in 0..8 {
474        let (sub, borrow) = result.sub_raw(&MODULUS_P);
475        result = CurveUint::ct_select(&sub, &result, borrow == 0);
476    }
477    result
478}
479
480// Reduce a big-endian value modulo n, accepting zero and values >= n.
481#[inline]
482fn reduce_mod(value: CurveUint) -> CurveUint {
483    let (sub_value, _) = value.sub_raw(&MODULUS_N);
484    CurveUint::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N))
485}
486
487impl Curve for P384 {
488    type U = CurveUint;
489    type FieldBytes = [u8; SECRET_KEY_SIZE];
490    type DigestBytes = [u8; 48];
491    type CompressedBytes = [u8; PUBLIC_KEY_COMPRESSED_SIZE];
492    type UncompressedBytes = [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
493    type SignatureBytes = [u8; SIGNATURE_SIZE];
494
495    const FIELD_BITS: usize = 384;
496    const FIELD_BYTES: usize = SECRET_KEY_SIZE;
497    const DIGEST_BYTES: usize = 48;
498    const COMPRESSED_BYTES: usize = PUBLIC_KEY_COMPRESSED_SIZE;
499    const UNCOMPRESSED_BYTES: usize = PUBLIC_KEY_UNCOMPRESSED_SIZE;
500
501    const MODULUS_P: CurveUint = MODULUS_P;
502    const MODULUS_N: CurveUint = MODULUS_N;
503    const N_HALF: CurveUint = N_HALF;
504    const P_MINUS_TWO: CurveUint = P_MINUS_TWO;
505    const N_MINUS_TWO: CurveUint = N_MINUS_TWO;
506    const CURVE_B: CurveUint = CURVE_B;
507    const GENERATOR_X: CurveUint = GENERATOR_X;
508    const GENERATOR_Y: CurveUint = GENERATOR_Y;
509
510    #[inline]
511    fn field_mul(a: &Self::U, b: &Self::U) -> Self::U {
512        p384_fast_mul_mod(a, b)
513    }
514
515    #[inline]
516    fn scalar_mul(a: &Self::U, b: &Self::U) -> Self::U {
517        a.mul_mod_barrett_w::<14>(b, &MODULUS_N, &N_MU)
518    }
519
520    #[inline]
521    fn field_sqrt(a: Self::U) -> Option<Self::U> {
522        let candidate = field_pow::<Self>(a, &P_PLUS_ONE_OVER_FOUR);
523        if a.ct_eq(&Self::field_mul(&candidate, &candidate)) {
524            Some(candidate)
525        } else {
526            None
527        }
528    }
529
530    #[inline]
531    fn hash(data: &[u8]) -> Self::DigestBytes {
532        Sha384::hash(data).as_ref().try_into().unwrap()
533    }
534
535    #[inline]
536    fn hmac(key: &[u8], data: &[u8]) -> Self::DigestBytes {
537        Hmac::<Sha384>::mac(key, data).as_ref().try_into().unwrap()
538    }
539
540    #[inline]
541    fn scalar_from_digest(digest: &Self::DigestBytes) -> Self::U {
542        reduce_mod(CurveUint::read_be(digest.as_ref()))
543    }
544
545    #[inline]
546    fn scalar_from_field_bytes(bytes: &Self::FieldBytes) -> Self::U {
547        reduce_mod(CurveUint::read_be(bytes.as_ref()))
548    }
549
550    #[cfg(feature = "random")]
551    #[inline]
552    fn random_secret_key() -> Result<Self::FieldBytes, RandomError> {
553        crate::random::bytes()
554    }
555}
556
557/// Returns `true` when `public_key` is a valid SEC1 encoding of a point on the
558/// P-384 curve.
559pub fn is_valid_public_key(public_key: &[u8]) -> bool {
560    p_curves::is_valid_public_key::<P384>(public_key)
561}
562
563#[cfg(test)]
564mod tests {
565    #![allow(dead_code)]
566    use super::*;
567    use crate::p_curves::p_curves;
568
569    type FieldElement = p_curves::FieldElement<P384>;
570    type Scalar = p_curves::Scalar<P384>;
571    type AffinePoint = p_curves::AffinePoint<P384>;
572    type ProjectivePoint = p_curves::ProjectivePoint<P384>;
573    type Rfc6979 = p_curves::Rfc6979<P384>;
574
575    // Concrete wrappers around the generic core helpers.
576    fn hash_message(message: &[u8]) -> [u8; 48] {
577        p_curves::hash_message::<P384>(message)
578    }
579
580    fn hmac_digest(key: &[u8], data: &[u8]) -> [u8; 48] {
581        p_curves::hmac_digest::<P384>(key, data)
582    }
583
584    fn bits2octets(hash: &[u8; 48]) -> [u8; SECRET_KEY_SIZE] {
585        p_curves::bits2octets::<P384>(hash)
586    }
587
588    fn derive_public_key_uncompressed(
589        private_key: &[u8; SECRET_KEY_SIZE],
590    ) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], crate::EllipticCurveError> {
591        p_curves::derive_public_key_uncompressed::<P384>(private_key)
592    }
593
594    fn derive_public_key_compressed(
595        private_key: &[u8; SECRET_KEY_SIZE],
596    ) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], crate::EllipticCurveError> {
597        p_curves::derive_public_key_compressed::<P384>(private_key)
598    }
599
600    fn ecdsa_sign_inner_impl(
601        scalar: &Scalar,
602        message: &[u8],
603        force_first_retry: bool,
604    ) -> Result<[u8; SIGNATURE_SIZE], crate::EllipticCurveError> {
605        p_curves::ecdsa_sign_inner_impl::<P384>(scalar, message, force_first_retry)
606    }
607
608    fn ecdsa_verify_inner(
609        public_point: &AffinePoint,
610        message: &[u8],
611        signature: &[u8; SIGNATURE_SIZE],
612    ) -> Result<(), crate::EllipticCurveError> {
613        p_curves::ecdsa_verify_inner::<P384>(public_point, message, signature)
614    }
615
616    fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, crate::EllipticCurveError> {
617        p_curves::parse_public_key::<P384>(public_key)
618    }
619
620    fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
621        p_curves::scalar_mul_generator::<P384>(scalar)
622    }
623
624    fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
625        p_curves::scalar_mul_affine::<P384>(base, scalar)
626    }
627
628    // Raw-byte ECDH built from the public method, for the vector tests.
629    fn ecdh(
630        secret_key: &[u8; SECRET_KEY_SIZE],
631        peer_public_key: &[u8],
632    ) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], crate::EllipticCurveError> {
633        SecretKey::from_bytes(secret_key)?.ecdh(&PublicKey::from_bytes(peer_public_key)?)
634    }
635
636    #[cfg(feature = "zeroize")]
637    #[test]
638    fn private_key_zeroize_clears_scalar() {
639        use zeroize::Zeroize;
640
641        let mut key = SecretKey::from_bytes(&[1u8; SECRET_KEY_SIZE]).unwrap();
642        assert_ne!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
643        key.zeroize();
644        assert_eq!(key.to_bytes(), [0u8; SECRET_KEY_SIZE]);
645    }
646
647    fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
648        let bytes = hex::decode(hex_bytes).unwrap();
649        assert_eq!(bytes.len(), N);
650        let mut out = [0u8; N];
651        out.copy_from_slice(&bytes);
652        out
653    }
654
655    fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
656        if *offset >= data.len() {
657            return None;
658        }
659        let tag = data[*offset];
660        *offset += 1;
661        if *offset >= data.len() {
662            return None;
663        }
664        let len_byte = data[*offset];
665        *offset += 1;
666        let (len, _) = if len_byte & 0x80 != 0 {
667            let num_bytes = (len_byte & 0x7f) as usize;
668            if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
669                return None;
670            }
671            if num_bytes > 1 && data[*offset] == 0 {
672                return None;
673            }
674            let mut l = 0usize;
675            for i in 0..num_bytes {
676                l = (l << 8) | data[*offset + i] as usize;
677            }
678            if l < 128 {
679                return None;
680            }
681            *offset += num_bytes;
682            (l, num_bytes + 1)
683        } else {
684            (len_byte as usize, 1)
685        };
686        if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
687            return None;
688        }
689        let value = &data[*offset..*offset + len];
690        *offset = (*offset).checked_add(len)?;
691        Some((tag, value))
692    }
693
694    fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 96]> {
695        let mut offset = 0;
696        let (tag, inner) = der_read_tlv(der, &mut offset)?;
697        if tag != 0x30 {
698            return None;
699        }
700        if offset != der.len() {
701            return None;
702        }
703        let mut inner_offset = 0;
704        let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
705        if rtag != 0x02 || rval.is_empty() || rval.len() > 49 {
706            return None;
707        }
708        let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
709        if stag != 0x02 || sval.is_empty() || sval.len() > 49 {
710            return None;
711        }
712        if inner_offset != inner.len() {
713            return None;
714        }
715        let r_valid = if rval.len() == 48 && rval[0] >= 0x80 {
716            false
717        } else if rval.len() == 49 && rval[0] != 0 {
718            false
719        } else if rval.len() == 49 && rval[0] == 0 && rval[1] < 0x80 {
720            false
721        } else if rval.len() > 49 {
722            false
723        } else {
724            true
725        };
726        let s_valid = if sval.len() == 48 && sval[0] >= 0x80 {
727            false
728        } else if sval.len() == 49 && sval[0] != 0 {
729            false
730        } else if sval.len() == 49 && sval[0] == 0 && sval[1] < 0x80 {
731            false
732        } else if sval.len() > 49 {
733            false
734        } else {
735            true
736        };
737        if !r_valid || !s_valid {
738            return None;
739        }
740
741        let r_trimmed = if rval.len() == 49 && rval[0] == 0 {
742            &rval[1..]
743        } else {
744            rval
745        };
746        let s_trimmed = if sval.len() == 49 && sval[0] == 0 {
747            &sval[1..]
748        } else {
749            sval
750        };
751        if r_trimmed.len() > 48 || s_trimmed.len() > 48 {
752            return None;
753        }
754        let mut sig = [0u8; 96];
755        sig[48 - r_trimmed.len()..48].copy_from_slice(r_trimmed);
756        sig[96 - s_trimmed.len()..96].copy_from_slice(s_trimmed);
757        Some(sig)
758    }
759
760    fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
761        let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
762        let secp384r1_oid: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x22];
763        let mut offset = 0;
764        let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
765        let mut inner = 0;
766        let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
767        if _alg_tag != 0x30 {
768            return None;
769        }
770        let mut ai = 0;
771        let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
772        if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
773            return None;
774        }
775        let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
776        if oid2_tag != 0x06 || oid2 != secp384r1_oid {
777            return None;
778        }
779        let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
780        if _bs_tag != 0x03 || bs_val.is_empty() {
781            return None;
782        }
783        Some(bs_val[1..].to_vec())
784    }
785
786    #[test]
787    fn derive_public_key_generator_matches_sec1_base_point() {
788        let mut private_key = [0u8; 48];
789        private_key[47] = 1;
790        let derived = derive_public_key_uncompressed(&private_key).unwrap();
791        let expected = decode_hex::<97>(
792            "04aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
793             5502f25dbf55296c3a545e3872760ab73617de4a96262c6f5d9e98bf9292dc29\
794             f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
795        );
796        assert_eq!(derived, expected);
797    }
798
799    #[test]
800    fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
801        let private_key = decode_hex::<48>(
802            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
803             96d5724e4c70a825f872c9ea60d2edf5",
804        );
805        let key = SecretKey::from_bytes(&private_key).unwrap();
806        let uncompressed = key.public_key();
807        let compressed = derive_public_key_compressed(&private_key).unwrap();
808        let signature = key.sign(b"sample").unwrap();
809
810        assert!(uncompressed.verify(b"sample", &signature).is_ok());
811        let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
812        assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
813    }
814
815    #[test]
816    fn invalid_inputs_are_rejected() {
817        let invalid_private_key = [0u8; SECRET_KEY_SIZE];
818        assert!(SecretKey::from_bytes(&invalid_private_key).is_err());
819        assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
820        assert!(derive_public_key_compressed(&invalid_private_key).is_err());
821
822        let private_key = decode_hex::<48>(
823            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
824             96d5724e4c70a825f872c9ea60d2edf5",
825        );
826        let key = SecretKey::from_bytes(&private_key).unwrap();
827        let signature = key.sign(b"msg").unwrap();
828        let mut zero_r = signature;
829        zero_r[..48].fill(0);
830        assert!(key.public_key().verify(b"msg", &zero_r).is_err());
831    }
832
833    #[test]
834    fn public_key_validation_accepts_known_good_points() {
835        assert!(is_valid_public_key(&decode_hex::<97>(
836            "04aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
837             5502f25dbf55296c3a545e3872760ab73617de4a96262c6f5d9e98bf9292dc29\
838             f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
839        )));
840    }
841
842    #[test]
843    fn ecdsa_sign_verify_round_trip_multiple_messages() {
844        let private_key = decode_hex::<48>(
845            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
846             96d5724e4c70a825f872c9ea60d2edf5",
847        );
848        let key = SecretKey::from_bytes(&private_key).unwrap();
849        let pub_key = key.public_key();
850
851        let messages: &[&[u8]] = &[
852            b"",
853            b"hello world",
854            b"The quick brown fox jumps over the lazy dog",
855            &[0u8; 0],
856            &[0xffu8; 100],
857            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
858        ];
859
860        for msg in messages {
861            let sig = key.sign(msg).unwrap();
862            assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {:?}", msg);
863            let mut wrong_msg = msg.to_vec();
864            wrong_msg.push(0x42);
865            assert!(pub_key.verify(&wrong_msg, &sig).is_err());
866        }
867    }
868
869    #[test]
870    fn rfc6979_retry_advances_drbg() {
871        let private_key = Scalar::from_bytes(&decode_hex::<48>(
872            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
873             96d5724e4c70a825f872c9ea60d2edf5",
874        ))
875        .unwrap();
876        let hash = hash_message(b"sample");
877
878        let mut drbg = Rfc6979::new(&private_key, &hash);
879        let k1 = drbg.generate();
880        assert_eq!(
881            k1.to_bytes(),
882            decode_hex::<48>(
883                "94ed910d1a099dad3254e9242ae85abde4ba15168eaf0ca87a555fd56d10fbca\
884                 2907e3e83ba95368623b8c4686915cf9"
885            )
886        );
887
888        // RFC 6979 §3.2 h.3: an r=0/s=0 retry must continue the DRBG, not
889        // reproduce the same nonce.
890        drbg.retry();
891        let k2 = drbg.generate();
892        assert_ne!(k1.to_bytes(), k2.to_bytes());
893        assert_eq!(
894            k2.to_bytes(),
895            decode_hex::<48>(
896                "9d63ce4c96d070a67f7bee49e870b64838c0ac65bb7440cf46017dca69d35d23\
897                 6219aae5e00a9f01f13e7774be1339fc"
898            )
899        );
900    }
901
902    #[test]
903    fn ecdsa_forced_retry_uses_fresh_nonce() {
904        let private_key = decode_hex::<48>(
905            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
906             96d5724e4c70a825f872c9ea60d2edf5",
907        );
908        let key = SecretKey::from_bytes(&private_key).unwrap();
909        let scalar = Scalar::from_bytes(&private_key).unwrap();
910
911        let normal = key.sign(b"sample").unwrap();
912        // Force the r=0/s=0 retry path. The signature must be produced with the
913        // next DRBG nonce; repeating the first nonce would yield `normal`.
914        let retried = ecdsa_sign_inner_impl(&scalar, b"sample", true).unwrap();
915
916        assert_ne!(normal, retried);
917        assert!(key.public_key().verify(b"sample", &retried).is_ok());
918    }
919
920    #[test]
921    fn ecdsa_sign_verify_different_keys() {
922        let keys: &[&str] = &[
923            "0000000000000000000000000000000000000000000000000000000000000000\
924             00000000000000000000000000000001",
925            "0000000000000000000000000000000000000000000000000000000000000000\
926             00000000000000000000000000000002",
927            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011\
928             2233445566778899aabbccddeeff0011",
929        ];
930
931        for key_hex in keys {
932            let private_key = decode_hex::<48>(key_hex);
933            let key = SecretKey::from_bytes(&private_key).unwrap();
934            let sig = key.sign(b"test message").unwrap();
935            assert!(
936                key.public_key().verify(b"test message", &sig).is_ok(),
937                "sign/verify failed for key {}",
938                key_hex
939            );
940        }
941    }
942
943    #[test]
944    fn ecdsa_verify_wrong_public_key_rejects() {
945        let private_key1 = decode_hex::<48>(
946            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
947             96d5724e4c70a825f872c9ea60d2edf5",
948        );
949        let private_key2 = decode_hex::<48>(
950            "0000000000000000000000000000000000000000000000000000000000000000\
951             00000000000000000000000000000001",
952        );
953        let key1 = SecretKey::from_bytes(&private_key1).unwrap();
954        let key2 = SecretKey::from_bytes(&private_key2).unwrap();
955
956        let sig = key1.sign(b"message").unwrap();
957        assert!(key2.public_key().verify(b"message", &sig).is_err());
958    }
959
960    #[test]
961    fn ecdsa_sign_emits_canonical_low_s() {
962        let private_key = decode_hex::<48>(
963            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
964             96d5724e4c70a825f872c9ea60d2edf5",
965        );
966        let key = SecretKey::from_bytes(&private_key).unwrap();
967        for msg in [&b"sample"[..], b"test", b"", b"another message"] {
968            let sig = key.sign(msg).unwrap();
969            let s = Scalar::from_bytes(sig[48..].try_into().unwrap()).unwrap();
970            assert!(!s.is_high(), "sign produced a high-s signature for {:?}", msg);
971        }
972    }
973
974    #[test]
975    fn ecdsa_verify_strict_rejects_high_s() {
976        let private_key = decode_hex::<48>(
977            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
978             96d5724e4c70a825f872c9ea60d2edf5",
979        );
980        let key = SecretKey::from_bytes(&private_key).unwrap();
981        let public_key = key.public_key();
982        let signature = key.sign(b"sample").unwrap();
983
984        // Canonical low-s signature passes both permissive and strict verification.
985        assert!(public_key.verify(b"sample", &signature).is_ok());
986        assert!(public_key.verify_strict(b"sample", &signature).is_ok());
987
988        // The malleable counterpart (r, n - s) is rejected only by strict verify.
989        let r = Scalar::from_bytes(signature[..48].try_into().unwrap()).unwrap();
990        let s = Scalar::from_bytes(signature[48..].try_into().unwrap()).unwrap();
991        let high_s = Scalar::ZERO.sub(s);
992        assert!(high_s.is_high());
993
994        let mut malleable = [0u8; SIGNATURE_SIZE];
995        malleable[..48].copy_from_slice(&r.to_bytes());
996        malleable[48..].copy_from_slice(&high_s.to_bytes());
997
998        assert!(public_key.verify(b"sample", &malleable).is_ok());
999        assert!(public_key.verify_strict(b"sample", &malleable).is_err());
1000    }
1001
1002    #[test]
1003    fn scalar_from_bytes_rejects_boundary_values() {
1004        let zero = [0u8; 48];
1005        assert!(Scalar::from_bytes(&zero).is_none());
1006
1007        let n_bytes = decode_hex::<48>(
1008            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1009             581a0db248b0a77aecec196accc52973",
1010        );
1011        assert!(Scalar::from_bytes(&n_bytes).is_none());
1012
1013        let n_minus_1 = decode_hex::<48>(
1014            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1015             581a0db248b0a77aecec196accc52972",
1016        );
1017        assert!(Scalar::from_bytes(&n_minus_1).is_some());
1018
1019        let one = decode_hex::<48>(
1020            "0000000000000000000000000000000000000000000000000000000000000000\
1021             00000000000000000000000000000001",
1022        );
1023        assert!(Scalar::from_bytes(&one).is_some());
1024    }
1025
1026    #[test]
1027    fn field_element_from_bytes_rejects_boundary_values() {
1028        let p_bytes = decode_hex::<48>(
1029            "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
1030        );
1031        assert!(FieldElement::from_bytes(&p_bytes).is_none());
1032
1033        let p_minus_1 = decode_hex::<48>(
1034            "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffe",
1035        );
1036        assert!(FieldElement::from_bytes(&p_minus_1).is_some());
1037
1038        let zero = [0u8; 48];
1039        assert!(FieldElement::from_bytes(&zero).is_some());
1040    }
1041
1042    #[test]
1043    fn point_decompression_round_trip() {
1044        let keys: &[&str] = &[
1045            "0000000000000000000000000000000000000000000000000000000000000000\
1046             00000000000000000000000000000001",
1047            "0000000000000000000000000000000000000000000000000000000000000000\
1048             00000000000000000000000000000002",
1049            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1050             96d5724e4c70a825f872c9ea60d2edf5",
1051            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011\
1052             2233445566778899aabbccddeeff0011",
1053        ];
1054
1055        for key_hex in keys {
1056            let private_key = decode_hex::<48>(key_hex);
1057            let key = SecretKey::from_bytes(&private_key).unwrap();
1058            let uncompressed = key.public_key();
1059            let compressed = derive_public_key_compressed(&private_key).unwrap();
1060
1061            let sig = key.sign(b"round-trip").unwrap();
1062            assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
1063            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1064            assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
1065
1066            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1067            assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
1068        }
1069    }
1070
1071    #[test]
1072    fn scalar_inversion_correctness() {
1073        let k = Scalar::from_bytes(&decode_hex::<48>(
1074            "c22b201cc45cd130ef80acfc70e84fa17b91b0ffbfe4c9c44eda37e1ad1d7f8f\
1075             ae4c4c8b52559930e08ba1c822c105b0",
1076        ))
1077        .unwrap();
1078        let k_inv = k.invert().unwrap();
1079        let product = k.mul(k_inv);
1080        assert_eq!(product, Scalar::ONE);
1081    }
1082
1083    #[test]
1084    fn field_element_inversion_correctness() {
1085        let x = FieldElement::from_bytes(&decode_hex::<48>(
1086            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1087             5502f25dbf55296c3a545e3872760ab7",
1088        ))
1089        .unwrap();
1090        let x_inv = x.invert().unwrap();
1091        let product = x.mul(x_inv);
1092        assert_eq!(product, FieldElement::ONE);
1093    }
1094
1095    #[test]
1096    fn p384_fast_mul_mod_matches_generic() {
1097        for _ in 0..1000 {
1098            let a_bytes: [u8; 48] = rand::random();
1099            let b_bytes: [u8; 48] = rand::random();
1100            let a_opt = FieldElement::from_bytes(&a_bytes);
1101            let b_opt = FieldElement::from_bytes(&b_bytes);
1102            if a_opt.is_none() || b_opt.is_none() {
1103                continue;
1104            }
1105            let a = a_opt.unwrap();
1106            let b = b_opt.unwrap();
1107            let expected = U384::from_limbs({
1108                let mut p = [0u64; 12];
1109                for i in 0..6 {
1110                    let mut c = 0u64;
1111                    for j in 0..6 {
1112                        let (v, cc) = mac(p[i + j], a.0.limbs[i], b.0.limbs[j], c);
1113                        p[i + j] = v;
1114                        c = cc;
1115                    }
1116                    p[i + 6] = c;
1117                }
1118                let mut rem = [0u64; 6];
1119                for bi in (0..768).rev() {
1120                    let li = bi / 64;
1121                    let pi = bi % 64;
1122                    let bit = ((p[li] >> pi) & 1) as u64;
1123                    let mut shifted = [0u64; 6];
1124                    let mut carry = bit;
1125                    for j in 0..6 {
1126                        let next = rem[j] >> 63;
1127                        shifted[j] = (rem[j] << 1) | carry;
1128                        carry = next;
1129                    }
1130                    let (red, br) = U384::from_limbs(shifted).sub_raw(&MODULUS_P);
1131                    if carry == 1 || br == 0 {
1132                        rem = red.limbs;
1133                    } else {
1134                        rem = shifted;
1135                    }
1136                }
1137                rem
1138            });
1139            let fast = p384_fast_mul_mod(&a.0, &b.0);
1140            assert_eq!(expected, fast, "mismatch in p384_fast_mul_mod");
1141        }
1142    }
1143
1144    #[test]
1145    fn scalar_mul_generator_n_gives_identity() {
1146        let n_minus_1 = Scalar::from_bytes(&decode_hex::<48>(
1147            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1148             581a0db248b0a77aecec196accc52972",
1149        ))
1150        .unwrap();
1151        let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1152        assert_eq!(result.x, FieldElement::from_uint(GENERATOR_X));
1153        let neg_gy = FieldElement::from_uint(GENERATOR_Y).negate();
1154        assert_eq!(result.y, neg_gy);
1155    }
1156
1157    #[test]
1158    fn ecdh_rejects_invalid_private_key_zero() {
1159        let zero_key = [0u8; 48];
1160        assert!(SecretKey::from_bytes(&zero_key).is_err());
1161        let bob = SecretKey::generate().unwrap();
1162        assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1163    }
1164
1165    #[test]
1166    fn ecdsa_rejects_non_canonical_r_and_s() {
1167        let key = SecretKey::generate().unwrap();
1168        let valid_sig = key.sign(b"msg").unwrap();
1169
1170        let mut bad_r = valid_sig;
1171        bad_r[..48].copy_from_slice(&decode_hex::<48>(
1172            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52974",
1173        ));
1174        assert!(key.public_key().verify(b"msg", &bad_r).is_err());
1175
1176        let mut bad_s = valid_sig;
1177        bad_s[48..].copy_from_slice(&decode_hex::<48>(
1178            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52974",
1179        ));
1180        assert!(key.public_key().verify(b"msg", &bad_s).is_err());
1181    }
1182
1183    #[test]
1184    fn verify_rejects_tampered_message_and_signature() {
1185        let key = SecretKey::generate().unwrap();
1186        let pub_key = key.public_key();
1187        let sig = key.sign(b"message").unwrap();
1188
1189        assert!(pub_key.verify(b"tampered", &sig).is_err());
1190
1191        let mut bad_sig = sig;
1192        bad_sig[10] ^= 0x80;
1193        assert!(pub_key.verify(b"message", &bad_sig).is_err());
1194    }
1195
1196    #[test]
1197    fn public_key_rejects_off_curve_point() {
1198        let key = SecretKey::generate().unwrap();
1199        let mut off_curve = key.public_key().to_bytes();
1200        off_curve[96] ^= 0x01;
1201        assert!(!is_valid_public_key(&off_curve));
1202        assert!(PublicKey::from_bytes(&off_curve).is_err());
1203    }
1204
1205    #[test]
1206    fn x_y_round_trip() {
1207        let key = SecretKey::generate().unwrap();
1208        let pub_key = key.public_key();
1209        let (x, y) = pub_key.x_y();
1210        let pub_key2 = PublicKey::from_x_y(&x, &y).unwrap();
1211        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1212    }
1213
1214    #[test]
1215    fn from_x_y_matches_generator() {
1216        let key = PublicKey::from_x_y(
1217            &decode_hex::<48>(
1218                "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1219                 5502f25dbf55296c3a545e3872760ab7",
1220            ),
1221            &decode_hex::<48>(
1222                "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0\
1223                 0a60b1ce1d7e819d7a431d7c90ea0e5f",
1224            ),
1225        )
1226        .unwrap();
1227        let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1228        assert_eq!(key, from_sec1);
1229    }
1230
1231    #[test]
1232    fn field_element_add_sub_mul_consistency() {
1233        let a = FieldElement::from_bytes(&decode_hex::<48>(
1234            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1235             5502f25dbf55296c3a545e3872760ab7",
1236        ))
1237        .unwrap();
1238        let b = FieldElement::from_bytes(&decode_hex::<48>(
1239            "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0\
1240             0a60b1ce1d7e819d7a431d7c90ea0e5f",
1241        ))
1242        .unwrap();
1243
1244        assert_eq!(a.add(b).sub(b), a);
1245        assert_eq!(a.add(b), b.add(a));
1246        assert_eq!(a.mul(b), b.mul(a));
1247
1248        let c = FieldElement::from_bytes(&decode_hex::<48>(
1249            "2a85c8edd3ec2aefc656398d8a2ed19d0314088f5013875a181d9c6efe814112\
1250             988e056be3f82d19b3312fa7e23ee7e4",
1251        ))
1252        .unwrap();
1253        assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
1254    }
1255
1256    #[test]
1257    fn scalar_add_sub_mul_consistency() {
1258        let a = Scalar::from_bytes(&decode_hex::<48>(
1259            "c22b201cc45cd130ef80acfc70e84fa17b91b0ffbfe4c9c44eda37e1ad1d7f8f\
1260             ae4c4c8b52559930e08ba1c822c105b0",
1261        ))
1262        .unwrap();
1263        let one = Scalar::from_bytes(&decode_hex::<48>(
1264            "0000000000000000000000000000000000000000000000000000000000000000\
1265             00000000000000000000000000000001",
1266        ))
1267        .unwrap();
1268
1269        assert_eq!(a.add(one).sub(one), a);
1270        assert_eq!(a.mul(one), a);
1271
1272        let b = Scalar::from_bytes(&decode_hex::<48>(
1273            "7cf1be7a45d8d72e6e974229bfad108f3d2d4aa6208248adb9343258e4f30f80\
1274             8252a11a87ddc7e0d8ba3b5e28878944",
1275        ))
1276        .unwrap();
1277        assert_eq!(a.mul(b), b.mul(a));
1278        assert_eq!(a.add(b), b.add(a));
1279    }
1280
1281    #[test]
1282    fn field_element_negate_round_trip() {
1283        let x = FieldElement::from_bytes(&decode_hex::<48>(
1284            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1285             5502f25dbf55296c3a545e3872760ab7",
1286        ))
1287        .unwrap();
1288        let neg = x.negate();
1289        assert_eq!(neg.negate(), x);
1290        assert_eq!(x.add(neg), FieldElement::ZERO);
1291    }
1292
1293    #[test]
1294    fn scalar_mul_by_two_matches_double() {
1295        let two = Scalar::from_bytes(&decode_hex::<48>(
1296            "0000000000000000000000000000000000000000000000000000000000000000\
1297             00000000000000000000000000000002",
1298        ))
1299        .unwrap();
1300        let g_times_2 = scalar_mul_affine(&AffinePoint::GENERATOR, &two).to_affine().unwrap();
1301        let proj_g = ProjectivePoint::from_affine(&AffinePoint::GENERATOR);
1302        let g_doubled = proj_g.double().to_affine().unwrap();
1303
1304        assert_eq!(g_times_2.to_uncompressed_bytes(), g_doubled.to_uncompressed_bytes());
1305    }
1306
1307    #[test]
1308    fn ecdsa_sign_then_verify_consistent_for_random_keys() {
1309        for _ in 0..5 {
1310            let key = SecretKey::generate().unwrap();
1311            let msg = rand::random::<[u8; 32]>();
1312            let sig = key.sign(&msg).unwrap();
1313            assert!(key.public_key().verify(&msg, &sig).is_ok());
1314        }
1315    }
1316
1317    #[test]
1318    fn is_on_curve_accepts_generator_and_random_points() {
1319        assert!(AffinePoint::GENERATOR.is_on_curve());
1320        for _ in 0..5 {
1321            let key = SecretKey::generate().unwrap();
1322            let pb = key.public_key().to_bytes();
1323            let pk = PublicKey::from_bytes(&pb).unwrap();
1324            let _ = pk;
1325        }
1326    }
1327
1328    #[test]
1329    fn ecdh_with_self_is_consistent() {
1330        let key = SecretKey::generate().unwrap();
1331        let shared1 = key.ecdh(&key.public_key()).unwrap();
1332        let shared2 = key.ecdh(&key.public_key()).unwrap();
1333        assert_eq!(shared1, shared2);
1334    }
1335
1336    #[test]
1337    fn field_element_pow_correctness() {
1338        let x = FieldElement::from_bytes(&decode_hex::<48>(
1339            "0000000000000000000000000000000000000000000000000000000000000000\
1340             00000000000000000000000000000002",
1341        ))
1342        .unwrap();
1343        let x3 = x.pow(&U384::from_u64(3));
1344        let expected = x.mul(x).mul(x);
1345        assert_eq!(x3, expected);
1346
1347        let x0 = x.pow(&U384::ZERO);
1348        assert_eq!(x0, FieldElement::ONE);
1349    }
1350
1351    // --- Wycheproof test vectors ---
1352
1353    #[test]
1354    fn wycheproof_ecdsa_p384_sha384_p1363() {
1355        let data: serde_json::Value = serde_json::from_str(include_str!(
1356            "../../testdata/wycheproof/testvectors_v1/ecdsa_secp384r1_sha384_p1363_test.json"
1357        ))
1358        .unwrap();
1359        let mut valid_tested = 0u64;
1360        let mut invalid_tested = 0u64;
1361        for group in data["testGroups"].as_array().unwrap() {
1362            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1363            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1364            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1365
1366            for test in group["tests"].as_array().unwrap() {
1367                let msg_hex = test["msg"].as_str().unwrap();
1368                let sig_hex = test["sig"].as_str().unwrap();
1369                let result = test["result"].as_str().unwrap();
1370
1371                let msg = hex::decode(msg_hex).unwrap();
1372
1373                if sig_hex.len() != SIGNATURE_SIZE * 2 {
1374                    continue;
1375                }
1376                let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1377
1378                let verify_result = pk.verify(&msg, &sig);
1379
1380                if result == "valid" {
1381                    assert!(
1382                        verify_result.is_ok(),
1383                        "wycheproof ECDSA P384 P1363 tcId={} expected valid but failed",
1384                        test["tcId"]
1385                    );
1386                    valid_tested += 1;
1387                } else {
1388                    assert!(
1389                        verify_result.is_err(),
1390                        "wycheproof ECDSA P384 P1363 tcId={} expected invalid but passed",
1391                        test["tcId"]
1392                    );
1393                    invalid_tested += 1;
1394                }
1395            }
1396        }
1397        assert!(valid_tested > 0, "no valid ECDSA P384 P1363 wycheproof tests were run");
1398        assert!(invalid_tested > 0, "no invalid ECDSA P384 P1363 wycheproof tests were run");
1399    }
1400
1401    #[test]
1402    fn wycheproof_ecdsa_p384_sha384_der() {
1403        let data: serde_json::Value = serde_json::from_str(include_str!(
1404            "../../testdata/wycheproof/testvectors_v1/ecdsa_secp384r1_sha384_test.json"
1405        ))
1406        .unwrap();
1407        let mut valid_tested = 0u64;
1408        let mut invalid_tested = 0u64;
1409        for group in data["testGroups"].as_array().unwrap() {
1410            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1411            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1412            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1413
1414            for test in group["tests"].as_array().unwrap() {
1415                let msg_hex = test["msg"].as_str().unwrap();
1416                let sig_hex = test["sig"].as_str().unwrap();
1417                let result = test["result"].as_str().unwrap();
1418
1419                let msg = hex::decode(msg_hex).unwrap();
1420                let der_sig = hex::decode(sig_hex).unwrap();
1421                let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
1422                    continue;
1423                };
1424
1425                let verify_result = pk.verify(&msg, &sig);
1426
1427                if result == "valid" {
1428                    assert!(
1429                        verify_result.is_ok(),
1430                        "wycheproof ECDSA P384 DER SHA-384 tcId={} expected valid but failed",
1431                        test["tcId"]
1432                    );
1433                    valid_tested += 1;
1434                } else {
1435                    assert!(
1436                        verify_result.is_err(),
1437                        "wycheproof ECDSA P384 DER SHA-384 tcId={} expected invalid but passed",
1438                        test["tcId"]
1439                    );
1440                    invalid_tested += 1;
1441                }
1442            }
1443        }
1444        assert!(valid_tested > 0, "no valid ECDSA P384 DER SHA-384 wycheproof tests were run");
1445        assert!(
1446            invalid_tested > 0,
1447            "no invalid ECDSA P384 DER SHA-384 wycheproof tests were run"
1448        );
1449    }
1450
1451    #[test]
1452    fn wycheproof_ecdh_p384_ecpoint() {
1453        let data: serde_json::Value = serde_json::from_str(include_str!(
1454            "../../testdata/wycheproof/testvectors_v1/ecdh_secp384r1_ecpoint_test.json"
1455        ))
1456        .unwrap();
1457        let mut valid_tested = 0u64;
1458        let mut invalid_tested = 0u64;
1459        let mut acceptable_tested = 0u64;
1460        for group in data["testGroups"].as_array().unwrap() {
1461            if group["curve"].as_str() != Some("secp384r1") {
1462                continue;
1463            }
1464            for test in group["tests"].as_array().unwrap() {
1465                let public_hex = test["public"].as_str().unwrap();
1466                let private_hex = test["private"].as_str().unwrap();
1467                let expected_shared_hex = test["shared"].as_str().unwrap();
1468                let result = test["result"].as_str().unwrap();
1469
1470                let public_key = hex::decode(public_hex).unwrap();
1471
1472                let private_bytes = hex::decode(private_hex).unwrap();
1473                let mut private_key = [0u8; SECRET_KEY_SIZE];
1474                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
1475                let skip = if private_bytes.len() > SECRET_KEY_SIZE {
1476                    private_bytes.len() - SECRET_KEY_SIZE
1477                } else {
1478                    0
1479                };
1480                private_key[SECRET_KEY_SIZE - effective_len..]
1481                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1482
1483                let shared = ecdh(&private_key, &public_key);
1484
1485                if result == "valid" {
1486                    let shared = shared.unwrap();
1487                    let shared_hex = hex::encode(shared);
1488                    assert_eq!(
1489                        shared_hex, expected_shared_hex,
1490                        "wycheproof ECDH P384 ecpoint tcId={}",
1491                        test["tcId"]
1492                    );
1493                    valid_tested += 1;
1494                } else if result == "invalid" {
1495                    assert!(
1496                        shared.is_err(),
1497                        "wycheproof ECDH P384 ecpoint tcId={} expected invalid but passed",
1498                        test["tcId"]
1499                    );
1500                    invalid_tested += 1;
1501                } else {
1502                    acceptable_tested += 1;
1503                }
1504            }
1505        }
1506        assert!(valid_tested > 0, "no valid ECDH P384 ecpoint wycheproof tests were run");
1507        assert!(invalid_tested > 0, "no invalid ECDH P384 ecpoint wycheproof tests were run");
1508        assert!(
1509            acceptable_tested > 0,
1510            "no acceptable ECDH P384 ecpoint wycheproof tests were run"
1511        );
1512    }
1513
1514    #[test]
1515    fn wycheproof_ecdh_p384_asn() {
1516        let data: serde_json::Value = serde_json::from_str(include_str!(
1517            "../../testdata/wycheproof/testvectors_v1/ecdh_secp384r1_test.json"
1518        ))
1519        .unwrap();
1520        let mut valid_tested = 0u64;
1521        let mut invalid_tested = 0u64;
1522        let mut acceptable_tested = 0u64;
1523        for group in data["testGroups"].as_array().unwrap() {
1524            for test in group["tests"].as_array().unwrap() {
1525                let public_hex = test["public"].as_str().unwrap();
1526                let private_hex = test["private"].as_str().unwrap();
1527                let expected_shared_hex = test["shared"].as_str().unwrap();
1528                let result = test["result"].as_str().unwrap();
1529
1530                let spki_der = hex::decode(public_hex).unwrap();
1531                let Some(sec1_point) = spki_to_sec1_point(&spki_der) else {
1532                    if result == "valid" {
1533                        panic!("wycheproof ECDH P384 ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
1534                    }
1535                    invalid_tested += 1;
1536                    continue;
1537                };
1538
1539                let private_bytes = hex::decode(private_hex).unwrap();
1540                let mut private_key = [0u8; SECRET_KEY_SIZE];
1541                let effective_len = private_bytes.len().min(SECRET_KEY_SIZE);
1542                let skip = if private_bytes.len() > SECRET_KEY_SIZE {
1543                    private_bytes.len() - SECRET_KEY_SIZE
1544                } else {
1545                    0
1546                };
1547                private_key[SECRET_KEY_SIZE - effective_len..]
1548                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1549
1550                let shared = ecdh(&private_key, &sec1_point);
1551
1552                if result == "valid" {
1553                    let shared = shared.unwrap();
1554                    let shared_hex = hex::encode(shared);
1555                    assert_eq!(
1556                        shared_hex, expected_shared_hex,
1557                        "wycheproof ECDH P384 ASN tcId={}",
1558                        test["tcId"]
1559                    );
1560                    valid_tested += 1;
1561                } else if result == "invalid" {
1562                    assert!(
1563                        shared.is_err(),
1564                        "wycheproof ECDH P384 ASN tcId={} expected invalid but passed",
1565                        test["tcId"]
1566                    );
1567                    invalid_tested += 1;
1568                } else {
1569                    acceptable_tested += 1;
1570                }
1571            }
1572        }
1573        assert!(valid_tested > 0, "no valid ECDH P384 ASN wycheproof tests were run");
1574        assert!(invalid_tested > 0, "no invalid ECDH P384 ASN wycheproof tests were run");
1575        assert!(acceptable_tested > 0, "no acceptable ECDH P384 ASN wycheproof tests were run");
1576    }
1577
1578    // Shared curve-independent tests, instantiated for P384.
1579    #[test]
1580    fn generator_point_is_on_curve() {
1581        p_curves::test_support::generator_point_is_on_curve::<P384>();
1582    }
1583
1584    #[test]
1585    fn from_x_y_rejects_off_curve() {
1586        p_curves::test_support::from_x_y_rejects_off_curve::<P384>();
1587    }
1588
1589    #[test]
1590    fn ecdh_round_trip_alice_bob() {
1591        p_curves::test_support::ecdh_round_trip_alice_bob::<P384>();
1592    }
1593
1594    #[test]
1595    fn ecdh_rejects_off_curve_peer_public_key() {
1596        p_curves::test_support::ecdh_rejects_off_curve_peer_public_key::<P384>();
1597    }
1598
1599    #[test]
1600    fn ecdh_rejects_infinity_peer_public_key() {
1601        p_curves::test_support::ecdh_rejects_infinity_peer_public_key::<P384>();
1602    }
1603
1604    #[test]
1605    fn ecdh_rejects_bad_length_peer_public_key() {
1606        p_curves::test_support::ecdh_rejects_bad_length_peer_public_key::<P384>();
1607    }
1608
1609    #[test]
1610    fn ecdh_multiple_exchanges_consistency() {
1611        p_curves::test_support::ecdh_multiple_exchanges_consistency::<P384>();
1612    }
1613
1614    #[test]
1615    fn private_key_round_trip_bytes() {
1616        p_curves::test_support::private_key_round_trip_bytes::<P384>();
1617    }
1618
1619    #[test]
1620    fn public_key_round_trip_bytes() {
1621        p_curves::test_support::public_key_round_trip_bytes::<P384>();
1622    }
1623
1624    #[test]
1625    fn point_double_and_add_consistency() {
1626        p_curves::test_support::point_double_and_add_consistency::<P384>();
1627    }
1628
1629    #[test]
1630    fn compressed_public_key_has_correct_prefix() {
1631        p_curves::test_support::compressed_public_key_has_correct_prefix::<P384>();
1632    }
1633
1634    #[cfg(feature = "zeroize")]
1635    #[test]
1636    fn rfc6979_state_zeroize_clears_drbg() {
1637        p_curves::test_support::rfc6979_state_zeroize_clears_drbg::<P384>();
1638    }
1639}