Bug 1092606 - Don't import Chromium pinsets for domains that are already in our list. r=keeler, r=jcj, a=lsblakk
authorMonica Chew <mmc@mozilla.com>
Tue, 04 Nov 2014 10:53:52 -0800
changeset 233686 f46d7b20b032aefe7698e382221861a25d6697a8
parent 233685 339b62ba542d86163627fcc38501b2529e4b51f5
child 233687 8f76f9426e70d0e64dbccb3bd02cf4bed0d36431
push id4187
push userbhearsum@mozilla.com
push dateFri, 28 Nov 2014 15:29:12 +0000
treeherdermozilla-beta@f23cc6a30c11 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler, jcj, lsblakk
bugs1092606
milestone35.0a2
Bug 1092606 - Don't import Chromium pinsets for domains that are already in our list. r=keeler, r=jcj, a=lsblakk
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) {