add delta sync and fail fetch process
This commit is contained in:
@@ -7,6 +7,8 @@ use crate::data_model::rc::{
|
||||
};
|
||||
use x509_parser::prelude::{FromDer, X509Certificate};
|
||||
|
||||
use crate::validation::x509_name::x509_names_equivalent;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct ValidatedSubordinateCa {
|
||||
pub child_ca: ResourceCertificate,
|
||||
@@ -135,7 +137,7 @@ pub fn validate_subordinate_ca_cert(
|
||||
return Err(CaPathError::IssuerNotCa);
|
||||
}
|
||||
|
||||
if child_ca.tbs.issuer_name != issuer_ca.tbs.subject_name {
|
||||
if !x509_names_equivalent(&child_ca.tbs.issuer_name, &issuer_ca.tbs.subject_name) {
|
||||
return Err(CaPathError::IssuerSubjectMismatch {
|
||||
child_issuer_dn: child_ca.tbs.issuer_name.to_string(),
|
||||
issuer_subject_dn: issuer_ca.tbs.subject_name.to_string(),
|
||||
@@ -697,6 +699,7 @@ mod tests {
|
||||
};
|
||||
use crate::data_model::common::X509NameDer;
|
||||
use der_parser::num_bigint::BigUint;
|
||||
use std::process::Command;
|
||||
fn dummy_cert(
|
||||
kind: ResourceCertKind,
|
||||
subject_dn: &str,
|
||||
@@ -736,6 +739,158 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
fn openssl_available() -> bool {
|
||||
Command::new("openssl")
|
||||
.arg("version")
|
||||
.output()
|
||||
.map(|o| o.status.success())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
fn write_cert_der_with_addext(dir: &std::path::Path, addext: Option<&str>) -> Vec<u8> {
|
||||
assert!(openssl_available(), "openssl is required for this test");
|
||||
let key = dir.join("k.pem");
|
||||
let cert = dir.join("c.pem");
|
||||
let der = dir.join("c.der");
|
||||
|
||||
let mut cmd = Command::new("openssl");
|
||||
cmd.arg("req")
|
||||
.arg("-x509")
|
||||
.arg("-newkey")
|
||||
.arg("rsa:2048")
|
||||
.arg("-nodes")
|
||||
.arg("-keyout")
|
||||
.arg(&key)
|
||||
.arg("-subj")
|
||||
.arg("/CN=ku")
|
||||
.arg("-days")
|
||||
.arg("1")
|
||||
.arg("-out")
|
||||
.arg(&cert);
|
||||
if let Some(ext) = addext {
|
||||
cmd.arg("-addext").arg(ext);
|
||||
}
|
||||
let out = cmd.output().expect("openssl req");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"openssl req failed: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
|
||||
let out = Command::new("openssl")
|
||||
.arg("x509")
|
||||
.arg("-in")
|
||||
.arg(&cert)
|
||||
.arg("-outform")
|
||||
.arg("DER")
|
||||
.arg("-out")
|
||||
.arg(&der)
|
||||
.output()
|
||||
.expect("openssl x509");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"openssl x509 failed: {}",
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
std::fs::read(&der).expect("read der")
|
||||
}
|
||||
|
||||
fn gen_issuer_and_child_der(dir: &std::path::Path) -> (Vec<u8>, Vec<u8>, Vec<u8>) {
|
||||
assert!(openssl_available(), "openssl is required for this test");
|
||||
let issuer_key = dir.join("issuer.key");
|
||||
let issuer_csr = dir.join("issuer.csr");
|
||||
let issuer_pem = dir.join("issuer.pem");
|
||||
let issuer_der = dir.join("issuer.der");
|
||||
|
||||
let child_key = dir.join("child.key");
|
||||
let child_csr = dir.join("child.csr");
|
||||
let child_pem = dir.join("child.pem");
|
||||
let child_der = dir.join("child.der");
|
||||
|
||||
let other_key = dir.join("other.key");
|
||||
let other_csr = dir.join("other.csr");
|
||||
let other_pem = dir.join("other.pem");
|
||||
let other_der = dir.join("other.der");
|
||||
|
||||
let run = |cmd: &mut Command| {
|
||||
let out = cmd.output().expect("run openssl");
|
||||
assert!(
|
||||
out.status.success(),
|
||||
"command failed: {:?}\nstderr={}",
|
||||
cmd,
|
||||
String::from_utf8_lossy(&out.stderr)
|
||||
);
|
||||
};
|
||||
|
||||
// Issuer self-signed.
|
||||
run(Command::new("openssl").args(["genrsa", "-out"]).arg(&issuer_key).arg("2048"));
|
||||
run(Command::new("openssl")
|
||||
.args(["req", "-new", "-key"])
|
||||
.arg(&issuer_key)
|
||||
.args(["-subj", "/CN=issuer", "-out"])
|
||||
.arg(&issuer_csr));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-req", "-in"])
|
||||
.arg(&issuer_csr)
|
||||
.args(["-signkey"])
|
||||
.arg(&issuer_key)
|
||||
.args(["-days", "1", "-out"])
|
||||
.arg(&issuer_pem));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-in"])
|
||||
.arg(&issuer_pem)
|
||||
.args(["-outform", "DER", "-out"])
|
||||
.arg(&issuer_der));
|
||||
|
||||
// Child signed by issuer.
|
||||
run(Command::new("openssl").args(["genrsa", "-out"]).arg(&child_key).arg("2048"));
|
||||
run(Command::new("openssl")
|
||||
.args(["req", "-new", "-key"])
|
||||
.arg(&child_key)
|
||||
.args(["-subj", "/CN=child", "-out"])
|
||||
.arg(&child_csr));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-req", "-in"])
|
||||
.arg(&child_csr)
|
||||
.args(["-CA"])
|
||||
.arg(&issuer_pem)
|
||||
.args(["-CAkey"])
|
||||
.arg(&issuer_key)
|
||||
.args(["-CAcreateserial", "-days", "1", "-out"])
|
||||
.arg(&child_pem));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-in"])
|
||||
.arg(&child_pem)
|
||||
.args(["-outform", "DER", "-out"])
|
||||
.arg(&child_der));
|
||||
|
||||
// Other self-signed issuer.
|
||||
run(Command::new("openssl").args(["genrsa", "-out"]).arg(&other_key).arg("2048"));
|
||||
run(Command::new("openssl")
|
||||
.args(["req", "-new", "-key"])
|
||||
.arg(&other_key)
|
||||
.args(["-subj", "/CN=other", "-out"])
|
||||
.arg(&other_csr));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-req", "-in"])
|
||||
.arg(&other_csr)
|
||||
.args(["-signkey"])
|
||||
.arg(&other_key)
|
||||
.args(["-days", "1", "-out"])
|
||||
.arg(&other_pem));
|
||||
run(Command::new("openssl")
|
||||
.args(["x509", "-in"])
|
||||
.arg(&other_pem)
|
||||
.args(["-outform", "DER", "-out"])
|
||||
.arg(&other_der));
|
||||
|
||||
(
|
||||
std::fs::read(&issuer_der).expect("read issuer der"),
|
||||
std::fs::read(&child_der).expect("read child der"),
|
||||
std::fs::read(&other_der).expect("read other der"),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_child_ip_resources_rejects_inherit_without_parent_effective_resources() {
|
||||
let child = IpResourceSet {
|
||||
@@ -953,6 +1108,142 @@ mod tests {
|
||||
validate_child_crldp_contains_issuer_crl_uri(&child, "rsync://example.test/issuer.crl")
|
||||
.expect("crldp ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_child_ca_key_usage_accepts_only_keycertsign_and_crlsign_critical() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let der = write_cert_der_with_addext(
|
||||
td.path(),
|
||||
Some("keyUsage = critical, keyCertSign, cRLSign"),
|
||||
);
|
||||
validate_child_ca_key_usage(&der).expect("key usage ok");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn validate_child_ca_key_usage_rejects_missing_noncritical_and_invalid_bits() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let missing = write_cert_der_with_addext(td.path(), None);
|
||||
let err = validate_child_ca_key_usage(&missing).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::KeyUsageMissing), "{err}");
|
||||
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let noncritical =
|
||||
write_cert_der_with_addext(td.path(), Some("keyUsage = keyCertSign, cRLSign"));
|
||||
let err = validate_child_ca_key_usage(&noncritical).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::KeyUsageNotCritical), "{err}");
|
||||
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let invalid = write_cert_der_with_addext(
|
||||
td.path(),
|
||||
Some("keyUsage = critical, keyCertSign, cRLSign, digitalSignature"),
|
||||
);
|
||||
let err = validate_child_ca_key_usage(&invalid).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::KeyUsageInvalidBits), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn verify_cert_signature_with_issuer_accepts_valid_chain_and_rejects_wrong_issuer() {
|
||||
let td = tempfile::tempdir().expect("tempdir");
|
||||
let (issuer, child, other) = gen_issuer_and_child_der(td.path());
|
||||
verify_cert_signature_with_issuer(&child, &issuer).expect("signature ok");
|
||||
let err = verify_cert_signature_with_issuer(&child, &other).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::ChildSignatureInvalid(_)), "{err}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn resolve_child_ip_and_as_resources_success_paths() {
|
||||
use crate::data_model::rc::{AsIdOrRange, IpAddressOrRange, IpPrefix};
|
||||
|
||||
let parent_ip = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
afi: Afi::Ipv4,
|
||||
choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix(
|
||||
IpPrefix {
|
||||
afi: Afi::Ipv4,
|
||||
prefix_len: 8,
|
||||
addr: vec![10, 0, 0, 0],
|
||||
},
|
||||
)]),
|
||||
}],
|
||||
};
|
||||
|
||||
let child_ip_inherit = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
afi: Afi::Ipv4,
|
||||
choice: IpAddressChoice::Inherit,
|
||||
}],
|
||||
};
|
||||
let eff = resolve_child_ip_resources(Some(&child_ip_inherit), Some(&parent_ip))
|
||||
.expect("inherit resolves")
|
||||
.expect("some ip");
|
||||
assert_eq!(eff.families.len(), 1);
|
||||
assert!(matches!(
|
||||
eff.families[0].choice,
|
||||
IpAddressChoice::AddressesOrRanges(_)
|
||||
));
|
||||
|
||||
let child_ip_subset = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
afi: Afi::Ipv4,
|
||||
choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix(
|
||||
IpPrefix {
|
||||
afi: Afi::Ipv4,
|
||||
prefix_len: 16,
|
||||
addr: vec![10, 1, 0, 0],
|
||||
},
|
||||
)]),
|
||||
}],
|
||||
};
|
||||
resolve_child_ip_resources(Some(&child_ip_subset), Some(&parent_ip))
|
||||
.expect("subset ok")
|
||||
.expect("some");
|
||||
|
||||
let child_ip_bad = IpResourceSet {
|
||||
families: vec![IpAddressFamily {
|
||||
afi: Afi::Ipv4,
|
||||
choice: IpAddressChoice::AddressesOrRanges(vec![IpAddressOrRange::Prefix(
|
||||
IpPrefix {
|
||||
afi: Afi::Ipv4,
|
||||
prefix_len: 16,
|
||||
addr: vec![11, 0, 0, 0],
|
||||
},
|
||||
)]),
|
||||
}],
|
||||
};
|
||||
let err = resolve_child_ip_resources(Some(&child_ip_bad), Some(&parent_ip)).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::ResourcesNotSubset), "{err}");
|
||||
|
||||
let parent_as = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Range {
|
||||
min: 1,
|
||||
max: 100,
|
||||
}])),
|
||||
rdi: None,
|
||||
};
|
||||
let child_as_inherit = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::Inherit),
|
||||
rdi: None,
|
||||
};
|
||||
let eff_as = resolve_child_as_resources(Some(&child_as_inherit), Some(&parent_as))
|
||||
.expect("inherit as")
|
||||
.expect("some");
|
||||
assert_eq!(eff_as.asnum, parent_as.asnum);
|
||||
|
||||
let child_as_subset = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(50)])),
|
||||
rdi: None,
|
||||
};
|
||||
resolve_child_as_resources(Some(&child_as_subset), Some(&parent_as))
|
||||
.expect("subset as")
|
||||
.expect("some");
|
||||
|
||||
let child_as_bad = AsResourceSet {
|
||||
asnum: Some(AsIdentifierChoice::AsIdsOrRanges(vec![AsIdOrRange::Id(200)])),
|
||||
rdi: None,
|
||||
};
|
||||
let err = resolve_child_as_resources(Some(&child_as_bad), Some(&parent_as)).unwrap_err();
|
||||
assert!(matches!(err, CaPathError::ResourcesNotSubset), "{err}");
|
||||
}
|
||||
}
|
||||
|
||||
fn increment_bytes(v: &[u8]) -> Vec<u8> {
|
||||
|
||||
Reference in New Issue
Block a user