build.rs (5340B)
1 /* -*- Mode: rust; rust-indent-offset: 4 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 use base64::prelude::BASE64_STANDARD; 7 use base64::Engine; 8 use std::cmp::Ordering; 9 use std::fs::{read_dir, File}; 10 use std::io::{BufRead, BufReader, BufWriter, Write}; 11 use std::path::{Path, PathBuf}; 12 13 #[derive(Eq, PartialEq)] 14 struct TrustAnchor { 15 bytes: Vec<u8>, 16 subject: Vec<u8>, 17 subject_start: u16, 18 subject_len: u8, 19 } 20 21 impl PartialOrd for TrustAnchor { 22 fn partial_cmp(&self, other: &Self) -> Option<Ordering> { 23 self.subject.partial_cmp(&other.subject) 24 } 25 } 26 27 impl TrustAnchor { 28 fn new(bytes: Vec<u8>) -> TrustAnchor { 29 let (_, _, subject) = 30 rsclientcerts_util::read_encoded_certificate_identifiers(bytes.as_slice()) 31 .expect("Couldn't decode certificate."); 32 let subject_start = bytes 33 .windows(subject.len()) 34 .position(|s| s == subject) 35 .expect("subject should appear in bytes"); 36 let subject_start: u16 = subject_start 37 .try_into() 38 .expect("subject start hopefully fits in u16"); 39 let subject_len = subject 40 .len() 41 .try_into() 42 .expect("subject length hopefully fits in u8"); 43 TrustAnchor { 44 bytes, 45 subject, 46 subject_start, 47 subject_len, 48 } 49 } 50 } 51 52 fn read_trust_anchors( 53 trust_anchor_filename_or_directory: PathBuf, 54 ) -> std::io::Result<Vec<TrustAnchor>> { 55 let mut trust_anchors = if trust_anchor_filename_or_directory.is_dir() { 56 let mut trust_anchors = Vec::new(); 57 for dir_entry in read_dir(trust_anchor_filename_or_directory)? { 58 trust_anchors.append(&mut read_trust_anchors_from(dir_entry?.path())?); 59 } 60 trust_anchors 61 } else { 62 read_trust_anchors_from(trust_anchor_filename_or_directory)? 63 }; 64 65 trust_anchors.sort_by_cached_key(|trust_anchor| trust_anchor.subject.clone()); 66 Ok(trust_anchors) 67 } 68 69 fn read_trust_anchors_from(trust_anchor_file_path: PathBuf) -> std::io::Result<Vec<TrustAnchor>> { 70 let trust_anchor_file = File::open(trust_anchor_file_path)?; 71 let reader = BufReader::new(trust_anchor_file); 72 let mut maybe_current_trust_anchor: Option<Vec<String>> = None; 73 let mut trust_anchors = Vec::new(); 74 for line in reader.lines() { 75 let line = line.expect("Couldn't read contents of trust anchors file."); 76 match line.as_str() { 77 "-----BEGIN CERTIFICATE-----" => { 78 maybe_current_trust_anchor.replace(Vec::new()); 79 } 80 "-----END CERTIFICATE-----" => { 81 let current_trust_anchor = maybe_current_trust_anchor 82 .take() 83 .expect("END CERTIFICATE without BEGIN CERTIFICATE?"); 84 let base64 = current_trust_anchor.join(""); 85 let bytes = BASE64_STANDARD 86 .decode(base64) 87 .expect("Couldn't base64-decode trust anchor."); 88 let trust_anchor = TrustAnchor::new(bytes); 89 trust_anchors.push(trust_anchor); 90 } 91 _ => { 92 if let Some(current_trust_anchor) = maybe_current_trust_anchor.as_mut() { 93 current_trust_anchor.push(line); 94 } 95 } 96 } 97 } 98 Ok(trust_anchors) 99 } 100 101 fn emit_trust_anchors( 102 out: &mut dyn Write, 103 prefix: &str, 104 trust_anchors_filename_or_directory: &str, 105 ) -> std::io::Result<()> { 106 let trust_anchors_path = Path::new(trust_anchors_filename_or_directory); 107 let trust_anchors = read_trust_anchors(trust_anchors_path.to_path_buf())?; 108 for (index, trust_anchor) in trust_anchors.iter().enumerate() { 109 writeln!( 110 out, 111 "static {prefix}TRUST_ANCHOR_{index:0>4}_BYTES: &[u8] = &{:?};", 112 trust_anchor.bytes 113 )?; 114 } 115 116 writeln!( 117 out, 118 "pub (crate) static {prefix}TRUST_ANCHORS: [TrustAnchor; {num_trust_anchors}] = [", 119 num_trust_anchors = trust_anchors.len() 120 )?; 121 for (index, trust_anchor) in trust_anchors.iter().enumerate() { 122 writeln!(out, " TrustAnchor {{")?; 123 writeln!( 124 out, 125 " bytes: {prefix}TRUST_ANCHOR_{index:0>4}_BYTES," 126 )?; 127 writeln!( 128 out, 129 " subject: ({}, {}),", 130 trust_anchor.subject_start, trust_anchor.subject_len 131 )?; 132 writeln!(out, " }},")?; 133 } 134 writeln!(out, "];")?; 135 Ok(()) 136 } 137 138 fn main() -> std::io::Result<()> { 139 let trust_anchors = "trust_anchors.pem"; 140 let test_trust_anchors = "test_trust_anchors"; 141 println!("cargo:rerun-if-changed={}", trust_anchors); 142 println!("cargo:rerun-if-changed={}", test_trust_anchors); 143 144 let out_path = PathBuf::from(std::env::var("OUT_DIR").expect("OUT_DIR should be set in env.")); 145 let mut out = BufWriter::new( 146 File::create(out_path.join("trust_anchors.rs")).expect("Could not write trust_anchors.rs."), 147 ); 148 149 emit_trust_anchors(&mut out, "", trust_anchors)?; 150 emit_trust_anchors(&mut out, "TEST_", test_trust_anchors)?; 151 152 Ok(()) 153 }