use std::ascii::escape_default; use std::fmt::Write; use std::fs; use pki_types::CertificateDer; use ring::digest; use webpki::anchor_from_trusted_cert; use webpki_ccadb::fetch_ccadb_roots; use x509_parser::prelude::AttributeTypeAndValue; use x509_parser::x509::X509Name; #[tokio::test] async fn new_generated_code_is_fresh() { let tls_roots_map = fetch_ccadb_roots().await; let mut code = String::with_capacity(256 * 1_024); code.push_str(HEADER); code.push_str("pub const TLS_SERVER_ROOTS: &[TrustAnchor<'static>] = &[\n"); let (mut subject, mut spki, mut name_constraints) = (String::new(), String::new(), String::new()); for root in tls_roots_map.values() { // Verify the DER FP matches the metadata FP. let der = root.der(); let calculated_fp = digest::digest(&digest::SHA256, &der); let metadata_fp = hex::decode(&root.sha256_fingerprint).expect("malformed fingerprint"); assert_eq!(calculated_fp.as_ref(), metadata_fp.as_slice()); let ta_der = CertificateDer::from(der.as_ref()); let ta = anchor_from_trusted_cert(&ta_der).expect("malformed trust anchor der"); subject.clear(); for &b in ta.subject.as_ref() { write!(&mut subject, "{}", escape_default(b)).unwrap(); } spki.clear(); for &b in ta.subject_public_key_info.as_ref() { write!(&mut spki, "{}", escape_default(b)).unwrap(); } name_constraints.clear(); if let Some(nc) = &root.mozilla_applied_constraints() { for &b in nc.iter() { write!(&mut name_constraints, "{}", escape_default(b)).unwrap(); } } let (_, parsed_cert) = x509_parser::parse_x509_certificate(&der).expect("malformed x509 der"); let issuer = name_to_string(parsed_cert.issuer()); let subject_str = name_to_string(parsed_cert.subject()); let label = root.common_name_or_certificate_name.clone(); let serial = root.serial().to_string(); let sha256_fp = root.sha256_fp(); // Write comment code.push_str(" /*\n"); code.push_str(&format!(" * Issuer: {}\n", issuer)); code.push_str(&format!(" * Subject: {}\n", subject_str)); code.push_str(&format!(" * Label: {:?}\n", label)); code.push_str(&format!(" * Serial: {}\n", serial)); code.push_str(&format!(" * SHA256 Fingerprint: {}\n", sha256_fp)); for ln in root.pem().lines() { code.push_str(" * "); code.push_str(ln.trim()); code.push('\n'); } code.push_str(" */\n"); // Write the code code.push_str(" TrustAnchor {\n"); code.write_fmt(format_args!( " subject: Der::from_slice(b\"{subject}\"),\n" )) .unwrap(); code.write_fmt(format_args!( " subject_public_key_info: Der::from_slice(b\"{spki}\"),\n" )) .unwrap(); match name_constraints.is_empty() { false => code .write_fmt(format_args!( " name_constraints: Some(Der::from_slice(b\"{name_constraints}\"))\n" )) .unwrap(), true => code.push_str(" name_constraints: None\n"), } code.push_str(" },\n\n"); } code.push_str("];\n"); // Check that the generated code matches the checked-in code let old = fs::read_to_string("src/lib.rs").unwrap(); if old != code { fs::write("src/lib.rs", code).unwrap(); panic!("generated code changed"); } } /// The built-in x509_parser::X509Name Display impl uses a different sort order than /// the one historically used by mkcert.org^[0]. We re-create that sort order here to /// avoid unnecessary churn in the generated code. /// /// [0]: fn name_to_string(name: &X509Name) -> String { let mut ret = String::with_capacity(256); if let Some(cn) = name .iter_common_name() .next() .and_then(|cn| cn.as_str().ok()) { write!(ret, "CN={}", cn).unwrap(); } let mut append_attrs = |attrs: Vec<&AttributeTypeAndValue>, label| { let str_parts = attrs .iter() .filter_map(|attr| match attr.as_str() { Ok(s) => Some(s), Err(_) => None, }) .collect::>() .join("/"); if !str_parts.is_empty() { if !ret.is_empty() { ret.push(' '); } write!(ret, "{}={}", label, str_parts).unwrap(); } }; append_attrs(name.iter_organization().collect(), "O"); append_attrs(name.iter_organizational_unit().collect(), "OU"); ret } const HEADER: &str = r#"//! A compiled-in copy of the root certificates trusted by Mozilla. //! //! To use this library with rustls 0.22: //! //! ```rust //! let root_store = rustls::RootCertStore { //! roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), //! }; //! ``` //! //! This library is suitable for use in applications that can always be recompiled and instantly deployed. //! For applications that are deployed to end-users and cannot be recompiled, or which need certification //! before deployment, consider a library that uses the platform native certificate verifier such as //! [rustls-platform-verifier]. This has the additional benefit of supporting OS provided CA constraints //! and revocation data. //! //! [rustls-platform-verifier]: https://docs.rs/rustls-platform-verifier // // This library is automatically generated from the Mozilla // IncludedCACertificateReportPEMCSV report via ccadb.org. Don't edit it. // // The generation is done deterministically so you can verify it // yourself by inspecting and re-running the generation process. #![no_std] #![forbid(unsafe_code, unstable_features)] #![deny( elided_lifetimes_in_paths, trivial_casts, trivial_numeric_casts, unused_import_braces, unused_extern_crates, unused_qualifications )] use pki_types::{Der, TrustAnchor}; "#;