Bug 1092606: Don't import Chromium pinsets for domains that are already in our list (r=keeler,jcj)
authorMonica Chew <mmc@mozilla.com>
Tue, 04 Nov 2014 10:53:52 -0800
changeset 213947 1d0c12148cf658c4b77c03b4b6c69a475210a907
parent 213946 2e8c12e04a14bcf717346621dd8177c2c45f842c
child 213948 9c8ca05b975a0f22e9b2b3d8eb211d4242fcb2a9
push id27768
push userkwierso@gmail.com
push dateWed, 05 Nov 2014 02:19:03 +0000
treeherdermozilla-central@a1823d3c7365 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, jcj
bugs1092606
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1092606: Don't import Chromium pinsets for domains that are already in our list (r=keeler,jcj)
security/manager/tools/genHPKPStaticPins.js
--- a/security/manager/tools/genHPKPStaticPins.js
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -62,18 +62,18 @@ const PINSETDEF = "/* Pinsets are each a
   "  const char* const* data;\n" +
   "};\n\n" +
   "struct StaticPinset {\n" +
   "  const StaticFingerprints* sha1;\n" +
   "  const StaticFingerprints* sha256;\n" +
   "};\n\n";
 
 // Command-line arguments
-var gStaticPins = parseJson(arguments[0]);
-var gTestCertFile = arguments[1];
+let gStaticPins = parseJson(arguments[0]);
+let gTestCertFile = arguments[1];
 
 // Open the output file.
 let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
 file.initWithPath(arguments[2]);
 let gFileOutputStream = FileUtils.openSafeFileOutputStream(file);
 
 function writeString(string) {
   gFileOutputStream.write(string, string.length);
@@ -85,17 +85,17 @@ function readFileToString(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) {
-  var lines = buf.split("\n");
+  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];
     }
   }
@@ -113,45 +113,45 @@ function isCertBuiltIn(cert) {
   }
   if (tokenNames.some(isBuiltinToken)) {
     return true;
   }
   return false;
 }
 
 function download(filename) {
-  var req = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
+  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 "ERROR: problem downloading '" + filename + "': " + e;
   }
 
   if (req.status != 200) {
     throw("ERROR: problem downloading '" + filename + "': status " +
           req.status);
   }
 
-  var resultDecoded;
+  let resultDecoded;
   try {
     resultDecoded = atob(req.responseText);
   }
   catch (e) {
     throw "ERROR: could not decode data as base64 from '" + filename + "': " + e;
   }
   return resultDecoded;
 }
 
 function downloadAsJson(filename) {
   // we have to filter out '//' comments
-  var result = download(filename).replace(/\/\/[^\n]*\n/g, "");
-  var data = null;
+  let result = download(filename).replace(/\/\/[^\n]*\n/g, "");
+  let data = null;
   try {
     data = JSON.parse(result);
   }
   catch (e) {
     throw "ERROR: could not parse data from '" + filename + "': " + e;
   }
   return data;
 }
@@ -191,16 +191,17 @@ function downloadAndParseChromeCerts(fil
   let state = PRE_NAME;
 
   let lines = download(filename).split("\n");
   let name = "";
   let pemCert = "";
   let hash = "";
   let chromeNameToHash = {};
   let chromeNameToMozName = {}
+  let chromeName;
   for (let i = 0; i < lines.length; ++i) {
     let line = lines[i];
     // Skip comments and newlines.
     if (line.length == 0 || line[0] == '#') {
       continue;
     }
     switch(state) {
       case PRE_NAME:
@@ -227,16 +228,17 @@ function downloadAndParseChromeCerts(fil
           throw "ERROR: couldn't parse Chrome certificate file " + 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");
@@ -314,16 +316,20 @@ function downloadAndParseChromePins(file
     }
   });
 
   // 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;
     }
     let isProductionDomain =
       (cData.production_domains.indexOf(entry.name) != -1);
     let isProductionPinset =
       (cData.production_pinsets.indexOf(pinsetName) != -1);
@@ -469,51 +475,66 @@ function writeEntry(entry) {
     printVal += "false, ";
   }
   if (entry.is_moz || (entry.pins.indexOf("mozilla") != -1 &&
                        entry.pins != "mozilla_test")) {
     printVal += "true, ";
   } else {
     printVal += "false, ";
   }
-  if (entry.id >= 256) {
-    throw("Not enough buckets in histogram");
-  }
-  if (entry.id >= 0) {
-    printVal += entry.id + ", ";
+  if ("id" in entry) {
+    if (entry.id >= 256) {
+      throw("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.
-  usedFingerprints = {};
+  let usedFingerprints = {};
   gStaticPins.pinsets.forEach(function(pinset) {
     // We aren't guaranteed to have sha1_hashes in our own JSON.
     if (pinset.sha1_hashes) {
       pinset.sha1_hashes.forEach(function(name) {
         usedFingerprints[name] = true;
       });
     }
     if (pinset.sha256_hashes) {