summaryrefslogtreecommitdiffstats
path: root/security/ct/tests/gtest/BTSignedTreeHeadTest.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/ct/tests/gtest/BTSignedTreeHeadTest.cpp266
1 files changed, 266 insertions, 0 deletions
diff --git a/security/ct/tests/gtest/BTSignedTreeHeadTest.cpp b/security/ct/tests/gtest/BTSignedTreeHeadTest.cpp
new file mode 100644
index 0000000000..580d6a84f3
--- /dev/null
+++ b/security/ct/tests/gtest/BTSignedTreeHeadTest.cpp
@@ -0,0 +1,266 @@
+/* -*- 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 "BTVerifier.h"
+#include "CTTestUtils.h"
+#include "gtest/gtest.h"
+
+#include "nss.h"
+
+namespace mozilla {
+namespace ct {
+
+using namespace pkix;
+
+struct BTSignedTreeHeadTestParams {
+ const char* mSubjectPublicKeyInfoHex;
+ pkix::DigestAlgorithm mDigestAlgorithm;
+ pkix::der::PublicKeyAlgorithm mPublicKeyAlgorithm;
+ const char* mSignedTreeHeadHex;
+
+ pkix::Result mExpectedSignedTreeHeadResult;
+ uint64_t mExpectedTimestamp;
+ uint64_t mExpectedTreeSize;
+ const char* mExpectedRootHashHex;
+};
+
+class BTSignedTreeHeadTest
+ : public ::testing::Test,
+ public ::testing::WithParamInterface<BTSignedTreeHeadTestParams> {
+ void SetUp() override {
+ if (!NSS_IsInitialized()) {
+ if (NSS_NoDB_Init(nullptr) != SECSuccess) {
+ abort();
+ }
+ }
+ }
+};
+
+namespace ValidSTH {
+#include "valid-sth.inc"
+}
+namespace ValidWithExtensionSTH {
+#include "valid-with-extension-sth.inc"
+}
+namespace ValidSecp521r1SHA512STH {
+#include "valid-secp521r1-sha512-sth.inc"
+}
+namespace SignatureCoversLogIDSTH {
+#include "signature-covers-log-id-sth.inc"
+}
+namespace WrongSPKISTH {
+#include "wrong-spki-sth.inc"
+}
+namespace WrongSigningKeySTH {
+#include "wrong-signing-key-sth.inc"
+}
+namespace MissingLogIDSTH {
+#include "missing-log-id-sth.inc"
+}
+namespace MissingTimestampSTH {
+#include "missing-timestamp-sth.inc"
+}
+namespace MissingTreeSizeSTH {
+#include "missing-tree-size-sth.inc"
+}
+namespace MissingRootHashSTH {
+#include "missing-root-hash-sth.inc"
+}
+namespace MissingExtensionsSTH {
+#include "missing-extensions-sth.inc"
+}
+namespace TruncatedLogIDSTH {
+#include "truncated-log-id-sth.inc"
+}
+namespace TruncatedTimestampSTH {
+#include "truncated-timestamp-sth.inc"
+}
+namespace TruncatedTreeSizeSTH {
+#include "truncated-tree-size-sth.inc"
+}
+namespace TruncatedRootHashSTH {
+#include "truncated-root-hash-sth.inc"
+}
+namespace TruncatedExtensionSTH {
+#include "truncated-extension-sth.inc"
+}
+namespace RSASignerRSASPKISTH {
+#include "rsa-signer-rsa-spki-sth.inc"
+}
+namespace RSASignerECSPKISTH {
+#include "rsa-signer-ec-spki-sth.inc"
+}
+namespace ECSignerRSASPKISTH {
+#include "ec-signer-rsa-spki-sth.inc"
+}
+
+static const char* kValidRootHashHex =
+ "d1a0d3947db4ae8305f2ac32985957e02659b2ea3c10da52a48d2526e9af3bbc";
+
+static const char* kValidRootHashSHA512Hex =
+ "374d794a95cdcfd8b35993185fef9ba368f160d8daf432d08ba9f1ed1e5abe6c"
+ "c69291e0fa2fe0006a52570ef18c19def4e617c33ce52ef0a6e5fbe318cb0387";
+
+static const BTSignedTreeHeadTestParams BT_SIGNED_TREE_HEAD_TEST_PARAMS[] = {
+ {ValidSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidSTH::kSTHHex, Success,
+ 1541189938000, 7, kValidRootHashHex},
+ {ValidSTH::kSPKIHex, pkix::DigestAlgorithm::sha512,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidSTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {ValidSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::RSA_PKCS1, ValidSTH::kSTHHex,
+ Result::FATAL_ERROR_INVALID_ARGS, 0, 0, nullptr},
+ {ValidWithExtensionSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidWithExtensionSTH::kSTHHex,
+ Success, 1541189938000, 7, kValidRootHashHex},
+ {ValidSecp521r1SHA512STH::kSPKIHex, pkix::DigestAlgorithm::sha512,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidSecp521r1SHA512STH::kSTHHex,
+ Success, 1542136309473, 731393445, kValidRootHashSHA512Hex},
+ {ValidSecp521r1SHA512STH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidSecp521r1SHA512STH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {ValidSTH::kSPKIHex, pkix::DigestAlgorithm::sha512,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ValidSecp521r1SHA512STH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {SignatureCoversLogIDSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, SignatureCoversLogIDSTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {WrongSPKISTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, WrongSPKISTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {WrongSigningKeySTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, WrongSigningKeySTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {MissingLogIDSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, MissingLogIDSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {MissingTimestampSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, MissingTimestampSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {MissingTreeSizeSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, MissingTreeSizeSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {MissingRootHashSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, MissingRootHashSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {MissingExtensionsSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, MissingExtensionsSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {TruncatedLogIDSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, TruncatedLogIDSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {TruncatedTimestampSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, TruncatedTimestampSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {TruncatedTreeSizeSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, TruncatedTreeSizeSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {TruncatedRootHashSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, TruncatedRootHashSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {TruncatedExtensionSTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, TruncatedExtensionSTH::kSTHHex,
+ Result::ERROR_BAD_DER, 0, 0, nullptr},
+ {RSASignerRSASPKISTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, RSASignerRSASPKISTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {RSASignerECSPKISTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, RSASignerECSPKISTH::kSTHHex,
+ Result::ERROR_BAD_SIGNATURE, 0, 0, nullptr},
+ {ECSignerRSASPKISTH::kSPKIHex, pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA, ECSignerRSASPKISTH::kSTHHex,
+ Result::ERROR_INVALID_KEY, 0, 0, nullptr},
+};
+
+TEST_P(BTSignedTreeHeadTest, BTSignedTreeHeadSimpleTest) {
+ const BTSignedTreeHeadTestParams& params(GetParam());
+
+ Buffer subjectPublicKeyInfoBuffer(
+ HexToBytes(params.mSubjectPublicKeyInfoHex));
+ Input subjectPublicKeyInfoInput = InputForBuffer(subjectPublicKeyInfoBuffer);
+
+ Buffer signedTreeHeadBuffer(HexToBytes(params.mSignedTreeHeadHex));
+ Input signedTreeHeadInput = InputForBuffer(signedTreeHeadBuffer);
+
+ SignedTreeHeadDataV2 sth;
+ EXPECT_EQ(params.mExpectedSignedTreeHeadResult,
+ DecodeAndVerifySignedTreeHead(
+ subjectPublicKeyInfoInput, params.mDigestAlgorithm,
+ params.mPublicKeyAlgorithm, signedTreeHeadInput, sth));
+
+ if (params.mExpectedSignedTreeHeadResult == Success) {
+ EXPECT_EQ(params.mExpectedTimestamp, sth.timestamp);
+ EXPECT_EQ(params.mExpectedTreeSize, sth.treeSize);
+ EXPECT_EQ(HexToBytes(params.mExpectedRootHashHex), sth.rootHash);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(BTSignedTreeHeadTest, BTSignedTreeHeadTest,
+ testing::ValuesIn(BT_SIGNED_TREE_HEAD_TEST_PARAMS));
+
+TEST_F(BTSignedTreeHeadTest, BTSignedTreeHeadTamperedSignatureTest) {
+ Buffer subjectPublicKeyInfoBuffer(HexToBytes(ValidSTH::kSPKIHex));
+ Input subjectPublicKeyInfoInput = InputForBuffer(subjectPublicKeyInfoBuffer);
+
+ Buffer signedTreeHeadBuffer(HexToBytes(ValidSTH::kSTHHex));
+ ASSERT_TRUE(signedTreeHeadBuffer.size() > 15);
+ signedTreeHeadBuffer[signedTreeHeadBuffer.size() - 15] ^= 0xff;
+ Input signedTreeHeadInput = InputForBuffer(signedTreeHeadBuffer);
+
+ SignedTreeHeadDataV2 sth;
+ EXPECT_EQ(Result::ERROR_BAD_SIGNATURE,
+ DecodeAndVerifySignedTreeHead(subjectPublicKeyInfoInput,
+ pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA,
+ signedTreeHeadInput, sth));
+}
+
+TEST_F(BTSignedTreeHeadTest, BTSignedTreeHeadTruncatedSignatureTest) {
+ Buffer subjectPublicKeyInfoBuffer(HexToBytes(ValidSTH::kSPKIHex));
+ Input subjectPublicKeyInfoInput = InputForBuffer(subjectPublicKeyInfoBuffer);
+
+ Buffer signedTreeHeadBuffer(HexToBytes(ValidSTH::kSTHHex));
+ ASSERT_TRUE(signedTreeHeadBuffer.size() > 17);
+ signedTreeHeadBuffer.resize(signedTreeHeadBuffer.size() - 17);
+ Input signedTreeHeadInput = InputForBuffer(signedTreeHeadBuffer);
+
+ SignedTreeHeadDataV2 sth;
+ EXPECT_EQ(Result::ERROR_BAD_DER,
+ DecodeAndVerifySignedTreeHead(subjectPublicKeyInfoInput,
+ pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA,
+ signedTreeHeadInput, sth));
+}
+
+TEST_F(BTSignedTreeHeadTest, BTSignedTreeHeadMissingSignatureTest) {
+ Buffer subjectPublicKeyInfoBuffer(HexToBytes(ValidSTH::kSPKIHex));
+ Input subjectPublicKeyInfoInput = InputForBuffer(subjectPublicKeyInfoBuffer);
+
+ Buffer signedTreeHeadBuffer = {
+ 0x02, 0x00, 0x00,
+ // 1541189938000 milliseconds since the epoch
+ 0x00, 0x00, 0x01, 0x66, 0xd6, 0x14, 0x2b, 0x50, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x07, // 7 total nodes
+ 0x20, // 32 byte hash
+ 0xd1, 0xa0, 0xd3, 0x94, 0x7d, 0xb4, 0xae, 0x83, 0x05, 0xf2, 0xac, 0x32,
+ 0x98, 0x59, 0x57, 0xe0, 0x26, 0x59, 0xb2, 0xea, 0x3c, 0x10, 0xda, 0x52,
+ 0xa4, 0x8d, 0x25, 0x26, 0xe9, 0xaf, 0x3b, 0xbc, 0x00,
+ 0x00, // no extensions
+ // missing signature
+ };
+ Input signedTreeHeadInput = InputForBuffer(signedTreeHeadBuffer);
+
+ SignedTreeHeadDataV2 sth;
+ EXPECT_EQ(Result::ERROR_BAD_DER,
+ DecodeAndVerifySignedTreeHead(subjectPublicKeyInfoInput,
+ pkix::DigestAlgorithm::sha256,
+ pkix::der::PublicKeyAlgorithm::ECDSA,
+ signedTreeHeadInput, sth));
+}
+
+} // namespace ct
+} // namespace mozilla