Update NSS to 3.41

This commit is contained in:
wolfbeast 2018-12-15 01:42:53 +01:00 • committed by Roy Tam
commit 5f0986e66f
540 changed files with 49568 additions and 10631 deletions

View file

@ -0,0 +1,61 @@
-------------
Running Tests
-------------
Because of the rules below, you can run all the unit tests in this directory,
and only these tests, with:
mach gtest "pkix*"
You can run just the tests for functions defined in filename pkixfoo.cpp with:
mach gtest "pkixfoo*"
If you run "mach gtest" then you'll end up running every gtest in Gecko.
------------
Naming Files
------------
Name files containing tests according to one of the following patterns:
* <filename>_tests.cpp
* <filename>_<Function>_tests.cpp
* <filename>_<category>_tests.cpp
<filename> is the name of the file containing the definitions of the
function(s) being tested by every test.
<Function> is the name of the function that is being tested by every
test.
<category> describes the group of related functions that are being
tested by every test.
------------------------------------------------
Always Use a Fixture Class: TEST_F(), not TEST()
------------------------------------------------
Many tests don't technically need a fixture, and so TEST() could technically
be used to define the test. However, when you use TEST_F() instead of TEST(),
the compiler will not allow you to make any typos in the test case name, but
if you use TEST() then the name of the test case is not checked.
See https://code.google.com/p/googletest/wiki/Primer#Test_Fixtures:_Using_the_Same_Data_Configuration_for_Multiple_Te
to learn more about test fixtures.
---------------
Naming Fixtures
---------------
When all tests in a file use the same fixture, use the base name of the file
without the "_tests" suffix as the name of the fixture class; e.g. tests in
"pkixocsp.cpp" should use a fixture "class pkixocsp" by default.
Sometimes tests in a file need separate fixtures. In this case, name the
fixture class according to the pattern <fixture_base>_<fixture_suffix>, where
<fixture_base> is the base name of the file without the "_tests" suffix, and
<fixture_suffix> is a descriptive name for the fixture class, e.g.
"class pkixocsp_DelegatedResponder".

View file

@ -0,0 +1,71 @@
# 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/.
{
'includes': [
'../../coreconf/config.gypi',
'../common/gtest.gypi',
],
'targets': [
{
'target_name': 'mozpkix_gtest',
'type': 'executable',
'sources': [
'<(DEPTH)/gtests/common/gtests.cc',
'pkixbuild_tests.cpp',
'pkixcert_extension_tests.cpp',
'pkixcert_signature_algorithm_tests.cpp',
'pkixcheck_CheckExtendedKeyUsage_tests.cpp',
'pkixcheck_CheckIssuer_tests.cpp',
'pkixcheck_CheckKeyUsage_tests.cpp',
'pkixcheck_CheckSignatureAlgorithm_tests.cpp',
'pkixcheck_CheckValidity_tests.cpp',
'pkixcheck_ParseValidity_tests.cpp',
'pkixcheck_TLSFeaturesSatisfiedInternal_tests.cpp',
'pkixder_input_tests.cpp',
'pkixder_pki_types_tests.cpp',
'pkixder_universal_types_tests.cpp',
'pkixgtest.cpp',
'pkixnames_tests.cpp',
'pkixocsp_CreateEncodedOCSPRequest_tests.cpp',
'pkixocsp_VerifyEncodedOCSPResponse.cpp',
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
'<(DEPTH)/gtests/google_test/google_test.gyp:gtest',
'<(DEPTH)/lib/util/util.gyp:nssutil',
'<(DEPTH)/lib/ssl/ssl.gyp:ssl',
'<(DEPTH)/lib/nss/nss.gyp:nss_static',
'<(DEPTH)/lib/pk11wrap/pk11wrap.gyp:pk11wrap_static',
'<(DEPTH)/lib/cryptohi/cryptohi.gyp:cryptohi',
'<(DEPTH)/lib/certhigh/certhigh.gyp:certhi',
'<(DEPTH)/lib/certdb/certdb.gyp:certdb',
'<(DEPTH)/lib/base/base.gyp:nssb',
'<(DEPTH)/lib/dev/dev.gyp:nssdev',
'<(DEPTH)/lib/pki/pki.gyp:nsspki',
'<(DEPTH)/lib/mozpkix/mozpkix.gyp:mozpkix',
'<(DEPTH)/lib/mozpkix/mozpkix.gyp:mozpkix-testlib',
],
'include_dirs': [
'<(DEPTH)/lib/mozpkix/',
'<(DEPTH)/lib/mozpkix/lib',
'<(DEPTH)/lib/mozpkix/include/',
'<(DEPTH)/lib/mozpkix/include/pkix-test/',
],
'conditions': [
[ 'OS=="win"', {
'libraries': [
'advapi32.lib',
],
}],
],
'defines': [
'NSS_USE_STATIC_LIBS'
],
}
],
'variables': {
'module': 'nss',
'use_static_libs': 1,
}
}

View file

