blob: 108eeb5274c996c33fa1e3ebc9079fced4350263 [file] [log] [blame] [edit]
//! Certificate tests
use const_oid::AssociatedOid;
use der::asn1::{Ia5StringRef, OctetString, PrintableStringRef, Utf8StringRef};
use der::{Decode, Encode, ErrorKind, Length, Tag, Tagged};
use hex_literal::hex;
use x509_cert::ext::pkix::crl::dp::{DistributionPoint, ReasonFlags, Reasons};
use x509_cert::ext::pkix::name::{DistributionPointName, GeneralName, GeneralNames};
use x509_cert::ext::pkix::*;
use x509_cert::ext::Extensions;
use x509_cert::name::Name;
use x509_cert::{serial_number::SerialNumber, Certificate, Version};
use const_oid::db::rfc5280::*;
use const_oid::db::rfc5912::ID_CE_CERTIFICATE_POLICIES;
fn spin_over_exts(exts: Extensions) {
for ext in exts {
match ext.extn_id {
SubjectDirectoryAttributes::OID => {
let decoded =
SubjectDirectoryAttributes::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
SubjectKeyIdentifier::OID => {
let decoded = SubjectKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
KeyUsage::OID => {
let decoded = KeyUsage::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
PrivateKeyUsagePeriod::OID => {
let decoded = PrivateKeyUsagePeriod::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
SubjectAltName::OID => {
let decoded = SubjectAltName::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
IssuerAltName::OID => {
let decoded = IssuerAltName::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
BasicConstraints::OID => {
let decoded = BasicConstraints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
NameConstraints::OID => {
let decoded = NameConstraints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
CrlDistributionPoints::OID => {
let decoded = CrlDistributionPoints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
CertificatePolicies::OID => {
let decoded = CertificatePolicies::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
PolicyMappings::OID => {
let decoded = PolicyMappings::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
AuthorityKeyIdentifier::OID => {
let decoded = AuthorityKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
PolicyConstraints::OID => {
let decoded = PolicyConstraints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
ExtendedKeyUsage::OID => {
let decoded = ExtendedKeyUsage::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
FreshestCrl::OID => {
let decoded = FreshestCrl::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
InhibitAnyPolicy::OID => {
let decoded = InhibitAnyPolicy::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
AuthorityInfoAccessSyntax::OID => {
let decoded =
AuthorityInfoAccessSyntax::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
SubjectInfoAccessSyntax::OID => {
let decoded = SubjectInfoAccessSyntax::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
ext.extn_value,
decoded.to_der().and_then(OctetString::new).unwrap()
);
}
_ => {
eprintln!("ignoring {} with criticality {}", ext.extn_id, ext.critical);
}
}
}
}
#[test]
fn decode_general_name() {
// DnsName
let dns_name = GeneralName::from_der(&hex!("820C616D617A6F6E2E636F2E756B")[..]).unwrap();
match dns_name {
GeneralName::DnsName(dns_name) => assert_eq!(dns_name.to_string(), "amazon.co.uk"),
_ => panic!("Failed to parse DnsName from GeneralName"),
}
// Rfc822Name
let rfc822 = GeneralName::from_der(
&hex!("811B456D61696C5F38303837323037343440746D612E6F73642E6D696C")[..],
)
.unwrap();
match rfc822 {
GeneralName::Rfc822Name(rfc822) => {
assert_eq!(rfc822.to_string(), "[email protected]")
}
_ => panic!("Failed to parse Rfc822Name from GeneralName"),
}
// OtherName
let bytes = hex!("A021060A2B060104018237140203A0130C1155706E5F323134393530313330406D696C");
match GeneralName::from_der(&bytes).unwrap() {
GeneralName::OtherName(other_name) => {
let onval = Utf8StringRef::try_from(&other_name.value).unwrap();
assert_eq!(onval.to_string(), "Upn_214950130@mil");
}
_ => panic!("Failed to parse OtherName from GeneralName"),
}
}
#[test]
fn decode_cert() {
// cloned cert with variety of interesting bits, including subject DN encoded backwards, large
// policy mapping set, large policy set (including one with qualifiers), fairly typical set of
// extensions otherwise
let der_encoded_cert =
include_bytes!("examples/026EDA6FA1EDFA8C253936C75B5EEBD954BFF452.fake.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
let i = exts.iter();
let mut counter = 0;
for ext in i {
if 0 == counter {
assert_eq!(ext.extn_id.to_string(), ID_CE_KEY_USAGE.to_string());
assert_eq!(ext.critical, true);
let ku = KeyUsage::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(KeyUsages::KeyCertSign | KeyUsages::CRLSign, ku);
let reencoded = ku.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
} else if 1 == counter {
assert_eq!(ext.extn_id.to_string(), ID_CE_BASIC_CONSTRAINTS.to_string());
assert_eq!(ext.critical, true);
let bc = BasicConstraints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(true, bc.ca);
assert!(bc.path_len_constraint.is_none());
let reencoded = bc.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
} else if 2 == counter {
assert_eq!(ext.extn_id.to_string(), ID_CE_POLICY_MAPPINGS.to_string());
assert_eq!(ext.critical, false);
let pm = PolicyMappings::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(19, pm.0.len());
let reencoded = pm.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
let subject_domain_policy: [&str; 19] = [
"2.16.840.1.101.3.2.1.48.2",
"2.16.840.1.101.3.2.1.48.2",
"2.16.840.1.101.3.2.1.48.3",
"2.16.840.1.101.3.2.1.48.3",
"2.16.840.1.101.3.2.1.48.5",
"2.16.840.1.101.3.2.1.48.5",
"2.16.840.1.101.3.2.1.48.4",
"2.16.840.1.101.3.2.1.48.4",
"2.16.840.1.101.3.2.1.48.6",
"2.16.840.1.101.3.2.1.48.6",
"2.16.840.1.101.3.2.1.48.78",
"2.16.840.1.101.3.2.1.48.78",
"2.16.840.1.101.3.2.1.48.78",
"2.16.840.1.101.3.2.1.48.79",
"2.16.840.1.101.3.2.1.48.80",
"2.16.840.1.101.3.2.1.48.99",
"2.16.840.1.101.3.2.1.48.99",
"2.16.840.1.101.3.2.1.48.100",
"2.16.840.1.101.3.2.1.48.100",
];
let issuer_domain_policy: [&str; 19] = [
"2.16.840.1.113839.0.100.2.1",
"2.16.840.1.113839.0.100.2.2",
"2.16.840.1.113839.0.100.3.1",
"2.16.840.1.113839.0.100.3.2",
"2.16.840.1.113839.0.100.14.1",
"2.16.840.1.113839.0.100.14.2",
"2.16.840.1.113839.0.100.12.1",
"2.16.840.1.113839.0.100.12.2",
"2.16.840.1.113839.0.100.15.1",
"2.16.840.1.113839.0.100.15.2",
"2.16.840.1.113839.0.100.18.0",
"2.16.840.1.113839.0.100.18.1",
"2.16.840.1.113839.0.100.18.2",
"2.16.840.1.113839.0.100.19.1",
"2.16.840.1.113839.0.100.20.1",
"2.16.840.1.113839.0.100.37.1",
"2.16.840.1.113839.0.100.37.2",
"2.16.840.1.113839.0.100.38.1",
"2.16.840.1.113839.0.100.38.2",
];
let mut counter_pm = 0;
for mapping in pm.0 {
assert_eq!(
issuer_domain_policy[counter_pm],
mapping.issuer_domain_policy.to_string()
);
assert_eq!(
subject_domain_policy[counter_pm],
mapping.subject_domain_policy.to_string()
);
counter_pm += 1;
}
} else if 3 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_CERTIFICATE_POLICIES.to_string()
);
assert_eq!(ext.critical, false);
let cps = CertificatePolicies::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(19, cps.0.len());
let reencoded = cps.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
let ids: [&str; 19] = [
"2.16.840.1.113839.0.100.2.1",
"2.16.840.1.113839.0.100.2.2",
"2.16.840.1.113839.0.100.3.1",
"2.16.840.1.113839.0.100.3.2",
"2.16.840.1.113839.0.100.14.1",
"2.16.840.1.113839.0.100.14.2",
"2.16.840.1.113839.0.100.12.1",
"2.16.840.1.113839.0.100.12.2",
"2.16.840.1.113839.0.100.15.1",
"2.16.840.1.113839.0.100.15.2",
"2.16.840.1.113839.0.100.18.0",
"2.16.840.1.113839.0.100.18.1",
"2.16.840.1.113839.0.100.18.2",
"2.16.840.1.113839.0.100.19.1",
"2.16.840.1.113839.0.100.20.1",
"2.16.840.1.113839.0.100.37.1",
"2.16.840.1.113839.0.100.37.2",
"2.16.840.1.113839.0.100.38.1",
"2.16.840.1.113839.0.100.38.2",
];
let mut cp_counter = 0;
for cp in cps.0 {
assert_eq!(ids[cp_counter], cp.policy_identifier.to_string());
if 18 == cp_counter {
assert!(cp.policy_qualifiers.is_some());
let pq = cp.policy_qualifiers.unwrap();
let mut counter_pq = 0;
for pqi in pq.iter() {
if 0 == counter_pq {
assert_eq!("1.3.6.1.5.5.7.2.1", pqi.policy_qualifier_id.to_string());
let cpsval =
Ia5StringRef::try_from(pqi.qualifier.as_ref().unwrap()).unwrap();
assert_eq!(
"https://secure.identrust.com/certificates/policy/IGC/index.html",
cpsval.to_string()
);
} else if 1 == counter_pq {
assert_eq!("1.3.6.1.5.5.7.2.2", pqi.policy_qualifier_id.to_string());
// TODO VisibleString
}
counter_pq += 1;
}
} else {
assert!(cp.policy_qualifiers.is_none())
}
cp_counter += 1;
}
} else if 4 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_SUBJECT_KEY_IDENTIFIER.to_string()
);
assert_eq!(ext.critical, false);
let skid = SubjectKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(Length::new(21), skid.0.len());
assert_eq!(
&hex!("DBD3DEBF0D7B615B32803BC0206CD7AADD39B8ACFF"),
skid.0.as_bytes()
);
let reencoded = skid.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
} else if 5 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_CRL_DISTRIBUTION_POINTS.to_string()
);
assert_eq!(ext.critical, false);
let crl_dps = CrlDistributionPoints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(2, crl_dps.0.len());
let reencoded = crl_dps.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
let mut crldp_counter = 0;
for crldp in crl_dps.0 {
let dpn = crldp.distribution_point.unwrap();
if 0 == crldp_counter {
match dpn {
DistributionPointName::FullName(gns) => {
assert_eq!(1, gns.len());
let gn = gns.get(0).unwrap();
match gn {
GeneralName::UniformResourceIdentifier(uri) => {
assert_eq!(
"http://crl-pte.identrust.com.test/crl/IGCRootca1.crl",
uri.to_string()
);
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
}
_ => {
panic!("Expected FullName");
}
}
} else if 1 == crldp_counter {
match dpn {
DistributionPointName::FullName(gns) => {
assert_eq!(1, gns.len());
let gn = gns.get(0).unwrap();
match gn {
GeneralName::UniformResourceIdentifier(uri) => {
assert_eq!("ldap://ldap-pte.identrust.com.test/cn%3DIGC%20Root%20CA1%2Co%3DIdenTrust%2Cc%3DUS%3FcertificateRevocationList%3Bbinary", uri.to_string());
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
}
crldp_counter += 1;
}
} else if 6 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_PE_SUBJECT_INFO_ACCESS.to_string()
);
assert_eq!(ext.critical, false);
let sias = SubjectInfoAccessSyntax::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(1, sias.0.len());
let reencoded = sias.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
for sia in sias.0 {
assert_eq!("1.3.6.1.5.5.7.48.5", sia.access_method.to_string());
let gn = sia.access_location;
match gn {
GeneralName::UniformResourceIdentifier(gn) => {
assert_eq!(
"http://http.cite.fpki-lab.gov.test/bridge/caCertsIssuedBytestFBCA.p7c",
gn.to_string()
);
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
}
} else if 7 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_PE_AUTHORITY_INFO_ACCESS.to_string()
);
assert_eq!(ext.critical, false);
let aias = AuthorityInfoAccessSyntax::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(2, aias.0.len());
let mut aia_counter = 0;
let reencoded = aias.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
for aia in aias.0 {
if 0 == aia_counter {
assert_eq!("1.3.6.1.5.5.7.48.2", aia.access_method.to_string());
let gn = aia.access_location;
match gn {
GeneralName::UniformResourceIdentifier(gn) => {
assert_eq!(
"http://apps-stg.identrust.com.test/roots/IGCRootca1.p7c",
gn.to_string()
);
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
} else if 1 == aia_counter {
assert_eq!("1.3.6.1.5.5.7.48.1", aia.access_method.to_string());
let gn = aia.access_location;
match gn {
GeneralName::UniformResourceIdentifier(gn) => {
assert_eq!(
"http://igcrootpte.ocsp.identrust.com.test:8125",
gn.to_string()
);
}
_ => {
panic!("Expected UniformResourceIdentifier");
}
}
}
aia_counter += 1;
}
} else if 8 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_INHIBIT_ANY_POLICY.to_string()
);
assert_eq!(ext.critical, false);
let iap = InhibitAnyPolicy::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(0, iap.0);
let reencoded = iap.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
} else if 9 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_AUTHORITY_KEY_IDENTIFIER.to_string()
);
assert_eq!(ext.critical, false);
let akid = AuthorityKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
&hex!("7C4C863AB80BD589870BEDB7E11BBD2A08BB3D23FF"),
akid.key_identifier.as_ref().unwrap().as_bytes()
);
let reencoded = akid.to_der().and_then(OctetString::new).unwrap();
assert_eq!(ext.extn_value, reencoded);
}
counter += 1;
}
let der_encoded_cert = include_bytes!("examples/GoodCACert.crt");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
assert_eq!(cert.tbs_certificate.version, Version::V3);
let target_serial: [u8; 1] = [2];
assert_eq!(
cert.tbs_certificate.serial_number,
SerialNumber::new(&target_serial).unwrap()
);
assert_eq!(
cert.tbs_certificate.signature.oid.to_string(),
"1.2.840.113549.1.1.11"
);
assert_eq!(
cert.tbs_certificate
.signature
.parameters
.as_ref()
.unwrap()
.tag(),
Tag::Null
);
assert_eq!(
cert.tbs_certificate
.signature
.parameters
.as_ref()
.unwrap()
.is_null(),
true
);
let mut counter = 0;
let i = cert.tbs_certificate.issuer.0.iter();
for rdn in i {
let i1 = rdn.0.iter();
for atav in i1 {
if 0 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.6");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"US"
);
} else if 1 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.10");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"Test Certificates 2011"
);
} else if 2 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.3");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"Trust Anchor"
);
}
counter += 1;
}
}
assert_eq!(
cert.tbs_certificate
.validity
.not_before
.to_unix_duration()
.as_secs(),
1262334600
);
assert_eq!(
cert.tbs_certificate
.validity
.not_after
.to_unix_duration()
.as_secs(),
1924936200
);
counter = 0;
let i = cert.tbs_certificate.subject.0.iter();
for rdn in i {
let i1 = rdn.0.iter();
for atav in i1 {
if 0 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.6");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"US"
);
} else if 1 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.10");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"Test Certificates 2011"
);
} else if 2 == counter {
assert_eq!(atav.oid.to_string(), "2.5.4.3");
assert_eq!(
PrintableStringRef::try_from(&atav.value)
.unwrap()
.to_string(),
"Good CA"
);
}
counter += 1;
}
}
assert_eq!(
cert.tbs_certificate
.subject_public_key_info
.algorithm
.oid
.to_string(),
"1.2.840.113549.1.1.1"
);
assert_eq!(
cert.tbs_certificate
.subject_public_key_info
.algorithm
.parameters
.as_ref()
.unwrap()
.tag(),
Tag::Null
);
assert_eq!(
cert.tbs_certificate
.subject_public_key_info
.algorithm
.parameters
.as_ref()
.unwrap()
.is_null(),
true
);
// TODO - parse and compare public key
counter = 0;
let exts = cert.tbs_certificate.extensions.unwrap();
let i = exts.iter();
for ext in i {
if 0 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_AUTHORITY_KEY_IDENTIFIER.to_string()
);
assert_eq!(ext.critical, false);
let akid = AuthorityKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
akid.key_identifier.unwrap().as_bytes(),
&hex!("E47D5FD15C9586082C05AEBE75B665A7D95DA866")[..]
);
} else if 1 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_SUBJECT_KEY_IDENTIFIER.to_string()
);
assert_eq!(ext.critical, false);
let skid = SubjectKeyIdentifier::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(
skid.0.as_bytes(),
&hex!("580184241BBC2B52944A3DA510721451F5AF3AC9")[..]
);
} else if 2 == counter {
assert_eq!(ext.extn_id.to_string(), ID_CE_KEY_USAGE.to_string());
assert_eq!(ext.critical, true);
let ku = KeyUsage::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(KeyUsages::KeyCertSign | KeyUsages::CRLSign, ku);
} else if 3 == counter {
assert_eq!(
ext.extn_id.to_string(),
ID_CE_CERTIFICATE_POLICIES.to_string()
);
assert_eq!(ext.critical, false);
let r = CertificatePolicies::from_der(ext.extn_value.as_bytes());
let cp = r.unwrap();
let i = cp.0.iter();
for p in i {
assert_eq!(p.policy_identifier.to_string(), "2.16.840.1.101.3.2.1.48.1");
}
} else if 4 == counter {
assert_eq!(ext.extn_id.to_string(), ID_CE_BASIC_CONSTRAINTS.to_string());
assert_eq!(ext.critical, true);
let bc = BasicConstraints::from_der(ext.extn_value.as_bytes()).unwrap();
assert_eq!(bc.ca, true);
assert_eq!(bc.path_len_constraint, Option::None);
}
counter += 1;
}
assert_eq!(
cert.signature_algorithm.oid.to_string(),
"1.2.840.113549.1.1.11"
);
assert_eq!(
cert.signature_algorithm.parameters.as_ref().unwrap().tag(),
Tag::Null
);
assert_eq!(cert.signature_algorithm.parameters.unwrap().is_null(), true);
// TODO - parse and compare signature value
// This cert adds extended key usage and netscape cert type vs above samples
let der_encoded_cert = include_bytes!("examples/0954e2343dd5efe0a7f0967d69caf33e5f893720.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds extended key usage and name constraints vs above samples
let der_encoded_cert = include_bytes!("examples/0fcc78fbbca9f32b08b19b032b84f2c86a128f35.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds logotype (which is unrecognized) vs above samples
let der_encoded_cert = include_bytes!("examples/15b05c4865410c6b3ff76a4e8f3d87276756bd0c.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert features an EC key unlike the above samples
let der_encoded_cert = include_bytes!("examples/16ee54e48c76eaa1052e09010d8faefee95e5ebb.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds issuer alt name vs above samples
let der_encoded_cert = include_bytes!("examples/342cd9d3062da48c346965297f081ebc2ef68fdc.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds policy constraints vs above samples
let der_encoded_cert = include_bytes!("examples/2049a5b28f104b2c6e1a08546f9cfc0353d6fd30.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds subject alt name vs above samples
let der_encoded_cert = include_bytes!("examples/21723e7a0fb61a0bd4a29879b82a02b2fb4ad096.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds subject directory attributes vs above samples
let der_encoded_cert =
include_bytes!("examples/085B1E2F40254F9C7A2387BE9FF4EC116C326E10.fake.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds private key usage period (and an unprocessed Entrust extension) vs above samples
let der_encoded_cert =
include_bytes!("examples/554D5FF11DA613A155584D8D4AA07F67724D8077.fake.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds OCSP no check vs above samples
let der_encoded_cert =
include_bytes!("examples/28879DABB0FD11618FB74E47BE049D2933866D53.fake.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
// This cert adds PIV NACI indicator vs above samples
let der_encoded_cert =
include_bytes!("examples/288C8BCFEE6B89D110DAE2C9873897BF7FF53382.fake.der");
let result = Certificate::from_der(der_encoded_cert);
let cert: Certificate = result.unwrap();
let exts = cert.tbs_certificate.extensions.unwrap();
spin_over_exts(exts);
}
#[test]
fn decode_idp() {
use der::TagNumber;
// IDP from 04A8739769B3C090A11DCDFABA3CF33F4BEF21F3.crl in PKITS 2048 in ficam-scvp-testing repo
let idp = IssuingDistributionPoint::from_der(&hex!("30038201FF")).unwrap();
assert_eq!(idp.only_contains_ca_certs, true);
assert_eq!(idp.only_contains_attribute_certs, false);
assert_eq!(idp.only_contains_user_certs, false);
assert_eq!(idp.indirect_crl, false);
assert!(idp.only_some_reasons.is_none());
assert!(idp.distribution_point.is_none());
let n =
Name::from_der(&hex!("305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C")).unwrap();
assert_eq!(4, n.0.len());
let gn =
GeneralName::from_der(&hex!("A45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C")).unwrap();
match gn {
GeneralName::DirectoryName(gn) => {
assert_eq!(4, gn.0.len());
}
_ => {}
}
let gns =
GeneralNames::from_der(&hex!("305EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C")).unwrap();
assert_eq!(1, gns.len());
let gn = gns.get(0).unwrap();
match gn {
GeneralName::DirectoryName(gn) => {
assert_eq!(4, gn.0.len());
}
_ => {}
}
//TODO - fix decode impl (expecting a SEQUENCE despite this being a CHOICE). Sort out FixedTag implementation.
// let dpn =
// DistributionPointName::from_der(&hex!("A05EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C")).unwrap();
// match dpn {
// DistributionPointName::FullName(dpn) => {
// assert_eq!(1, dpn.len());
// let gn = dpn.get(0).unwrap();
// match gn {
// GeneralName::DirectoryName(gn) => {
// assert_eq!(4, gn.len());
// }
// _ => {}
// }
// }
// _ => {}
// }
let dp =
DistributionPoint::from_der(&hex!("3062A060A05EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C")).unwrap();
let dpn = dp.distribution_point.unwrap();
match dpn {
DistributionPointName::FullName(dpn) => {
assert_eq!(1, dpn.len());
let gn = dpn.get(0).unwrap();
match gn {
GeneralName::DirectoryName(gn) => {
assert_eq!(4, gn.0.len());
}
_ => {}
}
}
_ => {}
}
assert!(dp.crl_issuer.is_none());
assert!(dp.reasons.is_none());
// 0 103: SEQUENCE {
// 2 96: [0] {
// 4 94: [0] {
// 6 92: [4] {
// 8 90: SEQUENCE {
// 10 11: SET {
// 12 9: SEQUENCE {
// 14 3: OBJECT IDENTIFIER countryName (2 5 4 6)
// 19 2: PrintableString 'US'
// : }
// : }
// 23 31: SET {
// 25 29: SEQUENCE {
// 27 3: OBJECT IDENTIFIER organizationName (2 5 4 10)
// 32 22: PrintableString 'Test Certificates 2017'
// : }
// : }
// 56 28: SET {
// 58 26: SEQUENCE {
// 60 3: OBJECT IDENTIFIER organizationalUnitName (2 5 4 11)
// 65 19: PrintableString 'onlySomeReasons CA3'
// : }
// : }
// 86 12: SET {
// 88 10: SEQUENCE {
// 90 3: OBJECT IDENTIFIER commonName (2 5 4 3)
// 95 3: PrintableString 'CRL'
// : }
// : }
// : }
// : }
// : }
// : }
// 100 3: [3] 07 9F 80
// : }
// IDP from 54B0D2A6F6AA4780771CC4F9F076F623CEB0F57E.crl in PKITS 2048 in ficam-scvp-testing repo
let idp =
IssuingDistributionPoint::from_der(&hex!("3067A060A05EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C8303079F80")).unwrap();
assert_eq!(idp.only_contains_ca_certs, false);
assert_eq!(idp.only_contains_attribute_certs, false);
assert_eq!(idp.only_contains_user_certs, false);
assert_eq!(idp.indirect_crl, false);
assert!(idp.only_some_reasons.is_some());
assert!(idp.distribution_point.is_some());
assert_eq!(
Reasons::Unused
| Reasons::AffiliationChanged
| Reasons::Superseded
| Reasons::CessationOfOperation
| Reasons::CertificateHold
| Reasons::PrivilegeWithdrawn
| Reasons::AaCompromise,
idp.only_some_reasons.unwrap()
);
// 930 360: SEQUENCE {
// 934 353: [0] {
// 938 349: [0] {
// 942 117: [4] {
// 944 115: SEQUENCE {
// 946 11: SET {
// 948 9: SEQUENCE {
// 950 3: OBJECT IDENTIFIER countryName (2 5 4 6)
// 955 2: PrintableString 'US'
// : }
// : }
// 959 31: SET {
// 961 29: SEQUENCE {
// 963 3: OBJECT IDENTIFIER
// : organizationName (2 5 4 10)
// 968 22: PrintableString 'Test Certificates 2017'
// : }
// : }
// 992 24: SET {
// 994 22: SEQUENCE {
// 996 3: OBJECT IDENTIFIER
// : organizationalUnitName (2 5 4 11)
// 1001 15: PrintableString 'indirectCRL CA5'
// : }
// : }
// 1018 41: SET {
// 1020 39: SEQUENCE {
// 1022 3: OBJECT IDENTIFIER commonName (2 5 4 3)
// 1027 32: PrintableString 'indirect CRL for indirectCRL CA6'
// : }
// : }
// : }
// : }
// 1061 117: [4] {
// 1063 115: SEQUENCE {
// 1065 11: SET {
// 1067 9: SEQUENCE {
// 1069 3: OBJECT IDENTIFIER countryName (2 5 4 6)
// 1074 2: PrintableString 'US'
// : }
// : }
// 1078 31: SET {
// 1080 29: SEQUENCE {
// 1082 3: OBJECT IDENTIFIER
// : organizationName (2 5 4 10)
// 1087 22: PrintableString 'Test Certificates 2017'
// : }
// : }
// 1111 24: SET {
// 1113 22: SEQUENCE {
// 1115 3: OBJECT IDENTIFIER
// : organizationalUnitName (2 5 4 11)
// 1120 15: PrintableString 'indirectCRL CA5'
// : }
// : }
// 1137 41: SET {
// 1139 39: SEQUENCE {
// 1141 3: OBJECT IDENTIFIER commonName (2 5 4 3)
// 1146 32: PrintableString 'indirect CRL for indirectCRL CA7'
// : }
// : }
// : }
// : }
// 1180 109: [4] {
// 1182 107: SEQUENCE {
// 1184 11: SET {
// 1186 9: SEQUENCE {
// 1188 3: OBJECT IDENTIFIER countryName (2 5 4 6)
// 1193 2: PrintableString 'US'
// : }
// : }
// 1197 31: SET {
// 1199 29: SEQUENCE {
// 1201 3: OBJECT IDENTIFIER
// : organizationName (2 5 4 10)
// 1206 22: PrintableString 'Test Certificates 2017'
// : }
// : }
// 1230 24: SET {
// 1232 22: SEQUENCE {
// 1234 3: OBJECT IDENTIFIER
// : organizationalUnitName (2 5 4 11)
// 1239 15: PrintableString 'indirectCRL CA5'
// : }
// : }
// 1256 33: SET {
// 1258 31: SEQUENCE {
// 1260 3: OBJECT IDENTIFIER commonName (2 5 4 3)
// 1265 24: PrintableString 'CRL1 for indirectCRL CA5'
// : }
// : }
// : }
// : }
// : }
// : }
// 1291 1: [4] FF
// : }
// : }
// : }
// IDP from 959528526E54B646AF895E2362D3AD20F4B3284D.crl in PKITS 2048 in ficam-scvp-testing repo
let idp =
IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C204341358401FF")).unwrap();
assert_eq!(idp.only_contains_ca_certs, false);
assert_eq!(idp.only_contains_attribute_certs, false);
assert_eq!(idp.only_contains_user_certs, false);
assert_eq!(idp.indirect_crl, true);
assert!(idp.only_some_reasons.is_none());
assert!(idp.distribution_point.is_some());
let dp = idp.distribution_point.unwrap();
match dp {
DistributionPointName::FullName(dp) => {
assert_eq!(3, dp.len());
for gn in dp {
match gn {
GeneralName::DirectoryName(gn) => {
assert_eq!(4, gn.0.len());
}
_ => {
panic!("Expected DirectoryName")
}
}
}
}
_ => {
panic!("Expected FullName")
}
}
// Tag on second RDN in first name is TeletexString (20) instead of PrintableString (19)
let idp =
IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A14165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C204341358401FF")).unwrap();
assert_eq!(idp.only_contains_ca_certs, false);
assert_eq!(idp.only_contains_attribute_certs, false);
assert_eq!(idp.only_contains_user_certs, false);
assert_eq!(idp.indirect_crl, true);
assert!(idp.only_some_reasons.is_none());
assert!(idp.distribution_point.is_some());
let dp = idp.distribution_point.unwrap();
match dp {
DistributionPointName::FullName(dp) => {
assert_eq!(3, dp.len());
for gn in dp {
match gn {
GeneralName::DirectoryName(gn) => {
assert_eq!(4, gn.0.len());
}
_ => {
panic!("Expected DirectoryName")
}
}
}
}
_ => {
panic!("Expected FullName")
}
}
//---------------------------------
// Negative tests
//---------------------------------
// Value contains more than length value indicates
let reason_flags = ReasonFlags::from_der(&hex!("0302079F80"));
let err = reason_flags.err().unwrap();
assert_eq!(
ErrorKind::TrailingData {
decoded: 4u8.into(),
remaining: 1u8.into()
},
err.kind()
);
// Value incomplete relative to length value
let reason_flags = ReasonFlags::from_der(&hex!("0304079F80"));
let err = reason_flags.err().unwrap();
assert_eq!(
ErrorKind::Incomplete {
expected_len: 6u8.into(),
actual_len: 5u8.into()
},
err.kind()
);
// Value incomplete relative to length value
let idp =
IssuingDistributionPoint::from_der(&hex!("3067A060A05EA45C305A310B3009060355040613025553311F301D060355040A131654657374204365727469666963617465732032303137311C301A060355040B13136F6E6C79536F6D65526561736F6E7320434133310C300A0603550403130343524C8304079F80"));
let err = idp.err().unwrap();
assert_eq!(err.position().unwrap(), 103u8.into());
assert_eq!(
ErrorKind::Incomplete {
expected_len: 106u8.into(),
actual_len: 105u8.into()
},
err.kind()
);
// Truncated
let reason_flags = ReasonFlags::from_der(&hex!("0303079F"));
let err = reason_flags.err().unwrap();
assert_eq!(
ErrorKind::Incomplete {
expected_len: 5u8.into(),
actual_len: 4u8.into()
},
err.kind()
);
// Nonsensical tag where BIT STRING tag should be
let reason_flags = ReasonFlags::from_der(&hex!("FF03079F80"));
let err = reason_flags.err().unwrap();
assert_eq!(ErrorKind::TagNumberInvalid, err.kind());
// INTEGER tag where BIT STRING expected
let reason_flags = ReasonFlags::from_der(&hex!("0203079F80"));
let err = reason_flags.err().unwrap();
assert_eq!(
ErrorKind::TagUnexpected {
expected: Some(Tag::BitString),
actual: Tag::Integer
},
err.kind()
);
// Context specific tag that should be primitive is constructed
let idp = IssuingDistributionPoint::from_der(&hex!("3003A201FF"));
let err = idp.err().unwrap();
assert_eq!(
ErrorKind::Noncanonical {
tag: Tag::ContextSpecific {
constructed: true,
number: TagNumber::new(2)
}
},
err.kind()
);
// Boolean value is two bytes long
let idp =
IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C204341358402FFFF"));
let err = idp.err().unwrap();
assert_eq!(ErrorKind::Length { tag: Tag::Boolean }, err.kind());
// Boolean value is neither 0x00 nor 0xFF
let idp =
IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C20434135840175"));
let err = idp.err().unwrap();
assert_eq!(ErrorKind::Noncanonical { tag: Tag::Boolean }, err.kind());
// Length on second RDN in first name indicates more bytes than are present
let idp =
IssuingDistributionPoint::from_der(&hex!("30820168A0820161A082015DA4753073310B3009060355040613025553311F301D060355040A13995465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434136A4753073310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353129302706035504031320696E6469726563742043524C20666F7220696E64697265637443524C20434137A46D306B310B3009060355040613025553311F301D060355040A13165465737420436572746966696361746573203230313731183016060355040B130F696E64697265637443524C204341353121301F0603550403131843524C3120666F7220696E64697265637443524C204341358401FF"));
let err = idp.err().unwrap();
assert_eq!(
ErrorKind::Length {
tag: Tag::PrintableString
},
err.kind()
);
}