blob: 5cd14bbb8c2e5101e02ddf216ca34b832afad818 [file] [log] [blame]
// This test attempts to verify that the set of 'native'
// certificates produced by this crate is roughly similar
// to the set of certificates in the mozilla root program
// as expressed by the `webpki-roots` crate.
//
// This is, obviously, quite a heuristic test.
use std::collections::HashMap;
use ring::io::der;
use untrusted;
fn stringify_x500name(subject: &[u8]) -> String {
let mut parts = vec![];
let mut reader = untrusted::Reader::new(subject.into());
while !reader.at_end() {
let (tag, contents) = der::read_tag_and_get_value(&mut reader)
.unwrap();
assert!(tag == 0x31); // sequence, constructed, context=1
let mut inner = untrusted::Reader::new(contents.into());
let pair = der::expect_tag_and_get_value(&mut inner, der::Tag::Sequence)
.unwrap();
let mut pair = untrusted::Reader::new(pair.into());
let oid = der::expect_tag_and_get_value(&mut pair, der::Tag::OID)
.unwrap();
let (valuety, value) = der::read_tag_and_get_value(&mut pair)
.unwrap();
let name = match oid.as_slice_less_safe() {
&[0x55, 0x04, 0x03] => "CN",
&[0x55, 0x04, 0x05] => "serialNumber",
&[0x55, 0x04, 0x06] => "C",
&[0x55, 0x04, 0x07] => "L",
&[0x55, 0x04, 0x08] => "ST",
&[0x55, 0x04, 0x09] => "STREET",
&[0x55, 0x04, 0x0a] => "O",
&[0x55, 0x04, 0x0b] => "OU",
&[0x55, 0x04, 0x11] => "postalCode",
&[0x55, 0x04, 0x61] => "organizationIdentifier",
&[0x09, 0x92, 0x26, 0x89, 0x93, 0xf2, 0x2c, 0x64, 0x01, 0x19] => "domainComponent",
&[0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x01] => "emailAddress",
_ => panic!("unhandled x500 attr {:?}", oid)
};
let str_value = match valuety {
// PrintableString, UTF8String, TeletexString or IA5String
0x0c | 0x13 | 0x14 | 0x16 => std::str::from_utf8(value.as_slice_less_safe()).unwrap(),
_ => panic!("unhandled x500 value type {:?}", valuety)
};
parts.push(format!("{}={}", name, str_value));
}
parts.join(", ")
}
fn to_map<'a>(anchors: &'a [webpki::TrustAnchor<'a>]) -> HashMap<Vec<u8>, &'a webpki::TrustAnchor<'a>> {
let mut r = HashMap::new();
for anchor in anchors {
r.insert(anchor.spki.to_vec(), anchor);
}
r
}
#[test]
fn test_does_not_have_many_roots_unknown_by_mozilla() {
let native = rustls_native_certs::load_native_certs()
.unwrap();
let mozilla = to_map(webpki_roots::TLS_SERVER_ROOTS.0);
let mut missing_in_moz_roots = 0;
for cert in &native.roots {
let cert = cert.to_trust_anchor();
if let Some(moz) = mozilla.get(cert.spki) {
assert_eq!(cert.subject, moz.subject,
"subjects differ for public key");
} else {
println!("Native anchor {:?} is missing from mozilla set", stringify_x500name(cert.subject));
missing_in_moz_roots += 1;
}
}
#[cfg(windows)]
let threshold = 1.6; // no more than 160% extra roots; windows CI vm has lots of extra roots
#[cfg(not(windows))]
let threshold = 0.5; // no more than 50% extra roots
let diff = (missing_in_moz_roots as f64) / (mozilla.len() as f64);
println!("mozilla: {:?}", mozilla.len());
println!("native: {:?}", native.len());
println!("{:?} anchors present in native set but not mozilla ({}%)", missing_in_moz_roots, diff * 100.);
assert!(diff < threshold, "too many unknown roots");
}
#[test]
fn test_contains_most_roots_known_by_mozilla() {
let native = rustls_native_certs::load_native_certs()
.unwrap();
let mut native_map = HashMap::new();
for anchor in &native.roots {
native_map.insert(anchor.to_trust_anchor().spki.to_vec(), anchor);
}
let mut missing_in_native_roots = 0;
let mozilla = webpki_roots::TLS_SERVER_ROOTS.0;
for cert in mozilla {
if native_map.get(cert.spki).is_none() {
println!("Mozilla anchor {:?} is missing from native set", stringify_x500name(cert.subject));
missing_in_native_roots += 1;
}
}
#[cfg(windows)]
let threshold = 0.95; // no more than 95% extra roots; windows misses *many* roots
#[cfg(not(windows))]
let threshold = 0.5; // no more than 50% extra roots
let diff = (missing_in_native_roots as f64) / (mozilla.len() as f64);
println!("mozilla: {:?}", mozilla.len());
println!("native: {:?}", native.len());
println!("{:?} anchors present in mozilla set but not native ({}%)",
missing_in_native_roots, diff * 100.);
assert!(diff < threshold, "too many missing roots");
}
#[test]
fn util_list_certs() {
let native = rustls_native_certs::load_native_certs()
.unwrap();
for (i, cert) in native.roots.iter().enumerate() {
println!("cert[{}] = {}", i, stringify_x500name(cert.to_trust_anchor().subject));
}
}