[tor-commits] [tor-browser/tor-browser-31.2.0esr-4.5-1] Bug #11955 Backport certificate pinning

mikeperry at torproject.org mikeperry at torproject.org
Thu Oct 30 01:01:49 UTC 2014


commit cd9887977227df33462e755200ac6f6ade351bea
Author: Camilo Viecco <cviecco at mozilla.com>
Date:   Thu Jun 20 10:35:43 2013 -0700

    Bug #11955 Backport certificate pinning
    
    Includes the following Mozilla patches, some modified for Tor Browser:
    
    Bug 744204 - Allow Key pining part 1 - Built-in Pinning Service. r=keeler
    
    Bug 744204 - Allow Certificate key pinning Part 2 - Certverifier Interface. r=keeler
    
      --HG--
      extra : rebase_source : 2f9748ba0b241c697e22b7ff72f2f5a0fad4a2ca
    
    Bug 998057: Add test pinset to the pin generator (r=cviecco)
    
      --HG--
      rename : security/manager/ssl/tests/unit/tlsserver/default-ee.der => security/manager/boot/src/default-ee.der
    
    Bug 998057: Add tests for certificate pinning (r=cviecco,dkeeler)
    
    Bug 1002696 - Minimum set of changes to make genHPKPStaticPins.js productionizable. r=cviecco, dkeeler
    
      --HG--
      rename : security/manager/boot/src/PreloadedHPKPins.json => security/manager/tools/PreloadedHPKPins.json
      rename : security/manager/boot/src/genHPKPStaticPins.js => security/manager/tools/genHPKPStaticPins.js
    
    Bug 951315 - Add telemetry to PK pinning. r=dkeeler
    
    Bug 1006107 - Disable pining by default, setup pinning for *.addons.mozilla.org. r=dkeeler
    
      Tor project: only patching two files:
      security/manager/ssl/src/nsNSSComponent.cpp
      netwerk/base/public/security-prefs.js
    
      --HG--
      extra : rebase_source : 93b1dbd5dc31490424060729a3941deffa8ee1d5
    
    Bug 772756: Implement sha1 support, import Chrome's pinsets wholesale, add test mode (r=cviecco,keeler)
    
      Tor project, we only patch:
      security/manager/ssl/tests/unit/test_pinning.js
      security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
      security/manager/ssl/tests/unit/tlsserver/default-ee.der
      security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
      security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
      security/manager/ssl/tests/unit/tlsserver/test-ca.der
    
    Bug 1009720: Telemetry for CERT_PINNING_TEST_RESULTS (r=keeler)
    
    Bug 1007844: Implement per-host telemetry for pin violations for AMO and aus4 (r=keeler)
    
      Only patching toolkit/components/telemetry/Histograms.json
    
    Bug 1011269: Add CertVerifier::pinningEnforceTestMode (r=keeler)
    
      Tor project, only commit:
      security/certverifier/CertVerifier.cpp
      security/certverifier/CertVerifier.h
      security/manager/ssl/src/nsNSSComponent.cpp
    
    Bug 1012882: Restrict pinning to desktop (r=keeler)
    
    Tor Bug #11955: Backport certificate pinning
    
      Bring the following files up to date:
    
      security/manager/boot/src/PublicKeyPinningService.cpp
      security/manager/boot/src/PublicKeyPinningService.h
      security/manager/boot/src/StaticHPKPins.h
      security/manager/ssl/tests/unit/test_pinning.js
      security/manager/tools/PreloadedHPKPins.json
      security/manager/tools/genHPKPStaticPins.js
      security/pkix/include/pkix/Time.h
      security/pkix/lib/pkixtime.cpp
---
 .gitignore                                         |    2 +-
 browser/app/profile/firefox.js                     |    3 +
 modules/libpref/src/init/all.js                    |    3 +
 security/apps/AppTrustDomain.h                     |    2 +
 security/certverifier/CertVerifier.cpp             |  225 +++-
 security/certverifier/CertVerifier.h               |   17 +-
 security/certverifier/NSSCertDBTrustDomain.cpp     |   35 +-
 security/certverifier/NSSCertDBTrustDomain.h       |    6 +-
 security/certverifier/moz.build                    |    1 +
 .../manager/boot/src/PublicKeyPinningService.cpp   |  306 ++++++
 .../boot/src/PublicKeyPinningService.cpp.rej       |   11 +
 .../manager/boot/src/PublicKeyPinningService.h     |   35 +
 security/manager/boot/src/StaticHPKPins.h          | 1095 ++++++++++++++++++++
 security/manager/boot/src/moz.build                |    6 +
 .../manager/ssl/src/SSLServerCertVerification.cpp  |    5 +-
 security/manager/ssl/src/SharedCertVerifier.h      |    6 +-
 security/manager/ssl/src/nsCMS.cpp                 |    5 +-
 security/manager/ssl/src/nsNSSCertificate.cpp      |   13 +-
 security/manager/ssl/src/nsNSSCertificateDB.cpp    |   19 +-
 security/manager/ssl/src/nsNSSComponent.cpp        |   12 +-
 security/manager/ssl/src/nsUsageArrayHelper.cpp    |    5 +-
 security/manager/ssl/tests/unit/head_psm.js        |    1 +
 .../manager/ssl/tests/unit/test_cert_overrides.js  |    2 +-
 security/manager/ssl/tests/unit/test_pinning.js    |  182 ++++
 security/manager/ssl/tests/unit/tlsserver/cert8.db |  Bin 65536 -> 65536 bytes
 .../ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp |   11 +
 .../ssl/tests/unit/tlsserver/default-ee.der        |  Bin 527 -> 639 bytes
 .../ssl/tests/unit/tlsserver/generate_certs.sh     |   17 +-
 security/manager/ssl/tests/unit/tlsserver/key3.db  |  Bin 49152 -> 57344 bytes
 .../ssl/tests/unit/tlsserver/other-test-ca.der     |  Bin 452 -> 452 bytes
 .../manager/ssl/tests/unit/tlsserver/secmod.db     |  Bin 16384 -> 16384 bytes
 .../manager/ssl/tests/unit/tlsserver/test-ca.der   |  Bin 440 -> 440 bytes
 security/manager/ssl/tests/unit/xpcshell.ini       |    4 +
 security/manager/tools/PreloadedHPKPins.json       |  247 +++++
 security/manager/tools/genHPKPStaticPins.js        |  576 ++++++++++
 security/pkix/include/pkix/Result.h                |  174 ++++
 security/pkix/include/pkix/Time.h                  |  126 +++
 security/pkix/include/pkix/pkixtypes.h             |    5 +
 security/pkix/lib/pkixbuild.cpp                    |   24 +
 security/pkix/lib/pkixtime.cpp                     |   70 ++
 toolkit/components/telemetry/Histograms.json       |   32 +
 41 files changed, 3198 insertions(+), 85 deletions(-)

diff --git a/.gitignore b/.gitignore
index 8df1754..6d17511 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,7 +13,7 @@ ID
 .*.sw[a-z]
 
 # User files that may appear at the root
-/.mozconfig*
+#/.mozconfig*
 /mozconfig
 /configure
 /config.cache
diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index 2ec7f9a..b61756d 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1484,6 +1484,9 @@ pref("security.csp.speccompliant", true);
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
+// 1 = allow MITM for certificate pinning checks.
+pref("security.cert_pinning.enforcement_level", 1);
+
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js
index 8efad8f..8a389e3 100644
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1566,6 +1566,9 @@ pref("security.csp.experimentalEnabled", false);
 pref("security.mixed_content.block_active_content", false);
 pref("security.mixed_content.block_display_content", false);
 
+// Disable pinning checks by default.
+pref("security.cert_pinning.enforcement_level", 0);
+
 // Modifier key prefs: default to Windows settings,
 // menu access key = alt, accelerator key = control.
 // Use 17 for Ctrl, 18 for Alt, 224 for Meta, 91 for Win, 0 for none. Mac settings in macprefs.js
diff --git a/security/apps/AppTrustDomain.h b/security/apps/AppTrustDomain.h
index 875c1db..d7e4734 100644
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -35,6 +35,8 @@ public:
                             /*const*/ CERTCertificate* issuerCertToDup,
                             PRTime time,
                             /*optional*/ const SECItem* stapledOCSPresponse);
+  SECStatus IsChainValid(const CERTCertList* certChain) { return SECSuccess; }
+
 private:
   void* mPinArg; // non-owning!
   mozilla::pkix::ScopedCERTCertificate mTrustedRoot;
diff --git a/security/certverifier/CertVerifier.cpp b/security/certverifier/CertVerifier.cpp
index b8b84d7..b2db9fc 100644
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -11,9 +11,11 @@
 #include "pkix/pkix.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
+#include "PublicKeyPinningService.h"
 #include "cert.h"
 #include "ocsp.h"
 #include "secerr.h"
+#include "pk11pub.h"
 #include "prerror.h"
 #include "sslerr.h"
 
@@ -38,7 +40,8 @@ CertVerifier::CertVerifier(implementation_config ic,
 #endif
                            ocsp_download_config odc,
                            ocsp_strict_config osc,
-                           ocsp_get_config ogc)
+                           ocsp_get_config ogc,
+                           pinning_enforcement_config pel)
   : mImplementation(ic)
 #ifndef NSS_NO_LIBPKIX
   , mMissingCertDownloadEnabled(mcdc == missing_cert_download_on)
@@ -47,6 +50,7 @@ CertVerifier::CertVerifier(implementation_config ic,
   , mOCSPDownloadEnabled(odc == ocsp_on)
   , mOCSPStrict(osc == ocsp_strict)
   , mOCSPGETEnabled(ogc == ocsp_get_enabled)
+  , mPinningEnforcementLevel(pel)
 {
 }
 
@@ -64,7 +68,6 @@ InitCertVerifierLog()
 #endif
 }
 
-#if 0
 // Once we migrate to mozilla::pkix or change the overridable error
 // logic this will become unnecesary.
 static SECStatus
@@ -95,23 +98,102 @@ insertErrorIntoVerifyLog(CERTCertificate* cert, const PRErrorCode err,
 
   return SECSuccess;
 }
-#endif
+
+SECStatus
+IsCertBuiltInRoot(CERTCertificate* cert, bool& result) {
+  result = false;
+  ScopedPtr<PK11SlotList, PK11_FreeSlotList> slots;
+  slots = PK11_GetAllSlotsForCert(cert, nullptr);
+  if (!slots) {
+    if (PORT_GetError() == SEC_ERROR_NO_TOKEN) {
+      // no list
+      return SECSuccess;
+    }
+    return SECFailure;
+  }
+  for (PK11SlotListElement* le = slots->head; le; le = le->next) {
+    char* token = PK11_GetTokenName(le->slot);
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("BuiltInRoot? subject=%s token=%s",cert->subjectName, token));
+    if (strcmp("Builtin Object Token", token) == 0) {
+      result = true;
+      return SECSuccess;
+    }
+  }
+  return SECSuccess;
+}
+
+struct ChainValidationCallbackState
+{
+  const char* hostname;
+  const CertVerifier::pinning_enforcement_config pinningEnforcementLevel;
+  const SECCertificateUsage usage;
+  const PRTime time;
+};
 
 SECStatus chainValidationCallback(void* state, const CERTCertList* certList,
                                   PRBool* chainOK)
 {
+  ChainValidationCallbackState* callbackState =
+    reinterpret_cast<ChainValidationCallbackState*>(state);
+
   *chainOK = PR_FALSE;
 
-  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Inside the Callback \n"));
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+         ("verifycert: Inside the Callback \n"));
 
   // On sanity failure we fail closed.
   if (!certList) {
-    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("verifycert: Short circuit, callback, "
-                                            "sanity check failed \n"));
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("verifycert: Short circuit, callback, sanity check failed \n"));
+    PR_SetError(PR_INVALID_STATE_ERROR, 0);
+    return SECFailure;
+  }
+  if (!callbackState) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("verifycert: Short circuit, callback, no state! \n"));
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
     return SECFailure;
   }
-  *chainOK = PR_TRUE;
+
+  if (callbackState->usage != certificateUsageSSLServer ||
+      callbackState->pinningEnforcementLevel == CertVerifier::pinningDisabled) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("verifycert: Callback shortcut pel=%d \n",
+            callbackState->pinningEnforcementLevel));
+    *chainOK = PR_TRUE;
+    return SECSuccess;
+  }
+
+  for (CERTCertListNode* node = CERT_LIST_HEAD(certList);
+       !CERT_LIST_END(node, certList);
+       node = CERT_LIST_NEXT(node)) {
+    CERTCertificate* currentCert = node->cert;
+    if (CERT_LIST_END(CERT_LIST_NEXT(node), certList)) {
+      bool isBuiltInRoot = false;
+      SECStatus srv = IsCertBuiltInRoot(currentCert, isBuiltInRoot);
+      if (srv != SECSuccess) {
+        PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("Is BuiltInRoot failure"));
+        return srv;
+      }
+      // If desired, the user can enable "allow user CA MITM mode", in which
+      // case key pinning is not enforced for certificates that chain to trust
+      // anchors that are not in Mozilla's root program
+      if (!isBuiltInRoot &&
+          (callbackState->pinningEnforcementLevel ==
+             CertVerifier::pinningAllowUserCAMITM)) {
+        *chainOK = PR_TRUE;
+        return SECSuccess;
+      }
+    }
+  }
+
+  const bool enforceTestMode = (callbackState->pinningEnforcementLevel ==
+                                CertVerifier::pinningEnforceTestMode);
+  *chainOK = PublicKeyPinningService::
+    ChainHasValidPins(certList, callbackState->hostname, callbackState->time,
+                      enforceTestMode);
+
   return SECSuccess;
 }
 
@@ -120,42 +202,41 @@ ClassicVerifyCert(CERTCertificate* cert,
                   const SECCertificateUsage usage,
                   const PRTime time,
                   void* pinArg,
+                  ChainValidationCallbackState* callbackState,
                   /*optional out*/ ScopedCERTCertList* validationChain,
                   /*optional out*/ CERTVerifyLog* verifyLog)
 {
   SECStatus rv;
   SECCertUsage enumUsage;
-  if (validationChain) {
-    switch(usage){
-      case  certificateUsageSSLClient:
-        enumUsage = certUsageSSLClient;
-        break;
-      case  certificateUsageSSLServer:
-        enumUsage = certUsageSSLServer;
-        break;
-      case certificateUsageSSLCA:
-        enumUsage = certUsageSSLCA;
-        break;
-      case certificateUsageEmailSigner:
-        enumUsage = certUsageEmailSigner;
-        break;
-      case certificateUsageEmailRecipient:
-        enumUsage = certUsageEmailRecipient;
-        break;
-      case certificateUsageObjectSigner:
-        enumUsage = certUsageObjectSigner;
-        break;
-      case certificateUsageVerifyCA:
-        enumUsage = certUsageVerifyCA;
-        break;
-      case certificateUsageStatusResponder:
-        enumUsage = certUsageStatusResponder;
-        break;
-      default:
-        PR_NOT_REACHED("unexpected usage");
-        PORT_SetError(SEC_ERROR_INVALID_ARGS);
-        return SECFailure;
-    }
+  switch (usage) {
+    case certificateUsageSSLClient:
+      enumUsage = certUsageSSLClient;
+      break;
+    case certificateUsageSSLServer:
+      enumUsage = certUsageSSLServer;
+      break;
+    case certificateUsageSSLCA:
+      enumUsage = certUsageSSLCA;
+      break;
+    case certificateUsageEmailSigner:
+      enumUsage = certUsageEmailSigner;
+      break;
+    case certificateUsageEmailRecipient:
+      enumUsage = certUsageEmailRecipient;
+      break;
+    case certificateUsageObjectSigner:
+      enumUsage = certUsageObjectSigner;
+      break;
+    case certificateUsageVerifyCA:
+      enumUsage = certUsageVerifyCA;
+      break;
+    case certificateUsageStatusResponder:
+      enumUsage = certUsageStatusResponder;
+      break;
+    default:
+      PR_NOT_REACHED("unexpected usage");
+      PORT_SetError(SEC_ERROR_INVALID_ARGS);
+      return SECFailure;
   }
   if (usage == certificateUsageSSLServer) {
     // SSL server cert verification has always used CERT_VerifyCert, so we
@@ -168,13 +249,38 @@ ClassicVerifyCert(CERTCertificate* cert,
     rv = CERT_VerifyCertificate(CERT_GetDefaultCertDB(), cert, true,
                                 usage, time, pinArg, verifyLog, nullptr);
   }
-  if (rv == SECSuccess && validationChain) {
-    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG, ("VerifyCert: getting chain in 'classic' \n"));
-    *validationChain = CERT_GetCertChainFromCert(cert, time, enumUsage);
-    if (!*validationChain) {
-      rv = SECFailure;
+
+  if (rv == SECSuccess &&
+      (validationChain || usage == certificateUsageSSLServer)) {
+    PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+           ("VerifyCert: getting chain in 'classic' \n"));
+    ScopedCERTCertList certChain(CERT_GetCertChainFromCert(cert, time,
+                                                           enumUsage));
+    if (!certChain) {
+      return SECFailure;
+    }
+    if (usage == certificateUsageSSLServer) {
+      PRBool chainOK = PR_FALSE;
+      SECStatus srv = chainValidationCallback(callbackState, certChain.get(),
+                                              &chainOK);
+      if (srv != SECSuccess) {
+        return srv;
+      }
+      if (chainOK != PR_TRUE) {
+        if (verifyLog) {
+          insertErrorIntoVerifyLog(cert,
+                                   SEC_ERROR_APPLICATION_CALLBACK_ERROR,
+                                   verifyLog);
+        }
+        PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0); // same as libpkix
+        return SECFailure;
+      }
+    }
+    if (rv == SECSuccess && validationChain) {
+      *validationChain = certChain.release();
     }
   }
+
   return rv;
 }
 
@@ -227,6 +333,7 @@ CertVerifier::MozillaPKIXVerifyCert(
                    const PRTime time,
                    void* pinArg,
                    const Flags flags,
+                   ChainValidationCallbackState* callbackState,
       /*optional*/ const SECItem* stapledOCSPResponse,
   /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
   /*optional out*/ SECOidTag* evOidPolicy)
@@ -249,6 +356,10 @@ CertVerifier::MozillaPKIXVerifyCert(
     return SECFailure;
   }
 
+  CERTChainVerifyCallback callbackContainer;
+  callbackContainer.isChainValid = chainValidationCallback;
+  callbackContainer.isChainValidArg = callbackState;
+
   NSSCertDBTrustDomain::OCSPFetching ocspFetching
     = !mOCSPDownloadEnabled ||
       (flags & FLAG_LOCAL_ONLY) ? NSSCertDBTrustDomain::NeverFetchOCSP
@@ -295,7 +406,7 @@ CertVerifier::MozillaPKIXVerifyCert(
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
-                      mOCSPCache, pinArg);
+                      mOCSPCache, pinArg, &callbackContainer);
         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                           KeyUsage::digitalSignature, // ECDHE/DHE
                                           KeyUsage::keyEncipherment, // RSA
