Skip to main content

crypto/
p384.rs

1use big_number::{Uint, mac};
2
3use crate::{EllipticCurveError, Hasher, hmac::Hmac, sha2::Sha384};
4
5/// Size of a P-384 private key in bytes (48 bytes).
6pub const PRIVATE_KEY_SIZE: usize = 48;
7/// Size of a compressed P-384 public key in bytes (49 bytes, includes 0x02/0x03 prefix).
8pub const PUBLIC_KEY_COMPRESSED_SIZE: usize = 49;
9/// Size of an uncompressed P-384 public key in bytes (97 bytes, includes 0x04 prefix).
10pub const PUBLIC_KEY_UNCOMPRESSED_SIZE: usize = 97;
11/// Size of a P-384 ECDSA signature in bytes (96 bytes, r || s).
12pub const SIGNATURE_SIZE: usize = 96;
13/// Size of the raw ECDH shared secret in bytes (48 bytes). **Must not** be used directly
14/// as an encryption key; apply a KDF first.
15pub const ECDH_SHARED_SECRET_SIZE: usize = 48;
16
17/// P-384 (secp384r1) ECDSA private key.
18///
19/// Supports signing and ECDH key agreement.
20///
21/// # Signing
22///
23/// ```ignore
24/// use crypto::p384::PrivateKey;
25///
26/// let key = PrivateKey::generate().unwrap();
27/// let signature = key.sign(b"message").unwrap();
28/// ```
29///
30/// # ECDH key exchange
31///
32/// ```ignore
33/// use crypto::p384::PrivateKey;
34///
35/// let alice = PrivateKey::generate().unwrap();
36/// let bob = PrivateKey::generate().unwrap();
37/// let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
38/// let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
39/// assert_eq!(alice_shared, bob_shared);
40/// ```
41///
42/// # Security
43///
44/// The raw shared secret from [`ecdh`](Self::ecdh) **must not** be used
45/// directly as an encryption key. Apply a KDF (e.g. HKDF) first.
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub struct PrivateKey {
48    scalar: Scalar,
49    public_point: AffinePoint,
50}
51
52impl PrivateKey {
53    #[cfg(feature = "random")]
54    pub fn generate() -> Result<PrivateKey, EllipticCurveError> {
55        let key: [u8; PRIVATE_KEY_SIZE] = crate::random::random_bytes();
56        Self::from_bytes(&key)
57    }
58
59    pub fn from_bytes(key: &[u8; PRIVATE_KEY_SIZE]) -> Result<PrivateKey, EllipticCurveError> {
60        let scalar = Scalar::from_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
61        let public_point = scalar_mul_generator(&scalar)
62            .to_affine()
63            .ok_or(EllipticCurveError::Unspecified)?;
64        Ok(PrivateKey {
65            scalar,
66            public_point,
67        })
68    }
69
70    pub fn public_key(&self) -> PublicKey {
71        PublicKey {
72            point: self.public_point,
73        }
74    }
75
76    pub fn sign(&self, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
77        ecdsa_sign_inner(&self.scalar, message)
78    }
79
80    pub fn ecdh(&self, peer_public: &PublicKey) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
81        ecdh_inner(&self.scalar, &peer_public.point)
82    }
83
84    pub fn to_bytes(&self) -> [u8; PRIVATE_KEY_SIZE] {
85        self.scalar.to_bytes()
86    }
87}
88
89/// P-384 (secp384r1) ECDSA public key.
90///
91/// Supports signature verification and ECDH key agreement.
92///
93/// # Verification
94///
95/// ```ignore
96/// use crypto::p384::PrivateKey;
97///
98/// let key = PrivateKey::generate().unwrap();
99/// let signature = key.sign(b"message").unwrap();
100/// assert!(key.public_key().verify(b"message", &signature).is_ok());
101/// ```
102#[derive(Clone, Copy, Debug, PartialEq, Eq)]
103pub struct PublicKey {
104    point: AffinePoint,
105}
106
107impl PublicKey {
108    pub fn from_bytes(key: &[u8]) -> Result<PublicKey, EllipticCurveError> {
109        let point = AffinePoint::from_sec1_bytes(key).ok_or(EllipticCurveError::InvalidKey)?;
110        Ok(PublicKey {
111            point,
112        })
113    }
114
115    /// Build a public key from raw affine x and y coordinates (both
116    /// big-endian, 48 bytes each). Returns `InvalidKey` if the coordinates
117    /// are not a valid point on the P-384 curve.
118    ///
119    /// This is useful when importing keys from formats like JWK where `x`
120    /// and `y` are available directly.
121    #[inline]
122    pub fn from_x_y(x_bytes: &[u8; 48], y_bytes: &[u8; 48]) -> Result<PublicKey, EllipticCurveError> {
123        let x = FieldElement::from_bytes(x_bytes).ok_or(EllipticCurveError::InvalidKey)?;
124        let y = FieldElement::from_bytes(y_bytes).ok_or(EllipticCurveError::InvalidKey)?;
125        let point = AffinePoint::new(x, y).ok_or(EllipticCurveError::InvalidKey)?;
126        Ok(PublicKey {
127            point,
128        })
129    }
130
131    pub fn verify(&self, message: &[u8], signature: &[u8; SIGNATURE_SIZE]) -> Result<(), EllipticCurveError> {
132        ecdsa_verify_inner(&self.point, message, signature)
133    }
134
135    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
136        self.point.to_uncompressed_bytes()
137    }
138
139    /// Returns the `X` and `Y` points as big-endian arrays.
140    #[inline]
141    pub fn x_y(&self) -> ([u8; 48], [u8; 48]) {
142        (self.point.x.to_bytes(), self.point.y.to_bytes())
143    }
144}
145
146type U384 = Uint<384, 6>;
147
148const MODULUS_P: U384 = U384::from_limbs([
149    0x00000000ffffffff,
150    0xffffffff00000000,
151    0xfffffffffffffffe,
152    0xffffffffffffffff,
153    0xffffffffffffffff,
154    0xffffffffffffffff,
155]);
156
157const MODULUS_N: U384 = U384::from_limbs([
158    0xecec196accc52973,
159    0x581a0db248b0a77a,
160    0xc7634d81f4372ddf,
161    0xffffffffffffffff,
162    0xffffffffffffffff,
163    0xffffffffffffffff,
164]);
165
166const P_MINUS_TWO: U384 = U384::from_limbs([
167    0x00000000fffffffd,
168    0xffffffff00000000,
169    0xfffffffffffffffe,
170    0xffffffffffffffff,
171    0xffffffffffffffff,
172    0xffffffffffffffff,
173]);
174
175const P_PLUS_ONE_OVER_FOUR: U384 = U384::from_limbs([
176    0x0000000040000000,
177    0xbfffffffc0000000,
178    0xffffffffffffffff,
179    0xffffffffffffffff,
180    0xffffffffffffffff,
181    0x3fffffffffffffff,
182]);
183
184const N_MINUS_TWO: U384 = U384::from_limbs([
185    0xecec196accc52971,
186    0x581a0db248b0a77a,
187    0xc7634d81f4372ddf,
188    0xffffffffffffffff,
189    0xffffffffffffffff,
190    0xffffffffffffffff,
191]);
192
193const CURVE_B: FieldElement = FieldElement(U384::from_limbs([
194    0x2a85c8edd3ec2aef,
195    0xc656398d8a2ed19d,
196    0x0314088f5013875a,
197    0x181d9c6efe814112,
198    0x988e056be3f82d19,
199    0xb3312fa7e23ee7e4,
200]));
201
202const GENERATOR_X: FieldElement = FieldElement(U384::from_limbs([
203    0x3a545e3872760ab7,
204    0x5502f25dbf55296c,
205    0x59f741e082542a38,
206    0x6e1d3b628ba79b98,
207    0x8eb1c71ef320ad74,
208    0xaa87ca22be8b0537,
209]));
210
211const GENERATOR_Y: FieldElement = FieldElement(U384::from_limbs([
212    0x7a431d7c90ea0e5f,
213    0x0a60b1ce1d7e819d,
214    0xe9da3113b5f0b8c0,
215    0xf8f41dbd289a147c,
216    0x5d9e98bf9292dc29,
217    0x3617de4a96262c6f,
218]));
219
220// P-384 fast reduction constants: S_i = 2^(64i) mod p for i=6..11
221// Derived from p = 2^384 - 2^128 - 2^96 + 2^32 - 1
222// Each S_i is a 6-limb U384 value.
223const S6: [u64; 6] = [
224    0xffffffff00000001,
225    0x00000000ffffffff,
226    0x0000000000000001,
227    0x0000000000000000,
228    0x0000000000000000,
229    0x0000000000000000,
230];
231
232const S7: [u64; 6] = [
233    0x0000000000000000,
234    0xffffffff00000001,
235    0x00000000ffffffff,
236    0x0000000000000001,
237    0x0000000000000000,
238    0x0000000000000000,
239];
240
241const S8: [u64; 6] = [
242    0x0000000000000000,
243    0x0000000000000000,
244    0xffffffff00000001,
245    0x00000000ffffffff,
246    0x0000000000000001,
247    0x0000000000000000,
248];
249
250const S9: [u64; 6] = [
251    0x0000000000000000,
252    0x0000000000000000,
253    0x0000000000000000,
254    0xffffffff00000001,
255    0x00000000ffffffff,
256    0x0000000000000001,
257];
258
259const S10: [u64; 6] = [
260    0xffffffff00000001,
261    0x00000000ffffffff,
262    0x0000000000000001,
263    0x0000000000000000,
264    0xffffffff00000001,
265    0x00000000ffffffff,
266];
267
268const S11: [u64; 6] = [
269    0x00000001ffffffff,
270    0xfffffffe00000000,
271    0x00000001ffffffff,
272    0x0000000000000001,
273    0x0000000000000000,
274    0xffffffff00000001,
275];
276
277#[inline]
278fn ct_select_u128(a: u128, b: u128, choice: bool) -> u128 {
279    let mask = (choice as u128).wrapping_neg();
280    (a & mask) | (b & !mask)
281}
282
283// P-384 fast modular multiplication using u128 accumulators.
284// All loops run fixed iteration counts with ct_select for constant-time.
285fn p384_fast_mul_mod(a: &U384, b: &U384) -> U384 {
286    let al = a.limbs;
287    let bl = b.limbs;
288
289    let mut prod = [0u64; 12];
290    for i in 0..6 {
291        let mut carry = 0u64;
292        for j in 0..6 {
293            let (v, cc) = mac(prod[i + j], al[i], bl[j], carry);
294            prod[i + j] = v;
295            carry = cc;
296        }
297        prod[i + 6] = carry;
298    }
299
300    const MASK: u128 = 0xffffffffffffffff;
301    let c0 = [
302        S6[0] as u128,
303        S6[1] as u128,
304        S6[2] as u128,
305        S6[3] as u128,
306        S6[4] as u128,
307        S6[5] as u128,
308    ];
309    let c1 = [
310        S7[0] as u128,
311        S7[1] as u128,
312        S7[2] as u128,
313        S7[3] as u128,
314        S7[4] as u128,
315        S7[5] as u128,
316    ];
317    let c2 = [
318        S8[0] as u128,
319        S8[1] as u128,
320        S8[2] as u128,
321        S8[3] as u128,
322        S8[4] as u128,
323        S8[5] as u128,
324    ];
325    let c3 = [
326        S9[0] as u128,
327        S9[1] as u128,
328        S9[2] as u128,
329        S9[3] as u128,
330        S9[4] as u128,
331        S9[5] as u128,
332    ];
333    let c4 = [
334        S10[0] as u128,
335        S10[1] as u128,
336        S10[2] as u128,
337        S10[3] as u128,
338        S10[4] as u128,
339        S10[5] as u128,
340    ];
341    let c5 = [
342        S11[0] as u128,
343        S11[1] as u128,
344        S11[2] as u128,
345        S11[3] as u128,
346        S11[4] as u128,
347        S11[5] as u128,
348    ];
349    let coeffs: [&[u128; 6]; 6] = [&c0, &c1, &c2, &c3, &c4, &c5];
350
351    let mut r0 = prod[0] as u128;
352    let mut r1 = prod[1] as u128;
353    let mut r2 = prod[2] as u128;
354    let mut r3 = prod[3] as u128;
355    let mut r4 = prod[4] as u128;
356    let mut r5 = prod[5] as u128;
357
358    for i in 0..6 {
359        let w = prod[6 + i] as u128;
360        let c = coeffs[i];
361
362        r0 = r0.wrapping_add(w.wrapping_mul(c[0]));
363        r1 = r1.wrapping_add(w.wrapping_mul(c[1]));
364        r2 = r2.wrapping_add(w.wrapping_mul(c[2]));
365        r3 = r3.wrapping_add(w.wrapping_mul(c[3]));
366        r4 = r4.wrapping_add(w.wrapping_mul(c[4]));
367        r5 = r5.wrapping_add(w.wrapping_mul(c[5]));
368
369        // Fixed 4 iterations: carry propagation + conditional residual reduction.
370        for _ in 0..4 {
371            let carry = r0 >> 64;
372            r1 = r1.wrapping_add(carry);
373            r0 &= MASK;
374            let carry = r1 >> 64;
375            r2 = r2.wrapping_add(carry);
376            r1 &= MASK;
377            let carry = r2 >> 64;
378            r3 = r3.wrapping_add(carry);
379            r2 &= MASK;
380            let carry = r3 >> 64;
381            r4 = r4.wrapping_add(carry);
382            r3 &= MASK;
383            let carry = r4 >> 64;
384            r5 = r5.wrapping_add(carry);
385            r4 &= MASK;
386
387            let residual = r5 >> 64;
388            let need_reduce = residual != 0;
389
390            let rr5 = r5 & MASK;
391            let rr0 = r0.wrapping_add(residual.wrapping_mul(c0[0]));
392            let rr1 = r1.wrapping_add(residual.wrapping_mul(c0[1]));
393            let rr2 = r2.wrapping_add(residual.wrapping_mul(c0[2]));
394            let rr3 = r3.wrapping_add(residual.wrapping_mul(c0[3]));
395            let rr4 = r4.wrapping_add(residual.wrapping_mul(c0[4]));
396            let rr5r = rr5.wrapping_add(residual.wrapping_mul(c0[5]));
397
398            r0 = ct_select_u128(rr0, r0, need_reduce);
399            r1 = ct_select_u128(rr1, r1, need_reduce);
400            r2 = ct_select_u128(rr2, r2, need_reduce);
401            r3 = ct_select_u128(rr3, r3, need_reduce);
402            r4 = ct_select_u128(rr4, r4, need_reduce);
403            r5 = ct_select_u128(rr5r, r5, need_reduce);
404        }
405    }
406
407    // Fixed 8 conditional subtractions (result may be up to ~16×p).
408    let mut result = U384::from_limbs([r0 as u64, r1 as u64, r2 as u64, r3 as u64, r4 as u64, r5 as u64]);
409    for _ in 0..8 {
410        let (sub, borrow) = result.sub_raw(&MODULUS_P);
411        result = U384::ct_select(&sub, &result, borrow == 0);
412    }
413    result
414}
415
416#[derive(Clone, Copy, Debug, PartialEq, Eq)]
417struct FieldElement(U384);
418
419impl FieldElement {
420    const ZERO: Self = Self(U384::ZERO);
421    const ONE: Self = Self(U384::ONE);
422
423    #[inline]
424    fn from_bytes(bytes: &[u8; 48]) -> Option<Self> {
425        let value = U384::from_be_slice(bytes);
426        if value.ct_ge(&MODULUS_P) {
427            None
428        } else {
429            Some(Self(value))
430        }
431    }
432
433    #[inline]
434    fn to_bytes(self) -> [u8; 48] {
435        self.0.to_be_bytes_fixed::<48>()
436    }
437
438    #[inline]
439    fn is_zero(&self) -> bool {
440        self.0.is_zero()
441    }
442
443    #[inline]
444    fn is_odd(&self) -> bool {
445        self.0.is_odd()
446    }
447
448    #[inline]
449    fn add(self, rhs: Self) -> Self {
450        Self(self.0.add_mod(&rhs.0, &MODULUS_P))
451    }
452
453    #[inline]
454    fn sub(self, rhs: Self) -> Self {
455        Self(self.0.sub_mod(&rhs.0, &MODULUS_P))
456    }
457
458    #[inline]
459    fn double(self) -> Self {
460        Self(self.0.double_mod(&MODULUS_P))
461    }
462
463    #[inline]
464    fn square(self) -> Self {
465        self.mul(self)
466    }
467
468    #[inline]
469    fn mul(self, rhs: Self) -> Self {
470        Self(p384_fast_mul_mod(&self.0, &rhs.0))
471    }
472
473    #[inline]
474    fn triple(self) -> Self {
475        self.double().add(self)
476    }
477
478    #[inline]
479    fn negate(self) -> Self {
480        let (diff, _) = MODULUS_P.sub_raw(&self.0);
481        Self(U384::ct_select(&U384::ZERO, &diff, self.is_zero()))
482    }
483
484    #[inline]
485    fn pow(self, exponent: &U384) -> Self {
486        let mut result = Self::ONE;
487        let mut i = 384usize;
488        while i > 0 {
489            i -= 1;
490            result = result.square();
491            let product = result.mul(self);
492            result = Self::select(&product, &result, exponent.bit(i));
493        }
494        result
495    }
496
497    #[inline]
498    fn invert(self) -> Option<Self> {
499        Some(self.pow(&P_MINUS_TWO))
500    }
501
502    #[inline]
503    fn sqrt(self) -> Option<Self> {
504        let candidate = self.pow(&P_PLUS_ONE_OVER_FOUR);
505        if U384::ct_eq(&self.0, &candidate.square().0) {
506            Some(candidate)
507        } else {
508            None
509        }
510    }
511
512    #[inline]
513    fn select(a: &Self, b: &Self, choice: bool) -> Self {
514        Self(U384::ct_select(&a.0, &b.0, choice))
515    }
516}
517
518#[derive(Clone, Copy, Debug, PartialEq, Eq)]
519struct Scalar(U384);
520
521impl Scalar {
522    const ZERO: Self = Self(U384::ZERO);
523    const ONE: Self = Self(U384::ONE);
524
525    #[inline]
526    fn from_bytes(bytes: &[u8; 48]) -> Option<Self> {
527        let value = U384::from_be_slice(bytes);
528        if value.is_zero() || value.ct_ge(&MODULUS_N) {
529            None
530        } else {
531            Some(Self(value))
532        }
533    }
534
535    #[inline]
536    fn from_hash(hash: &[u8; 48]) -> Self {
537        let value = U384::from_be_slice(hash);
538        let (sub_value, _) = value.sub_raw(&MODULUS_N);
539        let reduced = U384::ct_select(&sub_value, &value, value.ct_ge(&MODULUS_N));
540        Self(reduced)
541    }
542
543    #[inline]
544    fn to_bytes(self) -> [u8; 48] {
545        self.0.to_be_bytes_fixed::<48>()
546    }
547
548    #[inline]
549    fn is_zero(&self) -> bool {
550        self.0.is_zero()
551    }
552
553    #[inline]
554    fn bit(&self, index: usize) -> bool {
555        self.0.bit(index)
556    }
557
558    #[inline]
559    fn add(self, rhs: Self) -> Self {
560        Self(self.0.add_mod(&rhs.0, &MODULUS_N))
561    }
562
563    #[cfg(test)]
564    #[inline]
565    fn sub(self, rhs: Self) -> Self {
566        Self(self.0.sub_mod(&rhs.0, &MODULUS_N))
567    }
568
569    #[inline]
570    fn mul(self, rhs: Self) -> Self {
571        Self(self.0.mul_mod(&rhs.0, &MODULUS_N))
572    }
573
574    #[inline]
575    fn invert(self) -> Option<Self> {
576        Some(Self(self.scalar_pow(&N_MINUS_TWO)))
577    }
578
579    #[inline]
580    fn scalar_pow(self, exponent: &U384) -> U384 {
581        let mut result = Scalar::ONE;
582        let mut i = 384usize;
583        while i > 0 {
584            i -= 1;
585            result = result.mul(result);
586            let product = result.mul(self);
587            result = Scalar::select(&product, &result, exponent.bit(i));
588        }
589        result.0
590    }
591
592    #[inline]
593    fn select(a: &Self, b: &Self, choice: bool) -> Self {
594        Self(U384::ct_select(&a.0, &b.0, choice))
595    }
596}
597
598#[derive(Clone, Copy, Debug, PartialEq, Eq)]
599struct AffinePoint {
600    x: FieldElement,
601    y: FieldElement,
602    infinity: bool,
603}
604
605impl AffinePoint {
606    const GENERATOR: Self = Self {
607        x: GENERATOR_X,
608        y: GENERATOR_Y,
609        infinity: false,
610    };
611
612    #[inline]
613    fn new(x: FieldElement, y: FieldElement) -> Option<Self> {
614        let point = Self {
615            x,
616            y,
617            infinity: false,
618        };
619        if point.is_on_curve() { Some(point) } else { None }
620    }
621
622    #[inline]
623    fn is_on_curve(&self) -> bool {
624        if self.infinity {
625            return false;
626        }
627        let x2 = self.x.square();
628        let x3 = x2.mul(self.x);
629        let rhs = x3.sub(self.x.triple()).add(CURVE_B);
630        self.y.square() == rhs
631    }
632
633    #[inline]
634    fn to_uncompressed_bytes(&self) -> [u8; PUBLIC_KEY_UNCOMPRESSED_SIZE] {
635        let mut out = [0u8; PUBLIC_KEY_UNCOMPRESSED_SIZE];
636        out[0] = 0x04;
637        out[1..49].copy_from_slice(&self.x.to_bytes());
638        out[49..97].copy_from_slice(&self.y.to_bytes());
639        out
640    }
641
642    #[cfg(test)]
643    #[inline]
644    fn to_compressed_bytes(&self) -> [u8; PUBLIC_KEY_COMPRESSED_SIZE] {
645        let mut out = [0u8; PUBLIC_KEY_COMPRESSED_SIZE];
646        out[0] = if self.y.is_odd() { 0x03 } else { 0x02 };
647        out[1..49].copy_from_slice(&self.x.to_bytes());
648        out
649    }
650
651    fn from_sec1_bytes(bytes: &[u8]) -> Option<Self> {
652        match bytes.len() {
653            PUBLIC_KEY_UNCOMPRESSED_SIZE if bytes[0] == 0x04 => {
654                let x = FieldElement::from_bytes(bytes[1..49].try_into().unwrap())?;
655                let y = FieldElement::from_bytes(bytes[49..97].try_into().unwrap())?;
656                Self::new(x, y)
657            }
658            PUBLIC_KEY_COMPRESSED_SIZE if bytes[0] == 0x02 || bytes[0] == 0x03 => {
659                let x = FieldElement::from_bytes(bytes[1..49].try_into().unwrap())?;
660                let rhs = x.square().mul(x).sub(x.triple()).add(CURVE_B);
661                let y = rhs.sqrt()?;
662                let y_is_odd = y.is_odd();
663                let select_neg = y_is_odd != (bytes[0] == 0x03);
664                let y = FieldElement::select(&y.negate(), &y, select_neg);
665                Self::new(x, y)
666            }
667            _ => None,
668        }
669    }
670}
671
672#[derive(Clone, Copy, Debug, PartialEq, Eq)]
673struct ProjectivePoint {
674    x: FieldElement,
675    y: FieldElement,
676    z: FieldElement,
677}
678
679impl ProjectivePoint {
680    const IDENTITY: Self = Self {
681        x: FieldElement::ZERO,
682        y: FieldElement::ONE,
683        z: FieldElement::ZERO,
684    };
685
686    #[cfg(test)]
687    #[inline]
688    fn from_affine(point: &AffinePoint) -> Self {
689        if point.infinity {
690            Self::IDENTITY
691        } else {
692            Self {
693                x: point.x,
694                y: point.y,
695                z: FieldElement::ONE,
696            }
697        }
698    }
699
700    #[inline]
701    fn is_identity(&self) -> bool {
702        self.z.is_zero()
703    }
704
705    #[inline]
706    fn select(a: &Self, b: &Self, choice: bool) -> Self {
707        Self {
708            x: FieldElement::select(&a.x, &b.x, choice),
709            y: FieldElement::select(&a.y, &b.y, choice),
710            z: FieldElement::select(&a.z, &b.z, choice),
711        }
712    }
713
714    #[inline]
715    fn to_affine(&self) -> Option<AffinePoint> {
716        if self.is_identity() {
717            return None;
718        }
719        let z_inv = self.z.invert()?;
720        AffinePoint::new(self.x.mul(z_inv), self.y.mul(z_inv))
721    }
722
723    fn add(&self, rhs: &Self) -> Self {
724        let xx = self.x.mul(rhs.x);
725        let yy = self.y.mul(rhs.y);
726        let zz = self.z.mul(rhs.z);
727        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
728        let yz_pairs = self.y.add(self.z).mul(rhs.y.add(rhs.z)).sub(yy.add(zz));
729        let xz_pairs = self.x.add(self.z).mul(rhs.x.add(rhs.z)).sub(xx.add(zz));
730
731        let bzz_part = xz_pairs.sub(CURVE_B.mul(zz));
732        let bzz3_part = bzz_part.triple();
733        let yy_m_bzz3 = yy.sub(bzz3_part);
734        let yy_p_bzz3 = yy.add(bzz3_part);
735
736        let zz3 = zz.triple();
737        let bxz_part = CURVE_B.mul(xz_pairs).sub(zz3.add(xx));
738        let bxz3_part = bxz_part.triple();
739        let xx3_m_zz3 = xx.triple().sub(zz3);
740
741        Self {
742            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
743            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
744            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
745        }
746    }
747
748    fn add_mixed(&self, rhs: &AffinePoint) -> Self {
749        if rhs.infinity {
750            return *self;
751        }
752
753        let xx = self.x.mul(rhs.x);
754        let yy = self.y.mul(rhs.y);
755        let xy_pairs = self.x.add(self.y).mul(rhs.x.add(rhs.y)).sub(xx.add(yy));
756        let yz_pairs = rhs.y.mul(self.z).add(self.y);
757        let xz_pairs = rhs.x.mul(self.z).add(self.x);
758
759        let bz_part = xz_pairs.sub(CURVE_B.mul(self.z));
760        let bz3_part = bz_part.triple();
761        let yy_m_bzz3 = yy.sub(bz3_part);
762        let yy_p_bzz3 = yy.add(bz3_part);
763
764        let z3 = self.z.triple();
765        let bxz_part = CURVE_B.mul(xz_pairs).sub(z3.add(xx));
766        let bxz3_part = bxz_part.triple();
767        let xx3_m_zz3 = xx.triple().sub(z3);
768
769        Self {
770            x: yy_p_bzz3.mul(xy_pairs).sub(yz_pairs.mul(bxz3_part)),
771            y: yy_p_bzz3.mul(yy_m_bzz3).add(xx3_m_zz3.mul(bxz3_part)),
772            z: yy_m_bzz3.mul(yz_pairs).add(xy_pairs.mul(xx3_m_zz3)),
773        }
774    }
775
776    fn double(&self) -> Self {
777        let xx = self.x.square();
778        let yy = self.y.square();
779        let zz = self.z.square();
780        let xy2 = self.x.mul(self.y).double();
781        let xz2 = self.x.mul(self.z).double();
782
783        let bzz_part = CURVE_B.mul(zz).sub(xz2);
784        let bzz3_part = bzz_part.triple();
785        let yy_m_bzz3 = yy.sub(bzz3_part);
786        let yy_p_bzz3 = yy.add(bzz3_part);
787        let y_frag = yy_p_bzz3.mul(yy_m_bzz3);
788        let x_frag = yy_m_bzz3.mul(xy2);
789
790        let zz3 = zz.triple();
791        let bxz2_part = CURVE_B.mul(xz2).sub(zz3.add(xx));
792        let bxz6_part = bxz2_part.triple();
793        let xx3_m_zz3 = xx.triple().sub(zz3);
794
795        let y = y_frag.add(xx3_m_zz3.mul(bxz6_part));
796        let yz2 = self.y.mul(self.z).double();
797        let x = x_frag.sub(bxz6_part.mul(yz2));
798        let z = yz2.mul(yy).double().double();
799
800        Self {
801            x,
802            y,
803            z,
804        }
805    }
806}
807
808fn scalar_mul_generator(scalar: &Scalar) -> ProjectivePoint {
809    scalar_mul_affine(&AffinePoint::GENERATOR, scalar)
810}
811
812fn scalar_mul_affine(base: &AffinePoint, scalar: &Scalar) -> ProjectivePoint {
813    let mut acc = ProjectivePoint::IDENTITY;
814    let mut bit = 384usize;
815    while bit > 0 {
816        bit -= 1;
817        acc = acc.double();
818        let candidate = acc.add_mixed(base);
819        acc = ProjectivePoint::select(&candidate, &acc, scalar.bit(bit));
820    }
821    acc
822}
823
824#[inline]
825fn hash_message(message: &[u8]) -> [u8; 48] {
826    let digest = Sha384::hash(message);
827    digest.as_ref().try_into().unwrap()
828}
829
830#[inline]
831fn hmac_sha384(key: &[u8], data: &[u8]) -> [u8; 48] {
832    let mac = Hmac::<Sha384>::mac(key, data);
833    mac.as_ref().try_into().unwrap()
834}
835
836fn bits2octets(hash: &[u8; 48]) -> [u8; 48] {
837    Scalar::from_hash(hash).to_bytes()
838}
839
840fn rfc6979_init_state(private_key: &Scalar, message_hash: &[u8; 48]) -> ([u8; 48], [u8; 48]) {
841    let x = private_key.to_bytes();
842    let h1 = bits2octets(message_hash);
843
844    let mut v = [0x01u8; 48];
845    let mut k = [0u8; 48];
846
847    let mut buf = [0u8; 145];
848    buf[..48].copy_from_slice(&v);
849    buf[48] = 0x00;
850    buf[49..97].copy_from_slice(&x);
851    buf[97..145].copy_from_slice(&h1);
852    k = hmac_sha384(&k, &buf);
853    v = hmac_sha384(&k, &v);
854
855    buf[..48].copy_from_slice(&v);
856    buf[48] = 0x01;
857    k = hmac_sha384(&k, &buf);
858    v = hmac_sha384(&k, &v);
859
860    (k, v)
861}
862
863fn rfc6979_retry(k: &mut [u8; 48], v: &mut [u8; 48]) {
864    let mut retry_buf = [0u8; 49];
865    retry_buf[..48].copy_from_slice(v);
866    retry_buf[48] = 0x00;
867    *k = hmac_sha384(k, &retry_buf);
868    *v = hmac_sha384(k, v);
869}
870
871fn rfc6979_retry_clone(k: &[u8; 48], v: &[u8; 48]) -> ([u8; 48], [u8; 48]) {
872    let mut retry_buf = [0u8; 49];
873    retry_buf[..48].copy_from_slice(v);
874    retry_buf[48] = 0x00;
875    let k_new = hmac_sha384(k, &retry_buf);
876    let v_new = hmac_sha384(&k_new, v);
877    (k_new, v_new)
878}
879
880fn ct_select_bytes<const N: usize>(a: &[u8; N], b: &[u8; N], choice: bool) -> [u8; N] {
881    let mask = (choice as u8).wrapping_neg();
882    let mut out = [0u8; N];
883    for i in 0..N {
884        out[i] = (a[i] & mask) | (b[i] & !mask);
885    }
886    out
887}
888
889fn rfc6979_generate_k(private_key: &Scalar, message_hash: &[u8; 48]) -> Scalar {
890    let (mut k, mut v) = rfc6979_init_state(private_key, message_hash);
891
892    let mut candidate = [0u8; 48];
893    let mut found = false;
894
895    for _ in 0..3 {
896        v = hmac_sha384(&k, &v);
897        let val = U384::from_be_slice(&v);
898        let is_valid = !val.is_zero() && !val.ct_ge(&MODULUS_N);
899
900        let take = is_valid && !found;
901        candidate = ct_select_bytes(&v, &candidate, take);
902        found = found || is_valid;
903
904        let (k_retry, v_retry) = rfc6979_retry_clone(&k, &v);
905        k = ct_select_bytes(&k, &k_retry, !is_valid);
906        v = ct_select_bytes(&v, &v_retry, !is_valid);
907    }
908
909    if found {
910        return Scalar::from_bytes(&candidate).unwrap_or(Scalar::ZERO);
911    }
912
913    v = hmac_sha384(&k, &v);
914    if let Some(sc) = Scalar::from_bytes(&v) {
915        return sc;
916    }
917
918    loop {
919        v = hmac_sha384(&k, &v);
920        if let Some(sc) = Scalar::from_bytes(&v) {
921            return sc;
922        }
923        rfc6979_retry(&mut k, &mut v);
924    }
925}
926
927fn parse_private_key(private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Scalar, EllipticCurveError> {
928    Scalar::from_bytes(private_key).ok_or(EllipticCurveError::InvalidKey)
929}
930
931fn parse_public_key(public_key: &[u8]) -> Result<AffinePoint, EllipticCurveError> {
932    AffinePoint::from_sec1_bytes(public_key).ok_or(EllipticCurveError::InvalidKey)
933}
934
935#[cfg(test)]
936fn derive_public_key_uncompressed(
937    private_key: &[u8; PRIVATE_KEY_SIZE],
938) -> Result<[u8; PUBLIC_KEY_UNCOMPRESSED_SIZE], EllipticCurveError> {
939    let scalar = parse_private_key(private_key)?;
940    let point = scalar_mul_generator(&scalar)
941        .to_affine()
942        .ok_or(EllipticCurveError::Unspecified)?;
943    Ok(point.to_uncompressed_bytes())
944}
945
946#[cfg(test)]
947fn derive_public_key_compressed(
948    private_key: &[u8; PRIVATE_KEY_SIZE],
949) -> Result<[u8; PUBLIC_KEY_COMPRESSED_SIZE], EllipticCurveError> {
950    let scalar = parse_private_key(private_key)?;
951    let point = scalar_mul_generator(&scalar)
952        .to_affine()
953        .ok_or(EllipticCurveError::Unspecified)?;
954    Ok(point.to_compressed_bytes())
955}
956
957fn ecdh_inner(scalar: &Scalar, peer_point: &AffinePoint) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
958    let shared_point = scalar_mul_affine(peer_point, scalar)
959        .to_affine()
960        .ok_or(EllipticCurveError::Unspecified)?;
961    Ok(shared_point.x.to_bytes())
962}
963
964pub fn ecdh(
965    private_key: &[u8; PRIVATE_KEY_SIZE],
966    peer_public_key: &[u8],
967) -> Result<[u8; ECDH_SHARED_SECRET_SIZE], EllipticCurveError> {
968    let scalar = parse_private_key(private_key)?;
969    let peer_point = parse_public_key(peer_public_key)?;
970    ecdh_inner(&scalar, &peer_point)
971}
972
973fn ecdsa_sign_inner(scalar: &Scalar, message: &[u8]) -> Result<[u8; SIGNATURE_SIZE], EllipticCurveError> {
974    let message_hash = hash_message(message);
975    let z = Scalar::from_hash(&message_hash);
976
977    for _ in 0..2 {
978        let k = rfc6979_generate_k(scalar, &message_hash);
979
980        let r_point = scalar_mul_generator(&k)
981            .to_affine()
982            .ok_or(EllipticCurveError::Unspecified)?;
983        let r = Scalar::from_hash(&r_point.x.to_bytes());
984        if r.is_zero() {
985            continue;
986        }
987
988        let kinv = k.invert().ok_or(EllipticCurveError::Unspecified)?;
989        let s = kinv.mul(z.add(r.mul(*scalar)));
990        if s.is_zero() {
991            continue;
992        }
993
994        let mut out = [0u8; SIGNATURE_SIZE];
995        out[..48].copy_from_slice(&r.to_bytes());
996        out[48..].copy_from_slice(&s.to_bytes());
997        return Ok(out);
998    }
999
1000    Err(EllipticCurveError::Unspecified)
1001}
1002
1003fn ecdsa_verify_inner(
1004    public_point: &AffinePoint,
1005    message: &[u8],
1006    signature: &[u8; SIGNATURE_SIZE],
1007) -> Result<(), EllipticCurveError> {
1008    let r = Scalar::from_bytes(signature[..48].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
1009    let s = Scalar::from_bytes(signature[48..].try_into().unwrap()).ok_or(EllipticCurveError::Unspecified)?;
1010    let z = Scalar::from_hash(&hash_message(message));
1011
1012    let w = s.invert().ok_or(EllipticCurveError::Unspecified)?;
1013    let u1 = z.mul(w);
1014    let u2 = r.mul(w);
1015
1016    let point = scalar_mul_generator(&u1).add(&scalar_mul_affine(public_point, &u2));
1017    let affine = point.to_affine().ok_or(EllipticCurveError::Unspecified)?;
1018    let x_mod_n = Scalar::from_hash(&affine.x.to_bytes());
1019
1020    if x_mod_n == r {
1021        Ok(())
1022    } else {
1023        Err(EllipticCurveError::Unspecified)
1024    }
1025}
1026
1027pub fn is_valid_public_key(public_key: &[u8]) -> bool {
1028    AffinePoint::from_sec1_bytes(public_key).is_some()
1029}
1030
1031#[cfg(test)]
1032mod tests {
1033    use super::*;
1034
1035    fn decode_hex<const N: usize>(hex_bytes: &str) -> [u8; N] {
1036        let bytes = hex::decode(hex_bytes).unwrap();
1037        assert_eq!(bytes.len(), N);
1038        let mut out = [0u8; N];
1039        out.copy_from_slice(&bytes);
1040        out
1041    }
1042
1043    fn der_read_tlv<'a>(data: &'a [u8], offset: &mut usize) -> Option<(u8, &'a [u8])> {
1044        if *offset >= data.len() {
1045            return None;
1046        }
1047        let tag = data[*offset];
1048        *offset += 1;
1049        if *offset >= data.len() {
1050            return None;
1051        }
1052        let len_byte = data[*offset];
1053        *offset += 1;
1054        let (len, _) = if len_byte & 0x80 != 0 {
1055            let num_bytes = (len_byte & 0x7f) as usize;
1056            if num_bytes == 0 || num_bytes > core::mem::size_of::<usize>() || *offset + num_bytes > data.len() {
1057                return None;
1058            }
1059            if num_bytes > 1 && data[*offset] == 0 {
1060                return None;
1061            }
1062            let mut l = 0usize;
1063            for i in 0..num_bytes {
1064                l = (l << 8) | data[*offset + i] as usize;
1065            }
1066            if l < 128 {
1067                return None;
1068            }
1069            *offset += num_bytes;
1070            (l, num_bytes + 1)
1071        } else {
1072            (len_byte as usize, 1)
1073        };
1074        if (*offset).checked_add(len).map_or(true, |sum| sum > data.len()) {
1075            return None;
1076        }
1077        let value = &data[*offset..*offset + len];
1078        *offset = (*offset).checked_add(len)?;
1079        Some((tag, value))
1080    }
1081
1082    fn der_ecdsa_sig_to_p1363(der: &[u8]) -> Option<[u8; 96]> {
1083        let mut offset = 0;
1084        let (tag, inner) = der_read_tlv(der, &mut offset)?;
1085        if tag != 0x30 {
1086            return None;
1087        }
1088        if offset != der.len() {
1089            return None;
1090        }
1091        let mut inner_offset = 0;
1092        let (rtag, rval) = der_read_tlv(inner, &mut inner_offset)?;
1093        if rtag != 0x02 || rval.is_empty() || rval.len() > 49 {
1094            return None;
1095        }
1096        let (stag, sval) = der_read_tlv(inner, &mut inner_offset)?;
1097        if stag != 0x02 || sval.is_empty() || sval.len() > 49 {
1098            return None;
1099        }
1100        if inner_offset != inner.len() {
1101            return None;
1102        }
1103        let r_valid = if rval.len() == 48 && rval[0] >= 0x80 {
1104            false
1105        } else if rval.len() == 49 && rval[0] != 0 {
1106            false
1107        } else if rval.len() == 49 && rval[0] == 0 && rval[1] < 0x80 {
1108            false
1109        } else if rval.len() > 49 {
1110            false
1111        } else {
1112            true
1113        };
1114        let s_valid = if sval.len() == 48 && sval[0] >= 0x80 {
1115            false
1116        } else if sval.len() == 49 && sval[0] != 0 {
1117            false
1118        } else if sval.len() == 49 && sval[0] == 0 && sval[1] < 0x80 {
1119            false
1120        } else if sval.len() > 49 {
1121            false
1122        } else {
1123            true
1124        };
1125        if !r_valid || !s_valid {
1126            return None;
1127        }
1128
1129        let r_trimmed = if rval.len() == 49 && rval[0] == 0 {
1130            &rval[1..]
1131        } else {
1132            rval
1133        };
1134        let s_trimmed = if sval.len() == 49 && sval[0] == 0 {
1135            &sval[1..]
1136        } else {
1137            sval
1138        };
1139        if r_trimmed.len() > 48 || s_trimmed.len() > 48 {
1140            return None;
1141        }
1142        let mut sig = [0u8; 96];
1143        sig[48 - r_trimmed.len()..48].copy_from_slice(r_trimmed);
1144        sig[96 - s_trimmed.len()..96].copy_from_slice(s_trimmed);
1145        Some(sig)
1146    }
1147
1148    fn spki_to_sec1_point(spki: &[u8]) -> Option<Vec<u8>> {
1149        let ec_public_key_oid: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
1150        let secp384r1_oid: &[u8] = &[0x2b, 0x81, 0x04, 0x00, 0x22];
1151        let mut offset = 0;
1152        let (_tag, outer) = der_read_tlv(spki, &mut offset)?;
1153        let mut inner = 0;
1154        let (_alg_tag, alg_content) = der_read_tlv(outer, &mut inner)?;
1155        if _alg_tag != 0x30 {
1156            return None;
1157        }
1158        let mut ai = 0;
1159        let (oid1_tag, oid1) = der_read_tlv(alg_content, &mut ai)?;
1160        if oid1_tag != 0x06 || oid1 != ec_public_key_oid {
1161            return None;
1162        }
1163        let (oid2_tag, oid2) = der_read_tlv(alg_content, &mut ai)?;
1164        if oid2_tag != 0x06 || oid2 != secp384r1_oid {
1165            return None;
1166        }
1167        let (_bs_tag, bs_val) = der_read_tlv(outer, &mut inner)?;
1168        if _bs_tag != 0x03 || bs_val.is_empty() {
1169            return None;
1170        }
1171        Some(bs_val[1..].to_vec())
1172    }
1173
1174    #[test]
1175    fn derive_public_key_generator_matches_sec1_base_point() {
1176        let mut private_key = [0u8; 48];
1177        private_key[47] = 1;
1178        let derived = derive_public_key_uncompressed(&private_key).unwrap();
1179        let expected = decode_hex::<97>(
1180            "04aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1181             5502f25dbf55296c3a545e3872760ab73617de4a96262c6f5d9e98bf9292dc29\
1182             f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
1183        );
1184        assert_eq!(derived, expected);
1185    }
1186
1187    #[test]
1188    fn ecdsa_verify_accepts_compressed_and_uncompressed_public_keys() {
1189        let private_key = decode_hex::<48>(
1190            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1191             96d5724e4c70a825f872c9ea60d2edf5",
1192        );
1193        let key = PrivateKey::from_bytes(&private_key).unwrap();
1194        let uncompressed = key.public_key();
1195        let compressed = derive_public_key_compressed(&private_key).unwrap();
1196        let signature = key.sign(b"sample").unwrap();
1197
1198        assert!(uncompressed.verify(b"sample", &signature).is_ok());
1199        let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1200        assert!(ecdsa_verify_inner(&point, b"sample", &signature).is_ok());
1201    }
1202
1203    #[test]
1204    fn invalid_inputs_are_rejected() {
1205        let invalid_private_key = [0u8; PRIVATE_KEY_SIZE];
1206        assert!(PrivateKey::from_bytes(&invalid_private_key).is_err());
1207        assert!(derive_public_key_uncompressed(&invalid_private_key).is_err());
1208        assert!(derive_public_key_compressed(&invalid_private_key).is_err());
1209
1210        let private_key = decode_hex::<48>(
1211            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1212             96d5724e4c70a825f872c9ea60d2edf5",
1213        );
1214        let key = PrivateKey::from_bytes(&private_key).unwrap();
1215        let signature = key.sign(b"msg").unwrap();
1216        let mut zero_r = signature;
1217        zero_r[..48].fill(0);
1218        assert!(key.public_key().verify(b"msg", &zero_r).is_err());
1219    }
1220
1221    #[test]
1222    fn public_key_validation_accepts_known_good_points() {
1223        assert!(is_valid_public_key(&decode_hex::<97>(
1224            "04aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1225             5502f25dbf55296c3a545e3872760ab73617de4a96262c6f5d9e98bf9292dc29\
1226             f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f",
1227        )));
1228    }
1229
1230    #[test]
1231    fn ecdsa_sign_verify_round_trip_multiple_messages() {
1232        let private_key = decode_hex::<48>(
1233            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1234             96d5724e4c70a825f872c9ea60d2edf5",
1235        );
1236        let key = PrivateKey::from_bytes(&private_key).unwrap();
1237        let pub_key = key.public_key();
1238
1239        let messages: &[&[u8]] = &[
1240            b"",
1241            b"hello world",
1242            b"The quick brown fox jumps over the lazy dog",
1243            &[0u8; 0],
1244            &[0xffu8; 100],
1245            b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f",
1246        ];
1247
1248        for msg in messages {
1249            let sig = key.sign(msg).unwrap();
1250            assert!(pub_key.verify(msg, &sig).is_ok(), "round-trip failed for message {:?}", msg);
1251            let mut wrong_msg = msg.to_vec();
1252            wrong_msg.push(0x42);
1253            assert!(pub_key.verify(&wrong_msg, &sig).is_err());
1254        }
1255    }
1256
1257    #[test]
1258    fn ecdsa_sign_verify_different_keys() {
1259        let keys: &[&str] = &[
1260            "0000000000000000000000000000000000000000000000000000000000000000\
1261             00000000000000000000000000000001",
1262            "0000000000000000000000000000000000000000000000000000000000000000\
1263             00000000000000000000000000000002",
1264            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011\
1265             2233445566778899aabbccddeeff0011",
1266        ];
1267
1268        for key_hex in keys {
1269            let private_key = decode_hex::<48>(key_hex);
1270            let key = PrivateKey::from_bytes(&private_key).unwrap();
1271            let sig = key.sign(b"test message").unwrap();
1272            assert!(
1273                key.public_key().verify(b"test message", &sig).is_ok(),
1274                "sign/verify failed for key {}",
1275                key_hex
1276            );
1277        }
1278    }
1279
1280    #[test]
1281    fn ecdsa_verify_wrong_public_key_rejects() {
1282        let private_key1 = decode_hex::<48>(
1283            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1284             96d5724e4c70a825f872c9ea60d2edf5",
1285        );
1286        let private_key2 = decode_hex::<48>(
1287            "0000000000000000000000000000000000000000000000000000000000000000\
1288             00000000000000000000000000000001",
1289        );
1290        let key1 = PrivateKey::from_bytes(&private_key1).unwrap();
1291        let key2 = PrivateKey::from_bytes(&private_key2).unwrap();
1292
1293        let sig = key1.sign(b"message").unwrap();
1294        assert!(key2.public_key().verify(b"message", &sig).is_err());
1295    }
1296
1297    #[test]
1298    fn scalar_from_bytes_rejects_boundary_values() {
1299        let zero = [0u8; 48];
1300        assert!(Scalar::from_bytes(&zero).is_none());
1301
1302        let n_bytes = decode_hex::<48>(
1303            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1304             581a0db248b0a77aecec196accc52973",
1305        );
1306        assert!(Scalar::from_bytes(&n_bytes).is_none());
1307
1308        let n_minus_1 = decode_hex::<48>(
1309            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1310             581a0db248b0a77aecec196accc52972",
1311        );
1312        assert!(Scalar::from_bytes(&n_minus_1).is_some());
1313
1314        let one = decode_hex::<48>(
1315            "0000000000000000000000000000000000000000000000000000000000000000\
1316             00000000000000000000000000000001",
1317        );
1318        assert!(Scalar::from_bytes(&one).is_some());
1319    }
1320
1321    #[test]
1322    fn field_element_from_bytes_rejects_boundary_values() {
1323        let p_bytes = decode_hex::<48>(
1324            "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff",
1325        );
1326        assert!(FieldElement::from_bytes(&p_bytes).is_none());
1327
1328        let p_minus_1 = decode_hex::<48>(
1329            "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffe",
1330        );
1331        assert!(FieldElement::from_bytes(&p_minus_1).is_some());
1332
1333        let zero = [0u8; 48];
1334        assert!(FieldElement::from_bytes(&zero).is_some());
1335    }
1336
1337    #[test]
1338    fn point_decompression_round_trip() {
1339        let keys: &[&str] = &[
1340            "0000000000000000000000000000000000000000000000000000000000000000\
1341             00000000000000000000000000000001",
1342            "0000000000000000000000000000000000000000000000000000000000000000\
1343             00000000000000000000000000000002",
1344            "6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
1345             96d5724e4c70a825f872c9ea60d2edf5",
1346            "a0b1c2d3e4f5a6b7c8d9e0f1a2b3c4d5e6f7a8b9c0d1e2f3a4b5c6d7e8f90011\
1347             2233445566778899aabbccddeeff0011",
1348        ];
1349
1350        for key_hex in keys {
1351            let private_key = decode_hex::<48>(key_hex);
1352            let key = PrivateKey::from_bytes(&private_key).unwrap();
1353            let uncompressed = key.public_key();
1354            let compressed = derive_public_key_compressed(&private_key).unwrap();
1355
1356            let sig = key.sign(b"round-trip").unwrap();
1357            assert!(uncompressed.verify(b"round-trip", &sig).is_ok());
1358            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1359            assert!(ecdsa_verify_inner(&point, b"round-trip", &sig).is_ok());
1360
1361            let point = AffinePoint::from_sec1_bytes(&compressed).unwrap();
1362            assert_eq!(point.to_uncompressed_bytes(), uncompressed.to_bytes());
1363        }
1364    }
1365
1366    #[test]
1367    fn scalar_inversion_correctness() {
1368        let k = Scalar::from_bytes(&decode_hex::<48>(
1369            "c22b201cc45cd130ef80acfc70e84fa17b91b0ffbfe4c9c44eda37e1ad1d7f8f\
1370             ae4c4c8b52559930e08ba1c822c105b0",
1371        ))
1372        .unwrap();
1373        let k_inv = k.invert().unwrap();
1374        let product = k.mul(k_inv);
1375        assert_eq!(product, Scalar::ONE);
1376    }
1377
1378    #[test]
1379    fn field_element_inversion_correctness() {
1380        let x = FieldElement::from_bytes(&decode_hex::<48>(
1381            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1382             5502f25dbf55296c3a545e3872760ab7",
1383        ))
1384        .unwrap();
1385        let x_inv = x.invert().unwrap();
1386        let product = x.mul(x_inv);
1387        assert_eq!(product, FieldElement::ONE);
1388    }
1389
1390    #[test]
1391    fn generator_point_is_on_curve() {
1392        assert!(AffinePoint::GENERATOR.is_on_curve());
1393    }
1394
1395    #[test]
1396    fn p384_fast_mul_mod_matches_generic() {
1397        for _ in 0..1000 {
1398            let a_bytes: [u8; 48] = rand::random();
1399            let b_bytes: [u8; 48] = rand::random();
1400            let a_opt = FieldElement::from_bytes(&a_bytes);
1401            let b_opt = FieldElement::from_bytes(&b_bytes);
1402            if a_opt.is_none() || b_opt.is_none() {
1403                continue;
1404            }
1405            let a = a_opt.unwrap();
1406            let b = b_opt.unwrap();
1407            let expected = U384::from_limbs({
1408                let mut p = [0u64; 12];
1409                for i in 0..6 {
1410                    let mut c = 0u64;
1411                    for j in 0..6 {
1412                        let (v, cc) = mac(p[i + j], a.0.limbs[i], b.0.limbs[j], c);
1413                        p[i + j] = v;
1414                        c = cc;
1415                    }
1416                    p[i + 6] = c;
1417                }
1418                let mut rem = [0u64; 6];
1419                for bi in (0..768).rev() {
1420                    let li = bi / 64;
1421                    let pi = bi % 64;
1422                    let bit = ((p[li] >> pi) & 1) as u64;
1423                    let mut shifted = [0u64; 6];
1424                    let mut carry = bit;
1425                    for j in 0..6 {
1426                        let next = rem[j] >> 63;
1427                        shifted[j] = (rem[j] << 1) | carry;
1428                        carry = next;
1429                    }
1430                    let (red, br) = U384::from_limbs(shifted).sub_raw(&MODULUS_P);
1431                    if carry == 1 || br == 0 {
1432                        rem = red.limbs;
1433                    } else {
1434                        rem = shifted;
1435                    }
1436                }
1437                rem
1438            });
1439            let fast = p384_fast_mul_mod(&a.0, &b.0);
1440            assert_eq!(expected, fast, "mismatch in p384_fast_mul_mod");
1441        }
1442    }
1443
1444    #[test]
1445    fn scalar_mul_generator_n_gives_identity() {
1446        let n_minus_1 = Scalar::from_bytes(&decode_hex::<48>(
1447            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf\
1448             581a0db248b0a77aecec196accc52972",
1449        ))
1450        .unwrap();
1451        let result = scalar_mul_generator(&n_minus_1).to_affine().unwrap();
1452        assert_eq!(result.x, GENERATOR_X);
1453        let neg_gy = GENERATOR_Y.negate();
1454        assert_eq!(result.y, neg_gy);
1455    }
1456
1457    #[test]
1458    fn ecdh_round_trip_alice_bob() {
1459        let alice = PrivateKey::generate().unwrap();
1460        let bob = PrivateKey::generate().unwrap();
1461
1462        let alice_shared = alice.ecdh(&bob.public_key()).unwrap();
1463        let bob_shared = bob.ecdh(&alice.public_key()).unwrap();
1464
1465        assert_eq!(alice_shared, bob_shared);
1466        assert_eq!(alice_shared.len(), ECDH_SHARED_SECRET_SIZE);
1467    }
1468
1469    #[test]
1470    fn ecdh_rejects_off_curve_peer_public_key() {
1471        let alice = PrivateKey::generate().unwrap();
1472        let mut bad_pub = alice.public_key().to_bytes().to_vec();
1473        bad_pub[96] ^= 0x01;
1474        assert!(!is_valid_public_key(&bad_pub));
1475        assert!(ecdh(&alice.to_bytes(), &bad_pub).is_err());
1476    }
1477
1478    #[test]
1479    fn ecdh_rejects_infinity_peer_public_key() {
1480        let alice = PrivateKey::generate().unwrap();
1481        let infinity = [0x00u8];
1482        assert!(ecdh(&alice.to_bytes(), &infinity).is_err());
1483    }
1484
1485    #[test]
1486    fn ecdh_rejects_bad_length_peer_public_key() {
1487        let alice = PrivateKey::generate().unwrap();
1488        assert!(ecdh(&alice.to_bytes(), &[]).is_err());
1489        assert!(ecdh(&alice.to_bytes(), &[0x04, 0x00]).is_err());
1490        let mut long = [0x04u8; 200];
1491        long[0] = 0x04;
1492        assert!(ecdh(&alice.to_bytes(), &long).is_err());
1493    }
1494
1495    #[test]
1496    fn ecdh_rejects_invalid_private_key_zero() {
1497        let zero_key = [0u8; 48];
1498        assert!(PrivateKey::from_bytes(&zero_key).is_err());
1499        let bob = PrivateKey::generate().unwrap();
1500        assert!(ecdh(&zero_key, &bob.public_key().to_bytes()).is_err());
1501    }
1502
1503    #[test]
1504    fn ecdh_multiple_exchanges_consistency() {
1505        let alice = PrivateKey::generate().unwrap();
1506        let bob = PrivateKey::generate().unwrap();
1507        let charlie = PrivateKey::generate().unwrap();
1508
1509        let alice_bob = alice.ecdh(&bob.public_key()).unwrap();
1510        let bob_alice = bob.ecdh(&alice.public_key()).unwrap();
1511        assert_eq!(alice_bob, bob_alice);
1512
1513        let alice_charlie = alice.ecdh(&charlie.public_key()).unwrap();
1514        let charlie_alice = charlie.ecdh(&alice.public_key()).unwrap();
1515        assert_eq!(alice_charlie, charlie_alice);
1516
1517        let bob_charlie = bob.ecdh(&charlie.public_key()).unwrap();
1518        let charlie_bob = charlie.ecdh(&bob.public_key()).unwrap();
1519        assert_eq!(bob_charlie, charlie_bob);
1520
1521        assert_ne!(alice_bob, alice_charlie);
1522        assert_ne!(alice_bob, bob_charlie);
1523        assert_ne!(alice_charlie, bob_charlie);
1524    }
1525
1526    #[test]
1527    fn ecdsa_rejects_non_canonical_r_and_s() {
1528        let key = PrivateKey::generate().unwrap();
1529        let valid_sig = key.sign(b"msg").unwrap();
1530
1531        let mut bad_r = valid_sig;
1532        bad_r[..48].copy_from_slice(&decode_hex::<48>(
1533            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52974",
1534        ));
1535        assert!(key.public_key().verify(b"msg", &bad_r).is_err());
1536
1537        let mut bad_s = valid_sig;
1538        bad_s[48..].copy_from_slice(&decode_hex::<48>(
1539            "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52974",
1540        ));
1541        assert!(key.public_key().verify(b"msg", &bad_s).is_err());
1542    }
1543
1544    #[test]
1545    fn verify_rejects_tampered_message_and_signature() {
1546        let key = PrivateKey::generate().unwrap();
1547        let pub_key = key.public_key();
1548        let sig = key.sign(b"message").unwrap();
1549
1550        assert!(pub_key.verify(b"tampered", &sig).is_err());
1551
1552        let mut bad_sig = sig;
1553        bad_sig[10] ^= 0x80;
1554        assert!(pub_key.verify(b"message", &bad_sig).is_err());
1555    }
1556
1557    #[test]
1558    fn public_key_rejects_off_curve_point() {
1559        let key = PrivateKey::generate().unwrap();
1560        let mut off_curve = key.public_key().to_bytes();
1561        off_curve[96] ^= 0x01;
1562        assert!(!is_valid_public_key(&off_curve));
1563        assert!(PublicKey::from_bytes(&off_curve).is_err());
1564    }
1565
1566    #[test]
1567    fn private_key_round_trip_bytes() {
1568        let key = PrivateKey::generate().unwrap();
1569        let bytes = key.to_bytes();
1570        let key2 = PrivateKey::from_bytes(&bytes).unwrap();
1571        assert_eq!(key.to_bytes(), key2.to_bytes());
1572        assert_eq!(key.public_key().to_bytes(), key2.public_key().to_bytes());
1573    }
1574
1575    #[test]
1576    fn public_key_round_trip_bytes() {
1577        let key = PrivateKey::generate().unwrap();
1578        let pub_key = key.public_key();
1579        let bytes = pub_key.to_bytes();
1580        let pub_key2 = PublicKey::from_bytes(&bytes).unwrap();
1581        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1582    }
1583
1584    #[test]
1585    fn x_y_round_trip() {
1586        let key = PrivateKey::generate().unwrap();
1587        let pub_key = key.public_key();
1588        let (x, y) = pub_key.x_y();
1589        let pub_key2 = PublicKey::from_x_y(&x, &y).unwrap();
1590        assert_eq!(pub_key.to_bytes(), pub_key2.to_bytes());
1591    }
1592
1593    #[test]
1594    fn from_x_y_matches_generator() {
1595        let key = PublicKey::from_x_y(
1596            &decode_hex::<48>(
1597                "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1598                 5502f25dbf55296c3a545e3872760ab7",
1599            ),
1600            &decode_hex::<48>(
1601                "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0\
1602                 0a60b1ce1d7e819d7a431d7c90ea0e5f",
1603            ),
1604        )
1605        .unwrap();
1606        let from_sec1 = PublicKey::from_bytes(&key.to_bytes()).unwrap();
1607        assert_eq!(key, from_sec1);
1608    }
1609
1610    #[test]
1611    fn from_x_y_rejects_off_curve() {
1612        assert!(PublicKey::from_x_y(&[0u8; 48], &[0u8; 48]).is_err());
1613    }
1614
1615    #[test]
1616    fn field_element_add_sub_mul_consistency() {
1617        let a = FieldElement::from_bytes(&decode_hex::<48>(
1618            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1619             5502f25dbf55296c3a545e3872760ab7",
1620        ))
1621        .unwrap();
1622        let b = FieldElement::from_bytes(&decode_hex::<48>(
1623            "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c0\
1624             0a60b1ce1d7e819d7a431d7c90ea0e5f",
1625        ))
1626        .unwrap();
1627
1628        assert_eq!(a.add(b).sub(b), a);
1629        assert_eq!(a.add(b), b.add(a));
1630        assert_eq!(a.mul(b), b.mul(a));
1631
1632        let c = FieldElement::from_bytes(&decode_hex::<48>(
1633            "2a85c8edd3ec2aefc656398d8a2ed19d0314088f5013875a181d9c6efe814112\
1634             988e056be3f82d19b3312fa7e23ee7e4",
1635        ))
1636        .unwrap();
1637        assert_eq!(a.add(b).mul(c), a.mul(c).add(b.mul(c)));
1638    }
1639
1640    #[test]
1641    fn scalar_add_sub_mul_consistency() {
1642        let a = Scalar::from_bytes(&decode_hex::<48>(
1643            "c22b201cc45cd130ef80acfc70e84fa17b91b0ffbfe4c9c44eda37e1ad1d7f8f\
1644             ae4c4c8b52559930e08ba1c822c105b0",
1645        ))
1646        .unwrap();
1647        let one = Scalar::from_bytes(&decode_hex::<48>(
1648            "0000000000000000000000000000000000000000000000000000000000000000\
1649             00000000000000000000000000000001",
1650        ))
1651        .unwrap();
1652
1653        assert_eq!(a.add(one).sub(one), a);
1654        assert_eq!(a.mul(one), a);
1655
1656        let b = Scalar::from_bytes(&decode_hex::<48>(
1657            "7cf1be7a45d8d72e6e974229bfad108f3d2d4aa6208248adb9343258e4f30f80\
1658             8252a11a87ddc7e0d8ba3b5e28878944",
1659        ))
1660        .unwrap();
1661        assert_eq!(a.mul(b), b.mul(a));
1662        assert_eq!(a.add(b), b.add(a));
1663    }
1664
1665    #[test]
1666    fn field_element_negate_round_trip() {
1667        let x = FieldElement::from_bytes(&decode_hex::<48>(
1668            "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a38\
1669             5502f25dbf55296c3a545e3872760ab7",
1670        ))
1671        .unwrap();
1672        let neg = x.negate();
1673        assert_eq!(neg.negate(), x);
1674        assert_eq!(x.add(neg), FieldElement::ZERO);
1675    }
1676
1677    #[test]
1678    fn point_double_and_add_consistency() {
1679        let g = AffinePoint::GENERATOR;
1680        let proj_g = ProjectivePoint::from_affine(&g);
1681        let doubled = proj_g.double();
1682        let added = proj_g.add(&proj_g);
1683        assert_eq!(
1684            doubled.to_affine().unwrap().to_uncompressed_bytes(),
1685            added.to_affine().unwrap().to_uncompressed_bytes(),
1686        );
1687    }
1688
1689    #[test]
1690    fn scalar_mul_by_two_matches_double() {
1691        let two = Scalar::from_bytes(&decode_hex::<48>(
1692            "0000000000000000000000000000000000000000000000000000000000000000\
1693             00000000000000000000000000000002",
1694        ))
1695        .unwrap();
1696        let g_times_2 = scalar_mul_affine(&AffinePoint::GENERATOR, &two).to_affine().unwrap();
1697        let proj_g = ProjectivePoint::from_affine(&AffinePoint::GENERATOR);
1698        let g_doubled = proj_g.double().to_affine().unwrap();
1699
1700        assert_eq!(g_times_2.to_uncompressed_bytes(), g_doubled.to_uncompressed_bytes());
1701    }
1702
1703    #[test]
1704    fn compressed_public_key_has_correct_prefix() {
1705        for _ in 0..5 {
1706            let key = PrivateKey::generate().unwrap();
1707            let compressed = derive_public_key_compressed(&key.to_bytes()).unwrap();
1708            let prefix = compressed[0];
1709            assert!(prefix == 0x02 || prefix == 0x03, "invalid compressed prefix: {prefix:#x}");
1710        }
1711    }
1712
1713    #[test]
1714    fn ecdsa_sign_then_verify_consistent_for_random_keys() {
1715        for _ in 0..5 {
1716            let key = PrivateKey::generate().unwrap();
1717            let msg = rand::random::<[u8; 32]>();
1718            let sig = key.sign(&msg).unwrap();
1719            assert!(key.public_key().verify(&msg, &sig).is_ok());
1720        }
1721    }
1722
1723    #[test]
1724    fn is_on_curve_accepts_generator_and_random_points() {
1725        assert!(AffinePoint::GENERATOR.is_on_curve());
1726        for _ in 0..5 {
1727            let key = PrivateKey::generate().unwrap();
1728            let pb = key.public_key().to_bytes();
1729            let pk = PublicKey::from_bytes(&pb).unwrap();
1730            let _ = pk;
1731        }
1732    }
1733
1734    #[test]
1735    fn ecdh_with_self_is_consistent() {
1736        let key = PrivateKey::generate().unwrap();
1737        let shared1 = key.ecdh(&key.public_key()).unwrap();
1738        let shared2 = key.ecdh(&key.public_key()).unwrap();
1739        assert_eq!(shared1, shared2);
1740    }
1741
1742    #[test]
1743    fn field_element_pow_correctness() {
1744        let x = FieldElement::from_bytes(&decode_hex::<48>(
1745            "0000000000000000000000000000000000000000000000000000000000000000\
1746             00000000000000000000000000000002",
1747        ))
1748        .unwrap();
1749        let x3 = x.pow(&U384::from_u64(3));
1750        let expected = x.mul(x).mul(x);
1751        assert_eq!(x3, expected);
1752
1753        let x0 = x.pow(&U384::ZERO);
1754        assert_eq!(x0, FieldElement::ONE);
1755    }
1756
1757    // --- Wycheproof test vectors ---
1758
1759    #[test]
1760    fn wycheproof_ecdsa_p384_sha384_p1363() {
1761        let data: serde_json::Value = serde_json::from_str(include_str!(
1762            "../testdata/wycheproof/testvectors_v1/ecdsa_secp384r1_sha384_p1363_test.json"
1763        ))
1764        .unwrap();
1765        let mut valid_tested = 0u64;
1766        let mut invalid_tested = 0u64;
1767        for group in data["testGroups"].as_array().unwrap() {
1768            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1769            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1770            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1771
1772            for test in group["tests"].as_array().unwrap() {
1773                let msg_hex = test["msg"].as_str().unwrap();
1774                let sig_hex = test["sig"].as_str().unwrap();
1775                let result = test["result"].as_str().unwrap();
1776
1777                let msg = hex::decode(msg_hex).unwrap();
1778
1779                if sig_hex.len() != SIGNATURE_SIZE * 2 {
1780                    continue;
1781                }
1782                let sig = decode_hex::<SIGNATURE_SIZE>(sig_hex);
1783
1784                let verify_result = pk.verify(&msg, &sig);
1785
1786                if result == "valid" {
1787                    assert!(
1788                        verify_result.is_ok(),
1789                        "wycheproof ECDSA P384 P1363 tcId={} expected valid but failed",
1790                        test["tcId"]
1791                    );
1792                    valid_tested += 1;
1793                } else {
1794                    assert!(
1795                        verify_result.is_err(),
1796                        "wycheproof ECDSA P384 P1363 tcId={} expected invalid but passed",
1797                        test["tcId"]
1798                    );
1799                    invalid_tested += 1;
1800                }
1801            }
1802        }
1803        assert!(valid_tested > 0, "no valid ECDSA P384 P1363 wycheproof tests were run");
1804        assert!(invalid_tested > 0, "no invalid ECDSA P384 P1363 wycheproof tests were run");
1805    }
1806
1807    #[test]
1808    fn wycheproof_ecdsa_p384_sha384_der() {
1809        let data: serde_json::Value = serde_json::from_str(include_str!(
1810            "../testdata/wycheproof/testvectors_v1/ecdsa_secp384r1_sha384_test.json"
1811        ))
1812        .unwrap();
1813        let mut valid_tested = 0u64;
1814        let mut invalid_tested = 0u64;
1815        for group in data["testGroups"].as_array().unwrap() {
1816            let uncompressed_hex = group["publicKey"]["uncompressed"].as_str().unwrap();
1817            let pubkey_bytes = hex::decode(uncompressed_hex).unwrap();
1818            let pk = PublicKey::from_bytes(&pubkey_bytes).unwrap();
1819
1820            for test in group["tests"].as_array().unwrap() {
1821                let msg_hex = test["msg"].as_str().unwrap();
1822                let sig_hex = test["sig"].as_str().unwrap();
1823                let result = test["result"].as_str().unwrap();
1824
1825                let msg = hex::decode(msg_hex).unwrap();
1826                let der_sig = hex::decode(sig_hex).unwrap();
1827                let Some(sig) = der_ecdsa_sig_to_p1363(&der_sig) else {
1828                    continue;
1829                };
1830
1831                let verify_result = pk.verify(&msg, &sig);
1832
1833                if result == "valid" {
1834                    assert!(
1835                        verify_result.is_ok(),
1836                        "wycheproof ECDSA P384 DER SHA-384 tcId={} expected valid but failed",
1837                        test["tcId"]
1838                    );
1839                    valid_tested += 1;
1840                } else {
1841                    assert!(
1842                        verify_result.is_err(),
1843                        "wycheproof ECDSA P384 DER SHA-384 tcId={} expected invalid but passed",
1844                        test["tcId"]
1845                    );
1846                    invalid_tested += 1;
1847                }
1848            }
1849        }
1850        assert!(valid_tested > 0, "no valid ECDSA P384 DER SHA-384 wycheproof tests were run");
1851        assert!(
1852            invalid_tested > 0,
1853            "no invalid ECDSA P384 DER SHA-384 wycheproof tests were run"
1854        );
1855    }
1856
1857    #[test]
1858    fn wycheproof_ecdh_p384_ecpoint() {
1859        let data: serde_json::Value = serde_json::from_str(include_str!(
1860            "../testdata/wycheproof/testvectors_v1/ecdh_secp384r1_ecpoint_test.json"
1861        ))
1862        .unwrap();
1863        let mut valid_tested = 0u64;
1864        let mut invalid_tested = 0u64;
1865        let mut acceptable_tested = 0u64;
1866        for group in data["testGroups"].as_array().unwrap() {
1867            if group["curve"].as_str() != Some("secp384r1") {
1868                continue;
1869            }
1870            for test in group["tests"].as_array().unwrap() {
1871                let public_hex = test["public"].as_str().unwrap();
1872                let private_hex = test["private"].as_str().unwrap();
1873                let expected_shared_hex = test["shared"].as_str().unwrap();
1874                let result = test["result"].as_str().unwrap();
1875
1876                let public_key = hex::decode(public_hex).unwrap();
1877
1878                let private_bytes = hex::decode(private_hex).unwrap();
1879                let mut private_key = [0u8; PRIVATE_KEY_SIZE];
1880                let effective_len = private_bytes.len().min(PRIVATE_KEY_SIZE);
1881                let skip = if private_bytes.len() > PRIVATE_KEY_SIZE {
1882                    private_bytes.len() - PRIVATE_KEY_SIZE
1883                } else {
1884                    0
1885                };
1886                private_key[PRIVATE_KEY_SIZE - effective_len..]
1887                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1888
1889                let shared = ecdh(&private_key, &public_key);
1890
1891                if result == "valid" {
1892                    let shared = shared.unwrap();
1893                    let shared_hex = hex::encode(shared);
1894                    assert_eq!(
1895                        shared_hex, expected_shared_hex,
1896                        "wycheproof ECDH P384 ecpoint tcId={}",
1897                        test["tcId"]
1898                    );
1899                    valid_tested += 1;
1900                } else if result == "invalid" {
1901                    assert!(
1902                        shared.is_err(),
1903                        "wycheproof ECDH P384 ecpoint tcId={} expected invalid but passed",
1904                        test["tcId"]
1905                    );
1906                    invalid_tested += 1;
1907                } else {
1908                    acceptable_tested += 1;
1909                }
1910            }
1911        }
1912        assert!(valid_tested > 0, "no valid ECDH P384 ecpoint wycheproof tests were run");
1913        assert!(invalid_tested > 0, "no invalid ECDH P384 ecpoint wycheproof tests were run");
1914        assert!(
1915            acceptable_tested > 0,
1916            "no acceptable ECDH P384 ecpoint wycheproof tests were run"
1917        );
1918    }
1919
1920    #[test]
1921    fn wycheproof_ecdh_p384_asn() {
1922        let data: serde_json::Value =
1923            serde_json::from_str(include_str!("../testdata/wycheproof/testvectors_v1/ecdh_secp384r1_test.json"))
1924                .unwrap();
1925        let mut valid_tested = 0u64;
1926        let mut invalid_tested = 0u64;
1927        let mut acceptable_tested = 0u64;
1928        for group in data["testGroups"].as_array().unwrap() {
1929            for test in group["tests"].as_array().unwrap() {
1930                let public_hex = test["public"].as_str().unwrap();
1931                let private_hex = test["private"].as_str().unwrap();
1932                let expected_shared_hex = test["shared"].as_str().unwrap();
1933                let result = test["result"].as_str().unwrap();
1934
1935                let spki_der = hex::decode(public_hex).unwrap();
1936                let Some(sec1_point) = spki_to_sec1_point(&spki_der) else {
1937                    if result == "valid" {
1938                        panic!("wycheproof ECDH P384 ASN tcId={}: failed to parse valid SPKI", test["tcId"]);
1939                    }
1940                    invalid_tested += 1;
1941                    continue;
1942                };
1943
1944                let private_bytes = hex::decode(private_hex).unwrap();
1945                let mut private_key = [0u8; PRIVATE_KEY_SIZE];
1946                let effective_len = private_bytes.len().min(PRIVATE_KEY_SIZE);
1947                let skip = if private_bytes.len() > PRIVATE_KEY_SIZE {
1948                    private_bytes.len() - PRIVATE_KEY_SIZE
1949                } else {
1950                    0
1951                };
1952                private_key[PRIVATE_KEY_SIZE - effective_len..]
1953                    .copy_from_slice(&private_bytes[skip..skip + effective_len]);
1954
1955                let shared = ecdh(&private_key, &sec1_point);
1956
1957                if result == "valid" {
1958                    let shared = shared.unwrap();
1959                    let shared_hex = hex::encode(shared);
1960                    assert_eq!(
1961                        shared_hex, expected_shared_hex,
1962                        "wycheproof ECDH P384 ASN tcId={}",
1963                        test["tcId"]
1964                    );
1965                    valid_tested += 1;
1966                } else if result == "invalid" {
1967                    assert!(
1968                        shared.is_err(),
1969                        "wycheproof ECDH P384 ASN tcId={} expected invalid but passed",
1970                        test["tcId"]
1971                    );
1972                    invalid_tested += 1;
1973                } else {
1974                    acceptable_tested += 1;
1975                }
1976            }
1977        }
1978        assert!(valid_tested > 0, "no valid ECDH P384 ASN wycheproof tests were run");
1979        assert!(invalid_tested > 0, "no invalid ECDH P384 ASN wycheproof tests were run");
1980        assert!(acceptable_tested > 0, "no acceptable ECDH P384 ASN wycheproof tests were run");
1981    }
1982}