手动执行全量同步
This commit is contained in:
@@ -6,6 +6,8 @@ use crate::data_model::rc::{
|
||||
use x509_parser::prelude::{FromDer, X509Certificate};
|
||||
|
||||
use crate::validation::x509_name::x509_names_equivalent;
|
||||
use std::collections::HashSet;
|
||||
use x509_parser::x509::SubjectPublicKeyInfo;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ValidatedEeCertPath {
|
||||
@@ -44,6 +46,14 @@ pub enum CertPathError {
|
||||
#[error("EE certificate signature verification failed: {0} (RFC 5280 §6.1)")]
|
||||
EeSignatureInvalid(String),
|
||||
|
||||
#[error("issuer SubjectPublicKeyInfo parse error: {0} (RFC 5280 §4.1.2.7)")]
|
||||
IssuerSpkiParse(String),
|
||||
|
||||
#[error(
|
||||
"trailing bytes after issuer SubjectPublicKeyInfo DER: {0} bytes (DER; RFC 5280 §4.1.2.7)"
|
||||
)]
|
||||
IssuerSpkiTrailingBytes(usize),
|
||||
|
||||
#[error("EE KeyUsage extension missing (RFC 6487 §4.8.4; RFC 5280 §4.2.1.3)")]
|
||||
KeyUsageMissing,
|
||||
|
||||
@@ -113,6 +123,7 @@ pub fn validate_ee_cert_path(
|
||||
if issuer_ca.kind != ResourceCertKind::Ca {
|
||||
return Err(CertPathError::IssuerNotCa);
|
||||
}
|
||||
let issuer_spki = parse_subject_pki_from_der(&issuer_ca.tbs.subject_public_key_info)?;
|
||||
|
||||
if !x509_names_equivalent(&ee.tbs.issuer_name, &issuer_ca.tbs.subject_name) {
|
||||
return Err(CertPathError::IssuerSubjectMismatch {
|
||||
@@ -141,8 +152,9 @@ pub fn validate_ee_cert_path(
|
||||
return Err(CertPathError::CertificateNotValidAtTime);
|
||||
}
|
||||
|
||||
verify_cert_signature_with_issuer(ee_cert_der, issuer_ca_der)?;
|
||||
validate_ee_key_usage(ee_cert_der)?;
|
||||
let ee_x509 = parse_x509_cert(ee_cert_der)?;
|
||||
verify_ee_cert_signature(&ee_x509, &issuer_spki)?;
|
||||
validate_ee_key_usage(&ee_x509)?;
|
||||
|
||||
let issuer_crl = RpkixCrl::decode_der(issuer_crl_der)?;
|
||||
issuer_crl.verify_signature_with_issuer_certificate_der(issuer_ca_der)?;
|
||||
@@ -161,6 +173,104 @@ pub fn validate_ee_cert_path(
|
||||
})
|
||||
}
|
||||
|
||||
/// Validate the EE certificate path using a *pre-decoded issuer CA* and a *pre-decoded and
|
||||
/// pre-verified issuer CRL*.
|
||||
///
|
||||
/// This is a performance-oriented helper for stage2 serial runs: it avoids repeating issuer CA
|
||||
/// decode and issuer CRL decode+signature verification for every signed object in a publication point.
|
||||
///
|
||||
/// The caller must ensure:
|
||||
/// - `issuer_ca` corresponds to `issuer_ca_der`
|
||||
/// - `issuer_crl` cryptographic signature/binding has already been verified against `issuer_ca_der`
|
||||
pub fn validate_ee_cert_path_with_prevalidated_issuer(
|
||||
ee_cert_der: &[u8],
|
||||
issuer_ca: &ResourceCertificate,
|
||||
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
||||
issuer_crl: &RpkixCrl,
|
||||
issuer_crl_revoked_serials: &HashSet<Vec<u8>>,
|
||||
issuer_ca_rsync_uri: Option<&str>,
|
||||
issuer_crl_rsync_uri: Option<&str>,
|
||||
validation_time: time::OffsetDateTime,
|
||||
) -> Result<ResourceCertificate, CertPathError> {
|
||||
let ee = ResourceCertificate::decode_der(ee_cert_der)?;
|
||||
if ee.kind != ResourceCertKind::Ee {
|
||||
return Err(CertPathError::EeNotEe);
|
||||
}
|
||||
if issuer_ca.kind != ResourceCertKind::Ca {
|
||||
return Err(CertPathError::IssuerNotCa);
|
||||
}
|
||||
|
||||
if !x509_names_equivalent(&ee.tbs.issuer_name, &issuer_ca.tbs.subject_name) {
|
||||
return Err(CertPathError::IssuerSubjectMismatch {
|
||||
ee_issuer_dn: ee.tbs.issuer_name.to_string(),
|
||||
issuer_subject_dn: issuer_ca.tbs.subject_name.to_string(),
|
||||
});
|
||||
}
|
||||
|
||||
validate_ee_aki_matches_issuer_ski(&ee, issuer_ca)?;
|
||||
if let Some(expected_issuer_uri) = issuer_ca_rsync_uri {
|
||||
validate_ee_aia_points_to_issuer_uri(&ee, expected_issuer_uri)?;
|
||||
}
|
||||
if let Some(expected_crl_uri) = issuer_crl_rsync_uri {
|
||||
validate_ee_crldp_contains_issuer_crl_uri(&ee, expected_crl_uri)?;
|
||||
}
|
||||
|
||||
if !time_within_validity(
|
||||
validation_time,
|
||||
ee.tbs.validity_not_before,
|
||||
ee.tbs.validity_not_after,
|
||||
) || !time_within_validity(
|
||||
validation_time,
|
||||
issuer_ca.tbs.validity_not_before,
|
||||
issuer_ca.tbs.validity_not_after,
|
||||
) {
|
||||
return Err(CertPathError::CertificateNotValidAtTime);
|
||||
}
|
||||
|
||||
let ee_x509 = parse_x509_cert(ee_cert_der)?;
|
||||
verify_ee_cert_signature(&ee_x509, issuer_spki)?;
|
||||
validate_ee_key_usage(&ee_x509)?;
|
||||
|
||||
if !crl_valid_at_time(issuer_crl, validation_time) {
|
||||
return Err(CertPathError::CrlNotValidAtTime);
|
||||
}
|
||||
|
||||
let serial = BigUnsigned::from_biguint(&ee.tbs.serial_number);
|
||||
if issuer_crl_revoked_serials.contains(&serial.bytes_be) {
|
||||
return Err(CertPathError::EeRevoked);
|
||||
}
|
||||
|
||||
Ok(ee)
|
||||
}
|
||||
|
||||
fn parse_subject_pki_from_der(der: &[u8]) -> Result<SubjectPublicKeyInfo<'_>, CertPathError> {
|
||||
let (rem, spki) = SubjectPublicKeyInfo::from_der(der)
|
||||
.map_err(|e| CertPathError::IssuerSpkiParse(e.to_string()))?;
|
||||
if !rem.is_empty() {
|
||||
return Err(CertPathError::IssuerSpkiTrailingBytes(rem.len()));
|
||||
}
|
||||
Ok(spki)
|
||||
}
|
||||
|
||||
fn parse_x509_cert(der: &[u8]) -> Result<X509Certificate<'_>, CertPathError> {
|
||||
let (rem, cert) = X509Certificate::from_der(der)
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))?;
|
||||
if !rem.is_empty() {
|
||||
return Err(CertPathError::EeSignatureInvalid(
|
||||
"trailing bytes after EE certificate".to_string(),
|
||||
));
|
||||
}
|
||||
Ok(cert)
|
||||
}
|
||||
|
||||
fn verify_ee_cert_signature(
|
||||
ee: &X509Certificate<'_>,
|
||||
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
||||
) -> Result<(), CertPathError> {
|
||||
ee.verify_signature(Some(issuer_spki))
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))
|
||||
}
|
||||
|
||||
fn validate_ee_aki_matches_issuer_ski(
|
||||
ee: &ResourceCertificate,
|
||||
issuer_ca: &ResourceCertificate,
|
||||
@@ -203,15 +313,7 @@ fn validate_ee_crldp_contains_issuer_crl_uri(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_ee_key_usage(ee_cert_der: &[u8]) -> Result<(), CertPathError> {
|
||||
let (rem, cert) = X509Certificate::from_der(ee_cert_der)
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))?;
|
||||
if !rem.is_empty() {
|
||||
return Err(CertPathError::EeSignatureInvalid(
|
||||
"trailing bytes after EE certificate".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
fn validate_ee_key_usage(cert: &X509Certificate<'_>) -> Result<(), CertPathError> {
|
||||
let mut ku_critical: Option<bool> = None;
|
||||
for ext in cert.extensions() {
|
||||
if ext.oid.as_bytes() == crate::data_model::oid::OID_KEY_USAGE_RAW {
|
||||
@@ -250,29 +352,6 @@ fn validate_ee_key_usage(ee_cert_der: &[u8]) -> Result<(), CertPathError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn verify_cert_signature_with_issuer(
|
||||
ee_cert_der: &[u8],
|
||||
issuer_ca_der: &[u8],
|
||||
) -> Result<(), CertPathError> {
|
||||
let (rem, ee) = X509Certificate::from_der(ee_cert_der)
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))?;
|
||||
if !rem.is_empty() {
|
||||
return Err(CertPathError::EeSignatureInvalid(
|
||||
"trailing bytes after EE certificate".to_string(),
|
||||
));
|
||||
}
|
||||
let (rem, issuer) = X509Certificate::from_der(issuer_ca_der)
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))?;
|
||||
if !rem.is_empty() {
|
||||
return Err(CertPathError::EeSignatureInvalid(
|
||||
"trailing bytes after issuer certificate".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
ee.verify_signature(Some(&issuer.tbs_certificate.subject_pki))
|
||||
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))
|
||||
}
|
||||
|
||||
fn time_within_validity(
|
||||
t: time::OffsetDateTime,
|
||||
not_before: time::OffsetDateTime,
|
||||
@@ -301,10 +380,10 @@ fn is_serial_revoked_by_crl(ee: &ResourceCertificate, crl: &RpkixCrl) -> bool {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::data_model::common::X509NameDer;
|
||||
use crate::data_model::rc::{
|
||||
RcExtensions, ResourceCertKind, ResourceCertificate, RpkixTbsCertificate,
|
||||
};
|
||||
use crate::data_model::common::X509NameDer;
|
||||
use der_parser::num_bigint::BigUint;
|
||||
|
||||
fn dummy_cert(
|
||||
|
||||
Reference in New Issue
Block a user