@ -0,0 +1,894 @@
/* -*- 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.
*/
#if defined(_MSC_VER) && _MSC_VER < 1900
// When building with -D_HAS_EXCEPTIONS=0, MSVC's <xtree> header triggers
// warning C4702: unreachable code.
// https://connect.microsoft.com/VisualStudio/feedback/details/809962
#pragma warning(push)
#pragma warning(disable: 4702)
#endif
#include <map>
#include <vector>
#if defined(_MSC_VER) && _MSC_VER < 1900
#pragma warning(pop)
#endif
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static ByteString
CreateCert(const char* issuerCN, // null means "empty name"
const char* subjectCN, // null means "empty name"
EndEntityOrCA endEntityOrCA,
/*optional modified*/ std::map<ByteString, ByteString>*
subjectDERToCertDER = nullptr,
/*optional*/ const ByteString* extension = nullptr,
/*optional*/ const TestKeyPair* issuerKeyPair = nullptr,
/*optional*/ const TestKeyPair* subjectKeyPair = nullptr)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(issuerCN ? CNToDERName(issuerCN) : Name(ByteString()));
ByteString subjectDER(subjectCN ? CNToDERName(subjectCN) : Name(ByteString()));
std::vector<ByteString> extensions;
if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
ByteString basicConstraints =
CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(basicConstraints));
extensions.push_back(basicConstraints);
}
if (extension) {
extensions.push_back(*extension);
}
extensions.push_back(ByteString()); // marks the end of the list
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow, subjectDER,
subjectKeyPair ? *subjectKeyPair : *reusedKey,
extensions.data(),
issuerKeyPair ? *issuerKeyPair : *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
if (subjectDERToCertDER) {
(*subjectDERToCertDER)[subjectDER] = certDER;
}
return certDER;
}
class TestTrustDomain final : public DefaultCryptoTrustDomain
{
public:
// The "cert chain tail" is a longish chain of certificates that is used by
// all of the tests here. We share this chain across all the tests in order
// to speed up the tests (generating keypairs for the certs is very slow).
bool SetUpCertChainTail()
{
static char const* const names[] = {
"CA1 (Root)", "CA2", "CA3", "CA4", "CA5", "CA6", "CA7"
};
for (size_t i = 0; i < MOZILLA_PKIX_ARRAY_LENGTH(names); ++i) {
const char* issuerName = i == 0 ? names[0] : names[i-1];
CreateCACert(issuerName, names[i]);
if (i == 0) {
rootCACertDER = leafCACertDER;
}
}
return true;
}
void CreateCACert(const char* issuerName, const char* subjectName)
{
leafCACertDER = CreateCert(issuerName, subjectName,
EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
assert(!ENCODING_FAILED(leafCACertDER));
}
ByteString GetLeafCACertDER() const { return leafCACertDER; }
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString subjectDER(InputToByteString(encodedIssuerName));
ByteString certDER(subjectDERToCertDER[subjectDER]);
Input derCert;
Result rv = derCert.Init(certDER.data(), certDER.length());
if (rv != Success) {
return rv;
}
bool keepGoing;
rv = checker.Check(derCert, nullptr/*additionalNameConstraints*/,
keepGoing);
if (rv != Success) {
return rv;
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::map<ByteString, ByteString> subjectDERToCertDER;
ByteString leafCACertDER;
ByteString rootCACertDER;
};
class pkixbuild : public ::testing::Test
{
public:
static void SetUpTestCase()
{
if (!trustDomain.SetUpCertChainTail()) {
abort();
}
}
protected:
static TestTrustDomain trustDomain;
};
/*static*/ TestTrustDomain pkixbuild::trustDomain;
TEST_F(pkixbuild, MaxAcceptableCertChainLength)
{
{
ByteString leafCACert(trustDomain.GetLeafCACertDER());
Input certDER;
ASSERT_EQ(Success, certDER.Init(leafCACert.data(), leafCACert.length()));
ASSERT_EQ(Success,
BuildCertChain(trustDomain, certDER, Now(),
EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
{
ByteString certDER(CreateCert("CA7", "Direct End-Entity",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Success,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
}
TEST_F(pkixbuild, BeyondMaxAcceptableCertChainLength)
{
static char const* const caCertName = "CA Too Far";
trustDomain.CreateCACert("CA7", caCertName);
{
ByteString certDER(trustDomain.GetLeafCACertDER());
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeCA,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
{
ByteString certDER(CreateCert(caCertName, "End-Entity Too Far",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(trustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
}
// A TrustDomain that checks certificates against a given root certificate.
// It is initialized with the DER encoding of a root certificate that
// is treated as a trust anchor and is assumed to have issued all certificates
// (i.e. FindIssuer always attempts to build the next step in the chain with
// it).
class SingleRootTrustDomain : public DefaultCryptoTrustDomain
{
public:
explicit SingleRootTrustDomain(ByteString aRootDER)
: rootDER(aRootDER)
{
}
// The CertPolicyId argument is unused because we don't care about EV.
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
Input rootCert;
Result rv = rootCert.Init(rootDER.data(), rootDER.length());
if (rv != Success) {
return rv;
}
if (InputsAreEqual(candidateCert, rootCert)) {
trustLevel = TrustLevel::TrustAnchor;
} else {
trustLevel = TrustLevel::InheritsTrust;
}
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
// keepGoing is an out parameter from IssuerChecker.Check. It would tell us
// whether or not to continue attempting other potential issuers. We only
// know of one potential issuer, however, so we ignore it.
bool keepGoing;
Input rootCert;
Result rv = rootCert.Init(rootDER.data(), rootDER.length());
if (rv != Success) {
return rv;
}
return checker.Check(rootCert, nullptr, keepGoing);
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
private:
ByteString rootDER;
};
// A TrustDomain that explicitly fails if CheckRevocation is called.
class ExpiredCertTrustDomain final : public SingleRootTrustDomain
{
public:
explicit ExpiredCertTrustDomain(ByteString aRootDER)
: SingleRootTrustDomain(aRootDER)
{
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
ADD_FAILURE();
return NotReached("CheckRevocation should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
};
TEST_F(pkixbuild, NoRevocationCheckingForExpiredCert)
{
const char* rootCN = "Root CA";
ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
nullptr));
EXPECT_FALSE(ENCODING_FAILED(rootDER));
ExpiredCertTrustDomain expiredCertTrustDomain(rootDER);
ByteString serialNumber(CreateEncodedSerialNumber(100));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(rootCN));
ByteString subjectDER(CNToDERName("Expired End-Entity Cert"));
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
twoDaysBeforeNow,
oneDayBeforeNow,
subjectDER, *reusedKey, nullptr, *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
Input cert;
ASSERT_EQ(Success, cert.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
BuildCertChain(expiredCertTrustDomain, cert, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr));
}
class DSSTrustDomain final : public EverythingFailsByDefaultTrustDomain
{
public:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
Input, /*out*/ TrustLevel& trustLevel) override
{
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
};
class pkixbuild_DSS : public ::testing::Test { };
TEST_F(pkixbuild_DSS, DSSEndEntityKeyNotAccepted)
{
DSSTrustDomain trustDomain;
ByteString serialNumber(CreateEncodedSerialNumber(1));
ASSERT_FALSE(ENCODING_FAILED(serialNumber));
ByteString subjectDER(CNToDERName("DSS"));
ASSERT_FALSE(ENCODING_FAILED(subjectDER));
ScopedTestKeyPair subjectKey(GenerateDSSKeyPair());
ASSERT_TRUE(subjectKey.get());
ByteString issuerDER(CNToDERName("RSA"));
ASSERT_FALSE(ENCODING_FAILED(issuerDER));
ScopedTestKeyPair issuerKey(CloneReusedKeyPair());
ASSERT_TRUE(issuerKey.get());
ByteString cert(CreateEncodedCertificate(v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *subjectKey, nullptr,
*issuerKey, sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certDER;
ASSERT_EQ(Success, certDER.Init(cert.data(), cert.length()));
ASSERT_EQ(Result::ERROR_UNSUPPORTED_KEYALG,
BuildCertChain(trustDomain, certDER, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
class IssuerNameCheckTrustDomain final : public DefaultCryptoTrustDomain
{
public:
IssuerNameCheckTrustDomain(const ByteString& aIssuer, bool aExpectedKeepGoing)
: issuer(aIssuer)
, expectedKeepGoing(aExpectedKeepGoing)
{
}
Result GetCertTrust(EndEntityOrCA endEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = endEntityOrCA == EndEntityOrCA::MustBeCA
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
Input issuerInput;
EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
bool keepGoing;
EXPECT_EQ(Success,
checker.Check(issuerInput, nullptr /*additionalNameConstraints*/,
keepGoing));
EXPECT_EQ(expectedKeepGoing, keepGoing);
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
private:
const ByteString issuer;
const bool expectedKeepGoing;
};
struct IssuerNameCheckParams
{
const char* subjectIssuerCN; // null means "empty name"
const char* issuerSubjectCN; // null means "empty name"
bool matches;
Result expectedError;
};
static const IssuerNameCheckParams ISSUER_NAME_CHECK_PARAMS[] =
{
{ "foo", "foo", true, Success },
{ "foo", "bar", false, Result::ERROR_UNKNOWN_ISSUER },
{ "f", "foo", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
{ "foo", "f", false, Result::ERROR_UNKNOWN_ISSUER }, // prefix
{ "foo", "Foo", false, Result::ERROR_UNKNOWN_ISSUER }, // case sensitive
{ "", "", true, Success },
{ nullptr, nullptr, false, Result::ERROR_EMPTY_ISSUER_NAME }, // empty issuer
// check that certificate-related errors are deferred and superseded by
// ERROR_UNKNOWN_ISSUER when a chain can't be built due to name mismatches
{ "foo", nullptr, false, Result::ERROR_UNKNOWN_ISSUER },
{ nullptr, "foo", false, Result::ERROR_UNKNOWN_ISSUER }
};
class pkixbuild_IssuerNameCheck
: public ::testing::Test
, public ::testing::WithParamInterface<IssuerNameCheckParams>
{
};
TEST_P(pkixbuild_IssuerNameCheck, MatchingName)
{
const IssuerNameCheckParams& params(GetParam());
ByteString issuerCertDER(CreateCert(params.issuerSubjectCN,
params.issuerSubjectCN,
EndEntityOrCA::MustBeCA, nullptr));
ASSERT_FALSE(ENCODING_FAILED(issuerCertDER));
ByteString subjectCertDER(CreateCert(params.subjectIssuerCN, "end-entity",
EndEntityOrCA::MustBeEndEntity,
nullptr));
ASSERT_FALSE(ENCODING_FAILED(subjectCertDER));
Input subjectCertDERInput;
ASSERT_EQ(Success, subjectCertDERInput.Init(subjectCertDER.data(),
subjectCertDER.length()));
IssuerNameCheckTrustDomain trustDomain(issuerCertDER, !params.matches);
ASSERT_EQ(params.expectedError,
BuildCertChain(trustDomain, subjectCertDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
INSTANTIATE_TEST_CASE_P(pkixbuild_IssuerNameCheck, pkixbuild_IssuerNameCheck,
testing::ValuesIn(ISSUER_NAME_CHECK_PARAMS));
// Records the embedded SCT list extension for later examination.
class EmbeddedSCTListTestTrustDomain final : public SingleRootTrustDomain
{
public:
explicit EmbeddedSCTListTestTrustDomain(ByteString aRootDER)
: SingleRootTrustDomain(aRootDER)
{
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension extension,
Input extensionData) override
{
if (extension == AuxiliaryExtension::EmbeddedSCTList) {
signedCertificateTimestamps = InputToByteString(extensionData);
} else {
ADD_FAILURE();
}
}
ByteString signedCertificateTimestamps;
};
TEST_F(pkixbuild, CertificateTransparencyExtension)
{
// python security/pkix/tools/DottedOIDToCode.py --tlv
// id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
static const uint8_t tlv_id_embeddedSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
ByteString ctExtension = TLV(der::SEQUENCE,
BytesToByteString(tlv_id_embeddedSctList) +
Boolean(false) +
TLV(der::OCTET_STRING,
// SignedCertificateTimestampList structure is encoded as an OCTET STRING
// within the X.509v3 extension (see RFC 6962 section 3.3).
// pkix decodes it internally and returns the actual structure.
TLV(der::OCTET_STRING, BytesToByteString(dummySctList))));
const char* rootCN = "Root CA";
ByteString rootDER(CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA));
ASSERT_FALSE(ENCODING_FAILED(rootDER));
ByteString certDER(CreateCert(rootCN, "Cert with SCT list",
EndEntityOrCA::MustBeEndEntity,
nullptr, /*subjectDERToCertDER*/
&ctExtension));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certInput;
ASSERT_EQ(Success, certInput.Init(certDER.data(), certDER.length()));
EmbeddedSCTListTestTrustDomain extTrustDomain(rootDER);
ASSERT_EQ(Success,
BuildCertChain(extTrustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr /*stapledOCSPResponse*/));
ASSERT_EQ(BytesToByteString(dummySctList),
extTrustDomain.signedCertificateTimestamps);
}
// This TrustDomain implements a hierarchy like so:
//
// A B
// | |
// C D
// \ /
// E
//
// where A is a trust anchor, B is not a trust anchor and has no known issuer, C
// and D are intermediates with the same subject and subject public key, and E
// is an end-entity (in practice, the end-entity will be generated by the test
// functions using this trust domain).
class MultiplePathTrustDomain: public DefaultCryptoTrustDomain
{
public:
void SetUpCerts()
{
ASSERT_FALSE(ENCODING_FAILED(CreateCert("UntrustedRoot", "UntrustedRoot",
EndEntityOrCA::MustBeCA,
&subjectDERToCertDER)));
// The subject DER -> cert DER mapping would be overwritten for subject
// "Intermediate" when we create the second "Intermediate" certificate, so
// we keep a copy of this "Intermediate".
intermediateSignedByUntrustedRootCertDER =
CreateCert("UntrustedRoot", "Intermediate", EndEntityOrCA::MustBeCA);
ASSERT_FALSE(ENCODING_FAILED(intermediateSignedByUntrustedRootCertDER));
rootCACertDER = CreateCert("TrustedRoot", "TrustedRoot",
EndEntityOrCA::MustBeCA, &subjectDERToCertDER);
ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
ASSERT_FALSE(ENCODING_FAILED(CreateCert("TrustedRoot", "Intermediate",
EndEntityOrCA::MustBeCA,
&subjectDERToCertDER)));
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result CheckCert(ByteString& certDER, IssuerChecker& checker, bool& keepGoing)
{
Input derCert;
Result rv = derCert.Init(certDER.data(), certDER.length());
if (rv != Success) {
return rv;
}
return checker.Check(derCert, nullptr/*additionalNameConstraints*/,
keepGoing);
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString subjectDER(InputToByteString(encodedIssuerName));
ByteString certDER(subjectDERToCertDER[subjectDER]);
assert(!ENCODING_FAILED(certDER));
bool keepGoing;
Result rv = CheckCert(certDER, checker, keepGoing);
if (rv != Success) {
return rv;
}
// Also try the other intermediate.
if (keepGoing) {
rv = CheckCert(intermediateSignedByUntrustedRootCertDER, checker,
keepGoing);
if (rv != Success) {
return rv;
}
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::map<ByteString, ByteString> subjectDERToCertDER;
ByteString rootCACertDER;
ByteString intermediateSignedByUntrustedRootCertDER;
};
TEST_F(pkixbuild, BadEmbeddedSCTWithMultiplePaths)
{
MultiplePathTrustDomain localTrustDomain;
localTrustDomain.SetUpCerts();
// python security/pkix/tools/DottedOIDToCode.py --tlv
// id-embeddedSctList 1.3.6.1.4.1.11129.2.4.2
static const uint8_t tlv_id_embeddedSctList[] = {
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xd6, 0x79, 0x02, 0x04, 0x02
};
static const uint8_t dummySctList[] = {
0x01, 0x02, 0x03, 0x04, 0x05
};
ByteString ctExtension = TLV(der::SEQUENCE,
BytesToByteString(tlv_id_embeddedSctList) +
Boolean(false) +
// The contents of the OCTET STRING are supposed to consist of an OCTET
// STRING of useful data. We're testing what happens if it isn't, so shove
// some bogus (non-OCTET STRING) data in there.
TLV(der::OCTET_STRING, BytesToByteString(dummySctList)));
ByteString certDER(CreateCert("Intermediate", "Cert with bogus SCT list",
EndEntityOrCA::MustBeEndEntity,
nullptr, /*subjectDERToCertDER*/
&ctExtension));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_BAD_DER,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
// Same as a MultiplePathTrustDomain, but the end-entity is revoked.
class RevokedEndEntityTrustDomain final : public MultiplePathTrustDomain
{
public:
Result CheckRevocation(EndEntityOrCA endEntityOrCA, const CertID&, Time,
Duration, /*optional*/ const Input*,
/*optional*/ const Input*) override
{
if (endEntityOrCA == EndEntityOrCA::MustBeEndEntity) {
return Result::ERROR_REVOKED_CERTIFICATE;
}
return Success;
}
};
TEST_F(pkixbuild, RevokedEndEntityWithMultiplePaths)
{
RevokedEndEntityTrustDomain localTrustDomain;
localTrustDomain.SetUpCerts();
ByteString certDER(CreateCert("Intermediate", "RevokedEndEntity",
EndEntityOrCA::MustBeEndEntity));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_REVOKED_CERTIFICATE,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
// This represents a collection of different certificates that all have the same
// subject and issuer distinguished name.
class SelfIssuedCertificatesTrustDomain final : public DefaultCryptoTrustDomain
{
public:
void SetUpCerts(size_t totalCerts)
{
ASSERT_TRUE(totalCerts > 0);
// First we generate a trust anchor.
ScopedTestKeyPair rootKeyPair(GenerateKeyPair());
rootCACertDER = CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, rootKeyPair.get(), rootKeyPair.get());
ASSERT_FALSE(ENCODING_FAILED(rootCACertDER));
certs.push_back(rootCACertDER);
ScopedTestKeyPair issuerKeyPair(rootKeyPair.release());
size_t subCAsGenerated;
// Then we generate 6 sub-CAs (given that we were requested to generate at
// least that many).
for (subCAsGenerated = 0;
subCAsGenerated < totalCerts - 1 && subCAsGenerated < 6;
subCAsGenerated++) {
// Each certificate has to have a unique SPKI (mozilla::pkix does loop
// detection and stops searching if it encounters two certificates in a
// path with the same subject and SPKI).
ScopedTestKeyPair keyPair(GenerateKeyPair());
ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, issuerKeyPair.get(), keyPair.get()));
ASSERT_FALSE(ENCODING_FAILED(cert));
certs.push_back(cert);
issuerKeyPair.reset(keyPair.release());
}
// We set firstIssuerKey here because we can't end up with a path that has
// more than 7 CAs in it (because mozilla::pkix limits the path length).
firstIssuerKey.reset(issuerKeyPair.release());
// For any more sub CAs we generate, it doesn't matter what their keys are
// as long as they're different.
for (; subCAsGenerated < totalCerts - 1; subCAsGenerated++) {
ScopedTestKeyPair keyPair(GenerateKeyPair());
ByteString cert(CreateCert("DN", "DN", EndEntityOrCA::MustBeCA, nullptr,
nullptr, keyPair.get(), keyPair.get()));
ASSERT_FALSE(ENCODING_FAILED(cert));
certs.insert(certs.begin(), cert);
}
}
const TestKeyPair* GetFirstIssuerKey()
{
return firstIssuerKey.get();
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, rootCACertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
bool keepGoing;
for (auto& cert: certs) {
Input certInput;
Result rv = certInput.Init(cert.data(), cert.length());
if (rv != Success) {
return rv;
}
rv = checker.Check(certInput, nullptr, keepGoing);
if (rv != Success || !keepGoing) {
return rv;
}
}
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
std::vector<ByteString> certs;
ByteString rootCACertDER;
ScopedTestKeyPair firstIssuerKey;
};
TEST_F(pkixbuild, AvoidUnboundedPathSearchingFailure)
{
SelfIssuedCertificatesTrustDomain localTrustDomain;
// This creates a few hundred million potential paths of length 8 (end entity
// + 6 sub-CAs + root). It would be prohibitively expensive to enumerate all
// of these, so we give mozilla::pkix a budget that is spent when searching
// paths. If the budget is exhausted, it simply returns an unknown issuer
// error. In the future it might be nice to return a specific error that would
// give the front-end a hint that maybe it shouldn't have so many certificates
// that all have the same subject and issuer DN but different SPKIs.
localTrustDomain.SetUpCerts(18);
ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
nullptr, nullptr,
localTrustDomain.GetFirstIssuerKey()));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Result::ERROR_UNKNOWN_ISSUER,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
TEST_F(pkixbuild, AvoidUnboundedPathSearchingSuccess)
{
SelfIssuedCertificatesTrustDomain localTrustDomain;
// This creates a few hundred thousand possible potential paths of length 8
// (end entity + 6 sub-CAs + root). This will nearly exhaust mozilla::pkix's
// search budget, so this should succeed.
localTrustDomain.SetUpCerts(10);
ByteString certDER(CreateCert("DN", "DN", EndEntityOrCA::MustBeEndEntity,
nullptr, nullptr,
localTrustDomain.GetFirstIssuerKey()));
ASSERT_FALSE(ENCODING_FAILED(certDER));
Input certDERInput;
ASSERT_EQ(Success, certDERInput.Init(certDER.data(), certDER.length()));
ASSERT_EQ(Success,
BuildCertChain(localTrustDomain, certDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}

View file

@ -0,0 +1,276 @@
/* -*- 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"
#include "mozpkix/pkixder.h"
#include "mozpkix/test/pkixtestutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
// Creates a self-signed certificate with the given extension.
static ByteString
CreateCertWithExtensions(const char* subjectCN,
const ByteString* extensions)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(subjectCN));
EXPECT_FALSE(ENCODING_FAILED(issuerDER));
ByteString subjectDER(CNToDERName(subjectCN));
EXPECT_FALSE(ENCODING_FAILED(subjectDER));
ScopedTestKeyPair subjectKey(CloneReusedKeyPair());
return CreateEncodedCertificate(v3, sha256WithRSAEncryption(),
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *subjectKey, extensions,
*subjectKey,
sha256WithRSAEncryption());
}
// Creates a self-signed certificate with the given extension.
static ByteString
CreateCertWithOneExtension(const char* subjectStr, const ByteString& extension)
{
const ByteString extensions[] = { extension, ByteString() };
return CreateCertWithExtensions(subjectStr, extensions);
}
class TrustEverythingTrustDomain final : public DefaultCryptoTrustDomain
{
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel& trustLevel) override
{
trustLevel = TrustLevel::TrustAnchor;
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*, /*optional*/ const Input*)
override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
};
// python DottedOIDToCode.py --tlv unknownExtensionOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
static const uint8_t tlv_unknownExtensionOID[] = {
0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a,
0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03
};
// python DottedOIDToCode.py --tlv id-pe-authorityInformationAccess 1.3.6.1.5.5.7.1.1
static const uint8_t tlv_id_pe_authorityInformationAccess[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv wrongExtensionOID 1.3.6.6.1.5.5.7.1.1
// (there is an extra "6" that shouldn't be in this OID)
static const uint8_t tlv_wrongExtensionOID[] = {
0x06, 0x09, 0x2b, 0x06, 0x06, 0x01, 0x05, 0x05, 0x07, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv id-ce-unknown 2.5.29.55
// (this is a made-up OID for testing "id-ce"-prefixed OIDs that mozilla::pkix
// doesn't handle)
static const uint8_t tlv_id_ce_unknown[] = {
0x06, 0x03, 0x55, 0x1d, 0x37
};
// python DottedOIDToCode.py --tlv id-ce-inhibitAnyPolicy 2.5.29.54
static const uint8_t tlv_id_ce_inhibitAnyPolicy[] = {
0x06, 0x03, 0x55, 0x1d, 0x36
};
// python DottedOIDToCode.py --tlv id-pkix-ocsp-nocheck 1.3.6.1.5.5.7.48.1.5
static const uint8_t tlv_id_pkix_ocsp_nocheck[] = {
0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05
};
struct ExtensionTestcase
{
ByteString extension;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os, const ExtensionTestcase&)
{
return os << "TODO (bug 1318770)";
}
static const ExtensionTestcase EXTENSION_TESTCASES[] =
{
// Tests that a non-critical extension not in the id-ce or id-pe arcs (which
// is thus unknown to us) verifies successfully even if empty (extensions we
// know about aren't normally allowed to be empty).
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_unknownExtensionOID) +
TLV(der::OCTET_STRING, ByteString())),
Success
},
// Tests that a critical extension not in the id-ce or id-pe arcs (which is
// thus unknown to us) is detected and that verification fails with the
// appropriate error.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_unknownExtensionOID) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// Tests that a id-pe-authorityInformationAccess critical extension
// is detected and that verification succeeds.
// XXX: According to RFC 5280 an AIA that consists of an empty sequence is
// not legal, but we accept it and that is not what we're testing here.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pe_authorityInformationAccess) +
Boolean(true) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ByteString()))),
Success
},
// Tests that an incorrect OID for id-pe-authorityInformationAccess
// (when marked critical) is detected and that verification fails.
// (Until bug 1020993 was fixed, this wrong value was used for
// id-pe-authorityInformationAccess.)
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_wrongExtensionOID) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// 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
// fails.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_unknown) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Result::ERROR_UNKNOWN_CRITICAL_EXTENSION
},
// 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.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_inhibitAnyPolicy) +
Boolean(true) +
TLV(der::OCTET_STRING, Integer(0))),
Success
},
// Tests that a certificate with the id-pkix-ocsp-nocheck extension (marked
// critical) verifies successfully.
// RFC 6960:
// ext-ocsp-nocheck EXTENSION ::= { SYNTAX NULL IDENTIFIED
// BY id-pkix-ocsp-nocheck }
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pkix_ocsp_nocheck) +
Boolean(true) +
TLV(der::OCTET_STRING, TLV(der::NULLTag, ByteString()))),
Success
},
// Tests that a certificate with another representation of the
// id-pkix-ocsp-nocheck extension (marked critical) verifies successfully.
// According to http://comments.gmane.org/gmane.ietf.x509/30947,
// some code creates certificates where value of the extension is
// an empty OCTET STRING.
{ TLV(der::SEQUENCE,
BytesToByteString(tlv_id_pkix_ocsp_nocheck) +
Boolean(true) +
TLV(der::OCTET_STRING, ByteString())),
Success
},
};
class pkixcert_extension
: public ::testing::Test
, public ::testing::WithParamInterface<ExtensionTestcase>
{
protected:
static TrustEverythingTrustDomain trustDomain;
};
/*static*/ TrustEverythingTrustDomain pkixcert_extension::trustDomain;
TEST_P(pkixcert_extension, ExtensionHandledProperly)
{
const ExtensionTestcase& testcase(GetParam());
const char* cn = "Cert Extension Test";
ByteString cert(CreateCertWithOneExtension(cn, testcase.extension));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certInput;
ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
ASSERT_EQ(testcase.expectedResult,
BuildCertChain(trustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}
INSTANTIATE_TEST_CASE_P(pkixcert_extension,
pkixcert_extension,
testing::ValuesIn(EXTENSION_TESTCASES));
// Two subjectAltNames must result in an error.
TEST_F(pkixcert_extension, DuplicateSubjectAltName)
{
// python DottedOIDToCode.py --tlv id-ce-subjectAltName 2.5.29.17
static const uint8_t tlv_id_ce_subjectAltName[] = {
0x06, 0x03, 0x55, 0x1d, 0x11
};
ByteString subjectAltName(
TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_subjectAltName) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, DNSName("example.com")))));
static const ByteString extensions[] = { subjectAltName, subjectAltName,
ByteString() };
static const char* certCN = "Cert With Duplicate subjectAltName";
ByteString cert(CreateCertWithExtensions(certCN, extensions));
ASSERT_FALSE(ENCODING_FAILED(cert));
Input certInput;
ASSERT_EQ(Success, certInput.Init(cert.data(), cert.length()));
ASSERT_EQ(Result::ERROR_EXTENSION_VALUE_INVALID,
BuildCertChain(trustDomain, certInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr/*stapledOCSPResponse*/));
}

View file

@ -0,0 +1,259 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static ByteString
CreateCert(const char* issuerCN,
const char* subjectCN,
EndEntityOrCA endEntityOrCA,
const TestSignatureAlgorithm& signatureAlgorithm,
/*out*/ ByteString& subjectDER)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(issuerCN));
EXPECT_FALSE(ENCODING_FAILED(issuerDER));
subjectDER = CNToDERName(subjectCN);
EXPECT_FALSE(ENCODING_FAILED(subjectDER));
ByteString extensions[2];
if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
extensions[0] =
CreateEncodedBasicConstraints(true, nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
}
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(v3, signatureAlgorithm,
serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow,
subjectDER, *reusedKey,
extensions, *reusedKey,
signatureAlgorithm));
EXPECT_FALSE(ENCODING_FAILED(certDER));
return certDER;
}
class AlgorithmTestsTrustDomain final : public DefaultCryptoTrustDomain
{
public:
AlgorithmTestsTrustDomain(const ByteString& aRootDER,
const ByteString& aRootSubjectDER,
/*optional*/ const ByteString& aIntDER,
/*optional*/ const ByteString& aIntSubjectDER)
: rootDER(aRootDER)
, rootSubjectDER(aRootSubjectDER)
, intDER(aIntDER)
, intSubjectDER(aIntSubjectDER)
{
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
/*out*/ TrustLevel& trustLevel) override
{
if (InputEqualsByteString(candidateCert, rootDER)) {
trustLevel = TrustLevel::TrustAnchor;
} else {
trustLevel = TrustLevel::InheritsTrust;
}
return Success;
}
Result FindIssuer(Input encodedIssuerName, IssuerChecker& checker, Time)
override
{
ByteString* issuerDER = nullptr;
if (InputEqualsByteString(encodedIssuerName, rootSubjectDER)) {
issuerDER = &rootDER;
} else if (InputEqualsByteString(encodedIssuerName, intSubjectDER)) {
issuerDER = &intDER;
} else {
// FindIssuer just returns success if it can't find a potential issuer.
return Success;
}
Input issuerCert;
Result rv = issuerCert.Init(issuerDER->data(), issuerDER->length());
if (rv != Success) {
return rv;
}
bool keepGoing;
return checker.Check(issuerCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString rootDER;
ByteString rootSubjectDER;
ByteString intDER;
ByteString intSubjectDER;
};
static const TestSignatureAlgorithm NO_INTERMEDIATE
{
TestPublicKeyAlgorithm(ByteString()),
TestDigestAlgorithmID::MD2,
ByteString(),
false
};
struct ChainValidity final
{
ChainValidity(const TestSignatureAlgorithm& aEndEntitySignatureAlgorithm,
const TestSignatureAlgorithm& aOptionalIntSignatureAlgorithm,
const TestSignatureAlgorithm& aRootSignatureAlgorithm,
bool aIsValid)
: endEntitySignatureAlgorithm(aEndEntitySignatureAlgorithm)
, optionalIntermediateSignatureAlgorithm(aOptionalIntSignatureAlgorithm)
, rootSignatureAlgorithm(aRootSignatureAlgorithm)
, isValid(aIsValid)
{ }
// In general, a certificate is generated for each of these. However, if
// optionalIntermediateSignatureAlgorithm is NO_INTERMEDIATE, then only 2
// certificates are generated.
// The certificate generated for the given rootSignatureAlgorithm is the
// trust anchor.
TestSignatureAlgorithm endEntitySignatureAlgorithm;
TestSignatureAlgorithm optionalIntermediateSignatureAlgorithm;
TestSignatureAlgorithm rootSignatureAlgorithm;
bool isValid;
};
static const ChainValidity CHAIN_VALIDITY[] =
{
// The trust anchor may have a signature with an unsupported signature
// algorithm.
ChainValidity(sha256WithRSAEncryption(),
NO_INTERMEDIATE,
md5WithRSAEncryption(),
true),
ChainValidity(sha256WithRSAEncryption(),
NO_INTERMEDIATE,
md2WithRSAEncryption(),
true),
// Certificates that are not trust anchors must not have a signature with an
// unsupported signature algorithm.
ChainValidity(md5WithRSAEncryption(),
NO_INTERMEDIATE,
sha256WithRSAEncryption(),
false),
ChainValidity(md2WithRSAEncryption(),
NO_INTERMEDIATE,
sha256WithRSAEncryption(),
false),
ChainValidity(md2WithRSAEncryption(),
NO_INTERMEDIATE,
md5WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md5WithRSAEncryption(),
sha256WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md2WithRSAEncryption(),
sha256WithRSAEncryption(),
false),
ChainValidity(sha256WithRSAEncryption(),
md2WithRSAEncryption(),
md5WithRSAEncryption(),
false),
};
class pkixcert_IsValidChainForAlgorithm
: public ::testing::Test
, public ::testing::WithParamInterface<ChainValidity>
{
};
::std::ostream& operator<<(::std::ostream& os,
const pkixcert_IsValidChainForAlgorithm&)
{
return os << "TODO (bug 1318770)";
}
::std::ostream& operator<<(::std::ostream& os, const ChainValidity&)
{
return os << "TODO (bug 1318770)";
}
TEST_P(pkixcert_IsValidChainForAlgorithm, IsValidChainForAlgorithm)
{
const ChainValidity& chainValidity(GetParam());
const char* rootCN = "CN=Root";
ByteString rootSubjectDER;
ByteString rootEncoded(
CreateCert(rootCN, rootCN, EndEntityOrCA::MustBeCA,
chainValidity.rootSignatureAlgorithm, rootSubjectDER));
EXPECT_FALSE(ENCODING_FAILED(rootEncoded));
EXPECT_FALSE(ENCODING_FAILED(rootSubjectDER));
const char* issuerCN = rootCN;
const char* intermediateCN = "CN=Intermediate";
ByteString intermediateSubjectDER;
ByteString intermediateEncoded;
// If the the algorithmIdentifier is empty, then it's NO_INTERMEDIATE.
if (!chainValidity.optionalIntermediateSignatureAlgorithm
.algorithmIdentifier.empty()) {
intermediateEncoded =
CreateCert(rootCN, intermediateCN, EndEntityOrCA::MustBeCA,
chainValidity.optionalIntermediateSignatureAlgorithm,
intermediateSubjectDER);
EXPECT_FALSE(ENCODING_FAILED(intermediateEncoded));
EXPECT_FALSE(ENCODING_FAILED(intermediateSubjectDER));
issuerCN = intermediateCN;
}
AlgorithmTestsTrustDomain trustDomain(rootEncoded, rootSubjectDER,
intermediateEncoded,
intermediateSubjectDER);
const char* endEntityCN = "CN=End Entity";
ByteString endEntitySubjectDER;
ByteString endEntityEncoded(
CreateCert(issuerCN, endEntityCN, EndEntityOrCA::MustBeEndEntity,
chainValidity.endEntitySignatureAlgorithm,
endEntitySubjectDER));
EXPECT_FALSE(ENCODING_FAILED(endEntityEncoded));
EXPECT_FALSE(ENCODING_FAILED(endEntitySubjectDER));
Input endEntity;
ASSERT_EQ(Success, endEntity.Init(endEntityEncoded.data(),
endEntityEncoded.length()));
Result expectedResult = chainValidity.isValid
? Success
: Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
ASSERT_EQ(expectedResult,
BuildCertChain(trustDomain, endEntity, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::id_kp_serverAuth,
CertPolicyId::anyPolicy, nullptr));
}
INSTANTIATE_TEST_CASE_P(pkixcert_IsValidChainForAlgorithm,
pkixcert_IsValidChainForAlgorithm,
testing::ValuesIn(CHAIN_VALIDITY));

View file

@ -0,0 +1,722 @@
/* -*- 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 2016 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"
#include "mozpkix/pkixder.h"
#include "mozpkix/pkixutil.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckExtendedKeyUsage(EndEntityOrCA endEntityOrCA,
const Input* encodedExtendedKeyUsage,
KeyPurposeId requiredEKU,
TrustDomain& trustDomain, Time notBefore);
} } // namespace mozilla::pkix
class pkixcheck_CheckExtendedKeyUsage : public ::testing::Test
{
protected:
DefaultCryptoTrustDomain mTrustDomain;
};
#define ASSERT_BAD(x) ASSERT_EQ(Result::ERROR_INADEQUATE_CERT_TYPE, x)
// tlv_id_kp_OCSPSigning and tlv_id_kp_serverAuth are defined in pkixtestutil.h
// python DottedOIDToCode.py --tlv id-kp-clientAuth 1.3.6.1.5.5.7.3.2
static const uint8_t tlv_id_kp_clientAuth[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x02
};
// python DottedOIDToCode.py --tlv id-kp-codeSigning 1.3.6.1.5.5.7.3.3
static const uint8_t tlv_id_kp_codeSigning[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x03
};
// python DottedOIDToCode.py --tlv id_kp_emailProtection 1.3.6.1.5.5.7.3.4
static const uint8_t tlv_id_kp_emailProtection[] = {
0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x04
};
// python DottedOIDToCode.py --tlv id-Netscape-stepUp 2.16.840.1.113730.4.1
static const uint8_t tlv_id_Netscape_stepUp[] = {
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xf8, 0x42, 0x04, 0x01
};
// python DottedOIDToCode.py --tlv unknownOID 1.3.6.1.4.1.13769.666.666.666.1.500.9.3
static const uint8_t tlv_unknownOID[] = {
0x06, 0x12, 0x2b, 0x06, 0x01, 0x04, 0x01, 0xeb, 0x49, 0x85, 0x1a, 0x85, 0x1a,
0x85, 0x1a, 0x01, 0x83, 0x74, 0x09, 0x03
};
// python DottedOIDToCode.py --tlv anyExtendedKeyUsage 2.5.29.37.0
static const uint8_t tlv_anyExtendedKeyUsage[] = {
0x06, 0x04, 0x55, 0x1d, 0x25, 0x00
};
TEST_F(pkixcheck_CheckExtendedKeyUsage, none)
{
// The input Input is nullptr. This means the cert had no extended key usage
// extension. This is always valid except for when the certificate is an
// end-entity and the required usage is id-kp-OCSPSigning.
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::anyExtendedKeyUsage,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::anyExtendedKeyUsage,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_clientAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_clientAuth,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_codeSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_codeSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity,
nullptr,
KeyPurposeId::id_kp_emailProtection,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_emailProtection,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyPurposeId::id_kp_OCSPSigning,
mTrustDomain, Now()));
ASSERT_EQ(Success, CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyPurposeId::id_kp_OCSPSigning,
mTrustDomain, Now()));
}
static const Input empty_null;
TEST_F(pkixcheck_CheckExtendedKeyUsage, empty)
{
// The input Input is empty. The cert has an empty extended key usage
// extension, which is syntactically invalid.
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
ASSERT_BAD(CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
KeyPurposeId::id_kp_serverAuth,
mTrustDomain, Now()));
}
struct EKUTestcase
{
ByteString ekuSEQUENCE;
KeyPurposeId keyPurposeId;
Result expectedResultEndEntity;
Result expectedResultCA;
};
::std::ostream& operator<<(::std::ostream& os, const EKUTestcase&)
{
return os << "TODO (bug 1318770)";
}
class CheckExtendedKeyUsageTest
: public ::testing::Test
, public ::testing::WithParamInterface<EKUTestcase>
{
protected:
DefaultCryptoTrustDomain mTrustDomain;
};
TEST_P(CheckExtendedKeyUsageTest, EKUTestcase)
{
const EKUTestcase& param(GetParam());
Input encodedEKU;
ASSERT_EQ(Success, encodedEKU.Init(param.ekuSEQUENCE.data(),
param.ekuSEQUENCE.length()));
ASSERT_EQ(param.expectedResultEndEntity,
CheckExtendedKeyUsage(EndEntityOrCA::MustBeEndEntity, &encodedEKU,
param.keyPurposeId,
mTrustDomain, Now()));
ASSERT_EQ(param.expectedResultCA,
CheckExtendedKeyUsage(EndEntityOrCA::MustBeCA, &encodedEKU,
param.keyPurposeId,
mTrustDomain, Now()));
}
#define SINGLE_EKU_SUCCESS(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Success, Success }
#define SINGLE_EKU_SUCCESS_CA(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Success }
#define SINGLE_EKU_FAILURE(oidBytes, keyPurposeId) \
{ TLV(der::SEQUENCE, BytesToByteString(oidBytes)), keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Result::ERROR_INADEQUATE_CERT_TYPE }
#define DOUBLE_EKU_SUCCESS(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Success, Success }
#define DOUBLE_EKU_SUCCESS_CA(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Success }
#define DOUBLE_EKU_FAILURE(oidBytes1, oidBytes2, keyPurposeId) \
{ TLV(der::SEQUENCE, \
BytesToByteString(oidBytes1) + BytesToByteString(oidBytes2)), \
keyPurposeId, \
Result::ERROR_INADEQUATE_CERT_TYPE, Result::ERROR_INADEQUATE_CERT_TYPE }
static const EKUTestcase EKU_TESTCASES[] =
{
SINGLE_EKU_SUCCESS(tlv_id_kp_serverAuth, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_SUCCESS(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_serverAuth, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_clientAuth, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_SUCCESS(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_clientAuth, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_SUCCESS(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_SUCCESS(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
// For end-entities, if id-kp-OCSPSigning is present, no usage is allowed
// except OCSPSigning.
SINGLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
// For compatibility, id-Netscape-stepUp is treated as equivalent to
// id-kp-serverAuth for CAs.
SINGLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
SINGLE_EKU_SUCCESS(tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
SINGLE_EKU_FAILURE(tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_clientAuth, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_serverAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_codeSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_clientAuth, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_emailProtection, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_SUCCESS(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_codeSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_kp_OCSPSigning, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_SUCCESS(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_kp_emailProtection, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_id_Netscape_stepUp, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS_CA(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_SUCCESS(tlv_id_kp_OCSPSigning, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_unknownOID, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_SUCCESS_CA(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_id_Netscape_stepUp, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
DOUBLE_EKU_SUCCESS(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::anyExtendedKeyUsage),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_serverAuth),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_clientAuth),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_codeSigning),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_emailProtection),
DOUBLE_EKU_FAILURE(tlv_unknownOID, tlv_anyExtendedKeyUsage, KeyPurposeId::id_kp_OCSPSigning),
};
INSTANTIATE_TEST_CASE_P(pkixcheck_CheckExtendedKeyUsage,
CheckExtendedKeyUsageTest,
::testing::ValuesIn(EKU_TESTCASES));
struct EKUChainTestcase
{
ByteString ekuExtensionEE;
ByteString ekuExtensionCA;
KeyPurposeId keyPurposeId;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os, const EKUChainTestcase&)
{
return os << "TODO (bug 1318770)";
}
class CheckExtendedKeyUsageChainTest
: public ::testing::Test
, public ::testing::WithParamInterface<EKUChainTestcase>
{
};
static ByteString
CreateCert(const char* issuerCN, const char* subjectCN,
EndEntityOrCA endEntityOrCA, ByteString encodedEKU)
{
static long serialNumberValue = 0;
++serialNumberValue;
ByteString serialNumber(CreateEncodedSerialNumber(serialNumberValue));
EXPECT_FALSE(ENCODING_FAILED(serialNumber));
ByteString issuerDER(CNToDERName(issuerCN));
ByteString subjectDER(CNToDERName(subjectCN));
ByteString extensions[3];
extensions[0] =
CreateEncodedBasicConstraints(endEntityOrCA == EndEntityOrCA::MustBeCA,
nullptr, Critical::Yes);
EXPECT_FALSE(ENCODING_FAILED(extensions[0]));
if (encodedEKU.length() > 0) {
extensions[1] = encodedEKU;
}
ScopedTestKeyPair reusedKey(CloneReusedKeyPair());
ByteString certDER(CreateEncodedCertificate(
v3, sha256WithRSAEncryption(), serialNumber, issuerDER,
oneDayBeforeNow, oneDayAfterNow, subjectDER,
*reusedKey, extensions, *reusedKey,
sha256WithRSAEncryption()));
EXPECT_FALSE(ENCODING_FAILED(certDER));
return certDER;
}
class EKUTrustDomain final : public DefaultCryptoTrustDomain
{
public:
explicit EKUTrustDomain(ByteString issuerCertDER)
: mIssuerCertDER(issuerCertDER)
{
}
private:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input candidateCert,
TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(candidateCert, mIssuerCertDER)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
Input derCert;
Result rv = derCert.Init(mIssuerCertDER.data(), mIssuerCertDER.length());
if (rv != Success) {
return rv;
}
bool keepGoing;
return checker.Check(derCert, nullptr, keepGoing);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
const Input*, const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString mIssuerCertDER;
};
TEST_P(CheckExtendedKeyUsageChainTest, EKUChainTestcase)
{
const EKUChainTestcase& param(GetParam());
ByteString issuerCertDER(CreateCert("CA", "CA", EndEntityOrCA::MustBeCA,
param.ekuExtensionCA));
ByteString subjectCertDER(CreateCert("CA", "EE",
EndEntityOrCA::MustBeEndEntity,
param.ekuExtensionEE));
EKUTrustDomain trustDomain(issuerCertDER);
Input subjectCertDERInput;
ASSERT_EQ(Success, subjectCertDERInput.Init(subjectCertDER.data(),
subjectCertDER.length()));
ASSERT_EQ(param.expectedResult,
BuildCertChain(trustDomain, subjectCertDERInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
param.keyPurposeId,
CertPolicyId::anyPolicy,
nullptr));
}
// python DottedOIDToCode.py --tlv id-ce-extKeyUsage 2.5.29.37
static const uint8_t tlv_id_ce_extKeyUsage[] = {
0x06, 0x03, 0x55, 0x1d, 0x25
};
static inline ByteString
CreateEKUExtension(ByteString ekuOIDs)
{
return TLV(der::SEQUENCE,
BytesToByteString(tlv_id_ce_extKeyUsage) +
TLV(der::OCTET_STRING, TLV(der::SEQUENCE, ekuOIDs)));
}
static const EKUChainTestcase EKU_CHAIN_TESTCASES[] =
{
{
// Both end-entity and CA have id-kp-serverAuth => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// CA has no EKU extension => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has no EKU extension => should succeed
ByteString(),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// No EKU extensions at all => should succeed
ByteString(),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// CA has EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity has EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// Both end-entity and CA have EKU without id-kp-serverAuth => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity has no EKU, CA doesn't have id-kp-serverAuth => should fail
ByteString(),
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity doesn't have id-kp-serverAuth, CA has no EKU => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_clientAuth)),
ByteString(),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// CA has id-Netscape-stepUp => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_Netscape_stepUp)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has id-Netscape-stepUp => should fail
CreateEKUExtension(BytesToByteString(tlv_id_Netscape_stepUp)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// End-entity and CA have id-kp-serverAuth and id-kp-clientAuth => should
// succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Success
},
{
// End-entity has id-kp-serverAuth and id-kp-OCSPSigning => should fail
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_OCSPSigning)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
KeyPurposeId::id_kp_serverAuth,
Result::ERROR_INADEQUATE_CERT_TYPE
},
{
// CA has id-kp-serverAuth and id-kp-OCSPSigning => should succeed
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_clientAuth)),
CreateEKUExtension(BytesToByteString(tlv_id_kp_serverAuth) +
BytesToByteString(tlv_id_kp_OCSPSigning)),
KeyPurposeId::id_kp_serverAuth,
Success
},
};
INSTANTIATE_TEST_CASE_P(pkixcheck_CheckExtendedKeyUsage,
CheckExtendedKeyUsageChainTest,
::testing::ValuesIn(EKU_CHAIN_TESTCASES));

View file

@ -0,0 +1,63 @@
/* -*- 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 2016 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"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
class pkixcheck_CheckIssuer : public ::testing::Test { };
static const uint8_t EMPTY_NAME_DATA[] = {
0x30, 0x00 /* tag, length */
};
static const Input EMPTY_NAME(EMPTY_NAME_DATA);
static const uint8_t VALID_NAME_DATA[] = {
/* From https://www.example.com/: C=US, O=DigiCert Inc, OU=www.digicert.com,
* CN=DigiCert SHA2 High Assurance Server CA */
0x30, 0x70, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
0x02, 0x55, 0x53, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0A,
0x13, 0x0C, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74, 0x20, 0x49,
0x6E, 0x63, 0x31, 0x19, 0x30, 0x17, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13,
0x10, 0x77, 0x77, 0x77, 0x2E, 0x64, 0x69, 0x67, 0x69, 0x63, 0x65, 0x72,
0x74, 0x2E, 0x63, 0x6F, 0x6D, 0x31, 0x2F, 0x30, 0x2D, 0x06, 0x03, 0x55,
0x04, 0x03, 0x13, 0x26, 0x44, 0x69, 0x67, 0x69, 0x43, 0x65, 0x72, 0x74,
0x20, 0x53, 0x48, 0x41, 0x32, 0x20, 0x48, 0x69, 0x67, 0x68, 0x20, 0x41,
0x73, 0x73, 0x75, 0x72, 0x61, 0x6E, 0x63, 0x65, 0x20, 0x53, 0x65, 0x72,
0x76, 0x65, 0x72, 0x20, 0x43, 0x41
};
static const Input VALID_NAME(VALID_NAME_DATA);
TEST_F(pkixcheck_CheckIssuer, ValidIssuer)
{
ASSERT_EQ(Success, CheckIssuer(VALID_NAME));
}
TEST_F(pkixcheck_CheckIssuer, EmptyIssuer)
{
ASSERT_EQ(Result::ERROR_EMPTY_ISSUER_NAME, CheckIssuer(EMPTY_NAME));
}

View file

@ -0,0 +1,284 @@
/* -*- 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 Input* encodedKeyUsage,
KeyUsage requiredKeyUsageIfPresent);
} } // namespace mozilla::pkix
class pkixcheck_CheckKeyUsage : public ::testing::Test { };
#define ASSERT_BAD(x) ASSERT_EQ(Result::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 Input name(name##_bytes);
static const Input empty_null;
// Note that keyCertSign is really the only interesting case for CA
// certificates since we don't support cRLSign.
TEST_F(pkixcheck_CheckKeyUsage, EE_none)
{
// The input Input 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_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::noParticularKeyUsageRequired));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::digitalSignature));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::nonRepudiation));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::keyEncipherment));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::dataEncipherment));
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, nullptr,
KeyUsage::keyAgreement));
}
TEST_F(pkixcheck_CheckKeyUsage, EE_empty)
{
// The input Input is empty. The cert had an empty keyUsage extension,
// which is syntactically invalid.
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_null,
KeyUsage::digitalSignature));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &empty_nonnull,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, CA_none)
{
// A CA certificate does not have a KU extension.
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeCA, nullptr,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, CA_empty)
{
// A CA certificate has an empty KU extension.
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_null,
KeyUsage::keyCertSign));
static const uint8_t dummy = 0x00;
Input empty_nonnull;
ASSERT_EQ(Success, empty_nonnull.Init(&dummy, 0));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &empty_nonnull,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, maxUnusedBits)
{
NAMED_SIMPLE_KU(encoded, 7, 0x80);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &encoded,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, tooManyUnusedBits)
{
static uint8_t oneValueByteData[] = {
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 8/*unused bits*/, 0x80
};
static const Input oneValueByte(oneValueByteData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &oneValueByte,
KeyUsage::digitalSignature));
static uint8_t twoValueBytesData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, 8/*unused bits*/, 0x01, 0x00
};
static const Input twoValueBytes(twoValueBytesData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
KeyUsage::digitalSignature));
}
TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_NoPaddingBits)
{
static const uint8_t DER_BYTES[] = {
0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 0/*unused bits*/
};
static const Input DER(DER_BYTES);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, NoValueBytes_7PaddingBits)
{
static const uint8_t DER_BYTES[] = {
0x03/*BIT STRING*/, 0x01/*LENGTH=1*/, 7/*unused bits*/
};
static const Input DER(DER_BYTES);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &DER,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &DER,
KeyUsage::keyCertSign));
}
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_EQ(Success,
CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good, usage));
ASSERT_EQ(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.
const uint8_t twoByteNotGoodData[] = {
0x03/*BIT STRING*/, 0x03/*LENGTH=3*/, unusedBits,
static_cast<uint8_t>(~bits),
static_cast<uint8_t>((0xFFu >> unusedBits) << unusedBits)
};
Input twoByteNotGood(twoByteNotGoodData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
usage));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood, usage));
}
TEST_F(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.
// End-entity certs may assert it along with other key usages if keyCertSign
// isn't the required key usage. This is for compatibility.
TEST_F(pkixcheck_CheckKeyUsage, keyCertSign)
{
NAMED_SIMPLE_KU(good, 2, 0x04);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &good,
KeyUsage::keyCertSign));
ASSERT_EQ(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 Input twoByteNotGood(twoByteNotGoodData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoByteNotGood,
KeyUsage::keyCertSign));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoByteNotGood,
KeyUsage::keyCertSign));
// If an end-entity certificate does assert keyCertSign, this is allowed
// as long as that isn't the required key usage.
NAMED_SIMPLE_KU(digitalSignatureAndKeyCertSign, 2, 0x84);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&digitalSignatureAndKeyCertSign,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&digitalSignatureAndKeyCertSign,
KeyUsage::keyCertSign));
}
TEST_F(pkixcheck_CheckKeyUsage, unusedBitNotZero)
{
// single byte control case
static uint8_t controlOneValueByteData[] = {
0x03/*BIT STRING*/, 0x02/*LENGTH=2*/, 7/*unused bits*/, 0x80
};
static const Input controlOneValueByte(controlOneValueByteData);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&controlOneValueByte,
KeyUsage::digitalSignature));
ASSERT_EQ(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
};
static const Input oneValueByte(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
};
static const Input controlTwoValueBytes(controlTwoValueBytesData);
ASSERT_EQ(Success, CheckKeyUsage(EndEntityOrCA::MustBeEndEntity,
&controlTwoValueBytes,
KeyUsage::digitalSignature));
ASSERT_EQ(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
};
static const Input twoValueBytes(twoValueBytesData);
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeEndEntity, &twoValueBytes,
KeyUsage::digitalSignature));
ASSERT_BAD(CheckKeyUsage(EndEntityOrCA::MustBeCA, &twoValueBytes,
KeyUsage::digitalSignature));
}