@@ -321,7 +432,7 @@ CertVerifier::MozillaPKIXVerifyCert(
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
-                                       pinArg);
+                                       pinArg, &callbackContainer);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KeyUsage::digitalSignature, // (EC)DHE
                                         KeyUsage::keyEncipherment, // RSA
@@ -443,19 +554,25 @@ CertVerifier::MozillaPKIXVerifyCert(
 
 SECStatus
 CertVerifier::VerifyCert(CERTCertificate* cert,
-            /*optional*/ const SECItem* stapledOCSPResponse,
                          const SECCertificateUsage usage,
                          const PRTime time,
                          void* pinArg,
+                         const char* hostname,
                          const Flags flags,
+                         /*optional in*/ const SECItem* stapledOCSPResponse,
                          /*optional out*/ ScopedCERTCertList* validationChain,
                          /*optional out*/ SECOidTag* evOidPolicy,
                          /*optional out*/ CERTVerifyLog* verifyLog)
 {
+  ChainValidationCallbackState callbackState = { hostname,
+                                                 mPinningEnforcementLevel,
+                                                 usage,
+                                                 time };
+
   if (mImplementation == mozillapkix) {
     return MozillaPKIXVerifyCert(cert, usage, time, pinArg, flags,
-                                 stapledOCSPResponse, validationChain,
-                                 evOidPolicy);
+                                 &callbackState, stapledOCSPResponse,
+                                 validationChain, evOidPolicy);
   }
 
   if (!cert)
@@ -581,7 +698,7 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
   CERTChainVerifyCallback callbackContainer;
   if (usage == certificateUsageSSLServer) {
     callbackContainer.isChainValid = chainValidationCallback;
-    callbackContainer.isChainValidArg = nullptr;
+    callbackContainer.isChainValidArg = &callbackState;
     cvin[i].type = cert_pi_chainVerifyCallback;
     cvin[i].value.pointer.chainVerifyCallback = &callbackContainer;
     ++i;
@@ -685,8 +802,8 @@ CertVerifier::VerifyCert(CERTCertificate* cert,
   if (mImplementation == classic) {
     // XXX: we do not care about the localOnly flag (currently) as the
     // caller that wants localOnly should disable and reenable the fetching.
-    return ClassicVerifyCert(cert, usage, time, pinArg, validationChain,
-                             verifyLog);
+    return ClassicVerifyCert(cert, usage, time, pinArg, &callbackState,
+                             validationChain, verifyLog);
   }
 
 #ifdef NSS_NO_LIBPKIX
@@ -826,9 +943,9 @@ CertVerifier::VerifySSLServerCert(CERTCertificate* peerCert,
   // CreateCertErrorRunnable assumes that CERT_VerifyCertName is only called
   // if VerifyCert succeeded.
   ScopedCERTCertList validationChain;
-  SECStatus rv = VerifyCert(peerCert, stapledOCSPResponse,
-                            certificateUsageSSLServer, time,
-                            pinarg, 0, &validationChain, evOidPolicy);
+  SECStatus rv = VerifyCert(peerCert, certificateUsageSSLServer, time, pinarg,
+                            hostname, 0, stapledOCSPResponse, &validationChain,
+                            evOidPolicy, nullptr);
   if (rv != SECSuccess) {
     return rv;
   }
diff --git a/security/certverifier/CertVerifier.h b/security/certverifier/CertVerifier.h
index 09ed4b0..270e9a1 100644
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -12,6 +12,8 @@
 
 namespace mozilla { namespace psm {
 
+struct ChainValidationCallbackState;
+
 class CertVerifier
 {
 public:
@@ -24,11 +26,12 @@ public:
   // *evOidPolicy == SEC_OID_UNKNOWN means the cert is NOT EV
   // Only one usage per verification is supported.
   SECStatus VerifyCert(CERTCertificate* cert,
-          /*optional*/ const SECItem* stapledOCSPResponse,
                        const SECCertificateUsage usage,
                        const PRTime time,
                        void* pinArg,
+                       const char* hostname,
                        const Flags flags = 0,
+       /*optional in*/ const SECItem* stapledOCSPResponse = nullptr,
       /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain = nullptr,
       /*optional out*/ SECOidTag* evOidPolicy = nullptr ,
       /*optional out*/ CERTVerifyLog* verifyLog = nullptr);
@@ -52,6 +55,13 @@ public:
     mozillapkix = 2
   };
 
+  enum pinning_enforcement_config {
+    pinningDisabled = 0,
+    pinningAllowUserCAMITM = 1,
+    pinningStrict = 2,
+    pinningEnforceTestMode = 3
+  };
+
   enum missing_cert_download_config { missing_cert_download_off = 0, missing_cert_download_on };
   enum crl_download_config { crl_local_only = 0, crl_download_allowed };
   enum ocsp_download_config { ocsp_off = 0, ocsp_on };
@@ -65,7 +75,8 @@ public:
                missing_cert_download_config ac, crl_download_config cdc,
 #endif
                ocsp_download_config odc, ocsp_strict_config osc,
-               ocsp_get_config ogc);
+               ocsp_get_config ogc,
+               pinning_enforcement_config pinningEnforcementLevel);
   ~CertVerifier();
 
   void ClearOCSPCache() { mOCSPCache.Clear(); }
@@ -78,6 +89,7 @@ public:
   const bool mOCSPDownloadEnabled;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
+  const pinning_enforcement_config mPinningEnforcementLevel;
 
 private:
   SECStatus MozillaPKIXVerifyCert(CERTCertificate* cert,
@@ -85,6 +97,7 @@ private:
       const PRTime time,
       void* pinArg,
       const Flags flags,
+      ChainValidationCallbackState* callbackState,
       /*optional*/ const SECItem* stapledOCSPResponse,
       /*optional out*/ mozilla::pkix::ScopedCERTCertList* validationChain,
       /*optional out*/ SECOidTag* evOidPolicy);
diff --git a/security/certverifier/NSSCertDBTrustDomain.cpp b/security/certverifier/NSSCertDBTrustDomain.cpp
index 9bed2ce..fd2e363 100644
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -43,11 +43,13 @@ typedef ScopedPtr<SECMODModule, SECMOD_DestroyModule> ScopedSECMODModule;
 NSSCertDBTrustDomain::NSSCertDBTrustDomain(SECTrustType certDBTrustType,
                                            OCSPFetching ocspFetching,
                                            OCSPCache& ocspCache,
-                                           void* pinArg)
+                                           void* pinArg,
+                                           CERTChainVerifyCallback* checkChainCallback)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
+  , mCheckChainCallback(checkChainCallback)
 {
 }
 
@@ -475,6 +477,37 @@ NSSCertDBTrustDomain::VerifyAndMaybeCacheEncodedOCSPResponse(
   return rv;
 }
 
+SECStatus
+NSSCertDBTrustDomain::IsChainValid(const CERTCertList* certChain) {
+  SECStatus rv = SECFailure;
+
+  PR_LOG(gCertVerifierLog, PR_LOG_DEBUG,
+      ("NSSCertDBTrustDomain: Top of IsChainValid mCheckCallback=%p",
+       mCheckChainCallback));
+
+  if (!mCheckChainCallback) {
+    return SECSuccess;
+  }
+  if (!mCheckChainCallback->isChainValid) {
+    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
+    return SECFailure;
+  }
+  PRBool chainOK;
+  rv = (mCheckChainCallback->isChainValid)(mCheckChainCallback->isChainValidArg,
+                                           certChain, &chainOK);
+  if (rv != SECSuccess) {
+    return rv;
+  }
+  // rv = SECSuccess only implies successful call, now is time
+  // to check the chain check status
+  // we should only return success if the chain is valid
+  if (chainOK) {
+    return SECSuccess;
+  }
+  PR_SetError(SEC_ERROR_APPLICATION_CALLBACK_ERROR, 0);
+  return SECFailure;
+}
+
 namespace {
 
 static char*
diff --git a/security/certverifier/NSSCertDBTrustDomain.h b/security/certverifier/NSSCertDBTrustDomain.h
index 979c3e2..c2f211d 100644
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -57,7 +57,8 @@ public:
     LocalOnlyOCSPForEV = 4,
   };
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
-                       OCSPCache& ocspCache, void* pinArg);
+                       OCSPCache& ocspCache, void* pinArg,
+                       CERTChainVerifyCallback* checkChainCallback = nullptr);
 
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
@@ -78,6 +79,8 @@ public:
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPResponse);
 
+  virtual SECStatus IsChainValid(const CERTCertList* certChain);
+
 private:
   enum EncodedResponseSource {
     ResponseIsFromNetwork = 1,
@@ -93,6 +96,7 @@ private:
   const OCSPFetching mOCSPFetching;
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
+  CERTChainVerifyCallback* mCheckChainCallback; // non-owning!
 };
 
 } } // namespace mozilla::psm
