Bug 1006958: Use mozilla::pkix::der to parse certificate policies instead of NSS, r=keeler
authorBrian Smith <brian@briansmith.org>
Thu, 15 May 2014 18:59:52 -0700
changeset 183447 a4ae7060f43ac1a4e49b30dfd7a95c5212940d4b
parent 183446 fe7bffe6bb06fbe0cf047f1c979acb14875d3d1c
child 183448 6deed68c2358d584d0988b200adc23fc88aa4113
push id43556
push userbrian@briansmith.org
push dateFri, 16 May 2014 07:43:05 +0000
treeherdermozilla-inbound@6deed68c2358 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1006958
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1006958: Use mozilla::pkix::der to parse certificate policies instead of NSS, r=keeler
security/apps/AppSignatureVerification.cpp
security/apps/AppTrustDomain.cpp
security/apps/AppTrustDomain.h
security/certverifier/CertVerifier.cpp
security/certverifier/ExtendedValidation.cpp
security/certverifier/ExtendedValidation.h
security/certverifier/NSSCertDBTrustDomain.cpp
security/certverifier/NSSCertDBTrustDomain.h
security/pkix/include/pkix/bind.h
security/pkix/include/pkix/pkix.h
security/pkix/include/pkix/pkixtypes.h
security/pkix/lib/pkixbuild.cpp
security/pkix/lib/pkixcheck.cpp
security/pkix/lib/pkixcheck.h
security/pkix/lib/pkixder.h
security/pkix/lib/pkixocsp.cpp
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -600,17 +600,18 @@ VerifySignature(AppTrustedRoot trustedRo
 
   // Verify certificate.
   AppTrustDomain trustDomain(nullptr); // TODO: null pinArg
   if (trustDomain.SetTrustedRoot(trustedRoot) != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
   if (BuildCertChain(trustDomain, signerCert, PR_Now(),
                      EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
-                     KeyPurposeId::id_kp_codeSigning, SEC_OID_X509_ANY_POLICY,
+                     KeyPurposeId::id_kp_codeSigning,
+                     CertPolicyId::anyPolicy,
                      nullptr, builtChain)
         != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
   SECOidData* contentTypeOidData =
     SECOID_FindOID(&signedData->contentInfo.contentType);
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -98,25 +98,25 @@ AppTrustDomain::FindPotentialIssuers(con
 
   results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                        encodedIssuerName, time, true);
   return SECSuccess;
 }
 
 SECStatus
 AppTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
-                             SECOidTag policy,
+                             const CertPolicyId& policy,
                              const CERTCertificate* candidateCert,
                      /*out*/ TrustLevel* trustLevel)
 {
-  MOZ_ASSERT(policy == SEC_OID_X509_ANY_POLICY);
+  MOZ_ASSERT(policy.IsAnyPolicy());
   MOZ_ASSERT(candidateCert);
   MOZ_ASSERT(trustLevel);
   MOZ_ASSERT(mTrustedRoot);
-  if (!candidateCert || !trustLevel || policy != SEC_OID_X509_ANY_POLICY) {
+  if (!candidateCert || !trustLevel || !policy.IsAnyPolicy()) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
   }
   if (!mTrustedRoot) {
     PR_SetError(PR_INVALID_STATE_ERROR, 0);
     return SECFailure;
   }
 
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -16,17 +16,17 @@ namespace mozilla { namespace psm {
 class AppTrustDomain MOZ_FINAL : public mozilla::pkix::TrustDomain
 {
 public:
   AppTrustDomain(void* pinArg);
 
   SECStatus SetTrustedRoot(AppTrustedRoot trustedRoot);
 
   SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                         SECOidTag policy,
+                         const mozilla::pkix::CertPolicyId& policy,
                          const CERTCertificate* candidateCert,
                  /*out*/ mozilla::pkix::TrustLevel* trustLevel) MOZ_OVERRIDE;
   SECStatus FindPotentialIssuers(const SECItem* encodedIssuerName,
                                  PRTime time,
                          /*out*/ mozilla::pkix::ScopedCERTCertList& results)
                                  MOZ_OVERRIDE;
   SECStatus VerifySignedData(const CERTSignedData* signedData,
                              const CERTCertificate* cert) MOZ_OVERRIDE;
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -296,17 +296,17 @@ destroyCertListThatShouldNotExist(CERTCe
   }
 }
 #endif
 
 static SECStatus
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
                              PRTime time, KeyUsages ku1, KeyUsages ku2,
                              KeyUsages ku3, KeyPurposeId eku,
-                             SECOidTag requiredPolicy,
+                             const CertPolicyId& requiredPolicy,
                              const SECItem* stapledOCSPResponse,
                              ScopedCERTCertList& builtChain)
 {
   PR_ASSERT(ku1);
   PR_ASSERT(ku2);
 
   SECStatus rv = BuildCertChain(trustDomain, cert, time,
                                 EndEntityOrCA::MustBeEndEntity, ku1,
@@ -387,47 +387,48 @@ CertVerifier::MozillaPKIXVerifyCert(
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
                           KeyPurposeId::id_kp_clientAuth,
-                          SEC_OID_X509_ANY_POLICY, stapledOCSPResponse,
+                          CertPolicyId::anyPolicy, stapledOCSPResponse,
                           builtChain);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
       // restrict the acceptable key usage based on the key exchange method
       // chosen by the server.
 
 #ifndef MOZ_NO_EV_CERTS
       // Try to validate for EV first.
-      SECOidTag evPolicy = SEC_OID_UNKNOWN;
-      rv = GetFirstEVPolicy(cert, evPolicy);
-      if (rv == SECSuccess && evPolicy != SEC_OID_UNKNOWN) {
+      CertPolicyId evPolicy;
+      SECOidTag evPolicyOidTag;
+      rv = GetFirstEVPolicy(cert, evPolicy, evPolicyOidTag);
+      if (rv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
                       mOCSPCache, pinArg, &callbackContainer);
         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                           KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                           KU_KEY_ENCIPHERMENT, // RSA
                                           KU_KEY_AGREEMENT, // ECDH/DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse,
                                           builtChain);
         if (rv == SECSuccess) {
           if (evOidPolicy) {
-            *evOidPolicy = evPolicy;
+            *evOidPolicy = evPolicyOidTag;
           }
           break;
         }
         builtChain = nullptr; // clear built chain, just in case.
       }
 #endif
 
       if (flags & FLAG_MUST_BE_EV) {
@@ -439,63 +440,65 @@ CertVerifier::MozillaPKIXVerifyCert(
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg, &callbackContainer);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_DIGITAL_SIGNATURE, // ECDHE/DHE
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         KeyPurposeId::id_kp_serverAuth,
-                                        SEC_OID_X509_ANY_POLICY,
+                                        CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg);
       rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
                           KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth,
-                          SEC_OID_X509_ANY_POLICY,
+                          CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
-                          KeyPurposeId::id_kp_emailProtection, SEC_OID_X509_ANY_POLICY,
+                          KeyPurposeId::id_kp_emailProtection,
+                          CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailRecipient: {
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
                                         KU_KEY_ENCIPHERMENT, // RSA
                                         KU_KEY_AGREEMENT, // ECDH/DH
                                         0,
                                         KeyPurposeId::id_kp_emailProtection,
-                                        SEC_OID_X509_ANY_POLICY,
+                                        CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        mOCSPCache, pinArg);
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
-                          KeyPurposeId::id_kp_codeSigning, SEC_OID_X509_ANY_POLICY,
+                          KeyPurposeId::id_kp_codeSigning,
+                          CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageVerifyCA:
     case certificateUsageStatusResponder: {
       // XXX This is a pretty useless way to verify a certificate. It is used
       // by the implementation of window.crypto.importCertificates and in the
@@ -512,30 +515,30 @@ CertVerifier::MozillaPKIXVerifyCert(
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
         keyUsage = KU_DIGITAL_SIGNATURE;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
                                     pinArg);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
-                          keyUsage, eku, SEC_OID_X509_ANY_POLICY,
+                          keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv == SECFailure && PR_GetError() == SEC_ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, ocspFetching, mOCSPCache,
                                         pinArg);
         rv = BuildCertChain(emailTrust, cert, time, endEntityOrCA, keyUsage,
-                            eku, SEC_OID_X509_ANY_POLICY,
+                            eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse, builtChain);
         if (rv == SECFailure && SEC_ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   ocspFetching, mOCSPCache,
                                                   pinArg);
           rv = BuildCertChain(objectSigningTrust, cert, time, endEntityOrCA,
-                              keyUsage, eku, SEC_OID_X509_ANY_POLICY,
+                              keyUsage, eku, CertPolicyId::anyPolicy,
                               stapledOCSPResponse, builtChain);
         }
       }
 
       break;
     }
 
     default:
