From 55aa6ce7b3eca4f0afe2ac6e63dee3ce7de800a3 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Sat, 28 Mar 2020 01:06:56 +0100 Subject: Issue #1280 - Part 1: Remove HPKP components. This also removes leftover plumbing for storing preload information in SiteSecurityService since no service still uses it. --- security/manager/tools/PreloadedHPKPins.json | 222 ---------- security/manager/tools/genHPKPStaticPins.js | 630 --------------------------- 2 files changed, 852 deletions(-) delete mode 100644 security/manager/tools/PreloadedHPKPins.json delete mode 100644 security/manager/tools/genHPKPStaticPins.js (limited to 'security/manager/tools') diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json deleted file mode 100644 index d9c394a1d..000000000 --- a/security/manager/tools/PreloadedHPKPins.json +++ /dev/null @@ -1,222 +0,0 @@ -// -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- -// 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/. - -// The top-level element is a dictionary with two keys: "pinsets" maps details -// of certificate pinning to a name and "entries" contains the HPKP details for -// each host. -// -// "pinsets" is a list of objects. Each object has the following members: -// name: (string) the name of the pinset -// sha256_hashes: (list of strings) the set of allowed SPKIs hashes -// -// For a given pinset, a certificate is accepted if at least one of the -// Subject Public Key Infos (SPKIs) is found in the chain. SPKIs are specified -// as names, which must match up with the name given in the Mozilla root store. -// -// "entries" is a list of objects. Each object has the following members: -// name: (string) the DNS name of the host in question -// include_subdomains: (optional bool) whether subdomains of |name| are also covered -// pins: (string) the |name| member of an object in |pinsets| -// -// "extra_certs" is a list of base64-encoded certificates. These are used in -// pinsets that reference certificates not in our root program (for example, -// Facebook). - -// equifax -> aus3 -// Geotrust Primary -> www.mozilla.org -// Geotrust Global -> *. addons.mozilla.org -{ - "chromium_data" : { - "cert_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.pins?format=TEXT", - "json_file_url": "https://chromium.googlesource.com/chromium/src/net/+/master/http/transport_security_state_static.json?format=TEXT", - "substitute_pinsets": { - // Use the larger google_root_pems pinset instead of google - "google": "google_root_pems" - }, - "production_pinsets": [ - "google_root_pems", - "facebook" - ], - "production_domains": [ - // Chrome's test domains. - "pinningtest.appspot.com", - "pinning-test.badssl.com", - // Dropbox - "dropbox.com", - "www.dropbox.com", - // Twitter - "api.twitter.com", - "business.twitter.com", - "dev.twitter.com", - "mobile.twitter.com", - "oauth.twitter.com", - "platform.twitter.com", - "twimg.com", - "www.twitter.com", - // Tor - "torproject.org", - "blog.torproject.org", - "check.torproject.org", - "dist.torproject.org", - "www.torproject.org", - // SpiderOak - "spideroak.com" - ], - "exclude_domains" : [ - // Chrome's entry for twitter.com doesn't include subdomains, so replace - // it with our own entry below which also uses an expanded pinset. - "twitter.com" - ] - }, - "pinsets": [ - { - // From bug 772756, mozilla uses GeoTrust, Digicert and Thawte. Our - // cdn sites use Verisign and Baltimore. We exclude 1024-bit root certs - // from all providers. geotrust ca info: - // http://www.geotrust.com/resources/root-certificates/index.html - "name": "mozilla", - "sha256_hashes": [ - "Baltimore CyberTrust Root", - "DigiCert Assured ID Root CA", - "DigiCert Global Root CA", - "DigiCert High Assurance EV Root CA", - "GeoTrust Global CA", - "GeoTrust Global CA 2", - "GeoTrust Primary Certification Authority", - "GeoTrust Primary Certification Authority - G2", - "GeoTrust Primary Certification Authority - G3", - "GeoTrust Universal CA", - "GeoTrust Universal CA 2", - "thawte Primary Root CA", - "thawte Primary Root CA - G2", - "thawte Primary Root CA - G3", - "Verisign Class 1 Public Primary Certification Authority - G3", - "Verisign Class 2 Public Primary Certification Authority - G3", - "Verisign Class 3 Public Primary Certification Authority - G3", - "VeriSign Class 3 Public Primary Certification Authority - G4", - "VeriSign Class 3 Public Primary Certification Authority - G5", - // "Verisign Class 4 Public Primary Certification Authority - G3", - "VeriSign Universal Root Certification Authority" - ] - }, - { - "name": "mozilla_services", - "sha256_hashes": [ - "DigiCert Global Root CA" - ] - }, - // For pinning tests on pinning.example.com, the certificate must be 'End - // Entity Test Cert' - { - "name": "mozilla_test", - "sha256_hashes": [ - "End Entity Test Cert" - ] - }, - // Google's root PEMs. Chrome pins only to their intermediate certs, but - // they'd like us to be more liberal. For the initial list, we are using - // the certs from http://pki.google.com/roots.pem. - // We have no built-in for commented out CAs. - { - "name": "google_root_pems", - "sha256_hashes": [ - "AddTrust External Root", - "AddTrust Low-Value Services Root", - "AddTrust Public Services Root", - "AddTrust Qualified Certificates Root", - "AffirmTrust Commercial", - "AffirmTrust Networking", - "AffirmTrust Premium", - "AffirmTrust Premium ECC", - "Baltimore CyberTrust Root", - "Comodo AAA Services root", - "COMODO Certification Authority", - "COMODO ECC Certification Authority", - "COMODO RSA Certification Authority", - "Comodo Secure Services root", - "Comodo Trusted Services root", - "Cybertrust Global Root", - "DigiCert Assured ID Root CA", - "DigiCert Assured ID Root G2", - "DigiCert Assured ID Root G3", - "DigiCert Global Root CA", - "DigiCert Global Root G2", - "DigiCert Global Root G3", - "DigiCert High Assurance EV Root CA", - "DigiCert Trusted Root G4", - "Entrust Root Certification Authority", - "Entrust Root Certification Authority - EC1", - "Entrust Root Certification Authority - G2", - "Entrust.net Premium 2048 Secure Server CA", - // "Equifax Secure Certificate Authority", - "GeoTrust Global CA", - "GeoTrust Global CA 2", - "GeoTrust Primary Certification Authority", - "GeoTrust Primary Certification Authority - G2", - "GeoTrust Primary Certification Authority - G3", - "GeoTrust Universal CA", - "GeoTrust Universal CA 2", - "GlobalSign ECC Root CA - R4", - "GlobalSign ECC Root CA - R5", - "GlobalSign Root CA", - "GlobalSign Root CA - R2", - "GlobalSign Root CA - R3", - "Go Daddy Class 2 CA", - "Go Daddy Root Certificate Authority - G2", - "Starfield Class 2 CA", - "Starfield Root Certificate Authority - G2", - "thawte Primary Root CA", - "thawte Primary Root CA - G2", - "thawte Primary Root CA - G3", - "USERTrust ECC Certification Authority", - "USERTrust RSA Certification Authority", - "UTN USERFirst Hardware Root CA", - "Verisign Class 3 Public Primary Certification Authority - G3", - "VeriSign Class 3 Public Primary Certification Authority - G4", - "VeriSign Class 3 Public Primary Certification Authority - G5", - "VeriSign Universal Root Certification Authority" - ] - } - ], - - "entries": [ - // Only domains that are operationally crucial to Firefox can have per-host - // telemetry reporting (the "id") field - { "name": "addons.mozilla.org", "include_subdomains": true, - "pins": "mozilla", "test_mode": false, "id": 1 }, - { "name": "addons.mozilla.net", "include_subdomains": true, - "pins": "mozilla", "test_mode": false, "id": 2 }, - { "name": "aus4.mozilla.org", "include_subdomains": true, - "pins": "mozilla", "test_mode": true, "id": 3 }, - { "name": "accounts.firefox.com", "include_subdomains": true, - "pins": "mozilla_services", "test_mode": false, "id": 4 }, - { "name": "api.accounts.firefox.com", "include_subdomains": true, - "pins": "mozilla_services", "test_mode": false, "id": 5 }, - { "name": "cdn.mozilla.net", "include_subdomains": true, - "pins": "mozilla", "test_mode": false }, - { "name": "cdn.mozilla.org", "include_subdomains": true, - "pins": "mozilla", "test_mode": false }, - { "name": "services.mozilla.com", "include_subdomains": true, - "pins": "mozilla_services", "test_mode": false, "id": 6 }, - { "name": "include-subdomains.pinning.example.com", - "include_subdomains": true, "pins": "mozilla_test", - "test_mode": false }, - // Example domain to collect per-host stats for telemetry tests. - { "name": "exclude-subdomains.pinning.example.com", - "include_subdomains": false, "pins": "mozilla_test", - "test_mode": false, "id": 0 }, - { "name": "test-mode.pinning.example.com", "include_subdomains": true, - "pins": "mozilla_test", "test_mode": true }, - // Expand twitter's pinset to include all of *.twitter.com and use - // twitterCDN. More specific rules take precedence because we search for - // exact domain name first. - { "name": "twitter.com", "include_subdomains": true, - "pins": "twitterCDN", "test_mode": false }, - { "name": "aus5.mozilla.org", "include_subdomains": true, - "pins": "mozilla", "test_mode": true, "id": 7 } - ], - - "extra_certificates": [] -} diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js deleted file mode 100644 index f2b9dbdda..000000000 --- a/security/manager/tools/genHPKPStaticPins.js +++ /dev/null @@ -1,630 +0,0 @@ -/* 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/. */ - -// How to run this file: -// 1. [obtain firefox source code] -// 2. [build/obtain firefox binaries] -// 3. run `[path to]/run-mozilla.sh [path to]/xpcshell \ -// [path to]/genHPKPStaticpins.js \ -// [absolute path to]/PreloadedHPKPins.json \ -// [an unused argument - see bug 1205406] \ -// [absolute path to]/StaticHPKPins.h -"use strict"; - -if (arguments.length != 3) { - throw new Error("Usage: genHPKPStaticPins.js " + - " " + - " " + - ""); -} - -var { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components; - -var { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {}); -var { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {}); -var { Services } = Cu.import("resource://gre/modules/Services.jsm", {}); - -var gCertDB = Cc["@mozilla.org/security/x509certdb;1"] - .getService(Ci.nsIX509CertDB); - -const BUILT_IN_NICK_PREFIX = "Builtin Object Token:"; -const SHA256_PREFIX = "sha256/"; -const GOOGLE_PIN_PREFIX = "GOOGLE_PIN_"; - -// Pins expire in 14 weeks (6 weeks on Beta + 8 weeks on stable) -const PINNING_MINIMUM_REQUIRED_MAX_AGE = 60 * 60 * 24 * 7 * 14; - -const FILE_HEADER = "/* This Source Code Form is subject to the terms of the Mozilla Public\n" + -" * License, v. 2.0. If a copy of the MPL was not distributed with this\n" + -" * file, You can obtain one at http://mozilla.org/MPL/2.0/. */\n" + -"\n" + -"/*****************************************************************************/\n" + -"/* This is an automatically generated file. If you're not */\n" + -"/* PublicKeyPinningService.cpp, you shouldn't be #including it. */\n" + -"/*****************************************************************************/\n" + -"#include " + -"\n"; - -const DOMAINHEADER = "/* Domainlist */\n" + - "struct TransportSecurityPreload {\n" + - " const char* mHost;\n" + - " const bool mIncludeSubdomains;\n" + - " const bool mTestMode;\n" + - " const bool mIsMoz;\n" + - " const int32_t mId;\n" + - " const StaticFingerprints* pinset;\n" + - "};\n\n"; - -const PINSETDEF = "/* Pinsets are each an ordered list by the actual value of the fingerprint */\n" + - "struct StaticFingerprints {\n" + - " const size_t size;\n" + - " const char* const* data;\n" + - "};\n\n"; - -// Command-line arguments -var gStaticPins = parseJson(arguments[0]); - -// arguments[1] is ignored for now. See bug 1205406. - -// Open the output file. -var file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); -file.initWithPath(arguments[2]); -var gFileOutputStream = FileUtils.openSafeFileOutputStream(file); - -function writeString(string) { - gFileOutputStream.write(string, string.length); -} - -function readFileToString(filename) { - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile); - file.initWithPath(filename); - let stream = Cc["@mozilla.org/network/file-input-stream;1"] - .createInstance(Ci.nsIFileInputStream); - stream.init(file, -1, 0, 0); - let buf = NetUtil.readInputStreamToString(stream, stream.available()); - return buf; -} - -function stripComments(buf) { - let lines = buf.split("\n"); - let entryRegex = /^\s*\/\//; - let data = ""; - for (let i = 0; i < lines.length; ++i) { - let match = entryRegex.exec(lines[i]); - if (!match) { - data = data + lines[i]; - } - } - return data; -} - -function isBuiltinToken(tokenName) { - return tokenName == "Builtin Object Token"; -} - -function isCertBuiltIn(cert) { - let tokenNames = cert.getAllTokenNames({}); - if (!tokenNames) { - return false; - } - if (tokenNames.some(isBuiltinToken)) { - return true; - } - return false; -} - -function download(filename) { - let req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(Ci.nsIXMLHttpRequest); - req.open("GET", filename, false); // doing the request synchronously - try { - req.send(); - } - catch (e) { - throw new Error(`ERROR: problem downloading '${filename}': ${e}`); - } - - if (req.status != 200) { - throw new Error("ERROR: problem downloading '" + filename + "': status " + - req.status); - } - - let resultDecoded; - try { - resultDecoded = atob(req.responseText); - } - catch (e) { - throw new Error("ERROR: could not decode data as base64 from '" + filename + - "': " + e); - } - return resultDecoded; -} - -function downloadAsJson(filename) { - // we have to filter out '//' comments, while not mangling the json - let result = download(filename).replace(/^(\s*)?\/\/[^\n]*\n/mg, ""); - let data = null; - try { - data = JSON.parse(result); - } - catch (e) { - throw new Error("ERROR: could not parse data from '" + filename + "': " + e); - } - return data; -} - -// Returns a Subject Public Key Digest from the given pem, if it exists. -function getSKDFromPem(pem) { - let cert = gCertDB.constructX509FromBase64(pem, pem.length); - return cert.sha256SubjectPublicKeyInfoDigest; -} - -/** - * Hashes |input| using the SHA-256 algorithm in the following manner: - * btoa(sha256(atob(input))) - * - * @argument {String} input Base64 string to decode and return the hash of. - * @returns {String} Base64 encoded SHA-256 hash. - */ -function sha256Base64(input) { - let decodedValue; - try { - decodedValue = atob(input); - } - catch (e) { - throw new Error(`ERROR: could not decode as base64: '${input}': ${e}`); - } - - // Convert |decodedValue| to an array so that it can be hashed by the - // nsICryptoHash instance below. - // In most cases across the code base, convertToByteArray() of - // nsIScriptableUnicodeConverter is used to do this, but the method doesn't - // seem to work here. - let data = []; - for (let i = 0; i < decodedValue.length; i++) { - data[i] = decodedValue.charCodeAt(i); - } - - let hasher = Cc["@mozilla.org/security/hash;1"] - .createInstance(Ci.nsICryptoHash); - hasher.init(hasher.SHA256); - hasher.update(data, data.length); - - // true is passed so that the hasher returns a Base64 encoded string. - return hasher.finish(true); -} - -// Downloads the static certs file and tries to map Google Chrome nicknames -// to Mozilla nicknames, as well as storing any hashes for pins for which we -// don't have root PEMs. Each entry consists of a line containing the name of -// the pin followed either by a hash in the format "sha256/" + base64(hash), -// a PEM encoded public key, or a PEM encoded certificate. -// For certificates that we have in our database, -// return a map of Google's nickname to ours. For ones that aren't return a -// map of Google's nickname to SHA-256 values. This code is modeled after agl's -// https://github.com/agl/transport-security-state-generate, which doesn't -// live in the Chromium repo because go is not an official language in -// Chromium. -// For all of the entries in this file: -// - If the entry has a hash format, find the Mozilla pin name (cert nickname) -// and stick the hash into certSKDToName -// - If the entry has a PEM format, parse the PEM, find the Mozilla pin name -// and stick the hash in certSKDToName -// We MUST be able to find a corresponding cert nickname for the Chrome names, -// otherwise we skip all pinsets referring to that Chrome name. -function downloadAndParseChromeCerts(filename, certNameToSKD, certSKDToName) { - // Prefixes that we care about. - const BEGIN_CERT = "-----BEGIN CERTIFICATE-----"; - const END_CERT = "-----END CERTIFICATE-----"; - const BEGIN_PUB_KEY = "-----BEGIN PUBLIC KEY-----"; - const END_PUB_KEY = "-----END PUBLIC KEY-----"; - - // Parsing states. - const PRE_NAME = 0; - const POST_NAME = 1; - const IN_CERT = 2; - const IN_PUB_KEY = 3; - let state = PRE_NAME; - - let lines = download(filename).split("\n"); - let name = ""; - let pemCert = ""; - let pemPubKey = ""; - let hash = ""; - let chromeNameToHash = {}; - let chromeNameToMozName = {}; - let chromeName; - for (let line of lines) { - // Skip comments and newlines. - if (line.length == 0 || line[0] == '#') { - continue; - } - switch (state) { - case PRE_NAME: - chromeName = line; - state = POST_NAME; - break; - case POST_NAME: - if (line.startsWith(SHA256_PREFIX)) { - hash = line.substring(SHA256_PREFIX.length); - chromeNameToHash[chromeName] = hash; - certNameToSKD[chromeName] = hash; - certSKDToName[hash] = chromeName; - state = PRE_NAME; - } else if (line.startsWith(BEGIN_CERT)) { - state = IN_CERT; - } else if (line.startsWith(BEGIN_PUB_KEY)) { - state = IN_PUB_KEY; - } else { - throw new Error("ERROR: couldn't parse Chrome certificate file " + - "line: " + line); - } - break; - case IN_CERT: - if (line.startsWith(END_CERT)) { - state = PRE_NAME; - hash = getSKDFromPem(pemCert); - pemCert = ""; - let mozName; - if (hash in certSKDToName) { - mozName = certSKDToName[hash]; - } else { - // Not one of our built-in certs. Prefix the name with - // GOOGLE_PIN_. - mozName = GOOGLE_PIN_PREFIX + chromeName; - dump("Can't find hash in builtin certs for Chrome nickname " + - chromeName + ", inserting " + mozName + "\n"); - certSKDToName[hash] = mozName; - certNameToSKD[mozName] = hash; - } - chromeNameToMozName[chromeName] = mozName; - } else { - pemCert += line; - } - break; - case IN_PUB_KEY: - if (line.startsWith(END_PUB_KEY)) { - state = PRE_NAME; - hash = sha256Base64(pemPubKey); - pemPubKey = ""; - chromeNameToHash[chromeName] = hash; - certNameToSKD[chromeName] = hash; - certSKDToName[hash] = chromeName; - } else { - pemPubKey += line; - } - break; - default: - throw new Error("ERROR: couldn't parse Chrome certificate file " + line); - } - } - return [ chromeNameToHash, chromeNameToMozName ]; -} - -// We can only import pinsets from chrome if for every name in the pinset: -// - We have a hash from Chrome's static certificate file -// - We have a builtin cert -// If the pinset meets these requirements, we store a map array of pinset -// objects: -// { -// pinset_name : { -// // Array of names with entries in certNameToSKD -// sha256_hashes: [] -// } -// } -// and an array of imported pinset entries: -// { name: string, include_subdomains: boolean, test_mode: boolean, -// pins: pinset_name } -function downloadAndParseChromePins(filename, - chromeNameToHash, - chromeNameToMozName, - certNameToSKD, - certSKDToName) { - let chromePreloads = downloadAsJson(filename); - let chromePins = chromePreloads.pinsets; - let chromeImportedPinsets = {}; - let chromeImportedEntries = []; - - chromePins.forEach(function(pin) { - let valid = true; - let pinset = { name: pin.name, sha256_hashes: [] }; - // Translate the Chrome pinset format to ours - pin.static_spki_hashes.forEach(function(name) { - if (name in chromeNameToHash) { - let hash = chromeNameToHash[name]; - pinset.sha256_hashes.push(certSKDToName[hash]); - - // We should have already added hashes for all of these when we - // imported the certificate file. - if (!certNameToSKD[name]) { - throw new Error("ERROR: No hash for name: " + name); - } - } else if (name in chromeNameToMozName) { - pinset.sha256_hashes.push(chromeNameToMozName[name]); - } else { - dump("Skipping Chrome pinset " + pinset.name + ", couldn't find " + - "builtin " + name + " from cert file\n"); - valid = false; - } - }); - if (valid) { - chromeImportedPinsets[pinset.name] = pinset; - } - }); - - // Grab the domain entry lists. Chrome's entry format is similar to - // ours, except theirs includes a HSTS mode. - const cData = gStaticPins.chromium_data; - let entries = chromePreloads.entries; - entries.forEach(function(entry) { - // HSTS entry only - if (!entry.pins) { - return; - } - let pinsetName = cData.substitute_pinsets[entry.pins]; - if (!pinsetName) { - pinsetName = entry.pins; - } - - // We trim the entry name here to avoid breaking hostname comparisons in the - // HPKP implementation. - entry.name = entry.name.trim(); - - let isProductionDomain = - (cData.production_domains.indexOf(entry.name) != -1); - let isProductionPinset = - (cData.production_pinsets.indexOf(pinsetName) != -1); - let excludeDomain = - (cData.exclude_domains.indexOf(entry.name) != -1); - let isTestMode = !isProductionPinset && !isProductionDomain; - if (entry.pins && !excludeDomain && chromeImportedPinsets[entry.pins]) { - chromeImportedEntries.push({ - name: entry.name, - include_subdomains: entry.include_subdomains, - test_mode: isTestMode, - is_moz: false, - pins: pinsetName }); - } - }); - return [ chromeImportedPinsets, chromeImportedEntries ]; -} - -// Returns a pair of maps [certNameToSKD, certSKDToName] between cert -// nicknames and digests of the SPKInfo for the mozilla trust store -function loadNSSCertinfo(extraCertificates) { - let allCerts = gCertDB.getCerts(); - let enumerator = allCerts.getEnumerator(); - let certNameToSKD = {}; - let certSKDToName = {}; - while (enumerator.hasMoreElements()) { - let cert = enumerator.getNext().QueryInterface(Ci.nsIX509Cert); - if (!isCertBuiltIn(cert)) { - continue; - } - let name = cert.nickname.substr(BUILT_IN_NICK_PREFIX.length); - let SKD = cert.sha256SubjectPublicKeyInfoDigest; - certNameToSKD[name] = SKD; - certSKDToName[SKD] = name; - } - - for (let cert of extraCertificates) { - let name = cert.commonName; - let SKD = cert.sha256SubjectPublicKeyInfoDigest; - certNameToSKD[name] = SKD; - certSKDToName[SKD] = name; - } - - { - // This is the pinning test certificate. The key hash identifies the - // default RSA key from pykey. - let name = "End Entity Test Cert"; - let SKD = "VCIlmPM9NkgFQtrs4Oa5TeFcDu6MWRTKSNdePEhOgD8="; - certNameToSKD[name] = SKD; - certSKDToName[SKD] = name; - } - return [certNameToSKD, certSKDToName]; -} - -function parseJson(filename) { - let json = stripComments(readFileToString(filename)); - return JSON.parse(json); -} - -function nameToAlias(certName) { - // change the name to a string valid as a c identifier - // remove non-ascii characters - certName = certName.replace(/[^[:ascii:]]/g, "_"); - // replace non word characters - certName = certName.replace(/[^A-Za-z0-9]/g, "_"); - - return "k" + certName + "Fingerprint"; -} - -function compareByName (a, b) { - return a.name.localeCompare(b.name); -} - -function genExpirationTime() { - let now = new Date(); - let nowMillis = now.getTime(); - let expirationMillis = nowMillis + (PINNING_MINIMUM_REQUIRED_MAX_AGE * 1000); - let expirationMicros = expirationMillis * 1000; - return "static const PRTime kPreloadPKPinsExpirationTime = INT64_C(" + - expirationMicros + ");\n"; -} - -function writeFullPinset(certNameToSKD, certSKDToName, pinset) { - let prefix = "kPinset_" + pinset.name; - if (!pinset.sha256_hashes || pinset.sha256_hashes.length == 0) { - throw new Error(`ERROR: Pinset ${pinset.name} does not contain any hashes`); - } - writeFingerprints(certNameToSKD, certSKDToName, pinset.name, - pinset.sha256_hashes); -} - -function writeFingerprints(certNameToSKD, certSKDToName, name, hashes) { - let varPrefix = "kPinset_" + name; - writeString("static const char* const " + varPrefix + "_Data[] = {\n"); - let SKDList = []; - for (let certName of hashes) { - if (!(certName in certNameToSKD)) { - throw new Error(`ERROR: Can't find '${certName}' in certNameToSKD`); - } - SKDList.push(certNameToSKD[certName]); - } - for (let skd of SKDList.sort()) { - writeString(" " + nameToAlias(certSKDToName[skd]) + ",\n"); - } - if (hashes.length == 0) { - // ANSI C requires that an initialiser list be non-empty. - writeString(" 0\n"); - } - writeString("};\n"); - writeString("static const StaticFingerprints " + varPrefix + " = {\n " + - "sizeof(" + varPrefix + "_Data) / sizeof(const char*),\n " + varPrefix + - "_Data\n};\n\n"); -} - -function writeEntry(entry) { - let printVal = " { \"" + entry.name + "\",\ "; - if (entry.include_subdomains) { - printVal += "true, "; - } else { - printVal += "false, "; - } - // Default to test mode if not specified. - let testMode = true; - if (entry.hasOwnProperty("test_mode")) { - testMode = entry.test_mode; - } - if (testMode) { - printVal += "true, "; - } else { - printVal += "false, "; - } - if (entry.is_moz || (entry.pins.indexOf("mozilla") != -1 && - entry.pins != "mozilla_test")) { - printVal += "true, "; - } else { - printVal += "false, "; - } - if ("id" in entry) { - if (entry.id >= 256) { - throw new Error("ERROR: Not enough buckets in histogram"); - } - if (entry.id >= 0) { - printVal += entry.id + ", "; - } - } else { - printVal += "-1, "; - } - printVal += "&kPinset_" + entry.pins; - printVal += " },\n"; - writeString(printVal); -} - -function writeDomainList(chromeImportedEntries) { - writeString("/* Sort hostnames for binary search. */\n"); - writeString("static const TransportSecurityPreload " + - "kPublicKeyPinningPreloadList[] = {\n"); - let count = 0; - let mozillaDomains = {}; - gStaticPins.entries.forEach(function(entry) { - mozillaDomains[entry.name] = true; - }); - // For any domain for which we have set pins, exclude them from - // chromeImportedEntries. - for (let i = chromeImportedEntries.length - 1; i >= 0; i--) { - if (mozillaDomains[chromeImportedEntries[i].name]) { - dump("Skipping duplicate pinset for domain " + - JSON.stringify(chromeImportedEntries[i], undefined, 2) + "\n"); - chromeImportedEntries.splice(i, 1); - } - } - let sortedEntries = gStaticPins.entries; - sortedEntries.push.apply(sortedEntries, chromeImportedEntries); - for (let entry of sortedEntries.sort(compareByName)) { - count++; - writeEntry(entry); - } - writeString("};\n"); - - writeString("\n// Pinning Preload List Length = " + count + ";\n"); - writeString("\nstatic const int32_t kUnknownId = -1;\n"); -} - -function writeFile(certNameToSKD, certSKDToName, - chromeImportedPinsets, chromeImportedEntries) { - // Compute used pins from both Chrome's and our pinsets, so we can output - // them later. - let usedFingerprints = {}; - let mozillaPins = {}; - gStaticPins.pinsets.forEach(function(pinset) { - mozillaPins[pinset.name] = true; - pinset.sha256_hashes.forEach(function (name) { - usedFingerprints[name] = true; - }); - }); - for (let key in chromeImportedPinsets) { - let pinset = chromeImportedPinsets[key]; - pinset.sha256_hashes.forEach(function(name) { - usedFingerprints[name] = true; - }); - } - - writeString(FILE_HEADER); - - // Write actual fingerprints. - Object.keys(usedFingerprints).sort().forEach(function(certName) { - if (certName) { - writeString("/* " + certName + " */\n"); - writeString("static const char " + nameToAlias(certName) + "[] =\n"); - writeString(" \"" + certNameToSKD[certName] + "\";\n"); - writeString("\n"); - } - }); - - // Write the pinsets - writeString(PINSETDEF); - writeString("/* PreloadedHPKPins.json pinsets */\n"); - gStaticPins.pinsets.sort(compareByName).forEach(function(pinset) { - writeFullPinset(certNameToSKD, certSKDToName, pinset); - }); - writeString("/* Chrome static pinsets */\n"); - for (let key in chromeImportedPinsets) { - if (mozillaPins[key]) { - dump("Skipping duplicate pinset " + key + "\n"); - } else { - dump("Writing pinset " + key + "\n"); - writeFullPinset(certNameToSKD, certSKDToName, chromeImportedPinsets[key]); - } - } - - // Write the domainlist entries. - writeString(DOMAINHEADER); - writeDomainList(chromeImportedEntries); - writeString("\n"); - writeString(genExpirationTime()); -} - -function loadExtraCertificates(certStringList) { - let constructedCerts = []; - for (let certString of certStringList) { - constructedCerts.push(gCertDB.constructX509FromBase64(certString)); - } - return constructedCerts; -} - -var extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates); -var [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(extraCertificates); -var [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts( - gStaticPins.chromium_data.cert_file_url, certNameToSKD, certSKDToName); -var [ chromeImportedPinsets, chromeImportedEntries ] = - downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url, - chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName); - -writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets, - chromeImportedEntries); - -FileUtils.closeSafeFileOutputStream(gFileOutputStream); -- cgit v1.2.3