Skip to main content

cryptography/public_key/
ecdsa.rs

1//! Elliptic-Curve Digital Signature Algorithm (ECDSA, FIPS 186-5).
2//!
3//! ECDSA is the elliptic-curve analogue of DSA: rather than computing scalar
4//! multiplications in a prime subgroup of `Z_p^*`, it uses the group of points
5//! on a short-Weierstrass elliptic curve and produces the same `(r, s)`
6//! signature shape.
7//!
8//! ## Algorithm summary
9//!
10//! **Key generation**: Choose a named curve with generator `G` and prime order
11//! `n`.  Sample a uniform random scalar `d ∈ [1, n)` and set `Q = d·G`.
12//! The public key is `(curve, Q)`; the private key is `(curve, d)`.
13//!
14//! **Signing** (given message digest representative `z` and nonce `k ∈ [1, n)`):
15//! 1. Compute `(x₁, y₁) = k·G`.
16//! 2. Set `r = x₁ mod n`.  If `r = 0`, retry with a new `k`.
17//! 3. Set `s = k⁻¹ · (z + r·d) mod n`.  If `s = 0`, retry.
18//!
19//! **Verification** (given public key `Q`, representative `z`, signature `(r, s)`):
20//! 1. Check `r, s ∈ [1, n)`.
21//! 2. Compute `w = s⁻¹ mod n`, `u₁ = z·w mod n`, `u₂ = r·w mod n`.
22//! 3. Compute `(x₁, y₁) = u₁·G + u₂·Q`.
23//! 4. Accept if and only if `r ≡ x₁ (mod n)`.
24//!
25//! ## Side-channel note
26//!
27//! The scalar multiplication in [`ec`] is not constant-time (left-to-right
28//! double-and-add, branching on secret bits).  This implementation is
29//! suitable for educational and experimental use.  Replace the scalar-mul
30//! loop with a Montgomery ladder before deploying in an environment with
31//! side-channel adversaries.
32//!
33//! [`ec`]: crate::public_key::ec
34
35use core::fmt;
36
37use crate::hash::Digest;
38use crate::public_key::bigint::BigUint;
39use crate::public_key::ec::{AffinePoint, CurveParams};
40use crate::public_key::io::{
41    decode_biguints, encode_biguints, pem_unwrap, pem_wrap, xml_unwrap, xml_wrap,
42};
43use crate::public_key::primes::{mod_inverse, random_nonzero_below};
44use crate::Csprng;
45use crate::Hmac;
46
47const ECDSA_PUBLIC_LABEL: &str = "CRYPTOGRAPHY ECDSA PUBLIC KEY";
48const ECDSA_PRIVATE_LABEL: &str = "CRYPTOGRAPHY ECDSA PRIVATE KEY";
49
50// ─── Key and signature types ─────────────────────────────────────────────────
51
52/// Public key for ECDSA.
53///
54/// Stores the curve parameters and the public point `Q = d·G`.
55#[derive(Clone, Debug)]
56pub struct EcdsaPublicKey {
57    /// Full short-Weierstrass curve parameters for this key.
58    curve: CurveParams,
59    /// Public point `Q = d·G`.
60    q: AffinePoint,
61}
62
63/// Private key for ECDSA.
64///
65/// Stores the curve parameters and the secret scalar `d ∈ [1, n)`.
66/// The matching public key is derived on demand via [`to_public_key`].
67///
68/// [`to_public_key`]: EcdsaPrivateKey::to_public_key
69#[derive(Clone)]
70pub struct EcdsaPrivateKey {
71    /// Full short-Weierstrass curve parameters for this key.
72    curve: CurveParams,
73    /// Secret scalar `d ∈ [1, n)`.
74    d: BigUint,
75    /// Cached public point `Q = d·G`.
76    q: AffinePoint,
77}
78
79/// Raw ECDSA signature pair `(r, s)`.
80///
81/// Both components are positive integers in `[1, n)` relative to the subgroup
82/// order of the signing curve.  The serialized form is a DER `SEQUENCE` of
83/// two `INTEGER` values, matching the shape used by [`DsaSignature`].
84///
85/// [`DsaSignature`]: crate::public_key::dsa::DsaSignature
86#[derive(Clone, Debug, Eq, PartialEq)]
87pub struct EcdsaSignature {
88    r: BigUint,
89    s: BigUint,
90}
91
92pub struct Ecdsa;
93
94// ─── EcdsaPublicKey ───────────────────────────────────────────────────────────
95
96impl EcdsaPublicKey {
97    /// The curve parameters for this key.
98    #[must_use]
99    pub fn curve(&self) -> &CurveParams {
100        &self.curve
101    }
102
103    /// The public point `Q = d·G`.
104    #[must_use]
105    pub fn public_point(&self) -> &AffinePoint {
106        &self.q
107    }
108
109    /// Encode the public point as a compact SEC 1 point string.
110    #[must_use]
111    pub fn to_wire_bytes(&self) -> Vec<u8> {
112        self.curve.encode_point(&self.q)
113    }
114
115    /// Rebuild a public key from a compact SEC 1 point string plus explicit curve parameters.
116    #[must_use]
117    pub fn from_wire_bytes(curve: CurveParams, bytes: &[u8]) -> Option<Self> {
118        let q = curve.decode_point(bytes)?;
119        // Same subgroup check as from_key_blob: reject torsion points on
120        // curves with h > 1 (e.g. binary curves where h = 2).
121        if !curve.scalar_mul(&q, &curve.n).is_infinity() {
122            return None;
123        }
124        Some(Self { curve, q })
125    }
126
127    /// Convenience: hashes `message` with `H` then calls [`verify`][Self::verify].
128    #[must_use]
129    pub fn verify_message<H: Digest>(&self, message: &[u8], signature: &EcdsaSignature) -> bool {
130        let digest = H::digest(message);
131        self.verify(&digest, signature)
132    }
133
134    /// Convenience: hashes `message` with `H` then calls [`verify_bytes`][Self::verify_bytes].
135    #[must_use]
136    pub fn verify_message_bytes<H: Digest>(&self, message: &[u8], signature: &[u8]) -> bool {
137        let digest = H::digest(message);
138        self.verify_bytes(&digest, signature)
139    }
140
141    /// Verify `signature` over a raw digest byte string.
142    ///
143    /// The digest is reduced to a scalar representative matching FIPS 186-5
144    /// (leftmost `bits(n)` bits of the hash output).
145    #[must_use]
146    pub fn verify(&self, digest: &[u8], signature: &EcdsaSignature) -> bool {
147        let z = digest_to_scalar(digest, &self.curve.n);
148        self.verify_digest_scalar(&z, signature)
149    }
150
151    /// Core ECDSA verification over a pre-reduced scalar `z`.
152    #[must_use]
153    pub fn verify_digest_scalar(&self, hash: &BigUint, signature: &EcdsaSignature) -> bool {
154        let n = &self.curve.n;
155
156        // Both components must lie in [1, n).
157        if signature.r.is_zero() || signature.s.is_zero() || &signature.r >= n || &signature.s >= n
158        {
159            return false;
160        }
161
162        // Reject high-s to prevent malleability: for any valid (r, s), the
163        // pair (r, n-s) also satisfies the verification equation, so without
164        // this check two distinct byte strings verify for the same message.
165        // Matches the low-s enforcement in sign_digest_with_nonce.
166        let mut half_n = n.clone();
167        half_n.shr1();
168        if signature.s.cmp(&half_n).is_gt() {
169            return false;
170        }
171
172        let Some(w) = mod_inverse(&signature.s, n) else {
173            return false;
174        };
175
176        // u₁ = z·w mod n,  u₂ = r·w mod n
177        let u1 = BigUint::mod_mul(hash, &w, n);
178        let u2 = BigUint::mod_mul(&signature.r, &w, n);
179
180        // (x₁, y₁) = u₁·G + u₂·Q
181        let g = self.curve.base_point();
182        let term1 = self.curve.scalar_mul(&g, &u1);
183        let term2 = self.curve.scalar_mul(&self.q, &u2);
184        let sum = self.curve.add(&term1, &term2);
185
186        if sum.is_infinity() {
187            return false;
188        }
189
190        // Accept iff r ≡ x₁ (mod n).
191        sum.x.modulo(n) == signature.r
192    }
193
194    /// Verify a byte-encoded signature produced by [`EcdsaPrivateKey::sign_bytes`].
195    #[must_use]
196    pub fn verify_bytes(&self, digest: &[u8], signature: &[u8]) -> bool {
197        let Some(sig) = EcdsaSignature::from_key_blob(signature) else {
198            return false;
199        };
200        self.verify(digest, &sig)
201    }
202
203    /// Encode the public key in the crate-defined binary format.
204    ///
205    /// Layout: one field-type byte (`0x00` = prime, `0x01` = binary) followed
206    /// by `[p, a, b, n, h, Gx, Gy, Qx, Qy]` as a DER `SEQUENCE` of positive
207    /// `INTEGER`s.
208    #[must_use]
209    pub fn to_key_blob(&self) -> Vec<u8> {
210        let h = BigUint::from_u64(self.curve.h);
211        let field_byte = u8::from(self.curve.gf2m_degree().is_some());
212        let mut out = vec![field_byte];
213        out.extend_from_slice(&encode_biguints(&[
214            &self.curve.p,
215            &self.curve.a,
216            &self.curve.b,
217            &self.curve.n,
218            &h,
219            &self.curve.gx,
220            &self.curve.gy,
221            &self.q.x,
222            &self.q.y,
223        ]));
224        out
225    }
226
227    /// Decode a public key from the crate-defined binary format.
228    #[must_use]
229    pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
230        let (&field_type, rest) = blob.split_first()?;
231        let mut fields = decode_biguints(rest)?.into_iter();
232        let field_prime = fields.next()?;
233        let curve_a = fields.next()?;
234        let curve_b = fields.next()?;
235        let subgroup_order = fields.next()?;
236        let cofactor_big = fields.next()?;
237        let base_x = fields.next()?;
238        let base_y = fields.next()?;
239        let public_x = fields.next()?;
240        let public_y = fields.next()?;
241        if fields.next().is_some() {
242            return None;
243        }
244        let cofactor = biguint_to_u64(&cofactor_big)?;
245        let curve = if field_type == 0x01 {
246            let field_degree = field_prime.bits().checked_sub(1)?;
247            CurveParams::new_binary(
248                field_prime,
249                field_degree,
250                curve_a,
251                curve_b,
252                subgroup_order,
253                cofactor,
254                (base_x, base_y),
255            )?
256        } else {
257            CurveParams::new(
258                field_prime,
259                curve_a,
260                curve_b,
261                subgroup_order,
262                cofactor,
263                base_x,
264                base_y,
265            )?
266        };
267        let public_point = AffinePoint::new(public_x, public_y);
268        if !curve.is_on_curve(&public_point) {
269            return None;
270        }
271        // For curves with cofactor h > 1 (e.g. binary curves), an on-curve
272        // point may lie in a small torsion subgroup of order h rather than n.
273        // Verify n·Q = O so that u₂·Q in verification is never decoupled from
274        // the private key.  For h = 1 this is always true but costs little.
275        if !curve.scalar_mul(&public_point, &curve.n).is_infinity() {
276            return None;
277        }
278        Some(Self {
279            curve,
280            q: public_point,
281        })
282    }
283
284    #[must_use]
285    pub fn to_pem(&self) -> String {
286        pem_wrap(ECDSA_PUBLIC_LABEL, &self.to_key_blob())
287    }
288
289    /// Returns `None` if the PEM label does not match or the payload is malformed.
290    #[must_use]
291    pub fn from_pem(pem: &str) -> Option<Self> {
292        let blob = pem_unwrap(ECDSA_PUBLIC_LABEL, pem)?;
293        Self::from_key_blob(&blob)
294    }
295
296    /// # Panics
297    ///
298    /// Panics only if a binary-field curve reports a degree that does not fit
299    /// in `u64`, which would indicate malformed curve parameters.
300    #[must_use]
301    pub fn to_xml(&self) -> String {
302        let h = BigUint::from_u64(self.curve.h);
303        let degree = BigUint::from_u64(
304            u64::try_from(self.curve.gf2m_degree().unwrap_or(0)).expect("degree fits in u64"),
305        );
306        xml_wrap(
307            "EcdsaPublicKey",
308            &[
309                ("p", &self.curve.p),
310                ("a", &self.curve.a),
311                ("b", &self.curve.b),
312                ("n", &self.curve.n),
313                ("h", &h),
314                ("degree", &degree),
315                ("gx", &self.curve.gx),
316                ("gy", &self.curve.gy),
317                ("qx", &self.q.x),
318                ("qy", &self.q.y),
319            ],
320        )
321    }
322
323    /// Returns `None` if the XML root element, tag names, or integer encoding is invalid.
324    #[must_use]
325    pub fn from_xml(xml: &str) -> Option<Self> {
326        let mut fields = xml_unwrap(
327            "EcdsaPublicKey",
328            &["p", "a", "b", "n", "h", "degree", "gx", "gy", "qx", "qy"],
329            xml,
330        )?
331        .into_iter();
332        let field_prime = fields.next()?;
333        let curve_a = fields.next()?;
334        let curve_b = fields.next()?;
335        let subgroup_order = fields.next()?;
336        let cofactor_big = fields.next()?;
337        let degree_big = fields.next()?;
338        let base_x = fields.next()?;
339        let base_y = fields.next()?;
340        let public_x = fields.next()?;
341        let public_y = fields.next()?;
342        if fields.next().is_some() {
343            return None;
344        }
345        let cofactor = biguint_to_u64(&cofactor_big)?;
346        let field_degree = usize::try_from(biguint_to_u64(&degree_big)?).ok()?;
347        let curve = if field_degree > 0 {
348            CurveParams::new_binary(
349                field_prime,
350                field_degree,
351                curve_a,
352                curve_b,
353                subgroup_order,
354                cofactor,
355                (base_x, base_y),
356            )?
357        } else {
358            CurveParams::new(
359                field_prime,
360                curve_a,
361                curve_b,
362                subgroup_order,
363                cofactor,
364                base_x,
365                base_y,
366            )?
367        };
368        let public_point = AffinePoint::new(public_x, public_y);
369        if !curve.is_on_curve(&public_point) {
370            return None;
371        }
372        if !curve.scalar_mul(&public_point, &curve.n).is_infinity() {
373            return None;
374        }
375        Some(Self {
376            curve,
377            q: public_point,
378        })
379    }
380}
381
382// ─── EcdsaPrivateKey ──────────────────────────────────────────────────────────
383
384impl EcdsaPrivateKey {
385    /// The curve parameters for this key.
386    #[must_use]
387    pub fn curve(&self) -> &CurveParams {
388        &self.curve
389    }
390
391    /// The private scalar `d ∈ [1, n)`.
392    #[must_use]
393    pub fn private_scalar(&self) -> &BigUint {
394        &self.d
395    }
396
397    /// Derive the matching public key `Q = d·G`.
398    #[must_use]
399    pub fn to_public_key(&self) -> EcdsaPublicKey {
400        EcdsaPublicKey {
401            curve: self.curve.clone(),
402            q: self.q.clone(),
403        }
404    }
405
406    /// Sign with an explicit nonce `k`.
407    ///
408    /// ECDSA requires a fresh `k ∈ [1, n)` for every signature. This
409    /// lower-level entry point keeps the arithmetic explicit for fixed-vector
410    /// tests.
411    ///
412    /// Reusing the same `k` for two different messages with the same key
413    /// immediately reveals the private scalar. Outside of fixed vectors,
414    /// prefer [`Self::sign_digest`] or [`Self::sign_message`].
415    ///
416    /// Returned signatures are canonicalized to low-`s` form (`s <= n/2`) to
417    /// maximize interoperability with protocols that reject high-`s` ECDSA.
418    #[must_use]
419    pub fn sign_digest_with_nonce(&self, digest: &[u8], nonce: &BigUint) -> Option<EcdsaSignature> {
420        let n = &self.curve.n;
421        if nonce.is_zero() || nonce >= n {
422            return None;
423        }
424
425        let z = digest_to_scalar(digest, n);
426
427        // (x₁, y₁) = k·G
428        let r_point = self.curve.scalar_mul(&self.curve.base_point(), nonce);
429        if r_point.is_infinity() {
430            return None;
431        }
432        let r = r_point.x.modulo(n);
433        if r.is_zero() {
434            return None;
435        }
436
437        // s = k⁻¹ · (z + r·d) mod n
438        let k_inv = mod_inverse(nonce, n)?;
439        let rd = BigUint::mod_mul(&r, &self.d, n);
440        let z_plus_rd = z.add_ref(&rd).modulo(n);
441        let mut s = BigUint::mod_mul(&k_inv, &z_plus_rd, n);
442        if s.is_zero() {
443            return None;
444        }
445        canonicalize_low_s(n, &mut s);
446
447        Some(EcdsaSignature { r, s })
448    }
449
450    /// Sign a digest using RFC 6979 deterministic nonce derivation.
451    #[must_use]
452    pub fn sign_digest<H: Digest>(&self, digest: &[u8]) -> Option<EcdsaSignature> {
453        let nonce = rfc6979_nonce::<H>(&self.curve.n, &self.d, digest)?;
454        self.sign_digest_with_nonce(digest, &nonce)
455    }
456
457    /// Sign a digest using a fresh random nonce.
458    ///
459    /// Retries only in the negligible edge cases where `r = 0` or `s = 0`.
460    #[must_use]
461    pub fn sign_digest_with_rng<R: Csprng>(
462        &self,
463        digest: &[u8],
464        rng: &mut R,
465    ) -> Option<EcdsaSignature> {
466        loop {
467            let nonce = random_nonzero_below(rng, &self.curve.n)?;
468            if let Some(sig) = self.sign_digest_with_nonce(digest, &nonce) {
469                return Some(sig);
470            }
471        }
472    }
473
474    /// Hash one message with `H`, then sign deterministically.
475    #[must_use]
476    pub fn sign_message<H: Digest>(&self, message: &[u8]) -> Option<EcdsaSignature> {
477        let digest = H::digest(message);
478        self.sign_digest::<H>(&digest)
479    }
480
481    /// Hash one message with `H`, then sign with randomized nonces.
482    #[must_use]
483    pub fn sign_message_with_rng<H: Digest, R: Csprng>(
484        &self,
485        message: &[u8],
486        rng: &mut R,
487    ) -> Option<EcdsaSignature> {
488        let digest = H::digest(message);
489        self.sign_digest_with_rng(&digest, rng)
490    }
491
492    /// Sign and serialize a digest using deterministic nonce derivation.
493    #[must_use]
494    pub fn sign_digest_bytes<H: Digest>(&self, digest: &[u8]) -> Option<Vec<u8>> {
495        let sig = self.sign_digest::<H>(digest)?;
496        Some(sig.to_key_blob())
497    }
498
499    /// Sign and serialize a digest using randomized nonces.
500    #[must_use]
501    pub fn sign_digest_bytes_with_rng<R: Csprng>(
502        &self,
503        digest: &[u8],
504        rng: &mut R,
505    ) -> Option<Vec<u8>> {
506        let sig = self.sign_digest_with_rng(digest, rng)?;
507        Some(sig.to_key_blob())
508    }
509
510    /// Hash one message with `H`, then sign and serialize deterministically.
511    #[must_use]
512    pub fn sign_message_bytes<H: Digest>(&self, message: &[u8]) -> Option<Vec<u8>> {
513        let sig = self.sign_message::<H>(message)?;
514        Some(sig.to_key_blob())
515    }
516
517    /// Hash one message with `H`, then sign and serialize with randomized nonces.
518    #[must_use]
519    pub fn sign_message_bytes_with_rng<H: Digest, R: Csprng>(
520        &self,
521        message: &[u8],
522        rng: &mut R,
523    ) -> Option<Vec<u8>> {
524        let sig = self.sign_message_with_rng::<H, R>(message, rng)?;
525        Some(sig.to_key_blob())
526    }
527
528    /// Encode the private key in the crate-defined binary format.
529    ///
530    /// Layout: one field-type byte (`0x00` = prime, `0x01` = binary) followed
531    /// by `[p, a, b, n, h, Gx, Gy, d]`.
532    #[must_use]
533    pub fn to_key_blob(&self) -> Vec<u8> {
534        let h = BigUint::from_u64(self.curve.h);
535        let field_byte = u8::from(self.curve.gf2m_degree().is_some());
536        let mut out = vec![field_byte];
537        out.extend_from_slice(&encode_biguints(&[
538            &self.curve.p,
539            &self.curve.a,
540            &self.curve.b,
541            &self.curve.n,
542            &h,
543            &self.curve.gx,
544            &self.curve.gy,
545            &self.d,
546        ]));
547        out
548    }
549
550    /// Decode a private key from the crate-defined binary format.
551    #[must_use]
552    pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
553        let (&field_type, rest) = blob.split_first()?;
554        let mut fields = decode_biguints(rest)?.into_iter();
555        let field_prime = fields.next()?;
556        let curve_a = fields.next()?;
557        let curve_b = fields.next()?;
558        let subgroup_order = fields.next()?;
559        let cofactor_big = fields.next()?;
560        let base_x = fields.next()?;
561        let base_y = fields.next()?;
562        let private_scalar = fields.next()?;
563        if fields.next().is_some() {
564            return None;
565        }
566        let cofactor = biguint_to_u64(&cofactor_big)?;
567        let curve = if field_type == 0x01 {
568            let field_degree = field_prime.bits().checked_sub(1)?;
569            CurveParams::new_binary(
570                field_prime,
571                field_degree,
572                curve_a,
573                curve_b,
574                subgroup_order,
575                cofactor,
576                (base_x, base_y),
577            )?
578        } else {
579            CurveParams::new(
580                field_prime,
581                curve_a,
582                curve_b,
583                subgroup_order,
584                cofactor,
585                base_x,
586                base_y,
587            )?
588        };
589        if private_scalar.is_zero() || private_scalar.cmp(&curve.n).is_ge() {
590            return None;
591        }
592        let q = curve.scalar_mul(&curve.base_point(), &private_scalar);
593        Some(Self {
594            curve,
595            d: private_scalar,
596            q,
597        })
598    }
599
600    #[must_use]
601    pub fn to_pem(&self) -> String {
602        pem_wrap(ECDSA_PRIVATE_LABEL, &self.to_key_blob())
603    }
604
605    /// Returns `None` if the PEM label does not match or the payload is malformed.
606    #[must_use]
607    pub fn from_pem(pem: &str) -> Option<Self> {
608        let blob = pem_unwrap(ECDSA_PRIVATE_LABEL, pem)?;
609        Self::from_key_blob(&blob)
610    }
611
612    /// # Panics
613    ///
614    /// Panics only if a binary-field curve reports a degree that does not fit
615    /// in `u64`, which would indicate malformed curve parameters.
616    #[must_use]
617    pub fn to_xml(&self) -> String {
618        let h = BigUint::from_u64(self.curve.h);
619        let degree = BigUint::from_u64(
620            u64::try_from(self.curve.gf2m_degree().unwrap_or(0)).expect("degree fits in u64"),
621        );
622        xml_wrap(
623            "EcdsaPrivateKey",
624            &[
625                ("p", &self.curve.p),
626                ("a", &self.curve.a),
627                ("b", &self.curve.b),
628                ("n", &self.curve.n),
629                ("h", &h),
630                ("degree", &degree),
631                ("gx", &self.curve.gx),
632                ("gy", &self.curve.gy),
633                ("d", &self.d),
634            ],
635        )
636    }
637
638    /// Returns `None` if the XML root element, tag names, or integer encoding is invalid.
639    #[must_use]
640    pub fn from_xml(xml: &str) -> Option<Self> {
641        let mut fields = xml_unwrap(
642            "EcdsaPrivateKey",
643            &["p", "a", "b", "n", "h", "degree", "gx", "gy", "d"],
644            xml,
645        )?
646        .into_iter();
647        let field_prime = fields.next()?;
648        let curve_a = fields.next()?;
649        let curve_b = fields.next()?;
650        let subgroup_order = fields.next()?;
651        let cofactor_big = fields.next()?;
652        let degree_big = fields.next()?;
653        let base_x = fields.next()?;
654        let base_y = fields.next()?;
655        let private_scalar = fields.next()?;
656        if fields.next().is_some() {
657            return None;
658        }
659        let cofactor = biguint_to_u64(&cofactor_big)?;
660        let field_degree = usize::try_from(biguint_to_u64(&degree_big)?).ok()?;
661        let curve = if field_degree > 0 {
662            CurveParams::new_binary(
663                field_prime,
664                field_degree,
665                curve_a,
666                curve_b,
667                subgroup_order,
668                cofactor,
669                (base_x, base_y),
670            )?
671        } else {
672            CurveParams::new(
673                field_prime,
674                curve_a,
675                curve_b,
676                subgroup_order,
677                cofactor,
678                base_x,
679                base_y,
680            )?
681        };
682        if private_scalar.is_zero() || private_scalar.cmp(&curve.n).is_ge() {
683            return None;
684        }
685        let q = curve.scalar_mul(&curve.base_point(), &private_scalar);
686        Some(Self {
687            curve,
688            d: private_scalar,
689            q,
690        })
691    }
692}
693
694impl fmt::Debug for EcdsaPrivateKey {
695    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
696        f.write_str("EcdsaPrivateKey(<redacted>)")
697    }
698}
699
700// ─── EcdsaSignature ───────────────────────────────────────────────────────────
701
702impl EcdsaSignature {
703    #[must_use]
704    pub fn r(&self) -> &BigUint {
705        &self.r
706    }
707
708    #[must_use]
709    pub fn s(&self) -> &BigUint {
710        &self.s
711    }
712
713    /// Encode the signature as a DER `SEQUENCE` of `(r, s)`.
714    #[must_use]
715    pub fn to_key_blob(&self) -> Vec<u8> {
716        encode_biguints(&[&self.r, &self.s])
717    }
718
719    /// Decode a crate-defined binary ECDSA signature.
720    ///
721    /// Zero values are rejected immediately.  Range checks against the curve
722    /// order happen during verification because the signature encoding does
723    /// not carry the curve parameters.
724    #[must_use]
725    pub fn from_key_blob(blob: &[u8]) -> Option<Self> {
726        let mut fields = decode_biguints(blob)?.into_iter();
727        let r = fields.next()?;
728        let s = fields.next()?;
729        if fields.next().is_some() || r.is_zero() || s.is_zero() {
730            return None;
731        }
732        Some(Self { r, s })
733    }
734}
735
736// ─── Ecdsa namespace ──────────────────────────────────────────────────────────
737
738impl Ecdsa {
739    /// Returns `(public_key, private_key)`.
740    #[must_use]
741    pub fn generate<R: Csprng>(
742        curve: CurveParams,
743        rng: &mut R,
744    ) -> (EcdsaPublicKey, EcdsaPrivateKey) {
745        let (d, q) = curve.generate_keypair(rng);
746        let public = EcdsaPublicKey {
747            curve: curve.clone(),
748            q: q.clone(),
749        };
750        let private = EcdsaPrivateKey { curve, d, q };
751        (public, private)
752    }
753
754    /// Derive a key pair from an explicit curve and secret scalar.
755    ///
756    /// Returns `None` if `secret` is zero or ≥ `n`.
757    #[must_use]
758    pub fn from_secret_scalar(
759        curve: CurveParams,
760        secret: &BigUint,
761    ) -> Option<(EcdsaPublicKey, EcdsaPrivateKey)> {
762        if secret.is_zero() || secret >= &curve.n {
763            return None;
764        }
765        let q = curve.scalar_mul(&curve.base_point(), secret);
766        Some((
767            EcdsaPublicKey {
768                curve: curve.clone(),
769                q: q.clone(),
770            },
771            EcdsaPrivateKey {
772                curve,
773                d: secret.clone(),
774                q,
775            },
776        ))
777    }
778}
779
780// ─── Helpers ─────────────────────────────────────────────────────────────────
781
782/// Convert a small `BigUint` (≤ 8 bytes) to a `u64`.
783///
784/// Returns `None` if the value has more than 8 bytes, which would indicate
785/// a corrupt or unusually large cofactor in a serialized key.
786fn biguint_to_u64(value: &BigUint) -> Option<u64> {
787    let bytes = value.to_be_bytes();
788    if bytes.len() > 8 {
789        return None;
790    }
791    let mut arr = [0u8; 8];
792    arr[8 - bytes.len()..].copy_from_slice(&bytes);
793    Some(u64::from_be_bytes(arr))
794}
795
796/// FIPS 186-5 digest representative reduction.
797///
798/// Keeps the leftmost `N = bits(n)` bits of the hash.  The shift amount is
799/// derived from `digest.len() * 8`, not from the trimmed width of the integer,
800/// to avoid a length-dependent branch on the hash output.
801fn digest_to_scalar(digest: &[u8], modulus: &BigUint) -> BigUint {
802    let mut value = BigUint::from_be_bytes(digest);
803    let hash_bits = digest.len() * 8;
804    let target_bits = modulus.bits();
805    if hash_bits > target_bits {
806        for _ in 0..(hash_bits - target_bits) {
807            value.shr1();
808        }
809    }
810    value
811}
812
813fn canonicalize_low_s(order: &BigUint, s: &mut BigUint) {
814    let mut half = order.clone();
815    half.shr1();
816    if (*s).cmp(&half).is_gt() {
817        *s = order.sub_ref(s);
818    }
819}
820
821fn int_to_octets(value: &BigUint, len: usize) -> Vec<u8> {
822    let bytes = value.to_be_bytes();
823    if bytes.len() >= len {
824        return bytes[bytes.len() - len..].to_vec();
825    }
826    let mut out = vec![0u8; len];
827    out[len - bytes.len()..].copy_from_slice(&bytes);
828    out
829}
830
831fn bits_to_int(input: &[u8], target_bits: usize) -> BigUint {
832    let mut value = BigUint::from_be_bytes(input);
833    let input_bits = input.len() * 8;
834    if input_bits > target_bits {
835        for _ in 0..(input_bits - target_bits) {
836            value.shr1();
837        }
838    }
839    value
840}
841
842fn bits_to_octets(input: &[u8], q: &BigUint, q_bits: usize, ro_len: usize) -> Vec<u8> {
843    let z1 = bits_to_int(input, q_bits);
844    let z2 = z1.modulo(q);
845    int_to_octets(&z2, ro_len)
846}
847
848fn rfc6979_nonce<H: Digest>(q: &BigUint, x: &BigUint, digest: &[u8]) -> Option<BigUint> {
849    if q <= &BigUint::one() {
850        return None;
851    }
852
853    let q_bits = q.bits();
854    let ro_len = q_bits.div_ceil(8);
855    let bx = int_to_octets(x, ro_len);
856    let bh = bits_to_octets(digest, q, q_bits, ro_len);
857
858    let mut v = vec![0x01; H::OUTPUT_LEN];
859    let mut k = vec![0x00; H::OUTPUT_LEN];
860
861    let mut data = Vec::with_capacity(v.len() + 1 + bx.len() + bh.len());
862    data.extend_from_slice(&v);
863    data.push(0x00);
864    data.extend_from_slice(&bx);
865    data.extend_from_slice(&bh);
866    k = Hmac::<H>::compute(&k, &data);
867    v = Hmac::<H>::compute(&k, &v);
868
869    data.clear();
870    data.extend_from_slice(&v);
871    data.push(0x01);
872    data.extend_from_slice(&bx);
873    data.extend_from_slice(&bh);
874    k = Hmac::<H>::compute(&k, &data);
875    v = Hmac::<H>::compute(&k, &v);
876
877    loop {
878        let mut t = Vec::with_capacity(ro_len);
879        while t.len() < ro_len {
880            v = Hmac::<H>::compute(&k, &v);
881            let take = (ro_len - t.len()).min(v.len());
882            t.extend_from_slice(&v[..take]);
883        }
884
885        let candidate = bits_to_int(&t, q_bits);
886        if !candidate.is_zero() && &candidate < q {
887            return Some(candidate);
888        }
889
890        data.clear();
891        data.extend_from_slice(&v);
892        data.push(0x00);
893        k = Hmac::<H>::compute(&k, &data);
894        v = Hmac::<H>::compute(&k, &v);
895    }
896}
897
898// ─── Tests ────────────────────────────────────────────────────────────────────
899
900#[cfg(test)]
901mod tests {
902    use super::{Ecdsa, EcdsaPrivateKey, EcdsaPublicKey, EcdsaSignature};
903    use crate::public_key::bigint::BigUint;
904    use crate::public_key::ec::{b163, p256, p384, p521, secp256k1};
905    use crate::{CtrDrbgAes256, Sha256, Sha384, Sha512};
906
907    fn rng() -> CtrDrbgAes256 {
908        CtrDrbgAes256::new(&[0xab; 48])
909    }
910
911    fn decode_hex(hex: &str) -> Vec<u8> {
912        let cleaned: String = hex.chars().filter(|c| !c.is_whitespace()).collect();
913        assert_eq!(
914            cleaned.len() % 2,
915            0,
916            "hex input must have an even number of nybbles"
917        );
918        (0..cleaned.len())
919            .step_by(2)
920            .map(|i| u8::from_str_radix(&cleaned[i..i + 2], 16).expect("valid hex byte"))
921            .collect()
922    }
923
924    fn from_hex(hex: &str) -> BigUint {
925        BigUint::from_be_bytes(&decode_hex(hex))
926    }
927
928    // ── Sign-and-verify round trips ──────────────────────────────────────────
929
930    #[test]
931    fn sign_verify_roundtrip_p256() {
932        let mut rng = rng();
933        let (public, private) = Ecdsa::generate(p256(), &mut rng);
934        let msg = b"hello world";
935        let sig = private.sign_message::<Sha256>(msg).expect("sign");
936        assert!(public.verify_message::<Sha256>(msg, &sig));
937    }
938
939    #[test]
940    fn sign_verify_roundtrip_p384() {
941        let mut rng = rng();
942        let (public, private) = Ecdsa::generate(p384(), &mut rng);
943        let msg = b"p384 test message";
944        let sig = private.sign_message::<Sha384>(msg).expect("sign");
945        assert!(public.verify_message::<Sha384>(msg, &sig));
946    }
947
948    #[test]
949    fn sign_verify_roundtrip_secp256k1() {
950        let mut rng = rng();
951        let (public, private) = Ecdsa::generate(secp256k1(), &mut rng);
952        let msg = b"secp256k1 test";
953        let sig = private.sign_message::<Sha256>(msg).expect("sign");
954        assert!(public.verify_message::<Sha256>(msg, &sig));
955    }
956
957    #[test]
958    fn sign_verify_roundtrip_p521() {
959        let mut rng = rng();
960        let (public, private) = Ecdsa::generate(p521(), &mut rng);
961        let msg = b"p521 test message";
962        let sig = private.sign_message::<Sha512>(msg).expect("sign");
963        assert!(public.verify_message::<Sha512>(msg, &sig));
964    }
965
966    #[test]
967    fn sign_verify_roundtrip_b163() {
968        let mut rng = rng();
969        let (public, private) = Ecdsa::generate(b163(), &mut rng);
970        let msg = b"binary curve ecdsa";
971        let sig = private.sign_message::<Sha256>(msg).expect("sign");
972        assert!(public.verify_message::<Sha256>(msg, &sig));
973    }
974
975    // ── Deterministic signing via explicit nonce ──────────────────────────────
976
977    #[test]
978    fn sign_digest_with_nonce_is_deterministic() {
979        let mut rng = rng();
980        let (_, private) = Ecdsa::generate(p256(), &mut rng);
981        let digest = [0x42u8; 32];
982        let k = BigUint::from_u64(12_345_678_901_234_567_u64);
983        let sig1 = private
984            .sign_digest_with_nonce(&digest, &k)
985            .expect("first sign");
986        let sig2 = private
987            .sign_digest_with_nonce(&digest, &k)
988            .expect("second sign");
989        assert_eq!(sig1, sig2);
990    }
991
992    #[test]
993    fn sign_digest_with_nonce_repeatable_for_fixed_nonce() {
994        let mut rng = rng();
995        let (_, private) = Ecdsa::generate(p256(), &mut rng);
996        let digest = [0x42u8; 32];
997        let nonce = BigUint::from_u64(12_345_678_901_234_567_u64);
998        let lhs = private
999            .sign_digest_with_nonce(&digest, &nonce)
1000            .expect("first");
1001        let rhs = private
1002            .sign_digest_with_nonce(&digest, &nonce)
1003            .expect("second");
1004        assert_eq!(lhs, rhs);
1005    }
1006
1007    #[test]
1008    fn sign_digest_with_nonce_zero_rejected() {
1009        let mut rng = rng();
1010        let (_, private) = Ecdsa::generate(p256(), &mut rng);
1011        let digest = [0x00u8; 32];
1012        assert!(private
1013            .sign_digest_with_nonce(&digest, &BigUint::zero())
1014            .is_none());
1015    }
1016
1017    #[test]
1018    fn sign_digest_with_nonce_equal_to_n_rejected() {
1019        let curve = p256();
1020        let n = curve.n.clone();
1021        let mut rng = rng();
1022        let (_, private) = Ecdsa::generate(curve, &mut rng);
1023        let digest = [0x01u8; 32];
1024        assert!(private.sign_digest_with_nonce(&digest, &n).is_none());
1025    }
1026
1027    #[test]
1028    fn sign_digest_with_nonce_returns_low_s() {
1029        let curve = p256();
1030        let secret = BigUint::from_u64(0x1234_5678_9abc_def0);
1031        let (_, private) = Ecdsa::from_secret_scalar(curve.clone(), &secret).expect("from secret");
1032        let digest = Sha256::digest(b"low-s canonicalization");
1033        let nonce = BigUint::from_u64(0xdead_beef_cafe_babe);
1034
1035        let sig = private
1036            .sign_digest_with_nonce(&digest, &nonce)
1037            .expect("sign with nonce");
1038        let mut half = curve.n.clone();
1039        half.shr1();
1040        assert!(
1041            sig.s.cmp(&half).is_le(),
1042            "signature must be canonical low-s"
1043        );
1044    }
1045
1046    // ── Rejection tests ───────────────────────────────────────────────────────
1047
1048    #[test]
1049    fn wrong_message_rejected() {
1050        let mut rng = rng();
1051        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1052        let msg = b"correct message";
1053        let wrong = b"wrong message";
1054        let sig = private.sign_message::<Sha256>(msg).expect("sign");
1055        assert!(!public.verify_message::<Sha256>(wrong, &sig));
1056    }
1057
1058    #[test]
1059    fn tampered_r_rejected() {
1060        let mut rng = rng();
1061        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1062        let msg = b"message";
1063        let sig = private.sign_message::<Sha256>(msg).expect("sign");
1064        let bad = EcdsaSignature {
1065            r: sig.r.add_ref(&BigUint::one()),
1066            s: sig.s.clone(),
1067        };
1068        assert!(!public.verify_message::<Sha256>(msg, &bad));
1069    }
1070
1071    #[test]
1072    fn tampered_s_rejected() {
1073        let mut rng = rng();
1074        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1075        let msg = b"message";
1076        let sig = private.sign_message::<Sha256>(msg).expect("sign");
1077        let bad = EcdsaSignature {
1078            r: sig.r.clone(),
1079            s: sig.s.add_ref(&BigUint::one()),
1080        };
1081        assert!(!public.verify_message::<Sha256>(msg, &bad));
1082    }
1083
1084    #[test]
1085    fn wrong_key_rejected() {
1086        let mut rng = rng();
1087        let (_, private1) = Ecdsa::generate(p256(), &mut rng);
1088        let (public2, _) = Ecdsa::generate(p256(), &mut rng);
1089        let msg = b"message";
1090        let sig = private1.sign_message::<Sha256>(msg).expect("sign");
1091        assert!(!public2.verify_message::<Sha256>(msg, &sig));
1092    }
1093
1094    // ── to_public_key ─────────────────────────────────────────────────────────
1095
1096    #[test]
1097    fn to_public_key_matches_generated() {
1098        let mut rng = rng();
1099        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1100        let derived = private.to_public_key();
1101        // Signing with private and verifying with the derived public key must work.
1102        let msg = b"derived key test";
1103        let sig = private.sign_message::<Sha256>(msg).expect("sign");
1104        assert!(derived.verify_message::<Sha256>(msg, &sig));
1105        // The derived public point must match the original.
1106        assert_eq!(derived.q, public.q);
1107    }
1108
1109    // ── from_secret_scalar ────────────────────────────────────────────────────
1110
1111    #[test]
1112    fn from_secret_scalar_rejects_zero() {
1113        assert!(Ecdsa::from_secret_scalar(p256(), &BigUint::zero()).is_none());
1114    }
1115
1116    #[test]
1117    fn from_secret_scalar_rejects_out_of_range() {
1118        let curve = p256();
1119        let too_large = curve.n.clone();
1120        assert!(Ecdsa::from_secret_scalar(curve, &too_large).is_none());
1121    }
1122
1123    // ── Serialization: binary ─────────────────────────────────────────────────
1124
1125    #[test]
1126    fn public_key_binary_roundtrip() {
1127        let mut rng = rng();
1128        let (public, _) = Ecdsa::generate(p256(), &mut rng);
1129        let blob = public.to_key_blob();
1130        let recovered = EcdsaPublicKey::from_key_blob(&blob).expect("from_binary");
1131        assert_eq!(recovered.q, public.q);
1132        assert_eq!(recovered.curve.n, public.curve.n);
1133    }
1134
1135    #[test]
1136    fn public_key_bytes_roundtrip() {
1137        let mut rng = rng();
1138        let (public, _) = Ecdsa::generate(p256(), &mut rng);
1139        let bytes = public.to_wire_bytes();
1140        let recovered = EcdsaPublicKey::from_wire_bytes(p256(), &bytes).expect("from_bytes");
1141        assert_eq!(recovered.q, public.q);
1142        assert_eq!(recovered.curve.n, public.curve.n);
1143    }
1144
1145    #[test]
1146    fn private_key_binary_roundtrip() {
1147        let mut rng = rng();
1148        let (_, private) = Ecdsa::generate(p256(), &mut rng);
1149        let blob = private.to_key_blob();
1150        let recovered = EcdsaPrivateKey::from_key_blob(&blob).expect("from_binary");
1151        assert_eq!(recovered.d, private.d);
1152        assert_eq!(recovered.curve.n, private.curve.n);
1153    }
1154
1155    #[test]
1156    fn signature_binary_roundtrip() {
1157        let mut rng = rng();
1158        let (_, private) = Ecdsa::generate(p256(), &mut rng);
1159        let msg = b"roundtrip test";
1160        let sig = private.sign_message::<Sha256>(msg).expect("sign");
1161        let blob = sig.to_key_blob();
1162        let recovered = EcdsaSignature::from_key_blob(&blob).expect("from_binary");
1163        assert_eq!(recovered, sig);
1164    }
1165
1166    // ── Serialization: PEM ────────────────────────────────────────────────────
1167
1168    #[test]
1169    fn public_key_pem_roundtrip() {
1170        let mut rng = rng();
1171        let (public, _) = Ecdsa::generate(p384(), &mut rng);
1172        let pem = public.to_pem();
1173        assert!(pem.contains("CRYPTOGRAPHY ECDSA PUBLIC KEY"));
1174        let recovered = EcdsaPublicKey::from_pem(&pem).expect("from_pem");
1175        assert_eq!(recovered.q, public.q);
1176    }
1177
1178    #[test]
1179    fn private_key_pem_roundtrip() {
1180        let mut rng = rng();
1181        let (_, private) = Ecdsa::generate(p384(), &mut rng);
1182        let pem = private.to_pem();
1183        assert!(pem.contains("CRYPTOGRAPHY ECDSA PRIVATE KEY"));
1184        let recovered = EcdsaPrivateKey::from_pem(&pem).expect("from_pem");
1185        assert_eq!(recovered.d, private.d);
1186    }
1187
1188    // ── Serialization: XML ────────────────────────────────────────────────────
1189
1190    #[test]
1191    fn public_key_xml_roundtrip() {
1192        let mut rng = rng();
1193        let (public, _) = Ecdsa::generate(secp256k1(), &mut rng);
1194        let xml = public.to_xml();
1195        assert!(xml.contains("EcdsaPublicKey"));
1196        let recovered = EcdsaPublicKey::from_xml(&xml).expect("from_xml");
1197        assert_eq!(recovered.q, public.q);
1198    }
1199
1200    #[test]
1201    fn private_key_xml_roundtrip() {
1202        let mut rng = rng();
1203        let (_, private) = Ecdsa::generate(secp256k1(), &mut rng);
1204        let xml = private.to_xml();
1205        assert!(xml.contains("EcdsaPrivateKey"));
1206        let recovered = EcdsaPrivateKey::from_xml(&xml).expect("from_xml");
1207        assert_eq!(recovered.d, private.d);
1208    }
1209
1210    // ── Byte-level sign_bytes / verify_bytes ──────────────────────────────────
1211
1212    #[test]
1213    fn sign_bytes_verify_bytes_roundtrip() {
1214        let mut rng = rng();
1215        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1216        let digest = Sha256::digest(b"test message bytes");
1217        let sig_bytes = private
1218            .sign_digest_bytes::<Sha256>(&digest)
1219            .expect("sign_digest_bytes");
1220        assert!(public.verify_bytes(&digest, &sig_bytes));
1221    }
1222
1223    #[test]
1224    fn sign_message_bytes_verify_message_bytes_roundtrip() {
1225        let mut rng = rng();
1226        let (public, private) = Ecdsa::generate(p256(), &mut rng);
1227        let msg = b"end-to-end bytes test";
1228        let sig_bytes = private
1229            .sign_message_bytes::<Sha256>(msg)
1230            .expect("sign_message_bytes");
1231        assert!(public.verify_message_bytes::<Sha256>(msg, &sig_bytes));
1232    }
1233
1234    // ── Debug impl ────────────────────────────────────────────────────────────
1235
1236    #[test]
1237    fn private_key_debug_redacted() {
1238        let mut rng = rng();
1239        let (_, private) = Ecdsa::generate(p256(), &mut rng);
1240        let s = format!("{private:?}");
1241        assert_eq!(s, "EcdsaPrivateKey(<redacted>)");
1242        // The scalar itself must not appear.
1243        assert!(!s.contains(&format!("{:?}", private.d)));
1244    }
1245
1246    #[test]
1247    fn rfc6979_ecdsa_p256_sha256_sample_vector_with_low_s_canonicalization() {
1248        // RFC 6979, Appendix A.2.5 (ECDSA over NIST P-256), SHA-256, "sample".
1249        let x = from_hex("C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721");
1250        let expected_ux =
1251            from_hex("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6");
1252        let expected_uy =
1253            from_hex("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299");
1254        let expected_k =
1255            from_hex("A6E3C57DD01ABE90086538398355DD4C3B17AA873382B0F24D6129493D8AAD60");
1256        let expected_r =
1257            from_hex("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716");
1258        let expected_s_rfc =
1259            from_hex("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
1260
1261        let (public, private) =
1262            Ecdsa::from_secret_scalar(p256(), &x).expect("RFC secret scalar must be valid");
1263        assert_eq!(public.q.x, expected_ux);
1264        assert_eq!(public.q.y, expected_uy);
1265
1266        let message = b"sample";
1267        let digest = Sha256::digest(message);
1268        let derived_k = super::rfc6979_nonce::<Sha256>(&private.curve.n, &private.d, &digest)
1269            .expect("RFC nonce must derive");
1270        assert_eq!(derived_k, expected_k, "RFC 6979 nonce mismatch");
1271
1272        let signature = private.sign_message::<Sha256>(message).expect("sign");
1273        assert_eq!(signature.r, expected_r);
1274
1275        // This implementation enforces low-s canonicalization.
1276        let expected_s_low = private.curve.n.sub_ref(&expected_s_rfc);
1277        assert_eq!(signature.s, expected_s_low);
1278        assert!(public.verify_message::<Sha256>(message, &signature));
1279    }
1280}