20260418 优化去掉冗余存储repo object,mix quick最快170s
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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"
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user