// Certificate profile, signature, CRL, and validity checks. fn parse_subject_pki_from_der(der: &[u8]) -> Result, CaPathError> { let (rem, spki) = SubjectPublicKeyInfo::from_der(der) .map_err(|e| CaPathError::IssuerSpkiParse(e.to_string()))?; if !rem.is_empty() { return Err(CaPathError::IssuerSpkiTrailingBytes(rem.len())); } Ok(spki) } fn parse_x509_cert(der: &[u8]) -> Result, CaPathError> { let (rem, cert) = X509Certificate::from_der(der) .map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string()))?; if !rem.is_empty() { return Err(CaPathError::ChildSignatureInvalid( "trailing bytes after child certificate".to_string(), )); } Ok(cert) } fn verify_child_signature( child: &X509Certificate<'_>, issuer_spki: &SubjectPublicKeyInfo<'_>, ) -> Result<(), CaPathError> { child .verify_signature(Some(issuer_spki)) .map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string())) } fn validate_child_aki_matches_issuer_ski( child: &ResourceCertificate, issuer: &ResourceCertificate, ) -> Result<(), CaPathError> { let Some(issuer_ski) = issuer.tbs.extensions.subject_key_identifier.as_deref() else { return Err(CaPathError::IssuerSkiMissing); }; let Some(child_aki) = child.tbs.extensions.authority_key_identifier.as_deref() else { return Err(CaPathError::ChildAkiMissing); }; if child_aki != issuer_ski { return Err(CaPathError::ChildAkiMismatch); } Ok(()) } fn validate_child_aia_points_to_issuer_uri( child: &ResourceCertificate, issuer_ca_rsync_uri: &str, ) -> Result<(), CaPathError> { let Some(uris) = child.tbs.extensions.ca_issuers_uris.as_ref() else { return Err(CaPathError::ChildAiaMissing); }; if !uris.iter().any(|u| u.as_str() == issuer_ca_rsync_uri) { return Err(CaPathError::ChildAiaIssuerUriMismatch); } Ok(()) } fn validate_child_crldp_contains_issuer_crl_uri( child: &ResourceCertificate, issuer_crl_rsync_uri: &str, ) -> Result<(), CaPathError> { let Some(uris) = child.tbs.extensions.crl_distribution_points_uris.as_ref() else { return Err(CaPathError::ChildCrlDpMissing); }; if !uris.iter().any(|u| u.as_str() == issuer_crl_rsync_uri) { return Err(CaPathError::ChildCrlDpUriMismatch); } Ok(()) } fn validate_child_ca_key_usage(cert: &X509Certificate<'_>) -> Result<(), CaPathError> { let mut ku_critical: Option = None; for ext in cert.extensions() { if ext.oid.as_bytes() == OID_KEY_USAGE_RAW { ku_critical = Some(ext.critical); break; } } let Some(critical) = ku_critical else { return Err(CaPathError::KeyUsageMissing); }; if !critical { return Err(CaPathError::KeyUsageNotCritical); } let Some(ku) = cert .key_usage() .map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string()))? else { return Err(CaPathError::KeyUsageMissing); }; let v = &ku.value; let ok = v.key_cert_sign() && v.crl_sign() && !v.digital_signature() && !v.non_repudiation() && !v.key_encipherment() && !v.data_encipherment() && !v.key_agreement() && !v.encipher_only() && !v.decipher_only(); if !ok { return Err(CaPathError::KeyUsageInvalidBits); } Ok(()) } fn time_within_validity( t: time::OffsetDateTime, not_before: time::OffsetDateTime, not_after: time::OffsetDateTime, ) -> bool { let t = t.to_offset(time::UtcOffset::UTC); let not_before = not_before.to_offset(time::UtcOffset::UTC); let not_after = not_after.to_offset(time::UtcOffset::UTC); t >= not_before && t <= not_after } fn crl_valid_at_time(crl: &RpkixCrl, t: time::OffsetDateTime) -> bool { let t = t.to_offset(time::UtcOffset::UTC); let this_update = crl.this_update.utc.to_offset(time::UtcOffset::UTC); let next_update = crl.next_update.utc.to_offset(time::UtcOffset::UTC); t >= this_update && t < next_update } fn is_serial_revoked_by_crl(cert: &ResourceCertificate, crl: &RpkixCrl) -> bool { let serial = BigUnsigned::from_biguint(&cert.tbs.serial_number); crl.revoked_certs .iter() .any(|rc| rc.serial_number == serial) }