1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/security/manager/tools/genHPKPStaticPins.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,576 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// How to run this file: 1.9 +// 1. [obtain firefox source code] 1.10 +// 2. [build/obtain firefox binaries] 1.11 +// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \ 1.12 +// [path to]/genHPKPStaticpins.js \ 1.13 +// [absolute path to]/PreloadedHPKPins.json \ 1.14 +// [absolute path to]/default-ee.der \ 1.15 +// [absolute path to]/StaticHPKPins.h 1.16 + 1.17 +if (arguments.length != 3) { 1.18 + throw "Usage: genHPKPStaticPins.js " + 1.19 + "<absolute path to PreloadedHPKPins.json> " + 1.20 + "<absolute path to default-ee.der> " + 1.21 + "<absolute path to StaticHPKPins.h>"; 1.22 +} 1.23 + 1.24 +const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components; 1.25 + 1.26 +let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); 1.27 +let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); 1.28 +let { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); 1.29 + 1.30 +let gCertDB = Cc["@mozilla.org/security/x509certdb;1"] 1.31 + .getService(Ci.nsIX509CertDB); 1.32 + 1.33 +const BUILT_IN_NICK_PREFIX = "Builtin Object Token:"; 1.34 +const SHA1_PREFIX = "sha1/"; 1.35 +const SHA256_PREFIX = "sha256/"; 1.36 +const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_"; 1.37 + 1.38 +// Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable) 1.39 +const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14; 1.40 + 1.41 +const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" + 1.42 +" * License, v. 2.0. If a copy of the MPL was not distributed with this\n" + 1.43 +" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" + 1.44 +"\n" + 1.45 +"/*****************************************************************************/\n" + 1.46 +"/* This is an automatically generated file. If you're not */\n" + 1.47 +"/* PublicKeyPinningService.cpp, you shouldn't be #including it. */\n" + 1.48 +"/*****************************************************************************/\n" + 1.49 +"#include <stdint.h>" + 1.50 +"\n"; 1.51 + 1.52 +const DOMAINHEADER = "/* Domainlist */\n" + 1.53 + "struct TransportSecurityPreload {\n" + 1.54 + " const char* mHost;\n" + 1.55 + " const bool mIncludeSubdomains;\n" + 1.56 + " const bool mTestMode;\n" + 1.57 + " const bool mIsMoz;\n" + 1.58 + " const int32_t mId;\n" + 1.59 + " const StaticPinset *pinset;\n" + 1.60 + "};\n\n"; 1.61 + 1.62 +const PINSETDEF = "/* Pinsets are each an ordered list by the actual value of the fingerprint */\n" + 1.63 + "struct StaticFingerprints {\n" + 1.64 + " const size_t size;\n" + 1.65 + " const char* const* data;\n" + 1.66 + "};\n\n" + 1.67 + "struct StaticPinset {\n" + 1.68 + " const StaticFingerprints* sha1;\n" + 1.69 + " const StaticFingerprints* sha256;\n" + 1.70 + "};\n\n"; 1.71 + 1.72 +// Command-line arguments 1.73 +var gStaticPins = parseJson(arguments[0]); 1.74 +var gTestCertFile = arguments[1]; 1.75 + 1.76 +// Open the output file. 1.77 +let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); 1.78 +file.initWithPath(arguments[2]); 1.79 +let gFileOutputStream = FileUtils.openSafeFileOutputStream(file); 1.80 + 1.81 +function writeString(string) { 1.82 + gFileOutputStream.write(string, string.length); 1.83 +} 1.84 + 1.85 +function readFileToString(filename) { 1.86 + let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); 1.87 + file.initWithPath(filename); 1.88 + let stream = Cc["@mozilla.org/network/file-input-stream;1"] 1.89 + .createInstance(Ci.nsIFileInputStream); 1.90 + stream.init(file, -1, 0, 0); 1.91 + let buf = NetUtil.readInputStreamToString(stream, stream.available()); 1.92 + return buf; 1.93 +} 1.94 + 1.95 +function stripComments(buf) { 1.96 + var lines = buf.split("\n"); 1.97 + let entryRegex = /^\s*\/\//; 1.98 + let data = ""; 1.99 + for (let i = 0; i < lines.length; ++i) { 1.100 + let match = entryRegex.exec(lines[i]); 1.101 + if (!match) { 1.102 + data = data + lines[i]; 1.103 + } 1.104 + } 1.105 + return data; 1.106 +} 1.107 + 1.108 +function isBuiltinToken(tokenName) { 1.109 + return tokenName == "Builtin Object Token"; 1.110 +} 1.111 + 1.112 +function isCertBuiltIn(cert) { 1.113 + let tokenNames = cert.getAllTokenNames({}); 1.114 + if (!tokenNames) { 1.115 + return false; 1.116 + } 1.117 + if (tokenNames.some(isBuiltinToken)) { 1.118 + return true; 1.119 + } 1.120 + return false; 1.121 +} 1.122 + 1.123 +function download(filename) { 1.124 + var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] 1.125 + .createInstance(Ci.nsIXMLHttpRequest); 1.126 + req.open("GET", filename, false); // doing the request synchronously 1.127 + try { 1.128 + req.send(); 1.129 + } 1.130 + catch (e) { 1.131 + throw "ERROR: problem downloading '" + filename + "': " + e; 1.132 + } 1.133 + 1.134 + if (req.status != 200) { 1.135 + throw("ERROR: problem downloading '" + filename + "': status " + 1.136 + req.status); 1.137 + } 1.138 + return req.responseText; 1.139 +} 1.140 + 1.141 +function downloadAsJson(filename) { 1.142 + // we have to filter out '//' comments 1.143 + var result = download(filename).replace(/\/\/[^\n]*\n/g, ""); 1.144 + var data = null; 1.145 + try { 1.146 + data = JSON.parse(result); 1.147 + } 1.148 + catch (e) { 1.149 + throw "ERROR: could not parse data from '" + filename + "': " + e; 1.150 + } 1.151 + return data; 1.152 +} 1.153 + 1.154 +// Returns a Subject Public Key Digest from the given pem, if it exists. 1.155 +function getSKDFromPem(pem) { 1.156 + let cert = gCertDB.constructX509FromBase64(pem, pem.length); 1.157 + return cert.sha256SubjectPublicKeyInfoDigest; 1.158 +} 1.159 + 1.160 +// Downloads the static certs file and tries to map Google Chrome nicknames 1.161 +// to Mozilla nicknames, as well as storing any hashes for pins for which we 1.162 +// don't have root PEMs. Each entry consists of a line containing the name of 1.163 +// the pin followed either by a hash in the format "sha1/" + base64(hash), or 1.164 +// a PEM encoded certificate. For certificates that we have in our database, 1.165 +// return a map of Google's nickname to ours. For ones that aren't return a 1.166 +// map of Google's nickname to sha1 values. This code is modeled after agl's 1.167 +// https://github.com/agl/transport-security-state-generate, which doesn't 1.168 +// live in the Chromium repo because go is not an official language in 1.169 +// Chromium. 1.170 +// For all of the entries in this file: 1.171 +// - If the entry has a hash format, find the Mozilla pin name (cert nickname) 1.172 +// and stick the hash into certSKDToName 1.173 +// - If the entry has a PEM format, parse the PEM, find the Mozilla pin name 1.174 +// and stick the hash in certSKDToName 1.175 +// We MUST be able to find a corresponding cert nickname for the Chrome names, 1.176 +// otherwise we skip all pinsets referring to that Chrome name. 1.177 +function downloadAndParseChromeCerts(filename, certSKDToName) { 1.178 + // Prefixes that we care about. 1.179 + const BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; 1.180 + const END_CERT = "-----END CERTIFICATE-----"; 1.181 + 1.182 + // Parsing states. 1.183 + const PRE_NAME = 0; 1.184 + const POST_NAME = 1; 1.185 + const IN_CERT = 2; 1.186 + let state = PRE_NAME; 1.187 + 1.188 + let lines = download(filename).split("\n"); 1.189 + let name = ""; 1.190 + let pemCert = ""; 1.191 + let hash = ""; 1.192 + let chromeNameToHash = {}; 1.193 + let chromeNameToMozName = {} 1.194 + for (let i = 0; i < lines.length; ++i) { 1.195 + let line = lines[i]; 1.196 + // Skip comments and newlines. 1.197 + if (line.length == 0 || line[0] == '#') { 1.198 + continue; 1.199 + } 1.200 + switch(state) { 1.201 + case PRE_NAME: 1.202 + chromeName = line; 1.203 + state = POST_NAME; 1.204 + break; 1.205 + case POST_NAME: 1.206 + if (line.startsWith(SHA1_PREFIX) || 1.207 + line.startsWith(SHA256_PREFIX)) { 1.208 + if (line.startsWith(SHA1_PREFIX)) { 1.209 + hash = line.substring(SHA1_PREFIX.length); 1.210 + } else if (line.startsWith(SHA256_PREFIX)) { 1.211 + hash = line.substring(SHA256_PREFIX); 1.212 + } 1.213 + // Store the entire prefixed hash, so we can disambiguate sha1 from 1.214 + // sha256 later. 1.215 + chromeNameToHash[chromeName] = line; 1.216 + certNameToSKD[chromeName] = hash; 1.217 + certSKDToName[hash] = chromeName; 1.218 + state = PRE_NAME; 1.219 + } else if (line.startsWith(BEGIN_CERT)) { 1.220 + state = IN_CERT; 1.221 + } else { 1.222 + throw "ERROR: couldn't parse Chrome certificate file " + line; 1.223 + } 1.224 + break; 1.225 + case IN_CERT: 1.226 + if (line.startsWith(END_CERT)) { 1.227 + state = PRE_NAME; 1.228 + hash = getSKDFromPem(pemCert); 1.229 + pemCert = ""; 1.230 + if (hash in certSKDToName) { 1.231 + mozName = certSKDToName[hash]; 1.232 + } else { 1.233 + // Not one of our built-in certs. Prefix the name with 1.234 + // GOOGLE_PIN_. 1.235 + mozName = GOOGLE_PIN_PREFIX + chromeName; 1.236 + dump("Can't find hash in builtin certs for Chrome nickname " + 1.237 + chromeName + ", inserting " + mozName + "\n"); 1.238 + certSKDToName[hash] = mozName; 1.239 + certNameToSKD[mozName] = hash; 1.240 + } 1.241 + chromeNameToMozName[chromeName] = mozName; 1.242 + } else { 1.243 + pemCert += line; 1.244 + } 1.245 + break; 1.246 + default: 1.247 + throw "ERROR: couldn't parse Chrome certificate file " + line; 1.248 + } 1.249 + } 1.250 + return [ chromeNameToHash, chromeNameToMozName ]; 1.251 +} 1.252 + 1.253 +// We can only import pinsets from chrome if for every name in the pinset: 1.254 +// - We have a hash from Chrome's static certificate file 1.255 +// - We have a builtin cert 1.256 +// If the pinset meets these requirements, we store a map array of pinset 1.257 +// objects: 1.258 +// { 1.259 +// pinset_name : { 1.260 +// // Array of names with entries in certNameToSKD 1.261 +// sha1_hashes: [], 1.262 +// sha256_hashes: [] 1.263 +// } 1.264 +// } 1.265 +// and an array of imported pinset entries: 1.266 +// { name: string, include_subdomains: boolean, test_mode: boolean, 1.267 +// pins: pinset_name } 1.268 +function downloadAndParseChromePins(filename, 1.269 + chromeNameToHash, 1.270 + chromeNameToMozName, 1.271 + certNameToSKD, 1.272 + certSKDToName) { 1.273 + let chromePreloads = downloadAsJson(filename); 1.274 + let chromePins = chromePreloads.pinsets; 1.275 + let chromeImportedPinsets = {}; 1.276 + let chromeImportedEntries = []; 1.277 + 1.278 + chromePins.forEach(function(pin) { 1.279 + let valid = true; 1.280 + let pinset = { name: pin.name, sha1_hashes: [], sha256_hashes: [] }; 1.281 + // Translate the Chrome pinset format to ours 1.282 + pin.static_spki_hashes.forEach(function(name) { 1.283 + if (name in chromeNameToHash) { 1.284 + let hash = chromeNameToHash[name]; 1.285 + if (hash.startsWith(SHA1_PREFIX)) { 1.286 + hash = hash.substring(SHA1_PREFIX.length); 1.287 + pinset.sha1_hashes.push(certSKDToName[hash]); 1.288 + } else if (hash.startsWith(SHA256_PREFIX)) { 1.289 + hash = hash.substring(SHA256_PREFIX.length); 1.290 + pinset.sha256_hashes.push(certSKDToName[hash]); 1.291 + } else { 1.292 + throw("Unsupported hash type: " + chromeNameToHash[name]); 1.293 + } 1.294 + // We should have already added hashes for all of these when we 1.295 + // imported the certificate file. 1.296 + if (!certNameToSKD[name]) { 1.297 + throw("No hash for name: " + name); 1.298 + } 1.299 + } else if (name in chromeNameToMozName) { 1.300 + pinset.sha256_hashes.push(chromeNameToMozName[name]); 1.301 + } else { 1.302 + dump("Skipping Chrome pinset " + pinset.name + ", couldn't find " + 1.303 + "builtin " + name + " from cert file\n"); 1.304 + valid = false; 1.305 + } 1.306 + }); 1.307 + if (valid) { 1.308 + chromeImportedPinsets[pinset.name] = pinset; 1.309 + } 1.310 + }); 1.311 + 1.312 + // Grab the domain entry lists. Chrome's entry format is similar to 1.313 + // ours, except theirs includes a HSTS mode. 1.314 + const cData = gStaticPins.chromium_data; 1.315 + let entries = chromePreloads.entries; 1.316 + entries.forEach(function(entry) { 1.317 + let pinsetName = cData.substitute_pinsets[entry.pins]; 1.318 + if (!pinsetName) { 1.319 + pinsetName = entry.pins; 1.320 + } 1.321 + let isProductionDomain = 1.322 + (cData.production_domains.indexOf(entry.name) != -1); 1.323 + let isProductionPinset = 1.324 + (cData.production_pinsets.indexOf(pinsetName) != -1); 1.325 + let excludeDomain = 1.326 + (cData.exclude_domains.indexOf(entry.name) != -1); 1.327 + let isTestMode = !isProductionPinset && !isProductionDomain; 1.328 + if (entry.pins && !excludeDomain && chromeImportedPinsets[entry.pins]) { 1.329 + chromeImportedEntries.push({ 1.330 + name: entry.name, 1.331 + include_subdomains: entry.include_subdomains, 1.332 + test_mode: isTestMode, 1.333 + is_moz: false, 1.334 + pins: pinsetName }); 1.335 + } 1.336 + }); 1.337 + return [ chromeImportedPinsets, chromeImportedEntries ]; 1.338 +} 1.339 + 1.340 +// Returns a pair of maps [certNameToSKD, certSKDToName] between cert 1.341 +// nicknames and digests of the SPKInfo for the mozilla trust store 1.342 +function loadNSSCertinfo(derTestFile, extraCertificates) { 1.343 + let allCerts = gCertDB.getCerts(); 1.344 + let enumerator = allCerts.getEnumerator(); 1.345 + let certNameToSKD = {}; 1.346 + let certSKDToName = {}; 1.347 + while (enumerator.hasMoreElements()) { 1.348 + let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert); 1.349 + if (!isCertBuiltIn(cert)) { 1.350 + continue; 1.351 + } 1.352 + let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length); 1.353 + let SKD = cert.sha256SubjectPublicKeyInfoDigest; 1.354 + certNameToSKD[name] = SKD; 1.355 + certSKDToName[SKD] = name; 1.356 + } 1.357 + 1.358 + for (let cert of extraCertificates) { 1.359 + let name = cert.commonName; 1.360 + let SKD = cert.sha256SubjectPublicKeyInfoDigest; 1.361 + certNameToSKD[name] = SKD; 1.362 + certSKDToName[SKD] = name; 1.363 + } 1.364 + 1.365 + { 1.366 + // A certificate for *.example.com. 1.367 + let der = readFileToString(derTestFile); 1.368 + let testCert = gCertDB.constructX509(der, der.length); 1.369 + // We can't include this cert in the previous loop, because it skips 1.370 + // non-builtin certs and the nickname is not built-in to the cert. 1.371 + let name = "End Entity Test Cert"; 1.372 + let SKD = testCert.sha256SubjectPublicKeyInfoDigest; 1.373 + certNameToSKD[name] = SKD; 1.374 + certSKDToName[SKD] = name; 1.375 + } 1.376 + return [certNameToSKD, certSKDToName]; 1.377 +} 1.378 + 1.379 +function parseJson(filename) { 1.380 + let json = stripComments(readFileToString(filename)); 1.381 + return JSON.parse(json); 1.382 +} 1.383 + 1.384 +function nameToAlias(certName) { 1.385 + // change the name to a string valid as a c identifier 1.386 + // remove non-ascii characters 1.387 + certName = certName.replace( /[^[:ascii:]]/g, "_"); 1.388 + // replace non word characters 1.389 + certName = certName.replace(/[^A-Za-z0-9]/g ,"_"); 1.390 + 1.391 + return "k" + certName + "Fingerprint"; 1.392 +} 1.393 + 1.394 +function compareByName (a, b) { 1.395 + return a.name.localeCompare(b.name); 1.396 +} 1.397 + 1.398 +function genExpirationTime() { 1.399 + let now = new Date(); 1.400 + let nowMillis = now.getTime(); 1.401 + let expirationMillis = nowMillis + (PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000); 1.402 + let expirationMicros = expirationMillis * 1000; 1.403 + return "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" + 1.404 + expirationMicros +");\n"; 1.405 +} 1.406 + 1.407 +function writeFullPinset(certNameToSKD, certSKDToName, pinset) { 1.408 + // We aren't guaranteed to have sha1 hashes in our own imported pins. 1.409 + let prefix = "kPinset_" + pinset.name; 1.410 + let sha1Name = "nullptr"; 1.411 + let sha256Name = "nullptr"; 1.412 + if (pinset.sha1_hashes && pinset.sha1_hashes.length > 0) { 1.413 + writeFingerprints(certNameToSKD, certSKDToName, pinset.name, 1.414 + pinset.sha1_hashes, "sha1"); 1.415 + sha1Name = "&" + prefix + "_sha1"; 1.416 + } 1.417 + if (pinset.sha256_hashes && pinset.sha256_hashes.length > 0) { 1.418 + writeFingerprints(certNameToSKD, certSKDToName, pinset.name, 1.419 + pinset.sha256_hashes, "sha256"); 1.420 + sha256Name = "&" + prefix + "_sha256"; 1.421 + } 1.422 + writeString("static const StaticPinset " + prefix + " = {\n" + 1.423 + " " + sha1Name + ",\n " + sha256Name + "\n};\n\n"); 1.424 +} 1.425 + 1.426 +function writeFingerprints(certNameToSKD, certSKDToName, name, hashes, type) { 1.427 + let varPrefix = "kPinset_" + name + "_" + type; 1.428 + writeString("static const char* " + varPrefix + "_Data[] = {\n"); 1.429 + let SKDList = []; 1.430 + for (let certName of hashes) { 1.431 + if (!(certName in certNameToSKD)) { 1.432 + throw "Can't find " + certName + " in certNameToSKD"; 1.433 + } 1.434 + SKDList.push(certNameToSKD[certName]); 1.435 + } 1.436 + for (let skd of SKDList.sort()) { 1.437 + writeString(" " + nameToAlias(certSKDToName[skd]) + ",\n"); 1.438 + } 1.439 + if (hashes.length == 0) { 1.440 + // ANSI C requires that an initialiser list be non-empty. 1.441 + writeString(" 0\n"); 1.442 + } 1.443 + writeString("};\n"); 1.444 + writeString("static const StaticFingerprints " + varPrefix + " = {\n " + 1.445 + "sizeof(" + varPrefix + "_Data) / sizeof(const char*),\n " + varPrefix + 1.446 + "_Data\n};\n\n"); 1.447 +} 1.448 + 1.449 +function writeEntry(entry) { 1.450 + let printVal = " { \"" + entry.name + "\",\ "; 1.451 + if (entry.include_subdomains) { 1.452 + printVal += "true, "; 1.453 + } else { 1.454 + printVal += "false, "; 1.455 + } 1.456 + // Default to test mode if not specified. 1.457 + let testMode = true; 1.458 + if (entry.hasOwnProperty("test_mode")) { 1.459 + testMode = entry.test_mode; 1.460 + } 1.461 + if (testMode) { 1.462 + printVal += "true, "; 1.463 + } else { 1.464 + printVal += "false, "; 1.465 + } 1.466 + if (entry.is_moz || (entry.pins == "mozilla")) { 1.467 + printVal += "true, "; 1.468 + } else { 1.469 + printVal += "false, "; 1.470 + } 1.471 + if (entry.id >= 256) { 1.472 + throw("Not enough buckets in histogram"); 1.473 + } 1.474 + if (entry.id >= 0) { 1.475 + printVal += entry.id + ", "; 1.476 + } else { 1.477 + printVal += "-1, "; 1.478 + } 1.479 + printVal += "&kPinset_" + entry.pins; 1.480 + printVal += " },\n"; 1.481 + writeString(printVal); 1.482 +} 1.483 + 1.484 +function writeDomainList(chromeImportedEntries) { 1.485 + writeString("/* Sort hostnames for binary search. */\n"); 1.486 + writeString("static const TransportSecurityPreload " + 1.487 + "kPublicKeyPinningPreloadList[] = {\n"); 1.488 + let count = 0; 1.489 + let sortedEntries = gStaticPins.entries; 1.490 + sortedEntries.push.apply(sortedEntries, chromeImportedEntries); 1.491 + for (let entry of sortedEntries.sort(compareByName)) { 1.492 + count++; 1.493 + writeEntry(entry); 1.494 + } 1.495 + writeString("};\n"); 1.496 + 1.497 + writeString("\n// Pinning Preload List Length = " + count + ";\n"); 1.498 + writeString("\nstatic const int32_t kUnknownId = -1;\n"); 1.499 +} 1.500 + 1.501 +function writeFile(certNameToSKD, certSKDToName, 1.502 + chromeImportedPinsets, chromeImportedEntries) { 1.503 + // Compute used pins from both Chrome's and our pinsets, so we can output 1.504 + // them later. 1.505 + usedFingerprints = {}; 1.506 + gStaticPins.pinsets.forEach(function(pinset) { 1.507 + // We aren't guaranteed to have sha1_hashes in our own JSON. 1.508 + if (pinset.sha1_hashes) { 1.509 + pinset.sha1_hashes.forEach(function(name) { 1.510 + usedFingerprints[name] = true; 1.511 + }); 1.512 + } 1.513 + if (pinset.sha256_hashes) { 1.514 + pinset.sha256_hashes.forEach(function(name) { 1.515 + usedFingerprints[name] = true; 1.516 + }); 1.517 + } 1.518 + }); 1.519 + for (let key in chromeImportedPinsets) { 1.520 + let pinset = chromeImportedPinsets[key]; 1.521 + pinset.sha1_hashes.forEach(function(name) { 1.522 + usedFingerprints[name] = true; 1.523 + }); 1.524 + pinset.sha256_hashes.forEach(function(name) { 1.525 + usedFingerprints[name] = true; 1.526 + }); 1.527 + } 1.528 + 1.529 + writeString(FILE_HEADER); 1.530 + 1.531 + // Write actual fingerprints. 1.532 + Object.keys(usedFingerprints).sort().forEach(function(certName) { 1.533 + if (certName) { 1.534 + writeString("/* " + certName + " */\n"); 1.535 + writeString("static const char " + nameToAlias(certName) + "[] =\n"); 1.536 + writeString(" \"" + certNameToSKD[certName] + "\";\n"); 1.537 + writeString("\n"); 1.538 + } 1.539 + }); 1.540 + 1.541 + // Write the pinsets 1.542 + writeString(PINSETDEF); 1.543 + writeString("/* PreloadedHPKPins.json pinsets */\n"); 1.544 + gStaticPins.pinsets.sort(compareByName).forEach(function(pinset) { 1.545 + writeFullPinset(certNameToSKD, certSKDToName, pinset); 1.546 + }); 1.547 + writeString("/* Chrome static pinsets */\n"); 1.548 + for (let key in chromeImportedPinsets) { 1.549 + writeFullPinset(certNameToSKD, certSKDToName, chromeImportedPinsets[key]); 1.550 + } 1.551 + 1.552 + // Write the domainlist entries. 1.553 + writeString(DOMAINHEADER); 1.554 + writeDomainList(chromeImportedEntries); 1.555 + writeString("\n"); 1.556 + writeString(genExpirationTime()); 1.557 +} 1.558 + 1.559 +function loadExtraCertificates(certStringList) { 1.560 + let constructedCerts = []; 1.561 + for (let certString of certStringList) { 1.562 + constructedCerts.push(gCertDB.constructX509FromBase64(certString)); 1.563 + } 1.564 + return constructedCerts; 1.565 +} 1.566 + 1.567 +let extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates); 1.568 +let [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(gTestCertFile, 1.569 + extraCertificates); 1.570 +let [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts( 1.571 + gStaticPins.chromium_data.cert_file_url, certSKDToName); 1.572 +let [ chromeImportedPinsets, chromeImportedEntries ] = 1.573 + downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url, 1.574 + chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName); 1.575 + 1.576 +writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets, 1.577 + chromeImportedEntries); 1.578 + 1.579 +FileUtils.closeSafeFileOutputStream(gFileOutputStream);