Bug 1006812: Use mozilla::pkix::der to decode the key usage extension, r=keeler
authorBrian Smith <brian@briansmith.org>
Thu, 19 Jun 2014 00:13:20 -0700
changeset 189701 2572716c3646aa70b0109e602ee5113c6ad17138
parent 189700 002ba7646333a658535d1da4467bff2fa1649876
child 189702 f2f60161a7ac105e535943cc9360e462aa7d69a7
push id26994
push useremorley@mozilla.com
push dateFri, 20 Jun 2014 16:42:33 +0000
treeherdermozilla-central@892d7a8eda45 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskeeler
bugs1006812
milestone33.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 1006812: Use mozilla::pkix::der to decode the key usage extension, r=keeler
security/apps/AppSignatureVerification.cpp
security/certverifier/CertVerifier.cpp
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/pkixocsp.cpp
security/pkix/test/gtest/moz.build
security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
security/pkix/test/gtest/pkix_cert_extension_tests.cpp
security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
--- a/security/apps/AppSignatureVerification.cpp
+++ b/security/apps/AppSignatureVerification.cpp
@@ -599,17 +599,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,
+                     EndEntityOrCA::MustBeEndEntity,
+                     KeyUsage::digitalSignature,
                      KeyPurposeId::id_kp_codeSigning,
                      CertPolicyId::anyPolicy,
                      nullptr, builtChain)
         != SECSuccess) {
     return MapSECStatus(SECFailure);
   }
 
   // See NSS_CMSContentInfo_GetContentTypeOID, which isn't exported from NSS.
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -297,37 +297,32 @@ destroyCertListThatShouldNotExist(CERTCe
     CERT_DestroyCertList(*certChain);
     *certChain = nullptr;
   }
 }
 #endif
 
 static SECStatus
 BuildCertChainForOneKeyUsage(TrustDomain& trustDomain, CERTCertificate* cert,
-                             PRTime time, KeyUsages ku1, KeyUsages ku2,
-                             KeyUsages ku3, KeyPurposeId eku,
+                             PRTime time, KeyUsage ku1, KeyUsage ku2,
+                             KeyUsage ku3, KeyPurposeId eku,
                              const CertPolicyId& requiredPolicy,
                              const SECItem* stapledOCSPResponse,
                              ScopedCERTCertList& builtChain)
 {
-  PR_ASSERT(ku1);
-  PR_ASSERT(ku2);
-
   SECStatus rv = BuildCertChain(trustDomain, cert, time,
                                 EndEntityOrCA::MustBeEndEntity, ku1,
                                 eku, requiredPolicy,
                                 stapledOCSPResponse, builtChain);
-  if (rv != SECSuccess && ku2 &&
-      PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+  if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
     rv = BuildCertChain(trustDomain, cert, time,
                         EndEntityOrCA::MustBeEndEntity, ku2,
                         eku, requiredPolicy,
                         stapledOCSPResponse, builtChain);
-    if (rv != SECSuccess && ku3 &&
-        PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+    if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
       rv = BuildCertChain(trustDomain, cert, time,
                           EndEntityOrCA::MustBeEndEntity, ku3,
                           eku, requiredPolicy,
                           stapledOCSPResponse, builtChain);
       if (rv != SECSuccess) {
         PR_SetError(SEC_ERROR_INADEQUATE_KEY_USAGE, 0);
       }
     }
@@ -383,17 +378,18 @@ CertVerifier::MozillaPKIXVerifyCert(
   mozilla::pkix::ScopedCERTCertList builtChain;
   switch (usage) {
     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, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
-                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
+                          EndEntityOrCA::MustBeEndEntity,
+                          KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse,
                           builtChain);
       break;
     }
 
     case certificateUsageSSLServer: {
       // TODO: When verifying a certificate in an SSL handshake, we should
@@ -408,19 +404,19 @@ CertVerifier::MozillaPKIXVerifyCert(
       if (rv == SECSuccess) {
         NSSCertDBTrustDomain
           trustDomain(trustSSL,
                       ocspFetching == NSSCertDBTrustDomain::NeverFetchOCSP
                         ? NSSCertDBTrustDomain::LocalOnlyOCSPForEV
                         : NSSCertDBTrustDomain::FetchOCSPForEV,
                       mOCSPCache, pinArg, ocspGETConfig, &callbackContainer);
         rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
-                                          KU_DIGITAL_SIGNATURE, // ECDHE/DHE
-                                          KU_KEY_ENCIPHERMENT, // RSA
-                                          KU_KEY_AGREEMENT, // ECDH/DH
+                                          KeyUsage::digitalSignature,// (EC)DHE
+                                          KeyUsage::keyEncipherment, // RSA
+                                          KeyUsage::keyAgreement,    // (EC)DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse,
                                           builtChain);
         if (rv == SECSuccess) {
           if (evOidPolicy) {
             *evOidPolicy = evPolicyOidTag;
           }
           break;
@@ -435,89 +431,99 @@ CertVerifier::MozillaPKIXVerifyCert(
         break;
       }
 
       // Now try non-EV.
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig,
                                        &callbackContainer);
       rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
-                                        KU_DIGITAL_SIGNATURE, // ECDHE/DHE
-                                        KU_KEY_ENCIPHERMENT, // RSA
-                                        KU_KEY_AGREEMENT, // ECDH/DH
+                                        KeyUsage::digitalSignature, // (EC)DHE
+                                        KeyUsage::keyEncipherment, // RSA
+                                        KeyUsage::keyAgreement, // (EC)DH
                                         KeyPurposeId::id_kp_serverAuth,
                                         CertPolicyId::anyPolicy,
                                         stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time, EndEntityOrCA::MustBeCA,
-                          KU_KEY_CERT_SIGN, KeyPurposeId::id_kp_serverAuth,
+                          KeyUsage::keyCertSign,
+                          KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, ocspFetching, mOCSPCache,
                                        pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
-                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
+                          EndEntityOrCA::MustBeEndEntity,
+                          KeyUsage::digitalSignature,
                           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, ocspGETConfig);
-      rv = BuildCertChainForOneKeyUsage(trustDomain, cert, time,
-                                        KU_KEY_ENCIPHERMENT, // RSA
-                                        KU_KEY_AGREEMENT, // ECDH/DH
-                                        0,
-                                        KeyPurposeId::id_kp_emailProtection,
-                                        CertPolicyId::anyPolicy,
-                                        stapledOCSPResponse, builtChain);
+      rv = BuildCertChain(trustDomain, cert, time,
+                          EndEntityOrCA::MustBeEndEntity,
+                          KeyUsage::keyEncipherment, // RSA
+                          KeyPurposeId::id_kp_emailProtection,
+                          CertPolicyId::anyPolicy,
+                          stapledOCSPResponse, builtChain);
+      if (rv != SECSuccess && PR_GetError() == SEC_ERROR_INADEQUATE_KEY_USAGE) {
+        rv = BuildCertChain(trustDomain, cert, time,
+                            EndEntityOrCA::MustBeEndEntity,
+                            KeyUsage::keyAgreement, // ECDH/DH
+                            KeyPurposeId::id_kp_emailProtection,
+                            CertPolicyId::anyPolicy,
+                            stapledOCSPResponse, builtChain);
+      }
       break;
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, ocspFetching,
                                        mOCSPCache, pinArg, ocspGETConfig);
       rv = BuildCertChain(trustDomain, cert, time,
-                          EndEntityOrCA::MustBeEndEntity, KU_DIGITAL_SIGNATURE,
+                          EndEntityOrCA::MustBeEndEntity,
+                          KeyUsage::digitalSignature,
                           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
       // certificate viewer UI. Because we don't know what trust bit is
       // interesting, we just try them all.
       mozilla::pkix::EndEntityOrCA endEntityOrCA;
-      mozilla::pkix::KeyUsages keyUsage;
+      mozilla::pkix::KeyUsage keyUsage;
       KeyPurposeId eku;
       if (usage == certificateUsageVerifyCA) {
         endEntityOrCA = EndEntityOrCA::MustBeCA;
-        keyUsage = KU_KEY_CERT_SIGN;
+        keyUsage = KeyUsage::keyCertSign;
         eku = KeyPurposeId::anyExtendedKeyUsage;
       } else {
         endEntityOrCA = EndEntityOrCA::MustBeEndEntity;
-        keyUsage = KU_DIGITAL_SIGNATURE;
+        keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, ocspFetching, mOCSPCache,
                                     pinArg, ocspGETConfig);
       rv = BuildCertChain(sslTrust, cert, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse, builtChain);
--- a/security/pkix/include/pkix/pkix.h
+++ b/security/pkix/include/pkix/pkix.h
@@ -88,17 +88,17 @@ namespace mozilla { namespace pkix {
 // SEC_ERROR_UNTRUSTED_ISSUER means that path building failed because of active
 //                            distrust.
 // TODO(bug 968451): Document more of these.
 
 SECStatus BuildCertChain(TrustDomain& trustDomain,
                          const CERTCertificate* cert,
                          PRTime time,
                          EndEntityOrCA endEntityOrCA,
-            /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
+                         KeyUsage requiredKeyUsageIfPresent,
                          KeyPurposeId requiredEKUIfPresent,
                          const CertPolicyId& requiredPolicy,
             /*optional*/ const SECItem* stapledOCSPResponse,
                  /*out*/ ScopedCERTCertList& results);
 
 // Verify the given signed data using the given public key.
 SECStatus VerifySignedData(const CERTSignedData* sd,
                            const SECItem& subjectPublicKeyInfo,
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -44,17 +44,28 @@ PORT_FreeArena_false(PLArenaPool* arena)
 typedef ScopedPtr<PLArenaPool, PORT_FreeArena_false> ScopedPLArenaPool;
 
 typedef ScopedPtr<CERTCertificate, CERT_DestroyCertificate>
         ScopedCERTCertificate;
 typedef ScopedPtr<CERTCertList, CERT_DestroyCertList> ScopedCERTCertList;
 
 MOZILLA_PKIX_ENUM_CLASS EndEntityOrCA { MustBeEndEntity = 0, MustBeCA = 1 };
 
-typedef unsigned int KeyUsages;
+MOZILLA_PKIX_ENUM_CLASS KeyUsage : uint8_t {
+  digitalSignature = 0,
+  nonRepudiation   = 1,
+  keyEncipherment  = 2,
+  dataEncipherment = 3,
+  keyAgreement     = 4,
+  keyCertSign      = 5,
+  // cRLSign       = 6,
+  // encipherOnly  = 7,
+  // decipherOnly  = 8,
+  noParticularKeyUsageRequired = 0xff,
+};
 
 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
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -137,33 +137,33 @@ BackCert::VerifyOwnSignatureWithKey(Trus
   return MapSECStatus(trustDomain.VerifySignedData(&nssCert->signatureWrap,
                                                    subjectPublicKeyInfo));
 }
 
 static Result BuildForward(TrustDomain& trustDomain,
                            BackCert& subject,
                            PRTime time,
                            EndEntityOrCA endEntityOrCA,
-                           KeyUsages requiredKeyUsagesIfPresent,
+                           KeyUsage requiredKeyUsageIfPresent,
                            KeyPurposeId requiredEKUIfPresent,
                            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,
                   KeyPurposeId requiredEKUIfPresent,
                   const CertPolicyId& requiredPolicy,
                   const SECItem& potentialIssuerDER,
                   unsigned int subCACount,
-                  ScopedCERTCertList& results)
+                  /*out*/ ScopedCERTCertList& results)
 {
   BackCert potentialIssuer(&subject, BackCert::IncludeCN::No);
   Result rv = potentialIssuer.Init(potentialIssuerDER);
   if (rv != Success) {
     return rv;
   }
 
   // RFC5280 4.2.1.1. Authority Key Identifier
@@ -183,19 +183,22 @@ BuildForwardInner(TrustDomain& trustDoma
     }
   }
 
   rv = CheckNameConstraints(potentialIssuer);
   if (rv != Success) {
     return rv;
   }
 
+  // RFC 5280, Section 4.2.1.3: "If the keyUsage extension is present, then the
+  // subject public key MUST NOT be used to verify signatures on certificates
+  // or CRLs unless the corresponding keyCertSign or cRLSign bit is set."
   rv = BuildForward(trustDomain, potentialIssuer, time, EndEntityOrCA::MustBeCA,
-                    KU_KEY_CERT_SIGN, requiredEKUIfPresent, requiredPolicy,
-                    nullptr, subCACount, results);
+                    KeyUsage::keyCertSign, requiredEKUIfPresent,
+                    requiredPolicy, nullptr, subCACount, results);
   if (rv != Success) {
     return rv;
   }
 
   return subject.VerifyOwnSignatureWithKey(
                    trustDomain, potentialIssuer.GetSubjectPublicKeyInfo());
 }
 
@@ -205,32 +208,32 @@ BuildForwardInner(TrustDomain& trustDoma
 // because it affects which error we return when a certificate or certificate
 // chain has multiple problems. See the error ranking documentation in
 // pkix/pkix.h.
 static Result
 BuildForward(TrustDomain& trustDomain,
              BackCert& subject,
              PRTime time,
              EndEntityOrCA endEntityOrCA,
-             KeyUsages requiredKeyUsagesIfPresent,
+             KeyUsage requiredKeyUsageIfPresent,
              KeyPurposeId requiredEKUIfPresent,
              const CertPolicyId& requiredPolicy,
              /*optional*/ const SECItem* stapledOCSPResponse,
              unsigned int subCACount,
              /*out*/ ScopedCERTCertList& results)
 {
   Result rv;
 
   TrustLevel trustLevel;
   // If this is an end-entity and not a trust anchor, we defer reporting
   // any error found here until after attempting to find a valid chain.
   // See the explanation of error prioritization in pkix.h.
   rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
                                         endEntityOrCA,
-                                        requiredKeyUsagesIfPresent,
+                                        requiredKeyUsageIfPresent,
                                         requiredEKUIfPresent, requiredPolicy,
                                         subCACount, &trustLevel);
   PRErrorCode deferredEndEntityError = 0;
   if (rv != Success) {
     if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
         trustLevel != TrustLevel::TrustAnchor) {
       deferredEndEntityError = PR_GetError();
     } else {
@@ -343,18 +346,18 @@ BuildForward(TrustDomain& trustDomain,
   return Fail(RecoverableError, errorToReturn);
 }
 
 SECStatus
 BuildCertChain(TrustDomain& trustDomain,
                const CERTCertificate* nssCert,
                PRTime time,
                EndEntityOrCA endEntityOrCA,
-               /*optional*/ KeyUsages requiredKeyUsagesIfPresent,
-               /*optional*/ KeyPurposeId requiredEKUIfPresent,
+               KeyUsage requiredKeyUsageIfPresent,
+               KeyPurposeId requiredEKUIfPresent,
                const CertPolicyId& requiredPolicy,
                /*optional*/ const SECItem* stapledOCSPResponse,
                /*out*/ ScopedCERTCertList& results)
 {
   if (!nssCert) {
     PR_NOT_REACHED("null cert passed to BuildCertChain");
     PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
     return SECFailure;
@@ -370,17 +373,17 @@ BuildCertChain(TrustDomain& trustDomain,
 
   BackCert cert(nullptr, includeCN);
   Result rv = cert.Init(nssCert->derCert);
   if (rv != Success) {
     return SECFailure;
   }
 
   rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
-                    requiredKeyUsagesIfPresent, requiredEKUIfPresent,
+                    requiredKeyUsageIfPresent, requiredEKUIfPresent,
                     requiredPolicy, stapledOCSPResponse, 0, results);
   if (rv != Success) {
     results = nullptr;
     return SECFailure;
   }
 
   return SECSuccess;
 }
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -41,71 +41,130 @@ CheckTimes(const CERTCertificate* cert, 
   if (validity != secCertTimeValid) {
     return Fail(RecoverableError, SEC_ERROR_EXPIRED_CERTIFICATE);
   }
 
   return Success;
 }
 
 // 4.2.1.3. Key Usage (id-ce-keyUsage)
-// Modeled after GetKeyUsage in certdb.c
+
+// As explained in the comment in CheckKeyUsage, bit 0 is the most significant
+// bit and bit 7 is the least significant bit.
+inline uint8_t KeyUsageToBitMask(KeyUsage keyUsage)
+{
+  PR_ASSERT(keyUsage != KeyUsage::noParticularKeyUsageRequired);
+  return 0x80u >> static_cast<uint8_t>(keyUsage);
+}
+
 Result
-CheckKeyUsage(EndEntityOrCA endEntityOrCA,
-              const SECItem* encodedKeyUsage,
-              KeyUsages requiredKeyUsagesIfPresent,
-              PLArenaPool* arena)
+CheckKeyUsage(EndEntityOrCA endEntityOrCA, const SECItem* encodedKeyUsage,
+              KeyUsage requiredKeyUsageIfPresent)
 {
   if (!encodedKeyUsage) {
     // TODO(bug 970196): Reject certificates that are being used to verify
     // certificate signatures unless the certificate is a trust anchor, to
     // reduce the chances of an end-entity certificate being abused as a CA
     // certificate.
     // if (endEntityOrCA == EndEntityOrCA::MustBeCA && !isTrustAnchor) {
     //   return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
     // }
     //
     // TODO: Users may configure arbitrary certificates as trust anchors, not
     // just roots. We should only allow a certificate without a key usage to be
     // used as a CA when it is self-issued and self-signed.
     return Success;
   }
 
-  SECItem tmpItem;
-  Result rv = MapSECStatus(SEC_QuickDERDecodeItem(arena, &tmpItem,
-                              SEC_ASN1_GET(SEC_BitStringTemplate),
-                              encodedKeyUsage));
-  if (rv != Success) {
-    return rv;
+  der::Input input;
+  if (input.Init(encodedKeyUsage->data, encodedKeyUsage->len) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+  }
+  der::Input value;
+  if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
   }
 
-  // TODO XXX: Why is tmpItem.len > 1?
+  uint8_t numberOfPaddingBits;
+  if (value.Read(numberOfPaddingBits) != der::Success) {
+    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+  }
+  if (numberOfPaddingBits > 7) {
+    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+  }
 
-  KeyUsages allowedKeyUsages = tmpItem.data[0];
-  if ((allowedKeyUsages & requiredKeyUsagesIfPresent)
-        != requiredKeyUsagesIfPresent) {
+  uint8_t bits;
+  if (value.Read(bits) != der::Success) {
+    // Reject empty bit masks.
     return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
   }
 
-  if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
-   // "If the keyUsage extension is present, then the subject public key
-   //  MUST NOT be used to verify signatures on certificates or CRLs unless
-   //  the corresponding keyCertSign or cRLSign bit is set."
-   if ((allowedKeyUsages & KU_KEY_CERT_SIGN) == 0) {
+  // The most significant bit is numbered 0 (digitalSignature) and the least
+  // significant bit is numbered 7 (encipherOnly), and the padding is in the
+  // least significant bits of the last byte. The numbering of bits in a byte
+  // is backwards from how we usually interpret them.
+  //
+  // For example, let's say bits is encoded in one byte with of value 0xB0 and
+  // numberOfPaddingBits == 4. Then, bits is 10110000 in binary:
+  //
+  //      bit 0  bit 3
+  //          |  |
+  //          v  v
+  //          10110000
+  //              ^^^^
+  //               |
+  //               4 padding bits
+  //
+  // Since bits is the last byte, we have to consider the padding by ensuring
+  // that the least significant 4 bits are all zero, since DER rules require
+  // all padding bits to be zero. Then we have to look at the bit N bits to the
+  // right of the most significant bit, where N is a value from the KeyUsage
+  // enumeration.
+  //
+  // Let's say we're interested in the keyCertSign (5) bit. We'd need to look
+  // at bit 5, which is zero, so keyCertSign is not asserted. (Since we check
+  // that the padding is all zeros, it is OK to read from the padding bits.)
+  //
+  // Let's say we're interested in the digitalSignature (0) bit. We'd need to
+  // look at the bit 0 (the most significant bit), which is set, so that means
+  // digitalSignature is asserted. Similarly, keyEncipherment (2) and
+  // dataEncipherment (3) are asserted.
+  //
+  // Note that since the KeyUsage enumeration is limited to values 0-7, we
+  // only ever need to examine the first byte test for
+  // requiredKeyUsageIfPresent.
+
+  if (requiredKeyUsageIfPresent != KeyUsage::noParticularKeyUsageRequired) {
+    // Check that the required key usage bit is set.
+    if ((bits & KeyUsageToBitMask(requiredKeyUsageIfPresent)) == 0) {
       return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
     }
-  } else {
-    // "The keyCertSign bit is asserted when the subject public key is
-    //  used for verifying signatures on public key certificates.  If the
-    //  keyCertSign bit is asserted, then the cA bit in the basic
-    //  constraints extension (Section 4.2.1.9) MUST also be asserted."
-    // TODO XXX: commented out to match classic NSS behavior.
-    //if ((allowedKeyUsages & KU_KEY_CERT_SIGN) != 0) {
-    //  // XXX: better error code.
-    //  return Fail(RecoverableError, SEC_ERROR_INADEQUATE_CERT_TYPE);
-    //}
+  }
+
+  if (endEntityOrCA != EndEntityOrCA::MustBeCA) {
+    // RFC 5280 says "The keyCertSign bit is asserted when the subject public
+    // key is used for verifying signatures on public key certificates. If the
+    // keyCertSign bit is asserted, then the cA bit in the basic constraints
+    // extension (Section 4.2.1.9) MUST also be asserted."
+    if ((bits & KeyUsageToBitMask(KeyUsage::keyCertSign)) != 0) {
+      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    }
+  }
+
+  // The padding applies to the last byte, so skip to the last byte.
+  while (!value.AtEnd()) {
+    if (value.Read(bits) != der::Success) {
+      return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
+    }
+  }
+
+  // All of the padding bits must be zero, according to DER rules.
+  uint8_t paddingMask = static_cast<uint8_t>((1 << numberOfPaddingBits) - 1);
+  if ((bits & paddingMask) != 0) {
+    return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
   }
 
   return Success;
 }
 
 // RFC5820 4.2.1.4. Certificate Policies
 
 // "The user-initial-policy-set contains the special value any-policy if the
@@ -544,17 +603,17 @@ CheckExtendedKeyUsage(EndEntityOrCA endE
   return Success;
 }
 
 Result
 CheckIssuerIndependentProperties(TrustDomain& trustDomain,
                                  BackCert& cert,
                                  PRTime time,
                                  EndEntityOrCA endEntityOrCA,
-                                 KeyUsages requiredKeyUsagesIfPresent,
+                                 KeyUsage requiredKeyUsageIfPresent,
                                  KeyPurposeId requiredEKUIfPresent,
                                  const CertPolicyId& requiredPolicy,
                                  unsigned int subCACount,
                 /*optional out*/ TrustLevel* trustLevelOut)
 {
   Result rv;
 
   TrustLevel trustLevel;
@@ -579,28 +638,23 @@ CheckIssuerIndependentProperties(TrustDo
   // 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;
 
-  PLArenaPool* arena = cert.GetArena();
-  if (!arena) {
-    return FatalError;
-  }
-
   // 4.2.1.1. Authority Key Identifier is ignored (see bug 965136).
 
   // 4.2.1.2. Subject Key Identifier is ignored (see bug 965136).
 
   // 4.2.1.3. Key Usage
   rv = CheckKeyUsage(endEntityOrCA, cert.encodedKeyUsage,
-                     requiredKeyUsagesIfPresent, arena);
+                     requiredKeyUsageIfPresent);
   if (rv != Success) {
     return rv;
   }
 
   // 4.2.1.4. Certificate Policies
   rv = CheckCertificatePolicies(endEntityOrCA, cert.encodedCertificatePolicies,
                                 cert.encodedInhibitAnyPolicy, trustLevel,
                                 requiredPolicy);
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -31,17 +31,17 @@
 
 namespace mozilla { namespace pkix {
 
 Result CheckIssuerIndependentProperties(
           TrustDomain& trustDomain,
           BackCert& cert,
           PRTime time,
           EndEntityOrCA endEntityOrCA,
-          KeyUsages requiredKeyUsagesIfPresent,
+          KeyUsage requiredKeyUsageIfPresent,
           KeyPurposeId requiredEKUIfPresent,
           const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*optional out*/ TrustLevel* trustLevel = nullptr);
 
 Result CheckNameConstraints(BackCert& cert);
 
 } } // namespace mozilla::pkix
--- a/security/pkix/lib/pkixocsp.cpp
+++ b/security/pkix/lib/pkixocsp.cpp
@@ -142,17 +142,18 @@ CheckOCSPResponseSignerCert(TrustDomain&
   // Note that CheckIssuerIndependentProperties processes
   // SEC_OID_OCSP_RESPONDER in the way that the OCSP specification requires us
   // 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, potentialSigner, time,
-                                        EndEntityOrCA::MustBeEndEntity, 0,
+                                        EndEntityOrCA::MustBeEndEntity,
+                                        KeyUsage::noParticularKeyUsageRequired,
                                         KeyPurposeId::id_kp_OCSPSigning,
                                         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
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -6,16 +6,17 @@
 
 LIBRARY_NAME = 'mozillapkix_gtest'
 
 SOURCES += [
     'nssgtest.cpp',
     'pkix_cert_chain_length_tests.cpp',
     'pkix_cert_extension_tests.cpp',
     'pkix_ocsp_request_tests.cpp',
+    'pkixcheck_CheckKeyUsage_tests.cpp',
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
     'pkixgtest.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '../../include',
--- a/security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
+++ b/security/pkix/test/gtest/pkix_cert_chain_length_tests.cpp
@@ -185,32 +185,32 @@ protected:
 
 /*static*/ TestTrustDomain pkix_cert_chain_length::trustDomain;
 
 TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
 {
   ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, trustDomain.GetLeafeCACert(),
                                    now, EndEntityOrCA::MustBeCA,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(),
                          trustDomain.GetLeafeCACert()->subjectName,
                          "CN=Direct End-Entity",
                          EndEntityOrCA::MustBeEndEntity,
                          trustDomain.leafCAKey.get(), privateKey, cert));
   ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(), now,
                                    EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
 {
@@ -222,30 +222,30 @@ TEST_F(pkix_cert_chain_length, BeyondMax
                          trustDomain.GetLeafeCACert()->subjectName,
                          "CN=CA Too Far", EndEntityOrCA::MustBeCA,
                          trustDomain.leafCAKey.get(),
                          caPrivateKey, caCert));
   PR_SetError(0, 0);
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
                     BuildCertChain(trustDomain, caCert.get(), now,
                                    EndEntityOrCA::MustBeCA,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 
   ScopedSECKEYPrivateKey privateKey;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), caCert->subjectName,
                          "CN=End-Entity Too Far",
                          EndEntityOrCA::MustBeEndEntity,
                          caPrivateKey.get(), privateKey, cert));
   PR_SetError(0, 0);
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_ISSUER,
                     BuildCertChain(trustDomain, cert.get(), now,
                                    EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::id_kp_serverAuth,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
--- a/security/pkix/test/gtest/pkix_cert_extension_tests.cpp
+++ b/security/pkix/test/gtest/pkix_cert_extension_tests.cpp
@@ -145,17 +145,17 @@ TEST_F(pkix_cert_extensions, UnknownCrit
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalExtension, key,
                          cert));
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a non-critical extension not in the id-ce or id-pe arcs (which is
 // thus unknown to us) verifies successfully.
@@ -177,17 +177,17 @@ TEST_F(pkix_cert_extensions, UnknownNonC
   const char* certCN = "CN=Cert With Unknown NonCritical Extension";
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownNonCriticalExtension, key,
                          cert));
   ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that an incorrect OID for id-pe-authorityInformationAccess
 // (when marked critical) is detected and that verification fails.
@@ -211,17 +211,17 @@ TEST_F(pkix_cert_extensions, WrongOIDCri
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &wrongOIDCriticalExtension, key,
                          cert));
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a id-pe-authorityInformationAccess critical extension
 // is detected and that verification succeeds.
@@ -243,17 +243,17 @@ TEST_F(pkix_cert_extensions, CriticalAIA
   const char* certCN = "CN=Cert With Critical AIA Extension";
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalAIAExtension, key,
                          cert));
   ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // We know about some id-ce extensions (OID arc 2.5.29), but not all of them.
 // Tests that an unknown id-ce extension is detected and that verification
@@ -276,17 +276,17 @@ TEST_F(pkix_cert_extensions, UnknownCrit
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &unknownCriticalCEExtension, key,
                          cert));
   ScopedCERTCertList results;
   ASSERT_SECFailure(SEC_ERROR_UNKNOWN_CRITICAL_EXTENSION,
                     BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
 
 // Tests that a certificate with a known critical id-ce extension (in this case,
 // OID 2.5.29.54, which is id-ce-inhibitAnyPolicy), verifies successfully.
@@ -306,14 +306,14 @@ TEST_F(pkix_cert_extensions, KnownCritic
   };
   const char* certCN = "CN=Cert With Known Critical id-ce Extension";
   ScopedSECKEYPrivateKey key;
   ScopedCERTCertificate cert;
   ASSERT_TRUE(CreateCert(arena.get(), certCN, &criticalCEExtension, key, cert));
   ScopedCERTCertList results;
   ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
                                    now, EndEntityOrCA::MustBeEndEntity,
-                                   0, // key usage
+                                   KeyUsage::noParticularKeyUsageRequired,
                                    KeyPurposeId::anyExtendedKeyUsage,
                                    CertPolicyId::anyPolicy,
                                    nullptr, // stapled OCSP response
                                    results));
 }
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixcheck_CheckKeyUsage_tests.cpp
@@ -0,0 +1,272 @@
+/* -*- 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.
+ */
+
+#include "pkixgtest.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+namespace mozilla { namespace pkix {
+
+extern Result CheckKeyUsage(EndEntityOrCA endEntityOrCA,
+                            const SECItem* encodedKeyUsage,
+                            KeyUsage requiredKeyUsageIfPresent);
+
+} } // namespace mozilla::pkix
+
+#define ASSERT_BAD(x) \
+  ASSERT_RecoverableError(SEC_ERROR_INADEQUATE_KEY_USAGE, x)
+
+// Make it easy to define test data for the common, simplest cases.
+#define NAMED_SIMPLE_KU(name, unusedBits, bits) \
+  const uint8_t name##_bytes[4] = { \
+    0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, unusedBits, bits \
+  }; \
+  const SECItem name = { \
+    siBuffer, \
+    const_cast<uint8_t*>(name##_bytes), \
+    4 \
+  }
+
+static uint8_t dummy;
+static const SECItem empty_null    = { siBuffer, nullptr, 0 };
+static const SECItem empty_nonnull = { siBuffer, &dummy, 0 };
+
+// Note that keyCertSign is really the only interesting case for CA
+// certificates since we don't support cRLSign.
+
+TEST(pkixcheck_CheckKeyUsage, EE_none)
+{
+  // The input SECItem is nullptr. This means the cert had no keyUsage
+  // extension. This is always valid because no key usage in an end-entity
+  // means that there are no key usage restrictions.
+
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::noParticularKeyUsageRequired));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::digitalSignature));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::nonRepudiation));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::keyEncipherment));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::dataEncipherment));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
+                               KeyUsage::keyAgreement));
+}
+
+TEST(pkixcheck_CheckKeyUsage, EE_empty)
+{
+  // The input SECItem is empty. The cert had an empty keyUsage extension,
+  // which is syntactically invalid.
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
+                           KeyUsage::digitalSignature));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
+                           KeyUsage::digitalSignature));
+}
+
+TEST(pkixcheck_CheckKeyUsage, CA_none)
+{
+  // A CA certificate does not have a KU extension.
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
+                               KeyUsage::keyCertSign));
+}
+
+TEST(pkixcheck_CheckKeyUsage, CA_empty)
+{
+  // A CA certificate has an empty KU extension.
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
+                           KeyUsage::keyCertSign));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
+                           KeyUsage::keyCertSign));
+}
+
+TEST(pkixchekc_CheckKeyusage, maxUnusedBits)
+{
+  NAMED_SIMPLE_KU(encoded, 7, 0x80);
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
+                               KeyUsage::digitalSignature));
+}
+
+TEST(pkixchekc_CheckKeyusage, tooManyUnusedBits)
+{
+  static uint8_t oneValueByteData[] = {
+    0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80
+  };
+  const SECItem oneValueByte = {
+    siBuffer,
+    oneValueByteData,
+    sizeof(oneValueByteData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
+                           KeyUsage::digitalSignature));
+
+  static uint8_t twoValueBytesData[] = {
+    0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00
+  };
+  const SECItem twoValueBytes = {
+    siBuffer,
+    twoValueBytesData,
+    sizeof(twoValueBytesData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
+                           KeyUsage::digitalSignature));
+}
+
+void ASSERT_SimpleCase(uint8_t unusedBits, uint8_t bits, KeyUsage usage)
+{
+  // Test that only the right bit is accepted for the usage for both EE and CA
+  // certs.
+  NAMED_SIMPLE_KU(good, unusedBits, bits);
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good, usage));
+
+  // We use (~bits >> unusedBits) << unusedBits) instead of using the same
+  // calculation that is in CheckKeyUsage to validate that the calculation in
+  // CheckKeyUsage is correct.
+
+  // Test that none of the other non-padding bits are mistaken for the given
+  // key usage in the single-byte value case.
+  NAMED_SIMPLE_KU(notGood, unusedBits,
+                  static_cast<uint8_t>((~bits >> unusedBits) << unusedBits));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood, usage));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood, usage));
+
+  // Test that none of the other non-padding bits are mistaken for the given
+  // key usage in the two-byte value case.
+  uint8_t twoByteNotGoodData[] = {
+    0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits,
+    static_cast<uint8_t>(~bits),
+    static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits)
+  };
+  const SECItem twoByteNotGood = {
+    siBuffer, twoByteNotGoodData, sizeof(twoByteNotGoodData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
+                           usage));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage));
+}
+
+TEST(pkixcheck_CheckKeyUsage, simpleCases)
+{
+  ASSERT_SimpleCase(7, 0x80, KeyUsage::digitalSignature);
+  ASSERT_SimpleCase(6, 0x40, KeyUsage::nonRepudiation);
+  ASSERT_SimpleCase(5, 0x20, KeyUsage::keyEncipherment);
+  ASSERT_SimpleCase(4, 0x10, KeyUsage::dataEncipherment);
+  ASSERT_SimpleCase(3, 0x08, KeyUsage::keyAgreement);
+}
+
+// Only CAs are allowed to assert keyCertSign
+TEST(pkixcheck_CheckKeyUsage, keyCertSign)
+{
+  NAMED_SIMPLE_KU(good, 2, 0x04);
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good,
+                           KeyUsage::keyCertSign));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &good,
+                               KeyUsage::keyCertSign));
+
+  // Test that none of the other non-padding bits are mistaken for the given
+  // key usage in the one-byte value case.
+  NAMED_SIMPLE_KU(notGood, 2, 0xFB);
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &notGood,
+                           KeyUsage::keyCertSign));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &notGood,
+                           KeyUsage::keyCertSign));
+
+  // Test that none of the other non-padding bits are mistaken for the given
+  // key usage in the two-byte value case.
+  static uint8_t twoByteNotGoodData[] = {
+    0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 2/*unused bits*/, 0xFBu, 0xFCu
+  };
+  static const SECItem twoByteNotGood = {
+    siBuffer, twoByteNotGoodData, sizeof(twoByteNotGoodData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
+                           KeyUsage::keyCertSign));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood,
+                           KeyUsage::keyCertSign));
+}
+
+TEST(pkixcheck_CheckKeyUsage, unusedBitNotZero)
+{
+  // single byte control case
+  static uint8_t controlOneValueByteData[] = {
+    0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
+  };
+  const SECItem controlOneValueByte = {
+    siBuffer,
+    controlOneValueByteData,
+    sizeof(controlOneValueByteData)
+  };
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+                               &controlOneValueByte,
+                               KeyUsage::digitalSignature));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlOneValueByte,
+                               KeyUsage::digitalSignature));
+
+  // single-byte test case
+  static uint8_t oneValueByteData[] = {
+    0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80 | 0x01
+  };
+  const SECItem oneValueByte = {
+    siBuffer,
+    oneValueByteData,
+    sizeof(oneValueByteData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
+                           KeyUsage::digitalSignature));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &oneValueByte,
+                           KeyUsage::digitalSignature));
+
+  // two-byte control case
+  static uint8_t controlTwoValueBytesData[] = {
+    0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
+    0x80 | 0x01, 0x80
+  };
+  const SECItem controlTwoValueBytes = {
+    siBuffer,
+    controlTwoValueBytesData,
+    sizeof(controlTwoValueBytesData)
+  };
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
+                               &controlTwoValueBytes,
+                               KeyUsage::digitalSignature));
+  ASSERT_Success(CheckKeyUsage(EndEntityOrCA::MustBeCA, &controlTwoValueBytes,
+                               KeyUsage::digitalSignature));
+
+  // two-byte test case
+  static uint8_t twoValueBytesData[] = {
+    0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 7/*unused bits*/,
+    0x80 | 0x01, 0x80 | 0x01
+  };
+  const SECItem twoValueBytes = {
+    siBuffer,
+    twoValueBytesData,
+    sizeof(twoValueBytesData)
+  };
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
+                           KeyUsage::digitalSignature));
+  ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes,
+                           KeyUsage::digitalSignature));
+}