Bug 1006812: Use mozilla::pkix::der to decode the key usage extension, r=keeler

--HG--
extra : rebase_source : e445c913994dc027e1179543d7b6cab2505e734d
This commit is contained in:
Brian Smith 2014-06-19 00:13:20 -07:00
Родитель 8d7ccbd531
Коммит b76e937c55
12 изменённых файлов: 441 добавлений и 92 удалений

Просмотреть файл

@ -604,7 +604,8 @@ VerifySignature(AppTrustedRoot trustedRoot,
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)

Просмотреть файл

@ -302,27 +302,22 @@ destroyCertListThatShouldNotExist(CERTCertList** certChain)
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,
@ -388,7 +383,8 @@ CertVerifier::MozillaPKIXVerifyCert(
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);
@ -413,9 +409,9 @@ CertVerifier::MozillaPKIXVerifyCert(
: 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);
@ -440,9 +436,9 @@ CertVerifier::MozillaPKIXVerifyCert(
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);
@ -453,7 +449,8 @@ CertVerifier::MozillaPKIXVerifyCert(
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;
@ -463,7 +460,8 @@ CertVerifier::MozillaPKIXVerifyCert(
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);
@ -476,13 +474,20 @@ CertVerifier::MozillaPKIXVerifyCert(
// 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;
}
@ -490,7 +495,8 @@ CertVerifier::MozillaPKIXVerifyCert(
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);
@ -504,15 +510,15 @@ CertVerifier::MozillaPKIXVerifyCert(
// 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;
}

Просмотреть файл

@ -93,7 +93,7 @@ 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,

Просмотреть файл

@ -49,7 +49,18 @@ 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,

Просмотреть файл

@ -142,7 +142,7 @@ static Result BuildForward(TrustDomain& trustDomain,
BackCert& subject,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsages requiredKeyUsagesIfPresent,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const SECItem* stapledOCSPResponse,
@ -158,7 +158,7 @@ BuildForwardInner(TrustDomain& trustDomain,
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);
@ -188,9 +188,12 @@ BuildForwardInner(TrustDomain& trustDomain,
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;
}
@ -210,7 +213,7 @@ BuildForward(TrustDomain& trustDomain,
BackCert& subject,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsages requiredKeyUsagesIfPresent,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
/*optional*/ const SECItem* stapledOCSPResponse,
@ -225,7 +228,7 @@ BuildForward(TrustDomain& trustDomain,
// See the explanation of error prioritization in pkix.h.
rv = CheckIssuerIndependentProperties(trustDomain, subject, time,
endEntityOrCA,
requiredKeyUsagesIfPresent,
requiredKeyUsageIfPresent,
requiredEKUIfPresent, requiredPolicy,
subCACount, &trustLevel);
PRErrorCode deferredEndEntityError = 0;
@ -348,8 +351,8 @@ 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)
@ -375,7 +378,7 @@ BuildCertChain(TrustDomain& trustDomain,
}
rv = BuildForward(trustDomain, cert, time, endEntityOrCA,
requiredKeyUsagesIfPresent, requiredEKUIfPresent,
requiredKeyUsageIfPresent, requiredEKUIfPresent,
requiredPolicy, stapledOCSPResponse, 0, results);
if (rv != Success) {
results = nullptr;

Просмотреть файл

@ -46,12 +46,18 @@ CheckTimes(const CERTCertificate* cert, PRTime time)
}
// 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
@ -68,39 +74,92 @@ CheckKeyUsage(EndEntityOrCA endEntityOrCA,
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);
}
// TODO XXX: Why is tmpItem.len > 1?
KeyUsages allowedKeyUsages = tmpItem.data[0];
if ((allowedKeyUsages & requiredKeyUsagesIfPresent)
!= requiredKeyUsagesIfPresent) {
der::Input value;
if (der::ExpectTagAndGetValue(input, der::BIT_STRING, value) != der::Success) {
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) {
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);
}
uint8_t bits;
if (value.Read(bits) != der::Success) {
// Reject empty bit masks.
return Fail(RecoverableError, SEC_ERROR_INADEQUATE_KEY_USAGE);
}
// 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;
@ -549,7 +608,7 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
BackCert& cert,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsages requiredKeyUsagesIfPresent,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
unsigned int subCACount,
@ -584,18 +643,13 @@ CheckIssuerIndependentProperties(TrustDomain& trustDomain,
!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;
}

Просмотреть файл

@ -36,7 +36,7 @@ Result CheckIssuerIndependentProperties(
BackCert& cert,
PRTime time,
EndEntityOrCA endEntityOrCA,
KeyUsages requiredKeyUsagesIfPresent,
KeyUsage requiredKeyUsageIfPresent,
KeyPurposeId requiredEKUIfPresent,
const CertPolicyId& requiredPolicy,
unsigned int subCACount,

Просмотреть файл

@ -147,7 +147,8 @@ CheckOCSPResponseSignerCert(TrustDomain& trustDomain,
// 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) {

Просмотреть файл

@ -11,6 +11,7 @@ SOURCES += [
'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',

Просмотреть файл

@ -190,7 +190,7 @@ 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
@ -205,7 +205,7 @@ TEST_F(pkix_cert_chain_length, MaxAcceptableCertChainLength)
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
@ -227,7 +227,7 @@ TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
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
@ -243,7 +243,7 @@ TEST_F(pkix_cert_chain_length, BeyondMaxAcceptableCertChainLength)
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

Просмотреть файл

@ -150,7 +150,7 @@ TEST_F(pkix_cert_extensions, UnknownCriticalExtension)
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
@ -182,7 +182,7 @@ TEST_F(pkix_cert_extensions, UnknownNonCriticalExtension)
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
0, // key usage
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr, // stapled OCSP response
@ -216,7 +216,7 @@ TEST_F(pkix_cert_extensions, WrongOIDCriticalExtension)
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
@ -248,7 +248,7 @@ TEST_F(pkix_cert_extensions, CriticalAIAExtension)
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
0, // key usage
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr, // stapled OCSP response
@ -281,7 +281,7 @@ TEST_F(pkix_cert_extensions, UnknownCriticalCEExtension)
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
@ -311,7 +311,7 @@ TEST_F(pkix_cert_extensions, KnownCriticalCEExtension)
ScopedCERTCertList results;
ASSERT_SECSuccess(BuildCertChain(trustDomain, cert.get(),
now, EndEntityOrCA::MustBeEndEntity,
0, // key usage
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr, // stapled OCSP response

Просмотреть файл

@ -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));
}