576 lines
21 KiB
Rust
576 lines
21 KiB
Rust
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::{FetchCachePpKey, FetchCachePpPack, RocksStore, StorageError};
|
|
use crate::validation::cert_path::{CertPathError, validate_ee_cert_path};
|
|
use sha2::Digest;
|
|
use std::cmp::Ordering;
|
|
use std::collections::HashMap;
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum PublicationPointSource {
|
|
Fresh,
|
|
FetchCachePp,
|
|
}
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
pub struct PublicationPointResult {
|
|
pub source: PublicationPointSource,
|
|
pub pack: FetchCachePpPack,
|
|
pub warnings: Vec<Warning>,
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum ManifestFreshError {
|
|
#[error("repo sync failed: {detail} (RFC 8182 §3.4.5; RFC 9286 §6.6)")]
|
|
RepoSyncFailed { detail: String },
|
|
|
|
#[error(
|
|
"manifest not found in raw_objects: {manifest_rsync_uri} (RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
MissingManifest { manifest_rsync_uri: String },
|
|
|
|
#[error("manifest decode failed: {0} (RFC 9286 §4; RFC 9286 §6.2; RFC 9286 §6.6)")]
|
|
Decode(#[from] ManifestDecodeError),
|
|
|
|
#[error(
|
|
"manifest embedded EE certificate resources invalid: {0} (RFC 9286 §5.1; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
EeResources(#[from] ManifestValidateError),
|
|
|
|
#[error(
|
|
"manifest CMS signature verification failed: {0} (RFC 6488 §3; RFC 9589 §4; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
Signature(#[from] SignedObjectVerifyError),
|
|
|
|
#[error(
|
|
"manifest embedded EE certificate path validation failed: {0} (RFC 6488 §3; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
EeCertPath(#[from] CertPathError),
|
|
|
|
#[error(
|
|
"manifest embedded EE certificate CRLDistributionPoints missing (cannot validate EE certificate) (RFC 6487 §4.8.6; RFC 6488 §3; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
EeCrlDpMissing,
|
|
|
|
#[error(
|
|
"publication point contains no CRL files (cannot validate manifest EE certificate) (RFC 9286 §7; RFC 6487 §4.8.6; RFC 6488 §3; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
NoCrlFiles,
|
|
|
|
#[error(
|
|
"CRL referenced by manifest embedded EE certificate CRLDistributionPoints not found at publication point: {0} (RFC 6487 §4.8.6; RFC 9286 §4.2.1; RFC 9286 §6.2; RFC 9286 §6.6)"
|
|
)]
|
|
EeCrlNotFound(String),
|
|
|
|
#[error(
|
|
"manifest is not valid at validation_time: this_update={this_update_rfc3339_utc} next_update={next_update_rfc3339_utc} validation_time={validation_time_rfc3339_utc} (RFC 9286 §6.3; RFC 9286 §6.6)"
|
|
)]
|
|
StaleOrEarly {
|
|
this_update_rfc3339_utc: String,
|
|
next_update_rfc3339_utc: String,
|
|
validation_time_rfc3339_utc: String,
|
|
},
|
|
|
|
#[error(
|
|
"manifest must reside at the same publication point as id-ad-caRepository: manifest={manifest_rsync_uri} publication_point={publication_point_rsync_uri} (RFC 9286 §6.1; RFC 9286 §6.6)"
|
|
)]
|
|
ManifestOutsidePublicationPoint {
|
|
manifest_rsync_uri: String,
|
|
publication_point_rsync_uri: String,
|
|
},
|
|
|
|
#[error(
|
|
"manifestNumber not higher than previously validated manifest: old={old_hex} new={new_hex} (RFC 9286 §4.2.1; RFC 9286 §6.6)"
|
|
)]
|
|
ManifestNumberNotIncreasing { old_hex: String, new_hex: String },
|
|
|
|
#[error(
|
|
"thisUpdate not more recent than previously validated manifest: old={old_rfc3339_utc} new={new_rfc3339_utc} (RFC 9286 §4.2.1; RFC 9286 §6.6)"
|
|
)]
|
|
ThisUpdateNotIncreasing {
|
|
old_rfc3339_utc: String,
|
|
new_rfc3339_utc: String,
|
|
},
|
|
|
|
#[error(
|
|
"manifest referenced file missing in raw_objects: {rsync_uri} (RFC 9286 §6.4; RFC 9286 §6.6)"
|
|
)]
|
|
MissingFile { rsync_uri: String },
|
|
|
|
#[error("manifest file hash mismatch: {rsync_uri} (RFC 9286 §6.5; RFC 9286 §6.6)")]
|
|
HashMismatch { rsync_uri: String },
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum ManifestCachedError {
|
|
#[error("fetch_cache_pp entry missing: {0} (RFC 9286 §6.6)")]
|
|
MissingFetchCachePp(String),
|
|
|
|
#[error("fetch_cache_pp pack invalid: {0}")]
|
|
InvalidPack(#[from] StorageError),
|
|
|
|
#[error("cached manifest revalidation failed: {0}")]
|
|
CachedManifestFresh(#[from] ManifestFreshError),
|
|
|
|
#[error(
|
|
"cached fetch_cache_pp missing file referenced by manifest: {rsync_uri} (RFC 9286 §6.4; RFC 9286 §6.6)"
|
|
)]
|
|
CachedMissingFile { rsync_uri: String },
|
|
|
|
#[error("cached fetch_cache_pp file hash mismatch: {rsync_uri} (RFC 9286 §6.5; RFC 9286 §6.6)")]
|
|
CachedHashMismatch { rsync_uri: String },
|
|
}
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum ManifestProcessError {
|
|
#[error("manifest processing failed and cache use is disabled: {0}")]
|
|
StopAllOutput(#[from] ManifestFreshError),
|
|
|
|
#[error(
|
|
"manifest processing failed and no usable fetch_cache_pp is available: fresh={fresh}; cached={cached}"
|
|
)]
|
|
NoUsableCache {
|
|
fresh: ManifestFreshError,
|
|
cached: ManifestCachedError,
|
|
},
|
|
|
|
#[error("storage error during manifest processing: {0}")]
|
|
Storage(#[from] StorageError),
|
|
}
|
|
|
|
pub fn process_manifest_publication_point(
|
|
store: &RocksStore,
|
|
policy: &Policy,
|
|
manifest_rsync_uri: &str,
|
|
publication_point_rsync_uri: &str,
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
validation_time: time::OffsetDateTime,
|
|
) -> Result<PublicationPointResult, ManifestProcessError> {
|
|
process_manifest_publication_point_after_repo_sync(
|
|
store,
|
|
policy,
|
|
manifest_rsync_uri,
|
|
publication_point_rsync_uri,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
true,
|
|
None,
|
|
)
|
|
}
|
|
|
|
pub fn process_manifest_publication_point_after_repo_sync(
|
|
store: &RocksStore,
|
|
policy: &Policy,
|
|
manifest_rsync_uri: &str,
|
|
publication_point_rsync_uri: &str,
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
validation_time: time::OffsetDateTime,
|
|
repo_sync_ok: bool,
|
|
repo_sync_error: Option<&str>,
|
|
) -> Result<PublicationPointResult, ManifestProcessError> {
|
|
let fresh = if repo_sync_ok {
|
|
try_build_fresh_pack(
|
|
store,
|
|
manifest_rsync_uri,
|
|
publication_point_rsync_uri,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
)
|
|
} else {
|
|
Err(ManifestFreshError::RepoSyncFailed {
|
|
detail: repo_sync_error.unwrap_or("repo sync failed").to_string(),
|
|
})
|
|
};
|
|
|
|
match fresh {
|
|
Ok(pack) => {
|
|
let key = FetchCachePpKey::from_manifest_rsync_uri(manifest_rsync_uri);
|
|
let bytes = pack.encode()?;
|
|
store.put_fetch_cache_pp(&key, &bytes)?;
|
|
Ok(PublicationPointResult {
|
|
source: PublicationPointSource::Fresh,
|
|
pack,
|
|
warnings: Vec::new(),
|
|
})
|
|
}
|
|
Err(fresh_err) => match policy.ca_failed_fetch_policy {
|
|
CaFailedFetchPolicy::StopAllOutput => {
|
|
Err(ManifestProcessError::StopAllOutput(fresh_err))
|
|
}
|
|
CaFailedFetchPolicy::UseFetchCachePp => {
|
|
let mut warnings = vec![
|
|
Warning::new(format!("manifest failed fetch: {fresh_err}"))
|
|
.with_rfc_refs(&[RfcRef("RFC 9286 §6.6")])
|
|
.with_context(manifest_rsync_uri),
|
|
];
|
|
|
|
match load_and_revalidate_cached_pack(
|
|
store,
|
|
manifest_rsync_uri,
|
|
publication_point_rsync_uri,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
) {
|
|
Ok(pack) => {
|
|
warnings.push(
|
|
Warning::new("using fetch_cache_pp for publication point")
|
|
.with_rfc_refs(&[RfcRef("RFC 9286 §6.6")])
|
|
.with_context(manifest_rsync_uri),
|
|
);
|
|
Ok(PublicationPointResult {
|
|
source: PublicationPointSource::FetchCachePp,
|
|
pack,
|
|
warnings,
|
|
})
|
|
}
|
|
Err(cached_err) => Err(ManifestProcessError::NoUsableCache {
|
|
fresh: fresh_err,
|
|
cached: cached_err,
|
|
}),
|
|
}
|
|
}
|
|
},
|
|
}
|
|
}
|
|
|
|
pub fn load_and_revalidate_cached_pack(
|
|
store: &RocksStore,
|
|
manifest_rsync_uri: &str,
|
|
publication_point_rsync_uri: &str,
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
validation_time: time::OffsetDateTime,
|
|
) -> Result<FetchCachePpPack, ManifestCachedError> {
|
|
let key = FetchCachePpKey::from_manifest_rsync_uri(manifest_rsync_uri);
|
|
let bytes = store
|
|
.get_fetch_cache_pp(&key)?
|
|
.ok_or_else(|| ManifestCachedError::MissingFetchCachePp(key.as_str().to_string()))?;
|
|
let pack = FetchCachePpPack::decode(&bytes)?;
|
|
|
|
if pack.manifest_rsync_uri != manifest_rsync_uri {
|
|
return Err(ManifestCachedError::InvalidPack(StorageError::RocksDb(
|
|
"cached pack manifest_rsync_uri does not match key".to_string(),
|
|
)));
|
|
}
|
|
if pack.publication_point_rsync_uri != publication_point_rsync_uri {
|
|
return Err(ManifestCachedError::InvalidPack(StorageError::RocksDb(
|
|
"cached pack publication_point_rsync_uri does not match expected".to_string(),
|
|
)));
|
|
}
|
|
|
|
revalidate_cached_pack_with_current_time(
|
|
&pack,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
)?;
|
|
Ok(pack)
|
|
}
|
|
|
|
fn revalidate_cached_pack_with_current_time(
|
|
pack: &FetchCachePpPack,
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
validation_time: time::OffsetDateTime,
|
|
) -> Result<(), ManifestCachedError> {
|
|
// First, re-validate the cached manifest itself with the current time.
|
|
let manifest =
|
|
decode_and_validate_manifest_with_current_time(&pack.manifest_bytes, validation_time)
|
|
.map_err(ManifestCachedError::from)?;
|
|
|
|
// Then, re-bind the manifest fileList to the cached pack contents, as per RFC 9286 §6.4-§6.5.
|
|
let by_uri: HashMap<&str, &crate::storage::PackFile> = pack
|
|
.files
|
|
.iter()
|
|
.map(|f| (f.rsync_uri.as_str(), f))
|
|
.collect();
|
|
|
|
let entries = manifest
|
|
.manifest
|
|
.parse_files()
|
|
.map_err(|e| ManifestFreshError::Decode(ManifestDecodeError::Validate(e)))?;
|
|
for entry in &entries {
|
|
let rsync_uri =
|
|
join_rsync_dir_and_file(&pack.publication_point_rsync_uri, entry.file_name.as_str());
|
|
let Some(file) = by_uri.get(rsync_uri.as_str()) else {
|
|
return Err(ManifestCachedError::CachedMissingFile { rsync_uri });
|
|
};
|
|
if file.sha256.as_slice() != entry.hash_bytes.as_ref() {
|
|
return Err(ManifestCachedError::CachedHashMismatch { rsync_uri });
|
|
}
|
|
}
|
|
|
|
// Finally, validate the manifest's embedded EE certificate path against the issuer CA + CRL.
|
|
// This enforces cert validity + CRL validity at `validation_time` for cached packs.
|
|
validate_manifest_embedded_ee_cert_path(
|
|
&manifest,
|
|
&pack.files,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
)
|
|
.map_err(ManifestCachedError::from)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn decode_and_validate_manifest_with_current_time(
|
|
manifest_bytes: &[u8],
|
|
validation_time: time::OffsetDateTime,
|
|
) -> Result<ManifestObject, ManifestFreshError> {
|
|
let manifest = ManifestObject::decode_der(manifest_bytes)?;
|
|
manifest.validate_embedded_ee_cert()?;
|
|
manifest.signed_object.verify()?;
|
|
|
|
let this_update = manifest
|
|
.manifest
|
|
.this_update
|
|
.to_offset(time::UtcOffset::UTC);
|
|
let next_update = manifest
|
|
.manifest
|
|
.next_update
|
|
.to_offset(time::UtcOffset::UTC);
|
|
let now = validation_time.to_offset(time::UtcOffset::UTC);
|
|
if now < this_update || now > next_update {
|
|
return Err(ManifestFreshError::StaleOrEarly {
|
|
this_update_rfc3339_utc: this_update
|
|
.format(&time::format_description::well_known::Rfc3339)
|
|
.expect("format thisUpdate"),
|
|
next_update_rfc3339_utc: next_update
|
|
.format(&time::format_description::well_known::Rfc3339)
|
|
.expect("format nextUpdate"),
|
|
validation_time_rfc3339_utc: now
|
|
.format(&time::format_description::well_known::Rfc3339)
|
|
.expect("format validation_time"),
|
|
});
|
|
}
|
|
|
|
Ok(manifest)
|
|
}
|
|
|
|
fn try_build_fresh_pack(
|
|
store: &RocksStore,
|
|
manifest_rsync_uri: &str,
|
|
publication_point_rsync_uri: &str,
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
validation_time: time::OffsetDateTime,
|
|
) -> Result<FetchCachePpPack, ManifestFreshError> {
|
|
if !rsync_uri_is_under_publication_point(manifest_rsync_uri, publication_point_rsync_uri) {
|
|
return Err(ManifestFreshError::ManifestOutsidePublicationPoint {
|
|
manifest_rsync_uri: manifest_rsync_uri.to_string(),
|
|
publication_point_rsync_uri: publication_point_rsync_uri.to_string(),
|
|
});
|
|
}
|
|
|
|
let manifest_bytes = store
|
|
.get_raw(manifest_rsync_uri)
|
|
.map_err(|e| ManifestFreshError::MissingManifest {
|
|
manifest_rsync_uri: format!("{manifest_rsync_uri} ({e})"),
|
|
})?
|
|
.ok_or_else(|| ManifestFreshError::MissingManifest {
|
|
manifest_rsync_uri: manifest_rsync_uri.to_string(),
|
|
})?;
|
|
|
|
let manifest =
|
|
decode_and_validate_manifest_with_current_time(&manifest_bytes, validation_time)?;
|
|
|
|
let this_update = manifest
|
|
.manifest
|
|
.this_update
|
|
.to_offset(time::UtcOffset::UTC);
|
|
let next_update = manifest
|
|
.manifest
|
|
.next_update
|
|
.to_offset(time::UtcOffset::UTC);
|
|
let now = validation_time.to_offset(time::UtcOffset::UTC);
|
|
|
|
// RFC 9286 §4.2.1: replay/rollback detection for manifestNumber and thisUpdate.
|
|
//
|
|
// Important nuance for revalidation across runs:
|
|
// - If the manifestNumber is equal to the previously validated manifestNumber *and* the
|
|
// manifest bytes are identical, then this is the same manifest being revalidated and MUST
|
|
// be accepted (otherwise, RPs would incorrectly treat stable repositories as "failed fetch"
|
|
// and fall back to fetch_cache_pp).
|
|
// - If manifestNumber is equal but the manifest bytes differ, treat this as invalid (a
|
|
// repository is not allowed to change the manifest while keeping the manifestNumber).
|
|
// - If manifestNumber is lower, treat as rollback and reject.
|
|
// - If manifestNumber is higher, require thisUpdate to be more recent than the previously
|
|
// validated thisUpdate.
|
|
let key = FetchCachePpKey::from_manifest_rsync_uri(manifest_rsync_uri);
|
|
if let Some(old_bytes) = store.get_fetch_cache_pp(&key).ok().flatten() {
|
|
if let Ok(old_pack) = FetchCachePpPack::decode(&old_bytes) {
|
|
if old_pack.manifest_rsync_uri == manifest_rsync_uri
|
|
&& old_pack.publication_point_rsync_uri == publication_point_rsync_uri
|
|
{
|
|
let new_num = manifest.manifest.manifest_number.bytes_be.as_slice();
|
|
let old_num = old_pack.manifest_number_be.as_slice();
|
|
match cmp_minimal_be_unsigned(new_num, old_num) {
|
|
Ordering::Greater => {
|
|
let old_this_update = old_pack
|
|
.this_update
|
|
.parse()
|
|
.expect("pack internal validation ensures this_update parses");
|
|
if this_update <= old_this_update {
|
|
use time::format_description::well_known::Rfc3339;
|
|
return Err(ManifestFreshError::ThisUpdateNotIncreasing {
|
|
old_rfc3339_utc: old_this_update
|
|
.to_offset(time::UtcOffset::UTC)
|
|
.format(&Rfc3339)
|
|
.expect("format old thisUpdate"),
|
|
new_rfc3339_utc: this_update
|
|
.format(&Rfc3339)
|
|
.expect("format new thisUpdate"),
|
|
});
|
|
}
|
|
}
|
|
Ordering::Equal => {
|
|
if old_pack.manifest_bytes != manifest_bytes {
|
|
return Err(ManifestFreshError::ManifestNumberNotIncreasing {
|
|
old_hex: hex::encode_upper(old_num),
|
|
new_hex: hex::encode_upper(new_num),
|
|
});
|
|
}
|
|
}
|
|
Ordering::Less => {
|
|
return Err(ManifestFreshError::ManifestNumberNotIncreasing {
|
|
old_hex: hex::encode_upper(old_num),
|
|
new_hex: hex::encode_upper(new_num),
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
let entries = manifest
|
|
.manifest
|
|
.parse_files()
|
|
.map_err(ManifestDecodeError::Validate)?;
|
|
let mut files = Vec::with_capacity(manifest.manifest.file_count());
|
|
for entry in &entries {
|
|
let rsync_uri =
|
|
join_rsync_dir_and_file(publication_point_rsync_uri, entry.file_name.as_str());
|
|
let bytes = store
|
|
.get_raw(&rsync_uri)
|
|
.map_err(|_e| ManifestFreshError::MissingFile {
|
|
rsync_uri: rsync_uri.clone(),
|
|
})?
|
|
.ok_or_else(|| ManifestFreshError::MissingFile {
|
|
rsync_uri: rsync_uri.clone(),
|
|
})?;
|
|
|
|
let computed = sha2::Sha256::digest(&bytes);
|
|
if computed.as_slice() != entry.hash_bytes.as_ref() {
|
|
return Err(ManifestFreshError::HashMismatch { rsync_uri });
|
|
}
|
|
|
|
files.push(crate::storage::PackFile::from_bytes_compute_sha256(
|
|
rsync_uri, bytes,
|
|
));
|
|
}
|
|
|
|
// RFC 6488 §3: manifest (signed object) validity includes a valid EE cert path.
|
|
// We validate this after §6.4/§6.5 so the issuer CRL can be selected from the publication point.
|
|
validate_manifest_embedded_ee_cert_path(
|
|
&manifest,
|
|
&files,
|
|
issuer_ca_der,
|
|
issuer_ca_rsync_uri,
|
|
validation_time,
|
|
)?;
|
|
|
|
Ok(FetchCachePpPack {
|
|
format_version: FetchCachePpPack::FORMAT_VERSION_V1,
|
|
manifest_rsync_uri: manifest_rsync_uri.to_string(),
|
|
publication_point_rsync_uri: publication_point_rsync_uri.to_string(),
|
|
manifest_number_be: manifest.manifest.manifest_number.bytes_be.clone(),
|
|
this_update: crate::storage::PackTime::from_utc_offset_datetime(this_update),
|
|
next_update: crate::storage::PackTime::from_utc_offset_datetime(next_update),
|
|
verified_at: crate::storage::PackTime::from_utc_offset_datetime(now),
|
|
manifest_bytes,
|
|
files,
|
|
})
|
|
}
|
|
|
|
fn cmp_minimal_be_unsigned(a: &[u8], b: &[u8]) -> Ordering {
|
|
// Compare two minimal big-endian byte strings as unsigned integers.
|
|
// (Leading zeros are not expected; callers store minimal big-endian.)
|
|
a.len().cmp(&b.len()).then_with(|| a.cmp(b))
|
|
}
|
|
|
|
fn join_rsync_dir_and_file(base: &str, file_name: &str) -> String {
|
|
if base.ends_with('/') {
|
|
format!("{base}{file_name}")
|
|
} else {
|
|
format!("{base}/{file_name}")
|
|
}
|
|
}
|
|
|
|
fn rsync_uri_is_under_publication_point(uri: &str, publication_point_rsync_uri: &str) -> bool {
|
|
let pp = if publication_point_rsync_uri.ends_with('/') {
|
|
publication_point_rsync_uri.to_string()
|
|
} else {
|
|
format!("{publication_point_rsync_uri}/")
|
|
};
|
|
uri.starts_with(&pp)
|
|
}
|
|
|
|
fn validate_manifest_embedded_ee_cert_path(
|
|
manifest: &ManifestObject,
|
|
files: &[crate::storage::PackFile],
|
|
issuer_ca_der: &[u8],
|
|
issuer_ca_rsync_uri: Option<&str>,
|
|
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()
|
|
.filter(|f| f.rsync_uri.ends_with(".crl"))
|
|
.collect::<Vec<_>>();
|
|
if crl_files.is_empty() {
|
|
return Err(ManifestFreshError::NoCrlFiles);
|
|
}
|
|
|
|
let Some(crldp_uris) = ee
|
|
.resource_cert
|
|
.tbs
|
|
.extensions
|
|
.crl_distribution_points_uris
|
|
.as_ref()
|
|
else {
|
|
return Err(ManifestFreshError::EeCrlDpMissing);
|
|
};
|
|
|
|
for u in crldp_uris {
|
|
let s = u.as_str();
|
|
if let Some(f) = crl_files.iter().find(|f| f.rsync_uri == s) {
|
|
let _validated = validate_ee_cert_path(
|
|
ee_der,
|
|
issuer_ca_der,
|
|
f.bytes.as_slice(),
|
|
issuer_ca_rsync_uri,
|
|
Some(f.rsync_uri.as_str()),
|
|
validation_time,
|
|
)?;
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
Err(ManifestFreshError::EeCrlNotFound(
|
|
crldp_uris
|
|
.iter()
|
|
.map(|u| u.as_str())
|
|
.collect::<Vec<_>>()
|
|
.join(", "),
|
|
))
|
|
}
|