diff options
Diffstat (limited to 'security/certverifier/tests')
-rw-r--r-- | security/certverifier/tests/gtest/CTLogVerifierTest.cpp | 129 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp | 83 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/CTSerializationTest.cpp | 271 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/CTTestUtils.cpp | 747 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/CTTestUtils.h | 132 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp | 233 | ||||
-rw-r--r-- | security/certverifier/tests/gtest/moz.build | 21 |
7 files changed, 1616 insertions, 0 deletions
diff --git a/security/certverifier/tests/gtest/CTLogVerifierTest.cpp b/security/certverifier/tests/gtest/CTLogVerifierTest.cpp new file mode 100644 index 000000000..2cf0085a1 --- /dev/null +++ b/security/certverifier/tests/gtest/CTLogVerifierTest.cpp @@ -0,0 +1,129 @@ +/* -*- 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 "CTLogVerifier.h" +#include "CTTestUtils.h" +#include "nss.h" + +#include "gtest/gtest.h" + +namespace mozilla { namespace ct { + +using namespace pkix; + +class CTLogVerifierTest : public ::testing::Test +{ +public: + void SetUp() override + { + // Does nothing if NSS is already initialized. + MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess); + + ASSERT_EQ(Success, mLog.Init(InputForBuffer(GetTestPublicKey()))); + ASSERT_EQ(GetTestPublicKeyId(), mLog.keyId()); + } + +protected: + CTLogVerifier mLog; +}; + +TEST_F(CTLogVerifierTest, VerifiesCertSCT) +{ + LogEntry certEntry; + GetX509CertLogEntry(certEntry); + + SignedCertificateTimestamp certSct; + GetX509CertSCT(certSct); + + EXPECT_EQ(Success, mLog.Verify(certEntry, certSct)); +} + +TEST_F(CTLogVerifierTest, VerifiesPrecertSCT) +{ + LogEntry precertEntry; + GetPrecertLogEntry(precertEntry); + + SignedCertificateTimestamp precertSct; + GetPrecertSCT(precertSct); + + EXPECT_EQ(Success, mLog.Verify(precertEntry, precertSct)); +} + +TEST_F(CTLogVerifierTest, FailsInvalidTimestamp) +{ + LogEntry certEntry; + GetX509CertLogEntry(certEntry); + + SignedCertificateTimestamp certSct; + GetX509CertSCT(certSct); + + // Mangle the timestamp, so that it should fail signature validation. + certSct.timestamp = 0; + + EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct)); +} + +TEST_F(CTLogVerifierTest, FailsInvalidSignature) +{ + LogEntry certEntry; + GetX509CertLogEntry(certEntry); + + // Mangle the signature, making VerifyECDSASignedDigestNSS (used by + // CTLogVerifier) return ERROR_BAD_SIGNATURE. + SignedCertificateTimestamp certSct; + GetX509CertSCT(certSct); + certSct.signature.signatureData[20] ^= '\xFF'; + EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct)); + + // Make VerifyECDSASignedDigestNSS return ERROR_BAD_DER. We still expect + // the verifier to return ERROR_BAD_SIGNATURE. + SignedCertificateTimestamp certSct2; + GetX509CertSCT(certSct2); + certSct2.signature.signatureData[0] ^= '\xFF'; + EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.Verify(certEntry, certSct2)); +} + +TEST_F(CTLogVerifierTest, FailsInvalidLogID) +{ + LogEntry certEntry; + GetX509CertLogEntry(certEntry); + + SignedCertificateTimestamp certSct; + GetX509CertSCT(certSct); + + // Mangle the log ID, which should cause it to match a different log before + // attempting signature validation. + MOZ_RELEASE_ASSERT(certSct.logId.append('\x0')); + + EXPECT_EQ(Result::FATAL_ERROR_INVALID_ARGS, mLog.Verify(certEntry, certSct)); +} + +TEST_F(CTLogVerifierTest, VerifiesValidSTH) +{ + SignedTreeHead sth; + GetSampleSignedTreeHead(sth); + EXPECT_EQ(Success, mLog.VerifySignedTreeHead(sth)); +} + +TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidSTH) +{ + SignedTreeHead sth; + GetSampleSignedTreeHead(sth); + sth.sha256RootHash[0] ^= '\xFF'; + EXPECT_EQ(Result::ERROR_BAD_SIGNATURE, mLog.VerifySignedTreeHead(sth)); +} + +// Test that excess data after the public key is rejected. +TEST_F(CTLogVerifierTest, ExcessDataInPublicKey) +{ + Buffer key = GetTestPublicKey(); + MOZ_RELEASE_ASSERT(key.append("extra", 5)); + + CTLogVerifier log; + EXPECT_NE(Success, log.Init(InputForBuffer(key))); +} + +} } // namespace mozilla::ct diff --git a/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp b/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp new file mode 100644 index 000000000..a00a47bf2 --- /dev/null +++ b/security/certverifier/tests/gtest/CTObjectsExtractorTest.cpp @@ -0,0 +1,83 @@ +/* -*- 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 "CTLogVerifier.h" +#include "CTObjectsExtractor.h" +#include "CTSerialization.h" +#include "CTTestUtils.h" +#include "gtest/gtest.h" +#include "nss.h" + +namespace mozilla { namespace ct { + +using namespace pkix; + +class CTObjectsExtractorTest : public ::testing::Test +{ +public: + void SetUp() override + { + // Does nothing if NSS is already initialized. + MOZ_RELEASE_ASSERT(NSS_NoDB_Init(nullptr) == SECSuccess); + + mTestCert = GetDEREncodedX509Cert(); + mEmbeddedCert = GetDEREncodedTestEmbeddedCert(); + mCaCert = GetDEREncodedCACert(); + mCaCertSPKI = ExtractCertSPKI(mCaCert); + + Buffer logPublicKey = GetTestPublicKey(); + ASSERT_EQ(Success, mLog.Init(InputForBuffer(logPublicKey))); + } + +protected: + Buffer mTestCert; + Buffer mEmbeddedCert; + Buffer mCaCert; + Buffer mCaCertSPKI; + CTLogVerifier mLog; +}; + +TEST_F(CTObjectsExtractorTest, ExtractPrecert) +{ + LogEntry entry; + ASSERT_EQ(Success, + GetPrecertLogEntry(InputForBuffer(mEmbeddedCert), + InputForBuffer(mCaCertSPKI), + entry)); + + EXPECT_EQ(LogEntry::Type::Precert, entry.type); + // Should have empty leaf cert for this log entry type. + EXPECT_TRUE(entry.leafCertificate.empty()); + EXPECT_EQ(GetDefaultIssuerKeyHash(), entry.issuerKeyHash); + EXPECT_EQ(GetDEREncodedTestTbsCert(), entry.tbsCertificate); +} + +TEST_F(CTObjectsExtractorTest, ExtractOrdinaryX509Cert) +{ + LogEntry entry; + ASSERT_EQ(Success, GetX509LogEntry(InputForBuffer(mTestCert), entry)); + + EXPECT_EQ(LogEntry::Type::X509, entry.type); + // Should have empty tbsCertificate / issuerKeyHash for this log entry type. + EXPECT_TRUE(entry.tbsCertificate.empty()); + EXPECT_TRUE(entry.issuerKeyHash.empty()); + // Length of leafCertificate should be 718, see the CT Serialization tests. + EXPECT_EQ(718U, entry.leafCertificate.length()); +} + +// Test that an externally-provided SCT verifies over the LogEntry +// of a regular X.509 Certificate +TEST_F(CTObjectsExtractorTest, ComplementarySCTVerifies) +{ + SignedCertificateTimestamp sct; + GetX509CertSCT(sct); + + LogEntry entry; + ASSERT_EQ(Success, GetX509LogEntry(InputForBuffer(mTestCert), entry)); + EXPECT_EQ(Success, mLog.Verify(entry, sct)); +} + +} } // namespace mozilla::ct diff --git a/security/certverifier/tests/gtest/CTSerializationTest.cpp b/security/certverifier/tests/gtest/CTSerializationTest.cpp new file mode 100644 index 000000000..7c5aadd23 --- /dev/null +++ b/security/certverifier/tests/gtest/CTSerializationTest.cpp @@ -0,0 +1,271 @@ +/* -*- 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 "CTSerialization.h" +#include "CTTestUtils.h" +#include "gtest/gtest.h" +#include "mozilla/Move.h" + +namespace mozilla { namespace ct { + +using namespace pkix; + +class CTSerializationTest : public ::testing::Test +{ +public: + void SetUp() override + { + mTestDigitallySigned = GetTestDigitallySigned(); + mTestSignatureData = GetTestDigitallySignedData(); + } + +protected: + Buffer mTestDigitallySigned; + Buffer mTestSignatureData; +}; + +TEST_F(CTSerializationTest, DecodesDigitallySigned) +{ + Input digitallySigned = InputForBuffer(mTestDigitallySigned); + Reader digitallySignedReader(digitallySigned); + + DigitallySigned parsed; + ASSERT_EQ(Success, + DecodeDigitallySigned(digitallySignedReader, parsed)); + EXPECT_TRUE(digitallySignedReader.AtEnd()); + + EXPECT_EQ(DigitallySigned::HashAlgorithm::SHA256, + parsed.hashAlgorithm); + EXPECT_EQ(DigitallySigned::SignatureAlgorithm::ECDSA, + parsed.signatureAlgorithm); + EXPECT_EQ(mTestSignatureData, parsed.signatureData); +} + +TEST_F(CTSerializationTest, FailsToDecodePartialDigitallySigned) +{ + Input partial; + ASSERT_EQ(Success, + partial.Init(mTestDigitallySigned.begin(), + mTestDigitallySigned.length() - 5)); + Reader partialReader(partial); + + DigitallySigned parsed; + + EXPECT_NE(Success, DecodeDigitallySigned(partialReader, parsed)); +} + +TEST_F(CTSerializationTest, EncodesDigitallySigned) +{ + DigitallySigned digitallySigned; + digitallySigned.hashAlgorithm = + DigitallySigned::HashAlgorithm::SHA256; + digitallySigned.signatureAlgorithm = + DigitallySigned::SignatureAlgorithm::ECDSA; + digitallySigned.signatureData = cloneBuffer(mTestSignatureData); + + Buffer encoded; + + ASSERT_EQ(Success, EncodeDigitallySigned(digitallySigned, encoded)); + EXPECT_EQ(mTestDigitallySigned, encoded); +} + +TEST_F(CTSerializationTest, EncodesLogEntryForX509Cert) +{ + LogEntry entry; + GetX509CertLogEntry(entry); + + Buffer encoded; + ASSERT_EQ(Success, EncodeLogEntry(entry, encoded)); + EXPECT_EQ((718U + 5U), encoded.length()); + // First two bytes are log entry type. Next, length: + // Length is 718 which is 512 + 206, which is 0x2ce + Buffer expectedPrefix; + MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\0\0\x2\xCE", 5)); + Buffer encodedPrefix; + MOZ_RELEASE_ASSERT(encodedPrefix. + append(encoded.begin(), encoded.begin() + 5)); + EXPECT_EQ(expectedPrefix, encodedPrefix); +} + +TEST_F(CTSerializationTest, EncodesLogEntryForPrecert) +{ + LogEntry entry; + GetPrecertLogEntry(entry); + + Buffer encoded; + ASSERT_EQ(Success, EncodeLogEntry(entry, encoded)); + // log entry type + issuer key + length + tbsCertificate + EXPECT_EQ((2U + 32U + 3U + entry.tbsCertificate.length()), encoded.length()); + + // First two bytes are log entry type. + Buffer expectedPrefix; + MOZ_RELEASE_ASSERT(expectedPrefix.append("\0\x1", 2)); + Buffer encodedPrefix; + MOZ_RELEASE_ASSERT(encodedPrefix. + append(encoded.begin(), encoded.begin() + 2)); + EXPECT_EQ(expectedPrefix, encodedPrefix); + + // Next is the issuer key (32 bytes). + Buffer encodedKeyHash; + MOZ_RELEASE_ASSERT(encodedKeyHash. + append(encoded.begin() + 2, encoded.begin() + 2 + 32)); + EXPECT_EQ(GetDefaultIssuerKeyHash(), encodedKeyHash); +} + +TEST_F(CTSerializationTest, EncodesV1SCTSignedData) +{ + uint64_t timestamp = UINT64_C(0x139fe353cf5); + const uint8_t DUMMY_BYTES[] = { 0x61, 0x62, 0x63 }; // abc + Input dummyEntry(DUMMY_BYTES); + Input emptyExtensions; + Buffer encoded; + ASSERT_EQ(Success, EncodeV1SCTSignedData( + timestamp, dummyEntry, emptyExtensions, encoded)); + EXPECT_EQ((size_t) 15, encoded.length()); + + const uint8_t EXPECTED_BYTES[] = { + 0x00, // version + 0x00, // signature type + 0x00, 0x00, 0x01, 0x39, 0xFE, 0x35, 0x3C, 0xF5, // timestamp + 0x61, 0x62, 0x63, // log signature + 0x00, 0x00 // extensions (empty) + }; + Buffer expectedBuffer; + MOZ_RELEASE_ASSERT( + expectedBuffer.append(EXPECTED_BYTES, sizeof(EXPECTED_BYTES))); + EXPECT_EQ(expectedBuffer, encoded); +} + +TEST_F(CTSerializationTest, DecodesSCTList) +{ + // Two items in the list: "abc", "def" + const uint8_t ENCODED[] = { + 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x03, 0x64, 0x65, 0x66 + }; + const uint8_t DECODED_1[] = { 0x61, 0x62, 0x63 }; + const uint8_t DECODED_2[] = { 0x64, 0x65, 0x66 }; + + Reader listReader; + ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader)); + + Input decoded1; + ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1)); + + Input decoded2; + ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2)); + + EXPECT_TRUE(listReader.AtEnd()); + EXPECT_TRUE(InputsAreEqual(decoded1, Input(DECODED_1))); + EXPECT_TRUE(InputsAreEqual(decoded2, Input(DECODED_2))); +} + +TEST_F(CTSerializationTest, FailsDecodingInvalidSCTList) +{ + // A list with one item that's too short (the second one) + const uint8_t ENCODED[] = { + 0x00, 0x0a, 0x00, 0x03, 0x61, 0x62, 0x63, 0x00, 0x05, 0x64, 0x65, 0x66 + }; + + Reader listReader; + ASSERT_EQ(Success, DecodeSCTList(Input(ENCODED), listReader)); + Input decoded1; + EXPECT_EQ(Success, ReadSCTListItem(listReader, decoded1)); + Input decoded2; + EXPECT_NE(Success, ReadSCTListItem(listReader, decoded2)); +} + +TEST_F(CTSerializationTest, EncodesSCTList) +{ + const uint8_t SCT_1[] = { 0x61, 0x62, 0x63 }; + const uint8_t SCT_2[] = { 0x64, 0x65, 0x66 }; + + Vector<Input> list; + ASSERT_TRUE(list.append(Move(Input(SCT_1)))); + ASSERT_TRUE(list.append(Move(Input(SCT_2)))); + + Buffer encodedList; + ASSERT_EQ(Success, EncodeSCTList(list, encodedList)); + + Reader listReader; + ASSERT_EQ(Success, DecodeSCTList(InputForBuffer(encodedList), listReader)); + + Input decoded1; + ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded1)); + EXPECT_TRUE(InputsAreEqual(decoded1, Input(SCT_1))); + + Input decoded2; + ASSERT_EQ(Success, ReadSCTListItem(listReader, decoded2)); + EXPECT_TRUE(InputsAreEqual(decoded2, Input(SCT_2))); + + EXPECT_TRUE(listReader.AtEnd()); +} + +TEST_F(CTSerializationTest, DecodesSignedCertificateTimestamp) +{ + Buffer encodedSctBuffer = GetTestSignedCertificateTimestamp(); + Input encodedSctInput = InputForBuffer(encodedSctBuffer); + Reader encodedSctReader(encodedSctInput); + + SignedCertificateTimestamp sct; + ASSERT_EQ(Success, + DecodeSignedCertificateTimestamp(encodedSctReader, sct)); + EXPECT_EQ(SignedCertificateTimestamp::Version::V1, sct.version); + EXPECT_EQ(GetTestPublicKeyId(), sct.logId); + const uint64_t expectedTime = 1365181456089; + EXPECT_EQ(expectedTime, sct.timestamp); + const size_t expectedSignatureLength = 71; + EXPECT_EQ(expectedSignatureLength, sct.signature.signatureData.length()); + EXPECT_TRUE(sct.extensions.empty()); +} + +TEST_F(CTSerializationTest, FailsDecodingInvalidSignedCertificateTimestamp) +{ + SignedCertificateTimestamp sct; + + // Invalid version + const uint8_t INVALID_VERSION_BYTES[] = { 0x02, 0x00 }; + Input invalidVersionSctInput(INVALID_VERSION_BYTES); + Reader invalidVersionSctReader(invalidVersionSctInput); + EXPECT_EQ(Result::ERROR_BAD_DER, + DecodeSignedCertificateTimestamp(invalidVersionSctReader, sct)); + + // Valid version, invalid length (missing data) + const uint8_t INVALID_LENGTH_BYTES[] = { 0x00, 0x0a, 0x0b, 0x0c }; + Input invalidLengthSctInput(INVALID_LENGTH_BYTES); + Reader invalidLengthSctReader(invalidLengthSctInput); + EXPECT_EQ(Result::ERROR_BAD_DER, + DecodeSignedCertificateTimestamp(invalidLengthSctReader, sct)); +} + +TEST_F(CTSerializationTest, EncodesValidSignedTreeHead) +{ + SignedTreeHead signedTreeHead; + GetSampleSignedTreeHead(signedTreeHead); + + Buffer encoded; + ASSERT_EQ(Success, + EncodeTreeHeadSignature(signedTreeHead, encoded)); + // Expected size is 50 bytes: + // Byte 0 is version, byte 1 is signature type + // Bytes 2-9 are timestamp + // Bytes 10-17 are tree size + // Bytes 18-49 are sha256 root hash + ASSERT_EQ(50u, encoded.length()); + const uint8_t EXPECTED_BYTES_PREFIX[] = { + 0x00, // version + 0x01, // signature type + 0x00, 0x00, 0x01, 0x45, 0x3c, 0x5f, 0xb8, 0x35, // timestamp + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x15 // tree size + // sha256 root hash should follow + }; + Buffer expectedBuffer; + MOZ_RELEASE_ASSERT(expectedBuffer.append(EXPECTED_BYTES_PREFIX, 18)); + Buffer hash = GetSampleSTHSHA256RootHash(); + MOZ_RELEASE_ASSERT(expectedBuffer.append(hash.begin(), hash.length())); + EXPECT_EQ(expectedBuffer, encoded); +} + +} } // namespace mozilla::ct diff --git a/security/certverifier/tests/gtest/CTTestUtils.cpp b/security/certverifier/tests/gtest/CTTestUtils.cpp new file mode 100644 index 000000000..18ae74035 --- /dev/null +++ b/security/certverifier/tests/gtest/CTTestUtils.cpp @@ -0,0 +1,747 @@ +/* -*- 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 "CTTestUtils.h" + +#include <stdint.h> +#include <iomanip> + +#include "CTSerialization.h" +#include "gtest/gtest.h" +#include "mozilla/Assertions.h" +#include "mozilla/Move.h" +#include "mozilla/Vector.h" +#include "pkix/Input.h" +#include "pkix/pkix.h" +#include "pkix/pkixnss.h" +#include "pkix/pkixtypes.h" +#include "pkix/Result.h" +#include "pkixcheck.h" +#include "pkixutil.h" +#include "SignedCertificateTimestamp.h" +#include "SignedTreeHead.h" + +namespace mozilla { namespace ct { + +using namespace mozilla::pkix; + +// The following test vectors are from the CT test data repository at +// https://github.com/google/certificate-transparency/tree/master/test/testdata + +// test-cert.pem +const char kDefaultDerCert[] = + "308202ca30820233a003020102020106300d06092a864886f70d01010505003055310b3009" + "06035504061302474231243022060355040a131b4365727469666963617465205472616e73" + "706172656e6379204341310e300c0603550408130557616c65733110300e06035504071307" + "4572772057656e301e170d3132303630313030303030305a170d3232303630313030303030" + "305a3052310b30090603550406130247423121301f060355040a1318436572746966696361" + "7465205472616e73706172656e6379310e300c0603550408130557616c65733110300e0603" + "55040713074572772057656e30819f300d06092a864886f70d010101050003818d00308189" + "02818100b1fa37936111f8792da2081c3fe41925008531dc7f2c657bd9e1de4704160b4c9f" + "19d54ada4470404c1c51341b8f1f7538dddd28d9aca48369fc5646ddcc7617f8168aae5b41" + "d43331fca2dadfc804d57208949061f9eef902ca47ce88c644e000f06eeeccabdc9dd2f68a" + "22ccb09dc76e0dbc73527765b1a37a8c676253dcc10203010001a381ac3081a9301d060355" + "1d0e041604146a0d982a3b62c44b6d2ef4e9bb7a01aa9cb798e2307d0603551d2304763074" + "80145f9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603550406" + "1302474231243022060355040a131b4365727469666963617465205472616e73706172656e" + "6379204341310e300c0603550408130557616c65733110300e060355040713074572772057" + "656e82010030090603551d1304023000300d06092a864886f70d010105050003818100171c" + "d84aac414a9a030f22aac8f688b081b2709b848b4e5511406cd707fed028597a9faefc2eee" + "2978d633aaac14ed3235197da87e0f71b8875f1ac9e78b281749ddedd007e3ecf50645f8cb" + "f667256cd6a1647b5e13203bb8582de7d6696f656d1c60b95f456b7fcf338571908f1c6972" + "7d24c4fccd249295795814d1dac0e6"; + +// key hash of test-cert.pem's issuer (ca-cert.pem) +const char kDefaultIssuerKeyHash[] = + "02adddca08b8bf9861f035940c940156d8350fdff899a6239c6bd77255b8f8fc"; + +const char kDefaultDerTbsCert[] = + "30820233a003020102020107300d06092a864886f70d01010505003055310b300906035504" + "061302474231243022060355040a131b4365727469666963617465205472616e7370617265" + "6e6379204341310e300c0603550408130557616c65733110300e0603550407130745727720" + "57656e301e170d3132303630313030303030305a170d3232303630313030303030305a3052" + "310b30090603550406130247423121301f060355040a131843657274696669636174652054" + "72616e73706172656e6379310e300c0603550408130557616c65733110300e060355040713" + "074572772057656e30819f300d06092a864886f70d010101050003818d0030818902818100" + "beef98e7c26877ae385f75325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee4" + "58bd7db86f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb41cd0e72805a410" + "cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39eed0b88119dc154dc68f7da8e30caf" + "158a33e6c9509f4a05b01409ff5dd87eb50203010001a381ac3081a9301d0603551d0e0416" + "04142031541af25c05ffd8658b6843794f5e9036f7b4307d0603551d230476307480145f9d" + "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b3009060355040613024742" + "31243022060355040a131b4365727469666963617465205472616e73706172656e63792043" + "41310e300c0603550408130557616c65733110300e060355040713074572772057656e8201" + "0030090603551d1304023000"; + +// DigitallySigned of test-cert.proof +const char kTestDigitallySigned[] = + "0403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c208dfbfe9ef53" + "6cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc45689a2c0187ef5" + "a5"; + +// test-cert.proof +const char kTestSignedCertificateTimestamp[] = + "00df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d7640000013d" + "db27ded900000403004730450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c2" + "08dfbfe9ef536cf7f2022100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc456" + "89a2c0187ef5a5"; + +// ct-server-key-public.pem +const char kEcP256PublicKey[] = + "3059301306072a8648ce3d020106082a8648ce3d0301070342000499783cb14533c0161a5a" + "b45bf95d08a29cd0ea8dd4c84274e2be59ad15c676960cf0afa1074a57ac644b23479e5b3f" + "b7b245eb4b420ef370210371a944beaceb"; + +// key id (sha256) of ct-server-key-public.pem +const char kTestKeyId[] = + "df1c2ec11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d764"; + +// signature field of DigitallySigned from test-cert.proof +const char kTestSCTSignatureData[] = + "30450220606e10ae5c2d5a1b0aed49dc4937f48de71a4e9784e9c208dfbfe9ef536cf7f202" + "2100beb29c72d7d06d61d06bdb38a069469aa86fe12e18bb7cc45689a2c0187ef5a5"; + +// signature field of DigitallySigned from test-embedded-pre-cert.proof +const char kTestSCTPrecertSignatureData[] = + "30450220482f6751af35dba65436be1fd6640f3dbf9a41429495924530288fa3e5e23e0602" + "2100e4edc0db3ac572b1e2f5e8ab6a680653987dcf41027dfeffa105519d89edbf08"; + +// For the sample STH +const char kSampleSTHSHA256RootHash[] = + "726467216167397babca293dca398e4ce6b621b18b9bc42f30c900d1f92ac1e4"; +const char kSampleSTHTreeHeadSignature[] = + "0403004730450220365a91a2a88f2b9332f41d8959fa7086da7e6d634b7b089bc9da066426" + "6c7a20022100e38464f3c0fd066257b982074f7ac87655e0c8f714768a050b4be9a7b441cb" + "d3"; +const size_t kSampleSTHTreeSize = 21u; +const uint64_t kSampleSTHTimestamp = 1396877277237u; + +// test-embedded-cert.pem +const char kTestEmbeddedCertData[] = + "30820359308202c2a003020102020107300d06092a864886f70d01010505" + "003055310b300906035504061302474231243022060355040a131b436572" + "7469666963617465205472616e73706172656e6379204341310e300c0603" + "550408130557616c65733110300e060355040713074572772057656e301e" + "170d3132303630313030303030305a170d3232303630313030303030305a" + "3052310b30090603550406130247423121301f060355040a131843657274" + "69666963617465205472616e73706172656e6379310e300c060355040813" + "0557616c65733110300e060355040713074572772057656e30819f300d06" + "092a864886f70d010101050003818d0030818902818100beef98e7c26877" + "ae385f75325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee4" + "58bd7db86f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb4" + "1cd0e72805a410cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39ee" + "d0b88119dc154dc68f7da8e30caf158a33e6c9509f4a05b01409ff5dd87e" + "b50203010001a382013a30820136301d0603551d0e041604142031541af2" + "5c05ffd8658b6843794f5e9036f7b4307d0603551d230476307480145f9d" + "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603" + "5504061302474231243022060355040a131b436572746966696361746520" + "5472616e73706172656e6379204341310e300c0603550408130557616c65" + "733110300e060355040713074572772057656e82010030090603551d1304" + "02300030818a060a2b06010401d679020402047c047a0078007600df1c2e" + "c11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d76400" + "00013ddb27df9300000403004730450220482f6751af35dba65436be1fd6" + "640f3dbf9a41429495924530288fa3e5e23e06022100e4edc0db3ac572b1" + "e2f5e8ab6a680653987dcf41027dfeffa105519d89edbf08300d06092a86" + "4886f70d0101050500038181008a0c4bef099d479279afa0a28e689f91e1" + "c4421be2d269a2ea6ca4e8215ddeddca1504a11e7c87c4b77e80f0e97903" + "5268f27ca20e166804ae556f316981f96a394ab7abfd3e255ac0044513fe" + "76570c6795abe4703133d303f89f3afa6bbcfc517319dfd95b934241211f" + "634035c3d078307a68c6075a2e20c89f36b8910ca0"; + +const char kTestTbsCertData[] = + "30820233a003020102020107300d06092a864886f70d0101050500305531" + "0b300906035504061302474231243022060355040a131b43657274696669" + "63617465205472616e73706172656e6379204341310e300c060355040813" + "0557616c65733110300e060355040713074572772057656e301e170d3132" + "303630313030303030305a170d3232303630313030303030305a3052310b" + "30090603550406130247423121301f060355040a13184365727469666963" + "617465205472616e73706172656e6379310e300c0603550408130557616c" + "65733110300e060355040713074572772057656e30819f300d06092a8648" + "86f70d010101050003818d0030818902818100beef98e7c26877ae385f75" + "325a0c1d329bedf18faaf4d796bf047eb7e1ce15c95ba2f80ee458bd7db8" + "6f8a4b252191a79bd700c38e9c0389b45cd4dc9a120ab21e0cb41cd0e728" + "05a410cd9c5bdb5d4927726daf1710f60187377ea25b1a1e39eed0b88119" + "dc154dc68f7da8e30caf158a33e6c9509f4a05b01409ff5dd87eb5020301" + "0001a381ac3081a9301d0603551d0e041604142031541af25c05ffd8658b" + "6843794f5e9036f7b4307d0603551d230476307480145f9d880dc873e654" + "d4f80dd8e6b0c124b447c355a159a4573055310b30090603550406130247" + "4231243022060355040a131b4365727469666963617465205472616e7370" + "6172656e6379204341310e300c0603550408130557616c65733110300e06" + "0355040713074572772057656e82010030090603551d1304023000"; + +// test-embedded-with-preca-cert.pem +const char kTestEmbeddedWithPreCaCertData[] = + "30820359308202c2a003020102020108300d06092a864886f70d01010505" + "003055310b300906035504061302474231243022060355040a131b436572" + "7469666963617465205472616e73706172656e6379204341310e300c0603" + "550408130557616c65733110300e060355040713074572772057656e301e" + "170d3132303630313030303030305a170d3232303630313030303030305a" + "3052310b30090603550406130247423121301f060355040a131843657274" + "69666963617465205472616e73706172656e6379310e300c060355040813" + "0557616c65733110300e060355040713074572772057656e30819f300d06" + "092a864886f70d010101050003818d0030818902818100afaeeacac51ab7" + "cebdf9eacae7dd175295e193955a17989aef8d97ab7cdff7761093c0b823" + "d2a4e3a51a17b86f28162b66a2538935ebecdc1036233da2dd6531b0c63b" + "cc68761ebdc854037b77399246b870a7b72b14c9b1667de09a9640ed9f3f" + "3c725d950b4d26559869fe7f1e919a66eb76d35c0117c6bcd0d8cfd21028" + "b10203010001a382013a30820136301d0603551d0e04160414612c64efac" + "79b728397c9d93e6df86465fa76a88307d0603551d230476307480145f9d" + "880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b30090603" + "5504061302474231243022060355040a131b436572746966696361746520" + "5472616e73706172656e6379204341310e300c0603550408130557616c65" + "733110300e060355040713074572772057656e82010030090603551d1304" + "02300030818a060a2b06010401d679020402047c047a0078007600df1c2e" + "c11500945247a96168325ddc5c7959e8f7c6d388fc002e0bbd3f74d76400" + "00013ddb27e05b000004030047304502207aa79604c47480f3727b084f90" + "b3989f79091885e00484431a2a297cbf3a355c022100b49fd8120b0d644c" + "d7e75269b4da6317a9356cb950224fc11cc296b2e39b2386300d06092a86" + "4886f70d010105050003818100a3a86c41ad0088a25aedc4e7b529a2ddbf" + "9e187ffb362157e9302d961b73b43cba0ae1e230d9e45049b7e8c924792e" + "bbe7d175baa87b170dfad8ee788984599d05257994084e2e0e796fca5836" + "881c3e053553e06ab230f919089b914e4a8e2da45f8a87f2c81a25a61f04" + "fe1cace60155653827d41fad9f0658f287d058192c"; + +// ca-cert.pem +const char kCaCertData[] = + "308202d030820239a003020102020100300d06092a864886f70d01010505" + "003055310b300906035504061302474231243022060355040a131b436572" + "7469666963617465205472616e73706172656e6379204341310e300c0603" + "550408130557616c65733110300e060355040713074572772057656e301e" + "170d3132303630313030303030305a170d3232303630313030303030305a" + "3055310b300906035504061302474231243022060355040a131b43657274" + "69666963617465205472616e73706172656e6379204341310e300c060355" + "0408130557616c65733110300e060355040713074572772057656e30819f" + "300d06092a864886f70d010101050003818d0030818902818100d58a6853" + "6210a27119936e778321181c2a4013c6d07b8c76eb9157d3d0fb4b3b516e" + "cecbd1c98d91c52f743fab635d55099cd13abaf31ae541442451a74c7816" + "f2243cf848cf2831cce67ba04a5a23819f3cba37e624d9c3bdb299b839dd" + "fe2631d2cb3a84fc7bb2b5c52fcfc14fff406f5cd44669cbb2f7cfdf86fb" + "6ab9d1b10203010001a381af3081ac301d0603551d0e041604145f9d880d" + "c873e654d4f80dd8e6b0c124b447c355307d0603551d230476307480145f" + "9d880dc873e654d4f80dd8e6b0c124b447c355a159a4573055310b300906" + "035504061302474231243022060355040a131b4365727469666963617465" + "205472616e73706172656e6379204341310e300c0603550408130557616c" + "65733110300e060355040713074572772057656e820100300c0603551d13" + "040530030101ff300d06092a864886f70d0101050500038181000608cc4a" + "6d64f2205e146c04b276f92b0efa94a5daf23afc3806606d3990d0a1ea23" + "3d40295769463b046661e7fa1d179915209aea2e0a775176411227d7c003" + "07c7470e61584fd7334224727f51d690bc47a9df354db0f6eb25955de189" + "3c4dd5202b24a2f3e440d274b54e1bd376269ca96289b76ecaa41090e14f" + "3b0a942e"; + +// intermediate-cert.pem +const char kIntermediateCertData[] = + "308202dd30820246a003020102020109300d06092a864886f70d01010505" + "003055310b300906035504061302474231243022060355040a131b436572" + "7469666963617465205472616e73706172656e6379204341310e300c0603" + "550408130557616c65733110300e060355040713074572772057656e301e" + "170d3132303630313030303030305a170d3232303630313030303030305a" + "3062310b30090603550406130247423131302f060355040a132843657274" + "69666963617465205472616e73706172656e637920496e7465726d656469" + "617465204341310e300c0603550408130557616c65733110300e06035504" + "0713074572772057656e30819f300d06092a864886f70d01010105000381" + "8d0030818902818100d76a678d116f522e55ff821c90642508b7074b14d7" + "71159064f7927efdedb87135a1365ee7de18cbd5ce865f860c78f433b4d0" + "d3d3407702e7a3ef542b1dfe9bbaa7cdf94dc5975fc729f86f105f381b24" + "3535cf9c800f5ca780c1d3c84400ee65d16ee9cf52db8adffe50f5c49335" + "0b2190bf50d5bc36f3cac5a8daae92cd8b0203010001a381af3081ac301d" + "0603551d0e04160414965508050278479e8773764131bc143a47e229ab30" + "7d0603551d230476307480145f9d880dc873e654d4f80dd8e6b0c124b447" + "c355a159a4573055310b300906035504061302474231243022060355040a" + "131b4365727469666963617465205472616e73706172656e637920434131" + "0e300c0603550408130557616c65733110300e0603550407130745727720" + "57656e820100300c0603551d13040530030101ff300d06092a864886f70d" + "0101050500038181002206dab1c66b71dce095c3f6aa2ef72cf7761be7ab" + "d7fc39c31a4cfe1bd96d6734ca82f22dde5a0c8bbbdd825d7b6f3e7612ad" + "8db300a7e21169886023262284c3aa5d2191efda10bf9235d37b3a2a340d" + "59419b94a48566f3fac3cd8b53d5a4e98270ead297b07210f9ce4a2138b1" + "8811143b93fa4e7a87dd37e1385f2c2908"; + +// test-embedded-with-intermediate-cert.pem +const char kTestEmbeddedWithIntermediateCertData[] = + "30820366308202cfa003020102020102300d06092a864886f70d01010505" + "003062310b30090603550406130247423131302f060355040a1328436572" + "7469666963617465205472616e73706172656e637920496e7465726d6564" + "69617465204341310e300c0603550408130557616c65733110300e060355" + "040713074572772057656e301e170d3132303630313030303030305a170d" + "3232303630313030303030305a3052310b30090603550406130247423121" + "301f060355040a13184365727469666963617465205472616e7370617265" + "6e6379310e300c0603550408130557616c65733110300e06035504071307" + "4572772057656e30819f300d06092a864886f70d010101050003818d0030" + "818902818100bb272b26e5deb5459d4acca027e8f12a4d839ac3730a6a10" + "9ff7e25498ddbd3f1895d08ba41f8de34967a3a086ce13a90dd5adbb5418" + "4bdc08e1ac7826adb8dc9c717bfd7da5b41b4db1736e00f1dac3cec9819c" + "cb1a28ba120b020a820e940dd61f95b5432a4bc05d0818f18ce2154eb38d" + "2fa7d22d72b976e560db0c7fc77f0203010001a382013a30820136301d06" + "03551d0e04160414b1b148e658e703f5f7f3105f20b3c384d7eff1bf307d" + "0603551d23047630748014965508050278479e8773764131bc143a47e229" + "aba159a4573055310b300906035504061302474231243022060355040a13" + "1b4365727469666963617465205472616e73706172656e6379204341310e" + "300c0603550408130557616c65733110300e060355040713074572772057" + "656e82010930090603551d130402300030818a060a2b06010401d6790204" + "02047c047a0078007600df1c2ec11500945247a96168325ddc5c7959e8f7" + "c6d388fc002e0bbd3f74d7640000013ddb27e2a400000403004730450221" + "00a6d34517f3392d9ec5d257adf1c597dc45bd4cd3b73856c616a9fb99e5" + "ae75a802205e26c8d1c7e222fe8cda29baeb04a834ee97d34fd81718f1aa" + "e0cd66f4b8a93f300d06092a864886f70d0101050500038181000f95a5b4" + "e128a914b1e88be8b32964221b58f4558433d020a8e246cca65a40bcbf5f" + "2d48933ebc99be6927ca756472fb0bdc7f505f41f462f2bc19d0b299c990" + "918df8820f3d31db37979e8bad563b17f00ae67b0f8731c106c943a73bf5" + "36af168afe21ef4adfcae19a3cc074899992bf506bc5ce1decaaf07ffeeb" + "c805c039"; + +// test-embedded-with-intermediate-preca-cert.pem +const char kTestEmbeddedWithIntermediatePreCaCertData[] = + "30820366308202cfa003020102020103300d06092a864886f70d01010505" + "003062310b30090603550406130247423131302f060355040a1328436572" + "7469666963617465205472616e73706172656e637920496e7465726d6564" + "69617465204341310e300c0603550408130557616c65733110300e060355" + "040713074572772057656e301e170d3132303630313030303030305a170d" + "3232303630313030303030305a3052310b30090603550406130247423121" + "301f060355040a13184365727469666963617465205472616e7370617265" + "6e6379310e300c0603550408130557616c65733110300e06035504071307" + "4572772057656e30819f300d06092a864886f70d010101050003818d0030" + "818902818100d4497056cdfc65e1342cc3df6e654b8af0104702acd2275c" + "7d3fb1fc438a89b212110d6419bcc13ae47d64bba241e6706b9ed627f8b3" + "4a0d7dff1c44b96287c54bea9d10dc017bceb64f7b6aff3c35a474afec40" + "38ab3640b0cd1fb0582ec03b179a2776c8c435d14ab4882d59d7b724fa37" + "7ca6db08392173f9c6056b3abadf0203010001a382013a30820136301d06" + "03551d0e0416041432da5518d87f1d26ea2767973c0bef286e786a4a307d" + "0603551d23047630748014965508050278479e8773764131bc143a47e229" + "aba159a4573055310b300906035504061302474231243022060355040a13" + "1b4365727469666963617465205472616e73706172656e6379204341310e" + "300c0603550408130557616c65733110300e060355040713074572772057" + "656e82010930090603551d130402300030818a060a2b06010401d6790204" + "02047c047a0078007600df1c2ec11500945247a96168325ddc5c7959e8f7" + "c6d388fc002e0bbd3f74d7640000013ddb27e3be00000403004730450221" + "00d9f61a07fee021e3159f3ca2f570d833ff01374b2096cba5658c5e16fb" + "43eb3002200b76fe475138d8cf76833831304dabf043eb1213c96e13ff4f" + "a37f7cd3c8dc1f300d06092a864886f70d01010505000381810088ee4e9e" + "5eed6b112cc764b151ed929400e9406789c15fbbcfcdab2f10b400234139" + "e6ce65c1e51b47bf7c8950f80bccd57168567954ed35b0ce9346065a5eae" + "5bf95d41da8e27cee9eeac688f4bd343f9c2888327abd8b9f68dcb1e3050" + "041d31bda8e2dd6d39b3664de5ce0870f5fc7e6a00d6ed00528458d953d2" + "37586d73"; + +static uint8_t +CharToByte(char c) +{ + if (c >= '0' && c <= '9') { + return c - '0'; + } else if (c >= 'a' && c <= 'f') { + return c - 'a' + 10; + } else if (c >= 'A' && c <= 'F') { + return c - 'A' + 10; + } + MOZ_RELEASE_ASSERT(false); + return 0; +} + +static Buffer +HexToBytes(const char* hexData) +{ + size_t hexLen = strlen(hexData); + MOZ_RELEASE_ASSERT(hexLen > 0 && (hexLen % 2 == 0)); + size_t resultLen = hexLen / 2; + Buffer result; + MOZ_RELEASE_ASSERT(result.reserve(resultLen)); + for (size_t i = 0; i < resultLen; ++i) { + uint8_t hi = CharToByte(hexData[i*2]); + uint8_t lo = CharToByte(hexData[i*2 + 1]); + result.infallibleAppend((hi << 4) | lo); + } + return result; +} + + +void +GetX509CertLogEntry(LogEntry& entry) +{ + entry.Reset(); + entry.type = ct::LogEntry::Type::X509; + entry.leafCertificate = HexToBytes(kDefaultDerCert); +} + +Buffer +GetDEREncodedX509Cert() +{ + return HexToBytes(kDefaultDerCert); +} + +void +GetPrecertLogEntry(LogEntry& entry) +{ + entry.Reset(); + entry.type = ct::LogEntry::Type::Precert; + entry.issuerKeyHash = HexToBytes(kDefaultIssuerKeyHash); + entry.tbsCertificate = HexToBytes(kDefaultDerTbsCert); +} + +Buffer +GetTestDigitallySigned() +{ + return HexToBytes(kTestDigitallySigned); +} + +Buffer +GetTestDigitallySignedData() +{ + Buffer encoded = GetTestDigitallySigned(); + // The encoded buffer contains the signature data itself from the 4th byte. + // The first bytes are: + // 1 byte of hash algorithm + // 1 byte of signature algorithm + // 2 bytes - prefix containing length of the signature data. + Buffer result; + MOZ_RELEASE_ASSERT(result.append(encoded.begin() + 4, encoded.end())); + return result; +} + +Buffer +GetTestSignedCertificateTimestamp() +{ + return HexToBytes(kTestSignedCertificateTimestamp); +} + +Buffer +GetTestPublicKey() +{ + return HexToBytes(kEcP256PublicKey); +} + +Buffer +GetTestPublicKeyId() +{ + return HexToBytes(kTestKeyId); +} + +void +GetX509CertSCT(SignedCertificateTimestamp& sct) +{ + sct.version = ct::SignedCertificateTimestamp::Version::V1; + sct.logId = HexToBytes(kTestKeyId); + // Time the log issued a SCT for this certificate, which is + // Fri Apr 5 10:04:16.089 2013 + sct.timestamp = INT64_C(1365181456089); + sct.extensions.clear(); + + sct.signature.hashAlgorithm = + ct::DigitallySigned::HashAlgorithm::SHA256; + sct.signature.signatureAlgorithm = + ct::DigitallySigned::SignatureAlgorithm::ECDSA; + sct.signature.signatureData = HexToBytes(kTestSCTSignatureData); +} + +void +GetPrecertSCT(SignedCertificateTimestamp& sct) +{ + sct.version = ct::SignedCertificateTimestamp::Version::V1; + sct.logId = HexToBytes(kTestKeyId); + // Time the log issued a SCT for this Precertificate, which is + // Fri Apr 5 10:04:16.275 2013 + sct.timestamp = INT64_C(1365181456275); + sct.extensions.clear(); + + sct.signature.hashAlgorithm = + ct::DigitallySigned::HashAlgorithm::SHA256; + sct.signature.signatureAlgorithm = + ct::DigitallySigned::SignatureAlgorithm::ECDSA; + sct.signature.signatureData = HexToBytes(kTestSCTPrecertSignatureData); +} + +Buffer +GetDefaultIssuerKeyHash() +{ + return HexToBytes(kDefaultIssuerKeyHash); +} + +// A sample, valid STH +void +GetSampleSignedTreeHead(SignedTreeHead& sth) +{ + sth.version = SignedTreeHead::Version::V1; + sth.timestamp = kSampleSTHTimestamp; + sth.treeSize = kSampleSTHTreeSize; + sth.sha256RootHash = GetSampleSTHSHA256RootHash(); + GetSampleSTHTreeHeadDecodedSignature(sth.signature); +} + +Buffer +GetSampleSTHSHA256RootHash() +{ + return HexToBytes(kSampleSTHSHA256RootHash); +} + +Buffer +GetSampleSTHTreeHeadSignature() +{ + return HexToBytes(kSampleSTHTreeHeadSignature); +} + +void +GetSampleSTHTreeHeadDecodedSignature(DigitallySigned& signature) +{ + Buffer ths = HexToBytes(kSampleSTHTreeHeadSignature); + Input thsInput; + ASSERT_EQ(Success, thsInput.Init(ths.begin(), ths.length())); + Reader thsReader(thsInput); + ASSERT_EQ(Success, DecodeDigitallySigned(thsReader, signature)); + ASSERT_TRUE(thsReader.AtEnd()); +} + +Buffer +GetDEREncodedTestEmbeddedCert() +{ + return HexToBytes(kTestEmbeddedCertData); +} + +Buffer +GetDEREncodedTestTbsCert() +{ + return HexToBytes(kTestTbsCertData); +} + +Buffer +GetDEREncodedTestEmbeddedWithPreCACert() +{ + return HexToBytes(kTestEmbeddedWithPreCaCertData); +} + +Buffer +GetDEREncodedCACert() +{ + return HexToBytes(kCaCertData); +} + +Buffer +GetDEREncodedIntermediateCert() +{ + return HexToBytes(kIntermediateCertData); +} + +Buffer +GetDEREncodedTestEmbeddedWithIntermediateCert() +{ + return HexToBytes(kTestEmbeddedWithIntermediateCertData); +} + +Buffer +GetDEREncodedTestEmbeddedWithIntermediatePreCACert() +{ + return HexToBytes(kTestEmbeddedWithIntermediatePreCaCertData); +} + +Buffer +ExtractCertSPKI(Input cert) +{ + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + MOZ_RELEASE_ASSERT(backCert.Init() == Success); + + Input spkiInput = backCert.GetSubjectPublicKeyInfo(); + Buffer spki; + MOZ_RELEASE_ASSERT(InputToBuffer(spkiInput, spki) == Success); + return spki; +} + +Buffer +ExtractCertSPKI(const Buffer& cert) +{ + return ExtractCertSPKI(InputForBuffer(cert)); +} + +void +ExtractEmbeddedSCTList(Input cert, Buffer& result) +{ + result.clear(); + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + ASSERT_EQ(Success, backCert.Init()); + const Input* scts = backCert.GetSignedCertificateTimestamps(); + if (scts) { + Input sctList; + ASSERT_EQ(Success, + ExtractSignedCertificateTimestampListFromExtension(*scts, + sctList)); + ASSERT_EQ(Success, InputToBuffer(sctList, result)); + } +} + +void +ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result) +{ + ExtractEmbeddedSCTList(InputForBuffer(cert), result); +} + +class OCSPExtensionTrustDomain : public TrustDomain +{ +public: + Result GetCertTrust(EndEntityOrCA, const CertPolicyId&, + Input, TrustLevel&) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result FindIssuer(Input, IssuerChecker&, Time) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result CheckRevocation(EndEntityOrCA, const CertID&, Time, Duration, + const Input*, const Input*) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result IsChainValid(const DERArray&, Time) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result DigestBuf(Input item, DigestAlgorithm digestAlg, + /*out*/ uint8_t* digestBuf, size_t digestBufLen) override + { + return DigestBufNSS(item, digestAlg, digestBuf, digestBufLen); + } + + Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time) + override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result VerifyECDSASignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return VerifyECDSASignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA, unsigned int) + override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result VerifyRSAPKCS1SignedDigest(const SignedDigest& signedDigest, + Input subjectPublicKeyInfo) override + { + return VerifyRSAPKCS1SignedDigestNSS(signedDigest, subjectPublicKeyInfo, + nullptr); + } + + Result CheckValidityIsAcceptable(Time, Time, EndEntityOrCA, KeyPurposeId) + override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + Result NetscapeStepUpMatchesServerAuth(Time, bool&) override + { + ADD_FAILURE(); + return Result::FATAL_ERROR_LIBRARY_FAILURE; + } + + void NoteAuxiliaryExtension(AuxiliaryExtension extension, Input data) override + { + if (extension != AuxiliaryExtension::SCTListFromOCSPResponse) { + ADD_FAILURE(); + return; + } + if (InputToBuffer(data, signedCertificateTimestamps) != Success) { + ADD_FAILURE(); + return; + } + } + + Buffer signedCertificateTimestamps; +}; + +void +ExtractSCTListFromOCSPResponse(Input cert, + Input issuerSPKI, + Input encodedResponse, + Time time, + Buffer& result) +{ + result.clear(); + + BackCert backCert(cert, EndEntityOrCA::MustBeEndEntity, nullptr); + ASSERT_EQ(Success, backCert.Init()); + + CertID certID(backCert.GetIssuer(), issuerSPKI, backCert.GetSerialNumber()); + + bool expired; + OCSPExtensionTrustDomain trustDomain; + Result rv = VerifyEncodedOCSPResponse(trustDomain, certID, + time, /*time*/ + 1000, /*maxLifetimeInDays*/ + encodedResponse, expired); + ASSERT_EQ(Success, rv); + + result = Move(trustDomain.signedCertificateTimestamps); +} + +Buffer +cloneBuffer(const Buffer& buffer) +{ + Buffer cloned; + MOZ_RELEASE_ASSERT(cloned.appendAll(buffer)); + return cloned; +} + +Input +InputForBuffer(const Buffer& buffer) +{ + Input input; + MOZ_RELEASE_ASSERT(Success == + input.Init(buffer.begin(), buffer.length())); + return input; +} + +Input InputForSECItem(const SECItem& item) +{ + Input input; + MOZ_RELEASE_ASSERT(Success == + input.Init(item.data, item.len)); + return input; +} + +} } // namespace mozilla::ct + +namespace mozilla { + +std::ostream& +operator<<(std::ostream& stream, const ct::Buffer& buffer) +{ + if (buffer.empty()) { + stream << "EMPTY"; + } else { + for (size_t i = 0; i < buffer.length(); ++i) { + if (i >= 1000) { + stream << "..."; + break; + } + stream << std::hex << std::setw(2) << std::setfill('0') + << static_cast<unsigned>(buffer[i]); + } + } + stream << std::dec; + return stream; +} + +} // namespace mozilla diff --git a/security/certverifier/tests/gtest/CTTestUtils.h b/security/certverifier/tests/gtest/CTTestUtils.h new file mode 100644 index 000000000..447d6bb1c --- /dev/null +++ b/security/certverifier/tests/gtest/CTTestUtils.h @@ -0,0 +1,132 @@ +/* -*- 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/. */ + +#ifndef CTTestUtils_h +#define CTTestUtils_h + +#include <iostream> + +#include "pkix/Input.h" +#include "pkix/Time.h" +#include "seccomon.h" +#include "SignedCertificateTimestamp.h" +#include "SignedTreeHead.h" + +namespace mozilla { namespace ct { + +// Note: unless specified otherwise, all test data is taken from +// Certificate Transparency test data repository at +// https://github.com/google/certificate-transparency/tree/master/test/testdata + +// Fills |entry| with test data for an X.509 entry. +void GetX509CertLogEntry(LogEntry& entry); + +// Returns a DER-encoded X509 cert. The SCT provided by +// GetX509CertSCT is signed over this certificate. +Buffer GetDEREncodedX509Cert(); + +// Fills |entry| with test data for a Precertificate entry. +void GetPrecertLogEntry(LogEntry& entry); + +// Returns the binary representation of a test DigitallySigned. +Buffer GetTestDigitallySigned(); + +// Returns the source data of the test DigitallySigned. +Buffer GetTestDigitallySignedData(); + +// Returns the binary representation of a test serialized SCT. +Buffer GetTestSignedCertificateTimestamp(); + +// Test log key. +Buffer GetTestPublicKey(); + +// ID of test log key. +Buffer GetTestPublicKeyId(); + +// SCT for the X509Certificate provided above. +void GetX509CertSCT(SignedCertificateTimestamp& sct); + +// SCT for the Precertificate log entry provided above. +void GetPrecertSCT(SignedCertificateTimestamp& sct); + +// Issuer key hash. +Buffer GetDefaultIssuerKeyHash(); + +// A sample, valid STH. +void GetSampleSignedTreeHead(SignedTreeHead& sth); + +// The SHA256 root hash for the sample STH. +Buffer GetSampleSTHSHA256RootHash(); + +// The tree head signature for the sample STH. +Buffer GetSampleSTHTreeHeadSignature(); + +// The same signature as GetSampleSTHTreeHeadSignature, decoded. +void GetSampleSTHTreeHeadDecodedSignature(DigitallySigned& signature); + +// Certificate with embedded SCT in an X509v3 extension. +Buffer GetDEREncodedTestEmbeddedCert(); + +// For the above certificate, the corresponsing TBSCertificate without +// the embedded SCT extension. +Buffer GetDEREncodedTestTbsCert(); + +// As above, but signed with an intermediate CA certificate containing +// the CT extended key usage OID 1.3.6.1.4.1.11129.2.4.4 for issuing precerts +// (i.e. signed with a "precert CA certificate"). +Buffer GetDEREncodedTestEmbeddedWithPreCACert(); + +// The issuer of the above certificates (self-signed root CA certificate). +Buffer GetDEREncodedCACert(); + +// An intermediate CA certificate issued by the above CA. +Buffer GetDEREncodedIntermediateCert(); + +// Certificate with embedded SCT signed by the intermediate certificate above. +Buffer GetDEREncodedTestEmbeddedWithIntermediateCert(); + +// As above, but signed by the precert CA certificate. +Buffer GetDEREncodedTestEmbeddedWithIntermediatePreCACert(); + +// Given a DER-encoded certificate, returns its SubjectPublicKeyInfo. +Buffer ExtractCertSPKI(pkix::Input cert); +Buffer ExtractCertSPKI(const Buffer& cert); + +// Extracts a SignedCertificateTimestampList from the provided leaf certificate +// (kept in X.509v3 extension with OID 1.3.6.1.4.1.11129.2.4.2). +void ExtractEmbeddedSCTList(pkix::Input cert, Buffer& result); +void ExtractEmbeddedSCTList(const Buffer& cert, Buffer& result); + +// Extracts a SignedCertificateTimestampList that has been embedded within +// an OCSP response as an extension with the OID 1.3.6.1.4.1.11129.2.4.5. +// The OCSP response is verified, and the verification must succeed for the +// extension to be extracted. +void ExtractSCTListFromOCSPResponse(pkix::Input cert, + pkix::Input issuerSPKI, + pkix::Input encodedResponse, + pkix::Time time, + Buffer& result); + +// We need this in tests code since mozilla::Vector only allows move assignment. +Buffer cloneBuffer(const Buffer& buffer); + +// Returns Input for the data stored in the buffer, failing assertion on error. +pkix::Input InputForBuffer(const Buffer& buffer); + +// Returns Input for the data stored in the item, failing assertion on error. +pkix::Input InputForSECItem(const SECItem& item); + +} } // namespace mozilla::ct + + +namespace mozilla { + +// GTest needs this to be in Buffer's namespace (i.e. in mozilla::Vector's). +std::ostream& operator<<(std::ostream& stream, const ct::Buffer& buf); + +} // namespace mozilla + +#endif // CTTestUtils_h diff --git a/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp b/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp new file mode 100644 index 000000000..f8f04804e --- /dev/null +++ b/security/certverifier/tests/gtest/MultiLogCTVerifierTest.cpp @@ -0,0 +1,233 @@ +/* -*- 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 <stdint.h> + +#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<Input> 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<SignedCertificateTimestamp::Origin> 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 diff --git a/security/certverifier/tests/gtest/moz.build b/security/certverifier/tests/gtest/moz.build new file mode 100644 index 000000000..c701e7eec --- /dev/null +++ b/security/certverifier/tests/gtest/moz.build @@ -0,0 +1,21 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SOURCES += [ + 'CTLogVerifierTest.cpp', + 'CTObjectsExtractorTest.cpp', + 'CTSerializationTest.cpp', + 'CTTestUtils.cpp', + 'MultiLogCTVerifierTest.cpp', +] + +LOCAL_INCLUDES += [ + '/security/certverifier', + '/security/pkix/include', + '/security/pkix/lib', +] + +FINAL_LIBRARY = 'xul-gtest' |