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