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