@@ -608,17 +611,18 @@ CertVerifier::VerifyCert(CERTCertificate
 
 #ifndef NSS_NO_LIBPKIX
   ScopedCERTCertList trustAnchors;
   SECStatus rv;
   SECOidTag evPolicy = SEC_OID_UNKNOWN;
 
   // Do EV checking only for sslserver usage
   if (usage == certificateUsageSSLServer) {
-    SECStatus srv = GetFirstEVPolicy(cert, evPolicy);
+    CertPolicyId unusedPolicyId;
+    SECStatus srv = GetFirstEVPolicy(cert, unusedPolicyId, evPolicy);
     if (srv == SECSuccess) {
       if (evPolicy != SEC_OID_UNKNOWN) {
         trustAnchors = GetRootsForOid(evPolicy);
       }
       if (!trustAnchors) {
         return SECFailure;
       }
       // pkix ignores an empty trustanchors list and
--- a/security/certverifier/ExtendedValidation.cpp
+++ b/security/certverifier/ExtendedValidation.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ExtendedValidation.h"
 
 #include "cert.h"
 #include "certdb.h"
 #include "base64.h"
 #include "pkix/nullptr.h"
+#include "pkix/pkixtypes.h"
 #include "pk11pub.h"
 #include "secerr.h"
 #include "prerror.h"
 #include "prinit.h"
 
 #ifdef PR_LOGGING
 extern PRLogModuleInfo* gPIPNSSLog;
 #endif
@@ -875,29 +876,31 @@ GetRootsForOid(SECOidTag oid_tag)
   }
 
   return certList;
 }
 #endif
 
 bool
 CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
-                               SECOidTag policyOidTag)
+                               const mozilla::pkix::CertPolicyId& policy)
 {
   PR_ASSERT(cert);
-  PR_ASSERT(policyOidTag != SEC_OID_UNKNOWN);
-  if (!cert || !policyOidTag) {
+  if (!cert) {
     return false;
   }
 
   for (size_t iEV = 0; iEV < PR_ARRAY_SIZE(myTrustedEVInfos); ++iEV) {
     nsMyTrustedEVInfo& entry = myTrustedEVInfos[iEV];
-    if (entry.oid_tag == policyOidTag && entry.cert &&
-        CERT_CompareCerts(cert, entry.cert)) {
-      return true;
+    if (entry.cert && CERT_CompareCerts(cert, entry.cert)) {
+      const SECOidData* oidData = SECOID_FindOIDByTag(entry.oid_tag);
+      if (oidData && oidData->oid.len == policy.numBytes &&
+          !memcmp(oidData->oid.data, policy.bytes, policy.numBytes)) {
+        return true;
+      }
     }
   }
 
   return false;
 }
 
 static PRStatus
 IdentityInfoInit()
@@ -1000,20 +1003,24 @@ CleanupIdentityInfo()
     }
   }
 
   memset(&sIdentityInfoCallOnce, 0, sizeof(PRCallOnceType));
 }
 
 // Find the first policy OID that is known to be an EV policy OID.
 SECStatus
-GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag)
+GetFirstEVPolicy(CERTCertificate* cert,
+                 /*out*/ mozilla::pkix::CertPolicyId& policy,
+                 /*out*/ SECOidTag& policyOidTag)
 {
-  if (!cert)
+  if (!cert) {
+    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
+  }
 
   if (cert->extensions) {
     for (int i=0; cert->extensions[i]; i++) {
       const SECItem* oid = &cert->extensions[i]->id;
 
       SECOidTag oidTag = SECOID_FindOIDTag(oid);
       if (oidTag != SEC_OID_X509_CERTIFICATE_POLICIES)
         continue;
@@ -1030,24 +1037,35 @@ GetFirstEVPolicy(CERTCertificate* cert, 
       policyInfos = policies->policyInfos;
 
       bool found = false;
       while (*policyInfos) {
         const CERTPolicyInfo* policyInfo = *policyInfos++;
 
         SECOidTag oid_tag = policyInfo->oid;
         if (oid_tag != SEC_OID_UNKNOWN && isEVPolicy(oid_tag)) {
-          // in our list of OIDs accepted for EV
-          outOidTag = oid_tag;
-          found = true;
+          const SECOidData* oidData = SECOID_FindOIDByTag(oid_tag);
+          PR_ASSERT(oidData);
+          PR_ASSERT(oidData->oid.data);
+          PR_ASSERT(oidData->oid.len > 0);
+          PR_ASSERT(oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES);
+          if (oidData && oidData->oid.data && oidData->oid.len > 0 &&
+              oidData->oid.len <= mozilla::pkix::CertPolicyId::MAX_BYTES) {
+            policy.numBytes = static_cast<uint16_t>(oidData->oid.len);
+            memcpy(policy.bytes, oidData->oid.data, policy.numBytes);
+            policyOidTag = oid_tag;
+            found = true;
+          }
           break;
         }
       }
       CERT_DestroyCertificatePoliciesExtension(policies);
-      if (found)
+      if (found) {
         return SECSuccess;
+      }
     }
   }
 
+  PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
   return SECFailure;
 }
 
 } } // namespace mozilla::psm
--- a/security/certverifier/ExtendedValidation.h
+++ b/security/certverifier/ExtendedValidation.h
@@ -4,27 +4,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_psm_ExtendedValidation_h
 #define mozilla_psm_ExtendedValidation_h
 
 #include "certt.h"
 #include "prtypes.h"
 
