138 lines
4.3 KiB
Rust
138 lines
4.3 KiB
Rust
// Certificate profile, signature, CRL, and validity checks.
|
|
|
|
fn parse_subject_pki_from_der(der: &[u8]) -> Result<SubjectPublicKeyInfo<'_>, CaPathError> {
|
|
let (rem, spki) = SubjectPublicKeyInfo::from_der(der)
|
|
.map_err(|e| CaPathError::IssuerSpkiParse(e.to_string()))?;
|
|
if !rem.is_empty() {
|
|
return Err(CaPathError::IssuerSpkiTrailingBytes(rem.len()));
|
|
}
|
|
Ok(spki)
|
|
}
|
|
|
|
fn parse_x509_cert(der: &[u8]) -> Result<X509Certificate<'_>, CaPathError> {
|
|
let (rem, cert) = X509Certificate::from_der(der)
|
|
.map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string()))?;
|
|
if !rem.is_empty() {
|
|
return Err(CaPathError::ChildSignatureInvalid(
|
|
"trailing bytes after child certificate".to_string(),
|
|
));
|
|
}
|
|
Ok(cert)
|
|
}
|
|
|
|
fn verify_child_signature(
|
|
child: &X509Certificate<'_>,
|
|
issuer_spki: &SubjectPublicKeyInfo<'_>,
|
|
) -> Result<(), CaPathError> {
|
|
child
|
|
.verify_signature(Some(issuer_spki))
|
|
.map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string()))
|
|
}
|
|
|
|
fn validate_child_aki_matches_issuer_ski(
|
|
child: &ResourceCertificate,
|
|
issuer: &ResourceCertificate,
|
|
) -> Result<(), CaPathError> {
|
|
let Some(issuer_ski) = issuer.tbs.extensions.subject_key_identifier.as_deref() else {
|
|
return Err(CaPathError::IssuerSkiMissing);
|
|
};
|
|
let Some(child_aki) = child.tbs.extensions.authority_key_identifier.as_deref() else {
|
|
return Err(CaPathError::ChildAkiMissing);
|
|
};
|
|
if child_aki != issuer_ski {
|
|
return Err(CaPathError::ChildAkiMismatch);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_child_aia_points_to_issuer_uri(
|
|
child: &ResourceCertificate,
|
|
issuer_ca_rsync_uri: &str,
|
|
) -> Result<(), CaPathError> {
|
|
let Some(uris) = child.tbs.extensions.ca_issuers_uris.as_ref() else {
|
|
return Err(CaPathError::ChildAiaMissing);
|
|
};
|
|
if !uris.iter().any(|u| u.as_str() == issuer_ca_rsync_uri) {
|
|
return Err(CaPathError::ChildAiaIssuerUriMismatch);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_child_crldp_contains_issuer_crl_uri(
|
|
child: &ResourceCertificate,
|
|
issuer_crl_rsync_uri: &str,
|
|
) -> Result<(), CaPathError> {
|
|
let Some(uris) = child.tbs.extensions.crl_distribution_points_uris.as_ref() else {
|
|
return Err(CaPathError::ChildCrlDpMissing);
|
|
};
|
|
if !uris.iter().any(|u| u.as_str() == issuer_crl_rsync_uri) {
|
|
return Err(CaPathError::ChildCrlDpUriMismatch);
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn validate_child_ca_key_usage(cert: &X509Certificate<'_>) -> Result<(), CaPathError> {
|
|
let mut ku_critical: Option<bool> = None;
|
|
for ext in cert.extensions() {
|
|
if ext.oid.as_bytes() == OID_KEY_USAGE_RAW {
|
|
ku_critical = Some(ext.critical);
|
|
break;
|
|
}
|
|
}
|
|
|
|
let Some(critical) = ku_critical else {
|
|
return Err(CaPathError::KeyUsageMissing);
|
|
};
|
|
if !critical {
|
|
return Err(CaPathError::KeyUsageNotCritical);
|
|
}
|
|
|
|
let Some(ku) = cert
|
|
.key_usage()
|
|
.map_err(|e| CaPathError::ChildSignatureInvalid(e.to_string()))?
|
|
else {
|
|
return Err(CaPathError::KeyUsageMissing);
|
|
};
|
|
|
|
let v = &ku.value;
|
|
let ok = v.key_cert_sign()
|
|
&& v.crl_sign()
|
|
&& !v.digital_signature()
|
|
&& !v.non_repudiation()
|
|
&& !v.key_encipherment()
|
|
&& !v.data_encipherment()
|
|
&& !v.key_agreement()
|
|
&& !v.encipher_only()
|
|
&& !v.decipher_only();
|
|
if !ok {
|
|
return Err(CaPathError::KeyUsageInvalidBits);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn time_within_validity(
|
|
t: time::OffsetDateTime,
|
|
not_before: time::OffsetDateTime,
|
|
not_after: time::OffsetDateTime,
|
|
) -> bool {
|
|
let t = t.to_offset(time::UtcOffset::UTC);
|
|
let not_before = not_before.to_offset(time::UtcOffset::UTC);
|
|
let not_after = not_after.to_offset(time::UtcOffset::UTC);
|
|
t >= not_before && t <= not_after
|
|
}
|
|
|
|
fn crl_valid_at_time(crl: &RpkixCrl, t: time::OffsetDateTime) -> bool {
|
|
let t = t.to_offset(time::UtcOffset::UTC);
|
|
let this_update = crl.this_update.utc.to_offset(time::UtcOffset::UTC);
|
|
let next_update = crl.next_update.utc.to_offset(time::UtcOffset::UTC);
|
|
t >= this_update && t < next_update
|
|
}
|
|
|
|
fn is_serial_revoked_by_crl(cert: &ResourceCertificate, crl: &RpkixCrl) -> bool {
|
|
let serial = BigUnsigned::from_biguint(&cert.tbs.serial_number);
|
|
crl.revoked_certs
|
|
.iter()
|
|
.any(|rc| rc.serial_number == serial)
|
|
}
|