View file

@ -0,0 +1,367 @@
/* -*- 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 2015 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"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result CheckSignatureAlgorithm(
TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA,
Time notBefore,
const der::SignedDataWithSignature& signedData,
Input signatureValue);
} } // namespace mozilla::pkix
struct CheckSignatureAlgorithmTestParams
{
ByteString signatureAlgorithmValue;
ByteString signatureValue;
unsigned int signatureLengthInBytes;
Result expectedResult;
};
::std::ostream& operator<<(::std::ostream& os,
const CheckSignatureAlgorithmTestParams&)
{
return os << "TODO (bug 1318770)";
}
#define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s))
// python DottedOIDToCode.py --tlv sha256WithRSAEncryption 1.2.840.113549.1.1.11
static const uint8_t tlv_sha256WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
// Same as tlv_sha256WithRSAEncryption, except one without the "0x0b" and with
// the DER length decreased accordingly.
static const uint8_t tlv_sha256WithRSAEncryption_truncated[] = {
0x06, 0x08, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01
};
// python DottedOIDToCode.py --tlv sha-1WithRSAEncryption 1.2.840.113549.1.1.5
static const uint8_t tlv_sha_1WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05
};
// python DottedOIDToCode.py --tlv sha1WithRSASignature 1.3.14.3.2.29
static const uint8_t tlv_sha1WithRSASignature[] = {
0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1d
};
// python DottedOIDToCode.py --tlv md5WithRSAEncryption 1.2.840.113549.1.1.4
static const uint8_t tlv_md5WithRSAEncryption[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04
};
static const CheckSignatureAlgorithmTestParams
CHECKSIGNATUREALGORITHM_TEST_PARAMS[] =
{
{ // Both algorithm IDs are empty
ByteString(),
ByteString(),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // signatureAlgorithm is empty, signature is supported.
ByteString(),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // signatureAlgorithm is supported, signature is empty.
BS(tlv_sha256WithRSAEncryption),
ByteString(),
2048 / 8,
Result::ERROR_BAD_DER,
},
{ // Algorithms match, both are supported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Success
},
{ // Algorithms do not match because signatureAlgorithm is truncated.
BS(tlv_sha256WithRSAEncryption_truncated),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Algorithms do not match because signature is truncated.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption_truncated),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Algorithms do not match, both are supported.
BS(tlv_sha_1WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
},
{ // Algorithms do not match, both are supported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha_1WithRSAEncryption),
2048 / 8,
Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH,
},
{ // Algorithms match, both are unsupported.
BS(tlv_md5WithRSAEncryption),
BS(tlv_md5WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // signatureAlgorithm is unsupported, signature is supported.
BS(tlv_md5WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // signatureAlgorithm is supported, signature is unsupported.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_md5WithRSAEncryption),
2048 / 8,
Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
},
{ // Both have the optional NULL parameter.
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
2048 / 8,
Success
},
{ // signatureAlgorithm has the optional NULL parameter, signature doesn't.
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
BS(tlv_sha256WithRSAEncryption),
2048 / 8,
Success
},
{ // signatureAlgorithm does not have the optional NULL parameter, signature
// does.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption) + TLV(der::NULLTag, ByteString()),
2048 / 8,
Success
},
{ // The different OIDs for RSA-with-SHA1 we support are semantically
// equivalent.
BS(tlv_sha1WithRSASignature),
BS(tlv_sha_1WithRSAEncryption),
2048 / 8,
Success,
},
{ // The different OIDs for RSA-with-SHA1 we support are semantically
// equivalent (opposite order).
BS(tlv_sha_1WithRSAEncryption),
BS(tlv_sha1WithRSASignature),
2048 / 8,
Success,
},
{ // Algorithms match, both are supported, key size is not a multile of 128
// bits. This test verifies that we're not wrongly rounding up the
// signature size like we did in the original patch for bug 1131767.
BS(tlv_sha256WithRSAEncryption),
BS(tlv_sha256WithRSAEncryption),
(2048 / 8) - 1,
Success
},
};
class pkixcheck_CheckSignatureAlgorithm
: public ::testing::Test
, public ::testing::WithParamInterface<CheckSignatureAlgorithmTestParams>
{
};
class pkixcheck_CheckSignatureAlgorithm_TrustDomain final
: public EverythingFailsByDefaultTrustDomain
{
public:
explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain(
unsigned int aPublicKeySizeInBits)
: publicKeySizeInBits(aPublicKeySizeInBits)
, checkedDigestAlgorithm(false)
, checkedModulusSizeInBits(false)
{
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time)
override
{
checkedDigestAlgorithm = true;
return Success;
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA,
unsigned int modulusSizeInBits)
override
{
EXPECT_EQ(EndEntityOrCA::MustBeEndEntity, endEntityOrCA);
EXPECT_EQ(publicKeySizeInBits, modulusSizeInBits);
checkedModulusSizeInBits = true;
return Success;
}
const unsigned int publicKeySizeInBits;
bool checkedDigestAlgorithm;
bool checkedModulusSizeInBits;
};
TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm)
{
const Time now(Now());
const CheckSignatureAlgorithmTestParams& params(GetParam());
Input signatureValueInput;
ASSERT_EQ(Success,
signatureValueInput.Init(params.signatureValue.data(),
params.signatureValue.length()));
pkixcheck_CheckSignatureAlgorithm_TrustDomain
trustDomain(params.signatureLengthInBytes * 8);
der::SignedDataWithSignature signedData;
ASSERT_EQ(Success,
signedData.algorithm.Init(params.signatureAlgorithmValue.data(),
params.signatureAlgorithmValue.length()));
ByteString dummySignature(params.signatureLengthInBytes, 0xDE);
ASSERT_EQ(Success,
signedData.signature.Init(dummySignature.data(),
dummySignature.length()));
ASSERT_EQ(params.expectedResult,
CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity,
now, signedData, signatureValueInput));
ASSERT_EQ(params.expectedResult == Success,
trustDomain.checkedDigestAlgorithm);
ASSERT_EQ(params.expectedResult == Success,
trustDomain.checkedModulusSizeInBits);
}
INSTANTIATE_TEST_CASE_P(
pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm,
testing::ValuesIn(CHECKSIGNATUREALGORITHM_TEST_PARAMS));
class pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
: public DefaultCryptoTrustDomain
{
public:
explicit pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain(
const ByteString& aIssuer)
: issuer(aIssuer)
{
}
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&,
Input cert, /*out*/ TrustLevel& trustLevel) override
{
trustLevel = InputEqualsByteString(cert, issuer)
? TrustLevel::TrustAnchor
: TrustLevel::InheritsTrust;
return Success;
}
Result FindIssuer(Input, IssuerChecker& checker, Time) override
{
EXPECT_FALSE(ENCODING_FAILED(issuer));
Input issuerInput;
EXPECT_EQ(Success, issuerInput.Init(issuer.data(), issuer.length()));
bool keepGoing;
EXPECT_EQ(Success, checker.Check(issuerInput, nullptr, keepGoing));
EXPECT_FALSE(keepGoing);
return Success;
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override
{
return Success;
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override
{
return Success;
}
ByteString issuer;
};
// Test that CheckSignatureAlgorithm actually gets called at some point when
// BuildCertChain is called.
TEST_F(pkixcheck_CheckSignatureAlgorithm, BuildCertChain)
{
ScopedTestKeyPair keyPair(CloneReusedKeyPair());
ASSERT_TRUE(keyPair.get());
ByteString issuerExtensions[2];
issuerExtensions[0] = CreateEncodedBasicConstraints(true, nullptr,
Critical::No);
ASSERT_FALSE(ENCODING_FAILED(issuerExtensions[0]));
ByteString issuer(CreateEncodedCertificate(3,
sha256WithRSAEncryption(),
CreateEncodedSerialNumber(1),
CNToDERName("issuer"),
oneDayBeforeNow, oneDayAfterNow,
CNToDERName("issuer"),
*keyPair,
issuerExtensions,
*keyPair,
sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(issuer));
ByteString subject(CreateEncodedCertificate(3,
sha1WithRSAEncryption(),
CreateEncodedSerialNumber(2),
CNToDERName("issuer"),
oneDayBeforeNow, oneDayAfterNow,
CNToDERName("subject"),
*keyPair,
nullptr,
*keyPair,
sha256WithRSAEncryption()));
ASSERT_FALSE(ENCODING_FAILED(subject));
Input subjectInput;
ASSERT_EQ(Success, subjectInput.Init(subject.data(), subject.length()));
pkixcheck_CheckSignatureAlgorithm_BuildCertChain_TrustDomain
trustDomain(issuer);
Result rv = BuildCertChain(trustDomain, subjectInput, Now(),
EndEntityOrCA::MustBeEndEntity,
KeyUsage::noParticularKeyUsageRequired,
KeyPurposeId::anyExtendedKeyUsage,
CertPolicyId::anyPolicy,
nullptr);
ASSERT_EQ(Result::ERROR_SIGNATURE_ALGORITHM_MISMATCH, rv);
}

View file

@ -0,0 +1,128 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pkixgtest.h"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
static const Time PAST_TIME(YMDHMS(1998, 12, 31, 12, 23, 56));
#define OLDER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'1', '9', '9', '9', '0', '1', '0', '1', /* 1999-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define OLDER_UTCTIME \
0x17, 13, /* tag, length */ \
'9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time NOW(YMDHMS(2016, 12, 31, 12, 23, 56));
#define NEWER_GENERALIZEDTIME \
0x18, 15, /* tag, length */ \
'2', '0', '2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define NEWER_UTCTIME \
0x17, 13, /* tag, length */ \
'2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_CheckValidity : public ::testing::Test { };
static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = {
OLDER_UTCTIME,
NEWER_UTCTIME,
};
static const Input
OLDER_UTCTIME_NEWER_UTCTIME(OLDER_UTCTIME_NEWER_UTCTIME_DATA);
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_GENERALIZEDTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME)
{
static const uint8_t DER[] = {
OLDER_GENERALIZEDTIME,
NEWER_UTCTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME)
{
static const uint8_t DER[] = {
OLDER_UTCTIME,
NEWER_GENERALIZEDTIME,
};
static const Input validity(DER);
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Result::ERROR_NOT_YET_VALID_CERTIFICATE, CheckValidity(PAST_TIME, notBefore, notAfter));
}
TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter)
{
static Time notBefore(Time::uninitialized);
static Time notAfter(Time::uninitialized);
ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(FUTURE_TIME, notBefore, notAfter));
}

