582 lines
20 KiB
Rust
582 lines
20 KiB
Rust
/* -*- Mode: rust; rust-indent-offset: 4 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
extern crate bindgen;
|
|
extern crate nom;
|
|
|
|
use bindgen::callbacks::*;
|
|
use bindgen::*;
|
|
|
|
use mozbuild::TOPSRCDIR;
|
|
|
|
use nom::branch::alt;
|
|
use nom::bytes::complete::{tag, take_until};
|
|
use nom::character::complete::{
|
|
char, multispace0, newline, not_line_ending, one_of, space0, space1,
|
|
};
|
|
use nom::combinator::{fail, recognize};
|
|
use nom::multi::{many1, separated_list0};
|
|
use nom::sequence::{delimited, separated_pair, terminated, tuple};
|
|
use nom::IResult;
|
|
|
|
use std::collections::HashMap;
|
|
use std::env;
|
|
use std::fmt;
|
|
use std::fs::File;
|
|
use std::io::{BufWriter, Write};
|
|
use std::path::PathBuf;
|
|
|
|
fn octal_block_to_vec_u8(octal_block: &str) -> Vec<u8> {
|
|
octal_block
|
|
.lines()
|
|
.flat_map(|x| x.split('\\').skip(1))
|
|
.map(|x| u8::from_str_radix(x, 8).expect("octal value out of range."))
|
|
.collect()
|
|
}
|
|
|
|
fn octal_block_to_hex_string(octal: &str) -> String {
|
|
octal_block_to_vec_u8(octal)
|
|
.iter()
|
|
.map(|x| format!("0x{:02X}, ", x))
|
|
.collect()
|
|
}
|
|
|
|
// Wrapper around values parsed out of certdata.txt
|
|
enum Ck<'a> {
|
|
Class(&'a str),
|
|
Comment(&'a str),
|
|
DistrustAfter(Option<&'a str>),
|
|
Empty,
|
|
MultilineOctal(&'a str),
|
|
OptionBool(&'a str),
|
|
Trust(&'a str),
|
|
Utf8(&'a str),
|
|
}
|
|
|
|
// Translation of parsed values into the output rust code
|
|
impl fmt::Display for Ck<'_> {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self {
|
|
Ck::Class(s) => write!(f, "{s}_BYTES"),
|
|
Ck::Comment(s) => write!(f, "{}", s.replace('#', "//")),
|
|
Ck::DistrustAfter(None) => write!(f, "Some(CK_FALSE_BYTES)"),
|
|
Ck::DistrustAfter(Some(s)) => write!(f, "Some(&[{}])", octal_block_to_hex_string(s)),
|
|
Ck::Empty => write!(f, "None"),
|
|
Ck::MultilineOctal(s) => write!(f, "&[{}]", octal_block_to_hex_string(s)),
|
|
Ck::OptionBool(s) => write!(f, "Some({s}_BYTES)"),
|
|
Ck::Trust(s) => write!(f, "{s}_BYTES"),
|
|
Ck::Utf8(s) => write!(f, "\"{s}\\0\""),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PartialEq for Ck<'_> {
|
|
fn eq(&self, other: &Self) -> bool {
|
|
match (self, other) {
|
|
(Ck::Class(s), Ck::Class(t)) => s.eq(t),
|
|
(Ck::Comment(s), Ck::Comment(t)) => s.eq(t),
|
|
(Ck::DistrustAfter(None), Ck::DistrustAfter(None)) => true,
|
|
(Ck::DistrustAfter(Some(s)), Ck::DistrustAfter(Some(t))) => {
|
|
// compare the data rather than the presentation
|
|
let vec_s = octal_block_to_vec_u8(s);
|
|
let vec_t = octal_block_to_vec_u8(t);
|
|
vec_s.eq(&vec_t)
|
|
}
|
|
(Ck::Empty, Ck::Empty) => true,
|
|
(Ck::MultilineOctal(s), Ck::MultilineOctal(t)) => {
|
|
// compare the data rather than the presentation
|
|
let vec_s = octal_block_to_vec_u8(s);
|
|
let vec_t = octal_block_to_vec_u8(t);
|
|
vec_s.eq(&vec_t)
|
|
}
|
|
(Ck::Trust(s), Ck::Trust(t)) => s.eq(t),
|
|
(Ck::Utf8(s), Ck::Utf8(t)) => s.eq(t),
|
|
_ => false,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn class(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_OBJECT_CLASS")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, class) = alt((
|
|
tag("CKO_NSS_BUILTIN_ROOT_LIST"),
|
|
tag("CKO_CERTIFICATE"),
|
|
tag("CKO_NSS_TRUST"),
|
|
))(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Class(class)))
|
|
}
|
|
|
|
fn trust(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_TRUST")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, trust) = alt((
|
|
tag("CKT_NSS_TRUSTED_DELEGATOR"),
|
|
tag("CKT_NSS_MUST_VERIFY_TRUST"),
|
|
tag("CKT_NSS_NOT_TRUSTED"),
|
|
))(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Trust(trust)))
|
|
}
|
|
|
|
// Parses a CK_BBOOL and wraps it with Ck::OptionBool so that it gets printed as
|
|
// "Some(CK_TRUE_BYTES)" instead of "CK_TRUE_BYTES".
|
|
fn option_bbool(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_BBOOL")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, b) = alt((tag("CK_TRUE"), tag("CK_FALSE")))(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::OptionBool(b)))
|
|
}
|
|
|
|
fn bbool_true(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_BBOOL")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, _) = tag("CK_TRUE")(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Empty))
|
|
}
|
|
|
|
fn bbool_false(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_BBOOL")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, _) = tag("CK_FALSE")(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Empty))
|
|
}
|
|
|
|
fn utf8(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("UTF8")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, _) = char('"')(i)?;
|
|
let (i, utf8) = take_until("\"")(i)?;
|
|
let (i, _) = char('"')(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Utf8(utf8)))
|
|
}
|
|
|
|
fn certificate_type(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("CK_CERTIFICATE_TYPE")(i)?;
|
|
let (i, _) = space1(i)?;
|
|
let (i, _) = tag("CKC_X_509")(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
Ok((i, Ck::Empty))
|
|
}
|
|
|
|
// A CKA_NSS_{EMAIL,SERVER}_DISTRUST_AFTER line in certdata.txt is encoded either as a CK_BBOOL
|
|
// with value CK_FALSE (when there is no distrust after date) or as a MULTILINE_OCTAL block.
|
|
fn distrust_after(i: &str) -> IResult<&str, Ck> {
|
|
let (i, value) = alt((multiline_octal, bbool_false))(i)?;
|
|
match value {
|
|
Ck::Empty => Ok((i, Ck::DistrustAfter(None))),
|
|
Ck::MultilineOctal(data) => Ok((i, Ck::DistrustAfter(Some(data)))),
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
fn octal_octet(i: &str) -> IResult<&str, &str> {
|
|
recognize(tuple((
|
|
tag("\\"),
|
|
one_of("0123"), // 255 = \377
|
|
one_of("01234567"),
|
|
one_of("01234567"),
|
|
)))(i)
|
|
}
|
|
|
|
fn multiline_octal(i: &str) -> IResult<&str, Ck> {
|
|
let (i, _) = tag("MULTILINE_OCTAL")(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
let (i, lines) = recognize(many1(terminated(many1(octal_octet), newline)))(i)?;
|
|
let (i, _) = tag("END")(i)?;
|
|
let (i, _) = space0(i)?;
|
|
let (i, _) = newline(i)?;
|
|
return Ok((i, Ck::MultilineOctal(lines)));
|
|
}
|
|
|
|
fn distrust_comment(i: &str) -> IResult<&str, (&str, Ck)> {
|
|
let (i, comment) = recognize(delimited(
|
|
alt((
|
|
tag("# For Email Distrust After: "),
|
|
tag("# For Server Distrust After: "),
|
|
)),
|
|
not_line_ending,
|
|
newline,
|
|
))(i)?;
|
|
Ok((i, ("DISTRUST_COMMENT", Ck::Comment(comment))))
|
|
}
|
|
|
|
fn comment(i: &str) -> IResult<&str, (&str, Ck)> {
|
|
let (i, comment) = recognize(many1(delimited(char('#'), not_line_ending, newline)))(i)?;
|
|
Ok((i, ("COMMENT", Ck::Comment(comment))))
|
|
}
|
|
|
|
fn certdata_line(i: &str) -> IResult<&str, (&str, Ck)> {
|
|
let (i, (attr, value)) = alt((
|
|
distrust_comment, // must be listed before `comment`
|
|
comment,
|
|
separated_pair(tag("CKA_CLASS"), space1, class),
|
|
separated_pair(tag("CKA_CERTIFICATE_TYPE"), space1, certificate_type),
|
|
separated_pair(alt((tag("CKA_ID"), tag("CKA_LABEL"))), space1, utf8),
|
|
separated_pair(
|
|
alt((
|
|
tag("CKA_ISSUER"),
|
|
tag("CKA_CERT_SHA1_HASH"),
|
|
tag("CKA_CERT_MD5_HASH"),
|
|
tag("CKA_SERIAL_NUMBER"),
|
|
tag("CKA_SUBJECT"),
|
|
tag("CKA_VALUE"),
|
|
)),
|
|
space1,
|
|
multiline_octal,
|
|
),
|
|
separated_pair(
|
|
alt((
|
|
tag("CKA_NSS_SERVER_DISTRUST_AFTER"),
|
|
tag("CKA_NSS_EMAIL_DISTRUST_AFTER"),
|
|
)),
|
|
space1,
|
|
distrust_after,
|
|
),
|
|
separated_pair(
|
|
alt((
|
|
tag("CKA_TRUST_EMAIL_PROTECTION"),
|
|
tag("CKA_TRUST_CODE_SIGNING"),
|
|
tag("CKA_TRUST_SERVER_AUTH"),
|
|
)),
|
|
space1,
|
|
trust,
|
|
),
|
|
separated_pair(tag("CKA_NSS_MOZILLA_CA_POLICY"), space1, option_bbool),
|
|
separated_pair(tag("CKA_TOKEN"), space1, bbool_true),
|
|
separated_pair(
|
|
alt((
|
|
tag("CKA_TRUST_STEP_UP_APPROVED"),
|
|
tag("CKA_PRIVATE"),
|
|
tag("CKA_MODIFIABLE"),
|
|
)),
|
|
space1,
|
|
bbool_false,
|
|
),
|
|
))(i)?;
|
|
Ok((i, (attr, value)))
|
|
}
|
|
|
|
type Block<'a> = HashMap<&'a str, Ck<'a>>;
|
|
|
|
fn attr<'a>(block: &'a Block, attr: &str) -> &'a Ck<'a> {
|
|
block.get(attr).unwrap_or(&Ck::Empty)
|
|
}
|
|
|
|
fn parse(i: &str) -> IResult<&str, Vec<Block>> {
|
|
let mut out: Vec<Block> = vec![];
|
|
let (i, _) = take_until("BEGINDATA\n")(i)?;
|
|
let (i, _) = tag("BEGINDATA\n")(i)?;
|
|
let (i, mut raw_blocks) = separated_list0(many1(char('\n')), many1(certdata_line))(i)?;
|
|
let (i, _) = multispace0(i)?; // allow trailing whitespace
|
|
if !i.is_empty() {
|
|
// The first line of i contains an error.
|
|
let (line, _) = i.split_once('\n').unwrap_or((i, ""));
|
|
fail::<_, &str, _>(line)?;
|
|
}
|
|
for raw_block in raw_blocks.drain(..) {
|
|
out.push(raw_block.into_iter().collect())
|
|
}
|
|
Ok((i, out))
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
struct PKCS11TypesParseCallbacks;
|
|
|
|
impl ParseCallbacks for PKCS11TypesParseCallbacks {
|
|
fn int_macro(&self, _name: &str, _value: i64) -> Option<IntKind> {
|
|
Some(IntKind::U8)
|
|
}
|
|
}
|
|
|
|
// If we encounter a problem parsing certdata.txt we'll try to turn it into a compile time
|
|
// error in builtins.rs. We need to output definitions for ROOT_LIST_LABEL and BUILTINS to
|
|
// cut down on the number of errors the compiler produces.
|
|
macro_rules! emit_build_error {
|
|
($out:ident, $err:expr) => {
|
|
writeln!($out, "std::compile_error!(\"{}\");", $err)?;
|
|
writeln!($out, "pub static ROOT_LIST_LABEL: [u8; 0] = [];")?;
|
|
writeln!($out, "pub static BUILTINS: [Root; 0] = [];")?;
|
|
};
|
|
}
|
|
|
|
fn main() -> std::io::Result<()> {
|
|
let testlib_certdata =
|
|
TOPSRCDIR.join("security/manager/ssl/tests/unit/test_trust_anchors/certdata.txt");
|
|
let mozilla_certdata = TOPSRCDIR.join("security/nss/lib/ckfw/builtins/certdata.txt");
|
|
let nssckbi_header = TOPSRCDIR.join("security/nss/lib/ckfw/builtins/nssckbi.h");
|
|
let bundled_intermediates =
|
|
TOPSRCDIR.join("security/manager/ssl/trust_anchors/bundled_intermediates.txt");
|
|
println!("cargo:rerun-if-changed={}", testlib_certdata.display());
|
|
println!("cargo:rerun-if-changed={}", mozilla_certdata.display());
|
|
println!("cargo:rerun-if-changed={}", nssckbi_header.display());
|
|
println!("cargo:rerun-if-changed={}", bundled_intermediates.display());
|
|
|
|
let bindings = Builder::default()
|
|
.header(nssckbi_header.display().to_string())
|
|
.allowlist_var("NSS_BUILTINS_CRYPTOKI_VERSION_MAJOR")
|
|
.allowlist_var("NSS_BUILTINS_CRYPTOKI_VERSION_MINOR")
|
|
.allowlist_var("NSS_BUILTINS_LIBRARY_VERSION_MAJOR")
|
|
.allowlist_var("NSS_BUILTINS_LIBRARY_VERSION_MINOR")
|
|
.allowlist_var("NSS_BUILTINS_HARDWARE_VERSION_MAJOR")
|
|
.allowlist_var("NSS_BUILTINS_HARDWARE_VERSION_MINOR")
|
|
.allowlist_var("NSS_BUILTINS_FIRMWARE_VERSION_MAJOR")
|
|
.allowlist_var("NSS_BUILTINS_FIRMWARE_VERSION_MINOR")
|
|
.parse_callbacks(Box::new(PKCS11TypesParseCallbacks))
|
|
.generate()
|
|
.expect("Unable to generate bindings.");
|
|
|
|
let out_path = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR should be set in env."));
|
|
bindings
|
|
.write_to_file(out_path.join("version.rs"))
|
|
.expect("Could not write version.rs.");
|
|
|
|
let mut out = BufWriter::new(
|
|
File::create(out_path.join("builtins.rs")).expect("Could not write builtins.rs."),
|
|
);
|
|
|
|
// If we are building the test module, use the certdata.txt in the test directory.
|
|
#[cfg(feature = "testlib")]
|
|
let mut input =
|
|
std::fs::read_to_string(testlib_certdata).expect("Unable to read certdata.txt.");
|
|
|
|
// Otherwise, use the official certdata.txt for the Mozilla root store.
|
|
#[cfg(not(feature = "testlib"))]
|
|
let mut input =
|
|
std::fs::read_to_string(mozilla_certdata).expect("Unable to read certdata.txt.");
|
|
|
|
#[cfg(not(feature = "testlib"))]
|
|
input.push_str(
|
|
&std::fs::read_to_string(bundled_intermediates)
|
|
.expect("Unable to read bundled_intermediates.txt."),
|
|
);
|
|
|
|
// Add a trailing newline to simplify parsing.
|
|
input.push('\n');
|
|
|
|
let blocks = match parse(&input) {
|
|
Ok((_, blocks)) => blocks,
|
|
Err(e) => {
|
|
let input = match e {
|
|
nom::Err::Error(nom::error::Error { input, .. }) => input,
|
|
_ => "Unknown",
|
|
};
|
|
emit_build_error!(
|
|
out,
|
|
&format!(
|
|
"Could not parse certdata.txt. Failed at: \'{}\');",
|
|
input.escape_debug().to_string().escape_debug()
|
|
)
|
|
);
|
|
return Ok(());
|
|
}
|
|
};
|
|
|
|
let root_lists: Vec<&Block> = blocks
|
|
.iter()
|
|
.filter(|x| attr(x, "CKA_CLASS") == &Ck::Class("CKO_NSS_BUILTIN_ROOT_LIST"))
|
|
.collect();
|
|
|
|
if root_lists.len() != 1 {
|
|
emit_build_error!(
|
|
out,
|
|
"certdata.txt does not define a CKO_NSS_BUILTIN_ROOT_LIST object."
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
let mut certs: Vec<&Block> = blocks
|
|
.iter()
|
|
.filter(|x| attr(x, "CKA_CLASS") == &Ck::Class("CKO_CERTIFICATE"))
|
|
.collect();
|
|
|
|
let trusts: Vec<&Block> = blocks
|
|
.iter()
|
|
.filter(|x| attr(x, "CKA_CLASS") == &Ck::Class("CKO_NSS_TRUST"))
|
|
.collect();
|
|
|
|
if certs.len() != trusts.len() {
|
|
emit_build_error!(
|
|
out,
|
|
"certdata.txt has a mismatched number of certificate and trust objects"
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
// Ensure that every certificate has a CKA_SUBJECT attribute for the sort
|
|
for (i, cert) in certs.iter().enumerate() {
|
|
match cert.get("CKA_SUBJECT") {
|
|
Some(Ck::MultilineOctal(_)) => (),
|
|
_ => {
|
|
emit_build_error!(
|
|
out,
|
|
format!("Certificate {i} in certdata.txt has no CKA_SUBJECT attribute.")
|
|
);
|
|
return Ok(());
|
|
}
|
|
}
|
|
}
|
|
|
|
certs.sort_by_cached_key(|x| match x.get("CKA_SUBJECT") {
|
|
Some(Ck::MultilineOctal(data)) => octal_block_to_vec_u8(data),
|
|
_ => unreachable!(),
|
|
});
|
|
|
|
// Write out arrays for the DER encoded certificate, serial number, and subject of each root.
|
|
// Since the serial number and the subject are in the DER cert, we don't need to store
|
|
// additional data for them.
|
|
for (i, cert) in certs.iter().enumerate() {
|
|
// Preserve the comment from certdata.txt
|
|
match attr(cert, "COMMENT") {
|
|
Ck::Empty => (),
|
|
comment => write!(out, "{comment}")?,
|
|
};
|
|
|
|
let der = attr(cert, "CKA_VALUE");
|
|
writeln!(out, "static ROOT_{i}: &[u8] = {der};")?;
|
|
|
|
// Search for the serial number and subject in the DER cert. We want to search on the raw
|
|
// bytes, not the octal presentation, so we have to unpack the enums.
|
|
let der_data = match der {
|
|
Ck::MultilineOctal(x) => octal_block_to_vec_u8(x),
|
|
_ => unreachable!(),
|
|
};
|
|
let serial_data = match attr(cert, "CKA_SERIAL_NUMBER") {
|
|
Ck::MultilineOctal(x) => octal_block_to_vec_u8(x),
|
|
_ => unreachable!(),
|
|
};
|
|
let subject_data = match attr(cert, "CKA_SUBJECT") {
|
|
Ck::MultilineOctal(x) => octal_block_to_vec_u8(x),
|
|
_ => unreachable!(),
|
|
};
|
|
|
|
fn need_u16(out: &mut impl Write, attr: &str, what: &str, i: usize) -> std::io::Result<()> {
|
|
emit_build_error!(
|
|
out,
|
|
format!("Certificate {i} in certdata.txt has a {attr} whose {what} doesn't fit in a u8. Time to upgrade to u16 at the expense of size?")
|
|
);
|
|
Ok(())
|
|
}
|
|
|
|
let serial_len = serial_data.len();
|
|
if let Some(serial_offset) = &der_data.windows(serial_len).position(|s| s == serial_data) {
|
|
if *serial_offset > u8::MAX.into() {
|
|
return need_u16(&mut out, "CKA_SERIAL_NUMBER", "offset", i);
|
|
}
|
|
if serial_len > u8::MAX.into() {
|
|
return need_u16(&mut out, "CKA_SERIAL_NUMBER", "length", i);
|
|
}
|
|
writeln!(
|
|
out,
|
|
"const SERIAL_{i}: (u8, u8) = ({serial_offset}, {serial_len});"
|
|
)?;
|
|
} else {
|
|
emit_build_error!(
|
|
out,
|
|
format!("Certificate {i} in certdata.txt has a CKA_SERIAL_NUMBER that does not match its CKA_VALUE.")
|
|
);
|
|
return Ok(());
|
|
}
|
|
|
|
let subject_len = subject_data.len();
|
|
if let Some(subject_offset) = &der_data
|
|
.windows(subject_len)
|
|
.position(|s| s == subject_data)
|
|
{
|
|
if *subject_offset > u8::MAX.into() {
|
|
return need_u16(&mut out, "CKA_SUBJECT", "offset", i);
|
|
}
|
|
if subject_len > u8::MAX.into() {
|
|
return need_u16(&mut out, "CKA_SUBJECT", "length", i);
|
|
}
|
|
writeln!(
|
|
out,
|
|
"const SUBJECT_{i}: (u8, u8) = ({subject_offset}, {subject_len});"
|
|
)?;
|
|
} else {
|
|
emit_build_error!(
|
|
out,
|
|
format!("Certificate {i} in certdata.txt has a CKA_SUBJECT that does not match its CKA_VALUE.")
|
|
);
|
|
return Ok(());
|
|
}
|
|
}
|
|
|
|
let root_list_label = attr(root_lists[0], "CKA_LABEL");
|
|
let root_list_label_len = match root_list_label {
|
|
Ck::Utf8(x) => x.len() + 1,
|
|
_ => unreachable!(),
|
|
};
|
|
writeln!(
|
|
out,
|
|
"pub const ROOT_LIST_LABEL: [u8; {root_list_label_len}] = *b{root_list_label};"
|
|
)?;
|
|
|
|
writeln!(out, "pub static BUILTINS: [Root; {}] = [", certs.len())?;
|
|
for (i, cert) in certs.iter().enumerate() {
|
|
let label = attr(cert, "CKA_LABEL");
|
|
let mozpol = attr(cert, "CKA_NSS_MOZILLA_CA_POLICY");
|
|
let server_distrust = attr(cert, "CKA_NSS_SERVER_DISTRUST_AFTER");
|
|
let email_distrust = attr(cert, "CKA_NSS_EMAIL_DISTRUST_AFTER");
|
|
let matching_trusts: Vec<&&Block> = trusts
|
|
.iter()
|
|
.filter(|trust| {
|
|
(attr(cert, "CKA_ISSUER") == attr(trust, "CKA_ISSUER"))
|
|
&& (attr(cert, "CKA_SERIAL_NUMBER") == attr(trust, "CKA_SERIAL_NUMBER"))
|
|
})
|
|
.collect();
|
|
if matching_trusts.len() != 1 {
|
|
writeln!(out, "];")?; // end the definition of BUILTINS
|
|
let label = format!("{}", label);
|
|
writeln!(out, "std::compile_error!(\"Could not find unique trust object for {} in certdata.txt\");", label.escape_debug())?;
|
|
return Ok(());
|
|
}
|
|
let trust = *matching_trusts[0];
|
|
let sha1 = match attr(trust, "CKA_CERT_SHA1_HASH") {
|
|
Ck::MultilineOctal(x) => octal_block_to_hex_string(x),
|
|
_ => unreachable!(),
|
|
};
|
|
let md5 = match attr(trust, "CKA_CERT_MD5_HASH") {
|
|
Ck::MultilineOctal(x) => octal_block_to_hex_string(x),
|
|
_ => unreachable!(),
|
|
};
|
|
let server = attr(trust, "CKA_TRUST_SERVER_AUTH");
|
|
let email = attr(trust, "CKA_TRUST_EMAIL_PROTECTION");
|
|
|
|
writeln!(
|
|
out,
|
|
" Root {{
|
|
label: {label},
|
|
der_name: SUBJECT_{i},
|
|
der_serial: SERIAL_{i},
|
|
der_cert: ROOT_{i},
|
|
mozilla_ca_policy: {mozpol},
|
|
server_distrust_after: {server_distrust},
|
|
email_distrust_after: {email_distrust},
|
|
sha1: [{sha1}],
|
|
md5: [{md5}],
|
|
trust_server: {server},
|
|
trust_email: {email},
|
|
}},"
|
|
)?;
|
|
}
|
|
writeln!(out, "];")?;
|
|
|
|
let _ = out.flush();
|
|
Ok(())
|
|
}
|