159 lines
5.5 KiB
Rust
159 lines
5.5 KiB
Rust
//! 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.
|
|
mod common;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use pki_types::Der;
|
|
use ring::io::der;
|
|
use serial_test::serial;
|
|
use webpki::anchor_from_trusted_cert;
|
|
|
|
fn stringify_x500name(subject: &Der<'_>) -> String {
|
|
let mut parts = vec![];
|
|
let mut reader = untrusted::Reader::new(subject.as_ref().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);
|
|
let pair = der::expect_tag_and_get_value(&mut inner, der::Tag::Sequence).unwrap();
|
|
|
|
let mut pair = untrusted::Reader::new(pair);
|
|
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(", ")
|
|
}
|
|
|
|
#[test]
|
|
fn test_does_not_have_many_roots_unknown_by_mozilla() {
|
|
let native = rustls_native_certs::load_native_certs().unwrap();
|
|
let mozilla = webpki_roots::TLS_SERVER_ROOTS
|
|
.iter()
|
|
.map(|ta| (ta.subject_public_key_info.as_ref(), ta))
|
|
.collect::<HashMap<_, _>>();
|
|
|
|
let mut missing_in_moz_roots = 0;
|
|
|
|
for cert in &native {
|
|
let cert = anchor_from_trusted_cert(&cert).unwrap();
|
|
if let Some(moz) = mozilla.get(cert.subject_public_key_info.as_ref()) {
|
|
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 = 2.0; // no more than 160% extra roots; windows CI vm has lots of extra roots
|
|
#[cfg(target_os = "macos")]
|
|
let threshold = 0.6; // macOS has a bunch of extra roots, too
|
|
#[cfg(not(any(windows, target_os = "macos")))]
|
|
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 {
|
|
let cert = anchor_from_trusted_cert(&anchor).unwrap();
|
|
let spki = cert.subject_public_key_info.as_ref();
|
|
native_map.insert(spki.to_owned(), anchor);
|
|
}
|
|
|
|
let mut missing_in_native_roots = 0;
|
|
let mozilla = webpki_roots::TLS_SERVER_ROOTS;
|
|
for cert in mozilla {
|
|
if native_map
|
|
.get(cert.subject_public_key_info.as_ref())
|
|
.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(target_os = "macos")]
|
|
let threshold = 0.6; // no more than 60% extra roots; macOS has a bunch of extra roots, too
|
|
#[cfg(not(any(windows, target_os = "macos")))]
|
|
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]
|
|
#[serial]
|
|
fn util_list_certs() {
|
|
unsafe {
|
|
// SAFETY: safe because of #[serial]
|
|
common::clear_env();
|
|
}
|
|
|
|
let native = rustls_native_certs::load_native_certs().unwrap();
|
|
|
|
for (i, cert) in native.iter().enumerate() {
|
|
let cert = anchor_from_trusted_cert(&cert).unwrap();
|
|
println!("cert[{i}] = {}", stringify_x500name(&cert.subject));
|
|
}
|
|
}
|