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 195950 a4ae7060f43ac1a4e49b30dfd7a95c5212940d4b
parent 195949 fe7bffe6bb06fbe0cf047f1c979acb14875d3d1c
child 195951 6deed68c2358d584d0988b200adc23fc88aa4113
push idunknown
push userunknown
push dateunknown
reviewerskeeler
bugs1006958
milestone32.0a1
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,