we (web engine): Experimental web browser project to understand the limits of Claude
1//! ECDSA signature verification for P-256 and P-384 (FIPS 186-4).
2//!
3//! Supports verification of ECDSA signatures using the NIST P-256 (secp256r1)
4//! and P-384 (secp384r1) curves with SHA-256 and SHA-384 respectively.
5
6use crate::asn1::{
7 self, Asn1Error, OID_EC_PUBLIC_KEY, OID_PRIME256V1, OID_SECP384R1, TAG_SEQUENCE,
8};
9use crate::bigint::BigUint;
10use crate::sha2::{sha256, sha384};
11
12use core::cmp::Ordering;
13use core::fmt;
14
15// ---------------------------------------------------------------------------
16// Error type
17// ---------------------------------------------------------------------------
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20pub enum EcdsaError {
21 /// ASN.1 parsing error.
22 Asn1(Asn1Error),
23 /// Unsupported or unknown curve.
24 UnsupportedCurve,
25 /// Invalid public key (not on curve, wrong format, etc.).
26 InvalidPublicKey,
27 /// Invalid signature format.
28 InvalidSignature,
29 /// Signature component r or s is out of range [1, n-1].
30 SignatureOutOfRange,
31 /// Signature verification failed.
32 VerificationFailed,
33 /// Invalid key format.
34 InvalidKeyFormat,
35}
36
37impl fmt::Display for EcdsaError {
38 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39 match self {
40 Self::Asn1(e) => write!(f, "ASN.1 error: {e}"),
41 Self::UnsupportedCurve => write!(f, "unsupported elliptic curve"),
42 Self::InvalidPublicKey => write!(f, "invalid EC public key"),
43 Self::InvalidSignature => write!(f, "invalid ECDSA signature format"),
44 Self::SignatureOutOfRange => write!(f, "signature component out of range"),
45 Self::VerificationFailed => write!(f, "ECDSA verification failed"),
46 Self::InvalidKeyFormat => write!(f, "invalid key format"),
47 }
48 }
49}
50
51impl From<Asn1Error> for EcdsaError {
52 fn from(e: Asn1Error) -> Self {
53 Self::Asn1(e)
54 }
55}
56
57pub type Result<T> = core::result::Result<T, EcdsaError>;
58
59// ---------------------------------------------------------------------------
60// Curve identifiers
61// ---------------------------------------------------------------------------
62
63/// Supported elliptic curves.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub enum Curve {
66 P256,
67 P384,
68}
69
70// ---------------------------------------------------------------------------
71// Modular arithmetic helpers (over BigUint)
72// ---------------------------------------------------------------------------
73
74fn mod_add(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
75 a.add(b).modulo(m)
76}
77
78fn mod_sub(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
79 if a.cmp(b) != Ordering::Less {
80 a.sub(b)
81 } else {
82 // a < b: result = m - (b - a)
83 m.sub(&b.sub(a))
84 }
85}
86
87fn mod_mul(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint {
88 a.mul(b).modulo(m)
89}
90
91/// Modular inverse using Fermat's little theorem (for prime moduli).
92/// a^(-1) = a^(p-2) mod p
93fn mod_inv(a: &BigUint, p: &BigUint) -> BigUint {
94 let exp = p.sub(&BigUint::from_u64(2));
95 a.modpow(&exp, p)
96}
97
98// ---------------------------------------------------------------------------
99// Curve parameters
100// ---------------------------------------------------------------------------
101
102struct CurveParams {
103 /// Field prime p.
104 p: BigUint,
105 /// Curve parameter a.
106 a: BigUint,
107 /// Curve parameter b.
108 b: BigUint,
109 /// Generator x-coordinate.
110 gx: BigUint,
111 /// Generator y-coordinate.
112 gy: BigUint,
113 /// Curve order n.
114 n: BigUint,
115 /// Byte length of field elements.
116 field_len: usize,
117}
118
119fn from_hex(s: &str) -> Vec<u8> {
120 (0..s.len())
121 .step_by(2)
122 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
123 .collect()
124}
125
126fn p256_params() -> CurveParams {
127 let p = BigUint::from_be_bytes(&from_hex(
128 "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF",
129 ));
130 let a = BigUint::from_be_bytes(&from_hex(
131 "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC",
132 ));
133 let b = BigUint::from_be_bytes(&from_hex(
134 "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B",
135 ));
136 let gx = BigUint::from_be_bytes(&from_hex(
137 "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296",
138 ));
139 let gy = BigUint::from_be_bytes(&from_hex(
140 "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5",
141 ));
142 let n = BigUint::from_be_bytes(&from_hex(
143 "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551",
144 ));
145 CurveParams {
146 p,
147 a,
148 b,
149 gx,
150 gy,
151 n,
152 field_len: 32,
153 }
154}
155
156fn p384_params() -> CurveParams {
157 let p = BigUint::from_be_bytes(&from_hex(
158 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF",
159 ));
160 let a = BigUint::from_be_bytes(&from_hex(
161 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC",
162 ));
163 let b = BigUint::from_be_bytes(&from_hex(
164 "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF",
165 ));
166 let gx = BigUint::from_be_bytes(&from_hex(
167 "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7",
168 ));
169 let gy = BigUint::from_be_bytes(&from_hex(
170 "3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F",
171 ));
172 let n = BigUint::from_be_bytes(&from_hex(
173 "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973",
174 ));
175 CurveParams {
176 p,
177 a,
178 b,
179 gx,
180 gy,
181 n,
182 field_len: 48,
183 }
184}
185
186fn curve_params(curve: Curve) -> CurveParams {
187 match curve {
188 Curve::P256 => p256_params(),
189 Curve::P384 => p384_params(),
190 }
191}
192
193// ---------------------------------------------------------------------------
194// Point types
195// ---------------------------------------------------------------------------
196
197/// A point on the curve in Jacobian coordinates (X, Y, Z).
198/// Affine (x, y) = (X/Z^2, Y/Z^3). Point at infinity has Z = 0.
199#[derive(Clone)]
200struct JacobianPoint {
201 x: BigUint,
202 y: BigUint,
203 z: BigUint,
204}
205
206impl JacobianPoint {
207 fn infinity() -> Self {
208 Self {
209 x: BigUint::one(),
210 y: BigUint::one(),
211 z: BigUint::zero(),
212 }
213 }
214
215 fn from_affine(x: &BigUint, y: &BigUint) -> Self {
216 Self {
217 x: x.clone(),
218 y: y.clone(),
219 z: BigUint::one(),
220 }
221 }
222
223 fn is_infinity(&self) -> bool {
224 self.z.is_zero()
225 }
226
227 /// Convert to affine coordinates (x, y).
228 fn to_affine(&self, p: &BigUint) -> (BigUint, BigUint) {
229 if self.is_infinity() {
230 return (BigUint::zero(), BigUint::zero());
231 }
232
233 let z_inv = mod_inv(&self.z, p);
234 let z_inv2 = mod_mul(&z_inv, &z_inv, p);
235 let z_inv3 = mod_mul(&z_inv2, &z_inv, p);
236
237 let x = mod_mul(&self.x, &z_inv2, p);
238 let y = mod_mul(&self.y, &z_inv3, p);
239 (x, y)
240 }
241}
242
243// ---------------------------------------------------------------------------
244// Elliptic curve point operations
245// ---------------------------------------------------------------------------
246
247/// Point doubling in Jacobian coordinates.
248/// Uses the optimization for a = p - 3 (both P-256 and P-384).
249fn point_double(pt: &JacobianPoint, params: &CurveParams) -> JacobianPoint {
250 let p = ¶ms.p;
251
252 if pt.is_infinity() {
253 return JacobianPoint::infinity();
254 }
255
256 // For a = -3 (mod p): M = 3*(X - Z^2)*(X + Z^2)
257 let z2 = mod_mul(&pt.z, &pt.z, p);
258 let x_minus_z2 = mod_sub(&pt.x, &z2, p);
259 let x_plus_z2 = mod_add(&pt.x, &z2, p);
260 let m = mod_mul(&x_minus_z2, &x_plus_z2, p);
261 let m = mod_mul(&BigUint::from_u64(3), &m, p);
262
263 // S = 4*X*Y^2
264 let y2 = mod_mul(&pt.y, &pt.y, p);
265 let s = mod_mul(&BigUint::from_u64(4), &mod_mul(&pt.x, &y2, p), p);
266
267 // X3 = M^2 - 2*S
268 let m2 = mod_mul(&m, &m, p);
269 let two_s = mod_add(&s, &s, p);
270 let x3 = mod_sub(&m2, &two_s, p);
271
272 // Y3 = M*(S - X3) - 8*Y^4
273 let s_minus_x3 = mod_sub(&s, &x3, p);
274 let y4 = mod_mul(&y2, &y2, p);
275 let eight_y4 = mod_mul(&BigUint::from_u64(8), &y4, p);
276 let y3 = mod_sub(&mod_mul(&m, &s_minus_x3, p), &eight_y4, p);
277
278 // Z3 = 2*Y*Z
279 let z3 = mod_mul(&BigUint::from_u64(2), &mod_mul(&pt.y, &pt.z, p), p);
280
281 JacobianPoint {
282 x: x3,
283 y: y3,
284 z: z3,
285 }
286}
287
288/// Point addition in Jacobian coordinates.
289fn point_add(p1: &JacobianPoint, p2: &JacobianPoint, params: &CurveParams) -> JacobianPoint {
290 let p = ¶ms.p;
291
292 if p1.is_infinity() {
293 return p2.clone();
294 }
295 if p2.is_infinity() {
296 return p1.clone();
297 }
298
299 // U1 = X1*Z2^2, U2 = X2*Z1^2
300 let z1_sq = mod_mul(&p1.z, &p1.z, p);
301 let z2_sq = mod_mul(&p2.z, &p2.z, p);
302 let u1 = mod_mul(&p1.x, &z2_sq, p);
303 let u2 = mod_mul(&p2.x, &z1_sq, p);
304
305 // S1 = Y1*Z2^3, S2 = Y2*Z1^3
306 let z1_cu = mod_mul(&z1_sq, &p1.z, p);
307 let z2_cu = mod_mul(&z2_sq, &p2.z, p);
308 let s1 = mod_mul(&p1.y, &z2_cu, p);
309 let s2 = mod_mul(&p2.y, &z1_cu, p);
310
311 if u1 == u2 {
312 if s1 == s2 {
313 // Points are equal, use doubling.
314 return point_double(p1, params);
315 }
316 // Points are inverses of each other.
317 return JacobianPoint::infinity();
318 }
319
320 // H = U2 - U1
321 let h = mod_sub(&u2, &u1, p);
322 // R = S2 - S1
323 let r = mod_sub(&s2, &s1, p);
324
325 let h2 = mod_mul(&h, &h, p);
326 let h3 = mod_mul(&h2, &h, p);
327 let u1_h2 = mod_mul(&u1, &h2, p);
328
329 // X3 = R^2 - H^3 - 2*U1*H^2
330 let r2 = mod_mul(&r, &r, p);
331 let two_u1_h2 = mod_add(&u1_h2, &u1_h2, p);
332 let x3 = mod_sub(&mod_sub(&r2, &h3, p), &two_u1_h2, p);
333
334 // Y3 = R*(U1*H^2 - X3) - S1*H^3
335 let y3 = mod_sub(
336 &mod_mul(&r, &mod_sub(&u1_h2, &x3, p), p),
337 &mod_mul(&s1, &h3, p),
338 p,
339 );
340
341 // Z3 = H*Z1*Z2
342 let z3 = mod_mul(&h, &mod_mul(&p1.z, &p2.z, p), p);
343
344 JacobianPoint {
345 x: x3,
346 y: y3,
347 z: z3,
348 }
349}
350
351/// Scalar multiplication using the Montgomery ladder (constant-time w.r.t. scalar bits).
352#[cfg_attr(not(test), allow(dead_code))]
353fn scalar_mult(k: &BigUint, point: &JacobianPoint, params: &CurveParams) -> JacobianPoint {
354 let bits = k.bit_len();
355 if bits == 0 {
356 return JacobianPoint::infinity();
357 }
358
359 // Montgomery ladder: R0 = infinity, R1 = point.
360 // For each bit from MSB to LSB:
361 // if bit == 0: R1 = R0 + R1, R0 = 2*R0
362 // if bit == 1: R0 = R0 + R1, R1 = 2*R1
363 let mut r0 = JacobianPoint::infinity();
364 let mut r1 = point.clone();
365
366 for i in (0..bits).rev() {
367 if k.bit(i) {
368 r0 = point_add(&r0, &r1, params);
369 r1 = point_double(&r1, params);
370 } else {
371 r1 = point_add(&r0, &r1, params);
372 r0 = point_double(&r0, params);
373 }
374 }
375
376 r0
377}
378
379/// Multi-scalar multiplication: k1*G + k2*Q using Shamir's trick.
380fn multi_scalar_mult(
381 k1: &BigUint,
382 g: &JacobianPoint,
383 k2: &BigUint,
384 q: &JacobianPoint,
385 params: &CurveParams,
386) -> JacobianPoint {
387 // Precompute G + Q.
388 let g_plus_q = point_add(g, q, params);
389
390 let bits1 = k1.bit_len();
391 let bits2 = k2.bit_len();
392 let max_bits = bits1.max(bits2);
393
394 let mut result = JacobianPoint::infinity();
395
396 for i in (0..max_bits).rev() {
397 result = point_double(&result, params);
398
399 let b1 = k1.bit(i);
400 let b2 = k2.bit(i);
401
402 match (b1, b2) {
403 (true, true) => result = point_add(&result, &g_plus_q, params),
404 (true, false) => result = point_add(&result, g, params),
405 (false, true) => result = point_add(&result, q, params),
406 (false, false) => {}
407 }
408 }
409
410 result
411}
412
413// ---------------------------------------------------------------------------
414// Point validation
415// ---------------------------------------------------------------------------
416
417/// Verify that (x, y) is a valid point on the curve: y^2 = x^3 + ax + b (mod p).
418fn is_on_curve(x: &BigUint, y: &BigUint, params: &CurveParams) -> bool {
419 let p = ¶ms.p;
420
421 // y^2 mod p
422 let y2 = mod_mul(y, y, p);
423
424 // x^3 + ax + b mod p
425 let x2 = mod_mul(x, x, p);
426 let x3 = mod_mul(&x2, x, p);
427 let ax = mod_mul(¶ms.a, x, p);
428 let rhs = mod_add(&mod_add(&x3, &ax, p), ¶ms.b, p);
429
430 y2 == rhs
431}
432
433// ---------------------------------------------------------------------------
434// ECDSA public key
435// ---------------------------------------------------------------------------
436
437/// An ECDSA public key.
438#[derive(Debug, Clone)]
439pub struct EcdsaPublicKey {
440 /// The curve this key is on.
441 pub curve: Curve,
442 /// The public key point x-coordinate.
443 pub x: BigUint,
444 /// The public key point y-coordinate.
445 pub y: BigUint,
446}
447
448impl EcdsaPublicKey {
449 /// Create a public key from uncompressed point bytes (0x04 || x || y).
450 pub fn from_uncompressed(curve: Curve, data: &[u8]) -> Result<Self> {
451 let params = curve_params(curve);
452 let expected_len = 1 + 2 * params.field_len;
453
454 if data.len() != expected_len || data[0] != 0x04 {
455 return Err(EcdsaError::InvalidPublicKey);
456 }
457
458 let x = BigUint::from_be_bytes(&data[1..1 + params.field_len]);
459 let y = BigUint::from_be_bytes(&data[1 + params.field_len..]);
460
461 // Validate the point is on the curve.
462 if !is_on_curve(&x, &y, ¶ms) {
463 return Err(EcdsaError::InvalidPublicKey);
464 }
465
466 Ok(Self { curve, x, y })
467 }
468
469 /// Parse an EC public key from DER-encoded SubjectPublicKeyInfo.
470 ///
471 /// ```asn1
472 /// SubjectPublicKeyInfo ::= SEQUENCE {
473 /// algorithm AlgorithmIdentifier,
474 /// subjectPublicKey BIT STRING
475 /// }
476 /// AlgorithmIdentifier ::= SEQUENCE {
477 /// algorithm OID, -- ecPublicKey
478 /// parameters OID -- curve OID
479 /// }
480 /// ```
481 pub fn from_spki_der(data: &[u8]) -> Result<Self> {
482 let seq = asn1::parse_one(data)?;
483 if seq.tag != TAG_SEQUENCE {
484 return Err(EcdsaError::InvalidKeyFormat);
485 }
486
487 let items = seq.sequence_items()?;
488 if items.len() != 2 {
489 return Err(EcdsaError::InvalidKeyFormat);
490 }
491
492 // Parse AlgorithmIdentifier.
493 let alg_id = &items[0];
494 if alg_id.tag != TAG_SEQUENCE {
495 return Err(EcdsaError::InvalidKeyFormat);
496 }
497 let alg_items = alg_id.sequence_items()?;
498 if alg_items.len() < 2 {
499 return Err(EcdsaError::InvalidKeyFormat);
500 }
501
502 // Verify algorithm OID is ecPublicKey.
503 let alg_oid = alg_items[0].as_oid()?;
504 if !alg_oid.matches(OID_EC_PUBLIC_KEY) {
505 return Err(EcdsaError::InvalidKeyFormat);
506 }
507
508 // Determine curve from parameters OID.
509 let curve_oid = alg_items[1].as_oid()?;
510 let curve = if curve_oid.matches(OID_PRIME256V1) {
511 Curve::P256
512 } else if curve_oid.matches(OID_SECP384R1) {
513 Curve::P384
514 } else {
515 return Err(EcdsaError::UnsupportedCurve);
516 };
517
518 // Parse the BIT STRING containing the uncompressed point.
519 let (unused_bits, key_data) = items[1].as_bit_string()?;
520 if unused_bits != 0 {
521 return Err(EcdsaError::InvalidKeyFormat);
522 }
523
524 Self::from_uncompressed(curve, key_data)
525 }
526
527 /// Verify an ECDSA signature over a message.
528 ///
529 /// The hash algorithm is determined by the curve:
530 /// - P-256 uses SHA-256
531 /// - P-384 uses SHA-384
532 pub fn verify(&self, message: &[u8], signature: &EcdsaSignature) -> Result<()> {
533 let hash = match self.curve {
534 Curve::P256 => sha256(message).to_vec(),
535 Curve::P384 => sha384(message).to_vec(),
536 };
537
538 self.verify_prehashed(&hash, signature)
539 }
540
541 /// Verify an ECDSA signature given a pre-computed hash.
542 pub fn verify_prehashed(&self, hash: &[u8], signature: &EcdsaSignature) -> Result<()> {
543 let params = curve_params(self.curve);
544 let n = ¶ms.n;
545 let one = BigUint::one();
546
547 // Verify r, s in [1, n-1].
548 let n_minus_1 = n.sub(&one);
549 if signature.r.is_zero()
550 || signature.r.cmp(&n_minus_1) == Ordering::Greater
551 || signature.s.is_zero()
552 || signature.s.cmp(&n_minus_1) == Ordering::Greater
553 {
554 return Err(EcdsaError::SignatureOutOfRange);
555 }
556
557 // Convert hash to integer z (truncated to field length if needed).
558 let z = hash_to_integer(hash, ¶ms);
559
560 // w = s^(-1) mod n
561 let w = mod_inv(&signature.s, n);
562
563 // u1 = z * w mod n
564 let u1 = mod_mul(&z, &w, n);
565 // u2 = r * w mod n
566 let u2 = mod_mul(&signature.r, &w, n);
567
568 // (x1, y1) = u1*G + u2*Q
569 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
570 let q = JacobianPoint::from_affine(&self.x, &self.y);
571
572 let point = multi_scalar_mult(&u1, &g, &u2, &q, ¶ms);
573
574 if point.is_infinity() {
575 return Err(EcdsaError::VerificationFailed);
576 }
577
578 let (x1, _) = point.to_affine(¶ms.p);
579
580 // v = x1 mod n
581 let v = x1.modulo(n);
582
583 if v == signature.r {
584 Ok(())
585 } else {
586 Err(EcdsaError::VerificationFailed)
587 }
588 }
589}
590
591/// Convert hash bytes to an integer, truncating to the curve's bit length if needed.
592fn hash_to_integer(hash: &[u8], params: &CurveParams) -> BigUint {
593 let n_bits = params.n.bit_len();
594 let hash_bits = hash.len() * 8;
595
596 if hash_bits <= n_bits {
597 BigUint::from_be_bytes(hash)
598 } else {
599 // Truncate: take the leftmost n_bits bits.
600 let z = BigUint::from_be_bytes(hash);
601 let shift = hash_bits - n_bits;
602 // Right-shift by `shift` bits to keep only the top n_bits.
603 // Since BigUint doesn't expose a general right-shift, we use division.
604 let divisor = BigUint::from_u64(1u64 << shift.min(63));
605 if shift <= 63 {
606 z.div_rem(&divisor).0
607 } else {
608 // For very large shifts, this shouldn't happen with P-256/P-384 + SHA-256/384.
609 z
610 }
611 }
612}
613
614// ---------------------------------------------------------------------------
615// ECDSA signature
616// ---------------------------------------------------------------------------
617
618/// An ECDSA signature (r, s).
619#[derive(Debug, Clone)]
620pub struct EcdsaSignature {
621 pub r: BigUint,
622 pub s: BigUint,
623}
624
625impl EcdsaSignature {
626 /// Create from raw r, s values.
627 pub fn new(r: BigUint, s: BigUint) -> Self {
628 Self { r, s }
629 }
630
631 /// Parse from DER-encoded signature.
632 ///
633 /// ```asn1
634 /// ECDSA-Sig-Value ::= SEQUENCE {
635 /// r INTEGER,
636 /// s INTEGER
637 /// }
638 /// ```
639 pub fn from_der(data: &[u8]) -> Result<Self> {
640 let seq = asn1::parse_one(data)?;
641 if seq.tag != TAG_SEQUENCE {
642 return Err(EcdsaError::InvalidSignature);
643 }
644
645 let items = seq.sequence_items()?;
646 if items.len() != 2 {
647 return Err(EcdsaError::InvalidSignature);
648 }
649
650 let r_bytes = items[0]
651 .as_positive_integer()
652 .map_err(|_| EcdsaError::InvalidSignature)?;
653 let s_bytes = items[1]
654 .as_positive_integer()
655 .map_err(|_| EcdsaError::InvalidSignature)?;
656
657 Ok(Self {
658 r: BigUint::from_be_bytes(r_bytes),
659 s: BigUint::from_be_bytes(s_bytes),
660 })
661 }
662}
663
664// ---------------------------------------------------------------------------
665// Tests
666// ---------------------------------------------------------------------------
667
668#[cfg(test)]
669mod tests {
670 use super::*;
671
672 fn hex_to_bytes(s: &str) -> Vec<u8> {
673 let s = s.replace(' ', "").replace('\n', "");
674 (0..s.len())
675 .step_by(2)
676 .map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
677 .collect()
678 }
679
680 // -------------------------------------------------------------------
681 // Curve parameter validation
682 // -------------------------------------------------------------------
683
684 #[test]
685 fn p256_generator_on_curve() {
686 let params = p256_params();
687 assert!(is_on_curve(¶ms.gx, ¶ms.gy, ¶ms));
688 }
689
690 #[test]
691 fn p384_generator_on_curve() {
692 let params = p384_params();
693 assert!(is_on_curve(¶ms.gx, ¶ms.gy, ¶ms));
694 }
695
696 // -------------------------------------------------------------------
697 // Point arithmetic
698 // -------------------------------------------------------------------
699
700 #[test]
701 fn point_double_p256() {
702 let params = p256_params();
703 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
704 let two_g = point_double(&g, ¶ms);
705 let (x, y) = two_g.to_affine(¶ms.p);
706 assert!(is_on_curve(&x, &y, ¶ms));
707 }
708
709 #[test]
710 fn point_add_p256() {
711 let params = p256_params();
712 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
713 let two_g = point_double(&g, ¶ms);
714 let three_g = point_add(&two_g, &g, ¶ms);
715 let (x, y) = three_g.to_affine(¶ms.p);
716 assert!(is_on_curve(&x, &y, ¶ms));
717 }
718
719 #[test]
720 fn scalar_mult_identity() {
721 let params = p256_params();
722 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
723 let result = scalar_mult(&BigUint::one(), &g, ¶ms);
724 let (x, y) = result.to_affine(¶ms.p);
725 assert_eq!(x, params.gx);
726 assert_eq!(y, params.gy);
727 }
728
729 #[test]
730 fn scalar_mult_two() {
731 let params = p256_params();
732 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
733 let two_g_direct = point_double(&g, ¶ms);
734 let two_g_scalar = scalar_mult(&BigUint::from_u64(2), &g, ¶ms);
735
736 let (x1, y1) = two_g_direct.to_affine(¶ms.p);
737 let (x2, y2) = two_g_scalar.to_affine(¶ms.p);
738 assert_eq!(x1, x2);
739 assert_eq!(y1, y2);
740 }
741
742 #[test]
743 fn scalar_mult_three() {
744 let params = p256_params();
745 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
746 let two_g = point_double(&g, ¶ms);
747 let three_g_direct = point_add(&two_g, &g, ¶ms);
748 let three_g_scalar = scalar_mult(&BigUint::from_u64(3), &g, ¶ms);
749
750 let (x1, y1) = three_g_direct.to_affine(¶ms.p);
751 let (x2, y2) = three_g_scalar.to_affine(¶ms.p);
752 assert_eq!(x1, x2);
753 assert_eq!(y1, y2);
754 }
755
756 #[test]
757 fn scalar_mult_order_gives_infinity() {
758 // n * G = O (point at infinity)
759 let params = p256_params();
760 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
761 let result = scalar_mult(¶ms.n, &g, ¶ms);
762 assert!(result.is_infinity());
763 }
764
765 #[test]
766 fn point_add_inverse_gives_infinity() {
767 // G + (-G) = O
768 let params = p256_params();
769 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
770 // -G has y-coordinate = p - Gy
771 let neg_gy = params.p.sub(¶ms.gy);
772 let neg_g = JacobianPoint::from_affine(¶ms.gx, &neg_gy);
773 let result = point_add(&g, &neg_g, ¶ms);
774 assert!(result.is_infinity());
775 }
776
777 // -------------------------------------------------------------------
778 // Public key parsing and validation
779 // -------------------------------------------------------------------
780
781 #[test]
782 fn public_key_from_uncompressed_p256() {
783 let params = p256_params();
784 let mut data = vec![0x04];
785 data.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
786 data.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
787
788 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &data).unwrap();
789 assert_eq!(key.x, params.gx);
790 assert_eq!(key.y, params.gy);
791 }
792
793 #[test]
794 fn public_key_reject_not_on_curve() {
795 let mut data = vec![0x04];
796 data.extend_from_slice(&[0x01; 32]); // x = 1
797 data.extend_from_slice(&[0x01; 32]); // y = 1
798 let result = EcdsaPublicKey::from_uncompressed(Curve::P256, &data);
799 assert_eq!(result.unwrap_err(), EcdsaError::InvalidPublicKey);
800 }
801
802 #[test]
803 fn public_key_reject_wrong_prefix() {
804 let params = p256_params();
805 let mut data = vec![0x03]; // compressed, not supported
806 data.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
807 data.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
808
809 let result = EcdsaPublicKey::from_uncompressed(Curve::P256, &data);
810 assert_eq!(result.unwrap_err(), EcdsaError::InvalidPublicKey);
811 }
812
813 #[test]
814 fn public_key_reject_wrong_length() {
815 let result = EcdsaPublicKey::from_uncompressed(Curve::P256, &[0x04; 10]);
816 assert_eq!(result.unwrap_err(), EcdsaError::InvalidPublicKey);
817 }
818
819 // -------------------------------------------------------------------
820 // Signature parsing
821 // -------------------------------------------------------------------
822
823 #[test]
824 fn signature_from_der() {
825 // SEQUENCE { INTEGER 1, INTEGER 2 }
826 let der = hex_to_bytes("3006020101020102");
827 let sig = EcdsaSignature::from_der(&der).unwrap();
828 assert_eq!(sig.r, BigUint::from_u64(1));
829 assert_eq!(sig.s, BigUint::from_u64(2));
830 }
831
832 #[test]
833 fn signature_reject_invalid_der() {
834 let result = EcdsaSignature::from_der(&[0x00, 0x01]);
835 assert!(result.is_err());
836 }
837
838 // -------------------------------------------------------------------
839 // ECDSA verification: self-generated test vectors
840 // -------------------------------------------------------------------
841
842 /// Generate an ECDSA signature for testing (not for production use).
843 /// Returns (public_key_x, public_key_y, r, s).
844 fn sign_for_test(
845 d: &BigUint,
846 k: &BigUint,
847 msg_hash: &[u8],
848 params: &CurveParams,
849 ) -> (BigUint, BigUint, BigUint, BigUint) {
850 let n = ¶ms.n;
851 let g = JacobianPoint::from_affine(¶ms.gx, ¶ms.gy);
852
853 // Q = d*G
854 let q = scalar_mult(d, &g, params);
855 let (qx, qy) = q.to_affine(¶ms.p);
856
857 // (rx, _) = k*G
858 let kg = scalar_mult(k, &g, params);
859 let (kgx, _) = kg.to_affine(¶ms.p);
860 let r = kgx.modulo(n);
861
862 // s = k^(-1) * (z + r*d) mod n
863 let z = hash_to_integer(msg_hash, params);
864 let k_inv = mod_inv(k, n);
865 let rd = mod_mul(&r, d, n);
866 let z_plus_rd = mod_add(&z, &rd, n);
867 let s = mod_mul(&k_inv, &z_plus_rd, n);
868
869 (qx, qy, r, s)
870 }
871
872 #[test]
873 fn verify_self_generated_p256() {
874 let params = p256_params();
875 let msg = b"hello world";
876 let hash = sha256(msg);
877
878 // Arbitrary private key and nonce (must be in [1, n-1]).
879 let d = BigUint::from_u64(123456789);
880 let k = BigUint::from_u64(987654321);
881
882 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
883
884 let mut point_data = vec![0x04];
885 point_data.extend_from_slice(&qx.to_be_bytes_padded(32));
886 point_data.extend_from_slice(&qy.to_be_bytes_padded(32));
887 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
888
889 let sig = EcdsaSignature::new(r, s);
890 let result = key.verify(msg, &sig);
891 assert!(
892 result.is_ok(),
893 "self-generated P-256 signature should verify: {result:?}"
894 );
895 }
896
897 #[test]
898 fn verify_self_generated_p256_different_key() {
899 let params = p256_params();
900 let msg = b"test message for ecdsa verification";
901 let hash = sha256(msg);
902
903 let d = BigUint::from_be_bytes(&hex_to_bytes(
904 "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
905 ));
906 let k = BigUint::from_be_bytes(&hex_to_bytes(
907 "a6e3c57dd01abe90086538398355dd4c3b17aa873382b0f24d6129493d8aad60",
908 ));
909
910 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
911
912 let mut point_data = vec![0x04];
913 point_data.extend_from_slice(&qx.to_be_bytes_padded(32));
914 point_data.extend_from_slice(&qy.to_be_bytes_padded(32));
915 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
916
917 let sig = EcdsaSignature::new(r, s);
918 let result = key.verify(msg, &sig);
919 assert!(
920 result.is_ok(),
921 "P-256 with RFC 6979 key should verify: {result:?}"
922 );
923 }
924
925 #[test]
926 fn verify_self_generated_p256_tampered() {
927 let params = p256_params();
928 let msg = b"hello world";
929 let hash = sha256(msg);
930
931 let d = BigUint::from_u64(123456789);
932 let k = BigUint::from_u64(987654321);
933
934 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
935
936 let mut point_data = vec![0x04];
937 point_data.extend_from_slice(&qx.to_be_bytes_padded(32));
938 point_data.extend_from_slice(&qy.to_be_bytes_padded(32));
939 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
940
941 // Tamper with the signature.
942 let tampered_s = s.add(&BigUint::one());
943 let sig = EcdsaSignature::new(r, tampered_s);
944 let result = key.verify(msg, &sig);
945 assert!(result.is_err(), "tampered signature should fail");
946 }
947
948 #[test]
949 fn verify_self_generated_p256_wrong_message() {
950 let params = p256_params();
951 let msg = b"hello world";
952 let hash = sha256(msg);
953
954 let d = BigUint::from_u64(123456789);
955 let k = BigUint::from_u64(987654321);
956
957 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
958
959 let mut point_data = vec![0x04];
960 point_data.extend_from_slice(&qx.to_be_bytes_padded(32));
961 point_data.extend_from_slice(&qy.to_be_bytes_padded(32));
962 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
963
964 let sig = EcdsaSignature::new(r, s);
965 let result = key.verify(b"wrong message", &sig);
966 assert!(result.is_err(), "wrong message should fail verification");
967 }
968
969 #[test]
970 fn verify_self_generated_p384() {
971 let params = p384_params();
972 let msg = b"hello p384";
973 let hash = sha384(msg);
974
975 let d = BigUint::from_u64(999999937); // a prime number
976 let k = BigUint::from_u64(999999893); // another prime
977
978 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
979
980 let mut point_data = vec![0x04];
981 point_data.extend_from_slice(&qx.to_be_bytes_padded(48));
982 point_data.extend_from_slice(&qy.to_be_bytes_padded(48));
983 let key = EcdsaPublicKey::from_uncompressed(Curve::P384, &point_data).unwrap();
984
985 let sig = EcdsaSignature::new(r, s);
986 let result = key.verify(msg, &sig);
987 assert!(
988 result.is_ok(),
989 "self-generated P-384 signature should verify: {result:?}"
990 );
991 }
992
993 #[test]
994 fn verify_self_generated_p384_wrong_message() {
995 let params = p384_params();
996 let msg = b"hello p384";
997 let hash = sha384(msg);
998
999 let d = BigUint::from_u64(999999937);
1000 let k = BigUint::from_u64(999999893);
1001
1002 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
1003
1004 let mut point_data = vec![0x04];
1005 point_data.extend_from_slice(&qx.to_be_bytes_padded(48));
1006 point_data.extend_from_slice(&qy.to_be_bytes_padded(48));
1007 let key = EcdsaPublicKey::from_uncompressed(Curve::P384, &point_data).unwrap();
1008
1009 let sig = EcdsaSignature::new(r, s);
1010 let result = key.verify(b"different message", &sig);
1011 assert!(result.is_err(), "P-384 wrong message should fail");
1012 }
1013
1014 // -------------------------------------------------------------------
1015 // RFC 6979 test vector (P-256, SHA-256, message "sample")
1016 // -------------------------------------------------------------------
1017
1018 #[test]
1019 fn verify_rfc6979_p256_sha256() {
1020 // From RFC 6979, Appendix A.2.5.
1021 // Private key x = C9AFA9D845BA75166B5C215767B1D6934E50C3DB36E89B127B8A622B120F6721
1022 // Public key:
1023 let qx = hex_to_bytes("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6");
1024 let qy = hex_to_bytes("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299");
1025 // Message: "sample", signature with SHA-256:
1026 let r = hex_to_bytes("EFD48B2AACB6A8FD1140DD9CD45E81D69D2C877B56AAF991C34D0EA84EAF3716");
1027 let s = hex_to_bytes("F7CB1C942D657C41D436C7A1B6E29F65F3E900DBB9AFF4064DC4AB2F843ACDA8");
1028
1029 let mut point_data = vec![0x04];
1030 point_data.extend_from_slice(&qx);
1031 point_data.extend_from_slice(&qy);
1032 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1033
1034 let sig = EcdsaSignature::new(BigUint::from_be_bytes(&r), BigUint::from_be_bytes(&s));
1035
1036 let result = key.verify(b"sample", &sig);
1037 assert!(
1038 result.is_ok(),
1039 "RFC 6979 P-256/SHA-256 'sample' should verify: {result:?}"
1040 );
1041 }
1042
1043 #[test]
1044 fn verify_rfc6979_p256_sha256_test() {
1045 // From RFC 6979, Appendix A.2.5, message "test".
1046 let qx = hex_to_bytes("60FED4BA255A9D31C961EB74C6356D68C049B8923B61FA6CE669622E60F29FB6");
1047 let qy = hex_to_bytes("7903FE1008B8BC99A41AE9E95628BC64F2F1B20C2D7E9F5177A3C294D4462299");
1048 let r = hex_to_bytes("F1ABB023518351CD71D881567B1EA663ED3EFCF6C5132B354F28D3B0B7D38367");
1049 let s = hex_to_bytes("019F4113742A2B14BD25926B49C649155F267E60D3814B4C0CC84250E46F0083");
1050
1051 let mut point_data = vec![0x04];
1052 point_data.extend_from_slice(&qx);
1053 point_data.extend_from_slice(&qy);
1054 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1055
1056 let sig = EcdsaSignature::new(BigUint::from_be_bytes(&r), BigUint::from_be_bytes(&s));
1057
1058 let result = key.verify(b"test", &sig);
1059 assert!(
1060 result.is_ok(),
1061 "RFC 6979 P-256/SHA-256 'test' should verify: {result:?}"
1062 );
1063 }
1064
1065 // -------------------------------------------------------------------
1066 // Prehashed verification
1067 // -------------------------------------------------------------------
1068
1069 #[test]
1070 fn verify_prehashed_p256() {
1071 let params = p256_params();
1072 let msg = b"prehash test";
1073 let hash = sha256(msg);
1074
1075 let d = BigUint::from_u64(42424242);
1076 let k = BigUint::from_u64(13131313);
1077
1078 let (qx, qy, r, s) = sign_for_test(&d, &k, &hash, ¶ms);
1079
1080 let mut point_data = vec![0x04];
1081 point_data.extend_from_slice(&qx.to_be_bytes_padded(32));
1082 point_data.extend_from_slice(&qy.to_be_bytes_padded(32));
1083 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1084
1085 let sig = EcdsaSignature::new(r, s);
1086 let result = key.verify_prehashed(&hash, &sig);
1087 assert!(result.is_ok(), "prehashed verify should pass: {result:?}");
1088 }
1089
1090 // -------------------------------------------------------------------
1091 // Signature rejection tests
1092 // -------------------------------------------------------------------
1093
1094 #[test]
1095 fn reject_signature_r_zero() {
1096 let params = p256_params();
1097 let mut point_data = vec![0x04];
1098 point_data.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
1099 point_data.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
1100 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1101
1102 let sig = EcdsaSignature::new(BigUint::zero(), BigUint::one());
1103 let result = key.verify(b"test", &sig);
1104 assert_eq!(result.unwrap_err(), EcdsaError::SignatureOutOfRange);
1105 }
1106
1107 #[test]
1108 fn reject_signature_s_zero() {
1109 let params = p256_params();
1110 let mut point_data = vec![0x04];
1111 point_data.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
1112 point_data.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
1113 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1114
1115 let sig = EcdsaSignature::new(BigUint::one(), BigUint::zero());
1116 let result = key.verify(b"test", &sig);
1117 assert_eq!(result.unwrap_err(), EcdsaError::SignatureOutOfRange);
1118 }
1119
1120 #[test]
1121 fn reject_signature_r_equals_n() {
1122 let params = p256_params();
1123 let mut point_data = vec![0x04];
1124 point_data.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
1125 point_data.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
1126 let key = EcdsaPublicKey::from_uncompressed(Curve::P256, &point_data).unwrap();
1127
1128 let sig = EcdsaSignature::new(params.n.clone(), BigUint::one());
1129 let result = key.verify(b"test", &sig);
1130 assert_eq!(result.unwrap_err(), EcdsaError::SignatureOutOfRange);
1131 }
1132
1133 // -------------------------------------------------------------------
1134 // SPKI DER parsing
1135 // -------------------------------------------------------------------
1136
1137 /// Encode a DER length.
1138 fn der_length(len: usize) -> Vec<u8> {
1139 if len < 0x80 {
1140 vec![len as u8]
1141 } else if len < 0x100 {
1142 vec![0x81, len as u8]
1143 } else {
1144 vec![0x82, (len >> 8) as u8, len as u8]
1145 }
1146 }
1147
1148 /// Build a DER TLV.
1149 fn der_tlv(tag: u8, value: &[u8]) -> Vec<u8> {
1150 let mut out = vec![tag];
1151 out.extend_from_slice(&der_length(value.len()));
1152 out.extend_from_slice(value);
1153 out
1154 }
1155
1156 #[test]
1157 fn parse_spki_p256() {
1158 let params = p256_params();
1159
1160 // Build AlgorithmIdentifier: SEQUENCE { OID ecPublicKey, OID prime256v1 }
1161 let alg_oid = der_tlv(0x06, OID_EC_PUBLIC_KEY);
1162 let curve_oid = der_tlv(0x06, OID_PRIME256V1);
1163 let mut alg_content = Vec::new();
1164 alg_content.extend_from_slice(&alg_oid);
1165 alg_content.extend_from_slice(&curve_oid);
1166 let alg_id = der_tlv(0x30, &alg_content);
1167
1168 // Build BIT STRING with uncompressed point.
1169 let mut point = vec![0x04];
1170 point.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
1171 point.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
1172 let mut bit_string_value = vec![0x00]; // unused bits
1173 bit_string_value.extend_from_slice(&point);
1174 let bit_string = der_tlv(0x03, &bit_string_value);
1175
1176 let mut spki_content = Vec::new();
1177 spki_content.extend_from_slice(&alg_id);
1178 spki_content.extend_from_slice(&bit_string);
1179 let spki = der_tlv(0x30, &spki_content);
1180
1181 let key = EcdsaPublicKey::from_spki_der(&spki).unwrap();
1182 assert_eq!(key.curve, Curve::P256);
1183 assert_eq!(key.x, params.gx);
1184 assert_eq!(key.y, params.gy);
1185 }
1186
1187 #[test]
1188 fn parse_spki_p384() {
1189 let params = p384_params();
1190
1191 let alg_oid = der_tlv(0x06, OID_EC_PUBLIC_KEY);
1192 let curve_oid = der_tlv(0x06, OID_SECP384R1);
1193 let mut alg_content = Vec::new();
1194 alg_content.extend_from_slice(&alg_oid);
1195 alg_content.extend_from_slice(&curve_oid);
1196 let alg_id = der_tlv(0x30, &alg_content);
1197
1198 let mut point = vec![0x04];
1199 point.extend_from_slice(¶ms.gx.to_be_bytes_padded(48));
1200 point.extend_from_slice(¶ms.gy.to_be_bytes_padded(48));
1201 let mut bit_string_value = vec![0x00];
1202 bit_string_value.extend_from_slice(&point);
1203 let bit_string = der_tlv(0x03, &bit_string_value);
1204
1205 let mut spki_content = Vec::new();
1206 spki_content.extend_from_slice(&alg_id);
1207 spki_content.extend_from_slice(&bit_string);
1208 let spki = der_tlv(0x30, &spki_content);
1209
1210 let key = EcdsaPublicKey::from_spki_der(&spki).unwrap();
1211 assert_eq!(key.curve, Curve::P384);
1212 assert_eq!(key.x, params.gx);
1213 assert_eq!(key.y, params.gy);
1214 }
1215
1216 #[test]
1217 fn parse_spki_reject_wrong_algorithm() {
1218 let params = p256_params();
1219
1220 // Use RSA OID instead of ecPublicKey.
1221 let wrong_oid = der_tlv(
1222 0x06,
1223 &[0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01],
1224 );
1225 let curve_oid = der_tlv(0x06, OID_PRIME256V1);
1226 let mut alg_content = Vec::new();
1227 alg_content.extend_from_slice(&wrong_oid);
1228 alg_content.extend_from_slice(&curve_oid);
1229 let alg_id = der_tlv(0x30, &alg_content);
1230
1231 let mut point = vec![0x04];
1232 point.extend_from_slice(¶ms.gx.to_be_bytes_padded(32));
1233 point.extend_from_slice(¶ms.gy.to_be_bytes_padded(32));
1234 let mut bit_string_value = vec![0x00];
1235 bit_string_value.extend_from_slice(&point);
1236 let bit_string = der_tlv(0x03, &bit_string_value);
1237
1238 let mut spki_content = Vec::new();
1239 spki_content.extend_from_slice(&alg_id);
1240 spki_content.extend_from_slice(&bit_string);
1241 let spki = der_tlv(0x30, &spki_content);
1242
1243 let result = EcdsaPublicKey::from_spki_der(&spki);
1244 assert_eq!(result.unwrap_err(), EcdsaError::InvalidKeyFormat);
1245 }
1246
1247 // -------------------------------------------------------------------
1248 // Modular arithmetic
1249 // -------------------------------------------------------------------
1250
1251 #[test]
1252 fn mod_sub_no_underflow() {
1253 let a = BigUint::from_u64(10);
1254 let b = BigUint::from_u64(3);
1255 let m = BigUint::from_u64(17);
1256 assert_eq!(mod_sub(&a, &b, &m), BigUint::from_u64(7));
1257 }
1258
1259 #[test]
1260 fn mod_sub_with_underflow() {
1261 let a = BigUint::from_u64(3);
1262 let b = BigUint::from_u64(10);
1263 let m = BigUint::from_u64(17);
1264 // 3 - 10 mod 17 = -7 mod 17 = 10
1265 assert_eq!(mod_sub(&a, &b, &m), BigUint::from_u64(10));
1266 }
1267
1268 #[test]
1269 fn mod_inv_basic() {
1270 // 3^(-1) mod 7 = 5 (since 3*5 = 15 = 1 mod 7)
1271 let a = BigUint::from_u64(3);
1272 let m = BigUint::from_u64(7);
1273 let inv = mod_inv(&a, &m);
1274 assert_eq!(inv, BigUint::from_u64(5));
1275 }
1276
1277 #[test]
1278 fn mod_inv_verify() {
1279 // a * a^(-1) mod p = 1
1280 let a = BigUint::from_u64(12345);
1281 let p = BigUint::from_u64(65537); // prime
1282 let inv = mod_inv(&a, &p);
1283 let product = mod_mul(&a, &inv, &p);
1284 assert_eq!(product, BigUint::one());
1285 }
1286}