View file

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "pkixgtest.h"
#include "mozpkix/pkixcheck.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
#define OLDER_UTCTIME \
0x17, 13, /* tag, length */ \
'9', '9', '0', '1', '0', '1', /* (19)99-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
#define NEWER_UTCTIME \
0x17, 13, /* tag, length */ \
'2', '1', '0', '1', '0', '1', /* 2021-01-01 */ \
'0', '0', '0', '0', '0', '0', 'Z' /* 00:00:00Z */
static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
class pkixcheck_ParseValidity : public ::testing::Test { };
TEST_F(pkixcheck_ParseValidity, BothEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0/*length*/,
0x17/*UTCTime*/, 0/*length*/,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, NotBeforeEmptyNull)
{
static const uint8_t DER[] = {
0x17/*UTCTime*/, 0x00/*length*/,
NEWER_UTCTIME
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, NotAfterEmptyNull)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
0x17/*UTCTime*/, 0x00/*length*/,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}
TEST_F(pkixcheck_ParseValidity, InvalidNotAfterBeforeNotBefore)
{
static const uint8_t DER[] = {
NEWER_UTCTIME,
OLDER_UTCTIME,
};
static const Input validity(DER);
ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
}

View file

@ -0,0 +1,120 @@
/* -*- 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 2015 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"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
namespace mozilla { namespace pkix {
extern Result TLSFeaturesSatisfiedInternal(const Input* requiredTLSFeatures,
const Input* stapledOCSPResponse);
} } // namespace mozilla::pkix
struct TLSFeaturesTestParams
{
ByteString requiredTLSFeatures;
Result expectedResultWithResponse;
Result expectedResultWithoutResponse;
};
::std::ostream& operator<<(::std::ostream& os, const TLSFeaturesTestParams&)
{
return os << "TODO (bug 1318770)";
}
#define BS(s) ByteString(s, MOZILLA_PKIX_ARRAY_LENGTH(s))
static const uint8_t statusRequest[] = {
0x30, 0x03, 0x02, 0x01, 0x05
};
static const uint8_t unknown[] = {
0x30, 0x03, 0x02, 0x01, 0x06
};
static const uint8_t statusRequestAndUnknown[] = {
0x30, 0x06, 0x02, 0x01, 0x05, 0x02, 0x01, 0x06
};
static const uint8_t duplicateStatusRequest[] = {
0x30, 0x06, 0x02, 0x01, 0x05, 0x02, 0x01, 0x05
};
static const uint8_t twoByteUnknown[] = {
0x30, 0x04, 0x02, 0x02, 0x05, 0x05
};
static const uint8_t zeroByteInteger[] = {
0x30, 0x02, 0x02, 0x00
};
static const TLSFeaturesTestParams
TLSFEATURESSATISFIED_TEST_PARAMS[] =
{
// some tests with checks enforced
{ ByteString(), Result::ERROR_BAD_DER, Result::ERROR_BAD_DER },
{ BS(statusRequest), Success, Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(unknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(statusRequestAndUnknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(duplicateStatusRequest), Success,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(twoByteUnknown), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
{ BS(zeroByteInteger), Result::ERROR_REQUIRED_TLS_FEATURE_MISSING,
Result::ERROR_REQUIRED_TLS_FEATURE_MISSING },
};
class pkixcheck_TLSFeaturesSatisfiedInternal
: public ::testing::Test
, public ::testing::WithParamInterface<TLSFeaturesTestParams>
{
};
TEST_P(pkixcheck_TLSFeaturesSatisfiedInternal, TLSFeaturesSatisfiedInternal) {
const TLSFeaturesTestParams& params(GetParam());
Input featuresInput;
ASSERT_EQ(Success, featuresInput.Init(params.requiredTLSFeatures.data(),
params.requiredTLSFeatures.length()));
Input responseInput;
// just create an input with any data in it
ByteString stapledOCSPResponse = BS(statusRequest);
ASSERT_EQ(Success, responseInput.Init(stapledOCSPResponse.data(),
stapledOCSPResponse.length()));
// first we omit the response
ASSERT_EQ(params.expectedResultWithoutResponse,
TLSFeaturesSatisfiedInternal(&featuresInput, nullptr));
// then we try again with the response
ASSERT_EQ(params.expectedResultWithResponse,
TLSFeaturesSatisfiedInternal(&featuresInput, &responseInput));
}
INSTANTIATE_TEST_CASE_P(
pkixcheck_TLSFeaturesSatisfiedInternal,
pkixcheck_TLSFeaturesSatisfiedInternal,
testing::ValuesIn(TLSFEATURESSATISFIED_TEST_PARAMS));

View file

@ -0,0 +1,920 @@
/* -*- 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 <functional>
#include <vector>
#include "pkixgtest.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::der;
namespace {
class pkixder_input_tests : public ::testing::Test { };
static const uint8_t DER_SEQUENCE_EMPTY[] = {
0x30, // SEQUENCE
0x00, // length
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY[] = {
0x30, // SEQUENCE
0x01, // length
'X', // value
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE[] = {
'X', // value
};
static const uint8_t DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED[] = {
0x30, // SEQUENCE
0x01, // length
};
const uint8_t DER_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02, // INTEGER length 1 value 0x02
0x02, 0x01, 0x03 // INTEGER length 1 value 0x03
};
const uint8_t DER_TRUNCATED_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02 // INTEGER length 1 value 0x02
// MISSING DATA HERE ON PURPOSE
};
const uint8_t DER_OVERRUN_SEQUENCE_OF_INT8[] = {
0x30, // SEQUENCE
0x09, // length
0x02, 0x01, 0x01, // INTEGER length 1 value 0x01
0x02, 0x01, 0x02, // INTEGER length 1 value 0x02
0x02, 0x02, 0xFF, 0x03 // INTEGER length 2 value 0xFF03
};
const uint8_t DER_INT16[] = {
0x02, // INTEGER
0x02, // length
0x12, 0x34 // 0x1234
};
static const Input EMPTY_INPUT;
TEST_F(pkixder_input_tests, InputInit)
{
Input buf;
ASSERT_EQ(Success,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}
TEST_F(pkixder_input_tests, InputInitWithNullPointerOrZeroLength)
{
Input buf;
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 0));
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init(nullptr, 100));
// Though it seems odd to initialize with zero-length and non-null ptr, this
// is working as intended. The Reader class was intended to protect against
// buffer overflows, and there's no risk with the current behavior. See bug
// 1000354.
ASSERT_EQ(Success, buf.Init((const uint8_t*) "hello", 0));
ASSERT_TRUE(buf.GetLength() == 0);
}
TEST_F(pkixder_input_tests, InputInitWithLargeData)
{
Input buf;
// Data argument length does not matter, it is not touched, just
// needs to be non-null
ASSERT_EQ(Result::ERROR_BAD_DER, buf.Init((const uint8_t*) "", 0xffff+1));
ASSERT_EQ(Success, buf.Init((const uint8_t*) "", 0xffff));
}
TEST_F(pkixder_input_tests, InputInitMultipleTimes)
{
Input buf;
ASSERT_EQ(Success,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS,
buf.Init(DER_SEQUENCE_OF_INT8, sizeof DER_SEQUENCE_OF_INT8));
}
TEST_F(pkixder_input_tests, PeekWithinBounds)
{
const uint8_t der[] = { 0x11, 0x11 };
Input buf(der);
Reader input(buf);
ASSERT_TRUE(input.Peek(0x11));
ASSERT_FALSE(input.Peek(0x22));
}
TEST_F(pkixder_input_tests, PeekPastBounds)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint8_t readByte;
ASSERT_EQ(Success, input.Read(readByte));
ASSERT_EQ(0x11, readByte);
ASSERT_FALSE(input.Peek(0x22));
}
TEST_F(pkixder_input_tests, ReadByte)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf(der);
Reader input(buf);
uint8_t readByte1;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x11, readByte1);
uint8_t readByte2;
ASSERT_EQ(Success, input.Read(readByte2));
ASSERT_EQ(0x22, readByte2);
}
TEST_F(pkixder_input_tests, ReadBytePastEnd)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x11, readByte1);
uint8_t readByte2 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readByte2));
ASSERT_NE(0x22, readByte2);
}
TEST_F(pkixder_input_tests, ReadByteWrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// a null pointer is undefined behavior according to the C++ language spec.,
// but this should catch the problem on at least some compilers, if not all of
// them.
const uint8_t* der = nullptr;
--der;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
uint8_t b;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}
TEST_F(pkixder_input_tests, ReadWord)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Success, input.Read(readWord1));
ASSERT_EQ(0x1122, readWord1);
uint16_t readWord2 = 0;
ASSERT_EQ(Success, input.Read(readWord2));
ASSERT_EQ(0x3344, readWord2);
}
TEST_F(pkixder_input_tests, ReadWordPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 2)); // Initialize with too-short length
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Success, input.Read(readWord1));
ASSERT_EQ(0x1122, readWord1);
uint16_t readWord2 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord2));
ASSERT_NE(0x3344, readWord2);
}
TEST_F(pkixder_input_tests, ReadWordWithInsufficentData)
{
const uint8_t der[] = { 0x11, 0x22 };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 1));
Reader input(buf);
uint16_t readWord1 = 0;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(readWord1));
ASSERT_NE(0x1122, readWord1);
}
TEST_F(pkixder_input_tests, ReadWordWrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// a null pointer is undefined behavior according to the C++ language spec.,
// but this should catch the problem on at least some compilers, if not all of
// them.
const uint8_t* der = nullptr;
--der;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
uint16_t b;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Read(b));
}
TEST_F(pkixder_input_tests, Skip)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(1));
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x22, readByte1);
ASSERT_EQ(Success, input.Skip(1));
uint8_t readByte2 = 0;
ASSERT_EQ(Success, input.Read(readByte2));
ASSERT_EQ(0x44, readByte2);
}
TEST_F(pkixder_input_tests, Skip_ToEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(sizeof der));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, Skip_PastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1));
}
TEST_F(pkixder_input_tests, Skip_ToNewInput)
{
const uint8_t der[] = { 0x01, 0x02, 0x03, 0x04 };
Input buf(der);
Reader input(buf);
Reader skippedInput;
ASSERT_EQ(Success, input.Skip(3, skippedInput));
uint8_t readByte1 = 0;
ASSERT_EQ(Success, input.Read(readByte1));
ASSERT_EQ(0x04, readByte1);
ASSERT_TRUE(input.AtEnd());
// Reader has no Remaining() or Length() so we simply read the bytes
// and then expect to be at the end.
for (uint8_t i = 1; i <= 3; ++i) {
uint8_t readByte = 0;
ASSERT_EQ(Success, skippedInput.Read(readByte));
ASSERT_EQ(i, readByte);
}
ASSERT_TRUE(skippedInput.AtEnd());
}
TEST_F(pkixder_input_tests, Skip_ToNewInputPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader skippedInput;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der * 2, skippedInput));
}
TEST_F(pkixder_input_tests, Skip_ToInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
Input item;
ASSERT_EQ(Success, input.Skip(sizeof expectedItemData, item));
Input expected(expectedItemData);
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_input_tests, Skip_WrapAroundPointer)
{
// The original implementation of our buffer read overflow checks was
// susceptible to integer overflows which could make the checks ineffective.
// This attempts to verify that we've fixed that. Unfortunately, decrementing
// a null pointer is undefined behavior according to the C++ language spec.,
// but this should catch the problem on at least some compilers, if not all of
// them.
const uint8_t* der = nullptr;
--der;
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(1));
}
TEST_F(pkixder_input_tests, Skip_ToInputPastEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Input skipped;
ASSERT_EQ(Result::ERROR_BAD_DER, input.Skip(sizeof der + 1, skipped));
}
TEST_F(pkixder_input_tests, SkipToEnd_ToInput)
{
static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Input skipped;
ASSERT_EQ(Success, input.SkipToEnd(skipped));
}
TEST_F(pkixder_input_tests, SkipToEnd_ToInput_InputAlreadyInited)
{
static const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
static const uint8_t initialValue[] = { 0x01, 0x02, 0x03 };
Input x(initialValue);
// Fails because skipped was already initialized once, and Inputs are not
// allowed to be Init()d multiple times.
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.SkipToEnd(x));
ASSERT_TRUE(InputsAreEqual(x, Input(initialValue)));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValue)
{
Input buf(DER_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithTruncatedData)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndSkipValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndSkipValueWithOverrunData)
{
Input buf(DER_OVERRUN_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndSkipValue(input, SEQUENCE));
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
TEST_F(pkixder_input_tests, AtEndOnUnInitializedInput)
{
Reader input;
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, AtEndAtBeginning)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_FALSE(input.AtEnd());
}
TEST_F(pkixder_input_tests, AtEndAtEnd)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(sizeof der));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, MarkAndGetInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader::Mark mark = input.GetMark();
const uint8_t expectedItemData[] = { 0x11, 0x22, 0x33 };
ASSERT_EQ(Success, input.Skip(sizeof expectedItemData));
Input item;
ASSERT_EQ(Success, input.GetInput(mark, item));
Input expected(expectedItemData);
ASSERT_TRUE(InputsAreEqual(expected, item));
}
// Cannot run this test on debug builds because of the NotReached
#ifdef NDEBUG
TEST_F(pkixder_input_tests, MarkAndGetInputDifferentInput)
{
const uint8_t der[] = { 0x11, 0x22, 0x33, 0x44 };
Input buf(der);
Reader input(buf);
Reader another;
Reader::Mark mark = another.GetMark();
ASSERT_EQ(Success, input.Skip(3));
Input item;
ASSERT_EQ(Result::FATAL_ERROR_INVALID_ARGS, input.GetInput(mark, item));
}
#endif
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_AtEnd)
{
Reader input(EMPTY_INPUT);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_TruncatedAfterTag)
{
static const uint8_t DER[] = { SEQUENCE };
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
uint8_t tag = 0;
Input value;
ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
ASSERT_EQ(SEQUENCE, tag);
ASSERT_EQ(0u, value.GetLength());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
uint8_t tag = 0;
Input value;
ASSERT_EQ(Success, ReadTagAndGetValue(input, tag, value));
ASSERT_EQ(SEQUENCE, tag);
Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
ASSERT_TRUE(InputsAreEqual(expected, value));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ReadTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER, ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm1)
{
// High tag number form is not allowed (illegal 1 byte tag)
//
// If the decoder treats 0x1F as a valid low tag number tag, then it will
// treat the actual tag (1) as a length, and then it will return Success
// with value == { 0x00 } and tag == 0x1f.
//
// It is illegal to encode tag 1 in the high tag number form because it isn't
// the shortest encoding (the low tag number form is).
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
1, // tag 1 (not legal!)
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm2)
{
// High tag number form is not allowed (legal 1 byte tag).
//
// ReadTagAndGetValue's check to prohibit the high tag number form has no
// effect on whether this test passes or fails, because ReadTagAndGetValue
// will interpret the second byte (31) as a length, and the input doesn't
// have 31 bytes following it. This test is here to guard against the case
// where somebody actually implements high tag number form parsing, to remind
// that person that they need to add tests here, including in particular
// tests for overly-long encodings.
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
31, // tag 31
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ReadTagAndGetValue_Input_InvalidHighTagNumberForm3)
{
// High tag number form is not allowed (2 byte legal tag)
//
// ReadTagAndGetValue's check to prohibit the high tag number form has no
// effect on whether this test passes or fails, because ReadTagAndGetValue
// will interpret the second byte as a length, and the input doesn't have
// that many bytes following it. This test is here to guard against the case
// where somebody actually implements high tag number form parsing, to remind
// that person that they need to add tests here, including in particular
// tests for overly-long encodings.
static const uint8_t DER[] = {
0x1F, // high tag number form indicator
0x80 | 0x01, 0x00, // tag 0x100 (256)
0 // length zero
};
Input buf(DER);
Reader input(buf);
uint8_t tag;
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ReadTagAndGetValue(input, tag, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_TRUE(value.AtEnd());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_TRUE(value.MatchRest(DER_SEQUENCE_NOT_EMPTY_VALUE));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetValue_Reader_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Reader_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Reader value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, INTEGER, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
ASSERT_EQ(0u, value.GetLength());
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Success, ExpectTagAndGetValue(input, SEQUENCE, value));
Input expected(DER_SEQUENCE_NOT_EMPTY_VALUE);
ASSERT_TRUE(InputsAreEqual(expected, value));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, SEQUENCE, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetValue_Input_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input value;
ASSERT_EQ(Result::ERROR_BAD_DER,
ExpectTagAndGetValue(input, INTEGER, value));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
ASSERT_EQ(Success, ExpectTagAndEmptyValue(input, SEQUENCE));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests,
ExpectTagAndEmptyValue_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, SEQUENCE));
}
TEST_F(pkixder_input_tests, ExpectTagAndEmptyValue_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndEmptyValue(input, INTEGER));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidEmpty)
{
Input buf(DER_SEQUENCE_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
Input expected(DER_SEQUENCE_EMPTY);
ASSERT_TRUE(InputsAreEqual(expected, tlv));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_ValidNotEmpty)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Success, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
Input expected(DER_SEQUENCE_NOT_EMPTY);
ASSERT_TRUE(InputsAreEqual(expected, tlv));
ASSERT_TRUE(input.AtEnd());
}
TEST_F(pkixder_input_tests,
ExpectTagAndGetTLV_Input_InvalidNotEmptyValueTruncated)
{
Input buf(DER_SEQUENCE_NOT_EMPTY_VALUE_TRUNCATED);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongLength)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, SEQUENCE, tlv));
}
TEST_F(pkixder_input_tests, ExpectTagAndGetTLV_Input_InvalidWrongTag)
{
Input buf(DER_SEQUENCE_NOT_EMPTY);
Reader input(buf);
Input tlv;
ASSERT_EQ(Result::ERROR_BAD_DER, ExpectTagAndGetTLV(input, INTEGER, tlv));
}
TEST_F(pkixder_input_tests, EndAtEnd)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(4));
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, EndBeforeEnd)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Success, input.Skip(2));
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
TEST_F(pkixder_input_tests, EndAtBeginning)
{
Input buf(DER_INT16);
Reader input(buf);
ASSERT_EQ(Result::ERROR_BAD_DER, End(input));
}
// TODO: Need tests for Nested too?
Result NestedOfHelper(Reader& input, std::vector<uint8_t>& readValues)
{
uint8_t value = 0;
Result rv = input.Read(value);
EXPECT_EQ(Success, rv);
if (rv != Success) {
return rv;
}
readValues.push_back(value);
return Success;
}
TEST_F(pkixder_input_tests, NestedOf)
{
Input buf(DER_SEQUENCE_OF_INT8);
Reader input(buf);
std::vector<uint8_t> readValues;
ASSERT_EQ(Success,
NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
[&readValues](Reader& r) {
return NestedOfHelper(r, readValues);
}));
ASSERT_EQ(3u, readValues.size());
ASSERT_EQ(0x01, readValues[0]);
ASSERT_EQ(0x02, readValues[1]);
ASSERT_EQ(0x03, readValues[2]);
ASSERT_EQ(Success, End(input));
}
TEST_F(pkixder_input_tests, NestedOfWithTruncatedData)
{
Input buf(DER_TRUNCATED_SEQUENCE_OF_INT8);
Reader input(buf);
std::vector<uint8_t> readValues;
ASSERT_EQ(Result::ERROR_BAD_DER,
NestedOf(input, SEQUENCE, INTEGER, EmptyAllowed::No,
[&readValues](Reader& r) {
return NestedOfHelper(r, readValues);
}));
ASSERT_EQ(0u, readValues.size());
}
TEST_F(pkixder_input_tests, MatchRestAtEnd)
{
static const uint8_t der[1] = { };
Input buf;
ASSERT_EQ(Success, buf.Init(der, 0));
Reader input(buf);
ASSERT_TRUE(input.AtEnd());
static const uint8_t toMatch[] = { 1 };
ASSERT_FALSE(input.MatchRest(toMatch));
}
TEST_F(pkixder_input_tests, MatchRest1Match)
{
static const uint8_t der[] = { 1 };
Input buf(der);
Reader input(buf);
ASSERT_FALSE(input.AtEnd());
ASSERT_TRUE(input.MatchRest(der));
}
TEST_F(pkixder_input_tests, MatchRest1Mismatch)
{
static const uint8_t der[] = { 1 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatch[] = { 2 };
ASSERT_FALSE(input.MatchRest(toMatch));
ASSERT_FALSE(input.AtEnd());
}
TEST_F(pkixder_input_tests, MatchRest2WithTrailingByte)
{
static const uint8_t der[] = { 1, 2, 3 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatch[] = { 1, 2 };
ASSERT_FALSE(input.MatchRest(toMatch));
}
TEST_F(pkixder_input_tests, MatchRest2Mismatch)
{
static const uint8_t der[] = { 1, 2, 3 };
Input buf(der);
Reader input(buf);
static const uint8_t toMatchMismatch[] = { 1, 3 };
ASSERT_FALSE(input.MatchRest(toMatchMismatch));
ASSERT_TRUE(input.MatchRest(der));
}
} // namespace

View file

@ -0,0 +1,480 @@
/* -*- 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 <functional>
#include <vector>
#include "pkixgtest.h"
#include "mozpkix/pkixtypes.h"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::der;
class pkixder_pki_types_tests : public ::testing::Test { };
TEST_F(pkixder_pki_types_tests, CertificateSerialNumber)
{
const uint8_t DER_CERT_SERIAL[] = {
0x02, // INTEGER
8, // length
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef
};
Input input(DER_CERT_SERIAL);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
Input expected;
ASSERT_EQ(Success,
expected.Init(DER_CERT_SERIAL + 2, sizeof DER_CERT_SERIAL - 2));
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberLongest)
{
const uint8_t DER_CERT_SERIAL_LONGEST[] = {
0x02, // INTEGER
20, // length
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
};
Input input(DER_CERT_SERIAL_LONGEST);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
Input expected;
ASSERT_EQ(Success,
expected.Init(DER_CERT_SERIAL_LONGEST + 2,
sizeof DER_CERT_SERIAL_LONGEST - 2));
ASSERT_TRUE(InputsAreEqual(expected, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberCrazyLong)
{
const uint8_t DER_CERT_SERIAL_CRAZY_LONG[] = {
0x02, // INTEGER
32, // length
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32
};
Input input(DER_CERT_SERIAL_CRAZY_LONG);
Reader reader(input);
Input item;
ASSERT_EQ(Success, CertificateSerialNumber(reader, item));
}
TEST_F(pkixder_pki_types_tests, CertificateSerialNumberZeroLength)
{
const uint8_t DER_CERT_SERIAL_ZERO_LENGTH[] = {
0x02, // INTEGER
0x00 // length
};
Input input(DER_CERT_SERIAL_ZERO_LENGTH);
Reader reader(input);
Input item;
ASSERT_EQ(Result::ERROR_INVALID_INTEGER_ENCODING,
CertificateSerialNumber(reader, item));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV1ExplicitEncodingAllowed)
{
const uint8_t DER_OPTIONAL_VERSION_V1[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x00 // INTEGER(0)
};
Input input(DER_OPTIONAL_VERSION_V1);
Reader reader(input);
// XXX(bug 1031093): We shouldn't accept an explicit encoding of v1, but we
// do here for compatibility reasons.
// Version version;
// ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
der::Version version = der::Version::v3;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v1, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV2)
{
const uint8_t DER_OPTIONAL_VERSION_V2[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x01 // INTEGER(1)
};
Input input(DER_OPTIONAL_VERSION_V2);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v2, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionV3)
{
const uint8_t DER_OPTIONAL_VERSION_V3[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x02 // INTEGER(2)
};
Input input(DER_OPTIONAL_VERSION_V3);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v3, version);
}
TEST_F(pkixder_pki_types_tests, OptionalVersionUnknown)
{
const uint8_t DER_OPTIONAL_VERSION_INVALID[] = {
0xa0, 0x03, // context specific 0
0x02, 0x01, 0x42 // INTEGER(0x42)
};
Input input(DER_OPTIONAL_VERSION_INVALID);
Reader reader(input);
der::Version version = der::Version::v1;
ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionInvalidTooLong)
{
const uint8_t DER_OPTIONAL_VERSION_INVALID_TOO_LONG[] = {
0xa0, 0x03, // context specific 0
0x02, 0x02, 0x12, 0x34 // INTEGER(0x1234)
};
Input input(DER_OPTIONAL_VERSION_INVALID_TOO_LONG);
Reader reader(input);
der::Version version;
ASSERT_EQ(Result::ERROR_BAD_DER, OptionalVersion(reader, version));
}
TEST_F(pkixder_pki_types_tests, OptionalVersionMissing)
{
const uint8_t DER_OPTIONAL_VERSION_MISSING[] = {
0x02, 0x11, 0x22 // INTEGER
};
Input input(DER_OPTIONAL_VERSION_MISSING);
Reader reader(input);
der::Version version = der::Version::v3;
ASSERT_EQ(Success, OptionalVersion(reader, version));
ASSERT_EQ(der::Version::v1, version);
}
static const size_t MAX_ALGORITHM_OID_DER_LENGTH = 13;
struct InvalidAlgorithmIdentifierTestInfo
{
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
struct ValidDigestAlgorithmIdentifierTestInfo
{
DigestAlgorithm algorithm;
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
class pkixder_DigestAlgorithmIdentifier_Valid
: public ::testing::Test
, public ::testing::WithParamInterface<ValidDigestAlgorithmIdentifierTestInfo>
{
};
static const ValidDigestAlgorithmIdentifierTestInfo
VALID_DIGEST_ALGORITHM_TEST_INFO[] =
{
{ DigestAlgorithm::sha512,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03 },
13
},
{ DigestAlgorithm::sha384,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02 },
13
},
{ DigestAlgorithm::sha256,
{ 0x30, 0x0b, 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
13
},
{ DigestAlgorithm::sha1,
{ 0x30, 0x07, 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1a },
9
},
};
TEST_P(pkixder_DigestAlgorithmIdentifier_Valid, Valid)
{
const ValidDigestAlgorithmIdentifierTestInfo& param(GetParam());
{
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
ASSERT_EQ(param.algorithm, alg);
ASSERT_EQ(Success, End(reader));
}
{
uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
memcpy(derWithNullParam, param.der, param.derLength);
derWithNullParam[1] += 2; // we're going to expand the value by 2 bytes
derWithNullParam[param.derLength] = 0x05; // NULL tag
derWithNullParam[param.derLength + 1] = 0x00; // length zero
Input input;
ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Success, DigestAlgorithmIdentifier(reader, alg));
ASSERT_EQ(param.algorithm, alg);
ASSERT_EQ(Success, End(reader));
}
}
INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Valid,
pkixder_DigestAlgorithmIdentifier_Valid,
testing::ValuesIn(VALID_DIGEST_ALGORITHM_TEST_INFO));
class pkixder_DigestAlgorithmIdentifier_Invalid
: public ::testing::Test
, public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
{
};
static const InvalidAlgorithmIdentifierTestInfo
INVALID_DIGEST_ALGORITHM_TEST_INFO[] =
{
{ // MD5
{ 0x30, 0x0a, 0x06, 0x08,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x02, 0x05 },
12,
},
{ // ecdsa-with-SHA256 (1.2.840.10045.4.3.2) (not a hash algorithm)
{ 0x30, 0x0a, 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
12,
},
};
TEST_P(pkixder_DigestAlgorithmIdentifier_Invalid, Invalid)
{
const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
DigestAlgorithm alg;
ASSERT_EQ(Result::ERROR_INVALID_ALGORITHM,
DigestAlgorithmIdentifier(reader, alg));
}
INSTANTIATE_TEST_CASE_P(pkixder_DigestAlgorithmIdentifier_Invalid,
pkixder_DigestAlgorithmIdentifier_Invalid,
testing::ValuesIn(INVALID_DIGEST_ALGORITHM_TEST_INFO));
struct ValidSignatureAlgorithmIdentifierValueTestInfo
{
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
uint8_t der[MAX_ALGORITHM_OID_DER_LENGTH];
size_t derLength;
};
static const ValidSignatureAlgorithmIdentifierValueTestInfo
VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
{
// ECDSA
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha512,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x04 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha384,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x03 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha256,
{ 0x06, 0x08,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02 },
10,
},
{ PublicKeyAlgorithm::ECDSA,
DigestAlgorithm::sha1,
{ 0x06, 0x07,
0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x01 },
9,
},
// RSA
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha512,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0d },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha384,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0c },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha256,
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha1,
// IETF Standard OID
{ 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05 },
11,
},
{ PublicKeyAlgorithm::RSA_PKCS1,
DigestAlgorithm::sha1,
// Legacy OIW OID (bug 1042479)
{ 0x06, 0x05,
0x2b, 0x0e, 0x03, 0x02, 0x1d },
7,
},
};
class pkixder_SignatureAlgorithmIdentifierValue_Valid
: public ::testing::Test
, public ::testing::WithParamInterface<
ValidSignatureAlgorithmIdentifierValueTestInfo>
{
};
TEST_P(pkixder_SignatureAlgorithmIdentifierValue_Valid, Valid)
{
const ValidSignatureAlgorithmIdentifierValueTestInfo& param(GetParam());
{
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Success,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
digestAlg));
ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
ASSERT_EQ(param.digestAlg, digestAlg);
ASSERT_EQ(Success, End(reader));
}
{
uint8_t derWithNullParam[MAX_ALGORITHM_OID_DER_LENGTH + 2];
memcpy(derWithNullParam, param.der, param.derLength);
derWithNullParam[param.derLength] = 0x05; // NULL tag
derWithNullParam[param.derLength + 1] = 0x00; // length zero
Input input;
ASSERT_EQ(Success, input.Init(derWithNullParam, param.derLength + 2));
Reader reader(input);
PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Success,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg,
digestAlg));
ASSERT_EQ(param.publicKeyAlg, publicKeyAlg);
ASSERT_EQ(param.digestAlg, digestAlg);
ASSERT_EQ(Success, End(reader));
}
}
INSTANTIATE_TEST_CASE_P(
pkixder_SignatureAlgorithmIdentifierValue_Valid,
pkixder_SignatureAlgorithmIdentifierValue_Valid,
testing::ValuesIn(VALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));
static const InvalidAlgorithmIdentifierTestInfo
INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO[] =
{
// id-dsa-with-sha256 (2.16.840.1.101.3.4.3.2)
{ { 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x03, 0x02 },
11,
},
// id-dsa-with-sha1 (1.2.840.10040.4.3)
{ { 0x06, 0x07,
0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x03 },
9,
},
// RSA-with-MD5 (1.2.840.113549.1.1.4)
{ { 0x06, 0x09,
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x04 },
11,
},
// id-sha256 (2.16.840.1.101.3.4.2.1). It is invalid because SHA-256 is not
// a signature algorithm.
{ { 0x06, 0x09,
0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01 },
11,
},
};
class pkixder_SignatureAlgorithmIdentifier_Invalid
: public ::testing::Test
, public ::testing::WithParamInterface<InvalidAlgorithmIdentifierTestInfo>
{
};
TEST_P(pkixder_SignatureAlgorithmIdentifier_Invalid, Invalid)
{
const InvalidAlgorithmIdentifierTestInfo& param(GetParam());
Input input;
ASSERT_EQ(Success, input.Init(param.der, param.derLength));
Reader reader(input);
der::PublicKeyAlgorithm publicKeyAlg;
DigestAlgorithm digestAlg;
ASSERT_EQ(Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED,
SignatureAlgorithmIdentifierValue(reader, publicKeyAlg, digestAlg));
}
INSTANTIATE_TEST_CASE_P(
pkixder_SignatureAlgorithmIdentifier_Invalid,
pkixder_SignatureAlgorithmIdentifier_Invalid,
testing::ValuesIn(INVALID_SIGNATURE_ALGORITHM_VALUE_TEST_INFO));

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,46 @@
/* -*- 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"
#include <ctime>
#include "mozpkix/Time.h"
namespace mozilla { namespace pkix { namespace test {
static const std::time_t ONE_DAY_IN_SECONDS_AS_TIME_T =
static_cast<std::time_t>(Time::ONE_DAY_IN_SECONDS);
// This assumes that time/time_t are POSIX-compliant in that time() returns
// the number of seconds since the Unix epoch.
static const std::time_t now(time(nullptr));
const std::time_t oneDayBeforeNow(now - ONE_DAY_IN_SECONDS_AS_TIME_T);
const std::time_t oneDayAfterNow(now + ONE_DAY_IN_SECONDS_AS_TIME_T);
const std::time_t twoDaysBeforeNow(now - (2 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t twoDaysAfterNow(now + (2 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t tenDaysBeforeNow(now - (10 * ONE_DAY_IN_SECONDS_AS_TIME_T));
const std::time_t tenDaysAfterNow(now + (10 * ONE_DAY_IN_SECONDS_AS_TIME_T));
} } } // namespace mozilla::pkix::test

View file

@ -0,0 +1,229 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This code is made available to you under your choice of the following sets
* of licensing terms:
*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* Copyright 2014 Mozilla Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef mozilla_pkix_pkixgtest_h
#define mozilla_pkix_pkixgtest_h
#include <ostream>
#if defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated"
#pragma clang diagnostic ignored "-Wmissing-noreturn"
#pragma clang diagnostic ignored "-Wshift-sign-overflow"
#pragma clang diagnostic ignored "-Wsign-conversion"
#pragma clang diagnostic ignored "-Wundef"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wextra"
#elif defined(_MSC_VER)
#pragma warning(push, 3)
// C4224: Nonstandard extension used: formal parameter 'X' was previously
// defined as a type.
#pragma warning(disable : 4224)
// C4826: Conversion from 'type1 ' to 'type_2' is sign - extended. This may
// cause unexpected runtime behavior.
#pragma warning(disable : 4826)
#endif
#include "gtest/gtest.h"
#if defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#elif defined(_MSC_VER)
#pragma warning(pop)
#endif
#include "mozpkix/pkix.h"
#include "mozpkix/test/pkixtestutil.h"
// PrintTo must be in the same namespace as the type we're overloading it for.
namespace mozilla {
namespace pkix {
inline void PrintTo(const Result& result, ::std::ostream* os) {
const char* stringified = MapResultToName(result);
if (stringified) {
*os << stringified;
} else {
*os << "mozilla::pkix::Result(" << static_cast<unsigned int>(result) << ")";
}
}
}
} // namespace mozilla::pkix
namespace mozilla {
namespace pkix {
namespace test {
extern const std::time_t oneDayBeforeNow;
extern const std::time_t oneDayAfterNow;
extern const std::time_t twoDaysBeforeNow;
extern const std::time_t twoDaysAfterNow;
extern const std::time_t tenDaysBeforeNow;
extern const std::time_t tenDaysAfterNow;
class EverythingFailsByDefaultTrustDomain : public TrustDomain {
public:
Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, Input,
/*out*/ TrustLevel&) override {
ADD_FAILURE();
return NotReached("GetCertTrust should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result FindIssuer(Input, IssuerChecker&, Time) override {
ADD_FAILURE();
return NotReached("FindIssuer should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration,
/*optional*/ const Input*,
/*optional*/ const Input*) override {
ADD_FAILURE();
return NotReached("CheckRevocation should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result IsChainValid(const DERArray&, Time, const CertPolicyId&) override {
ADD_FAILURE();
return NotReached("IsChainValid should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result DigestBuf(Input, DigestAlgorithm, /*out*/ uint8_t*, size_t) override {
ADD_FAILURE();
return NotReached("DigestBuf should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
ADD_FAILURE();
return NotReached("CheckSignatureDigestAlgorithm should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override {
ADD_FAILURE();
return NotReached("CheckECDSACurveIsAcceptable should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result VerifyECDSASignedDigest(const SignedDigest&, Input) override {
ADD_FAILURE();
return NotReached("VerifyECDSASignedDigest should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
unsigned int) override {
ADD_FAILURE();
return NotReached("CheckRSAPublicKeyModulusSizeInBits should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result VerifyRSAPKCS1SignedDigest(const SignedDigest&, Input) override {
ADD_FAILURE();
return NotReached("VerifyRSAPKCS1SignedDigest should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
ADD_FAILURE();
return NotReached("CheckValidityIsAcceptable should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
Result NetscapeStepUpMatchesServerAuth(Time, bool&) override {
ADD_FAILURE();
return NotReached("NetscapeStepUpMatchesServerAuth should not be called",
Result::FATAL_ERROR_LIBRARY_FAILURE);
}
virtual void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {
ADD_FAILURE();
}
};
class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain {
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t* digestBuf, size_t digestBufLen) override {
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
}
Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA,
Time) override {
return Success;
}
Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override {
return Success;
}
Result VerifyECDSASignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) override {
return TestVerifyECDSASignedDigest(signedDigest, subjectPublicKeyInfo);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA,
unsigned int) override {
return Success;
}
Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest,
Input subjectPublicKeyInfo) override {
return TestVerifyRSAPKCS1SignedDigest(signedDigest, subjectPublicKeyInfo);
}
Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA,
KeyPurposeId) override {
return Success;
}
Result NetscapeStepUpMatchesServerAuth(Time, /*out*/ bool& matches) override {
matches = true;
return Success;
}
void NoteAuxiliaryExtension(AuxiliaryExtension, Input) override {}
};
class DefaultNameMatchingPolicy : public NameMatchingPolicy {
public:
virtual Result FallBackToCommonName(
Time,
/*out*/ FallBackToSearchWithinSubject& fallBackToCommonName) override {
fallBackToCommonName = FallBackToSearchWithinSubject::Yes;
return Success;
}
};
}
}
} // namespace mozilla::pkix::test
#endif // mozilla_pkix_pkixgtest_h

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,146 @@
/* -*- 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"
#include "mozpkix/pkixder.h"
using namespace mozilla::pkix;
using namespace mozilla::pkix::test;
class CreateEncodedOCSPRequestTrustDomain final
: public EverythingFailsByDefaultTrustDomain
{
private:
Result DigestBuf(Input item, DigestAlgorithm digestAlg,
/*out*/ uint8_t *digestBuf, size_t digestBufLen)
override
{
return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
}
Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int)
override
{
return Success;
}
};
class pkixocsp_CreateEncodedOCSPRequest : public ::testing::Test
{
protected:
void MakeIssuerCertIDComponents(const char* issuerASCII,
/*out*/ ByteString& issuerDER,
/*out*/ ByteString& issuerSPKI)
{
issuerDER = CNToDERName(issuerASCII);
ASSERT_FALSE(ENCODING_FAILED(issuerDER));
ScopedTestKeyPair keyPair(GenerateKeyPair());
ASSERT_TRUE(keyPair.get());
issuerSPKI = keyPair->subjectPublicKeyInfo;
}
CreateEncodedOCSPRequestTrustDomain trustDomain;
};
// Test that the large length of the child serial number causes
// CreateEncodedOCSPRequest to fail.
TEST_F(pkixocsp_CreateEncodedOCSPRequest, ChildCertLongSerialNumberTest)
{
static const uint8_t UNSUPPORTED_LEN = 128; // must be larger than 127
ByteString serialNumberString;
// tag + length + value is 1 + 2 + UNSUPPORTED_LEN
// Encoding the length takes two bytes: one byte to indicate that a
// second byte follows, and the second byte to indicate the length.
serialNumberString.push_back(0x80 + 1);
serialNumberString.push_back(UNSUPPORTED_LEN);
// value is 0x010000...00
serialNumberString.push_back(0x01);
for (size_t i = 1; i < UNSUPPORTED_LEN; ++i) {
serialNumberString.push_back(0x00);
}
ByteString issuerDER;
ByteString issuerSPKI;
ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER,
issuerSPKI));
Input issuer;
ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length()));
Input spki;
ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length()));
Input serialNumber;
ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(),
serialNumberString.length()));
uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
size_t ocspRequestLength;
ASSERT_EQ(Result::ERROR_BAD_DER,
CreateEncodedOCSPRequest(trustDomain,
CertID(issuer, spki, serialNumber),
ocspRequest, ocspRequestLength));
}
// Test that CreateEncodedOCSPRequest handles the longest serial number that
// it's required to support (i.e. 20 octets).
TEST_F(pkixocsp_CreateEncodedOCSPRequest, LongestSupportedSerialNumberTest)
{
static const uint8_t LONGEST_REQUIRED_LEN = 20;
ByteString serialNumberString;
// tag + length + value is 1 + 1 + LONGEST_REQUIRED_LEN
serialNumberString.push_back(der::INTEGER);
serialNumberString.push_back(LONGEST_REQUIRED_LEN);
serialNumberString.push_back(0x01);
// value is 0x010000...00
for (size_t i = 1; i < LONGEST_REQUIRED_LEN; ++i) {
serialNumberString.push_back(0x00);
}
ByteString issuerDER;
ByteString issuerSPKI;
ASSERT_NO_FATAL_FAILURE(MakeIssuerCertIDComponents("CA", issuerDER,
issuerSPKI));
Input issuer;
ASSERT_EQ(Success, issuer.Init(issuerDER.data(), issuerDER.length()));
Input spki;
ASSERT_EQ(Success, spki.Init(issuerSPKI.data(), issuerSPKI.length()));
Input serialNumber;
ASSERT_EQ(Success, serialNumber.Init(serialNumberString.data(),
serialNumberString.length()));
uint8_t ocspRequest[OCSP_REQUEST_MAX_LENGTH];
size_t ocspRequestLength;
ASSERT_EQ(Success,
CreateEncodedOCSPRequest(trustDomain,
CertID(issuer, spki, serialNumber),
ocspRequest, ocspRequestLength));
}

File diff suppressed because it is too large Load diff