Bug 1343202 - Utility function for decoding an InclusionProof structure; r=keeler, r?ckerschb
MozReview-Commit-ID: 1x2Cwan8nLL
new file mode 100644
--- /dev/null
+++ b/security/certverifier/BTInclusionProof.h
@@ -0,0 +1,41 @@
+/* -*- 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 BTInclusionProof_h
+#define BTInclusionProof_h
+
+#include "Buffer.h"
+#include "mozilla/Vector.h"
+
+namespace mozilla { namespace ct {
+
+// Represents a Merkle inclusion proof for purposes of serialization,
+// deserialization, and verification of the proof. The format for inclusion
+// proofs in RFC 6962-bis is as follows:
+//
+// opaque LogID<2..127>;
+// opaque NodeHash<32..2^8-1>;
+//
+// struct {
+// LogID log_id;
+// uint64 tree_size;
+// uint64 leaf_index;
+// NodeHash inclusion_path<1..2^16-1>;
+// } InclusionProofDataV2;
+
+const uint64_t kInitialPathLengthCapacity = 32;
+
+struct InclusionProofDataV2
+{
+ Buffer logId;
+ uint64_t treeSize;
+ uint64_t leafIndex;
+ Vector<Buffer, kInitialPathLengthCapacity> inclusionPath;
+};
+
+} } // namespace mozilla:ct
+
+#endif // BTInclusionProof_h
new file mode 100644
--- /dev/null
+++ b/security/certverifier/BTVerifier.cpp
@@ -0,0 +1,104 @@
+/* -*- 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 "CTUtils.h"
+#include "SignedCertificateTimestamp.h"
+
+#include <stdint.h>
+
+#include "mozilla/Assertions.h"
+#include "mozilla/Move.h"
+#include "mozilla/TypeTraits.h"
+
+namespace mozilla { namespace ct {
+
+using namespace mozilla::pkix;
+
+typedef mozilla::pkix::Result Result;
+
+// Members of a Inclusion Proof struct
+static const size_t kLogIdPrefixLengthBytes = 1;
+static const size_t kProofTreeSizeLength = 8;
+static const size_t kLeafIndexLength = 8;
+static const size_t kInclusionPathLengthBytes = 2;
+static const size_t kNodeHashPrefixLengthBytes = 1;
+
+Result
+DecodeInclusionProof(pkix::Reader& reader, InclusionProofDataV2& output)
+{
+ InclusionProofDataV2 result;
+
+ Input logId;
+ Result rv = ReadVariableBytes<kLogIdPrefixLengthBytes>(reader, logId);
+ if (rv != Success) {
+ return rv;
+ }
+
+ rv = ReadUint<kProofTreeSizeLength>(reader, result.treeSize);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (result.treeSize < 1) {
+ return pkix::Result::ERROR_BAD_DER;
+ }
+
+ rv = ReadUint<kLeafIndexLength>(reader, result.leafIndex);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (result.leafIndex >= result.treeSize) {
+ return pkix::Result::ERROR_BAD_DER;
+ }
+
+ Input pathInput;
+ rv = ReadVariableBytes<kInclusionPathLengthBytes>(reader, pathInput);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (pathInput.GetLength() < 1) {
+ return pkix::Result::ERROR_BAD_DER;
+ }
+
+ Reader pathReader(pathInput);
+ Vector<Buffer, kInitialPathLengthCapacity> inclusionPath;
+
+ while (!pathReader.AtEnd()) {
+ Input hash;
+ rv = ReadVariableBytes<kNodeHashPrefixLengthBytes>(pathReader, hash);
+ if (rv != Success) {
+ return rv;
+ }
+
+ Buffer hashBuffer;
+ rv = InputToBuffer(hash, hashBuffer);
+ if (rv != Success) {
+ return rv;
+ }
+
+ if (!inclusionPath.append(Move(hashBuffer))) {
+ return pkix::Result::FATAL_ERROR_NO_MEMORY;
+ }
+ }
+
+ if (!reader.AtEnd()){
+ return pkix::Result::ERROR_BAD_DER;
+ }
+
+ rv = InputToBuffer(logId, result.logId);
+ if (rv != Success) {
+ return rv;
+ }
+
+ result.inclusionPath = Move(inclusionPath);
+
+ output = Move(result);
+ return Success;
+}
+} } //namespace mozilla::ct
new file mode 100644
--- /dev/null
+++ b/security/certverifier/BTVerifier.h
@@ -0,0 +1,23 @@
+/* -*- 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 BTVerifier_h
+#define BTVerifier_h
+
+#include "BTInclusionProof.h"
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+
+namespace mozilla { namespace ct {
+
+// Decodes an Inclusion Proof (InclusionProofDataV2 as defined in RFC
+// 6962-bis). This consumes the entirety of the input.
+pkix::Result DecodeInclusionProof(pkix::Reader& input,
+ InclusionProofDataV2& output);
+
+} } // namespace mozilla::ct
+
+#endif // BTVerifier_h
--- a/security/certverifier/CTSerialization.cpp
+++ b/security/certverifier/CTSerialization.cpp
@@ -1,15 +1,16 @@
/* -*- 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 "CTUtils.h"
#include <stdint.h>
#include "mozilla/Assertions.h"
#include "mozilla/Move.h"
#include "mozilla/TypeTraits.h"
namespace mozilla { namespace ct {
@@ -67,17 +68,17 @@ UncheckedReadUint(size_t length, Reader&
result = (result << 8) | value;
}
out = result;
return Success;
}
// Performs overflow sanity checks and calls UncheckedReadUint.
template <size_t length, typename T>
-static inline Result
+Result
ReadUint(Reader& in, T& out)
{
uint64_t value;
static_assert(mozilla::IsUnsigned<T>::value, "T must be unsigned");
static_assert(length <= 8, "At most 8 byte integers can be read");
static_assert(sizeof(T) >= length, "T must be able to hold <length> bytes");
Result rv = UncheckedReadUint(length, in, value);
if (rv != Success) {
@@ -93,17 +94,17 @@ ReadFixedBytes(size_t length, Reader& in
{
return in.Skip(length, out);
}
// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
// on success. |prefixLength| indicates the number of bytes needed to represent
// the length.
template <size_t prefixLength>
-static inline Result
+Result
ReadVariableBytes(Reader& in, Input& out)
{
size_t length;
Result rv = ReadUint<prefixLength>(in, length);
if (rv != Success) {
return rv;
}
return ReadFixedBytes(length, in, out);
new file mode 100644
--- /dev/null
+++ b/security/certverifier/CTUtils.h
@@ -0,0 +1,30 @@
+/* -*- 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 CTUtils_h
+#define CTUtils_h
+
+#include "pkix/Input.h"
+#include "pkix/Result.h"
+
+namespace mozilla { namespace ct {
+
+// Reads a TLS-encoded variable length unsigned integer from |in|.
+// The integer is expected to be in big-endian order, which is used by TLS.
+// Note: checks if the output parameter overflows while reading.
+// |length| indicates the size (in bytes) of the serialized integer.
+template <size_t length, typename T>
+pkix::Result ReadUint(Reader& in, T& out);
+
+// Reads a length-prefixed variable amount of bytes from |in|, updating |out|
+// on success. |prefixLength| indicates the number of bytes needed to represent
+// the length.
+template <size_t prefixLength>
+pkix::Result ReadVariableBytes(Reader& in, Input& out);
+
+} } // namespace mozilla::ct
+
+#endif //CTUtils_h
--- a/security/certverifier/moz.build
+++ b/security/certverifier/moz.build
@@ -4,28 +4,31 @@
# 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/.
with Files("**"):
BUG_COMPONENT = ("Core", "Security: PSM")
EXPORTS += [
'BRNameMatchingPolicy.h',
+ 'BTInclusionProof.h',
+ 'BTVerifier.h',
'Buffer.h',
'CertVerifier.h',
'CTLog.h',
'CTPolicyEnforcer.h',
'CTVerifyResult.h',
'OCSPCache.h',
'SignedCertificateTimestamp.h',
'SignedTreeHead.h',
]
UNIFIED_SOURCES += [
'BRNameMatchingPolicy.cpp',
+ 'BTVerifier.cpp',
'Buffer.cpp',
'CertVerifier.cpp',
'CTDiversityPolicy.cpp',
'CTLogVerifier.cpp',
'CTObjectsExtractor.cpp',
'CTPolicyEnforcer.cpp',
'CTSerialization.cpp',
'CTVerifyResult.cpp',
new file mode 100644
--- /dev/null
+++ b/security/certverifier/tests/gtest/BTSerializationTest.cpp
@@ -0,0 +1,160 @@
+/* -*- 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"
+
+namespace mozilla { namespace ct {
+
+using namespace pkix;
+
+class BTSerializationTest : public ::testing::Test
+{
+public:
+ void SetUp() override
+ {
+ mTestInclusionProof = GetTestInclusionProof();
+ mTestInclusionProofUnexpectedData = GetTestInclusionProofUnexpectedData();
+ mTestInclusionProofInvalidHashSize = GetTestInclusionProofInvalidHashSize();
+ mTestInclusionProofInvalidHash = GetTestInclusionProofInvalidHash();
+ mTestInclusionProofMissingLogId = GetTestInclusionProofMissingLogId();
+ mTestInclusionProofNullPathLength = GetTestInclusionProofNullPathLength();
+ mTestInclusionProofPathLengthTooSmall = GetTestInclusionProofPathLengthTooSmall();
+ mTestInclusionProofPathLengthTooLarge = GetTestInclusionProofPathLengthTooLarge();
+ mTestInclusionProofNullTreeSize = GetTestInclusionProofNullTreeSize();
+ mTestInclusionProofLeafIndexOutOfBounds = GetTestInclusionProofLeafIndexOutOfBounds();
+ mTestInclusionProofExtraData = GetTestInclusionProofExtraData();
+ }
+
+protected:
+ Buffer mTestInclusionProof;
+ Buffer mTestInclusionProofUnexpectedData;
+ Buffer mTestInclusionProofInvalidHashSize;
+ Buffer mTestInclusionProofInvalidHash;
+ Buffer mTestInclusionProofMissingLogId;
+ Buffer mTestInclusionProofNullPathLength;
+ Buffer mTestInclusionProofPathLengthTooSmall;
+ Buffer mTestInclusionProofPathLengthTooLarge;
+ Buffer mTestInclusionProofNullTreeSize;
+ Buffer mTestInclusionProofLeafIndexOutOfBounds;
+ Buffer mTestInclusionProofExtraData;
+};
+
+TEST_F(BTSerializationTest, DecodesInclusionProof)
+{
+ const uint64_t expectedTreeSize = 4;
+ const uint64_t expectedLeafIndex = 2;
+ const uint64_t expectedInclusionPathElements = 2;
+
+ const uint8_t EXPECTED_LOGID[] = { 0x01, 0x00 };
+ Buffer expectedLogId;
+ MOZ_RELEASE_ASSERT(expectedLogId.append(EXPECTED_LOGID, 2));
+
+
+ Input encodedProofInput = InputForBuffer(mTestInclusionProof);
+ Reader encodedProofReader(encodedProofInput);
+
+ InclusionProofDataV2 ipr;
+ ASSERT_EQ(Success, DecodeInclusionProof(encodedProofReader, ipr));
+ EXPECT_EQ(expectedLogId, ipr.logId);
+ EXPECT_EQ(expectedTreeSize, ipr.treeSize);
+ EXPECT_EQ(expectedLeafIndex, ipr.leafIndex);
+ EXPECT_EQ(expectedInclusionPathElements, ipr.inclusionPath.length());
+ EXPECT_EQ(GetTestNodeHash0(), ipr.inclusionPath[0]);
+ EXPECT_EQ(GetTestNodeHash1(), ipr.inclusionPath[1]);
+}
+
+TEST_F(BTSerializationTest, FailsDecodingInclusionProofUnexpectedData)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofUnexpectedData);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingInvalidHashSize)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofInvalidHashSize);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingInvalidHash)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofInvalidHash);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingMissingLogId)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofMissingLogId);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingNullPathLength)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofNullPathLength);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingPathLengthTooSmall)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofPathLengthTooSmall);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingPathLengthTooLarge)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofPathLengthTooLarge);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingNullTreeSize)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofNullTreeSize);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingLeafIndexOutOfBounds)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofLeafIndexOutOfBounds);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+
+TEST_F(BTSerializationTest, FailsDecodingExtraData)
+{
+ Input encodedProofInput = InputForBuffer(mTestInclusionProofExtraData);
+ Reader encodedProofReader(encodedProofInput);
+ InclusionProofDataV2 ipr;
+
+ ASSERT_EQ(Result::ERROR_BAD_DER, DecodeInclusionProof(encodedProofReader, ipr));
+}
+} } // namespace mozilla::ct
--- a/security/certverifier/tests/gtest/CTSerializationTest.cpp
+++ b/security/certverifier/tests/gtest/CTSerializationTest.cpp
@@ -262,10 +262,9 @@ TEST_F(CTSerializationTest, EncodesValid
// 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
+} } // namespace mozilla::ct
--- a/security/certverifier/tests/gtest/CTTestUtils.cpp
+++ b/security/certverifier/tests/gtest/CTTestUtils.cpp
@@ -4,16 +4,17 @@
* 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 "BTInclusionProof.h"
#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"
@@ -321,16 +322,114 @@ const char kTestEmbeddedWithIntermediate
"43eb3002200b76fe475138d8cf76833831304dabf043eb1213c96e13ff4f"
"a37f7cd3c8dc1f300d06092a864886f70d01010505000381810088ee4e9e"
"5eed6b112cc764b151ed929400e9406789c15fbbcfcdab2f10b400234139"
"e6ce65c1e51b47bf7c8950f80bccd57168567954ed35b0ce9346065a5eae"
"5bf95d41da8e27cee9eeac688f4bd343f9c2888327abd8b9f68dcb1e3050"
"041d31bda8e2dd6d39b3664de5ce0870f5fc7e6a00d6ed00528458d953d2"
"37586d73";
+// Given the ordered set of data [ 0x00, 0x01, 0x02, deadbeef ],
+// the 'inclusion proof' of the leaf of index '2' (for '0x02') is created from
+// the Merkle Tree generated for that set of data.
+// A Merkle inclusion proof for a leaf in a Merkle Tree is the shortest list
+// of additional nodes in the Merkle Tree required to compute the Merkle Tree
+// Hash (also called 'Merkle Tree head') for that tree.
+// This follows the structure defined in RFC 6962-bis.
+//
+// https://tools.ietf.org/html/draft-ietf-trans-rfc6962-bis-24#section-2.1
+
+const char kTestInclusionProof[] =
+ "020100" // logId
+ "0000000000000004" // tree size
+ "0000000000000002" // leaf index
+ "0042" // inclusion path length
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestNodeHash0[] =
+ "48c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729";
+
+const char kTestNodeHash1[] =
+ "a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a";
+
+const char kTestInclusionProofUnexpectedData[] = "12345678";
+
+const char kTestInclusionProofInvalidHashSize[] =
+ "020100" // logId
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0042" // inclusion path length
+ "3048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // invalid hash size
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofInvalidHash[] =
+ "020100" // logId
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0042" // inclusion path length
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427"; // truncated node hash 1
+
+const char kTestInclusionProofMissingLogId[] =
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0042"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofNullPathLength[] =
+ "020100"
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0000"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofPathLengthTooSmall[] =
+ "020100"
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0036"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofPathLengthTooLarge[] =
+ "020100"
+ "0000000000000004" // treesize
+ "0000000000000002" // leafindex
+ "0080"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofNullTreeSize[] =
+ "020100"
+ "0000000000000000" // treesize
+ "0000000000000002" // leafindex
+ "0042"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofLeafIndexOutOfBounds[] =
+ "020100"
+ "0000000000000004" // treesize
+ "0000000000000004" // leafindex
+ "0042"
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a"; // node hash 1
+
+const char kTestInclusionProofExtraData[] =
+ "020100" // logId
+ "0000000000000004" // tree size
+ "0000000000000002" // leaf index
+ "0042" // inclusion path length
+ "2048c90c8ae24688d6bef5d48a30c2cc8b6754335a8db21793cc0a8e3bed321729" // node hash 0
+ "20a20bf9a7cc2dc8a08f5f415a71b19f6ac427bab54d24eec868b5d3103449953a" // node hash 1
+ "123456"; // extra data after the proof
+
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') {
@@ -402,16 +501,94 @@ GetTestDigitallySignedData()
Buffer
GetTestSignedCertificateTimestamp()
{
return HexToBytes(kTestSignedCertificateTimestamp);
}
Buffer
+GetTestInclusionProof()
+{
+ return HexToBytes(kTestInclusionProof);
+}
+
+Buffer
+GetTestInclusionProofUnexpectedData()
+{
+ return HexToBytes(kTestInclusionProofUnexpectedData);
+}
+
+Buffer
+GetTestInclusionProofInvalidHashSize()
+{
+ return HexToBytes(kTestInclusionProofInvalidHashSize);
+}
+
+Buffer
+GetTestInclusionProofInvalidHash()
+{
+ return HexToBytes(kTestInclusionProofInvalidHash);
+}
+
+Buffer
+GetTestInclusionProofMissingLogId()
+{
+ return HexToBytes(kTestInclusionProofMissingLogId);
+}
+
+Buffer
+GetTestInclusionProofNullPathLength()
+{
+ return HexToBytes(kTestInclusionProofNullPathLength);
+}
+
+Buffer
+GetTestInclusionProofPathLengthTooSmall()
+{
+ return HexToBytes(kTestInclusionProofPathLengthTooSmall);
+}
+
+Buffer
+GetTestInclusionProofPathLengthTooLarge()
+{
+ return HexToBytes(kTestInclusionProofPathLengthTooLarge);
+}
+
+Buffer
+GetTestInclusionProofNullTreeSize()
+{
+ return HexToBytes(kTestInclusionProofNullTreeSize);
+}
+
+Buffer
+GetTestInclusionProofLeafIndexOutOfBounds()
+{
+ return HexToBytes(kTestInclusionProofLeafIndexOutOfBounds);
+}
+
+Buffer
+GetTestInclusionProofExtraData()
+{
+ return HexToBytes(kTestInclusionProofExtraData);
+}
+
+Buffer
+GetTestNodeHash0()
+{
+ return HexToBytes(kTestNodeHash0);
+}
+
+Buffer
+GetTestNodeHash1()
+{
+ return HexToBytes(kTestNodeHash1);
+}
+
+Buffer
GetTestPublicKey()
{
return HexToBytes(kEcP256PublicKey);
}
Buffer
GetTestPublicKeyId()
{
--- a/security/certverifier/tests/gtest/CTTestUtils.h
+++ b/security/certverifier/tests/gtest/CTTestUtils.h
@@ -35,16 +35,34 @@ void GetPrecertLogEntry(LogEntry& entry)
Buffer GetTestDigitallySigned();
// Returns the source data of the test DigitallySigned.
Buffer GetTestDigitallySignedData();
// Returns the binary representation of a test serialized SCT.
Buffer GetTestSignedCertificateTimestamp();
+// Returns the binary representation of a test serialized InclusionProof.
+Buffer GetTestInclusionProof();
+Buffer GetTestInclusionProofUnexpectedData();
+Buffer GetTestInclusionProofInvalidHashSize();
+Buffer GetTestInclusionProofInvalidHash();
+Buffer GetTestInclusionProofMissingLogId();
+Buffer GetTestInclusionProofNullPathLength();
+Buffer GetTestInclusionProofPathLengthTooSmall();
+Buffer GetTestInclusionProofPathLengthTooLarge();
+Buffer GetTestInclusionProofNullTreeSize();
+Buffer GetTestInclusionProofLeafIndexOutOfBounds();
+Buffer GetTestInclusionProofExtraData();
+
+// Returns the binary representation of test serialized node hashs from an
+// inclusion proof.
+Buffer GetTestNodeHash0();
+Buffer GetTestNodeHash1();
+
// Test log key.
Buffer GetTestPublicKey();
// ID of test log key.
Buffer GetTestPublicKeyId();
// SCT for the X509Certificate provided above.
void GetX509CertSCT(SignedCertificateTimestamp& sct);
--- a/security/certverifier/tests/gtest/moz.build
+++ b/security/certverifier/tests/gtest/moz.build
@@ -1,15 +1,16 @@
# -*- 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 += [
+ 'BTSerializationTest.cpp',
'CTDiversityPolicyTest.cpp',
'CTLogVerifierTest.cpp',
'CTObjectsExtractorTest.cpp',
'CTPolicyEnforcerTest.cpp',
'CTSerializationTest.cpp',
'CTTestUtils.cpp',
'MultiLogCTVerifierTest.cpp',
]