+namespace mozilla { namespace pkix { struct CertPolicyId; } }
+
 namespace mozilla { namespace psm {
 
 #ifndef MOZ_NO_EV_CERTS
 void EnsureIdentityInfoLoaded();
 void CleanupIdentityInfo();
-SECStatus GetFirstEVPolicy(CERTCertificate* cert, SECOidTag& outOidTag);
+SECStatus GetFirstEVPolicy(CERTCertificate* cert,
+                           /*out*/ mozilla::pkix::CertPolicyId& policy,
+                           /*out*/ SECOidTag& policyOidTag);
 
 // CertIsAuthoritativeForEVPolicy does NOT evaluate whether the cert is trusted
 // or distrusted.
 bool CertIsAuthoritativeForEVPolicy(const CERTCertificate* cert,
-                                    SECOidTag policyOidTag);
+                                    const mozilla::pkix::CertPolicyId& policy);
 #endif
 
 #ifndef NSS_NO_LIBPKIX
 CERTCertList* GetRootsForOid(SECOidTag oid_tag);
 #endif
 
 } } // namespace mozilla::psm
 
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -63,30 +63,30 @@ NSSCertDBTrustDomain::FindPotentialIssue
   // "there was an error trying to retrieve the potential issuers."
   results = CERT_CreateSubjectCertList(nullptr, CERT_GetDefaultCertDB(),
                                        encodedIssuerName, time, true);
   return SECSuccess;
 }
 
 SECStatus
 NSSCertDBTrustDomain::GetCertTrust(EndEntityOrCA endEntityOrCA,
-                                   SECOidTag policy,
+                                   const CertPolicyId& policy,
                                    const CERTCertificate* candidateCert,
                                    /*out*/ TrustLevel* trustLevel)
 {
   PR_ASSERT(candidateCert);
   PR_ASSERT(trustLevel);
 
   if (!candidateCert || !trustLevel) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
   }
 
 #ifdef MOZ_NO_EV_CERTS
