1use ring::signature;
13use x509_parser::prelude::FromDer;
14use x509_parser::public_key::{ECPoint, PublicKey};
15use x509_parser::x509::SubjectPublicKeyInfo;
16
17use super::parse::SignatureAlgorithm;
18
19#[derive(Debug, thiserror::Error)]
21#[non_exhaustive]
22pub enum SignatureVerificationError {
23 #[error("invalid PEM public key")]
25 InvalidKeyPem,
26
27 #[error("unsupported signature algorithm: {uri}")]
29 UnsupportedAlgorithm {
30 uri: String,
32 },
33
34 #[error("invalid key format: expected PUBLIC KEY PEM, got {label}")]
36 InvalidKeyFormat {
37 label: String,
39 },
40
41 #[error("invalid SubjectPublicKeyInfo DER")]
43 InvalidKeyDer,
44
45 #[error("public key does not match signature algorithm: {uri}")]
47 KeyAlgorithmMismatch {
48 uri: String,
50 },
51
52 #[error("invalid ECDSA signature encoding")]
55 InvalidSignatureFormat,
56}
57
58#[must_use = "discarding the verification result skips signature validation"]
63pub fn verify_rsa_signature_pem(
64 algorithm: SignatureAlgorithm,
65 public_key_pem: &str,
66 signed_data: &[u8],
67 signature_value: &[u8],
68) -> Result<bool, SignatureVerificationError> {
69 let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
70 verify_rsa_signature_spki(
71 algorithm,
72 &public_key_spki_der,
73 signed_data,
74 signature_value,
75 )
76}
77
78#[must_use = "discarding the verification result skips signature validation"]
88pub fn verify_ecdsa_signature_pem(
89 algorithm: SignatureAlgorithm,
90 public_key_pem: &str,
91 signed_data: &[u8],
92 signature_value: &[u8],
93) -> Result<bool, SignatureVerificationError> {
94 let public_key_spki_der = parse_public_key_pem(public_key_pem)?;
95 verify_ecdsa_signature_spki(
96 algorithm,
97 &public_key_spki_der,
98 signed_data,
99 signature_value,
100 )
101}
102
103fn parse_public_key_pem(public_key_pem: &str) -> Result<Vec<u8>, SignatureVerificationError> {
104 let (rest, pem) = x509_parser::pem::parse_x509_pem(public_key_pem.as_bytes())
105 .map_err(|_| SignatureVerificationError::InvalidKeyPem)?;
106 if !rest.iter().all(|byte| byte.is_ascii_whitespace()) {
107 return Err(SignatureVerificationError::InvalidKeyPem);
108 }
109 if pem.label != "PUBLIC KEY" {
110 return Err(SignatureVerificationError::InvalidKeyFormat { label: pem.label });
111 }
112
113 Ok(pem.contents)
114}
115
116#[must_use = "discarding the verification result skips signature validation"]
122pub fn verify_rsa_signature_spki(
123 algorithm: SignatureAlgorithm,
124 public_key_spki_der: &[u8],
125 signed_data: &[u8],
126 signature_value: &[u8],
127) -> Result<bool, SignatureVerificationError> {
128 let verification_algorithm = verification_algorithm(algorithm)?;
129 let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
130 .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
131 if !rest.is_empty() {
132 return Err(SignatureVerificationError::InvalidKeyDer);
133 }
134 let public_key = spki
135 .parsed()
136 .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
137
138 match public_key {
139 PublicKey::RSA(rsa) => {
140 validate_rsa_public_key(&rsa, algorithm)?;
141 let key = signature::UnparsedPublicKey::new(
142 verification_algorithm,
143 spki.subject_public_key.data,
144 );
145 Ok(key.verify(signed_data, signature_value).is_ok())
146 }
147 _ => Err(SignatureVerificationError::InvalidKeyDer),
148 }
149}
150
151#[must_use = "discarding the verification result skips signature validation"]
159pub fn verify_ecdsa_signature_spki(
160 algorithm: SignatureAlgorithm,
161 public_key_spki_der: &[u8],
162 signed_data: &[u8],
163 signature_value: &[u8],
164) -> Result<bool, SignatureVerificationError> {
165 if !matches!(
166 algorithm,
167 SignatureAlgorithm::EcdsaP256Sha256 | SignatureAlgorithm::EcdsaP384Sha384
168 ) {
169 return Err(SignatureVerificationError::UnsupportedAlgorithm {
170 uri: algorithm.uri().to_string(),
171 });
172 }
173
174 let (rest, spki) = SubjectPublicKeyInfo::from_der(public_key_spki_der)
175 .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
176 if !rest.is_empty() {
177 return Err(SignatureVerificationError::InvalidKeyDer);
178 }
179 let public_key = spki
180 .parsed()
181 .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
182
183 match public_key {
184 PublicKey::EC(ec) => {
185 validate_ec_public_key_encoding(&ec, &spki.subject_public_key.data)?;
186 let (fixed_algorithm, asn1_algorithm, signature_encoding) =
187 ecdsa_verification_algorithms(&spki, &ec, algorithm, signature_value)?;
188 let public_key = &spki.subject_public_key.data;
189 let fixed_key = signature::UnparsedPublicKey::new(fixed_algorithm, public_key);
190 let asn1_key = signature::UnparsedPublicKey::new(asn1_algorithm, public_key);
191
192 match signature_encoding {
193 EcdsaSignatureEncoding::XmlDsigFixed => {
194 Ok(fixed_key.verify(signed_data, signature_value).is_ok())
195 }
196 EcdsaSignatureEncoding::Asn1Der => {
197 Ok(asn1_key.verify(signed_data, signature_value).is_ok())
198 }
199 EcdsaSignatureEncoding::Ambiguous => {
200 if asn1_key.verify(signed_data, signature_value).is_ok() {
201 return Ok(true);
202 }
203
204 Ok(fixed_key.verify(signed_data, signature_value).is_ok())
205 }
206 }
207 }
208 _ => Err(SignatureVerificationError::KeyAlgorithmMismatch {
209 uri: algorithm.uri().to_string(),
210 }),
211 }
212}
213
214fn validate_rsa_public_key(
215 rsa: &x509_parser::public_key::RSAPublicKey<'_>,
216 algorithm: SignatureAlgorithm,
217) -> Result<(), SignatureVerificationError> {
218 let min_modulus_bits = minimum_rsa_modulus_bits(algorithm)?;
219 let modulus_start = rsa
220 .modulus
221 .iter()
222 .position(|byte| *byte != 0)
223 .ok_or(SignatureVerificationError::InvalidKeyDer)?;
224 let modulus = &rsa.modulus[modulus_start..];
225 if modulus.is_empty() {
226 return Err(SignatureVerificationError::InvalidKeyDer);
227 }
228 let modulus_bits = modulus
232 .len()
233 .checked_mul(8)
234 .ok_or(SignatureVerificationError::InvalidKeyDer)?;
235 if !(min_modulus_bits..=8192).contains(&modulus_bits) {
236 return Err(SignatureVerificationError::InvalidKeyDer);
237 }
238
239 let exponent = rsa
240 .try_exponent()
241 .map_err(|_| SignatureVerificationError::InvalidKeyDer)?;
242 if !(3..=((1_u64 << 33) - 1)).contains(&exponent) || exponent % 2 == 0 {
243 return Err(SignatureVerificationError::InvalidKeyDer);
244 }
245
246 Ok(())
247}
248
249fn minimum_rsa_modulus_bits(
250 algorithm: SignatureAlgorithm,
251) -> Result<usize, SignatureVerificationError> {
252 match algorithm {
253 SignatureAlgorithm::RsaSha1
254 | SignatureAlgorithm::RsaSha256
255 | SignatureAlgorithm::RsaSha384
256 | SignatureAlgorithm::RsaSha512 => Ok(2048),
257 _ => Err(SignatureVerificationError::UnsupportedAlgorithm {
258 uri: algorithm.uri().to_string(),
259 }),
260 }
261}
262
263fn verification_algorithm(
264 algorithm: SignatureAlgorithm,
265) -> Result<&'static dyn signature::VerificationAlgorithm, SignatureVerificationError> {
266 match algorithm {
267 SignatureAlgorithm::RsaSha1 => Ok(&signature::RSA_PKCS1_2048_8192_SHA1_FOR_LEGACY_USE_ONLY),
268 SignatureAlgorithm::RsaSha256 => Ok(&signature::RSA_PKCS1_2048_8192_SHA256),
269 SignatureAlgorithm::RsaSha384 => Ok(&signature::RSA_PKCS1_2048_8192_SHA384),
270 SignatureAlgorithm::RsaSha512 => Ok(&signature::RSA_PKCS1_2048_8192_SHA512),
271 _ => Err(SignatureVerificationError::UnsupportedAlgorithm {
272 uri: algorithm.uri().to_string(),
273 }),
274 }
275}
276
277fn ecdsa_verification_algorithms(
278 spki: &SubjectPublicKeyInfo<'_>,
279 ec: &ECPoint<'_>,
280 algorithm: SignatureAlgorithm,
281 signature_value: &[u8],
282) -> Result<
283 (
284 &'static dyn signature::VerificationAlgorithm,
285 &'static dyn signature::VerificationAlgorithm,
286 EcdsaSignatureEncoding,
287 ),
288 SignatureVerificationError,
289> {
290 let curve_oid = spki
291 .algorithm
292 .parameters
293 .as_ref()
294 .and_then(|params| params.as_oid().ok())
295 .ok_or(SignatureVerificationError::InvalidKeyDer)?;
296 let point_len = ec.key_size();
297
298 let curve_oid = curve_oid.to_id_string();
299 let (fixed_algorithm, asn1_algorithm, component_len) = match algorithm {
300 SignatureAlgorithm::EcdsaP256Sha256 => {
301 if curve_oid == "1.2.840.10045.3.1.7" && point_len == 256 {
302 (
303 &signature::ECDSA_P256_SHA256_FIXED,
304 &signature::ECDSA_P256_SHA256_ASN1,
305 32,
306 )
307 } else {
308 return Err(SignatureVerificationError::KeyAlgorithmMismatch {
309 uri: algorithm.uri().to_string(),
310 });
311 }
312 }
313 SignatureAlgorithm::EcdsaP384Sha384 => {
314 if curve_oid == "1.3.132.0.34" && point_len == 384 {
315 (
316 &signature::ECDSA_P384_SHA384_FIXED,
317 &signature::ECDSA_P384_SHA384_ASN1,
318 48,
319 )
320 } else {
321 return Err(SignatureVerificationError::KeyAlgorithmMismatch {
322 uri: algorithm.uri().to_string(),
323 });
324 }
325 }
326 _ => {
327 return Err(SignatureVerificationError::UnsupportedAlgorithm {
328 uri: algorithm.uri().to_string(),
329 });
330 }
331 };
332
333 Ok((
334 fixed_algorithm,
335 asn1_algorithm,
336 classify_ecdsa_signature_encoding(signature_value, component_len)?,
337 ))
338}
339
340#[derive(Clone, Copy, Debug, Eq, PartialEq)]
341enum EcdsaSignatureEncoding {
342 XmlDsigFixed,
343 Asn1Der,
344 Ambiguous,
345}
346
347fn classify_ecdsa_signature_encoding(
348 signature_value: &[u8],
349 component_len: usize,
350) -> Result<EcdsaSignatureEncoding, SignatureVerificationError> {
351 let expected_len = component_len
352 .checked_mul(2)
353 .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
354
355 match inspect_der_encoded_ecdsa_signature(signature_value, component_len) {
356 Ok(Some(())) if signature_value.len() == expected_len => {
357 Ok(EcdsaSignatureEncoding::Ambiguous)
358 }
359 Ok(Some(())) => Ok(EcdsaSignatureEncoding::Asn1Der),
360 Ok(None) | Err(_) if signature_value.len() == expected_len => {
361 Ok(EcdsaSignatureEncoding::XmlDsigFixed)
362 }
363 Ok(None) | Err(_) => Err(SignatureVerificationError::InvalidSignatureFormat),
364 }
365}
366
367fn inspect_der_encoded_ecdsa_signature(
368 signature_value: &[u8],
369 component_len: usize,
370) -> Result<Option<()>, SignatureVerificationError> {
371 let Some((&tag, rest)) = signature_value.split_first() else {
372 return Ok(None);
373 };
374 if tag != 0x30 {
375 return Ok(None);
376 }
377
378 let sequence = parse_der_length(rest)
379 .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
380 .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
381 let (sequence_len, sequence_rest) = sequence;
382 let (sequence_content, trailing) = sequence_rest
383 .split_at_checked(sequence_len)
384 .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
385 if !trailing.is_empty() {
386 return Err(SignatureVerificationError::InvalidSignatureFormat);
387 }
388
389 let after_r = parse_der_integer(sequence_content, component_len)?;
390 let after_s = parse_der_integer(after_r, component_len)?;
391 if !after_s.is_empty() {
392 return Err(SignatureVerificationError::InvalidSignatureFormat);
393 }
394
395 Ok(Some(()))
396}
397
398fn parse_der_integer(
399 input: &[u8],
400 component_len: usize,
401) -> Result<&[u8], SignatureVerificationError> {
402 let Some((&tag, rest)) = input.split_first() else {
403 return Err(SignatureVerificationError::InvalidSignatureFormat);
404 };
405 if tag != 0x02 {
406 return Err(SignatureVerificationError::InvalidSignatureFormat);
407 }
408
409 let (len, rest) = parse_der_length(rest)
410 .ok_or(SignatureVerificationError::InvalidSignatureFormat)?
411 .map_err(|_| SignatureVerificationError::InvalidSignatureFormat)?;
412 let (integer_bytes, remainder) = rest
413 .split_at_checked(len)
414 .ok_or(SignatureVerificationError::InvalidSignatureFormat)?;
415
416 if integer_bytes.is_empty() {
417 return Err(SignatureVerificationError::InvalidSignatureFormat);
418 }
419 if integer_bytes.len() > component_len + 1 {
420 return Err(SignatureVerificationError::InvalidSignatureFormat);
421 }
422 if integer_bytes.len() == component_len + 1 && integer_bytes[0] != 0 {
423 return Err(SignatureVerificationError::InvalidSignatureFormat);
424 }
425 if integer_bytes[0] & 0x80 != 0 {
426 return Err(SignatureVerificationError::InvalidSignatureFormat);
427 }
428 if integer_bytes.len() > 1 && integer_bytes[0] == 0 && integer_bytes[1] & 0x80 == 0 {
429 return Err(SignatureVerificationError::InvalidSignatureFormat);
430 }
431
432 Ok(remainder)
433}
434
435fn parse_der_length(input: &[u8]) -> Option<Result<(usize, &[u8]), ()>> {
436 let (&len_byte, rest) = input.split_first()?;
437
438 if len_byte & 0x80 == 0 {
439 return Some(Ok((usize::from(len_byte), rest)));
440 }
441
442 let len_len = usize::from(len_byte & 0x7f);
443 if len_len == 0 || len_len > std::mem::size_of::<usize>() || rest.len() < len_len {
444 return Some(Err(()));
445 }
446
447 let (len_bytes, remainder) = rest.split_at(len_len);
448 if len_bytes[0] == 0 {
449 return Some(Err(()));
450 }
451
452 let mut declared_len = 0_usize;
453 for &byte in len_bytes {
454 declared_len = match declared_len.checked_mul(256) {
455 Some(len) => len,
456 None => return Some(Err(())),
457 };
458 declared_len = match declared_len.checked_add(usize::from(byte)) {
459 Some(len) => len,
460 None => return Some(Err(())),
461 };
462 }
463
464 if declared_len < 128 {
465 return Some(Err(()));
466 }
467
468 Some(Ok((declared_len, remainder)))
469}
470
471fn validate_ec_public_key_encoding(
472 ec: &ECPoint<'_>,
473 public_key_bytes: &[u8],
474) -> Result<(), SignatureVerificationError> {
475 let coordinate_len = ec_coordinate_len_bytes(ec.key_size())?;
476 let expected_len = coordinate_len
477 .checked_mul(2)
478 .and_then(|len| len.checked_add(1))
479 .ok_or(SignatureVerificationError::InvalidKeyDer)?;
480
481 let is_uncompressed_sec1 =
482 public_key_bytes.len() == expected_len && public_key_bytes.first() == Some(&0x04);
483 if !is_uncompressed_sec1 {
484 return Err(SignatureVerificationError::InvalidKeyDer);
485 }
486
487 Ok(())
488}
489
490fn ec_coordinate_len_bytes(key_bits: usize) -> Result<usize, SignatureVerificationError> {
491 key_bits
492 .checked_add(7)
493 .and_then(|bits| bits.checked_div(8))
494 .ok_or(SignatureVerificationError::InvalidKeyDer)
495}
496
497#[cfg(test)]
498#[expect(clippy::unwrap_used, reason = "unit tests use fixed fixture data")]
499mod tests {
500 use super::*;
501
502 #[test]
503 fn ecdsa_algorithms_are_rejected_for_rsa_verification() {
504 for algorithm in [
505 SignatureAlgorithm::EcdsaP256Sha256,
506 SignatureAlgorithm::EcdsaP384Sha384,
507 ] {
508 let err = verification_algorithm(algorithm).unwrap_err();
509 assert!(matches!(
510 err,
511 SignatureVerificationError::UnsupportedAlgorithm { .. }
512 ));
513 }
514 }
515
516 #[test]
517 fn der_like_prefix_with_fixed_width_len_is_classified_as_raw() {
518 let mut signature = vec![0xAA_u8; 96];
519 signature[0] = 0x30;
520 signature[1] = 0x20;
521
522 let encoding = classify_ecdsa_signature_encoding(&signature, 48)
523 .expect("same-width signature with invalid DER must fall back to raw");
524 assert_eq!(encoding, EcdsaSignatureEncoding::XmlDsigFixed);
525 }
526
527 #[test]
528 fn overlong_der_length_below_128_is_rejected() {
529 let bad = [0x81_u8, 0x7f];
530 let parsed = parse_der_length(&bad).expect("length bytes should be present");
531 assert!(
532 matches!(parsed, Err(())),
533 "DER must reject long-form lengths below 128"
534 );
535 }
536
537 #[test]
538 fn ec_coordinate_length_rounds_up_for_non_byte_aligned_curves() {
539 assert_eq!(
540 ec_coordinate_len_bytes(521).expect("521-bit curves require rounded byte length"),
541 66
542 );
543 }
544
545 #[test]
546 fn same_width_valid_der_is_marked_ambiguous() {
547 let mut signature = Vec::with_capacity(64);
548 signature.extend_from_slice(&[0x30, 0x3e, 0x02, 0x1d]);
549 signature.extend(std::iter::repeat_n(0x11_u8, 29));
550 signature.extend_from_slice(&[0x02, 0x1d]);
551 signature.extend(std::iter::repeat_n(0x22_u8, 29));
552
553 let encoding = classify_ecdsa_signature_encoding(&signature, 32)
554 .expect("same-width structurally valid DER should classify as ambiguous");
555 assert_eq!(encoding, EcdsaSignatureEncoding::Ambiguous);
556 }
557
558 #[test]
559 fn der_integer_longer_than_component_requires_sign_byte() {
560 let mut signature = Vec::with_capacity(72);
561 signature.extend_from_slice(&[0x30, 0x46, 0x02, 0x21, 0x01]);
562 signature.extend(std::iter::repeat_n(0x11_u8, 32));
563 signature.extend_from_slice(&[0x02, 0x21, 0x01]);
564 signature.extend(std::iter::repeat_n(0x22_u8, 32));
565
566 let encoding = classify_ecdsa_signature_encoding(&signature, 32);
567 assert!(matches!(
568 encoding,
569 Err(SignatureVerificationError::InvalidSignatureFormat)
570 ));
571 }
572}