we (web engine): Experimental web browser project to understand the limits of Claude
at http-parser 835 lines 29 kB view raw
1//! RSA PKCS#1 v1.5 signature verification (RFC 8017). 2//! 3//! Supports RSA key sizes of 2048, 3072, and 4096 bits. Parses public keys 4//! from DER-encoded PKCS#1 RSAPublicKey and PKCS#8 SubjectPublicKeyInfo 5//! formats. Verifies RSASSA-PKCS1-v1_5 signatures with SHA-256 and SHA-384. 6 7use crate::asn1::{ 8 self, Asn1Error, OID_RSA_ENCRYPTION, OID_SHA256, OID_SHA384, OID_SHA512, TAG_NULL, TAG_SEQUENCE, 9}; 10use crate::bigint::BigUint; 11use crate::sha2::{sha256, sha384, sha512}; 12 13use core::fmt; 14 15// --------------------------------------------------------------------------- 16// Error type 17// --------------------------------------------------------------------------- 18 19#[derive(Debug, Clone, PartialEq, Eq)] 20pub enum RsaError { 21 /// ASN.1 parsing error. 22 Asn1(Asn1Error), 23 /// RSA modulus is too small (< 2048 bits). 24 ModulusTooSmall, 25 /// RSA modulus is too large (> 4096 bits). 26 ModulusTooLarge, 27 /// Public exponent is invalid (not odd, or too small). 28 InvalidExponent, 29 /// Signature length doesn't match modulus length. 30 InvalidSignatureLength, 31 /// Signature value is >= modulus. 32 SignatureOutOfRange, 33 /// EMSA-PKCS1-v1_5 encoding verification failed. 34 InvalidPadding, 35 /// DigestInfo doesn't match the expected hash. 36 DigestMismatch, 37 /// Unsupported hash algorithm. 38 UnsupportedAlgorithm, 39 /// Invalid key format. 40 InvalidKeyFormat, 41} 42 43impl fmt::Display for RsaError { 44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 45 match self { 46 Self::Asn1(e) => write!(f, "ASN.1 error: {e}"), 47 Self::ModulusTooSmall => write!(f, "RSA modulus too small (< 2048 bits)"), 48 Self::ModulusTooLarge => write!(f, "RSA modulus too large (> 4096 bits)"), 49 Self::InvalidExponent => write!(f, "invalid RSA public exponent"), 50 Self::InvalidSignatureLength => write!(f, "signature length doesn't match modulus"), 51 Self::SignatureOutOfRange => write!(f, "signature value >= modulus"), 52 Self::InvalidPadding => write!(f, "invalid PKCS#1 v1.5 padding"), 53 Self::DigestMismatch => write!(f, "digest mismatch"), 54 Self::UnsupportedAlgorithm => write!(f, "unsupported hash algorithm"), 55 Self::InvalidKeyFormat => write!(f, "invalid RSA key format"), 56 } 57 } 58} 59 60impl From<Asn1Error> for RsaError { 61 fn from(e: Asn1Error) -> Self { 62 Self::Asn1(e) 63 } 64} 65 66pub type Result<T> = core::result::Result<T, RsaError>; 67 68// --------------------------------------------------------------------------- 69// Hash algorithm identifiers 70// --------------------------------------------------------------------------- 71 72/// Hash algorithm used with RSA signature verification. 73#[derive(Debug, Clone, Copy, PartialEq, Eq)] 74pub enum HashAlgorithm { 75 Sha256, 76 Sha384, 77 Sha512, 78} 79 80impl HashAlgorithm { 81 /// DER-encoded DigestInfo prefix (AlgorithmIdentifier + digest wrapper) 82 /// per RFC 8017 Section 9.2 Notes. 83 fn digest_info_prefix(&self) -> &'static [u8] { 84 match self { 85 // DigestInfo for SHA-256: 86 // SEQUENCE { SEQUENCE { OID sha256, NULL }, OCTET STRING (32 bytes) } 87 Self::Sha256 => &[ 88 0x30, 0x31, // SEQUENCE, 49 bytes 89 0x30, 0x0d, // SEQUENCE, 13 bytes 90 0x06, 0x09, // OID, 9 bytes 91 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, // sha256 92 0x05, 0x00, // NULL 93 0x04, 0x20, // OCTET STRING, 32 bytes 94 ], 95 // DigestInfo for SHA-384: 96 Self::Sha384 => &[ 97 0x30, 0x41, // SEQUENCE, 65 bytes 98 0x30, 0x0d, // SEQUENCE, 13 bytes 99 0x06, 0x09, // OID, 9 bytes 100 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, // sha384 101 0x05, 0x00, // NULL 102 0x04, 0x30, // OCTET STRING, 48 bytes 103 ], 104 // DigestInfo for SHA-512: 105 Self::Sha512 => &[ 106 0x30, 0x51, // SEQUENCE, 81 bytes 107 0x30, 0x0d, // SEQUENCE, 13 bytes 108 0x06, 0x09, // OID, 9 bytes 109 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, // sha512 110 0x05, 0x00, // NULL 111 0x04, 0x40, // OCTET STRING, 64 bytes 112 ], 113 } 114 } 115 116 /// Compute the hash of the given message. 117 fn hash(&self, msg: &[u8]) -> Vec<u8> { 118 match self { 119 Self::Sha256 => sha256(msg).to_vec(), 120 Self::Sha384 => sha384(msg).to_vec(), 121 Self::Sha512 => sha512(msg).to_vec(), 122 } 123 } 124 125 /// Length of the hash output in bytes. 126 fn hash_len(&self) -> usize { 127 match self { 128 Self::Sha256 => 32, 129 Self::Sha384 => 48, 130 Self::Sha512 => 64, 131 } 132 } 133} 134 135// --------------------------------------------------------------------------- 136// RSA public key 137// --------------------------------------------------------------------------- 138 139/// An RSA public key. 140#[derive(Debug, Clone)] 141pub struct RsaPublicKey { 142 /// The modulus n. 143 pub n: BigUint, 144 /// The public exponent e. 145 pub e: BigUint, 146 /// Key size in bytes (octet length of modulus). 147 pub key_len: usize, 148} 149 150impl RsaPublicKey { 151 /// Parse an RSA public key from DER-encoded PKCS#1 RSAPublicKey format. 152 /// 153 /// ```asn1 154 /// RSAPublicKey ::= SEQUENCE { 155 /// modulus INTEGER, 156 /// publicExponent INTEGER 157 /// } 158 /// ``` 159 pub fn from_pkcs1_der(data: &[u8]) -> Result<Self> { 160 let seq = asn1::parse_one(data)?; 161 if seq.tag != TAG_SEQUENCE { 162 return Err(RsaError::InvalidKeyFormat); 163 } 164 165 let items = seq.sequence_items()?; 166 if items.len() != 2 { 167 return Err(RsaError::InvalidKeyFormat); 168 } 169 170 let n_bytes = items[0].as_positive_integer()?; 171 let e_bytes = items[1].as_positive_integer()?; 172 173 Self::from_components(n_bytes, e_bytes) 174 } 175 176 /// Parse an RSA public key from DER-encoded PKCS#8 SubjectPublicKeyInfo format. 177 /// 178 /// ```asn1 179 /// SubjectPublicKeyInfo ::= SEQUENCE { 180 /// algorithm AlgorithmIdentifier, 181 /// subjectPublicKey BIT STRING 182 /// } 183 /// AlgorithmIdentifier ::= SEQUENCE { 184 /// algorithm OBJECT IDENTIFIER, 185 /// parameters ANY OPTIONAL 186 /// } 187 /// ``` 188 pub fn from_pkcs8_der(data: &[u8]) -> Result<Self> { 189 let seq = asn1::parse_one(data)?; 190 if seq.tag != TAG_SEQUENCE { 191 return Err(RsaError::InvalidKeyFormat); 192 } 193 194 let items = seq.sequence_items()?; 195 if items.len() != 2 { 196 return Err(RsaError::InvalidKeyFormat); 197 } 198 199 // Parse AlgorithmIdentifier. 200 let alg_id = &items[0]; 201 if alg_id.tag != TAG_SEQUENCE { 202 return Err(RsaError::InvalidKeyFormat); 203 } 204 let alg_items = alg_id.sequence_items()?; 205 if alg_items.is_empty() { 206 return Err(RsaError::InvalidKeyFormat); 207 } 208 209 // Verify the algorithm OID is rsaEncryption. 210 let oid = alg_items[0].as_oid()?; 211 if !oid.matches(OID_RSA_ENCRYPTION) { 212 return Err(RsaError::InvalidKeyFormat); 213 } 214 215 // The parameters should be NULL (or absent). 216 if alg_items.len() > 1 && alg_items[1].tag != TAG_NULL { 217 return Err(RsaError::InvalidKeyFormat); 218 } 219 220 // Parse the BIT STRING containing the RSAPublicKey. 221 let (unused_bits, key_data) = items[1].as_bit_string()?; 222 if unused_bits != 0 { 223 return Err(RsaError::InvalidKeyFormat); 224 } 225 226 // The BIT STRING content is a DER-encoded RSAPublicKey. 227 Self::from_pkcs1_der(key_data) 228 } 229 230 /// Try to parse from either PKCS#1 or PKCS#8 format. 231 pub fn from_der(data: &[u8]) -> Result<Self> { 232 // Try PKCS#8 first (more common in certificates). 233 if let Ok(key) = Self::from_pkcs8_der(data) { 234 return Ok(key); 235 } 236 // Fall back to PKCS#1. 237 Self::from_pkcs1_der(data) 238 } 239 240 /// Create from raw modulus and exponent bytes (big-endian, no leading zeros). 241 fn from_components(n_bytes: &[u8], e_bytes: &[u8]) -> Result<Self> { 242 let n = BigUint::from_be_bytes(n_bytes); 243 let e = BigUint::from_be_bytes(e_bytes); 244 245 let bit_len = n.bit_len(); 246 if bit_len < 2048 { 247 return Err(RsaError::ModulusTooSmall); 248 } 249 if bit_len > 4096 { 250 return Err(RsaError::ModulusTooLarge); 251 } 252 253 // Public exponent must be odd and >= 3. 254 if e_bytes.is_empty() || !e.bit(0) { 255 return Err(RsaError::InvalidExponent); 256 } 257 258 let key_len = bit_len.div_ceil(8); 259 260 Ok(Self { n, e, key_len }) 261 } 262 263 /// Verify an RSASSA-PKCS1-v1_5 signature. 264 /// 265 /// `hash_alg` specifies the hash algorithm used. 266 /// `message` is the original message that was signed. 267 /// `signature` is the signature bytes. 268 pub fn verify_pkcs1v15( 269 &self, 270 hash_alg: HashAlgorithm, 271 message: &[u8], 272 signature: &[u8], 273 ) -> Result<()> { 274 // Step 1: Length check. 275 if signature.len() != self.key_len { 276 return Err(RsaError::InvalidSignatureLength); 277 } 278 279 // Step 2: RSA verification primitive (RSAVP1). 280 let s = BigUint::from_be_bytes(signature); 281 if s.cmp(&self.n) != core::cmp::Ordering::Less { 282 return Err(RsaError::SignatureOutOfRange); 283 } 284 285 // m = s^e mod n 286 let m = s.modpow(&self.e, &self.n); 287 let em = m.to_be_bytes_padded(self.key_len); 288 289 // Step 3: EMSA-PKCS1-v1_5 encoding. 290 let hash = hash_alg.hash(message); 291 let expected_em = emsa_pkcs1_v15_encode(&hash, hash_alg, self.key_len)?; 292 293 // Step 4: Compare. 294 if !constant_time_eq(&em, &expected_em) { 295 return Err(RsaError::InvalidPadding); 296 } 297 298 Ok(()) 299 } 300 301 /// Verify a signature where the hash has already been computed. 302 /// 303 /// `hash_alg` specifies which hash algorithm was used. 304 /// `hash` is the pre-computed hash of the message. 305 /// `signature` is the signature bytes. 306 pub fn verify_pkcs1v15_prehashed( 307 &self, 308 hash_alg: HashAlgorithm, 309 hash: &[u8], 310 signature: &[u8], 311 ) -> Result<()> { 312 if hash.len() != hash_alg.hash_len() { 313 return Err(RsaError::DigestMismatch); 314 } 315 316 // Step 1: Length check. 317 if signature.len() != self.key_len { 318 return Err(RsaError::InvalidSignatureLength); 319 } 320 321 // Step 2: RSAVP1. 322 let s = BigUint::from_be_bytes(signature); 323 if s.cmp(&self.n) != core::cmp::Ordering::Less { 324 return Err(RsaError::SignatureOutOfRange); 325 } 326 327 let m = s.modpow(&self.e, &self.n); 328 let em = m.to_be_bytes_padded(self.key_len); 329 330 // Step 3: EMSA-PKCS1-v1_5 encoding. 331 let expected_em = emsa_pkcs1_v15_encode(hash, hash_alg, self.key_len)?; 332 333 // Step 4: Compare. 334 if !constant_time_eq(&em, &expected_em) { 335 return Err(RsaError::InvalidPadding); 336 } 337 338 Ok(()) 339 } 340} 341 342// --------------------------------------------------------------------------- 343// EMSA-PKCS1-v1_5 encoding (RFC 8017 Section 9.2) 344// --------------------------------------------------------------------------- 345 346/// Construct EMSA-PKCS1-v1_5 encoded message: 347/// EM = 0x00 || 0x01 || PS || 0x00 || DigestInfo 348/// 349/// where PS is padding of 0xFF bytes, and DigestInfo = prefix || hash. 350fn emsa_pkcs1_v15_encode(hash: &[u8], hash_alg: HashAlgorithm, em_len: usize) -> Result<Vec<u8>> { 351 let prefix = hash_alg.digest_info_prefix(); 352 let t_len = prefix.len() + hash.len(); 353 354 // em_len must be >= t_len + 11 (0x00 + 0x01 + at least 8 bytes of PS + 0x00 + T) 355 if em_len < t_len + 11 { 356 return Err(RsaError::InvalidPadding); 357 } 358 359 let ps_len = em_len - t_len - 3; 360 let mut em = Vec::with_capacity(em_len); 361 em.push(0x00); 362 em.push(0x01); 363 em.extend(std::iter::repeat_n(0xFF, ps_len)); 364 em.push(0x00); 365 em.extend_from_slice(prefix); 366 em.extend_from_slice(hash); 367 368 debug_assert_eq!(em.len(), em_len); 369 Ok(em) 370} 371 372// --------------------------------------------------------------------------- 373// Utility: parse algorithm OID from a signature's DigestInfo 374// --------------------------------------------------------------------------- 375 376/// Identify the hash algorithm from a DigestInfo DER encoding. 377pub fn parse_digest_info_algorithm(digest_info: &[u8]) -> Result<HashAlgorithm> { 378 let seq = asn1::parse_one(digest_info)?; 379 if seq.tag != TAG_SEQUENCE { 380 return Err(RsaError::InvalidKeyFormat); 381 } 382 let items = seq.sequence_items()?; 383 if items.len() != 2 { 384 return Err(RsaError::InvalidKeyFormat); 385 } 386 387 // AlgorithmIdentifier 388 let alg_id = &items[0]; 389 if alg_id.tag != TAG_SEQUENCE { 390 return Err(RsaError::InvalidKeyFormat); 391 } 392 let alg_items = alg_id.sequence_items()?; 393 if alg_items.is_empty() { 394 return Err(RsaError::InvalidKeyFormat); 395 } 396 397 let oid = alg_items[0].as_oid()?; 398 if oid.matches(OID_SHA256) { 399 Ok(HashAlgorithm::Sha256) 400 } else if oid.matches(OID_SHA384) { 401 Ok(HashAlgorithm::Sha384) 402 } else if oid.matches(OID_SHA512) { 403 Ok(HashAlgorithm::Sha512) 404 } else { 405 Err(RsaError::UnsupportedAlgorithm) 406 } 407} 408 409// --------------------------------------------------------------------------- 410// Constant-time comparison 411// --------------------------------------------------------------------------- 412 413/// Constant-time byte slice comparison to prevent timing attacks. 414fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { 415 if a.len() != b.len() { 416 return false; 417 } 418 let mut diff = 0u8; 419 for (x, y) in a.iter().zip(b.iter()) { 420 diff |= x ^ y; 421 } 422 diff == 0 423} 424 425// --------------------------------------------------------------------------- 426// Tests 427// --------------------------------------------------------------------------- 428 429#[cfg(test)] 430mod tests { 431 use super::*; 432 433 // ----------------------------------------------------------------------- 434 // Test EMSA-PKCS1-v1_5 encoding 435 // ----------------------------------------------------------------------- 436 437 #[test] 438 fn emsa_encoding_sha256() { 439 let hash = sha256(b"test"); 440 let em = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 256).unwrap(); 441 442 // Check structure: 0x00 0x01 [0xFF padding] 0x00 [DigestInfo] 443 assert_eq!(em[0], 0x00); 444 assert_eq!(em[1], 0x01); 445 446 let prefix = HashAlgorithm::Sha256.digest_info_prefix(); 447 let t_len = prefix.len() + 32; // 19 + 32 = 51 448 let ps_len = 256 - t_len - 3; // 256 - 51 - 3 = 202 449 450 for i in 2..2 + ps_len { 451 assert_eq!(em[i], 0xFF, "byte {i} should be 0xFF"); 452 } 453 assert_eq!(em[2 + ps_len], 0x00); 454 assert_eq!(&em[3 + ps_len..3 + ps_len + prefix.len()], prefix); 455 assert_eq!(&em[3 + ps_len + prefix.len()..], &hash[..]); 456 } 457 458 #[test] 459 fn emsa_encoding_too_short() { 460 let hash = sha256(b"test"); 461 // em_len too small. 462 let result = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 50); 463 assert!(result.is_err()); 464 } 465 466 // ----------------------------------------------------------------------- 467 // DER construction helpers 468 // ----------------------------------------------------------------------- 469 470 /// Encode a DER length. 471 fn der_length(len: usize) -> Vec<u8> { 472 if len < 0x80 { 473 vec![len as u8] 474 } else if len < 0x100 { 475 vec![0x81, len as u8] 476 } else { 477 vec![0x82, (len >> 8) as u8, len as u8] 478 } 479 } 480 481 /// Build a DER TLV (tag + length + value). 482 fn der_tlv(tag: u8, value: &[u8]) -> Vec<u8> { 483 let mut out = vec![tag]; 484 out.extend_from_slice(&der_length(value.len())); 485 out.extend_from_slice(value); 486 out 487 } 488 489 /// Build a DER INTEGER from unsigned big-endian bytes (adds leading 0 if needed). 490 fn der_integer(bytes: &[u8]) -> Vec<u8> { 491 let needs_pad = !bytes.is_empty() && bytes[0] & 0x80 != 0; 492 let mut value = Vec::new(); 493 if needs_pad { 494 value.push(0x00); 495 } 496 value.extend_from_slice(bytes); 497 der_tlv(0x02, &value) 498 } 499 500 /// Build a PKCS#1 RSAPublicKey DER from modulus and exponent bytes. 501 fn build_pkcs1_key(modulus: &[u8], exponent: &[u8]) -> Vec<u8> { 502 let n_int = der_integer(modulus); 503 let e_int = der_integer(exponent); 504 let mut content = Vec::new(); 505 content.extend_from_slice(&n_int); 506 content.extend_from_slice(&e_int); 507 der_tlv(0x30, &content) 508 } 509 510 /// Build a PKCS#8 SubjectPublicKeyInfo DER for RSA. 511 fn build_pkcs8_key(modulus: &[u8], exponent: &[u8]) -> Vec<u8> { 512 // AlgorithmIdentifier: SEQUENCE { OID rsaEncryption, NULL } 513 let oid = der_tlv(0x06, OID_RSA_ENCRYPTION); 514 let null = vec![0x05, 0x00]; 515 let mut alg_content = Vec::new(); 516 alg_content.extend_from_slice(&oid); 517 alg_content.extend_from_slice(&null); 518 let alg_id = der_tlv(0x30, &alg_content); 519 520 // BIT STRING containing RSAPublicKey 521 let rsa_pub_key = build_pkcs1_key(modulus, exponent); 522 let mut bit_string_value = vec![0x00]; // unused bits = 0 523 bit_string_value.extend_from_slice(&rsa_pub_key); 524 let bit_string = der_tlv(0x03, &bit_string_value); 525 526 let mut content = Vec::new(); 527 content.extend_from_slice(&alg_id); 528 content.extend_from_slice(&bit_string); 529 der_tlv(0x30, &content) 530 } 531 532 // ----------------------------------------------------------------------- 533 // Test RSA public key parsing 534 // ----------------------------------------------------------------------- 535 536 #[test] 537 fn parse_pkcs1_key() { 538 // 2048-bit modulus (256 bytes, top bit set). 539 let mut modulus = vec![0x80; 256]; 540 modulus[255] = 0x01; // make it odd 541 let exponent = vec![0x01, 0x00, 0x01]; // 65537 542 543 let key_der = build_pkcs1_key(&modulus, &exponent); 544 let key = RsaPublicKey::from_pkcs1_der(&key_der).unwrap(); 545 assert!(key.n.bit_len() >= 2048); 546 assert_eq!(key.e, BigUint::from_u64(65537)); 547 } 548 549 #[test] 550 fn parse_pkcs8_key() { 551 let mut modulus = vec![0x80; 256]; 552 modulus[255] = 0x01; 553 let exponent = vec![0x01, 0x00, 0x01]; 554 555 let key_der = build_pkcs8_key(&modulus, &exponent); 556 let key = RsaPublicKey::from_pkcs8_der(&key_der).unwrap(); 557 assert!(key.n.bit_len() >= 2048); 558 assert_eq!(key.e, BigUint::from_u64(65537)); 559 } 560 561 #[test] 562 fn parse_from_der_auto_detect() { 563 let mut modulus = vec![0x80; 256]; 564 modulus[255] = 0x01; 565 let exponent = vec![0x01, 0x00, 0x01]; 566 567 // PKCS#8 auto-detect. 568 let pkcs8 = build_pkcs8_key(&modulus, &exponent); 569 let key = RsaPublicKey::from_der(&pkcs8).unwrap(); 570 assert_eq!(key.e, BigUint::from_u64(65537)); 571 572 // PKCS#1 auto-detect. 573 let pkcs1 = build_pkcs1_key(&modulus, &exponent); 574 let key = RsaPublicKey::from_der(&pkcs1).unwrap(); 575 assert_eq!(key.e, BigUint::from_u64(65537)); 576 } 577 578 #[test] 579 fn reject_small_modulus() { 580 let modulus = vec![0xFF; 128]; // 1024-bit 581 let exponent = vec![0x01, 0x00, 0x01]; 582 let key_der = build_pkcs1_key(&modulus, &exponent); 583 let result = RsaPublicKey::from_pkcs1_der(&key_der); 584 assert_eq!(result.unwrap_err(), RsaError::ModulusTooSmall); 585 } 586 587 #[test] 588 fn reject_even_exponent() { 589 let mut modulus = vec![0xFF; 256]; 590 modulus[255] = 0xFB; 591 let exponent = vec![0x02]; // even 592 let key_der = build_pkcs1_key(&modulus, &exponent); 593 let result = RsaPublicKey::from_pkcs1_der(&key_der); 594 assert_eq!(result.unwrap_err(), RsaError::InvalidExponent); 595 } 596 597 // ----------------------------------------------------------------------- 598 // End-to-end signature verification test 599 // ----------------------------------------------------------------------- 600 601 #[test] 602 fn verify_pkcs1v15_sha256_constructed() { 603 // Construct a test case using known values. 604 // We create a valid EMSA-PKCS1-v1_5 encoded message and verify the 605 // mathematical relationship holds: sig^e mod n == EM. 606 // 607 // Use small-ish but valid RSA parameters for a fast test. 608 // For a real test we'd use a 2048-bit key, but we test the encoding logic 609 // with a constructed example. 610 611 let message = b"hello world"; 612 let hash = sha256(message); 613 614 // Verify EMSA encoding is deterministic. 615 let em1 = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 256).unwrap(); 616 let em2 = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 256).unwrap(); 617 assert_eq!(em1, em2); 618 } 619 620 #[test] 621 fn verify_signature_wrong_length() { 622 let mut modulus = vec![0x80; 256]; 623 modulus[255] = 0x01; 624 let exponent = vec![0x01, 0x00, 0x01]; 625 let key_der = build_pkcs1_key(&modulus, &exponent); 626 let key = RsaPublicKey::from_pkcs1_der(&key_der).unwrap(); 627 628 // Wrong signature length. 629 let sig = vec![0u8; 128]; // should be 256 630 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"test", &sig); 631 assert_eq!(result.unwrap_err(), RsaError::InvalidSignatureLength); 632 } 633 634 #[test] 635 fn verify_signature_out_of_range() { 636 let mut modulus = vec![0x80; 256]; 637 modulus[255] = 0x01; 638 let exponent = vec![0x01, 0x00, 0x01]; 639 let key_der = build_pkcs1_key(&modulus, &exponent); 640 let key = RsaPublicKey::from_pkcs1_der(&key_der).unwrap(); 641 642 // Signature >= modulus. 643 let sig = vec![0xFF; 256]; 644 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"test", &sig); 645 assert_eq!(result.unwrap_err(), RsaError::SignatureOutOfRange); 646 } 647 648 #[test] 649 fn constant_time_eq_works() { 650 assert!(constant_time_eq(b"hello", b"hello")); 651 assert!(!constant_time_eq(b"hello", b"world")); 652 assert!(!constant_time_eq(b"hello", b"hell")); 653 assert!(constant_time_eq(b"", b"")); 654 } 655 656 #[test] 657 fn emsa_encoding_sha384() { 658 let hash = sha384(b"test"); 659 let em = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha384, 256).unwrap(); 660 661 assert_eq!(em[0], 0x00); 662 assert_eq!(em[1], 0x01); 663 664 let prefix = HashAlgorithm::Sha384.digest_info_prefix(); 665 let t_len = prefix.len() + 48; 666 let ps_len = 256 - t_len - 3; 667 668 for i in 2..2 + ps_len { 669 assert_eq!(em[i], 0xFF); 670 } 671 assert_eq!(em[2 + ps_len], 0x00); 672 assert_eq!(&em[3 + ps_len..3 + ps_len + prefix.len()], prefix); 673 assert_eq!(&em[3 + ps_len + prefix.len()..], &hash[..]); 674 } 675 676 #[test] 677 fn emsa_encoding_sha512() { 678 let hash = sha512(b"test"); 679 let em = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha512, 256).unwrap(); 680 681 assert_eq!(em[0], 0x00); 682 assert_eq!(em[1], 0x01); 683 684 let prefix = HashAlgorithm::Sha512.digest_info_prefix(); 685 let t_len = prefix.len() + 64; 686 let ps_len = 256 - t_len - 3; 687 688 for i in 2..2 + ps_len { 689 assert_eq!(em[i], 0xFF); 690 } 691 assert_eq!(em[2 + ps_len], 0x00); 692 } 693 694 #[test] 695 fn parse_digest_info_sha256() { 696 // Build a DigestInfo for SHA-256. 697 let hash = [0u8; 32]; 698 let mut digest_info = Vec::new(); 699 let prefix = HashAlgorithm::Sha256.digest_info_prefix(); 700 // The prefix includes the outer SEQUENCE header; build the full DigestInfo. 701 digest_info.extend_from_slice(prefix); 702 digest_info.extend_from_slice(&hash); 703 // Actually, the prefix already has the outer SEQUENCE, so we need to 704 // parse just the prefix part up to (but not including) the OCTET STRING value. 705 // For parse_digest_info_algorithm we need the full DigestInfo as a SEQUENCE. 706 // Let's build it properly: 707 let mut di = Vec::new(); 708 di.push(0x30); // SEQUENCE 709 di.push(0x31); // length = 49 710 di.push(0x30); // SEQUENCE (AlgorithmIdentifier) 711 di.push(0x0d); // length = 13 712 di.push(0x06); // OID 713 di.push(0x09); // length = 9 714 di.extend_from_slice(OID_SHA256); 715 di.push(0x05); // NULL 716 di.push(0x00); 717 di.push(0x04); // OCTET STRING 718 di.push(0x20); // length = 32 719 di.extend_from_slice(&hash); 720 721 let alg = parse_digest_info_algorithm(&di).unwrap(); 722 assert_eq!(alg, HashAlgorithm::Sha256); 723 } 724 725 #[test] 726 fn parse_digest_info_sha384() { 727 let hash = [0u8; 48]; 728 let mut di = Vec::new(); 729 di.push(0x30); // SEQUENCE 730 di.push(0x41); // length = 65 731 di.push(0x30); // SEQUENCE (AlgorithmIdentifier) 732 di.push(0x0d); // length = 13 733 di.push(0x06); // OID 734 di.push(0x09); // length = 9 735 di.extend_from_slice(OID_SHA384); 736 di.push(0x05); // NULL 737 di.push(0x00); 738 di.push(0x04); // OCTET STRING 739 di.push(0x30); // length = 48 740 di.extend_from_slice(&hash); 741 742 let alg = parse_digest_info_algorithm(&di).unwrap(); 743 assert_eq!(alg, HashAlgorithm::Sha384); 744 } 745 746 // ----------------------------------------------------------------------- 747 // Signature rejection tests with PKCS#8 key 748 // ----------------------------------------------------------------------- 749 750 #[test] 751 fn verify_invalid_signature_rejected() { 752 let mut modulus = vec![0x80; 256]; 753 modulus[255] = 0x01; 754 let exponent = vec![0x01, 0x00, 0x01]; 755 let key_der = build_pkcs8_key(&modulus, &exponent); 756 let key = RsaPublicKey::from_pkcs8_der(&key_der).unwrap(); 757 758 // Random signature should fail (invalid padding after modpow). 759 let mut sig = vec![0x42; 256]; 760 sig[0] = 0x01; // Ensure < modulus 761 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"test message", &sig); 762 assert!(result.is_err()); 763 } 764 765 #[test] 766 fn verify_tampered_signature_rejected() { 767 let mut modulus = vec![0x80; 256]; 768 modulus[255] = 0x01; 769 let exponent = vec![0x01, 0x00, 0x01]; 770 let key_der = build_pkcs8_key(&modulus, &exponent); 771 let key = RsaPublicKey::from_pkcs8_der(&key_der).unwrap(); 772 773 // All-zero signature should fail. 774 let sig = vec![0u8; 256]; 775 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"test", &sig); 776 assert!(result.is_err()); 777 778 // Signature of all 0x01 should fail. 779 let sig = vec![0x01; 256]; 780 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"test", &sig); 781 assert!(result.is_err()); 782 } 783 784 // ----------------------------------------------------------------------- 785 // Mathematical verification test 786 // ----------------------------------------------------------------------- 787 // 788 // We construct a scenario where we know sig^e mod n == EM, by computing 789 // EM first and setting sig = EM (with e=1, which is degenerate but tests 790 // the EMSA encoding path). 791 792 #[test] 793 fn verify_with_exponent_one() { 794 // With e=1, sig^1 mod n = sig (if sig < n). 795 // So if sig == EMSA(msg), verification passes. 796 let mut mod_bytes = vec![0xFF; 256]; 797 mod_bytes[255] = 0xFD; // make it odd 798 let exponent = vec![0x01]; // e=1 799 800 let key_der = build_pkcs1_key(&mod_bytes, &exponent); 801 let key = RsaPublicKey::from_pkcs1_der(&key_der).unwrap(); 802 803 // Construct the expected EM for "hello" with SHA-256. 804 let hash = sha256(b"hello"); 805 let em = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 256).unwrap(); 806 807 // With e=1, sig = EM should verify. 808 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"hello", &em); 809 assert!(result.is_ok(), "verification should pass: {result:?}"); 810 811 // Wrong message should fail. 812 let result = key.verify_pkcs1v15(HashAlgorithm::Sha256, b"world", &em); 813 assert!(result.is_err()); 814 } 815 816 #[test] 817 fn verify_prehashed() { 818 let mut mod_bytes = vec![0xFF; 256]; 819 mod_bytes[255] = 0xFD; 820 let exponent = vec![0x01]; // e=1 821 822 let key_der = build_pkcs1_key(&mod_bytes, &exponent); 823 let key = RsaPublicKey::from_pkcs1_der(&key_der).unwrap(); 824 825 let hash = sha256(b"hello"); 826 let em = emsa_pkcs1_v15_encode(&hash, HashAlgorithm::Sha256, 256).unwrap(); 827 828 let result = key.verify_pkcs1v15_prehashed(HashAlgorithm::Sha256, &hash, &em); 829 assert!(result.is_ok()); 830 831 // Wrong hash length should fail. 832 let result = key.verify_pkcs1v15_prehashed(HashAlgorithm::Sha256, &hash[..16], &em); 833 assert_eq!(result.unwrap_err(), RsaError::DigestMismatch); 834 } 835}