-  if (policy != SEC_OID_X509_ANY_POLICY) {
+  if (!policy.IsAnyPolicy()) {
     PR_SetError(SEC_ERROR_POLICY_VALIDATION_FAILED, 0);
     return SECFailure;
   }
 #endif
 
   // XXX: CERT_GetCertTrust seems to be abusing SECStatus as a boolean, where
   // SECSuccess means that there is a trust record and SECFailure means there
   // is not a trust record. I looked at NSS's internal uses of
@@ -109,17 +109,17 @@ NSSCertDBTrustDomain::GetCertTrust(EndEn
       *trustLevel = TrustLevel::ActivelyDistrusted;
       return SECSuccess;
     }
 
     // For TRUST, we only use the CERTDB_TRUSTED_CA bit, because Gecko hasn't
     // needed to consider end-entity certs to be their own trust anchors since
     // Gecko implemented nsICertOverrideService.
     if (flags & CERTDB_TRUSTED_CA) {
-      if (policy == SEC_OID_X509_ANY_POLICY) {
+      if (policy.IsAnyPolicy()) {
         *trustLevel = TrustLevel::TrustAnchor;
         return SECSuccess;
       }
 #ifndef MOZ_NO_EV_CERTS
       if (CertIsAuthoritativeForEVPolicy(candidateCert, policy)) {
         *trustLevel = TrustLevel::TrustAnchor;
         return SECSuccess;
       }
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -61,17 +61,17 @@ public:
                        CERTChainVerifyCallback* checkChainCallback = nullptr);
 
   virtual SECStatus FindPotentialIssuers(
                         const SECItem* encodedIssuerName,
                         PRTime time,
                 /*out*/ mozilla::pkix::ScopedCERTCertList& results);
 
   virtual SECStatus GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
-                                 SECOidTag policy,
+                                 const mozilla::pkix::CertPolicyId& policy,
                                  const CERTCertificate* candidateCert,
                          /*out*/ mozilla::pkix::TrustLevel* trustLevel);
 
   virtual SECStatus VerifySignedData(const CERTSignedData* signedData,
                                      const CERTCertificate* cert);
 
   virtual SECStatus CheckRevocation(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                                     const CERTCertificate* cert,
--- a/security/pkix/include/pkix/bind.h
+++ b/security/pkix/include/pkix/bind.h
@@ -91,17 +91,17 @@ private:
   B2& b2;
   void operator=(const Bind2&) /*= delete*/;
 };
 
 template <typename R, typename P1, typename B1, typename B2, typename B3>
 class Bind3
 {
 public:
-  typedef R (*F)(P1&, B1&, B2&, B3&);
+  typedef R (*F)(P1&, B1, B2&, B3&);
   Bind3(F f, B1& b1, B2& b2, B3& b3) : f(f), b1(b1), b2(b2), b3(b3) { }
   R operator()(P1& p1) const { return f(p1, b1, b2, b3); }
 private:
   const F f;
   B1& b1;
   B2& b2;
   B3& b3;
   void operator=(const Bind3&) /*= delete*/;
@@ -137,20 +137,20 @@ bind(R (*f)(P1&, B1&), Placeholder1&, B1
 template <typename R, typename P1, typename B1, typename B2>
 inline internal::Bind2<R, P1, B1, B2>
 bind(R (*f)(P1&, B1&, B2&), Placeholder1&, B1& b1, B2& b2)
 {
   return internal::Bind2<R, P1, B1, B2>(f, b1, b2);
 }
 
 template <typename R, typename P1, typename B1, typename B2, typename B3>
-inline internal::Bind3<R, P1, B1, B2, B3>
-bind(R (*f)(P1&, B1&, B2&, B3&), Placeholder1&, B1& b1, B2& b2, B3& b3)
+inline internal::Bind3<R, P1, const B1, const B2, B3>
+bind(R (*f)(P1&, B1, const B2&, B3&), Placeholder1&, B1& b1, const B2& b2, B3& b3)
 {
-  return internal::Bind3<R, P1, B1, B2, B3>(f, b1, b2, b3);
+  return internal::Bind3<R, P1, const B1, const B2, B3>(f, b1, b2, b3);
 }
 
 template <typename R, typename P1, typename B1, typename B2, typename B3,
           typename B4>
 inline internal::Bind4<R, P1, const B1, const B2, B3, B4>
 bind(R (*f)(P1&, B1, B2, B3&, B4&), Placeholder1&, const B1& b1, const B2& b2,
      B3& b3, B4& b4)
 {
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -90,17 +90,17 @@ namespace mozilla { namespace pkix {
 // TODO(bug 968451): Document more of these.
 
 SECStatus BuildCertChain(TrustDomain& trustDomain,
                          CERTCertificate* cert,
                          PRTime time,
                          EndEntityOrCA endEntityOrCA,
             /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
                          KeyPurposeId requiredEKUIfPresent,
-            /*optional*/ SECOidTag requiredPolicy,
+                         const CertPolicyId& requiredPolicy,
             /*optional*/ const SECItem* stapledOCSPResponse,
                  /*out*/ ScopedCERTCertList& results);
 
 // Verify the given signed data using the public key of the given certificate.
 // (EC)DSA parameter inheritance is not supported.
 SECStatus VerifySignedData(const CERTSignedData* sd,
                            const CERTCertificate* cert,
                            void* pkcs11PinArg);
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -25,16 +25,17 @@
 #ifndef mozilla_pkix__pkixtypes_h
 #define mozilla_pkix__pkixtypes_h
 
 #include "pkix/enumclass.h"
 #include "pkix/ScopedPtr.h"
 #include "plarena.h"
 #include "cert.h"
 #include "keyhi.h"
+#include "stdint.h"
 
 namespace mozilla { namespace pkix {
 
 typedef ScopedPtr<PLArenaPool, PL_FreeArenaPool> ScopedPLArenaPool;
 
 typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
         ScopedCERTCertificate;
 typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
@@ -49,16 +50,26 @@ MOZILLA_PKIX_ENUM_CLASS KeyPurposeId {
   anyExtendedKeyUsage = 0,
   id_kp_serverAuth = 1,           // id-kp-serverAuth
   id_kp_clientAuth = 2,           // id-kp-clientAuth
   id_kp_codeSigning = 3,          // id-kp-codeSigning
   id_kp_emailProtection = 4,      // id-kp-emailProtection
   id_kp_OCSPSigning = 9,          // id-kp-OCSPSigning
 };
 
+struct CertPolicyId {
+  uint16_t numBytes;
+  static const uint16_t MAX_BYTES = 24;
+  uint8_t bytes[MAX_BYTES];
+
+  bool IsAnyPolicy() const;
+
+  static const CertPolicyId anyPolicy;
+};
+
 MOZILLA_PKIX_ENUM_CLASS TrustLevel {
   TrustAnchor = 1,        // certificate is a trusted root CA certificate or
                           // equivalent *for the given policy*.
   ActivelyDistrusted = 2, // certificate is known to be bad
   InheritsTrust = 3       // certificate must chain to a trust anchor
 };
 
 // Applications control the behavior of path building and verification by
@@ -69,26 +80,26 @@ class TrustDomain
 {
 public:
   virtual ~TrustDomain() { }
 
   // Determine the level of trust in the given certificate for the given role.
   // This will be called for every certificate encountered during path
   // building.
   //
-  // When policy == SEC_OID_X509_ANY_POLICY, then no policy-related checking
-  // should be done. When policy != SEC_OID_X509_ANY_POLICY, then GetCertTrust
-  // MUST NOT return with *trustLevel == TrustAnchor unless the given cert is
-  // considered a trust anchor *for that policy*. In particular, if the user
-  // has marked an intermediate certificate as trusted, but that intermediate
-  // isn't in the list of EV roots, then GetCertTrust must result in
+  // When policy.IsAnyPolicy(), then no policy-related checking should be done.
+  // When !policy.IsAnyPolicy(), then GetCertTrust MUST NOT return with
+  // *trustLevel == TrustAnchor unless the given cert is considered a trust
+  // anchor *for that policy*. In particular, if the user has marked an
+  // intermediate certificate as trusted, but that intermediate isn't in the
+  // list of EV roots, then GetCertTrust must result in
   // *trustLevel == InheritsTrust instead of *trustLevel == TrustAnchor
   // (assuming the candidate cert is not actively distrusted).
   virtual SECStatus GetCertTrust(EndEntityOrCA endEntityOrCA,
-                                 SECOidTag policy,
+                                 const CertPolicyId& policy,
                                  const CERTCertificate* candidateCert,
                          /*out*/ TrustLevel* trustLevel) = 0;
 
   // Find all certificates (intermediate and/or root) in the certificate
   // database that have a subject name matching |encodedIssuerName| at
   // the given time. Certificates where the given time is not within the
   // certificate's validity period may be excluded. On input, |results|
   // will be null on input. If no potential issuers are found, then this
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -110,29 +110,29 @@ BackCert::Init()
 }
 
 static Result BuildForward(TrustDomain& trustDomain,
                            BackCert& subject,
                            PRTime time,
                            EndEntityOrCA endEntityOrCA,
                            KeyUsages requiredKeyUsagesIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
-                           SECOidTag requiredPolicy,
+                           const CertPolicyId& requiredPolicy,
                            /*optional*/ const SECItem* stapledOCSPResponse,
                            unsigned int subCACount,
                            /*out*/ ScopedCERTCertList& results);
 
 // The code that executes in the inner loop of BuildForward
 static Result
 BuildForwardInner(TrustDomain& trustDomain,
                   BackCert& subject,
                   PRTime time,
                   EndEntityOrCA endEntityOrCA,
                   KeyPurposeId requiredEKUIfPresent,
-                  SECOidTag requiredPolicy,
+                  const CertPolicyId& requiredPolicy,
                   CERTCertificate* potentialIssuerCertToDup,
                   unsigned int subCACount,
                   ScopedCERTCertList& results)
 {
   PORT_Assert(potentialIssuerCertToDup);
 
   BackCert potentialIssuer(potentialIssuerCertToDup, &subject,
                            BackCert::IncludeCN::No);
@@ -192,17 +192,17 @@ BuildForwardInner(TrustDomain& trustDoma
 // pkix/pkix.h.
 static Result
 BuildForward(TrustDomain& trustDomain,
              BackCert& subject,
              PRTime time,
              EndEntityOrCA endEntityOrCA,
              KeyUsages requiredKeyUsagesIfPresent,
              KeyPurposeId requiredEKUIfPresent,
-             SECOidTag requiredPolicy,
+             const CertPolicyId& requiredPolicy,
              /*optional*/ const SECItem* stapledOCSPResponse,
              unsigned int subCACount,
              /*out*/ ScopedCERTCertList& results)
 {
   // Avoid stack overflows and poor performance by limiting cert length.
   // XXX: 6 is not enough for chains.sh anypolicywithlevel.cfg tests
   static const size_t MAX_DEPTH = 8;
   if (subCACount >= MAX_DEPTH - 1) {
@@ -332,17 +332,17 @@ BuildForward(TrustDomain& trustDomain,
 
 SECStatus
 BuildCertChain(TrustDomain& trustDomain,
                CERTCertificate* certToDup,
                PRTime time,
                EndEntityOrCA endEntityOrCA,
                /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
                /*optional*/ KeyPurposeId requiredEKUIfPresent,
-               /*optional*/ SECOidTag requiredPolicy,
+               const CertPolicyId& requiredPolicy,
                /*optional*/ const SECItem* stapledOCSPResponse,
                /*out*/ ScopedCERTCertList& results)
 {
   PORT_Assert(certToDup);
 
   if (!certToDup) {
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -102,73 +102,126 @@ CheckKeyUsage(EndEntityOrCA endEntityOrC
     //  return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
     //}
   }
 
   return Success;
 }
 
 // RFC5820 4.2.1.4. Certificate Policies
-//
+
 // "The user-initial-policy-set contains the special value any-policy if the
 // user is not concerned about certificate policy."
-Result
-CheckCertificatePolicies(BackCert& cert, EndEntityOrCA endEntityOrCA,
-                         bool isTrustAnchor, SECOidTag requiredPolicy)
+//
+// id-ce OBJECT IDENTIFIER  ::=  {joint-iso-ccitt(2) ds(5) 29}
+// id-ce-certificatePolicies OBJECT IDENTIFIER ::=  { id-ce 32 }
+// anyPolicy OBJECT IDENTIFIER ::= { id-ce-certificatePolicies 0 }
+
+/*static*/ const CertPolicyId CertPolicyId::anyPolicy = {
+  4, { (40*2)+5, 29, 32, 0 }
+};
+
+bool CertPolicyId::IsAnyPolicy() const
 {
-  if (requiredPolicy == SEC_OID_X509_ANY_POLICY) {
-    return Success;
+  return this == &anyPolicy ||
+         (numBytes == anyPolicy.numBytes &&
+          !memcmp(bytes, anyPolicy.bytes, anyPolicy.numBytes));
+}
+
+// PolicyInformation ::= SEQUENCE {
+//         policyIdentifier   CertPolicyId,
+//         policyQualifiers   SEQUENCE SIZE (1..MAX) OF
+//                                 PolicyQualifierInfo OPTIONAL }
+inline der::Result
+CheckPolicyInformation(der::Input& input, EndEntityOrCA endEntityOrCA,
+                       const CertPolicyId& requiredPolicy,
+                       /*in/out*/ bool& found)
+{
+  if (input.MatchTLV(der::OIDTag, requiredPolicy.numBytes,
+                     requiredPolicy.bytes)) {
+    found = true;
+  } else if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
+             input.MatchTLV(der::OIDTag, CertPolicyId::anyPolicy.numBytes,
+                            CertPolicyId::anyPolicy.bytes)) {
+    found = true;
   }
 
-  // It is likely some callers will pass SEC_OID_UNKNOWN when they don't care,
-  // instead of passing SEC_OID_X509_ANY_POLICY. Help them out by failing hard.
-  if (requiredPolicy == SEC_OID_UNKNOWN) {
-    PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
-    return FatalError;
+  // RFC 5280 Section 4.2.1.4 says "Optional qualifiers, which MAY be present,
+  // are not expected to change the definition of the policy." Also, it seems
+  // that Section 6, which defines validation, does not require any matching of
+  // qualifiers. Thus, doing anything with the policy qualifiers would be a
+  // waste of time and a source of potential incompatibilities, so we just
+  // ignore them.
+
+  // Skip unmatched OID and/or policyQualifiers
+  input.SkipToEnd();
+
+  return der::Success;
+}
+
+// certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation
+Result
+CheckCertificatePolicies(EndEntityOrCA endEntityOrCA,
+                         const SECItem* encodedCertificatePolicies,
+                         const SECItem* encodedInhibitAnyPolicy,
+                         TrustLevel trustLevel,
+                         const CertPolicyId& requiredPolicy)
+{
+  if (requiredPolicy.numBytes == 0 ||
+      requiredPolicy.numBytes > sizeof requiredPolicy.bytes) {
+    return Fail(FatalError, SEC_ERROR_INVALID_ARGS);
+  }
+
+  // Ignore all policy information if the caller indicates any policy is
+  // acceptable. See TrustDomain::GetCertTrust and the policy part of
+  // BuildCertChain's documentation.
+  if (requiredPolicy.IsAnyPolicy()) {
+    return Success;
   }
 
   // Bug 989051. Until we handle inhibitAnyPolicy we will fail close when
   // inhibitAnyPolicy extension is present and we need to evaluate certificate
   // policies.
-  if (cert.encodedInhibitAnyPolicy) {
+  if (encodedInhibitAnyPolicy) {
     return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
   }
 
   // The root CA certificate may omit the policies that it has been
   // trusted for, so we cannot require the policies to be present in those
   // certificates. Instead, the determination of which roots are trusted for
   // which policies is made by the TrustDomain's GetCertTrust method.
-  if (isTrustAnchor && endEntityOrCA == EndEntityOrCA::MustBeCA) {
+  if (trustLevel == TrustLevel::TrustAnchor &&
+      endEntityOrCA == EndEntityOrCA::MustBeCA) {
     return Success;
   }
 
-  if (!cert.encodedCertificatePolicies) {
+  if (!encodedCertificatePolicies) {
     return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
   }
 
-  ScopedPtr<CERTCertificatePolicies, CERT_DestroyCertificatePoliciesExtension>
-    policies(CERT_DecodeCertificatePoliciesExtension(
-                cert.encodedCertificatePolicies));
-  if (!policies) {
-    return MapSECStatus(SECFailure);
+  bool found = false;
+
+  der::Input input;
+  if (input.Init(encodedCertificatePolicies->data,
+                 encodedCertificatePolicies->len) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+  }
+  if (der::NestedOf(input, der::SEQUENCE, der::SEQUENCE, der::EmptyAllowed::No,
+                    bind(CheckPolicyInformation, _1, endEntityOrCA,
+                         requiredPolicy, ref(found))) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+  }
+  if (der::End(input) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+  }
+  if (!found) {
+    return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
   }
 
-  for (const CERTPolicyInfo* const* policyInfos = policies->policyInfos;
-       *policyInfos; ++policyInfos) {
-    if ((*policyInfos)->oid == requiredPolicy) {
-      return Success;
-    }
-    // Intermediate certs are allowed to have the anyPolicy OID
-    if (endEntityOrCA == EndEntityOrCA::MustBeCA &&
-        (*policyInfos)->oid == SEC_OID_X509_ANY_POLICY) {
-      return Success;
-    }
-  }
-
-  return Fail(RecoverableError, SEC_ERROR_POLICY_VALIDATION_FAILED);
+  return Success;
 }
 
 static const long UNLIMITED_PATH_LEN = -1; // must be less than zero
 
 //  BasicConstraints ::= SEQUENCE {
 //          cA                      BOOLEAN DEFAULT FALSE,
 //          pathLenConstraint       INTEGER (0..MAX) OPTIONAL }
 static der::Result
@@ -495,17 +548,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
 
 Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
                                  BackCert& cert,
                                  PRTime time,
                                  EndEntityOrCA endEntityOrCA,
                                  KeyUsages requiredKeyUsagesIfPresent,
                                  KeyPurposeId requiredEKUIfPresent,
-                                 SECOidTag requiredPolicy,
+                                 const CertPolicyId& requiredPolicy,
                                  unsigned int subCACount,
                 /*optional out*/ TrustLevel* trustLevelOut)
 {
   Result rv;
 
   TrustLevel trustLevel;
   rv = MapSECStatus(trustDomain.GetCertTrust(endEntityOrCA,
                                              requiredPolicy,
@@ -522,19 +575,16 @@ CheckIssuerIndependentProperties(TrustDo
     // The TrustDomain returned a trust level that we weren't expecting.
     PORT_SetError(PR_INVALID_STATE_ERROR);
     return FatalError;
   }
   if (trustLevelOut) {
     *trustLevelOut = trustLevel;
   }
 
-  bool isTrustAnchor = endEntityOrCA == EndEntityOrCA::MustBeCA &&
-                       trustLevel == TrustLevel::TrustAnchor;
-
   // XXX: Good enough for now. There could be an illegal explicit version
   // number or one we don't support, but we can safely treat those all as v3
   // for now since processing of v3 certificates is strictly more strict than
   // processing of v1 certificates.
   der::Version version = (!cert.GetNSSCert()->version.data &&
                           !cert.GetNSSCert()->version.len) ? der::Version::v1
                                                            : der::Version::v3;
 
@@ -550,17 +600,18 @@ CheckIssuerIndependentProperties(TrustDo
   // 4.2.1.3. Key Usage
   rv = CheckKeyUsage(endEntityOrCA, cert.encodedKeyUsage,
                      requiredKeyUsagesIfPresent, arena);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.4. Certificate Policies
-  rv = CheckCertificatePolicies(cert, endEntityOrCA, isTrustAnchor,
+  rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies,
+                                cert.encodedInhibitAnyPolicy, trustLevel,
                                 requiredPolicy);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.5. Policy Mappings are not supported; see the documentation about
   //          policy enforcement in pkix.h.
 
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -33,17 +33,17 @@ namespace mozilla { namespace pkix {
 
 Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
           BackCert& cert,
           PRTime time,
           EndEntityOrCA endEntityOrCA,
           KeyUsages requiredKeyUsagesIfPresent,
           KeyPurposeId requiredEKUIfPresent,
-          SECOidTag requiredPolicy,
+          const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*optional out*/ TrustLevel* trustLevel = nullptr);
 
 Result CheckNameConstraints(BackCert& cert);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix__pkixcheck_h
--- a/security/pkix/lib/pkixder.h
+++ b/security/pkix/lib/pkixder.h
@@ -158,16 +158,41 @@ public:
     }
     if (memcmp(input, toMatch, N)) {
       return false;
     }
     input += N;
     return true;
   }
 
+  template <uint16_t N>
+  bool MatchTLV(uint8_t tag, uint16_t len, const uint8_t (&value)[N])
+  {
+    static_assert(N <= 127, "buffer larger than largest length supported");
+    if (len > N) {
+      PR_NOT_REACHED("overflow prevented dynamically instead of statically");
+      return false;
+    }
+    uint16_t totalLen = 2u + len;
+    if (EnsureLength(totalLen) != Success) {
+      return false;
+    }
+    if (*input != tag) {
+      return false;
+    }
+    if (*(input + 1) != len) {
+      return false;
+    }
+    if (memcmp(input + 2, value, len)) {
+      return false;
+    }
+    input += totalLen;
+    return true;
+  }
+
   Result Skip(uint16_t len)
   {
     if (EnsureLength(len) != Success) {
       return Fail(SEC_ERROR_BAD_DER);
     }
     input += len;
     return Success;
   }
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -142,17 +142,17 @@ CheckOCSPResponseSignerCert(TrustDomain&
   // to--in particular, it doesn't allow SEC_OID_OCSP_RESPONDER to be implied
   // by a missing EKU extension, unlike other EKUs.
   //
   // TODO(bug 926261): If we're validating for a policy then the policy OID we
   // are validating for should be passed to CheckIssuerIndependentProperties.
   rv = CheckIssuerIndependentProperties(trustDomain, cert, time,
                                         EndEntityOrCA::MustBeEndEntity, 0,
                                         KeyPurposeId::id_kp_OCSPSigning,
-                                        SEC_OID_X509_ANY_POLICY, 0);
+                                        CertPolicyId::anyPolicy, 0);
   if (rv != Success) {
     return rv;
   }
 
   // It is possible that there exists a certificate with the same key as the
   // issuer but with a different name, so we need to compare names
   // TODO: needs test
   if (!SECITEM_ItemsAreEqual(&cert.GetNSSCert()->derIssuer,