20260418 优化去掉冗余存储repo object,mix quick最快170s

This commit is contained in:
2026-04-18 14:10:47 +08:00
parent f485786470
commit 417c82bef6
34 changed files with 1482 additions and 328 deletions

View File

@@ -3,6 +3,8 @@ use crate::data_model::crl::{CrlDecodeError, CrlVerifyError, RpkixCrl};
use crate::data_model::rc::{
ResourceCertKind, ResourceCertificate, ResourceCertificateDecodeError,
};
use crate::data_model::signed_object::{EeKeyUsageSummary, ResourceEeCertificate};
use ring::signature;
use x509_parser::prelude::{FromDer, X509Certificate};
use crate::validation::x509_name::x509_names_equivalent;
@@ -63,6 +65,9 @@ pub enum CertPathError {
#[error("EE KeyUsage must have only digitalSignature set (RFC 6487 §4.8.4)")]
KeyUsageInvalidBits,
#[error("EE KeyUsage parse failed: {0} (RFC 6487 §4.8.4; RFC 5280 §4.2.1.3)")]
KeyUsageParse(String),
#[error("issuer CA subjectKeyIdentifier missing (RFC 6487 §4.8.2)")]
IssuerSkiMissing,
@@ -235,6 +240,34 @@ pub fn validate_ee_cert_path_with_predecoded_ee(
)
}
/// Validate a signed-object embedded EE certificate path using signed-object cached fields.
///
/// This avoids reparsing the EE certificate as `X509Certificate` and bypasses the
/// x509-parser signature wrapper by verifying the cached TBS/signature bytes directly.
pub fn validate_signed_object_ee_cert_path_fast(
ee: &ResourceEeCertificate,
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<(), CertPathError> {
validate_ee_cert_path_bindings_and_status(
&ee.resource_cert,
issuer_ca,
issuer_crl,
issuer_crl_revoked_serials,
issuer_ca_rsync_uri,
issuer_crl_rsync_uri,
validation_time,
)?;
validate_ee_key_usage_summary(&ee.key_usage_summary)?;
verify_ee_cert_signature_fast(&ee.tbs_certificate_der, &ee.signature_bytes, issuer_spki)?;
Ok(())
}
fn validate_ee_cert_path_components(
ee: &ResourceCertificate,
ee_cert_der: &[u8],
@@ -245,6 +278,32 @@ fn validate_ee_cert_path_components(
issuer_ca_rsync_uri: Option<&str>,
issuer_crl_rsync_uri: Option<&str>,
validation_time: time::OffsetDateTime,
) -> Result<(), CertPathError> {
validate_ee_cert_path_bindings_and_status(
ee,
issuer_ca,
issuer_crl,
issuer_crl_revoked_serials,
issuer_ca_rsync_uri,
issuer_crl_rsync_uri,
validation_time,
)?;
let ee_x509 = parse_x509_cert(ee_cert_der)?;
verify_ee_cert_signature(&ee_x509, issuer_spki)?;
validate_ee_key_usage(&ee_x509)?;
Ok(())
}
fn validate_ee_cert_path_bindings_and_status(
ee: &ResourceCertificate,
issuer_ca: &ResourceCertificate,
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<(), CertPathError> {
if ee.kind != ResourceCertKind::Ee {
return Err(CertPathError::EeNotEe);
@@ -280,10 +339,6 @@ fn validate_ee_cert_path_components(
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);
}
@@ -324,6 +379,19 @@ fn verify_ee_cert_signature(
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))
}
fn verify_ee_cert_signature_fast(
ee_tbs_der: &[u8],
ee_signature_bytes: &[u8],
issuer_spki: &SubjectPublicKeyInfo<'_>,
) -> Result<(), CertPathError> {
let key = signature::UnparsedPublicKey::new(
&signature::RSA_PKCS1_2048_8192_SHA256,
&issuer_spki.subject_public_key.data,
);
key.verify(ee_tbs_der, ee_signature_bytes)
.map_err(|e| CertPathError::EeSignatureInvalid(e.to_string()))
}
fn validate_ee_aki_matches_issuer_ski(
ee: &ResourceCertificate,
issuer_ca: &ResourceCertificate,
@@ -408,6 +476,16 @@ fn validate_ee_key_usage(cert: &X509Certificate<'_>) -> Result<(), CertPathError
Ok(())
}
fn validate_ee_key_usage_summary(summary: &EeKeyUsageSummary) -> Result<(), CertPathError> {
match summary {
EeKeyUsageSummary::DigitalSignatureOnly => Ok(()),
EeKeyUsageSummary::Missing => Err(CertPathError::KeyUsageMissing),
EeKeyUsageSummary::NotCritical => Err(CertPathError::KeyUsageNotCritical),
EeKeyUsageSummary::InvalidBits => Err(CertPathError::KeyUsageInvalidBits),
EeKeyUsageSummary::ParseError(err) => Err(CertPathError::KeyUsageParse(err.clone())),
}
}
fn time_within_validity(
t: time::OffsetDateTime,
not_before: time::OffsetDateTime,

View File

@@ -1,15 +1,15 @@
use crate::blob_store::RawObjectStore;
use crate::current_repo_index::CurrentRepoIndexHandle;
use crate::data_model::manifest::{ManifestDecodeError, ManifestObject, ManifestValidateError};
use crate::data_model::signed_object::SignedObjectVerifyError;
use crate::policy::{CaFailedFetchPolicy, Policy};
use crate::report::{RfcRef, Warning};
use crate::storage::{PackFile, PackTime, RocksStore, StorageError, VcirArtifactRole};
use crate::validation::cert_path::{CertPathError, validate_ee_cert_path};
use crate::validation::cert_path::{CertPathError, validate_signed_object_ee_cert_path_fast};
use crate::validation::publication_point::PublicationPointSnapshot;
use sha2::Digest;
use std::cmp::Ordering;
use std::collections::HashSet;
use x509_parser::prelude::FromDer;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum PublicationPointSource {
@@ -737,7 +737,10 @@ pub(crate) fn try_build_fresh_publication_point_with_timing(
timing.manifest_entries_ms = manifest_entries_started.elapsed().as_millis() as u64;
let mut files = Vec::with_capacity(manifest.manifest.file_count());
let pack_files_started = std::time::Instant::now();
let external_raw_store = store.external_raw_store_ref().cloned().map(std::sync::Arc::new);
let external_raw_store = store
.external_raw_store_ref()
.cloned()
.map(std::sync::Arc::new);
for entry in &entries {
let rsync_uri =
join_rsync_dir_and_file(publication_point_rsync_uri, entry.file_name.as_str());
@@ -768,7 +771,9 @@ pub(crate) fn try_build_fresh_publication_point_with_timing(
return Err(ManifestFreshError::HashMismatch { rsync_uri });
}
if let (Some(_), Some(raw_store)) = (current_index_guard.as_ref(), external_raw_store.as_ref()) {
if let (Some(_), Some(raw_store)) =
(current_index_guard.as_ref(), external_raw_store.as_ref())
{
files.push(PackFile::from_lazy_external_raw_store(
rsync_uri,
current_object.current_hash_hex,
@@ -855,7 +860,6 @@ fn validate_manifest_embedded_ee_cert_path(
validation_time: time::OffsetDateTime,
) -> Result<(), ManifestFreshError> {
let ee = &manifest.signed_object.signed_data.certificates[0];
let ee_der = ee.raw_der.as_slice();
let crl_files = files
.iter()
@@ -878,13 +882,31 @@ fn validate_manifest_embedded_ee_cert_path(
for u in crldp_uris {
let s = u.as_str();
if let Some(f) = crl_files.iter().find(|f| f.rsync_uri == s) {
let crl_bytes = f
.bytes()
.map_err(|e| ManifestFreshError::MissingFile { rsync_uri: format!("{s} ({e})") })?;
let _validated = validate_ee_cert_path(
ee_der,
issuer_ca_der,
crl_bytes,
let crl_bytes = f.bytes().map_err(|e| ManifestFreshError::MissingFile {
rsync_uri: format!("{s} ({e})"),
})?;
let issuer_ca = crate::data_model::rc::ResourceCertificate::decode_der(issuer_ca_der)
.map_err(CertPathError::IssuerDecode)?;
let (rem, issuer_spki) = x509_parser::x509::SubjectPublicKeyInfo::from_der(
&issuer_ca.tbs.subject_public_key_info,
)
.map_err(|e| CertPathError::IssuerSpkiParse(e.to_string()))?;
if !rem.is_empty() {
return Err(CertPathError::IssuerSpkiTrailingBytes(rem.len()).into());
}
let issuer_crl = crate::data_model::crl::RpkixCrl::decode_der(crl_bytes)
.map_err(CertPathError::from)?;
let revoked_serials = issuer_crl
.revoked_certs
.iter()
.map(|rc| rc.serial_number.bytes_be.clone())
.collect::<std::collections::HashSet<_>>();
validate_signed_object_ee_cert_path_fast(
ee,
&issuer_ca,
&issuer_spki,
&issuer_crl,
&revoked_serials,
issuer_ca_rsync_uri,
Some(f.rsync_uri.as_str()),
validation_time,
@@ -1418,7 +1440,10 @@ mod tests {
assert_eq!(point.manifest_bytes, manifest_bytes);
assert_eq!(point.files.len(), 1);
assert_eq!(point.files[0].rsync_uri, locked_uri);
assert_eq!(point.files[0].bytes_cloned().expect("locked bytes"), locked_bytes);
assert_eq!(
point.files[0].bytes_cloned().expect("locked bytes"),
locked_bytes
);
}
#[test]

View File

@@ -11,7 +11,7 @@ use crate::data_model::signed_object::SignedObjectVerifyError;
use crate::policy::{Policy, SignedObjectFailurePolicy};
use crate::report::{RfcRef, Warning};
use crate::storage::{PackFile, PackTime, VcirLocalOutput, VcirOutputType};
use crate::validation::cert_path::{CertPathError, validate_ee_cert_path_with_predecoded_ee};
use crate::validation::cert_path::{CertPathError, validate_signed_object_ee_cert_path_fast};
use crate::validation::manifest::PublicationPointData;
use crate::validation::publication_point::PublicationPointSnapshot;
use x509_parser::prelude::FromDer;
@@ -221,10 +221,7 @@ pub fn process_publication_point_for_issuer<P: PublicationPointData>(
let bytes = f
.bytes_cloned()
.expect("snapshot CRL bytes must be loadable");
(
f.rsync_uri.clone(),
CachedIssuerCrl::Pending(bytes),
)
(f.rsync_uri.clone(), CachedIssuerCrl::Pending(bytes))
})
.collect();
@@ -605,18 +602,21 @@ fn process_roa_with_issuer(
roa.signed_object.verify()?;
drop(_verify);
let ee = &roa.signed_object.signed_data.certificates[0].resource_cert;
let ee_der = &roa.signed_object.signed_data.certificates[0].raw_der;
let ee_crldp_uris = ee.tbs.extensions.crl_distribution_points_uris.as_ref();
let ee = &roa.signed_object.signed_data.certificates[0];
let ee_crldp_uris = ee
.resource_cert
.tbs
.extensions
.crl_distribution_points_uris
.as_ref();
let issuer_crl_rsync_uri = choose_crl_uri_for_certificate(ee_crldp_uris, crl_cache)?;
let verified_crl = ensure_issuer_crl_verified(issuer_crl_rsync_uri, crl_cache, issuer_ca_der)?;
let _cert_path = timing
.as_ref()
.map(|t| t.span_phase("objects_roa_validate_ee_cert_path_total"));
validate_ee_cert_path_with_predecoded_ee(
validate_signed_object_ee_cert_path_fast(
ee,
ee_der,
issuer_ca,
issuer_spki,
&verified_crl.crl,
@@ -631,7 +631,7 @@ fn process_roa_with_issuer(
.as_ref()
.map(|t| t.span_phase("objects_roa_validate_ee_resources_subset_total"));
validate_ee_resources_subset(
&ee,
&ee.resource_cert,
issuer_effective_ip,
issuer_effective_as,
issuer_resources_index,
@@ -641,7 +641,8 @@ fn process_roa_with_issuer(
let vrps = roa_to_vrps(&roa);
let source_object_hash = sha256_hex_from_32(&file.sha256);
let source_ee_cert_hash = crate::audit::sha256_hex(ee.raw_der.as_slice());
let item_effective_until = PackTime::from_utc_offset_datetime(ee.tbs.validity_not_after);
let item_effective_until =
PackTime::from_utc_offset_datetime(ee.resource_cert.tbs.validity_not_after);
let local_outputs = vrps
.iter()
.map(|vrp| {
@@ -713,18 +714,21 @@ fn process_aspa_with_issuer(
aspa.signed_object.verify()?;
drop(_verify);
let ee = &aspa.signed_object.signed_data.certificates[0].resource_cert;
let ee_der = &aspa.signed_object.signed_data.certificates[0].raw_der;
let ee_crldp_uris = ee.tbs.extensions.crl_distribution_points_uris.as_ref();
let ee = &aspa.signed_object.signed_data.certificates[0];
let ee_crldp_uris = ee
.resource_cert
.tbs
.extensions
.crl_distribution_points_uris
.as_ref();
let issuer_crl_rsync_uri = choose_crl_uri_for_certificate(ee_crldp_uris, crl_cache)?;
let verified_crl = ensure_issuer_crl_verified(issuer_crl_rsync_uri, crl_cache, issuer_ca_der)?;
let _cert_path = timing
.as_ref()
.map(|t| t.span_phase("objects_aspa_validate_ee_cert_path_total"));
validate_ee_cert_path_with_predecoded_ee(
validate_signed_object_ee_cert_path_fast(
ee,
ee_der,
issuer_ca,
issuer_spki,
&verified_crl.crl,
@@ -739,7 +743,7 @@ fn process_aspa_with_issuer(
.as_ref()
.map(|t| t.span_phase("objects_aspa_validate_ee_resources_subset_total"));
validate_ee_resources_subset(
&ee,
&ee.resource_cert,
issuer_effective_ip,
issuer_effective_as,
issuer_resources_index,
@@ -752,7 +756,8 @@ fn process_aspa_with_issuer(
};
let source_object_hash = sha256_hex_from_32(&file.sha256);
let source_ee_cert_hash = crate::audit::sha256_hex(ee.raw_der.as_slice());
let item_effective_until = PackTime::from_utc_offset_datetime(ee.tbs.validity_not_after);
let item_effective_until =
PackTime::from_utc_offset_datetime(ee.resource_cert.tbs.validity_not_after);
let providers = attestation
.provider_as_ids
.iter()

View File

@@ -823,10 +823,7 @@ fn discover_children_from_fresh_snapshot_with_audit<P: PublicationPointData>(
let bytes = f
.bytes_cloned()
.map_err(|e| format!("snapshot CRL bytes load failed: {e}"))?;
Ok((
f.rsync_uri.clone(),
CachedIssuerCrl::Pending(bytes),
))
Ok((f.rsync_uri.clone(), CachedIssuerCrl::Pending(bytes)))
})
.collect::<Result<_, String>>()?;
@@ -1480,7 +1477,11 @@ fn build_publication_point_audit_from_snapshot(
if !f.rsync_uri.ends_with(".crl") {
continue;
}
let ok = f.bytes().ok().and_then(|bytes| RpkixCrl::decode_der(bytes).ok()).is_some();
let ok = f
.bytes()
.ok()
.and_then(|bytes| RpkixCrl::decode_der(bytes).ok())
.is_some();
audit_by_uri.insert(
f.rsync_uri.clone(),
ObjectAuditEntry {
@@ -2477,7 +2478,7 @@ fn select_manifest_current_crl_from_snapshot(
file.bytes()
.map_err(|e| format!("load current CRL bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode current CRL for VCIR failed: {e}"))?;
.map_err(|e| format!("decode current CRL for VCIR failed: {e}"))?;
return Ok(CurrentCrlRef { file, crl });
}
}
@@ -2519,7 +2520,7 @@ fn build_vcir_local_outputs(
file.bytes()
.map_err(|e| format!("load accepted ROA bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode accepted ROA for VCIR failed: {e}"))?;
.map_err(|e| format!("decode accepted ROA for VCIR failed: {e}"))?;
let ee = &roa.signed_object.signed_data.certificates[0];
let source_ee_cert_hash = sha256_hex(ee.raw_der.as_slice());
let item_effective_until =
@@ -2561,7 +2562,7 @@ fn build_vcir_local_outputs(
file.bytes()
.map_err(|e| format!("load accepted ASPA bytes for VCIR failed: {e}"))?,
)
.map_err(|e| format!("decode accepted ASPA for VCIR failed: {e}"))?;
.map_err(|e| format!("decode accepted ASPA for VCIR failed: {e}"))?;
let ee = &aspa.signed_object.signed_data.certificates[0];
let source_ee_cert_hash = sha256_hex(ee.raw_der.as_slice());
let item_effective_until =
@@ -3610,8 +3611,7 @@ authorityKeyIdentifier = keyid:always
publication_point_rsync_uri: pack.publication_point_rsync_uri.clone(),
rrdp_notification_uri: None,
};
persist_vcir_non_repository_evidence(&store, &ca)
.expect("persist embedded evidence");
persist_vcir_non_repository_evidence(&store, &ca).expect("persist embedded evidence");
let issuer_hash = sha256_hex(&issuer_ca_der);
let issuer_entry = store
@@ -3901,7 +3901,8 @@ authorityKeyIdentifier = keyid:always
assert!(
!artifacts
.iter()
.any(|artifact| artifact.uri.is_none() && artifact.sha256 == sha256_hex(b"embedded-ee")),
.any(|artifact| artifact.uri.is_none()
&& artifact.sha256 == sha256_hex(b"embedded-ee")),
"embedded EE cert artifacts should no longer be persisted separately"
);
}