/* -*- 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*, /*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 { 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_SUITE_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*/)); }