diff --git a/security/certverifier/moz.build b/security/certverifier/moz.build
index 5e99b12..434c3cc 100644
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -17,6 +17,7 @@ if not CONFIG['NSS_NO_EV_CERTS']:
     ]
 
 LOCAL_INCLUDES += [
+    '../manager/boot/src',
     '../manager/ssl/src',
     '../pkix/include',
 ]
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp
new file mode 100644
index 0000000..82abaaf
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp
@@ -0,0 +1,306 @@
+/* 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/. */
+
+#include "PublicKeyPinningService.h"
+#include "pkix/nullptr.h"
+#include "StaticHPKPins.h" // autogenerated by genHPKPStaticpins.js
+
+#include "cert.h"
+#include "mozilla/Base64.h"
+#include "mozilla/Telemetry.h"
+#include "nsString.h"
+#include "nssb64.h"
+#include "pkix/pkixtypes.h"
+#include "prlog.h"
+#include "ScopedNSSTypes.h"
+#include "seccomon.h"
+#include "sechash.h"
+
+using namespace mozilla;
+using namespace mozilla::pkix;
+using namespace mozilla::psm;
+
+#if defined(PR_LOGGING)
+PRLogModuleInfo* gPublicKeyPinningLog =
+  PR_NewLogModule("PublicKeyPinningService");
+#endif
+
+/**
+ Computes in the location specified by base64Out the SHA256 digest
+ of the DER Encoded subject Public Key Info for the given cert
+*/
+static SECStatus
+GetBase64HashSPKI(const CERTCertificate* cert, SECOidTag hashType,
+                  nsACString& hashSPKIDigest)
+{
+  hashSPKIDigest.Truncate();
+  Digest digest;
+  nsresult rv = digest.DigestBuf(hashType, cert->derPublicKey.data,
+                                 cert->derPublicKey.len);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SECFailure;
+  }
+  rv = Base64Encode(nsDependentCSubstring(
+                      reinterpret_cast<const char*>(digest.get().data),
+                      digest.get().len),
+                      hashSPKIDigest);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return SECFailure;
+  }
+  return SECSuccess;
+}
+
+/*
+ * Returns true if a given cert matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalCertWithHashType(const CERTCertificate* cert, SECOidTag hashType,
+                     const StaticFingerprints* fingerprints)
+{
+  if (!fingerprints) {
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: No hashes found for hash type: %d\n", hashType));
+    return false;
+  }
+
+  nsAutoCString base64Out;
+  SECStatus srv = GetBase64HashSPKI(cert, hashType, base64Out);
+  if (srv != SECSuccess) {
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: GetBase64HashSPKI failed!\n"));
+    return false;
+  }
+
+  for (size_t i = 0; i < fingerprints->size; i++) {
+    if (base64Out.Equals(fingerprints->data[i])) {
+      PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+             ("pkpin: found pin base_64 ='%s'\n", base64Out.get()));
+      return true;
+    }
+  }
+  return false;
+}
+
+/*
+ * Returns true if a given chain matches any hashType fingerprints from the
+ * given pinset, false otherwise.
+ */
+static bool
+EvalChainWithHashType(const CERTCertList* certList, SECOidTag hashType,
+                      const StaticPinset* pinset)
+{
+  CERTCertificate* currentCert;
+
+  const StaticFingerprints* fingerprints = nullptr;
+  if (hashType == SEC_OID_SHA256) {
+    fingerprints = pinset->sha256;
+  } else if (hashType == SEC_OID_SHA1) {
+    fingerprints = pinset->sha1;
+  }
+  if (!fingerprints) {
+    return false;
+  }
+
+  CERTCertListNode* node;
+  for (node = CERT_LIST_HEAD(certList); !CERT_LIST_END(node, certList);
+       node = CERT_LIST_NEXT(node)) {
+    currentCert = node->cert;
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: certArray subject: '%s'\n",
+            currentCert->subjectName));
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: certArray common_name: '%s'\n",
+            CERT_GetCommonName(&(currentCert->issuer))));
+    if (EvalCertWithHashType(currentCert, hashType, fingerprints)) {
+      return true;
+    }
+  }
+  PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG, ("pkpin: no matches found\n"));
+  return false;
+}
+
+/**
+ * Given a pinset and certlist, return true if one of the certificates on
+ * the list matches a fingerprint in the pinset, false otherwise.
+ */
+static bool
+EvalChainWithPinset(const CERTCertList* certList,
+                    const StaticPinset* pinset) {
+  // SHA256 is more trustworthy, try that first.
+  if (EvalChainWithHashType(certList, SEC_OID_SHA256, pinset)) {
+    return true;
+  }
+  return EvalChainWithHashType(certList, SEC_OID_SHA1, pinset);
+}
+
+/**
+  Comparator for the is public key pinned host.
+*/
+static int
+TransportSecurityPreloadCompare(const void *key, const void *entry) {
+  const char *keyStr = reinterpret_cast<const char *>(key);
+  const TransportSecurityPreload *preloadEntry =
+    reinterpret_cast<const TransportSecurityPreload *>(entry);
+
+  return strcmp(keyStr, preloadEntry->mHost);
+}
+
+/**
+ * Check PKPins on the given certlist against the specified hostname
+ */
+static bool
+CheckPinsForHostname(const CERTCertList *certList, const char *hostname,
+                     bool enforceTestMode)
+{
+  if (!certList) {
+    return false;
+  }
+  if (!hostname || hostname[0] == 0) {
+    return false;
+  }
+
+  TransportSecurityPreload *foundEntry = nullptr;
+  char *evalHost = const_cast<char*>(hostname);
+  char *evalPart;
+  // Notice how the (xx = strchr) prevents pins for unqualified domain names.
+  while (!foundEntry && (evalPart = strchr(evalHost, '.'))) {
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: Querying pinsets for host: '%s'\n", evalHost));
+    foundEntry = (TransportSecurityPreload *)bsearch(evalHost,
+      kPublicKeyPinningPreloadList,
+      sizeof(kPublicKeyPinningPreloadList) / sizeof(TransportSecurityPreload),
+      sizeof(TransportSecurityPreload),
+      TransportSecurityPreloadCompare);
+    if (foundEntry) {
+      PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+             ("pkpin: Found pinset for host: '%s'\n", evalHost));
+      if (evalHost != hostname) {
+        if (!foundEntry->mIncludeSubdomains) {
+          // Does not apply to this host, continue iterating
+          foundEntry = nullptr;
+        }
+      }
+    } else {
+      PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+             ("pkpin: Didn't find pinset for host: '%s'\n", evalHost));
+    }
+    // Add one for '.'
+    evalHost = evalPart + 1;
+  }
+
+  if (foundEntry && foundEntry->pinset) {
+    bool result = EvalChainWithPinset(certList, foundEntry->pinset);
+    bool retval = result;
+    Telemetry::ID histogram = foundEntry->mIsMoz
+      ? Telemetry::CERT_PINNING_MOZ_RESULTS
+      : Telemetry::CERT_PINNING_RESULTS;
+    if (foundEntry->mTestMode) {
+      histogram = foundEntry->mIsMoz
+        ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS
+        : Telemetry::CERT_PINNING_TEST_RESULTS;
+      if (!enforceTestMode) {
+        retval = true;
+      }
+    }
+    // We can collect per-host pinning violations for this host because it is
+    // operationally critical to Firefox.
+    if (foundEntry->mId != kUnknownId) {
+      int32_t bucket = foundEntry->mId * 2 + (result ? 1 : 0);
+      histogram = foundEntry->mTestMode
+        ? Telemetry::CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST
+        : Telemetry::CERT_PINNING_MOZ_RESULTS_BY_HOST;
+      Telemetry::Accumulate(histogram, bucket);
+    } else {
+      Telemetry::Accumulate(histogram, result ? 1 : 0);
+    }
+    PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+           ("pkpin: Pin check %s for %s host '%s' (mode=%s)\n",
+            result ? "passed" : "failed",
+            foundEntry->mIsMoz ? "mozilla" : "non-mozilla",
+            hostname, foundEntry->mTestMode ? "test" : "production"));
+    return retval;
+  }
+  return true; // No pinning information for this hostname
+}
+
+/**
+ * Extract all the DNS names for a host (including CN) and evaluate the
+ * certifiate pins against all of them (Currently is an OR so we stop
+ * evaluating at the first OK pin).
+ */
+static bool
+CheckChainAgainstAllNames(const CERTCertList* certList, bool enforceTestMode)
+{
+  PR_LOG(gPublicKeyPinningLog, PR_LOG_DEBUG,
+         ("pkpin: top of checkChainAgainstAllNames"));
+  CERTCertListNode* node = CERT_LIST_HEAD(certList);
+  if (!node) {
+    return false;
+  }
+  CERTCertificate* cert = node->cert;
+  if (!cert) {
+    return false;
+  }
+
+  ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
+  if (!arena) {
+    return false;
+  }
+
+  bool hasValidPins = false;
+  CERTGeneralName* nameList;
+  CERTGeneralName* currentName;
+  nameList = CERT_GetConstrainedCertificateNames(cert, arena.get(), PR_TRUE);
+  if (!nameList) {
+    return false;
+  }
+
+  currentName = nameList;
+  do {
+    if (currentName->type == certDNSName
+        && currentName->name.other.data[0] != 0) {
+      // no need to cleaup, as the arena cleanup will do
+      char *hostName = (char *)PORT_ArenaAlloc(arena.get(),
+                                               currentName->name.other.len + 1);
+      if (!hostName) {
+        break;
+      }
+      // We use a temporary buffer as the hostname as returned might not be
+      // null terminated.
+      hostName[currentName->name.other.len] = 0;
+      memcpy(hostName, currentName->name.other.data,
+             currentName->name.other.len);
+      if (!hostName[0]) {
+        // cannot call CheckPinsForHostname on empty or null hostname
+        break;
+      }
+      if (CheckPinsForHostname(certList, hostName, enforceTestMode)) {
+        hasValidPins = true;
+        break;
+      }
+    }
+    currentName = CERT_GetNextGeneralName(currentName);
+  } while (currentName != nameList);
+
+  return hasValidPins;
+}
+
+bool
+PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+                                           const char* hostname,
+                                           const PRTime time,
+                                           bool enforceTestMode)
+{
+  if (!certList) {
+    return false;
+  }
+  if (time > kPreloadPKPinsExpirationTime) {
+    return true;
+  }
+  if (!hostname || hostname[0] == 0) {
+    return CheckChainAgainstAllNames(certList, enforceTestMode);
+  }
+  return CheckPinsForHostname(certList, hostname, enforceTestMode);
+}
diff --git a/security/manager/boot/src/PublicKeyPinningService.cpp.rej b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
new file mode 100644
index 0000000..e88f91b
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.cpp.rej
@@ -0,0 +1,11 @@
+diff a/security/manager/boot/src/PublicKeyPinningService.cpp b/security/manager/boot/src/PublicKeyPinningService.cpp	(rejected hunks)
+@@ -296,7 +296,8 @@ PublicKeyPinningService::ChainHasValidPins(const CERTCertList* certList,
+   if (!certList) {
+     return false;
+   }
+-  if (time > TimeFromElapsedSecondsAD(kPreloadPKPinsExpirationTime)) {
++  if (time > TimeFromEpochInSeconds(kPreloadPKPinsExpirationTime /
++                                    PR_USEC_PER_SEC)) {
+     return true;
+   }
+   if (!hostname || hostname[0] == 0) {
diff --git a/security/manager/boot/src/PublicKeyPinningService.h b/security/manager/boot/src/PublicKeyPinningService.h
new file mode 100644
index 0000000..978c5ec
--- /dev/null
+++ b/security/manager/boot/src/PublicKeyPinningService.h
@@ -0,0 +1,35 @@
+/* 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/. */
+
+#ifndef PublicKeyPinningService_h
+#define PublicKeyPinningService_h
+
+#include "cert.h"
+
+namespace mozilla {
+namespace psm {
+
+class PublicKeyPinningService
+{
+public:
+  /**
+   * Returns true if the given (host, certList) passes pinning checks,
+   * false otherwise. If the host is pinned, return true if one of the keys in
+   * the given certificate chain matches the pin set specified by the
+   * hostname. If the hostname is null or empty evaluate against all the
+   * possible names for the EE cert (Common Name (CN) plus all DNS Name:
+   * subject Alt Name entries). The certList's head is the EE cert and the
+   * tail is the trust anchor.
+   * Note: if an alt name is a wildcard, it won't necessarily find a pinset
+   * that would otherwise be valid for it
+   */
+  static bool ChainHasValidPins(const CERTCertList* certList,
+                                const char* hostname,
+                                const PRTime,
+                                bool enforceTestMode);
+};
+
+}} // namespace mozilla::psm
+
+#endif // PublicKeyPinningServiceService_h
diff --git a/security/manager/boot/src/StaticHPKPins.h b/security/manager/boot/src/StaticHPKPins.h
new file mode 100644
index 0000000..4506489
--- /dev/null
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -0,0 +1,1095 @@
+/* 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/. */
+
+/*****************************************************************************/
+/* This is an automatically generated file. If you're not                    */
+/* PublicKeyPinningService.cpp, you shouldn't be #including it.              */
+/*****************************************************************************/
+#include <stdint.h>
+/* AddTrust External Root */
+static const char kAddTrust_External_RootFingerprint[] =
+  "lCppFqbkrlJ3EcVFAkeip0+44VaoJUymbnOaEUk7tEU=";
+
+/* AddTrust Low-Value Services Root */
+static const char kAddTrust_Low_Value_Services_RootFingerprint[] =
+  "BStocQfshOhzA4JFLsKidFF0XXSFpX1vRk4Np6G2ryo=";
+
+/* AddTrust Public Services Root */
+static const char kAddTrust_Public_Services_RootFingerprint[] =
+  "OGHXtpYfzbISBFb/b8LrdwSxp0G0vZM6g3b14ZFcppg=";
+
+/* AddTrust Qualified Certificates Root */
+static const char kAddTrust_Qualified_Certificates_RootFingerprint[] =
+  "xzr8Lrp3DQy8HuQfJStS6Kk9ErctzOwDHY2DnL+Bink=";
+
+/* AffirmTrust Commercial */
+static const char kAffirmTrust_CommercialFingerprint[] =
+  "bEZLmlsjOl6HTadlwm8EUBDS3c/0V5TwtMfkqvpQFJU=";
+
+/* AffirmTrust Networking */
+static const char kAffirmTrust_NetworkingFingerprint[] =
+  "lAcq0/WPcPkwmOWl9sBMlscQvYSdgxhJGa6Q64kK5AA=";
+
+/* AffirmTrust Premium */
+static const char kAffirmTrust_PremiumFingerprint[] =
+  "x/Q7TPW3FWgpT4IrU3YmBfbd0Vyt7Oc56eLDy6YenWc=";
+
+/* AffirmTrust Premium ECC */
+static const char kAffirmTrust_Premium_ECCFingerprint[] =
+  "MhmwkRT/SVo+tusAwu/qs0ACrl8KVsdnnqCHo/oDfk8=";
+
+/* America Online Root Certification Authority 1 */
+static const char kAmerica_Online_Root_Certification_Authority_1Fingerprint[] =
+  "I4SdCUkj1EpIgbY6sYXpvhWqyO8sMETZNLx/JuLSzWk=";
+
+/* America Online Root Certification Authority 2 */
+static const char kAmerica_Online_Root_Certification_Authority_2Fingerprint[] =
+  "/PfamDYD6IhiAw2WE32OEwMbrftNVsH9TKzDOfa9uyo=";
+
+/* Baltimore CyberTrust Root */
+static const char kBaltimore_CyberTrust_RootFingerprint[] =
+  "Y9mvm0exBk1JoQ57f9Vm28jKo5lFm/woKcVxrYxu80o=";
+
+/* COMODO Certification Authority */
+static const char kCOMODO_Certification_AuthorityFingerprint[] =
+  "AG1751Vd2CAmRCxPGieoDomhmJy4ezREjtIZTBgZbV4=";
+
+/* COMODO ECC Certification Authority */
+static const char kCOMODO_ECC_Certification_AuthorityFingerprint[] =
+  "58qRu/uxh4gFezqAcERupSkRYBlBAvfcw7mEjGPLnNU=";
+
+/* Comodo AAA Services root */
+static const char kComodo_AAA_Services_rootFingerprint[] =
+  "vRU+17BDT2iGsXvOi76E7TQMcTLXAqj0+jGPdW7L1vM=";
+
+/* Comodo Secure Services root */
+static const char kComodo_Secure_Services_rootFingerprint[] =
+  "RpHL/ehKa2BS3b4VK7DCFq4lqG5XR4E9vA8UfzOFcL4=";
+
+/* Comodo Trusted Services root */
+static const char kComodo_Trusted_Services_rootFingerprint[] =
+  "4tiR77c4ZpEF1TDeXtcuKyrD9KZweLU0mz/ayklvXrg=";
+
+/* Cybertrust Global Root */
+static const char kCybertrust_Global_RootFingerprint[] =
+  "foeCwVDOOVL4AuY2AjpdPpW7XWjjPoWtsroXgSXOvxU=";
+
+/* DigiCert Assured ID Root CA */
+static const char kDigiCert_Assured_ID_Root_CAFingerprint[] =
+  "I/Lt/z7ekCWanjD0Cvj5EqXls2lOaThEA0H2Bg4BT/o=";
+
+/* DigiCert ECC Secure Server CA */
+static const char kDigiCert_ECC_Secure_Server_CAFingerprint[] =
+  "PZXN3lRAy+8tBKk2Ox6F7jIlnzr2Yzmwqc3JnyfXoCw=";
+
+/* DigiCert Global Root CA */
+static const char kDigiCert_Global_Root_CAFingerprint[] =
+  "r/mIkG3eEpVdm+u/ko/cwxzOMo1bk4TyHIlByibiA5E=";
+
+/* DigiCert High Assurance EV Root CA */
+static const char kDigiCert_High_Assurance_EV_Root_CAFingerprint[] =
+  "WoiWRyIOVNa9ihaBciRSC7XHjliYS9VwUGOIud4PB18=";
+
+/* End Entity Test Cert */
+static const char kEnd_Entity_Test_CertFingerprint[] =
+  "pVVgLk2kFI2WWRPwDMIX6YmzFhEW4DXQV/U5gP+feGA=";
+
+/* Entrust Root Certification Authority */
+static const char kEntrust_Root_Certification_AuthorityFingerprint[] =
+  "bb+uANN7nNc/j7R95lkXrwDg3d9C286sIMF8AnXuIJU=";
+
+/* Entrust.net Premium 2048 Secure Server CA */
+static const char kEntrust_net_Premium_2048_Secure_Server_CAFingerprint[] =
+  "HqPF5D7WbC2imDpCpKebHpBnhs6fG1hiFBmgBGOofTg=";
+
+/* Equifax Secure CA */
+static const char kEquifax_Secure_CAFingerprint[] =
+  "/1aAzXOlcD2gSBegdf1GJQanNQbEuBoVg+9UlHjSZHY=";
+
+/* Equifax Secure Global eBusiness CA */
+static const char kEquifax_Secure_Global_eBusiness_CAFingerprint[] =
+  "pvH5v4oKndwID7SbHvw9GhwsMtwOE2pbAMlzFvKj3BE=";
+
+/* Equifax Secure eBusiness CA 1 */
+static const char kEquifax_Secure_eBusiness_CA_1Fingerprint[] =
+  "JsGNxu6m9jL2drzrodjCtINS8pwtX82oeOCdy4Mt1uU=";
+
+/* GOOGLE_PIN_AlphaSSL_G2 */
+static const char kGOOGLE_PIN_AlphaSSL_G2Fingerprint[] =
+  "yxgiWGK++SFB9ySwt3M3qpn5HO0ZLFY5D+h+G/vcT/c=";
+
+/* GOOGLE_PIN_CryptoCat1 */
+static const char kGOOGLE_PIN_CryptoCat1Fingerprint[] =
+  "vKaqtTLWmVuXPVJE+0OqN5sRc4VCcSQHI/W3XTDVR24=";
+
+/* GOOGLE_PIN_EntrustRootEC1 */
+static const char kGOOGLE_PIN_EntrustRootEC1Fingerprint[] =
+  "/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
+
+/* GOOGLE_PIN_Entrust_G2 */
+static const char kGOOGLE_PIN_Entrust_G2Fingerprint[] =
+  "du6FkDdMcVQ3u8prumAo6t3i3G27uMP2EOhR8R0at/U=";
+
+/* GOOGLE_PIN_Entrust_SSL */
+static const char kGOOGLE_PIN_Entrust_SSLFingerprint[] =
+  "nsxRNo6G40YPZsKV5JQt1TCA8nseQQr/LRqp1Oa8fnw=";
+
+/* GOOGLE_PIN_GoDaddySecure */
+static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
+  "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
+
+/* GOOGLE_PIN_Libertylavabitcom */
+static const char kGOOGLE_PIN_LibertylavabitcomFingerprint[] =
+  "WnKzsDXgqPtS1KvtImrhQPqcxfpmfssuI2cSJt4LMks=";
+
+/* GOOGLE_PIN_RapidSSL */
+static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
+  "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
+
+/* GOOGLE_PIN_Tor2web */
+static const char kGOOGLE_PIN_Tor2webFingerprint[] =
+  "99ogQzjMuUTBkG1ZP7FME0K4kvBEti8Buzu4nZjRItM=";
+
+/* GTE CyberTrust Global Root */
+static const char kGTE_CyberTrust_Global_RootFingerprint[] =
+  "EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
+
+/* GeoTrust Global CA */
+static const char kGeoTrust_Global_CAFingerprint[] =
+  "h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=";
+
+/* GeoTrust Global CA 2 */
+static const char kGeoTrust_Global_CA_2Fingerprint[] =
+  "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
+
+/* GeoTrust Primary Certification Authority */
+static const char kGeoTrust_Primary_Certification_AuthorityFingerprint[] =
+  "SQVGZiOrQXi+kqxcvWWE96HhfydlLVqFr4lQTqI5qqo=";
+
+/* GeoTrust Primary Certification Authority - G2 */
+static const char kGeoTrust_Primary_Certification_Authority___G2Fingerprint[] =
+  "vPtEqrmtAhAVcGtBIep2HIHJ6IlnWQ9vlK50TciLePs=";
+
+/* GeoTrust Primary Certification Authority - G3 */
+static const char kGeoTrust_Primary_Certification_Authority___G3Fingerprint[] =
+  "q5hJUnat8eyv8o81xTBIeB5cFxjaucjmelBPT2pRMo8=";
+
+/* GeoTrust Universal CA */
+static const char kGeoTrust_Universal_CAFingerprint[] =
+  "lpkiXF3lLlbN0y3y6W0c/qWqPKC7Us2JM8I7XCdEOCA=";
+
+/* GeoTrust Universal CA 2 */
+static const char kGeoTrust_Universal_CA_2Fingerprint[] =
+  "fKoDRlEkWQxgHlZ+UhSOlSwM/+iQAFMP4NlbbVDqrkE=";
+
+/* GlobalSign Root CA */
+static const char kGlobalSign_Root_CAFingerprint[] =
+  "K87oWBWM9UZfyddvDfoxL+8lpNyoUB2ptGtn0fv6G2Q=";
+
+/* GlobalSign Root CA - R2 */
+static const char kGlobalSign_Root_CA___R2Fingerprint[] =
+  "iie1VXtL7HzAMF+/PVPR9xzT80kQxdZeJ+zduCB3uj0=";
+
+/* GlobalSign Root CA - R3 */
+static const char kGlobalSign_Root_CA___R3Fingerprint[] =
+  "cGuxAXyFXFkWm61cF4HPWX8S0srS9j0aSqN0k4AP+4A=";
+
+/* Go Daddy Class 2 CA */
+static const char kGo_Daddy_Class_2_CAFingerprint[] =
+  "VjLZe/p3W/PJnd6lL8JVNBCGQBZynFLdZSTIqcO0SJ8=";
+
+/* Go Daddy Root Certificate Authority - G2 */
+static const char kGo_Daddy_Root_Certificate_Authority___G2Fingerprint[] =
+  "Ko8tivDrEjiY90yGasP6ZpBU4jwXvHqVvQI0GS3GNdA=";
+
+/* GoogleBackup2048 */
+static const char kGoogleBackup2048Fingerprint[] =
+  "vq7OyjSnqOco9nyMCDGdy77eijM=";
+
+/* GoogleG2 */
+static const char kGoogleG2Fingerprint[] =
+  "Q9rWMO5T+KmAym79hfRqo3mQ4Oo=";
+
+/* Network Solutions Certificate Authority */
+static const char kNetwork_Solutions_Certificate_AuthorityFingerprint[] =
+  "MtGA7THJNVieydu7ciEjuIO1/C3BD5/KOpXXfhv8tTQ=";
+
+/* Starfield Class 2 CA */
+static const char kStarfield_Class_2_CAFingerprint[] =
+  "FfFKxFycfaIz00eRZOgTf+Ne4POK6FgYPwhBDqgqxLQ=";
+
+/* Starfield Root Certificate Authority - G2 */
+static const char kStarfield_Root_Certificate_Authority___G2Fingerprint[] =
+  "gI1os/q0iEpflxrOfRBVDXqVoWN3Tz7Dav/7IT++THQ=";
+
+/* Starfield Services Root Certificate Authority - G2 */
+static const char kStarfield_Services_Root_Certificate_Authority___G2Fingerprint[] =
+  "KwccWaCgrnaw6tsrrSO61FgLacNgG2MMLq8GE6+oP5I=";
+
+/* StartCom Certification Authority */
+static const char kStartCom_Certification_AuthorityFingerprint[] =
+  "5C8kvU039KouVrl52D0eZSGf4Onjo4Khs8tmyTlV3nU=";
+
+/* StartCom Certification Authority G2 */
+static const char kStartCom_Certification_Authority_G2Fingerprint[] =
+  "FSg5faISiQqDCwuVpZlozvI0dzd531GBzxD6ZHU0u2U=";
+
+/* TC TrustCenter Class 2 CA II */
+static const char kTC_TrustCenter_Class_2_CA_IIFingerprint[] =
+  "rPZeHWLLWKK6/W/6tA+4hpnEc5fPXLSD1C1pytNM1Is=";
+
+/* TC TrustCenter Class 3 CA II */
+static const char kTC_TrustCenter_Class_3_CA_IIFingerprint[] =
+  "k5KuIUmSSt435kXbof9L3dzaKykbYJdmnSr6XHo3Jhk=";
+
+/* TC TrustCenter Universal CA I */
+static const char kTC_TrustCenter_Universal_CA_IFingerprint[] =
+  "st71NirT+s0EvSkEekOET3ZwNOpIkvgOVr7mkCQ+JQI=";
+
+/* TC TrustCenter Universal CA III */
+static const char kTC_TrustCenter_Universal_CA_IIIFingerprint[] =
+  "q1zbM1Y5c1bW5pGXPCW4YYtl12qQSG6nqKXBd2f0Zzo=";
+
+/* TestSPKI */
+static const char kTestSPKIFingerprint[] =
+  "AAAAAAAAAAAAAAAAAAAAAAAAAAA=";
+
+/* Thawte Premium Server CA */
+static const char kThawte_Premium_Server_CAFingerprint[] =
+  "9TwiBZgX3Zb0AGUWOdL4V+IQcKWavtkHlADZ9pVQaQA=";
+
+/* Thawte Server CA */
+static const char kThawte_Server_CAFingerprint[] =
+  "nG9qEjy6pO402+zu4kyX1ziHjLQj88InOQNCT10fbdU=";
+
+/* Tor1 */
+static const char kTor1Fingerprint[] =
+  "juNxSTv9UANmpC9kF5GKpmWNx3Y=";
+
+/* Tor2 */
+static const char kTor2Fingerprint[] =
+  "lia43lPolzSPVIq34Dw57uYcLD8=";
+
+/* Tor3 */
+static const char kTor3Fingerprint[] =
+  "rzEyQIKOh77j87n5bjWUNguXF8Y=";
+
+/* Twitter1 */
+static const char kTwitter1Fingerprint[] =
+  "Vv7zwhR9TtOIN/29MFI4cgHld40=";
+
+/* UTN DATACorp SGC Root CA */
+static const char kUTN_DATACorp_SGC_Root_CAFingerprint[] =
+  "QAL80xHQczFWfnG82XHkYEjI3OjRZZcRdTs9qiommvo=";
+
+/* UTN USERFirst Email Root CA */
+static const char kUTN_USERFirst_Email_Root_CAFingerprint[] =
+  "Laj56jRU0hFGRko/nQKNxMf7tXscUsc8KwVyovWZotM=";
+
+/* UTN USERFirst Hardware Root CA */
+static const char kUTN_USERFirst_Hardware_Root_CAFingerprint[] =
+  "TUDnr0MEoJ3of7+YliBMBVFB4/gJsv5zO7IxD9+YoWI=";
+
+/* UTN USERFirst Object Root CA */
+static const char kUTN_USERFirst_Object_Root_CAFingerprint[] =
+  "D+FMJksXu28NZT56cOs2Pb9UvhWAOe3a5cJXEd9IwQM=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G4 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint[] =
+  "UZJDjsNp1+4M5x9cbbdflB779y5YRBcV6Z6rBMLIrO4=";
+
+/* VeriSign Class 3 Public Primary Certification Authority - G5 */
+static const char kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint[] =
+  "JbQbUG5JMJUoI6brnx0x3vZF6jilxsapbXGVfjhN8Fg=";
+
+/* VeriSign Universal Root Certification Authority */
+static const char kVeriSign_Universal_Root_Certification_AuthorityFingerprint[] =
+  "lnsM2T/O9/J84sJFdnrpsFp3awZJ+ZZbYpCWhGloaHI=";
+
+/* Verisign Class 1 Public Primary Certification Authority */
+static const char kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint[] =
+  "LclHC+Y+9KzxvYKGCUArt7h72ZY4pkOTTohoLRvowwg=";
+
+/* Verisign Class 1 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint[] =
+  "IgduWu9Eu5pBaii30cRDItcFn2D+/6XK9sW+hEeJEwM=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint[] =
+  "2oALgLKofTmeZvoZ1y/fSZg7R9jPMix8eVA6DH4o/q8=";
+
+/* Verisign Class 2 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint[] =
+  "cAajgxHlj7GTSEIzIYIQxmEloOSoJq7VOaxWHfv72QM=";
+
+/* Verisign Class 3 Public Primary Certification Authority */
+static const char kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint[] =
+  "sRJBQqWhpaKIGcc1NA7/jJ4vgWj+47oYfyU7waOS1+I=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G2 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint[] =
+  "AjyBzOjnxk+pQtPBUEhwfTXZu1uH9PVExb8bxWQ68vo=";
+
+/* Verisign Class 3 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint[] =
+  "SVqWumuteCQHvVIaALrOZXuzVVVeS7f4FGxxu6V+es4=";
+
+/* Verisign Class 4 Public Primary Certification Authority - G3 */
+static const char kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint[] =
+  "VnuCEf0g09KD7gzXzgZyy52ZvFtIeljJ1U7Gf3fUqPU=";
+
+/* XRamp Global CA Root */
+static const char kXRamp_Global_CA_RootFingerprint[] =
+  "BRz5+pXkDpuD7a7aaWH2Fox4ecRmAXJHnN1RqwPOpis=";
+
+/* thawte Primary Root CA */
+static const char kthawte_Primary_Root_CAFingerprint[] =
+  "HXXQgxueCIU5TTLHob/bPbwcKOKw6DkfsTWYHbxbqTY=";
+
+/* thawte Primary Root CA - G2 */
+static const char kthawte_Primary_Root_CA___G2Fingerprint[] =
+  "Z9xPMvoQ59AaeaBzqgyeAhLsL/w9d54Kp/nA8OHCyJM=";
+
+/* thawte Primary Root CA - G3 */
+static const char kthawte_Primary_Root_CA___G3Fingerprint[] =
+  "GQbGEk27Q4V40A4GbVBUxsN/D6YCjAVUXgmU7drshik=";
+
+/* Pinsets are each an ordered list by the actual value of the fingerprint */
+struct StaticFingerprints {
+  const size_t size;
+  const char* const* data;
+};
+
+struct StaticPinset {
+  const StaticFingerprints* sha1;
+  const StaticFingerprints* sha256;
+};
+
+/* PreloadedHPKPins.json pinsets */
+static const char* kPinset_facebook_sha256_Data[] = {
+  kDigiCert_ECC_Secure_Server_CAFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_facebook_sha256 = {
+  sizeof(kPinset_facebook_sha256_Data) / sizeof(const char*),
+  kPinset_facebook_sha256_Data
+};
+
+static const StaticPinset kPinset_facebook = {
+  nullptr,
+  &kPinset_facebook_sha256
+};
+
+static const char* kPinset_google_root_pems_sha256_Data[] = {
+  kEquifax_Secure_CAFingerprint,
+  kAmerica_Online_Root_Certification_Authority_2Fingerprint,
+  kComodo_Trusted_Services_rootFingerprint,
+  kCOMODO_ECC_Certification_AuthorityFingerprint,
+  kStartCom_Certification_AuthorityFingerprint,
+  kStartCom_Certification_AuthorityFingerprint,
+  kThawte_Premium_Server_CAFingerprint,
+  kCOMODO_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+  kXRamp_Global_CA_RootFingerprint,
+  kAddTrust_Low_Value_Services_RootFingerprint,
+  kGeoTrust_Global_CA_2Fingerprint,
+  kStartCom_Certification_Authority_G2Fingerprint,
+  kStarfield_Class_2_CAFingerprint,
+  kthawte_Primary_Root_CA___G3Fingerprint,
+  kthawte_Primary_Root_CAFingerprint,
+  kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+  kDigiCert_Assured_ID_Root_CAFingerprint,
+  kAmerica_Online_Root_Certification_Authority_1Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+  kEquifax_Secure_eBusiness_CA_1Fingerprint,
+  kGlobalSign_Root_CAFingerprint,
+  kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+  kStarfield_Services_Root_Certificate_Authority___G2Fingerprint,
+  kAffirmTrust_Premium_ECCFingerprint,
+  kNetwork_Solutions_Certificate_AuthorityFingerprint,
+  kAddTrust_Public_Services_RootFingerprint,
+  kUTN_DATACorp_SGC_Root_CAFingerprint,
+  kComodo_Secure_Services_rootFingerprint,
+  kGeoTrust_Primary_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kUTN_USERFirst_Hardware_Root_CAFingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+  kGo_Daddy_Class_2_CAFingerprint,
+  kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kBaltimore_CyberTrust_RootFingerprint,
+  kthawte_Primary_Root_CA___G2Fingerprint,
+  kAffirmTrust_CommercialFingerprint,
+  kEntrust_Root_Certification_AuthorityFingerprint,
+  kGlobalSign_Root_CA___R3Fingerprint,
+  kGeoTrust_Universal_CA_2Fingerprint,
+  kCybertrust_Global_RootFingerprint,
+  kStarfield_Root_Certificate_Authority___G2Fingerprint,
+  kGeoTrust_Global_CAFingerprint,
+  kGlobalSign_Root_CA___R2Fingerprint,
+  kTC_TrustCenter_Class_3_CA_IIFingerprint,
+  kAffirmTrust_NetworkingFingerprint,
+  kAddTrust_External_RootFingerprint,
+  kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+  kGeoTrust_Universal_CAFingerprint,
+  kThawte_Server_CAFingerprint,
+  kEquifax_Secure_Global_eBusiness_CAFingerprint,
+  kTC_TrustCenter_Universal_CA_IIIFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_Global_Root_CAFingerprint,
+  kTC_TrustCenter_Class_2_CA_IIFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+  kTC_TrustCenter_Universal_CA_IFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+  kComodo_AAA_Services_rootFingerprint,
+  kAffirmTrust_PremiumFingerprint,
+  kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_google_root_pems_sha256 = {
+  sizeof(kPinset_google_root_pems_sha256_Data) / sizeof(const char*),
+  kPinset_google_root_pems_sha256_Data
+};
+
+static const StaticPinset kPinset_google_root_pems = {
+  nullptr,
+  &kPinset_google_root_pems_sha256
+};
+
+static const char* kPinset_mozilla_sha256_Data[] = {
+  kGeoTrust_Global_CA_2Fingerprint,
+  kthawte_Primary_Root_CA___G3Fingerprint,
+  kthawte_Primary_Root_CAFingerprint,
+  kDigiCert_Assured_ID_Root_CAFingerprint,
+  kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+  kGeoTrust_Primary_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+  kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kBaltimore_CyberTrust_RootFingerprint,
+  kthawte_Primary_Root_CA___G2Fingerprint,
+  kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+  kGeoTrust_Universal_CA_2Fingerprint,
+  kGeoTrust_Global_CAFingerprint,
+  kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+  kGeoTrust_Universal_CAFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_Global_Root_CAFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_sha256 = {
+  sizeof(kPinset_mozilla_sha256_Data) / sizeof(const char*),
+  kPinset_mozilla_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla = {
+  nullptr,
+  &kPinset_mozilla_sha256
+};
+
+static const char* kPinset_mozilla_services_sha256_Data[] = {
+  kDigiCert_Global_Root_CAFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_services_sha256 = {
+  sizeof(kPinset_mozilla_services_sha256_Data) / sizeof(const char*),
+  kPinset_mozilla_services_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_services = {
+  nullptr,
+  &kPinset_mozilla_services_sha256
+};
+
+static const char* kPinset_mozilla_test_sha256_Data[] = {
+  kEnd_Entity_Test_CertFingerprint,
+};
+static const StaticFingerprints kPinset_mozilla_test_sha256 = {
+  sizeof(kPinset_mozilla_test_sha256_Data) / sizeof(const char*),
+  kPinset_mozilla_test_sha256_Data
+};
+
+static const StaticPinset kPinset_mozilla_test = {
+  nullptr,
+  &kPinset_mozilla_test_sha256
+};
+
+/* Chrome static pinsets */
+static const char* kPinset_test_sha1_Data[] = {
+  kTestSPKIFingerprint,
+};
+static const StaticFingerprints kPinset_test_sha1 = {
+  sizeof(kPinset_test_sha1_Data) / sizeof(const char*),
+  kPinset_test_sha1_Data
+};
+
+static const StaticPinset kPinset_test = {
+  &kPinset_test_sha1,
+  nullptr
+};
+
+static const char* kPinset_google_sha1_Data[] = {
+  kGoogleG2Fingerprint,
+  kGoogleBackup2048Fingerprint,
+};
+static const StaticFingerprints kPinset_google_sha1 = {
+  sizeof(kPinset_google_sha1_Data) / sizeof(const char*),
+  kPinset_google_sha1_Data
+};
+
+static const StaticPinset kPinset_google = {
+  &kPinset_google_sha1,
+  nullptr
+};
+
+static const char* kPinset_tor_sha1_Data[] = {
+  kTor1Fingerprint,
+  kTor2Fingerprint,
+  kTor3Fingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha1 = {
+  sizeof(kPinset_tor_sha1_Data) / sizeof(const char*),
+  kPinset_tor_sha1_Data
+};
+
+static const char* kPinset_tor_sha256_Data[] = {
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kGOOGLE_PIN_RapidSSLFingerprint,
+};
+static const StaticFingerprints kPinset_tor_sha256 = {
+  sizeof(kPinset_tor_sha256_Data) / sizeof(const char*),
+  kPinset_tor_sha256_Data
+};
+
+static const StaticPinset kPinset_tor = {
+  &kPinset_tor_sha1,
+  &kPinset_tor_sha256
+};
+
+static const char* kPinset_twitterCom_sha1_Data[] = {
+  kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha1 = {
+  sizeof(kPinset_twitterCom_sha1_Data) / sizeof(const char*),
+  kPinset_twitterCom_sha1_Data
+};
+
+static const char* kPinset_twitterCom_sha256_Data[] = {
+  kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+  kGeoTrust_Global_CA_2Fingerprint,
+  kDigiCert_Assured_ID_Root_CAFingerprint,
+  kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+  kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+  kGeoTrust_Primary_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+  kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+  kGeoTrust_Universal_CA_2Fingerprint,
+  kGeoTrust_Global_CAFingerprint,
+  kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+  kGeoTrust_Universal_CAFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_Global_Root_CAFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCom_sha256 = {
+  sizeof(kPinset_twitterCom_sha256_Data) / sizeof(const char*),
+  kPinset_twitterCom_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCom = {
+  &kPinset_twitterCom_sha1,
+  &kPinset_twitterCom_sha256
+};
+
+static const char* kPinset_twitterCDN_sha1_Data[] = {
+  kTwitter1Fingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha1 = {
+  sizeof(kPinset_twitterCDN_sha1_Data) / sizeof(const char*),
+  kPinset_twitterCDN_sha1_Data
+};
+
+static const char* kPinset_twitterCDN_sha256_Data[] = {
+  kVerisign_Class_2_Public_Primary_Certification_Authority___G2Fingerprint,
+  kComodo_Trusted_Services_rootFingerprint,
+  kCOMODO_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G2Fingerprint,
+  kAddTrust_Low_Value_Services_RootFingerprint,
+  kUTN_USERFirst_Object_Root_CAFingerprint,
+  kGTE_CyberTrust_Global_RootFingerprint,
+  kGeoTrust_Global_CA_2Fingerprint,
+  kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+  kDigiCert_Assured_ID_Root_CAFingerprint,
+  kVerisign_Class_1_Public_Primary_Certification_Authority___G3Fingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G5Fingerprint,
+  kGlobalSign_Root_CAFingerprint,
+  kUTN_USERFirst_Email_Root_CAFingerprint,
+  kVerisign_Class_1_Public_Primary_Certification_AuthorityFingerprint,
+  kAddTrust_Public_Services_RootFingerprint,
+  kUTN_DATACorp_SGC_Root_CAFingerprint,
+  kComodo_Secure_Services_rootFingerprint,
+  kGeoTrust_Primary_Certification_AuthorityFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_Authority___G3Fingerprint,
+  kUTN_USERFirst_Hardware_Root_CAFingerprint,
+  kVeriSign_Class_3_Public_Primary_Certification_Authority___G4Fingerprint,
+  kVerisign_Class_4_Public_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kBaltimore_CyberTrust_RootFingerprint,
+  kEntrust_Root_Certification_AuthorityFingerprint,
+  kVerisign_Class_2_Public_Primary_Certification_Authority___G3Fingerprint,
+  kGlobalSign_Root_CA___R3Fingerprint,
+  kGOOGLE_PIN_Entrust_G2Fingerprint,
+  kGeoTrust_Universal_CA_2Fingerprint,
+  kGeoTrust_Global_CAFingerprint,
+  kGlobalSign_Root_CA___R2Fingerprint,
+  kAddTrust_External_RootFingerprint,
+  kVeriSign_Universal_Root_Certification_AuthorityFingerprint,
+  kGeoTrust_Universal_CAFingerprint,
+  kGOOGLE_PIN_Entrust_SSLFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_Global_Root_CAFingerprint,
+  kVerisign_Class_3_Public_Primary_Certification_AuthorityFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+  kComodo_AAA_Services_rootFingerprint,
+  kAddTrust_Qualified_Certificates_RootFingerprint,
+};
+static const StaticFingerprints kPinset_twitterCDN_sha256 = {
+  sizeof(kPinset_twitterCDN_sha256_Data) / sizeof(const char*),
+  kPinset_twitterCDN_sha256_Data
+};
+
+static const StaticPinset kPinset_twitterCDN = {
+  &kPinset_twitterCDN_sha1,
+  &kPinset_twitterCDN_sha256
+};
+
+static const char* kPinset_tor2web_sha256_Data[] = {
+  kGOOGLE_PIN_Tor2webFingerprint,
+  kGOOGLE_PIN_AlphaSSL_G2Fingerprint,
+};
+static const StaticFingerprints kPinset_tor2web_sha256 = {
+  sizeof(kPinset_tor2web_sha256_Data) / sizeof(const char*),
+  kPinset_tor2web_sha256_Data
+};
+
+static const StaticPinset kPinset_tor2web = {
+  nullptr,
+  &kPinset_tor2web_sha256
+};
+
+static const char* kPinset_cryptoCat_sha256_Data[] = {
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kGOOGLE_PIN_CryptoCat1Fingerprint,
+};
+static const StaticFingerprints kPinset_cryptoCat_sha256 = {
+  sizeof(kPinset_cryptoCat_sha256_Data) / sizeof(const char*),
+  kPinset_cryptoCat_sha256_Data
+};
+
+static const StaticPinset kPinset_cryptoCat = {
+  nullptr,
+  &kPinset_cryptoCat_sha256
+};
+
+static const char* kPinset_lavabit_sha256_Data[] = {
+  kGOOGLE_PIN_LibertylavabitcomFingerprint,
+};
+static const StaticFingerprints kPinset_lavabit_sha256 = {
+  sizeof(kPinset_lavabit_sha256_Data) / sizeof(const char*),
+  kPinset_lavabit_sha256_Data
+};
+
+static const StaticPinset kPinset_lavabit = {
+  nullptr,
+  &kPinset_lavabit_sha256
+};
+
+static const char* kPinset_dropbox_sha256_Data[] = {
+  kGOOGLE_PIN_EntrustRootEC1Fingerprint,
+  kThawte_Premium_Server_CAFingerprint,
+  kthawte_Primary_Root_CA___G3Fingerprint,
+  kthawte_Primary_Root_CAFingerprint,
+  kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
+  kDigiCert_Assured_ID_Root_CAFingerprint,
+  kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
+  kGOOGLE_PIN_GoDaddySecureFingerprint,
+  kGeoTrust_Primary_Certification_AuthorityFingerprint,
+  kGo_Daddy_Class_2_CAFingerprint,
+  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
+  kthawte_Primary_Root_CA___G2Fingerprint,
+  kEntrust_Root_Certification_AuthorityFingerprint,
+  kGOOGLE_PIN_Entrust_G2Fingerprint,
+  kGeoTrust_Global_CAFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G3Fingerprint,
+  kDigiCert_Global_Root_CAFingerprint,
+  kGeoTrust_Primary_Certification_Authority___G2Fingerprint,
+};
+static const StaticFingerprints kPinset_dropbox_sha256 = {
+  sizeof(kPinset_dropbox_sha256_Data) / sizeof(const char*),
+  kPinset_dropbox_sha256_Data
+};
+
+static const StaticPinset kPinset_dropbox = {
+  nullptr,
+  &kPinset_dropbox_sha256
+};
+
+/* Domainlist */
+struct TransportSecurityPreload {
+  const char* mHost;
+  const bool mIncludeSubdomains;
+  const bool mTestMode;
+  const bool mIsMoz;
+  const int32_t mId;
+  const StaticPinset *pinset;
+};
+
+/* Sort hostnames for binary search. */
+static const TransportSecurityPreload kPublicKeyPinningPreloadList[] = {
+  { "2mdn.net", true, false, false, -1, &kPinset_google_root_pems },
+  { "accounts.firefox.com", true, false, false, 4, &kPinset_mozilla_services },
+  { "accounts.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "addons.mozilla.net", true, false, true, 2, &kPinset_mozilla },
+  { "addons.mozilla.org", true, false, true, 1, &kPinset_mozilla },
+  { "admin.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "android.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "api.accounts.firefox.com", true, false, false, 5, &kPinset_mozilla_services },
+  { "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+  { "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla },
+  { "blog.torproject.org", true, false, false, -1, &kPinset_tor },
+  { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+  { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla },
+  { "cdn.mozilla.org", true, false, true, -1, &kPinset_mozilla },
+  { "chart.apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "check.torproject.org", true, false, false, -1, &kPinset_tor },
+  { "checkout.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chrome-devtools-frontend.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chrome.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "chromiumcodereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "cloud.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "code.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "codereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "codereview.chromium.org", true, false, false, -1, &kPinset_google_root_pems },
+  { "crypto.cat", false, true, false, -1, &kPinset_cryptoCat },
+  { "dev.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+  { "dist.torproject.org", true, false, false, -1, &kPinset_tor },
+  { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
+  { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "dropbox.com", false, false, false, -1, &kPinset_dropbox },
+  { "encrypted.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "exclude-subdomains.pinning.example.com", false, false, false, 0, &kPinset_mozilla_test },
+  { "facebook.com", true, true, false, -1, &kPinset_facebook },
+  { "g.co", true, false, false, -1, &kPinset_google_root_pems },
+  { "glass.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "goo.gl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ac", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ad", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ae", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.af", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ag", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.am", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.as", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.at", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.az", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ba", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.be", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.bs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.by", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ca", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cat", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cd", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ch", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ci", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ao", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.bw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ck", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.cr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.hu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.id", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.il", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.im", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.in", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.je", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ke", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.kr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ls", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ma", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.mz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.nz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.th", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.tz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ug", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.uk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.uz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.ve", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.vi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.za", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.zm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.co.zw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.af", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ag", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ai", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ar", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.au", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bd", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.br", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.by", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.bz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.co", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.cy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.do", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ec", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.eg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.et", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.fj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ge", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.gt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.hk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.iq", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.jm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.jo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.kh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.kw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.lb", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ly", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.mt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.mx", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.my", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.na", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.nf", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ng", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ni", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.np", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.nr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.om", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pe", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ph", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.pr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.py", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.qa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ru", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sa", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sb", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.sv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.tw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ua", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.uy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.vc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.ve", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.com.vn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.cz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.de", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dj", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.dz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ee", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.es", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fi", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.fr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ga", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ge", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.gy", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ht", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.hu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ie", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.im", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.info", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.iq", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.is", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.it", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.it.ao", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.je", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jo", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jobs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.kg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ki", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.kz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.la", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.li", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.lv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.md", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.me", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ml", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ms", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mv", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.mw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ne", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ne.jp", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.net", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.no", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nr", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.nu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.off.ai", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ps", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.pt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ro", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.rs", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ru", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.rw", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sc", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.se", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sh", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.si", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.sn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.so", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.st", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.td", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tk", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tl", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tm", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tn", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.to", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.tt", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.us", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.uz", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.vg", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.vu", true, false, false, -1, &kPinset_google_root_pems },
+  { "google.ws", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleadservices.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlecode.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlecommerce.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlegroups.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "googleplex.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googlesyndication.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googletagmanager.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googletagservices.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "googleusercontent.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
+  { "liberty.lavabit.com", true, true, false, -1, &kPinset_lavabit },
+  { "login.corp.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "mail.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "market.android.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "media.mozilla.com", true, false, true, -1, &kPinset_mozilla },
+  { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+  { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+  { "pinningtest.appspot.com", true, false, false, -1, &kPinset_test },
+  { "platform.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+  { "play.google.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "plus.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "plus.sandbox.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "profiles.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "script.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "security.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "services.mozilla.com", true, true, false, -1, &kPinset_mozilla_services },
+  { "sites.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "ssl.google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
+  { "tor2web.org", true, true, false, -1, &kPinset_tor2web },
+  { "torproject.org", false, false, false, -1, &kPinset_tor },
+  { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
+  { "twitter.com", true, false, false, -1, &kPinset_twitterCDN },
+  { "urchin.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "w-spotlight.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wallet.google.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-eu-mirror.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings-mirror-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "webfilings.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-bigsky-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-demo-eu.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-demo-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-dogfood-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-pentest.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-staging-hr.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-training-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-training-master.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "wf-trial-hrd.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "www.dropbox.com", true, false, false, -1, &kPinset_dropbox },
+  { "www.gmail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "www.googlemail.com", false, false, false, -1, &kPinset_google_root_pems },
+  { "www.torproject.org", true, false, false, -1, &kPinset_tor },
+  { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
+  { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
+  { "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
+  { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
+};
+
+// Pinning Preload List Length = 331;
+
+static const int32_t kUnknownId = -1;
+
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1419674828470000);
diff --git a/security/manager/boot/src/moz.build b/security/manager/boot/src/moz.build
index b3d2127..50a345b 100644
--- a/security/manager/boot/src/moz.build
+++ b/security/manager/boot/src/moz.build
@@ -10,6 +10,7 @@ UNIFIED_SOURCES += [
     'nsSecurityHeaderParser.cpp',
     'nsSecurityWarningDialogs.cpp',
     'nsSiteSecurityService.cpp',
+    'PublicKeyPinningService.cpp',
 ]
 
 # nsSecureBrowserUIImpl.cpp cannot be built in unified mode because it forces NSPR logging.
@@ -17,6 +18,11 @@ SOURCES += [
     'nsSecureBrowserUIImpl.cpp',
 ]
 
+
+LOCAL_INCLUDES += [
+    '../../../pkix/include',
+]
+
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
diff --git a/security/manager/ssl/src/SSLServerCertVerification.cpp b/security/manager/ssl/src/SSLServerCertVerification.cpp
index cf15586..18d9f55 100644
--- a/security/manager/ssl/src/SSLServerCertVerification.cpp
+++ b/security/manager/ssl/src/SSLServerCertVerification.cpp
@@ -637,8 +637,9 @@ NSSDetermineCertOverrideErrors(CertVerifier& certVerifier,
   //                     possible failure.
   // XXX TODO: convert to VerifySSLServerCert
   // XXX TODO: get rid of error log
-  certVerifier.VerifyCert(cert, stapledOCSPResponse, certificateUsageSSLServer,
-                          now, infoObject, 0, nullptr, nullptr, verify_log);
+  certVerifier.VerifyCert(cert, certificateUsageSSLServer,
+                          now, infoObject, infoObject->GetHostNameRaw(),
+                          0, stapledOCSPResponse, nullptr, nullptr, verify_log);
 
   // Check the name field against the desired hostname.
   if (CERT_VerifyCertName(cert, infoObject->GetHostNameRaw()) != SECSuccess) {
diff --git a/security/manager/ssl/src/SharedCertVerifier.h b/security/manager/ssl/src/SharedCertVerifier.h
index 32a92a3..c7c1936 100644
--- a/security/manager/ssl/src/SharedCertVerifier.h
+++ b/security/manager/ssl/src/SharedCertVerifier.h
@@ -24,12 +24,14 @@ public:
                      missing_cert_download_config ac, crl_download_config cdc,
 #endif
                      ocsp_download_config odc, ocsp_strict_config osc,
-                     ocsp_get_config ogc)
+                     ocsp_get_config ogc,
+                     pinning_enforcement_config pinningEnforcementLevel)
     : mozilla::psm::CertVerifier(ic,
 #ifndef NSS_NO_LIBPKIX
                                  ac, cdc,
 #endif
-                                 odc, osc, ogc)
+                                 odc, osc, ogc,
+                                 pinningEnforcementLevel)
   {
   }
 };
diff --git a/security/manager/ssl/src/nsCMS.cpp b/security/manager/ssl/src/nsCMS.cpp
index 70a3c49..ee76c07 100644
--- a/security/manager/ssl/src/nsCMS.cpp
+++ b/security/manager/ssl/src/nsCMS.cpp
@@ -264,9 +264,10 @@ nsresult nsCMSMessage::CommonVerifySignature(unsigned char* aDigestData, uint32_
   NS_ENSURE_TRUE(certVerifier, NS_ERROR_UNEXPECTED);
 
   {
-    SECStatus srv = certVerifier->VerifyCert(si->cert, nullptr,
+    SECStatus srv = certVerifier->VerifyCert(si->cert,
                                              certificateUsageEmailSigner,
-                                             PR_Now(), nullptr /*XXX pinarg*/);
+                                             PR_Now(), nullptr /*XXX pinarg*/,
+                                             nullptr /*hostname*/);
     if (srv != SECSuccess) {
       PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
              ("nsCMSMessage::CommonVerifySignature - signing cert not trusted now\n"));
diff --git a/security/manager/ssl/src/nsNSSCertificate.cpp b/security/manager/ssl/src/nsNSSCertificate.cpp
index 88fb8ed..70b9c89 100644
--- a/security/manager/ssl/src/nsNSSCertificate.cpp
+++ b/security/manager/ssl/src/nsNSSCertificate.cpp
@@ -829,10 +829,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
 
   // We want to test all usages, but we start with server because most of the
   // time Firefox users care about server certs.
-  certVerifier->VerifyCert(mCert.get(), nullptr,
+  certVerifier->VerifyCert(mCert.get(),
                            certificateUsageSSLServer, PR_Now(),
                            nullptr, /*XXX fixme*/
+                           nullptr, /* hostname */
                            CertVerifier::FLAG_LOCAL_ONLY,
+                           nullptr, /* stapledOCSPResponse */
                            &nssChain);
   // This is the whitelist of all non-SSLServer usages that are supported by
   // verifycert.
@@ -851,10 +853,12 @@ nsNSSCertificate::GetChain(nsIArray** _rvChain)
     PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
            ("pipnss: PKIX attempting chain(%d) for '%s'\n",
             usage, mCert->nickname));
-    certVerifier->VerifyCert(mCert.get(), nullptr,
+    certVerifier->VerifyCert(mCert.get(),
                              usage, PR_Now(),
                              nullptr, /*XXX fixme*/
+                             nullptr, /*hostname*/
                              CertVerifier::FLAG_LOCAL_ONLY,
+                             nullptr, /* stapledOCSPResponse */
                              &nssChain);
   }
 
@@ -1467,10 +1471,11 @@ nsNSSCertificate::hasValidEVOidTag(SECOidTag& resultOidTag, bool& validEV)
 
   uint32_t flags = mozilla::psm::CertVerifier::FLAG_LOCAL_ONLY |
     mozilla::psm::CertVerifier::FLAG_MUST_BE_EV;
-  SECStatus rv = certVerifier->VerifyCert(mCert.get(), nullptr,
+  SECStatus rv = certVerifier->VerifyCert(mCert.get(),
     certificateUsageSSLServer, PR_Now(),
     nullptr /* XXX pinarg */,
-    flags, nullptr, &resultOidTag);
+    nullptr /* hostname */,
+    flags, nullptr /* stapledOCSPResponse */ , nullptr, &resultOidTag);
 
   if (rv != SECSuccess) {
     resultOidTag = SEC_OID_UNKNOWN;
diff --git a/security/manager/ssl/src/nsNSSCertificateDB.cpp b/security/manager/ssl/src/nsNSSCertificateDB.cpp
index 40b03c1..c9fd8ba 100644
--- a/security/manager/ssl/src/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/src/nsNSSCertificateDB.cpp
@@ -634,9 +634,10 @@ nsNSSCertificateDB::ImportEmailCertificate(uint8_t * data, uint32_t length,
 
     mozilla::pkix::ScopedCERTCertList certChain;
 
-    SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+    SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageEmailRecipient,
-                                            now, ctx, 0, &certChain);
+                                            now, ctx, nullptr, 0,
+                                            nullptr, &certChain);
 
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
@@ -801,9 +802,10 @@ nsNSSCertificateDB::ImportValidCACertsInList(CERTCertList *certList, nsIInterfac
        !CERT_LIST_END(node,certList);
        node = CERT_LIST_NEXT(node)) {
     mozilla::pkix::ScopedCERTCertList certChain;
-    SECStatus rv = certVerifier->VerifyCert(node->cert, nullptr,
+    SECStatus rv = certVerifier->VerifyCert(node->cert,
                                             certificateUsageVerifyCA,
-                                            PR_Now(), ctx, 0, &certChain);
+                                            PR_Now(), ctx, nullptr, 0, nullptr,
+                                            &certChain);
     if (rv != SECSuccess) {
       nsCOMPtr<nsIX509Cert> certToShow = nsNSSCertificate::Create(node->cert);
       DisplayCertificateAlert(ctx, "NotImportingUnverifiedCert", certToShow, proofOfLock);
@@ -1381,9 +1383,10 @@ nsNSSCertificateDB::FindCertByEmailAddress(nsISupports *aToken, const char *aEma
        !CERT_LIST_END(node, certlist);
        node = CERT_LIST_NEXT(node)) {
 
-    SECStatus srv = certVerifier->VerifyCert(node->cert, nullptr,
+    SECStatus srv = certVerifier->VerifyCert(node->cert,
                                              certificateUsageEmailRecipient,
-                                             PR_Now(), nullptr /*XXX pinarg*/);
+                                             PR_Now(), nullptr /*XXX pinarg*/,
+                                             nullptr /*hostname*/);
     if (srv == SECSuccess) {
       break;
     }
@@ -1772,10 +1775,12 @@ nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
   SECOidTag evOidPolicy;
   SECStatus srv;
 
-  srv = certVerifier->VerifyCert(nssCert, nullptr,
+  srv = certVerifier->VerifyCert(nssCert,
                                  aUsage, PR_Now(),
                                  nullptr, // Assume no context
+                                 nullptr, // hostname
                                  aFlags,
+                                 nullptr, // stapledOCSPResponse
                                  &resultChain,
                                  &evOidPolicy);
 
diff --git a/security/manager/ssl/src/nsNSSComponent.cpp b/security/manager/ssl/src/nsNSSComponent.cpp
index 44b3808..c4bd566 100644
--- a/security/manager/ssl/src/nsNSSComponent.cpp
+++ b/security/manager/ssl/src/nsNSSComponent.cpp
@@ -996,6 +996,13 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
     }
   }
 
+  // Default pinning enforcement level is disabled.
+  CertVerifier::pinning_enforcement_config
+    pinningEnforcementLevel =
+      static_cast<CertVerifier::pinning_enforcement_config>
+        (Preferences::GetInt("security.cert_pinning.enforcement_level",
+                             CertVerifier::pinningDisabled));
+
   CertVerifier::ocsp_download_config odc;
   CertVerifier::ocsp_strict_config osc;
   CertVerifier::ocsp_get_config ogc;
@@ -1009,7 +1016,7 @@ void nsNSSComponent::setValidationOptions(bool isInitialSetting,
       crlDownloading ?
         CertVerifier::crl_download_allowed : CertVerifier::crl_local_only,
 #endif
-      odc, osc, ogc);
+      odc, osc, ogc, pinningEnforcementLevel);
 
   // mozilla::pkix has its own OCSP cache, so disable the NSS cache
   // if appropriate.
@@ -1644,7 +1651,8 @@ nsNSSComponent::Observe(nsISupports* aSubject, const char* aTopic,
                || prefName.Equals("security.OCSP.GET.enabled")
                || prefName.Equals("security.ssl.enable_ocsp_stapling")
                || prefName.Equals("security.use_mozillapkix_verification")
-               || prefName.Equals("security.use_libpkix_verification")) {
+               || prefName.Equals("security.use_libpkix_verification")
+               || prefName.Equals("security.cert_pinning.enforcement_level")) {
       MutexAutoLock lock(mutex);
       setValidationOptions(false, lock);
     } else if (prefName.Equals("network.ntlm.send-lm-response")) {
diff --git a/security/manager/ssl/src/nsUsageArrayHelper.cpp b/security/manager/ssl/src/nsUsageArrayHelper.cpp
index 7f7249f..dd65ae5 100644
--- a/security/manager/ssl/src/nsUsageArrayHelper.cpp
+++ b/security/manager/ssl/src/nsUsageArrayHelper.cpp
@@ -105,8 +105,9 @@ nsUsageArrayHelper::check(uint32_t previousCheckResult,
     MOZ_CRASH("unknown cert usage passed to check()");
   }
 
-  SECStatus rv = certVerifier->VerifyCert(mCert, nullptr, aCertUsage,
-                         time, nullptr /*XXX:wincx*/, flags);
+  SECStatus rv = certVerifier->VerifyCert(mCert, aCertUsage, time,
+                                          nullptr /*XXX:wincx*/,
+                                          nullptr /*hostname*/, flags);
 
   if (rv == SECSuccess) {
     typestr.Append(suffix);
diff --git a/security/manager/ssl/tests/unit/head_psm.js b/security/manager/ssl/tests/unit/head_psm.js
index 58e838b..ae25ea9e 100644
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -52,6 +52,7 @@ const SEC_ERROR_OCSP_INVALID_SIGNING_CERT               = SEC_ERROR_BASE + 144;
 const SEC_ERROR_POLICY_VALIDATION_FAILED                = SEC_ERROR_BASE + 160; // -8032
 const SEC_ERROR_OCSP_BAD_SIGNATURE                      = SEC_ERROR_BASE + 157;
 const SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED       = SEC_ERROR_BASE + 176;
+const SEC_ERROR_APPLICATION_CALLBACK_ERROR              = SEC_ERROR_BASE + 178;
 
 const SSL_ERROR_BAD_CERT_DOMAIN                         = SSL_ERROR_BASE +  12;
 
diff --git a/security/manager/ssl/tests/unit/test_cert_overrides.js b/security/manager/ssl/tests/unit/test_cert_overrides.js
index 11e14f3b..91fc77eb 100644
--- a/security/manager/ssl/tests/unit/test_cert_overrides.js
+++ b/security/manager/ssl/tests/unit/test_cert_overrides.js
@@ -232,7 +232,7 @@ function add_distrust_override_test(certFileName, hostName,
   let certToDistrust = constructCertFromFile(certFileName);
 
   add_test(function () {
-    // "pu" means the cert is actively distrusted.
+    // Add an entry to the NSS certDB that says to distrust the cert
     setCertTrust(certToDistrust, "pu,,");
     clearSessionCache();
     run_next_test();
diff --git a/security/manager/ssl/tests/unit/test_pinning.js b/security/manager/ssl/tests/unit/test_pinning.js
new file mode 100644
index 0000000..dc779dd
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_pinning.js
@@ -0,0 +1,182 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 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/.
+//
+// For all cases, the acceptable pinset includes only certificates pinned to
+// Test End Entity Cert (signed by issuer testCA). Other certificates
+// are issued by otherCA, which is never in the pinset but is a user-specified
+// trust anchor. This test covers multiple cases:
+//
+// Pinned domain include-subdomains.pinning.example.com includes subdomains
+// - PASS: include-subdomains.pinning.example.com serves a correct cert
+// - PASS: good.include-subdomains.pinning.example.com serves a correct cert
+// - FAIL (strict): bad.include-subdomains.pinning.example.com serves a cert
+// not in the pinset
+// - PASS (mitm): bad.include-subdomains.pinning.example.com serves a cert not
+// in the pinset, but issued by a user-specified trust domain
+//
+// Pinned domain exclude-subdomains.pinning.example.com excludes subdomains
+// - PASS: exclude-subdomains.pinning.example.com serves a correct cert
+// - FAIL: exclude-subdomains.pinning.example.com services an incorrect cert
+// (TODO: test using verifyCertnow)
+// - PASS: sub.exclude-subdomains.pinning.example.com serves an incorrect cert
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                  .getService(Ci.nsIX509CertDB);
+
+function test_strict() {
+  // In strict mode, we always evaluate pinning data, regardless of whether the
+  // issuer is a built-in trust anchor. We only enforce pins that are not in
+  // test mode.
+  add_test(function() {
+    Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 2);
+    run_next_test();
+  });
+
+  // If a host should be pinned but other errors (particularly overridable
+  // errors) like 'unknown issuer' are encountered, the pinning error takes
+  // precedence. This prevents overrides for such hosts.
+  add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+  // Issued by otherCA, which is not in the pinset for pinning.example.com.
+  add_connection_test("bad.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+  // These domains serve certs that match the pinset.
+  add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+  // This domain serves a cert that doesn't match the pinset, but subdomains
+  // are excluded.
+  add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+  // This domain's pinset is exactly the same as
+  // include-subdomains.pinning.example.com, serves the same cert as
+  // bad.include-subdomains.pinning.example.com, but it should pass because
+  // it's in test_mode.
+  add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+}
+
+function test_mitm() {
+  // In MITM mode, we allow pinning to pass if the chain resolves to any
+  // user-specified trust anchor, even if it is not in the pinset.
+  add_test(function() {
+    Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 1);
+    run_next_test();
+  });
+
+  add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+  add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+
+  // In this case, even though otherCA is not in the pinset, it is a
+  // user-specified trust anchor and the pinning check succeeds.
+  add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+
+  add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+};
+
+function test_disabled() {
+  // Disable pinning.
+  add_test(function() {
+    Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 0);
+    run_next_test();
+  });
+
+  add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("bad.include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("test-mode.pinning.example.com", Cr.NS_OK);
+
+  add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(SEC_ERROR_UNKNOWN_ISSUER));
+}
+
+function test_enforce_test_mode() {
+  // In enforce test mode, we always enforce all pins, even test pins.
+  add_test(function() {
+    Services.prefs.setIntPref("security.cert_pinning.enforcement_level", 3);
+    run_next_test();
+  });
+
+  add_connection_test("unknownissuer.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+  // Issued by otherCA, which is not in the pinset for pinning.example.com.
+  add_connection_test("bad.include-subdomains.pinning.example.com",
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+
+  // These domains serve certs that match the pinset.
+  add_connection_test("include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("good.include-subdomains.pinning.example.com", Cr.NS_OK);
+  add_connection_test("exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+  // This domain serves a cert that doesn't match the pinset, but subdomains
+  // are excluded.
+  add_connection_test("sub.exclude-subdomains.pinning.example.com", Cr.NS_OK);
+
+  // This domain's pinset is exactly the same as
+  // include-subdomains.pinning.example.com, serves the same cert as
+  // bad.include-subdomains.pinning.example.com, is in test-mode, but we are
+  // enforcing test mode pins.
+  add_connection_test("test-mode.pinning.example.com",
+    getXPCOMStatusFromNSS(MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE));
+}
+
+function check_pinning_telemetry() {
+  let service = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry);
+  let prod_histogram = service.getHistogramById("CERT_PINNING_RESULTS")
+                         .snapshot();
+  let test_histogram = service.getHistogramById("CERT_PINNING_TEST_RESULTS")
+                         .snapshot();
+  // Because all of our test domains are pinned to user-specified trust
+  // anchors, effectively only strict mode and enforce test-mode get evaluated
+  do_check_eq(prod_histogram.counts[0], 4); // Failure count
+  do_check_eq(prod_histogram.counts[1], 4); // Success count
+  do_check_eq(test_histogram.counts[0], 2); // Failure count
+  do_check_eq(test_histogram.counts[1], 0); // Success count
+
+  let moz_prod_histogram = service.getHistogramById("CERT_PINNING_MOZ_RESULTS")
+                             .snapshot();
+  let moz_test_histogram =
+    service.getHistogramById("CERT_PINNING_MOZ_TEST_RESULTS").snapshot();
+  do_check_eq(moz_prod_histogram.counts[0], 0); // Failure count
+  do_check_eq(moz_prod_histogram.counts[1], 0); // Success count
+  do_check_eq(moz_test_histogram.counts[0], 0); // Failure count
+  do_check_eq(moz_test_histogram.counts[1], 0); // Success count
+
+  let per_host_histogram =
+    service.getHistogramById("CERT_PINNING_MOZ_RESULTS_BY_HOST").snapshot();
+  do_check_eq(per_host_histogram.counts[0], 0); // Failure count
+  do_check_eq(per_host_histogram.counts[1], 2); // Success count
+  run_next_test();
+}
+
+function run_test() {
+  add_tls_server_setup("BadCertServer");
+
+  // Add a user-specified trust anchor.
+  addCertFromFile(certdb, "tlsserver/other-test-ca.der", "CTu,u,u");
+
+  test_strict();
+  test_mitm();
+  test_disabled();
+  test_enforce_test_mode();
+
+  add_test(function () {
+    check_pinning_telemetry();
+  });
+  run_next_test();
+}
diff --git a/security/manager/ssl/tests/unit/tlsserver/cert8.db b/security/manager/ssl/tests/unit/tlsserver/cert8.db
index 13c101f..7da5d7f 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/cert8.db and b/security/manager/ssl/tests/unit/tlsserver/cert8.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
index a4c59a4..5df134a 100644
--- a/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
+++ b/security/manager/ssl/tests/unit/tlsserver/cmd/BadCertServer.cpp
@@ -24,6 +24,7 @@ struct BadCertHost
   const char *mCertName;
 };
 
+// Hostname, cert nickname pairs.
 const BadCertHost sBadCertHosts[] =
 {
   { "expired.example.com", "expired" },
@@ -42,6 +43,16 @@ const BadCertHost sBadCertHosts[] =
   { "inadequatekeyusage.example.com", "inadequatekeyusage" },
   { "selfsigned-inadequateEKU.example.com", "selfsigned-inadequateEKU" },
   { "self-signed-end-entity-with-cA-true.example.com", "self-signed-EE-with-cA-true" },
+  // All of include-subdomains.pinning.example.com is pinned to End Entity
+  // Test Cert with nick localhostAndExampleCom. Any other nick will only
+  // pass pinning when security.cert_pinning.enforcement.level != strict and
+  // otherCA is added as a user-specified trust anchor. See StaticHPKPins.h.
+  { "include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+  { "good.include-subdomains.pinning.example.com", "localhostAndExampleCom" },
+  { "bad.include-subdomains.pinning.example.com", "otherIssuerEE" },
+  { "exclude-subdomains.pinning.example.com", "localhostAndExampleCom" },
+  { "sub.exclude-subdomains.pinning.example.com", "otherIssuerEE" },
+  { "test-mode.pinning.example.com", "otherIssuerEE" },
   { nullptr, nullptr }
 };
 
diff --git a/security/manager/ssl/tests/unit/tlsserver/default-ee.der b/security/manager/ssl/tests/unit/tlsserver/default-ee.der
index ac98037..9b2359d 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/default-ee.der and b/security/manager/ssl/tests/unit/tlsserver/default-ee.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
index ef388bb..6075a5c 100755
--- a/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
+++ b/security/manager/ssl/tests/unit/tlsserver/generate_certs.sh
@@ -12,7 +12,10 @@
 #
 # NB: This will cause the following files to be overwritten if they are in
 # the output directory:
-#  cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der
+#  cert8.db, key3.db, secmod.db, ocsp-ca.der, ocsp-other-ca.der, default-ee.der
+# NB: You must run genHPKPStaticPins.js after running this file, since its
+# output (StaticHPKPins.h) depends on default-ee.der
+
 set -x
 set -e
 
@@ -25,11 +28,13 @@ OBJDIR=${1}
 OUTPUT_DIR=${2}
 RUN_MOZILLA="$OBJDIR/dist/bin/run-mozilla.sh"
 CERTUTIL="$OBJDIR/dist/bin/certutil"
+# On BSD, mktemp requires either a template or a prefix.
+MKTEMP="mktemp temp.XXXX"
 
-NOISE_FILE=`mktemp`
+NOISE_FILE=`$MKTEMP`
 # Make a good effort at putting something unique in the noise file.
 date +%s%N  > "$NOISE_FILE"
-PASSWORD_FILE=`mktemp`
+PASSWORD_FILE=`$MKTEMP`
 
 function cleanup {
   rm -f "$NOISE_FILE" "$PASSWORD_FILE"
@@ -134,7 +139,11 @@ function make_delegated {
 
 make_CA testCA 'CN=Test CA' test-ca.der
 make_CA otherCA 'CN=Other test CA' other-test-ca.der
-make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com"
+
+make_EE localhostAndExampleCom 'CN=Test End-entity' testCA "localhost,*.example.com,*.pinning.example.com,*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com"
+# Make an EE cert issued by otherCA
+make_EE otherIssuerEE 'CN=Wrong CA Pin Test End-Entity' otherCA "*.include-subdomains.pinning.example.com,*.exclude-subdomains.pinning.example.com,*.pinning.example.com"
+
 $RUN_MOZILLA $CERTUTIL -d $OUTPUT_DIR -L -n localhostAndExampleCom -r > $OUTPUT_DIR/default-ee.der
 # A cert that is like localhostAndExampleCom, but with a different serial number for
 # testing the "OCSP response is from the right issuer, but it is for the wrong cert"
diff --git a/security/manager/ssl/tests/unit/tlsserver/key3.db b/security/manager/ssl/tests/unit/tlsserver/key3.db
index 283e8fb..2d4dd29 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/key3.db and b/security/manager/ssl/tests/unit/tlsserver/key3.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der
index 794fb9c..742bb86 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/other-test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/secmod.db b/security/manager/ssl/tests/unit/tlsserver/secmod.db
index a5f2f60..7a0e2b5 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/secmod.db and b/security/manager/ssl/tests/unit/tlsserver/secmod.db differ
diff --git a/security/manager/ssl/tests/unit/tlsserver/test-ca.der b/security/manager/ssl/tests/unit/tlsserver/test-ca.der
index f4c4863..356de5b 100644
Binary files a/security/manager/ssl/tests/unit/tlsserver/test-ca.der and b/security/manager/ssl/tests/unit/tlsserver/test-ca.der differ
diff --git a/security/manager/ssl/tests/unit/xpcshell.ini b/security/manager/ssl/tests/unit/xpcshell.ini
index 7935c2d..24b0ffc 100644
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -78,3 +78,7 @@ requesttimeoutfactor = 4
 run-sequentially = hardcoded ports
 # Bug  676972: this test times out on Android and B2G
 skip-if = os == "android" || buildapp == "b2g"
+[test_pinning.js]
+run-sequentially = hardcoded ports
+# Bug 676972: test fails consistently on Android and B2G
+fail-if = os == "android" || buildapp == "b2g"
\ No newline at end of file
diff --git a/security/manager/tools/PreloadedHPKPins.json b/security/manager/tools/PreloadedHPKPins.json
new file mode 100644
index 0000000..ed3b9b7
--- /dev/null
+++ b/security/manager/tools/PreloadedHPKPins.json
@@ -0,0 +1,247 @@
+// -*- 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://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.certs",
+    "json_file_url": "https://src.chromium.org/chrome/trunk/src/net/http/transport_security_state_static.json",
+    "substitute_pinsets": {
+      // Use the larger google_root_pems pinset instead of google
+      "google": "google_root_pems"
+    },
+    "production_pinsets": [
+      "google_root_pems"
+    ],
+    "production_domains": [
+      // Chrome's test domain.
+      "pinningtest.appspot.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"
+    ],
+    "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",
+        "America Online Root Certification Authority 1",
+        "America Online Root Certification Authority 2",
+        "Baltimore CyberTrust Root",
+        "Comodo AAA Services root",
+        "COMODO Certification Authority",
+        "COMODO ECC Certification Authority",
+        "Comodo Secure Services root",
+        "Comodo Trusted Services root",
+        "Cybertrust Global Root",
+        "DigiCert Assured ID Root CA",
+        "DigiCert Global Root CA",
+        "DigiCert High Assurance EV Root CA",
+        "Entrust.net Premium 2048 Secure Server CA",
+        // "Entrust.net Secure Server CA",
+        "Entrust Root Certification Authority",
+        "Equifax Secure CA",
+        "Equifax Secure eBusiness CA 1",
+        // "Equifax Secure eBusiness CA 2",
+        "Equifax Secure Global eBusiness 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",
+        "GlobalSign Root CA",
+        "GlobalSign Root CA - R2",
+        "GlobalSign Root CA - R3",
+        "Go Daddy Class 2 CA",
+        "Go Daddy Root Certificate Authority - G2",
+        // "GTE CyberTrust Global Root",
+        "Network Solutions Certificate Authority",
+        // "RSA Root Certificate 1",
+        "Starfield Class 2 CA",
+        "Starfield Root Certificate Authority - G2",
+        "Starfield Services Root Certificate Authority - G2",
+        "StartCom Certification Authority",
+        "StartCom Certification Authority",
+        "StartCom Certification Authority G2",
+        "TC TrustCenter Class 2 CA II",
+        "TC TrustCenter Class 3 CA II",
+        "TC TrustCenter Universal CA I",
+        "TC TrustCenter Universal CA III",
+        "Thawte Premium Server CA",
+        "thawte Primary Root CA",
+        "thawte Primary Root CA - G2",
+        "thawte Primary Root CA - G3",
+        "Thawte Server CA",
+        "UTN DATACorp SGC Root CA",
+        "UTN USERFirst Hardware Root CA",
+        // "ValiCert Class 1 VA",
+        // "ValiCert Class 2 VA",
+        "Verisign Class 3 Public Primary Certification Authority",
+        "Verisign Class 3 Public Primary Certification Authority",
+        "Verisign Class 3 Public Primary Certification Authority - G2",
+        "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",
+        "XRamp Global CA Root"
+      ]
+    },
+    {
+      "name": "facebook",
+      "sha256_hashes": [
+        "Verisign Class 3 Public Primary Certification Authority - G3",
+        "DigiCert High Assurance EV Root CA",
+        "DigiCert ECC Secure Server CA"
+      ]
+    }
+  ],
+
+  "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": "media.mozilla.com", "include_subdomains": true,
+      "pins": "mozilla", "test_mode": false },
+    { "name": "services.mozilla.com", "include_subdomains": true,
+      "pins": "mozilla_services", "test_mode": true },
+    { "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 },
+    // Facebook (not pinned by Chrome)
+    { "name": "facebook.com", "include_subdomains": true,
+      "pins": "facebook", "test_mode": true }
+  ],
+
+  "extra_certificates": [
+     // DigiCert ECC Secure Server CA (for Facebook)
+     "MIIDrDCCApSgAwIBAgIQCssoukZe5TkIdnRw883GEjANBgkqhkiG9w0BAQwFADBhMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSAwHgYDVQQDExdEaWdpQ2VydCBHbG9iYWwgUm9vdCBDQTAeFw0xMzAzMDgxMjAwMDBaFw0yMzAzMDgxMjAwMDBaMEwxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxJjAkBgNVBAMTHURpZ2lDZXJ0IEVDQyBTZWN1cmUgU2VydmVyIENBMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE4ghC6nfYJN6gLGSkE85AnCNyqQIKDjc/ITa4jVMU9tWRlUvzlgKNcR7E2Munn17voOZ/WpIRllNv68DLP679Wz9HJOeaBy6Wvqgvu1cYr3GkvXg6HuhbPGtkESvMNCuMo4IBITCCAR0wEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8EBAMCAYYwNAYIKwYBBQUHAQEEKDAmMCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQgYDVR0fBDswOTA3oDWgM4YxaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0R2xvYmFsUm9vdENBLmNybDA9BgNVHSAENjA0MDIGBFUdIAAwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAdBgNVHQ4EFgQUo53mH/naOU/AbuiRy5Wl2jHiCp8wHwYDVR0jBBgwFoAUA95QNVbRTLtm8KPiGxvDl7I90VUwDQYJKoZIhvcNAQEMBQADggEBAMeKoENL7HTJxavVHzA1Nm6YVntIrAVjrnuaVyRXzG/63qttnMe2uuzO58pzZNvfBDcKAEmzP58
 mrZGMIOgfiA4q+2Y3yDDo0sIkp0VILeoBUEoxlBPfjV/aKrtJPGHzecicZpIalir0ezZYoyxBEHQa0+1IttK7igZFcTMQMHp6mCHdJLnsnLWSB62DxsRq+HfmNb4TDydkskO/g+l3VtsIh5RHFPVfKK+jaEyDj2D3loB5hWp2Jp2VDCADjT7ueihlZGak2YPqmXTNbk19HOuNssWvFhtOyPNV6og4ETQdEa8/B6hPatJ0ES8q/HO3X8IVQwVs1n3aAr0im0/T+Xc="
+  ]
+}
diff --git a/security/manager/tools/genHPKPStaticPins.js b/security/manager/tools/genHPKPStaticPins.js
new file mode 100644
index 0000000..d16d017
--- /dev/null
+++ b/security/manager/tools/genHPKPStaticPins.js
@@ -0,0 +1,576 @@
+/* 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 \
+//                                  [absolute path to]/default-ee.der \
+//                                  [absolute path to]/StaticHPKPins.h
+
+if (arguments.length != 3) {
+  throw "Usage: genHPKPStaticPins.js " +
+        "<absolute path to PreloadedHPKPins.json> " +
+        "<absolute path to default-ee.der> " +
+        "<absolute path to StaticHPKPins.h>";
+}
+
+const { 'classes': Cc, 'interfaces': Ci, 'utils': Cu, 'results': Cr } = Components;
+
+let { NetUtil } = Cu.import("resource://gre/modules/NetUtil.jsm", {});
+let { FileUtils } = Cu.import("resource://gre/modules/FileUtils.jsm", {});
+let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
+
+let gCertDB = Cc["@mozilla.org/security/x509certdb;1"]
+                .getService(Ci.nsIX509CertDB);
+
+const BUILT_IN_NICK_PREFIX = "Builtin Object Token:";
+const SHA1_PREFIX = "sha1/";
+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 <stdint.h>" +
+"\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 StaticPinset *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" +
+  "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];
+
+// 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);
+}
+
+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) {
+  var 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) {
+  var 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);
+  }
+  return req.responseText;
+}
+
+function downloadAsJson(filename) {
+  // we have to filter out '//' comments
+  var result = download(filename).replace(/\/\/[^\n]*\n/g, "");
+  var data = null;
+  try {
+    data = JSON.parse(result);
+  }
+  catch (e) {
+    throw "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;
+}
+
+// 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 "sha1/" + base64(hash), 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 sha1 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, certSKDToName) {
+  // Prefixes that we care about.
+  const BEGIN_CERT = "-----BEGIN CERTIFICATE-----";
+  const END_CERT = "-----END CERTIFICATE-----";
+
+  // Parsing states.
+  const PRE_NAME = 0;
+  const POST_NAME = 1;
+  const IN_CERT = 2;
+  let state = PRE_NAME;
+
+  let lines = download(filename).split("\n");
+  let name = "";
+  let pemCert = "";
+  let hash = "";
+  let chromeNameToHash = {};
+  let chromeNameToMozName = {}
+  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:
+        chromeName = line;
+        state = POST_NAME;
+        break;
+      case POST_NAME:
+        if (line.startsWith(SHA1_PREFIX) ||
+            line.startsWith(SHA256_PREFIX)) {
+          if (line.startsWith(SHA1_PREFIX)) {
+            hash = line.substring(SHA1_PREFIX.length);
+          } else if (line.startsWith(SHA256_PREFIX)) {
+            hash = line.substring(SHA256_PREFIX);
+          }
+          // Store the entire prefixed hash, so we can disambiguate sha1 from
+          // sha256 later.
+          chromeNameToHash[chromeName] = line;
+          certNameToSKD[chromeName] = hash;
+          certSKDToName[hash] = chromeName;
+          state = PRE_NAME;
+        } else if (line.startsWith(BEGIN_CERT)) {
+          state = IN_CERT;
+        } else {
+          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 = "";
+          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;
+      default:
+        throw "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
+//     sha1_hashes: [],
+//     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, sha1_hashes: [], sha256_hashes: [] };
+    // Translate the Chrome pinset format to ours
+    pin.static_spki_hashes.forEach(function(name) {
+      if (name in chromeNameToHash) {
+        let hash = chromeNameToHash[name];
+        if (hash.startsWith(SHA1_PREFIX)) {
+          hash = hash.substring(SHA1_PREFIX.length);
+          pinset.sha1_hashes.push(certSKDToName[hash]);
+        } else if (hash.startsWith(SHA256_PREFIX)) {
+          hash = hash.substring(SHA256_PREFIX.length);
+          pinset.sha256_hashes.push(certSKDToName[hash]);
+        } else {
+          throw("Unsupported hash type: " + chromeNameToHash[name]);
+        }
+        // We should have already added hashes for all of these when we
+        // imported the certificate file.
+        if (!certNameToSKD[name]) {
+          throw("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) {
+    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);
+    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(derTestFile, 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;
+  }
+
+  {
+    // A certificate for *.example.com.
+    let der = readFileToString(derTestFile);
+    let testCert = gCertDB.constructX509(der, der.length);
+    // We can't include this cert in the previous loop, because it skips
+    // non-builtin certs and the nickname is not built-in to the cert.
+    let name = "End Entity Test Cert";
+    let SKD  = testCert.sha256SubjectPublicKeyInfoDigest;
+    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) {
+  // We aren't guaranteed to have sha1 hashes in our own imported pins.
+  let prefix = "kPinset_" + pinset.name;
+  let sha1Name = "nullptr";
+  let sha256Name = "nullptr";
+  if (pinset.sha1_hashes && pinset.sha1_hashes.length > 0) {
+    writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+                      pinset.sha1_hashes, "sha1");
+    sha1Name = "&" + prefix + "_sha1";
+  }
+  if (pinset.sha256_hashes && pinset.sha256_hashes.length > 0) {
+    writeFingerprints(certNameToSKD, certSKDToName, pinset.name,
+                      pinset.sha256_hashes, "sha256");
+    sha256Name = "&" + prefix + "_sha256";
+  }
+  writeString("static const StaticPinset " + prefix + " = {\n" +
+          "  " + sha1Name + ",\n  " + sha256Name + "\n};\n\n");
+}
+
+function writeFingerprints(certNameToSKD, certSKDToName, name, hashes, type) {
+  let varPrefix = "kPinset_" + name + "_" + type;
+  writeString("static const char* " + varPrefix + "_Data[] = {\n");
+  let SKDList = [];
+  for (let certName of hashes) {
+    if (!(certName in certNameToSKD)) {
+      throw "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 == "mozilla")) {
+    printVal += "true, ";
+  } else {
+    printVal += "false, ";
+  }
+  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 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 = {};
+  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) {
+      pinset.sha256_hashes.forEach(function(name) {
+        usedFingerprints[name] = true;
+      });
+    }
+  });
+  for (let key in chromeImportedPinsets) {
+    let pinset = chromeImportedPinsets[key];
+    pinset.sha1_hashes.forEach(function(name) {
+      usedFingerprints[name] = true;
+    });
+    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) {
+    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;
+}
+
+let extraCertificates = loadExtraCertificates(gStaticPins.extra_certificates);
+let [ certNameToSKD, certSKDToName ] = loadNSSCertinfo(gTestCertFile,
+                                                       extraCertificates);
+let [ chromeNameToHash, chromeNameToMozName ] = downloadAndParseChromeCerts(
+  gStaticPins.chromium_data.cert_file_url, certSKDToName);
+let [ chromeImportedPinsets, chromeImportedEntries ] =
+  downloadAndParseChromePins(gStaticPins.chromium_data.json_file_url,
+    chromeNameToHash, chromeNameToMozName, certNameToSKD, certSKDToName);
+
+writeFile(certNameToSKD, certSKDToName, chromeImportedPinsets,
+          chromeImportedEntries);
+
+FileUtils.closeSafeFileOutputStream(gFileOutputStream);
diff --git a/security/pkix/include/pkix/Result.h b/security/pkix/include/pkix/Result.h
new file mode 100644
index 0000000..e82e6428
--- /dev/null
+++ b/security/pkix/include/pkix/Result.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2013 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Result_h
+#define mozilla_pkix__Result_h
+
+#include <cassert>
+
+#include "pkix/enumclass.h"
+
+namespace mozilla { namespace pkix {
+
+static const unsigned int FATAL_ERROR_FLAG = 0x800;
+
+// The first argument to MOZILLA_PKIX_MAP() is used for building the mapping
+// from error code to error name in MapResultToName.
+//
+// The second argument is for defining the value for the enum literal in the
+// Result enum class.
+//
+// The third argument to MOZILLA_PKIX_MAP() is used, along with the first
+// argument, for maintaining the mapping of mozilla::pkix error codes to
+// NSS/NSPR error codes in pkixnss.cpp.
+#define MOZILLA_PKIX_MAP_LIST \
+    MOZILLA_PKIX_MAP(Success, 0, 0) \
+    MOZILLA_PKIX_MAP(ERROR_BAD_DER, 1, \
+                     SEC_ERROR_BAD_DER) \
+    MOZILLA_PKIX_MAP(ERROR_CA_CERT_INVALID, 2, \
+                     SEC_ERROR_CA_CERT_INVALID) \
+    MOZILLA_PKIX_MAP(ERROR_BAD_SIGNATURE, 3, \
+                     SEC_ERROR_BAD_SIGNATURE) \
+    MOZILLA_PKIX_MAP(ERROR_CERT_BAD_ACCESS_LOCATION, 4, \
+                     SEC_ERROR_CERT_BAD_ACCESS_LOCATION) \
+    MOZILLA_PKIX_MAP(ERROR_CERT_NOT_IN_NAME_SPACE, 5, \
+                     SEC_ERROR_CERT_NOT_IN_NAME_SPACE) \
+    MOZILLA_PKIX_MAP(ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED, 6, \
+                     SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED) \
+    MOZILLA_PKIX_MAP(ERROR_CONNECT_REFUSED, 7, \
+                     PR_CONNECT_REFUSED_ERROR) \
+    MOZILLA_PKIX_MAP(ERROR_EXPIRED_CERTIFICATE, 8, \
+                     SEC_ERROR_EXPIRED_CERTIFICATE) \
+    MOZILLA_PKIX_MAP(ERROR_EXTENSION_VALUE_INVALID, 9, \
+                     SEC_ERROR_EXTENSION_VALUE_INVALID) \
+    MOZILLA_PKIX_MAP(ERROR_INADEQUATE_CERT_TYPE, 10, \
+                     SEC_ERROR_INADEQUATE_CERT_TYPE) \
+    MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_USAGE, 11, \
+                     SEC_ERROR_INADEQUATE_KEY_USAGE) \
+    MOZILLA_PKIX_MAP(ERROR_INVALID_ALGORITHM, 12, \
+                     SEC_ERROR_INVALID_ALGORITHM) \
+    MOZILLA_PKIX_MAP(ERROR_INVALID_TIME, 13, \
+                     SEC_ERROR_INVALID_TIME) \
+    MOZILLA_PKIX_MAP(ERROR_KEY_PINNING_FAILURE, 14, \
+                     MOZILLA_PKIX_ERROR_KEY_PINNING_FAILURE) \
+    MOZILLA_PKIX_MAP(ERROR_PATH_LEN_CONSTRAINT_INVALID, 15, \
+                     SEC_ERROR_PATH_LEN_CONSTRAINT_INVALID) \
+    MOZILLA_PKIX_MAP(ERROR_POLICY_VALIDATION_FAILED, 16, \
+                     SEC_ERROR_POLICY_VALIDATION_FAILED) \
+    MOZILLA_PKIX_MAP(ERROR_REVOKED_CERTIFICATE, 17, \
+                     SEC_ERROR_REVOKED_CERTIFICATE) \
+    MOZILLA_PKIX_MAP(ERROR_UNKNOWN_CRITICAL_EXTENSION, 18, \
+                     SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION) \
+    MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ERROR, 19, \
+                     PR_UNKNOWN_ERROR) \
+    MOZILLA_PKIX_MAP(ERROR_UNKNOWN_ISSUER, 20, \
+                     SEC_ERROR_UNKNOWN_ISSUER) \
+    MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_CERT, 21, \
+                     SEC_ERROR_UNTRUSTED_CERT) \
+    MOZILLA_PKIX_MAP(ERROR_UNTRUSTED_ISSUER, 22, \
+                     SEC_ERROR_UNTRUSTED_ISSUER) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_BAD_SIGNATURE, 23, \
+                     SEC_ERROR_OCSP_BAD_SIGNATURE) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_INVALID_SIGNING_CERT, 24, \
+                     SEC_ERROR_OCSP_INVALID_SIGNING_CERT) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_REQUEST, 25, \
+                     SEC_ERROR_OCSP_MALFORMED_REQUEST) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_MALFORMED_RESPONSE, 26, \
+                     SEC_ERROR_OCSP_MALFORMED_RESPONSE) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_OLD_RESPONSE, 27, \
+                     SEC_ERROR_OCSP_OLD_RESPONSE) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_REQUEST_NEEDS_SIG, 28, \
+                     SEC_ERROR_OCSP_REQUEST_NEEDS_SIG) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_RESPONDER_CERT_INVALID, 29, \
+                     SEC_ERROR_OCSP_RESPONDER_CERT_INVALID) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_SERVER_ERROR, 30, \
+                     SEC_ERROR_OCSP_SERVER_ERROR) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_TRY_SERVER_LATER, 31, \
+                     SEC_ERROR_OCSP_TRY_SERVER_LATER) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_UNAUTHORIZED_REQUEST, 32, \
+                     SEC_ERROR_OCSP_UNAUTHORIZED_REQUEST) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_RESPONSE_STATUS, 33, \
+                     SEC_ERROR_OCSP_UNKNOWN_RESPONSE_STATUS) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_UNKNOWN_CERT, 34, \
+                     SEC_ERROR_OCSP_UNKNOWN_CERT) \
+    MOZILLA_PKIX_MAP(ERROR_OCSP_FUTURE_RESPONSE, 35, \
+                     SEC_ERROR_OCSP_FUTURE_RESPONSE) \
+    MOZILLA_PKIX_MAP(ERROR_INVALID_KEY, 36, \
+                     SEC_ERROR_INVALID_KEY) \
+    MOZILLA_PKIX_MAP(ERROR_UNSUPPORTED_KEYALG, 37, \
+                     SEC_ERROR_UNSUPPORTED_KEYALG) \
+    MOZILLA_PKIX_MAP(ERROR_EXPIRED_ISSUER_CERTIFICATE, 38, \
+                     SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE) \
+    MOZILLA_PKIX_MAP(ERROR_CA_CERT_USED_AS_END_ENTITY, 39, \
+                     MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY) \
+    MOZILLA_PKIX_MAP(ERROR_INADEQUATE_KEY_SIZE, 40, \
+                     MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE) \
+    MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_ARGS, FATAL_ERROR_FLAG | 1, \
+                     SEC_ERROR_INVALID_ARGS) \
+    MOZILLA_PKIX_MAP(FATAL_ERROR_INVALID_STATE, FATAL_ERROR_FLAG | 2, \
+                     PR_INVALID_STATE_ERROR) \
+    MOZILLA_PKIX_MAP(FATAL_ERROR_LIBRARY_FAILURE, FATAL_ERROR_FLAG | 3, \
+                     SEC_ERROR_LIBRARY_FAILURE) \
+    MOZILLA_PKIX_MAP(FATAL_ERROR_NO_MEMORY, FATAL_ERROR_FLAG | 4, \
+                     SEC_ERROR_NO_MEMORY) \
+    /* nothing here */
+
+MOZILLA_PKIX_ENUM_CLASS Result
+{
+#define MOZILLA_PKIX_MAP(name, value, nss_name) name = value,
+  MOZILLA_PKIX_MAP_LIST
+#undef MOZILLA_PKIX_MAP
+};
+
+// Returns the stringified name of the given result, e.g. "Result::Success",
+// or nullptr if result is unknown (invalid).
+const char* MapResultToName(Result result);
+
+// We write many comparisons as (x != Success), and this shortened name makes
+// those comparisons clearer, especially because the shortened name often
+// results in less line wrapping.
+//
+// Visual Studio before VS2013 does not support "enum class," so
+// Result::Success will already be visible in this scope, and compilation will
+// fail if we try to define a variable with that name here.
+#if !defined(_MSC_VER) || (_MSC_VER >= 1700)
+static const Result Success = Result::Success;
+#endif
+
+inline bool
+IsFatalError(Result rv)
+{
+  return (static_cast<unsigned int>(rv) & FATAL_ERROR_FLAG) != 0;
+}
+
+inline Result
+NotReached(const char* /*explanation*/, Result result)
+{
+  assert(false);
+  return result;
+}
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Result_h
diff --git a/security/pkix/include/pkix/Time.h b/security/pkix/include/pkix/Time.h
new file mode 100644
index 0000000..b8d6ee9
--- /dev/null
+++ b/security/pkix/include/pkix/Time.h
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef mozilla_pkix__Time_h
+#define mozilla_pkix__Time_h
+
+#include <ctime>
+#include <limits>
+#include <stdint.h>
+
+#include "pkix/Result.h"
+
+namespace mozilla { namespace pkix {
+
+// Time with a range from the first second of year 0 (AD) through at least the
+// last second of year 9999, which is the range of legal times in X.509 and
+// OCSP. This type has second-level precision. The time zone is always UTC.
+//
+// Pass by value, not by reference.
+class Time
+{
+public:
+  // Construct an uninitilized instance.
+  //
+  // This will fail to compile because there is no default constructor:
+  //    Time x;
+  //
+  // This will succeed, leaving the time uninitialized:
+  //    Time x(Time::uninitialized);
+  enum Uninitialized { uninitialized };
+  explicit Time(Uninitialized) { }
+
+  bool operator==(const Time& other) const
+  {
+    return elapsedSecondsAD == other.elapsedSecondsAD;
+  }
+  bool operator>(const Time& other) const
+  {
+    return elapsedSecondsAD > other.elapsedSecondsAD;
+  }
+  bool operator>=(const Time& other) const
+  {
+    return elapsedSecondsAD >= other.elapsedSecondsAD;
+  }
+  bool operator<(const Time& other) const
+  {
+    return elapsedSecondsAD < other.elapsedSecondsAD;
+  }
+  bool operator<=(const Time& other) const
+  {
+    return elapsedSecondsAD <= other.elapsedSecondsAD;
+  }
+
+  Result AddSeconds(uint64_t seconds)
+  {
+    if (std::numeric_limits<uint64_t>::max() - elapsedSecondsAD
+          < seconds) {
+      return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+    }
+    elapsedSecondsAD += seconds;
+    return Success;
+  }
+
+  Result SubtractSeconds(uint64_t seconds)
+  {
+    if (seconds > elapsedSecondsAD) {
+      return Result::FATAL_ERROR_INVALID_ARGS; // integer overflow
+    }
+    elapsedSecondsAD -= seconds;
+    return Success;
+  }
+
+  static const uint64_t ONE_DAY_IN_SECONDS
+    = UINT64_C(24) * UINT64_C(60) * UINT64_C(60);
+
+private:
+  // This constructor is hidden to prevent accidents like this:
+  //
+  //    Time foo(time_t t)
+  //    {
+  //      // WRONG! 1970-01-01-00:00:00 == time_t(0), but not Time(0)!
+  //      return Time(t);
+  //    }
+  explicit Time(uint64_t elapsedSecondsAD)
+    : elapsedSecondsAD(elapsedSecondsAD)
+  {
+  }
+  friend Time TimeFromElapsedSecondsAD(uint64_t);
+
+  uint64_t elapsedSecondsAD;
+};
+
+inline Time TimeFromElapsedSecondsAD(uint64_t elapsedSecondsAD)
+{
+  return Time(elapsedSecondsAD);
+}
+
+Time Now();
+
+// Note the epoch is the unix epoch (ie 00:00:00 UTC, 1 January 1970)
+Time TimeFromEpochInSeconds(uint64_t secondsSinceEpoch);
+
+} } // namespace mozilla::pkix
+
+#endif // mozilla_pkix__Time_h
diff --git a/security/pkix/include/pkix/pkixtypes.h b/security/pkix/include/pkix/pkixtypes.h
index 0a64b9d..d38603b 100644
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -120,6 +120,11 @@ public:
                                     PRTime time,
                        /*optional*/ const SECItem* stapledOCSPresponse) = 0;
 
+  // Called as soon as we think we have a valid chain but before revocation
+  // checks are done. Called to compute additional chain level checks, by the
+  // TrustDomain.
+  virtual SECStatus IsChainValid(const CERTCertList* certChain) = 0;
+
 protected:
   TrustDomain() { }
 
diff --git a/security/pkix/lib/pkixbuild.cpp b/security/pkix/lib/pkixbuild.cpp
index 22e3f61..c078eda 100644
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -225,6 +225,30 @@ BuildForward(TrustDomain& trustDomain,
   }
 
   if (trustLevel == TrustDomain::TrustAnchor) {
+    ScopedCERTCertList certChain(CERT_NewCertList());
+    if (!certChain) {
+      PR_SetError(SEC_ERROR_NO_MEMORY, 0);
+      return MapSECStatus(SECFailure);
+    }
+
+    rv = subject.PrependNSSCertToList(certChain.get());
+    if (rv != Success) {
+      return rv;
+    }
+    BackCert* child = subject.childCert;
+    while (child) {
+      rv = child->PrependNSSCertToList(certChain.get());
+      if (rv != Success) {
+        return rv;
+      }
+      child = child->childCert;
+    }
+
+    SECStatus srv = trustDomain.IsChainValid(certChain.get());
+    if (srv != SECSuccess) {
+      return MapSECStatus(srv);
+    }
+
     // End of the recursion. Create the result list and add the trust anchor to
     // it.
     results = CERT_NewCertList();
diff --git a/security/pkix/lib/pkixtime.cpp b/security/pkix/lib/pkixtime.cpp
new file mode 100644
index 0000000..499784e
--- /dev/null
+++ b/security/pkix/lib/pkixtime.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkix/Time.h"
+#include "pkixutil.h"
+#ifdef WIN32
+#include "windows.h"
+#else
+#include "sys/time.h"
+#endif
+
+namespace mozilla { namespace pkix {
+
+Time
+Now()
+{
+  uint64_t seconds;
+
+#ifdef WIN32
+  // "Contains a 64-bit value representing the number of 100-nanosecond
+  // intervals since January 1, 1601 (UTC)."
+  //   - http://msdn.microsoft.com/en-us/library/windows/desktop/ms724284(v=vs.85).aspx
+  FILETIME ft;
+  GetSystemTimeAsFileTime(&ft);
+  uint64_t ft64 = (static_cast<uint64_t>(ft.dwHighDateTime) << 32) |
+                  ft.dwLowDateTime;
+  seconds = (DaysBeforeYear(1601) * Time::ONE_DAY_IN_SECONDS) +
+            ft64 / (1000u * 1000u * 1000u / 100u);
+#else
+  // "The gettimeofday() function shall obtain the current time, expressed as
+  // seconds and microseconds since the Epoch."
+  //   - http://pubs.opengroup.org/onlinepubs/009695399/functions/gettimeofday.html
+  timeval tv;
+  (void) gettimeofday(&tv, nullptr);
+  seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) + tv.tv_sec;
+#endif
+
+  return TimeFromElapsedSecondsAD(seconds);
+}
+
+Time
+TimeFromEpochInSeconds(uint64_t secondsSinceEpoch)
+{
+  uint64_t seconds = (DaysBeforeYear(1970) * Time::ONE_DAY_IN_SECONDS) +
+                     secondsSinceEpoch;
+  return TimeFromElapsedSecondsAD(seconds);
+}
+
+} } // namespace mozilla::pkix
diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json
index 01a27a1..bf96ccd 100644
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -5929,5 +5929,37 @@
     "high": "5000",
     "n_buckets": 10,
     "extended_statistics_ok": true
+  },
+  "CERT_PINNING_RESULTS": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Certificate pinning results (0 = failure, 1 = success)"
+  },
+  "CERT_PINNING_TEST_RESULTS": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Certificate pinning test results (0 = failure, 1 = success)"
+  },
+  "CERT_PINNING_MOZ_RESULTS": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Certificate pinning results for Mozilla sites (0 = failure, 1 = success)"
+  },
+  "CERT_PINNING_MOZ_TEST_RESULTS": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "Certificate pinning test results for Mozilla sites (0 = failure, 1 = success)"
+  },
+  "CERT_PINNING_MOZ_RESULTS_BY_HOST": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 512,
+    "description": "Certificate pinning results by host for Mozilla operational sites"
+  },
+  "CERT_PINNING_MOZ_TEST_RESULTS_BY_HOST": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 512,
+    "description": "Certificate pinning test results by host for Mozilla operational sites"
   }
 }



More information about the tor-commits mailing list