we (web engine): Experimental web browser project to understand the limits of Claude
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}