diff --git a/tests/generic.rs b/tests/generic.rs index 43b1e917..3c433f41 100644 --- a/tests/generic.rs +++ b/tests/generic.rs @@ -111,219 +111,215 @@ mod test_csr_exts { rcgen::ExtendedKeyUsagePurpose::ServerAuth, rcgen::ExtendedKeyUsagePurpose::ClientAuth, ]; + let permitted_subtree_dns = "example.com"; + let excluded_subtree_dns = "example.org"; params.name_constraints = Some(rcgen::NameConstraints { - permitted_subtrees: vec![GeneralSubtree::DnsName("example.com".into())], - excluded_subtrees: vec![GeneralSubtree::DnsName("example.org".into())], + permitted_subtrees: vec![GeneralSubtree::DnsName(permitted_subtree_dns.into())], + excluded_subtrees: vec![GeneralSubtree::DnsName(excluded_subtree_dns.into())], }); + let distribution_point_uri = "http://example.com"; params.crl_distribution_points = vec![CrlDistributionPoint { - uris: vec!["http://example.com".into()], + uris: vec![distribution_point_uri.into()], }]; - let test_cert = Certificate::from_params(params).unwrap(); - let test_cert_der = test_cert.serialize_der().unwrap(); - - // We should be able to parse it and find the expected extensions present. - let (_, x509_test_cert) = X509Certificate::from_der(&test_cert_der).unwrap(); - - macro_rules! assert_x509_ext { - ($oid:ident, $critical:expr, $pattern:pat $(if $guard:expr)? $(,)?, $parsed_expr:expr) => {{ - let ext = x509_test_cert + let cert = Certificate::from_params(params).unwrap(); + let cert_der = cert.serialize_der().unwrap(); + let csr_der = cert.serialize_request_der().unwrap(); + + // Parse the self-signed test certificate, and a CSR generated from the test certificate with x509-parser. + let (_, x509_cert) = X509Certificate::from_der(&cert_der).unwrap(); + let (_, x509_csr) = X509CertificationRequest::from_der(&csr_der).unwrap(); + + // Helper macro that tests both the parsed cert and CSR have an extension with specific + // properties. + macro_rules! assert_paired_ext { + ($oid:ident, $critical:expr, $pattern:pat, $parsed_expr:expr) => {{ + // 1. Find the extension in the certificate. + let cert_ext = x509_cert .get_extension_unique(&x509_parser::oid_registry::$oid) - .expect(concat!("malformed ", stringify!($oid))) - .expect(concat!("missing ", stringify!($oid))); - assert_eq!(ext.critical, $critical); - - match ext.parsed_extension() { - $pattern $(if $guard)? => $parsed_expr, - _ => panic!("unexpected extension"), - } + .expect(concat!("malformed cert ext for ", stringify!($oid))) + .expect(concat!("missing cert ext for ", stringify!($oid))); + + // 2. Verify criticality. + assert_eq!( + cert_ext.critical, $critical, + concat!("wrong criticality for ", stringify!($oid)) + ); + + // 3. Verify the parsed representation of the extension. + match cert_ext.parsed_extension() { + $pattern => $parsed_expr, + _ => panic!(concat!( + "unexpected parsed extension for ", + stringify!($oid) + )), + }; + + // 4. Verify the parsed CSR has the extension, and that it has the correct + // parsed representation. + x509_csr + .requested_extensions() + .expect("missing CSR requested extensions") + .find_map(|ext| match ext { + $pattern => Some($parsed_expr), + _ => None, + }) + .expect(concat!("missing CSR extension for ", stringify!($oid))) }}; } - // Check SAN is present, not critical, and has the expected DNSName. - let assert_san = |san: &x509_parser::prelude::SubjectAlternativeName| { - san.general_names - .iter() - .find(|name| match name { - x509_parser::prelude::GeneralName::DNSName(name) => name == &san_name, - _ => false, - }) - .expect("missing expected SAN"); - }; - assert_x509_ext!( + assert_paired_ext!( OID_X509_EXT_SUBJECT_ALT_NAME, false, ParsedExtension::SubjectAlternativeName(san), - assert_san(san) + { + san.general_names + .iter() + .find(|name| match name { + x509_parser::prelude::GeneralName::DNSName(name) => name == &san_name, + _ => false, + }) + .expect("missing expected SAN"); + } ); - // Check basic constraints are present, critical, and have the expected path len constraint. - let assert_bc = |bc: &x509_parser::prelude::BasicConstraints| { - assert!(bc.ca); - assert_eq!( - bc.path_len_constraint.expect("missing path len constraint"), - path_len_constraint as u32 - ) - }; - assert_x509_ext!( + assert_paired_ext!( OID_X509_EXT_BASIC_CONSTRAINTS, true, ParsedExtension::BasicConstraints(bc), - assert_bc(bc) + { + assert!(bc.ca); + assert_eq!( + bc.path_len_constraint.expect("missing path len constraint"), + path_len_constraint as u32 + ); + } ); - // Check name constraints are present, critical and non-empty. - let assert_nc = |nc: &x509_parser::prelude::NameConstraints| { - assert!(!nc - .permitted_subtrees - .clone() - .expect("missing permitted subtrees") - .is_empty()); - assert!(!nc - .excluded_subtrees - .clone() - .expect("missing excluded subtrees") - .is_empty()); - // TODO: find the subtree base names we expect. - }; - assert_x509_ext!( + fn assert_subtree_dns( + subtrees: Vec, + expected_dns: &str, + ) { + subtrees + .iter() + .find( + |subtree| + matches!(subtree.base, x509_parser::prelude::GeneralName::DNSName(dns) if dns == expected_dns) + ) + .expect("missing expected subtree URI"); + } + assert_paired_ext!( OID_X509_EXT_NAME_CONSTRAINTS, true, ParsedExtension::NameConstraints(name_constraints), - assert_nc(name_constraints) + { + assert_subtree_dns( + name_constraints + .permitted_subtrees + .clone() + .expect("missing permitted subtrees"), + &permitted_subtree_dns, + ); + assert_subtree_dns( + name_constraints + .excluded_subtrees + .clone() + .expect("missing excluded subtrees"), + &excluded_subtree_dns, + ); + } ); - // Check CRL distribution point is present and non-critical. - let assert_crl_dps = |crl_dps: &x509_parser::prelude::CRLDistributionPoints| { - assert!(!crl_dps.points.is_empty()); - // TODO: find the point URI we expect. - }; - assert_x509_ext!( + fn assert_crl_dps_uri( + crl_dps: &x509_parser::prelude::CRLDistributionPoints, + expected: &str, + ) { + crl_dps + .iter() + .find(|dp| { + let full_names = match dp + .distribution_point + .clone() + .expect("missing distribution point name") + { + x509_parser::prelude::DistributionPointName::FullName(full_names) => { + full_names + }, + _ => panic!("missing full names"), + }; + + full_names.iter().find(|general_name| + matches!(general_name, x509_parser::prelude::GeneralName::URI(uri) if uri == &expected)).is_some() + }) + .expect("missing expected CRL distribution point URI"); + } + assert_paired_ext!( OID_X509_EXT_CRL_DISTRIBUTION_POINTS, false, ParsedExtension::CRLDistributionPoints(crl_dps), - assert_crl_dps(crl_dps) + assert_crl_dps_uri(crl_dps, &distribution_point_uri) ); - // Check key usage is present, and critical. - let assert_ku = |ku: &x509_parser::prelude::KeyUsage| { - assert!(ku.digital_signature()); - assert!(ku.key_encipherment()); - // TODO: negative assert other usages. Do an eq test... - }; - assert_x509_ext!( + assert_paired_ext!( OID_X509_EXT_KEY_USAGE, true, - ParsedExtension::KeyUsage(key_usage), - assert_ku(key_usage) + ParsedExtension::KeyUsage(ku), + { + assert!(ku.digital_signature()); + assert!(ku.key_encipherment()); + assert!(!ku.non_repudiation()); + assert!(!ku.key_agreement()); + assert!(!ku.key_cert_sign()); + assert!(!ku.encipher_only()); + assert!(!ku.decipher_only()); + } ); - // Check extended key usage is present, and non-critical. - let assert_eku = |eku: &x509_parser::prelude::ExtendedKeyUsage| { - assert!(eku.server_auth); - assert!(eku.client_auth); - assert!(!eku.any); - assert!(eku.other.is_empty()); - assert!(!eku.code_signing); - assert!(!eku.ocsp_signing); - assert!(!eku.email_protection); - assert!(!eku.time_stamping); - }; - assert_x509_ext!( + assert_paired_ext!( OID_X509_EXT_EXTENDED_KEY_USAGE, false, - ParsedExtension::ExtendedKeyUsage(extended_key_usage), - assert_eku(extended_key_usage) + ParsedExtension::ExtendedKeyUsage(eku), + { + assert!(eku.server_auth); + assert!(eku.client_auth); + assert!(!eku.any); + assert!(eku.other.is_empty()); + assert!(!eku.code_signing); + assert!(!eku.ocsp_signing); + assert!(!eku.email_protection); + assert!(!eku.time_stamping); + } ); - // Check subject key identifier is present, non-critical, and matches the expected value. - let assert_ski = |ski: &x509_parser::prelude::KeyIdentifier| { - assert_eq!(ski.0, &test_cert.get_key_identifier()); - }; - assert_x509_ext!( + assert_paired_ext!( OID_X509_EXT_SUBJECT_KEY_IDENTIFIER, false, ParsedExtension::SubjectKeyIdentifier(ski), - assert_ski(ski) + assert_eq!(ski.0, &cert.get_key_identifier()) ); - // Check authority key identifier is present, non-critical and matches the expected value. - assert_x509_ext!( - OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER, - false, - ParsedExtension::AuthorityKeyIdentifier(aki), - assert_eq!( + // We should find the AKI extension in the self-signed certificate. + let aki = x509_cert + .get_extension_unique(&x509_parser::oid_registry::OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER) + .expect("malformed OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER") + .expect("missing OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER"); + assert!(!aki.critical); + match aki.parsed_extension() { + ParsedExtension::AuthorityKeyIdentifier(aki) => assert_eq!( aki.clone() .key_identifier .expect("missing key identifier") .0, - &test_cert.get_key_identifier() - ) - ); - - // Generate a CSR from our cert, parse it with x509-parser. - let test_cert_csr_der = test_cert.serialize_request_der(); - let test_cert_csr_der = test_cert_csr_der.unwrap(); - let (_, x509_csr) = X509CertificationRequest::from_der(&test_cert_csr_der).unwrap(); - - let parsed_exts = x509_csr - .requested_extensions() - .expect("missing CSR requested extensions") - .collect::>(); - - macro_rules! assert_x509_csr_ext { - ($expr:expr, $pattern:pat $(if $guard:expr)? $(,)?, $parsed_expr:expr) => {{ - $expr.iter().find_map(|ext| match ext { - $pattern $(if $guard)? => Some($parsed_expr), - _ => None, - }).expect(concat!("missing ", stringify!($pattern))) - }}; - } - - // The basic constraints we specified should be there. - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::BasicConstraints(bc), - assert_bc(bc) - ); - - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::SubjectKeyIdentifier(ski), - assert_ski(ski) - ); - - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::CRLDistributionPoints(crl_dps), - assert_crl_dps(crl_dps) - ); - - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::NameConstraints(name_constraints), - assert_nc(name_constraints) - ); - - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::ExtendedKeyUsage(eku), - assert_eku(eku) - ); - - assert_x509_csr_ext!(parsed_exts, ParsedExtension::KeyUsage(ku), assert_ku(ku)); - - assert_x509_csr_ext!( - parsed_exts, - ParsedExtension::SubjectAlternativeName(san), - assert_san(san) - ); + &cert.get_key_identifier() + ), + _ => panic!("unexpected parsed extension type"), + }; // We should not find the AKI extension in the CSR. That's provided by an issuer - // when issuing the certificate (e.g. as we did above w/ serialize_der, making a - // self signed cert that contained an AKI). + // when issuing the certificate. assert_eq!( - parsed_exts - .iter() + x509_csr + .requested_extensions() + .unwrap() .find(|ext| { matches!(ext, ParsedExtension::AuthorityKeyIdentifier(_)) }), None );