/* -*- 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 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/. */ #include "MultiLogCTVerifier.h" #include #include "CTLogVerifier.h" #include "CTObjectsExtractor.h" #include "CTSerialization.h" #include "CTTestUtils.h" #include "gtest/gtest.h" #include "mozilla/EnumSet.h" #include "mozilla/Move.h" #include "nss.h" namespace mozilla { namespace ct { using namespace mozilla::pkix; class MultiLogCTVerifierTest : public ::testing::Test { public: MultiLogCTVerifierTest() : mNow(Time::uninitialized) {} void SetUp() override { // Does nothing if NSS is already initialized. MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess); ASSERT_EQ(Success, mVerifier.AddLog(InputForBuffer(GetTestPublicKey()))); mTestCert = GetDEREncodedX509Cert(); mEmbeddedCert = GetDEREncodedTestEmbeddedCert(); mCaCert = GetDEREncodedCACert(); mCaCertSPKI = ExtractCertSPKI(mCaCert); mIntermediateCert = GetDEREncodedIntermediateCert(); mIntermediateCertSPKI = ExtractCertSPKI(mIntermediateCert); // Set the current time making sure all test timestamps are in the past. mNow = TimeFromEpochInSeconds(1451606400u); // Date.parse("2016-01-01")/1000 } void CheckForSingleVerifiedSCTInResult(const CTVerifyResult& result, SignedCertificateTimestamp::Origin origin) { EXPECT_EQ(0U, result.decodingErrors); ASSERT_EQ(1U, result.scts.length()); EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK, result.scts[0].verificationStatus); EXPECT_EQ(origin, result.scts[0].origin); } // Writes an SCTList containing a single |sct| into |output|. void EncodeSCTListForTesting(Input sct, Buffer& output) { Vector list; ASSERT_TRUE(list.append(Move(sct))); ASSERT_EQ(Success, EncodeSCTList(list, output)); } void GetSCTListWithInvalidLogID(Buffer& result) { result.clear(); Buffer sct(GetTestSignedCertificateTimestamp()); // Change a byte inside the Log ID part of the SCT so it does // not match the log used in the tests. sct[15] ^= '\xFF'; EncodeSCTListForTesting(InputForBuffer(sct), result); } void CheckPrecertVerification(const Buffer& cert, const Buffer& issuerSPKI) { Buffer sctList; ExtractEmbeddedSCTList(cert, sctList); ASSERT_FALSE(sctList.empty()); CTVerifyResult result; ASSERT_EQ(Success, mVerifier.Verify(InputForBuffer(cert), InputForBuffer(issuerSPKI), InputForBuffer(sctList), Input(), Input(), mNow, result)); CheckForSingleVerifiedSCTInResult(result, SignedCertificateTimestamp::Origin::Embedded); } protected: MultiLogCTVerifier mVerifier; Buffer mTestCert; Buffer mEmbeddedCert; Buffer mCaCert; Buffer mCaCertSPKI; Buffer mIntermediateCert; Buffer mIntermediateCertSPKI; Time mNow; }; // Test that an embedded SCT can be extracted and the extracted SCT contains // the expected data. This tests the ExtractEmbeddedSCTList function from // CTTestUtils.h that other tests here rely upon. TEST_F(MultiLogCTVerifierTest, ExtractEmbeddedSCT) { SignedCertificateTimestamp sct; // Extract the embedded SCT. Buffer sctList; ExtractEmbeddedSCTList(mEmbeddedCert, sctList); ASSERT_FALSE(sctList.empty()); Reader sctReader; ASSERT_EQ(Success, DecodeSCTList(InputForBuffer(sctList), sctReader)); Input sctItemInput; ASSERT_EQ(Success, ReadSCTListItem(sctReader, sctItemInput)); EXPECT_TRUE(sctReader.AtEnd()); // we only expect one sct in the list Reader sctItemReader(sctItemInput); ASSERT_EQ(Success, DecodeSignedCertificateTimestamp(sctItemReader, sct)); // Make sure the SCT contains the expected data. EXPECT_EQ(SignedCertificateTimestamp::Version::V1, sct.version); EXPECT_EQ(GetTestPublicKeyId(), sct.logId); uint64_t expectedTimestamp = 1365181456275; EXPECT_EQ(expectedTimestamp, sct.timestamp); } TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCT) { CheckPrecertVerification(mEmbeddedCert, mCaCertSPKI); } TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithPreCA) { CheckPrecertVerification(GetDEREncodedTestEmbeddedWithPreCACert(), mCaCertSPKI); } TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediate) { CheckPrecertVerification(GetDEREncodedTestEmbeddedWithIntermediateCert(), mIntermediateCertSPKI); } TEST_F(MultiLogCTVerifierTest, VerifiesEmbeddedSCTWithIntermediateAndPreCA) { CheckPrecertVerification(GetDEREncodedTestEmbeddedWithIntermediatePreCACert(), mIntermediateCertSPKI); } TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromOCSP) { Buffer sct(GetTestSignedCertificateTimestamp()); Buffer sctList; EncodeSCTListForTesting(InputForBuffer(sct), sctList); CTVerifyResult result; ASSERT_EQ(Success, mVerifier.Verify(InputForBuffer(mTestCert), Input(), Input(), InputForBuffer(sctList), Input(), mNow, result)); CheckForSingleVerifiedSCTInResult(result, SignedCertificateTimestamp::Origin::OCSPResponse); } TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromTLS) { Buffer sct(GetTestSignedCertificateTimestamp()); Buffer sctList; EncodeSCTListForTesting(InputForBuffer(sct), sctList); CTVerifyResult result; ASSERT_EQ(Success, mVerifier.Verify(InputForBuffer(mTestCert), Input(), Input(), Input(), InputForBuffer(sctList), mNow, result)); CheckForSingleVerifiedSCTInResult(result, SignedCertificateTimestamp::Origin::TLSExtension); } TEST_F(MultiLogCTVerifierTest, VerifiesSCTFromMultipleSources) { Buffer sct(GetTestSignedCertificateTimestamp()); Buffer sctList; EncodeSCTListForTesting(InputForBuffer(sct), sctList); CTVerifyResult result; ASSERT_EQ(Success, mVerifier.Verify(InputForBuffer(mTestCert), Input(), Input(), InputForBuffer(sctList), InputForBuffer(sctList), mNow, result)); // The result should contain verified SCTs from TLS and OCSP origins. EnumSet origins; for (const SignedCertificateTimestamp& sct : result.scts) { EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::OK, sct.verificationStatus); origins += sct.origin; } EXPECT_FALSE( origins.contains(SignedCertificateTimestamp::Origin::Embedded)); EXPECT_TRUE( origins.contains(SignedCertificateTimestamp::Origin::OCSPResponse)); EXPECT_TRUE( origins.contains(SignedCertificateTimestamp::Origin::TLSExtension)); } TEST_F(MultiLogCTVerifierTest, IdentifiesSCTFromUnknownLog) { Buffer sctList; GetSCTListWithInvalidLogID(sctList); CTVerifyResult result; ASSERT_EQ(Success, mVerifier.Verify(InputForBuffer(mTestCert), Input(), Input(), Input(), InputForBuffer(sctList), mNow, result)); EXPECT_EQ(0U, result.decodingErrors); ASSERT_EQ(1U, result.scts.length()); EXPECT_EQ(SignedCertificateTimestamp::VerificationStatus::UnknownLog, result.scts[0].verificationStatus); } } } // namespace mozilla::ct