1use core::fmt::{Display, Formatter};
19
20#[cfg(not(feature = "std"))]
21use crate::internal_alloc::ToOwned;
22use crate::internal_alloc::{String, Vec};
23
24use noxtls_crypto::{
25 ed25519_verify, mldsa_verify, p256_ecdsa_verify_sha256, rsassa_pss_sha256_verify,
26 rsassa_pss_sha384_verify, rsassa_sha256_verify, rsassa_sha384_verify, rsassa_sha512_verify,
27 Ed25519PublicKey, MlDsaPublicKey, P256PublicKey, RsaPublicKey, OID_ID_MLDSA65,
28};
29
30use super::{parse_der_node, Certificate};
31
32#[derive(Debug, Clone, Eq, PartialEq)]
34pub enum ValidationError {
35 InvalidNowTimeFormat,
36 CertificateNotYetValid,
37 CertificateExpired,
38 IssuerNotFound,
39 IssuerNotCa,
40 IssuerMissingKeyCertSign,
41 PathLenExceeded,
42 UntrustedRoot,
43 ChainLoopDetected,
44 MaxChainDepthExceeded,
45 SignatureAlgorithmMismatch,
46 UnsupportedSignatureAlgorithm,
47 UnsupportedPublicKeyAlgorithm,
48 PublicKeyDecodeFailed,
49 SignatureVerificationFailed,
50 MissingRequiredPolicy,
51 MissingRequiredExtendedKeyUsage,
52 ExplicitPolicyRequired,
53 PolicyMappingInhibited,
54 NameConstraintsViolation,
55 MissingRevocationInfo,
56 MissingRevocationLocator,
57}
58
59impl Display for ValidationError {
60 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
63 match self {
64 Self::InvalidNowTimeFormat => f.write_str("invalid now timestamp format"),
65 Self::CertificateNotYetValid => f.write_str("certificate is not yet valid"),
66 Self::CertificateExpired => f.write_str("certificate is expired"),
67 Self::IssuerNotFound => f.write_str("certificate issuer not found"),
68 Self::IssuerNotCa => f.write_str("issuer certificate is not a CA"),
69 Self::IssuerMissingKeyCertSign => {
70 f.write_str("issuer certificate key usage missing keyCertSign")
71 }
72 Self::PathLenExceeded => f.write_str("issuer pathLenConstraint exceeded"),
73 Self::UntrustedRoot => {
74 f.write_str("certificate chain does not terminate at trust anchor")
75 }
76 Self::ChainLoopDetected => f.write_str("certificate chain loop detected"),
77 Self::MaxChainDepthExceeded => f.write_str("certificate chain depth exceeded"),
78 Self::SignatureAlgorithmMismatch => {
79 f.write_str("certificate signature algorithm mismatch")
80 }
81 Self::UnsupportedSignatureAlgorithm => {
82 f.write_str("certificate signature algorithm is unsupported")
83 }
84 Self::UnsupportedPublicKeyAlgorithm => {
85 f.write_str("issuer public key algorithm is unsupported")
86 }
87 Self::PublicKeyDecodeFailed => f.write_str("issuer public key decode failed"),
88 Self::SignatureVerificationFailed => {
89 f.write_str("certificate signature verification failed")
90 }
91 Self::MissingRequiredPolicy => f.write_str("certificate missing required policy OID"),
92 Self::MissingRequiredExtendedKeyUsage => {
93 f.write_str("certificate missing required extended key usage")
94 }
95 Self::ExplicitPolicyRequired => {
96 f.write_str("effective certificate policy set is empty")
97 }
98 Self::PolicyMappingInhibited => {
99 f.write_str("certificate policyMappings present while policy mapping is inhibited")
100 }
101 Self::NameConstraintsViolation => {
102 f.write_str("certificate subject violates issuer name constraints")
103 }
104 Self::MissingRevocationInfo => {
105 f.write_str("certificate missing revocation distribution info")
106 }
107 Self::MissingRevocationLocator => {
108 f.write_str("certificate missing CRL distribution point and AIA locator")
109 }
110 }
111 }
112}
113
114#[cfg(feature = "std")]
115impl std::error::Error for ValidationError {}
116
117#[derive(Debug, Clone, Eq, PartialEq)]
119pub struct ValidationReport {
120 pub chain_len: usize,
121 pub trust_anchor_index: usize,
122 pub effective_policy_oids: Vec<Vec<u8>>,
123}
124
125#[derive(Debug, Clone, Eq, PartialEq, Default)]
127pub struct ValidationOptions {
128 pub required_policy_oid: Option<Vec<u8>>,
129 pub required_extended_key_usage_oid: Option<Vec<u8>>,
130 pub require_explicit_policy: bool,
131 pub require_crl_distribution_points: bool,
132 pub require_revocation_locator: bool,
133 pub inhibit_policy_mapping: bool,
134}
135
136pub fn validate_certificate_chain<'a>(
147 leaf: &Certificate<'a>,
148 intermediates: &[Certificate<'a>],
149 trust_anchors: &[Certificate<'a>],
150 now: &str,
151) -> core::result::Result<ValidationReport, ValidationError> {
152 validate_certificate_chain_with_options(
153 leaf,
154 intermediates,
155 trust_anchors,
156 now,
157 &ValidationOptions::default(),
158 )
159}
160
161pub fn validate_certificate_chain_with_options<'a>(
173 leaf: &Certificate<'a>,
174 intermediates: &[Certificate<'a>],
175 trust_anchors: &[Certificate<'a>],
176 now: &str,
177 options: &ValidationOptions,
178) -> core::result::Result<ValidationReport, ValidationError> {
179 validate_certificate_chain_internal(leaf, intermediates, trust_anchors, now, true, options)
180}
181
182pub fn validate_certificate_chain_constraints_only<'a>(
193 leaf: &Certificate<'a>,
194 intermediates: &[Certificate<'a>],
195 trust_anchors: &[Certificate<'a>],
196 now: &str,
197) -> core::result::Result<ValidationReport, ValidationError> {
198 validate_certificate_chain_internal(
199 leaf,
200 intermediates,
201 trust_anchors,
202 now,
203 false,
204 &ValidationOptions::default(),
205 )
206}
207
208pub fn validate_certificate_chain_strict<'a>(
219 leaf: &Certificate<'a>,
220 intermediates: &[Certificate<'a>],
221 trust_anchors: &[Certificate<'a>],
222 now: &str,
223) -> core::result::Result<ValidationReport, ValidationError> {
224 validate_certificate_chain(leaf, intermediates, trust_anchors, now)
225}
226
227fn validate_certificate_chain_internal<'a>(
250 leaf: &Certificate<'a>,
251 intermediates: &[Certificate<'a>],
252 trust_anchors: &[Certificate<'a>],
253 now: &str,
254 enforce_signatures: bool,
255 options: &ValidationOptions,
256) -> core::result::Result<ValidationReport, ValidationError> {
257 if trust_anchors.is_empty() {
258 return Err(ValidationError::UntrustedRoot);
259 }
260
261 let now_canonical = canonical_time(now).ok_or(ValidationError::InvalidNowTimeFormat)?;
262 validate_chain_step(
263 leaf,
264 intermediates,
265 trust_anchors,
266 &now_canonical,
267 enforce_signatures,
268 options,
269 1,
270 0,
271 0,
272 ChainValidationState {
273 visited_serials: Vec::new(),
274 effective_policy_oids: None,
275 explicit_policy_skip_certs: if options.require_explicit_policy {
276 Some(0)
277 } else {
278 None
279 },
280 inhibit_any_policy_skip_certs: None,
281 inhibit_policy_mapping_skip_certs: if options.inhibit_policy_mapping {
282 Some(0)
283 } else {
284 None
285 },
286 },
287 )
288}
289
290#[derive(Clone, Debug, Default)]
291struct ChainValidationState {
292 visited_serials: Vec<Vec<u8>>,
293 effective_policy_oids: Option<Vec<Vec<u8>>>,
294 explicit_policy_skip_certs: Option<u32>,
295 inhibit_any_policy_skip_certs: Option<u32>,
296 inhibit_policy_mapping_skip_certs: Option<u32>,
297}
298
299fn validate_chain_step<'a>(
326 current: &Certificate<'a>,
327 intermediates: &[Certificate<'a>],
328 trust_anchors: &[Certificate<'a>],
329 now_canonical: &str,
330 enforce_signatures: bool,
331 options: &ValidationOptions,
332 chain_len: usize,
333 ca_hops_below_issuer: usize,
334 hop_count: usize,
335 mut state: ChainValidationState,
336) -> core::result::Result<ValidationReport, ValidationError> {
337 const OID_ANY_POLICY: &[u8] = &[0x55, 0x1d, 0x20, 0x00];
338 if hop_count > 16 {
339 return Err(ValidationError::MaxChainDepthExceeded);
340 }
341
342 validate_time(current, now_canonical)?;
343 validate_policy_and_revocation(current, options, chain_len == 1)?;
344 update_inhibit_any_policy_skip_certs(&mut state.inhibit_any_policy_skip_certs, current);
345 update_effective_policies(
346 &mut state.effective_policy_oids,
347 current,
348 explicit_policy_is_active(state.inhibit_any_policy_skip_certs),
349 OID_ANY_POLICY,
350 );
351 update_explicit_policy_skip_certs(&mut state.explicit_policy_skip_certs, current);
352 enforce_explicit_policy_progress(
353 &state.effective_policy_oids,
354 state.explicit_policy_skip_certs,
355 )?;
356 update_inhibit_policy_mapping_skip_certs(&mut state.inhibit_policy_mapping_skip_certs, current);
357 enforce_policy_mapping(current, state.inhibit_policy_mapping_skip_certs)?;
358 if state
359 .visited_serials
360 .iter()
361 .any(|serial| serial.as_slice() == current.serial.as_slice())
362 {
363 return Err(ValidationError::ChainLoopDetected);
364 }
365 state.visited_serials.push(current.serial.clone());
366
367 if current.subject_raw == current.issuer_raw {
368 let anchor_idx = trust_anchors
369 .iter()
370 .position(|anchor| anchor.subject_raw == current.subject_raw)
371 .ok_or(ValidationError::UntrustedRoot)?;
372 let policies = finalize_effective_policies(
373 &state.effective_policy_oids,
374 state.explicit_policy_skip_certs,
375 )?;
376 return Ok(ValidationReport {
377 chain_len,
378 trust_anchor_index: anchor_idx,
379 effective_policy_oids: policies,
380 });
381 }
382
383 if let Some(anchor_idx) = trust_anchors
384 .iter()
385 .position(|anchor| anchor.subject_raw == current.issuer_raw)
386 {
387 let issuer = &trust_anchors[anchor_idx];
388 validate_time(issuer, now_canonical)?;
389 validate_policy_and_revocation(issuer, options, false)?;
390 apply_policy_mappings_for_issuer(&mut state.effective_policy_oids, issuer);
391 update_inhibit_any_policy_skip_certs(&mut state.inhibit_any_policy_skip_certs, issuer);
392 update_effective_policies(
393 &mut state.effective_policy_oids,
394 issuer,
395 explicit_policy_is_active(state.inhibit_any_policy_skip_certs),
396 OID_ANY_POLICY,
397 );
398 update_explicit_policy_skip_certs(&mut state.explicit_policy_skip_certs, issuer);
399 enforce_explicit_policy_progress(
400 &state.effective_policy_oids,
401 state.explicit_policy_skip_certs,
402 )?;
403 update_inhibit_policy_mapping_skip_certs(
404 &mut state.inhibit_policy_mapping_skip_certs,
405 issuer,
406 );
407 enforce_policy_mapping(current, state.inhibit_policy_mapping_skip_certs)?;
408 enforce_policy_mapping(issuer, state.inhibit_policy_mapping_skip_certs)?;
409 validate_issuer_constraints(issuer, ca_hops_below_issuer)?;
410 validate_name_constraints(issuer, current)?;
411 if enforce_signatures {
412 verify_certificate_signature(current, issuer)?;
413 }
414 let policies = finalize_effective_policies(
415 &state.effective_policy_oids,
416 state.explicit_policy_skip_certs,
417 )?;
418 return Ok(ValidationReport {
419 chain_len: chain_len + 1,
420 trust_anchor_index: anchor_idx,
421 effective_policy_oids: policies,
422 });
423 }
424
425 let issuer_candidates: Vec<&Certificate<'a>> = intermediates
426 .iter()
427 .filter(|candidate| candidate.subject_raw == current.issuer_raw)
428 .collect();
429 if issuer_candidates.is_empty() {
430 return Err(ValidationError::IssuerNotFound);
431 }
432
433 let mut last_error = ValidationError::IssuerNotFound;
434 for issuer in issuer_candidates {
435 let mut next_state = state.clone();
436 let candidate_result = (|| -> core::result::Result<ValidationReport, ValidationError> {
437 validate_time(issuer, now_canonical)?;
438 validate_policy_and_revocation(issuer, options, false)?;
439 apply_policy_mappings_for_issuer(&mut next_state.effective_policy_oids, issuer);
440 update_inhibit_any_policy_skip_certs(
441 &mut next_state.inhibit_any_policy_skip_certs,
442 issuer,
443 );
444 update_effective_policies(
445 &mut next_state.effective_policy_oids,
446 issuer,
447 explicit_policy_is_active(next_state.inhibit_any_policy_skip_certs),
448 OID_ANY_POLICY,
449 );
450 update_explicit_policy_skip_certs(&mut next_state.explicit_policy_skip_certs, issuer);
451 enforce_explicit_policy_progress(
452 &next_state.effective_policy_oids,
453 next_state.explicit_policy_skip_certs,
454 )?;
455 update_inhibit_policy_mapping_skip_certs(
456 &mut next_state.inhibit_policy_mapping_skip_certs,
457 issuer,
458 );
459 enforce_policy_mapping(current, next_state.inhibit_policy_mapping_skip_certs)?;
460 enforce_policy_mapping(issuer, next_state.inhibit_policy_mapping_skip_certs)?;
461 validate_issuer_constraints(issuer, ca_hops_below_issuer)?;
462 validate_name_constraints(issuer, current)?;
463 if enforce_signatures {
464 verify_certificate_signature(current, issuer)?;
465 }
466 decrement_skip_certs_counter(&mut next_state.inhibit_any_policy_skip_certs, current);
467 decrement_skip_certs_counter(&mut next_state.explicit_policy_skip_certs, current);
468 decrement_skip_certs_counter(
469 &mut next_state.inhibit_policy_mapping_skip_certs,
470 current,
471 );
472
473 validate_chain_step(
474 issuer,
475 intermediates,
476 trust_anchors,
477 now_canonical,
478 enforce_signatures,
479 options,
480 chain_len + 1,
481 ca_hops_below_issuer + 1,
482 hop_count + 1,
483 next_state,
484 )
485 })();
486 match candidate_result {
487 Ok(report) => return Ok(report),
488 Err(err) => last_error = err,
489 }
490 }
491 Err(last_error)
492}
493
494pub fn verify_certificate_signature(
503 certificate: &Certificate<'_>,
504 issuer: &Certificate<'_>,
505) -> core::result::Result<(), ValidationError> {
506 const OID_RSA_ENCRYPTION: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01];
507 const OID_EC_PUBLIC_KEY: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01];
508 const OID_SHA256_WITH_RSA: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b];
509 const OID_SHA384_WITH_RSA: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c];
510 const OID_SHA512_WITH_RSA: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d];
511 const OID_RSASSA_PSS: &[u8] = &[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0a];
512 const OID_ECDSA_WITH_SHA256: &[u8] = &[0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02];
513 const OID_ED25519: &[u8] = &[0x2b, 0x65, 0x70];
514
515 if certificate.tbs_signature_algorithm_oid != certificate.certificate_signature_algorithm_oid {
516 return Err(ValidationError::SignatureAlgorithmMismatch);
517 }
518 if issuer.subject_public_key_algorithm_oid == OID_RSA_ENCRYPTION {
519 let (n, e) = parse_rsa_public_key_der(&issuer.subject_public_key)?;
520 let public_key = RsaPublicKey::from_be_bytes(&n, &e)
521 .map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
522 if certificate.certificate_signature_algorithm_oid == OID_SHA256_WITH_RSA {
523 return rsassa_sha256_verify(
524 &public_key,
525 certificate.raw_tbs_der,
526 &certificate.signature_value,
527 )
528 .map_err(|_| ValidationError::SignatureVerificationFailed);
529 }
530 if certificate.certificate_signature_algorithm_oid == OID_SHA384_WITH_RSA {
531 return rsassa_sha384_verify(
532 &public_key,
533 certificate.raw_tbs_der,
534 &certificate.signature_value,
535 )
536 .map_err(|_| ValidationError::SignatureVerificationFailed);
537 }
538 if certificate.certificate_signature_algorithm_oid == OID_SHA512_WITH_RSA {
539 return rsassa_sha512_verify(
540 &public_key,
541 certificate.raw_tbs_der,
542 &certificate.signature_value,
543 )
544 .map_err(|_| ValidationError::SignatureVerificationFailed);
545 }
546 if certificate.certificate_signature_algorithm_oid == OID_RSASSA_PSS {
547 if rsassa_pss_sha256_verify(
550 &public_key,
551 certificate.raw_tbs_der,
552 &certificate.signature_value,
553 32,
554 )
555 .is_ok()
556 {
557 return Ok(());
558 }
559 return rsassa_pss_sha384_verify(
560 &public_key,
561 certificate.raw_tbs_der,
562 &certificate.signature_value,
563 48,
564 )
565 .map_err(|_| ValidationError::SignatureVerificationFailed);
566 }
567 return Err(ValidationError::UnsupportedSignatureAlgorithm);
568 }
569
570 if issuer.subject_public_key_algorithm_oid == OID_EC_PUBLIC_KEY {
571 if certificate.certificate_signature_algorithm_oid != OID_ECDSA_WITH_SHA256 {
572 return Err(ValidationError::UnsupportedSignatureAlgorithm);
573 }
574 let public_key = P256PublicKey::from_uncompressed(&issuer.subject_public_key)
575 .map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
576 let (r, s) = parse_ecdsa_signature_der(&certificate.signature_value)?;
577 return p256_ecdsa_verify_sha256(&public_key, certificate.raw_tbs_der, &r, &s)
578 .map_err(|_| ValidationError::SignatureVerificationFailed);
579 }
580
581 if issuer.subject_public_key_algorithm_oid.as_slice() == OID_ED25519 {
582 if certificate.certificate_signature_algorithm_oid.as_slice() != OID_ED25519 {
583 return Err(ValidationError::UnsupportedSignatureAlgorithm);
584 }
585 if certificate.signature_value.len() != 64 {
586 return Err(ValidationError::SignatureVerificationFailed);
587 }
588 let key_bytes: [u8; 32] = issuer
589 .subject_public_key
590 .as_slice()
591 .try_into()
592 .map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
593 let public_key = Ed25519PublicKey::from_bytes(&key_bytes)
594 .map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
595 return ed25519_verify(
596 &public_key,
597 certificate.raw_tbs_der,
598 certificate.signature_value.as_slice(),
599 )
600 .map_err(|_| ValidationError::SignatureVerificationFailed);
601 }
602
603 if issuer.subject_public_key_algorithm_oid.as_slice() == OID_ID_MLDSA65 {
604 if certificate.certificate_signature_algorithm_oid.as_slice() != OID_ID_MLDSA65 {
605 return Err(ValidationError::UnsupportedSignatureAlgorithm);
606 }
607 let public_key = MlDsaPublicKey::from_bytes(&issuer.subject_public_key)
608 .map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
609 return mldsa_verify(
610 &public_key,
611 certificate.raw_tbs_der,
612 certificate.signature_value.as_slice(),
613 )
614 .map_err(|_| ValidationError::SignatureVerificationFailed);
615 }
616
617 Err(ValidationError::UnsupportedPublicKeyAlgorithm)
618}
619
620fn validate_issuer_constraints(
639 issuer: &Certificate<'_>,
640 ca_hops_below_issuer: usize,
641) -> core::result::Result<(), ValidationError> {
642 if issuer.basic_constraints_ca != Some(true) {
643 return Err(ValidationError::IssuerNotCa);
644 }
645 if let Some(key_usage) = issuer.key_usage_bits {
646 if (key_usage & 0x0004) == 0 {
648 return Err(ValidationError::IssuerMissingKeyCertSign);
649 }
650 }
651 if let Some(path_len) = issuer.basic_constraints_path_len {
652 if ca_hops_below_issuer > path_len as usize {
653 return Err(ValidationError::PathLenExceeded);
654 }
655 }
656 Ok(())
657}
658
659fn validate_time(
678 cert: &Certificate<'_>,
679 now_canonical: &str,
680) -> core::result::Result<(), ValidationError> {
681 let not_before =
682 canonical_time(&cert.not_before).ok_or(ValidationError::InvalidNowTimeFormat)?;
683 let not_after = canonical_time(&cert.not_after).ok_or(ValidationError::InvalidNowTimeFormat)?;
684 if now_canonical < not_before.as_str() {
685 return Err(ValidationError::CertificateNotYetValid);
686 }
687 if now_canonical > not_after.as_str() {
688 return Err(ValidationError::CertificateExpired);
689 }
690 Ok(())
691}
692
693fn canonical_time(input: &str) -> Option<String> {
707 if input.len() == 15 && input.ends_with('Z') {
708 let body = &input[..14];
709 if body.chars().all(|c| c.is_ascii_digit()) {
710 return Some(input.to_owned());
711 }
712 return None;
713 }
714 if input.len() == 13 && input.ends_with('Z') {
715 let yy = &input[..2];
716 let rest = &input[2..12];
717 if !yy.chars().all(|c| c.is_ascii_digit()) || !rest.chars().all(|c| c.is_ascii_digit()) {
718 return None;
719 }
720 let yy_value = yy.parse::<u32>().ok()?;
721 let century = if yy_value >= 50 { "19" } else { "20" };
722 return Some(format!("{century}{yy}{rest}Z"));
723 }
724 None
725}
726
727fn parse_rsa_public_key_der(
745 public_key_der: &[u8],
746) -> core::result::Result<(Vec<u8>, Vec<u8>), ValidationError> {
747 let (rsa_seq, rem) =
748 parse_der_node(public_key_der).map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
749 if rsa_seq.tag != 0x30 || !rem.is_empty() {
750 return Err(ValidationError::PublicKeyDecodeFailed);
751 }
752 let (modulus_node, rest) =
753 parse_der_node(rsa_seq.body).map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
754 let (exponent_node, tail) =
755 parse_der_node(rest).map_err(|_| ValidationError::PublicKeyDecodeFailed)?;
756 if modulus_node.tag != 0x02 || exponent_node.tag != 0x02 || !tail.is_empty() {
757 return Err(ValidationError::PublicKeyDecodeFailed);
758 }
759 Ok((modulus_node.body.to_vec(), exponent_node.body.to_vec()))
760}
761
762fn parse_ecdsa_signature_der(
780 signature_der: &[u8],
781) -> core::result::Result<([u8; 32], [u8; 32]), ValidationError> {
782 let (seq, rem) =
783 parse_der_node(signature_der).map_err(|_| ValidationError::SignatureVerificationFailed)?;
784 if seq.tag != 0x30 || !rem.is_empty() {
785 return Err(ValidationError::SignatureVerificationFailed);
786 }
787 let (r_node, rest) =
788 parse_der_node(seq.body).map_err(|_| ValidationError::SignatureVerificationFailed)?;
789 let (s_node, tail) =
790 parse_der_node(rest).map_err(|_| ValidationError::SignatureVerificationFailed)?;
791 if r_node.tag != 0x02 || s_node.tag != 0x02 || !tail.is_empty() {
792 return Err(ValidationError::SignatureVerificationFailed);
793 }
794 let r = ecdsa_integer_to_scalar32(r_node.body)?;
795 let s = ecdsa_integer_to_scalar32(s_node.body)?;
796 Ok((r, s))
797}
798
799fn ecdsa_integer_to_scalar32(value: &[u8]) -> core::result::Result<[u8; 32], ValidationError> {
817 if value.is_empty() {
818 return Err(ValidationError::SignatureVerificationFailed);
819 }
820 if value[0] & 0x80 != 0 {
821 return Err(ValidationError::SignatureVerificationFailed);
822 }
823 if value.len() > 1 && value[0] == 0x00 && value[1] & 0x80 == 0 {
824 return Err(ValidationError::SignatureVerificationFailed);
825 }
826 let normalized = if value.len() > 1 && value[0] == 0x00 {
827 &value[1..]
828 } else {
829 value
830 };
831 if normalized.len() > 32 {
832 return Err(ValidationError::SignatureVerificationFailed);
833 }
834 let mut out = [0_u8; 32];
835 out[32 - normalized.len()..].copy_from_slice(normalized);
836 Ok(out)
837}
838
839fn validate_policy_and_revocation(
859 cert: &Certificate<'_>,
860 options: &ValidationOptions,
861 is_leaf: bool,
862) -> core::result::Result<(), ValidationError> {
863 if let Some(required_policy) = &options.required_policy_oid {
864 if cert
865 .certificate_policies
866 .iter()
867 .all(|policy| policy != required_policy)
868 {
869 return Err(ValidationError::MissingRequiredPolicy);
870 }
871 }
872 if options.require_crl_distribution_points
873 && cert.crl_distribution_uris.is_empty()
874 && cert.subject_raw != cert.issuer_raw
875 {
876 return Err(ValidationError::MissingRevocationInfo);
877 }
878 if options.require_revocation_locator
879 && cert.crl_distribution_uris.is_empty()
880 && cert.authority_info_access_uris.is_empty()
881 && cert.subject_raw != cert.issuer_raw
882 {
883 return Err(ValidationError::MissingRevocationLocator);
884 }
885 if is_leaf {
886 if let Some(required_eku) = &options.required_extended_key_usage_oid {
887 if cert
888 .extended_key_usage_oids
889 .iter()
890 .all(|usage| usage != required_eku)
891 {
892 return Err(ValidationError::MissingRequiredExtendedKeyUsage);
893 }
894 }
895 }
896 Ok(())
897}
898
899fn update_effective_policies(
916 effective_policy_oids: &mut Option<Vec<Vec<u8>>>,
917 cert: &Certificate<'_>,
918 inhibit_any_policy_active: bool,
919 any_policy_oid: &[u8],
920) {
921 if cert.certificate_policies.is_empty() {
922 return;
923 }
924 let current = unique_policies(&cert.certificate_policies);
925 let has_any_policy = current.iter().any(|policy| policy == any_policy_oid);
926 if has_any_policy && !inhibit_any_policy_active {
927 return;
929 }
930 match effective_policy_oids {
931 None => *effective_policy_oids = Some(current),
932 Some(existing) => {
933 existing.retain(|policy| current.iter().any(|candidate| candidate == policy));
934 }
935 }
936}
937
938fn update_inhibit_any_policy_skip_certs(
953 inhibit_any_policy_skip_certs: &mut Option<u32>,
954 cert: &Certificate<'_>,
955) {
956 if let Some(inhibit_any_policy) = cert.inhibit_any_policy_skip_certs {
957 let next_value = inhibit_any_policy_skip_certs.map_or(inhibit_any_policy, |existing| {
958 existing.min(inhibit_any_policy)
959 });
960 *inhibit_any_policy_skip_certs = Some(next_value);
961 }
962}
963
964fn apply_policy_mappings_for_issuer(
979 effective_policy_oids: &mut Option<Vec<Vec<u8>>>,
980 issuer: &Certificate<'_>,
981) {
982 let Some(existing) = effective_policy_oids.as_ref() else {
983 return;
984 };
985 if issuer.policy_mappings.is_empty() {
986 return;
987 }
988
989 let mut remapped = Vec::new();
990 for policy in existing {
991 let mut mapped = false;
992 for (issuer_policy, subject_policy) in &issuer.policy_mappings {
993 if policy == subject_policy {
995 remapped.push(issuer_policy.clone());
996 mapped = true;
997 }
998 }
999 if !mapped {
1000 remapped.push(policy.clone());
1001 }
1002 }
1003 *effective_policy_oids = Some(unique_policies(&remapped));
1004}
1005
1006fn unique_policies(policies: &[Vec<u8>]) -> Vec<Vec<u8>> {
1020 let mut out = Vec::new();
1021 for policy in policies {
1022 if out.iter().all(|existing| existing != policy) {
1023 out.push(policy.clone());
1024 }
1025 }
1026 out
1027}
1028
1029fn finalize_effective_policies(
1048 effective_policy_oids: &Option<Vec<Vec<u8>>>,
1049 explicit_policy_skip_certs: Option<u32>,
1050) -> core::result::Result<Vec<Vec<u8>>, ValidationError> {
1051 let policies = effective_policy_oids.clone().unwrap_or_default();
1052 if explicit_policy_is_active(explicit_policy_skip_certs) && policies.is_empty() {
1053 return Err(ValidationError::ExplicitPolicyRequired);
1054 }
1055 Ok(policies)
1056}
1057
1058fn enforce_explicit_policy_progress(
1077 effective_policy_oids: &Option<Vec<Vec<u8>>>,
1078 explicit_policy_skip_certs: Option<u32>,
1079) -> core::result::Result<(), ValidationError> {
1080 let has_effective_policies = effective_policy_oids
1081 .as_ref()
1082 .is_some_and(|value| !value.is_empty());
1083 if explicit_policy_is_active(explicit_policy_skip_certs) && !has_effective_policies {
1084 return Err(ValidationError::ExplicitPolicyRequired);
1085 }
1086 Ok(())
1087}
1088
1089fn update_explicit_policy_skip_certs(
1104 explicit_policy_skip_certs: &mut Option<u32>,
1105 cert: &Certificate<'_>,
1106) {
1107 if let Some(require_explicit_policy) = cert.policy_constraints_require_explicit_policy {
1108 let next_value = explicit_policy_skip_certs.map_or(require_explicit_policy, |existing| {
1109 existing.min(require_explicit_policy)
1110 });
1111 *explicit_policy_skip_certs = Some(next_value);
1112 }
1113}
1114
1115fn decrement_skip_certs_counter(
1130 explicit_policy_skip_certs: &mut Option<u32>,
1131 cert: &Certificate<'_>,
1132) {
1133 if cert.subject_raw == cert.issuer_raw {
1134 return;
1135 }
1136 if let Some(counter) = explicit_policy_skip_certs.as_mut() {
1137 *counter = counter.saturating_sub(1);
1138 }
1139}
1140
1141fn explicit_policy_is_active(explicit_policy_skip_certs: Option<u32>) -> bool {
1155 matches!(explicit_policy_skip_certs, Some(0))
1156}
1157
1158fn update_inhibit_policy_mapping_skip_certs(
1173 inhibit_policy_mapping_skip_certs: &mut Option<u32>,
1174 cert: &Certificate<'_>,
1175) {
1176 if let Some(inhibit_policy_mapping) = cert.policy_constraints_inhibit_policy_mapping {
1177 let next_value = inhibit_policy_mapping_skip_certs
1178 .map_or(inhibit_policy_mapping, |existing| {
1179 existing.min(inhibit_policy_mapping)
1180 });
1181 *inhibit_policy_mapping_skip_certs = Some(next_value);
1182 }
1183}
1184
1185fn enforce_policy_mapping(
1204 cert: &Certificate<'_>,
1205 inhibit_policy_mapping_skip_certs: Option<u32>,
1206) -> core::result::Result<(), ValidationError> {
1207 if matches!(inhibit_policy_mapping_skip_certs, Some(0)) && !cert.policy_mappings.is_empty() {
1208 return Err(ValidationError::PolicyMappingInhibited);
1209 }
1210 Ok(())
1211}
1212
1213fn validate_name_constraints(
1232 issuer: &Certificate<'_>,
1233 subject: &Certificate<'_>,
1234) -> core::result::Result<(), ValidationError> {
1235 let has_dns_constraints = !issuer.name_constraints_permitted_dns.is_empty()
1236 || !issuer.name_constraints_excluded_dns.is_empty();
1237 if !has_dns_constraints || subject.subject_alt_dns_names.is_empty() {
1238 return Ok(());
1239 }
1240
1241 for dns_name in &subject.subject_alt_dns_names {
1242 if issuer
1243 .name_constraints_excluded_dns
1244 .iter()
1245 .any(|constraint| dns_name_matches_constraint(dns_name, constraint))
1246 {
1247 return Err(ValidationError::NameConstraintsViolation);
1248 }
1249 if !issuer.name_constraints_permitted_dns.is_empty()
1250 && issuer
1251 .name_constraints_permitted_dns
1252 .iter()
1253 .all(|constraint| !dns_name_matches_constraint(dns_name, constraint))
1254 {
1255 return Err(ValidationError::NameConstraintsViolation);
1256 }
1257 }
1258 Ok(())
1259}
1260
1261fn dns_name_matches_constraint(dns_name: &str, constraint: &str) -> bool {
1276 let normalized_name = dns_name.to_ascii_lowercase();
1277 let normalized_constraint = constraint.trim_start_matches('.').to_ascii_lowercase();
1278 if normalized_constraint.is_empty() {
1279 return false;
1280 }
1281 normalized_name == normalized_constraint
1282 || normalized_name
1283 .strip_suffix(&normalized_constraint)
1284 .is_some_and(|prefix| prefix.ends_with('.'))
1285}