Skip to content

Commit 5cec848

Browse files
committed
tests: add x509-parser test for custom exts
Previously there was no test coverage for custom extensions in certificates or CSRs. This commit adds a simple example of encoding a custom extension, and then demonstrating that it can be parsed with `x509-parser`, both in a serialized certificate and in a CSR. There's no support in webpki, openssl-rs or botan-rs for handling custom extensions so no test coverage for those libraries is possible at this time.
1 parent 73cbec9 commit 5cec848

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

tests/generic.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,74 @@ mod test_convert_x509_subject_alternative_name {
8484
}
8585
}
8686

87+
#[cfg(feature = "x509-parser")]
88+
mod test_x509_custom_ext {
89+
use crate::util;
90+
use crate::Certificate;
91+
92+
use rcgen::CustomExtension;
93+
use x509_parser::oid_registry::asn1_rs;
94+
use x509_parser::prelude::{
95+
FromDer, ParsedCriAttribute, X509Certificate, X509CertificationRequest,
96+
};
97+
98+
#[test]
99+
fn custom_ext() {
100+
// Create an imaginary critical custom extension for testing.
101+
let test_oid = asn1_rs::Oid::from(&[2, 5, 29, 999999]).unwrap();
102+
let test_ext = yasna::construct_der(|writer| {
103+
writer.write_utf8_string("🦀 greetz to ferris 🦀");
104+
});
105+
let mut custom_ext = CustomExtension::from_oid_content(
106+
test_oid.iter().unwrap().collect::<Vec<u64>>().as_slice(),
107+
test_ext.clone(),
108+
);
109+
custom_ext.set_criticality(true);
110+
111+
// Generate a certificate with the custom extension, parse it with x509-parser.
112+
let mut params = util::default_params();
113+
params.custom_extensions = vec![custom_ext];
114+
let test_cert = Certificate::from_params(params).unwrap();
115+
let test_cert_der = test_cert.serialize_der().unwrap();
116+
let (_, x509_test_cert) = X509Certificate::from_der(&test_cert_der).unwrap();
117+
118+
// We should be able to find the extension by OID, with expected criticality and value.
119+
let favorite_drink_ext = x509_test_cert
120+
.get_extension_unique(&test_oid)
121+
.expect("invalid extensions")
122+
.expect("missing custom extension");
123+
assert_eq!(favorite_drink_ext.critical, true);
124+
assert_eq!(favorite_drink_ext.value, test_ext);
125+
126+
// Generate a CSR with the custom extension, parse it with x509-parser.
127+
let test_cert_csr_der = test_cert.serialize_request_der().unwrap();
128+
let (_, x509_csr) = X509CertificationRequest::from_der(&test_cert_csr_der).unwrap();
129+
130+
// We should find that the CSR contains requested extensions.
131+
// Note: we can't use `x509_csr.requested_extensions()` here because it maps the raw extension
132+
// request extensions to their parsed form, and of course x509-parser doesn't parse our custom extension.
133+
let exts = x509_csr
134+
.certification_request_info
135+
.iter_attributes()
136+
.find_map(|attr| {
137+
if let ParsedCriAttribute::ExtensionRequest(requested) = &attr.parsed_attribute() {
138+
Some(requested.extensions.iter().collect::<Vec<_>>())
139+
} else {
140+
None
141+
}
142+
})
143+
.expect("missing requested extensions");
144+
145+
// We should find the custom extension with expected criticality and value.
146+
let custom_ext = exts
147+
.iter()
148+
.find(|ext| ext.oid == test_oid)
149+
.expect("missing requested custom extension");
150+
assert_eq!(custom_ext.critical, true);
151+
assert_eq!(custom_ext.value, test_ext);
152+
}
153+
}
154+
87155
#[cfg(feature = "x509-parser")]
88156
mod test_x509_parser_crl {
89157
use crate::util;

0 commit comments

Comments
 (0)