手动执行全量同步

This commit is contained in:
2026-03-04 11:12:53 +08:00
parent 0f3d65254e
commit 6276d13814
54 changed files with 3879 additions and 670 deletions

View File

@@ -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(