we (web engine): Experimental web browser project to understand the limits of Claude
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

Implement ECDSA signature verification for P-256 and P-384 (FIPS 186-4)

Elliptic curve arithmetic in Jacobian coordinates with point addition,
doubling, and Montgomery ladder scalar multiplication. Field arithmetic
over the prime fields of P-256 (secp256r1) and P-384 (secp384r1) using
the existing BigUint type.

ECDSA verification per FIPS 186-4 §4.1.4 with Shamir's trick for
multi-scalar multiplication. Parses EC public keys from uncompressed
point format and SubjectPublicKeyInfo DER. Parses DER-encoded
ECDSA signatures.

34 tests including: curve parameter validation, point arithmetic,
RFC 6979 test vectors (P-256/SHA-256), self-generated test vectors
for both P-256 and P-384, signature rejection, and DER parsing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

authored by pierrelf.com

Claude Opus 4.6 and committed by tangled.org 914960d3 fcaa03b8

+1288 -1
+1286
crates/crypto/src/ecdsa.rs
··· 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 + 6 + use crate::asn1::{ 7 + self, Asn1Error, OID_EC_PUBLIC_KEY, OID_PRIME256V1, OID_SECP384R1, TAG_SEQUENCE, 8 + }; 9 + use crate::bigint::BigUint; 10 + use crate::sha2::{sha256, sha384}; 11 + 12 + use core::cmp::Ordering; 13 + use core::fmt; 14 + 15 + // --------------------------------------------------------------------------- 16 + // Error type 17 + // --------------------------------------------------------------------------- 18 + 19 + #[derive(Debug, Clone, PartialEq, Eq)] 20 + pub 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 + 37 + impl 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 + 51 + impl From<Asn1Error> for EcdsaError { 52 + fn from(e: Asn1Error) -> Self { 53 + Self::Asn1(e) 54 + } 55 + } 56 + 57 + pub 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)] 65 + pub enum Curve { 66 + P256, 67 + P384, 68 + } 69 + 70 + // --------------------------------------------------------------------------- 71 + // Modular arithmetic helpers (over BigUint) 72 + // --------------------------------------------------------------------------- 73 + 74 + fn mod_add(a: &BigUint, b: &BigUint, m: &BigUint) -> BigUint { 75 + a.add(b).modulo(m) 76 + } 77 + 78 + fn 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 + 87 + fn 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 93 + fn 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 + 102 + struct 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 + 119 + fn 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 + 126 + fn 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 + 156 + fn 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 + 186 + fn 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)] 200 + struct JacobianPoint { 201 + x: BigUint, 202 + y: BigUint, 203 + z: BigUint, 204 + } 205 + 206 + impl 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). 249 + fn point_double(pt: &JacobianPoint, params: &CurveParams) -> JacobianPoint { 250 + let p = &params.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. 289 + fn point_add(p1: &JacobianPoint, p2: &JacobianPoint, params: &CurveParams) -> JacobianPoint { 290 + let p = &params.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))] 353 + fn 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. 380 + fn 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). 418 + fn is_on_curve(x: &BigUint, y: &BigUint, params: &CurveParams) -> bool { 419 + let p = &params.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(&params.a, x, p); 428 + let rhs = mod_add(&mod_add(&x3, &ax, p), &params.b, p); 429 + 430 + y2 == rhs 431 + } 432 + 433 + // --------------------------------------------------------------------------- 434 + // ECDSA public key 435 + // --------------------------------------------------------------------------- 436 + 437 + /// An ECDSA public key. 438 + #[derive(Debug, Clone)] 439 + pub 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 + 448 + impl 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, &params) { 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 = &params.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, &params); 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(&params.gx, &params.gy); 570 + let q = JacobianPoint::from_affine(&self.x, &self.y); 571 + 572 + let point = multi_scalar_mult(&u1, &g, &u2, &q, &params); 573 + 574 + if point.is_infinity() { 575 + return Err(EcdsaError::VerificationFailed); 576 + } 577 + 578 + let (x1, _) = point.to_affine(&params.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. 592 + fn 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)] 620 + pub struct EcdsaSignature { 621 + pub r: BigUint, 622 + pub s: BigUint, 623 + } 624 + 625 + impl 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)] 669 + mod 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(&params.gx, &params.gy, &params)); 688 + } 689 + 690 + #[test] 691 + fn p384_generator_on_curve() { 692 + let params = p384_params(); 693 + assert!(is_on_curve(&params.gx, &params.gy, &params)); 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(&params.gx, &params.gy); 704 + let two_g = point_double(&g, &params); 705 + let (x, y) = two_g.to_affine(&params.p); 706 + assert!(is_on_curve(&x, &y, &params)); 707 + } 708 + 709 + #[test] 710 + fn point_add_p256() { 711 + let params = p256_params(); 712 + let g = JacobianPoint::from_affine(&params.gx, &params.gy); 713 + let two_g = point_double(&g, &params); 714 + let three_g = point_add(&two_g, &g, &params); 715 + let (x, y) = three_g.to_affine(&params.p); 716 + assert!(is_on_curve(&x, &y, &params)); 717 + } 718 + 719 + #[test] 720 + fn scalar_mult_identity() { 721 + let params = p256_params(); 722 + let g = JacobianPoint::from_affine(&params.gx, &params.gy); 723 + let result = scalar_mult(&BigUint::one(), &g, &params); 724 + let (x, y) = result.to_affine(&params.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(&params.gx, &params.gy); 733 + let two_g_direct = point_double(&g, &params); 734 + let two_g_scalar = scalar_mult(&BigUint::from_u64(2), &g, &params); 735 + 736 + let (x1, y1) = two_g_direct.to_affine(&params.p); 737 + let (x2, y2) = two_g_scalar.to_affine(&params.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(&params.gx, &params.gy); 746 + let two_g = point_double(&g, &params); 747 + let three_g_direct = point_add(&two_g, &g, &params); 748 + let three_g_scalar = scalar_mult(&BigUint::from_u64(3), &g, &params); 749 + 750 + let (x1, y1) = three_g_direct.to_affine(&params.p); 751 + let (x2, y2) = three_g_scalar.to_affine(&params.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(&params.gx, &params.gy); 761 + let result = scalar_mult(&params.n, &g, &params); 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(&params.gx, &params.gy); 770 + // -G has y-coordinate = p - Gy 771 + let neg_gy = params.p.sub(&params.gy); 772 + let neg_g = JacobianPoint::from_affine(&params.gx, &neg_gy); 773 + let result = point_add(&g, &neg_g, &params); 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(&params.gx.to_be_bytes_padded(32)); 786 + data.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(32)); 807 + data.extend_from_slice(&params.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 = &params.n; 851 + let g = JacobianPoint::from_affine(&params.gx, &params.gy); 852 + 853 + // Q = d*G 854 + let q = scalar_mult(d, &g, params); 855 + let (qx, qy) = q.to_affine(&params.p); 856 + 857 + // (rx, _) = k*G 858 + let kg = scalar_mult(k, &g, params); 859 + let (kgx, _) = kg.to_affine(&params.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, &params); 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, &params); 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, &params); 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, &params); 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, &params); 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, &params); 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, &params); 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(&params.gx.to_be_bytes_padded(32)); 1099 + point_data.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(32)); 1112 + point_data.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(32)); 1125 + point_data.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(32)); 1171 + point.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(48)); 1200 + point.extend_from_slice(&params.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(&params.gx.to_be_bytes_padded(32)); 1233 + point.extend_from_slice(&params.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 + }
+2 -1
crates/crypto/src/lib.rs
··· 1 - //! Pure Rust cryptography — AES-GCM, ChaCha20-Poly1305, SHA-2, X25519, RSA, X.509, ASN.1. 1 + //! Pure Rust cryptography — AES-GCM, ChaCha20-Poly1305, SHA-2, X25519, RSA, ECDSA, X.509, ASN.1. 2 2 3 3 pub mod aes_gcm; 4 4 pub mod asn1; 5 5 pub mod bigint; 6 6 pub mod chacha20_poly1305; 7 + pub mod ecdsa; 7 8 pub mod hkdf; 8 9 pub mod hmac; 9 10 pub mod rsa;