diff options
Diffstat (limited to 'security/pkix/test/gtest/pkixcert_extension_tests.cpp')
-rw-r--r-- | security/pkix/test/gtest/pkixcert_extension_tests.cpp | 270 |
1 files changed, 270 insertions, 0 deletions
diff --git a/security/pkix/test/gtest/pkixcert_extension_tests.cpp b/security/pkix/test/gtest/pkixcert_extension_tests.cpp new file mode 100644 index 000000000..464643134 --- /dev/null +++ b/security/pkix/test/gtest/pkixcert_extension_tests.cpp @@ -0,0 +1,270 @@ +/* -*- 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 "pkixder.h" +#include "pkixgtest.h" +#include "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) 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; +}; + +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*/)); +} |