xref: /aosp_15_r20/external/cronet/net/cert/ct_log_verifier_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/cert/ct_log_verifier.h"
6 
7 #include <stdint.h>
8 
9 #include <algorithm>
10 #include <memory>
11 #include <string>
12 #include <vector>
13 
14 #include "base/strings/string_number_conversions.h"
15 #include "base/time/time.h"
16 #include "crypto/secure_hash.h"
17 #include "net/base/hash_value.h"
18 #include "net/cert/ct_log_verifier_util.h"
19 #include "net/cert/merkle_audit_proof.h"
20 #include "net/cert/merkle_consistency_proof.h"
21 #include "net/cert/signed_certificate_timestamp.h"
22 #include "net/cert/signed_tree_head.h"
23 #include "net/test/ct_test_util.h"
24 #include "testing/gtest/include/gtest/gtest.h"
25 
26 namespace net {
27 
28 namespace {
29 
30 // Calculate the power of two nearest to, but less than, |n|.
31 // |n| must be at least 2.
CalculateNearestPowerOfTwo(size_t n)32 size_t CalculateNearestPowerOfTwo(size_t n) {
33   DCHECK_GT(n, 1u);
34 
35   size_t ret = size_t(1) << (sizeof(size_t) * 8 - 1);
36   while (ret >= n)
37     ret >>= 1;
38 
39   return ret;
40 }
41 
42 // All test data replicated from
43 // https://github.com/google/certificate-transparency/blob/c41b090ecc14ddd6b3531dc7e5ce36b21e253fdd/cpp/merkletree/merkle_tree_test.cc
44 
45 // The SHA-256 hash of an empty Merkle tree.
46 const uint8_t kEmptyTreeHash[32] = {
47     0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4,
48     0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b,
49     0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
50 
GetEmptyTreeHash()51 std::string GetEmptyTreeHash() {
52   return std::string(std::begin(kEmptyTreeHash), std::end(kEmptyTreeHash));
53 }
54 
55 // SHA-256 Merkle leaf hashes for the sample tree that all of the other test
56 // data relates to (8 leaves).
57 const char* const kLeafHashes[8] = {
58     "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
59     "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",
60     "0298d122906dcfc10892cb53a73992fc5b9f493ea4c9badb27b791b4127a7fe7",
61     "07506a85fd9dd2f120eb694f86011e5bb4662e5c415a62917033d4a9624487e7",
62     "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b",
63     "4271a26be0d8a84f0bd54c8c302e7cb3a3b5d1fa6780a40bcce2873477dab658",
64     "b08693ec2e721597130641e8211e7eedccb4c26413963eee6c1e2ed16ffb1a5f",
65     "46f6ffadd3d06a09ff3c5860d2755c8b9819db7df44251788c7d8e3180de8eb1"};
66 
67 // SHA-256 Merkle root hashes from building the sample tree leaf-by-leaf.
68 // The first entry is the root when the tree contains 1 leaf, and the last is
69 // the root when the tree contains all 8 leaves.
70 const char* const kRootHashes[8] = {
71     "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
72     "fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125",
73     "aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77",
74     "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7",
75     "4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4",
76     "76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef",
77     "ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c",
78     "5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328"};
79 
80 // A single consistency proof. Contains at most 3 proof nodes (all test proofs
81 // will be for a tree of size 8).
82 struct ConsistencyProofTestVector {
83   size_t old_tree_size;
84   size_t new_tree_size;
85   size_t proof_length;
86   const char* const proof[3];
87 };
88 
89 // A collection of consistency proofs between various sub-trees of the sample
90 // tree.
91 const ConsistencyProofTestVector kConsistencyProofs[] = {
92     // Empty consistency proof between trees of the same size (1).
93     {1, 1, 0, {"", "", ""}},
94     // Consistency proof between tree of size 1 and tree of size 8, with 3
95     // nodes in the proof.
96     {1,
97      8,
98      3,
99      {"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",
100       "5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e",
101       "6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"}},
102     // Consistency proof between tree of size 6 and tree of size 8, with 3
103     // nodes in the proof.
104     {6,
105      8,
106      3,
107      {"0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a",
108       "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0",
109       "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"}},
110     // Consistency proof between tree of size 2 and tree of size 5, with 2
111     // nodes in the proof.
112     {2,
113      5,
114      2,
115      {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e",
116       "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", ""}}};
117 
118 // A single audit proof. Contains at most 3 proof nodes (all test proofs will be
119 // for a tree of size 8).
120 struct AuditProofTestVector {
121   size_t leaf;
122   size_t tree_size;
123   size_t proof_length;
124   const char* const proof[3];
125 };
126 
127 // A collection of audit proofs for various leaves and sub-trees of the tree
128 // defined by |kRootHashes|.
129 const AuditProofTestVector kAuditProofs[] = {
130     {0, 1, 0, {"", "", ""}},
131     {0,
132      8,
133      3,
134      {"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7",
135       "5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e",
136       "6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"}},
137     {5,
138      8,
139      3,
140      {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b",
141       "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0",
142       "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"}},
143     {2,
144      3,
145      1,
146      {"fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", "",
147       ""}},
148     {1,
149      5,
150      3,
151      {"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d",
152       "5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e",
153       "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"}}};
154 
155 // Decodes a hexadecimal string into the binary data it represents.
HexToBytes(const std::string & hex_data)156 std::string HexToBytes(const std::string& hex_data) {
157   std::string result;
158   if (!base::HexStringToString(hex_data, &result))
159     result.clear();
160   return result;
161 }
162 
163 // Constructs a consistency/audit proof from a test vector.
164 // This is templated so that it can be used with both ConsistencyProofTestVector
165 // and AuditProofTestVector.
166 template <typename TestVectorType>
GetProof(const TestVectorType & test_vector)167 std::vector<std::string> GetProof(const TestVectorType& test_vector) {
168   std::vector<std::string> proof(test_vector.proof_length);
169   std::transform(test_vector.proof,
170                  test_vector.proof + test_vector.proof_length, proof.begin(),
171                  &HexToBytes);
172 
173   return proof;
174 }
175 
176 // Creates a ct::MerkleConsistencyProof from its arguments and returns the
177 // result of passing this to log.VerifyConsistencyProof().
VerifyConsistencyProof(const CTLogVerifier & log,size_t old_tree_size,const std::string & old_tree_root,size_t new_tree_size,const std::string & new_tree_root,const std::vector<std::string> & proof)178 bool VerifyConsistencyProof(const CTLogVerifier& log,
179                             size_t old_tree_size,
180                             const std::string& old_tree_root,
181                             size_t new_tree_size,
182                             const std::string& new_tree_root,
183                             const std::vector<std::string>& proof) {
184   return log.VerifyConsistencyProof(
185       ct::MerkleConsistencyProof(log.key_id(), proof, old_tree_size,
186                                  new_tree_size),
187       old_tree_root, new_tree_root);
188 }
189 
190 // Creates a ct::MerkleAuditProof from its arguments and returns the result of
191 // passing this to log.VerifyAuditProof().
VerifyAuditProof(const CTLogVerifier & log,size_t leaf,size_t tree_size,const std::vector<std::string> & proof,const std::string & tree_root,const std::string & leaf_hash)192 bool VerifyAuditProof(const CTLogVerifier& log,
193                       size_t leaf,
194                       size_t tree_size,
195                       const std::vector<std::string>& proof,
196                       const std::string& tree_root,
197                       const std::string& leaf_hash) {
198   return log.VerifyAuditProof(ct::MerkleAuditProof(leaf, tree_size, proof),
199                               tree_root, leaf_hash);
200 }
201 
202 class CTLogVerifierTest : public ::testing::Test {
203  public:
SetUp()204   void SetUp() override {
205     log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog");
206 
207     ASSERT_TRUE(log_);
208     EXPECT_EQ(ct::GetTestPublicKeyId(), log_->key_id());
209   }
210 
211  protected:
212   scoped_refptr<const CTLogVerifier> log_;
213 };
214 
215 // Given an audit proof for a leaf in a Merkle tree, asserts that it verifies
216 // and no other combination of leaves, tree sizes and proof nodes verifies.
CheckVerifyAuditProof(const CTLogVerifier & log,size_t leaf,size_t tree_size,const std::vector<std::string> & proof,const std::string & root_hash,const std::string & leaf_hash)217 void CheckVerifyAuditProof(const CTLogVerifier& log,
218                            size_t leaf,
219                            size_t tree_size,
220                            const std::vector<std::string>& proof,
221                            const std::string& root_hash,
222                            const std::string& leaf_hash) {
223   EXPECT_TRUE(
224       VerifyAuditProof(log, leaf, tree_size, proof, root_hash, leaf_hash))
225       << "proof for leaf " << leaf << " did not pass verification";
226   EXPECT_FALSE(
227       VerifyAuditProof(log, leaf - 1, tree_size, proof, root_hash, leaf_hash))
228       << "proof passed verification with wrong leaf index";
229   EXPECT_FALSE(
230       VerifyAuditProof(log, leaf + 1, tree_size, proof, root_hash, leaf_hash))
231       << "proof passed verification with wrong leaf index";
232   EXPECT_FALSE(
233       VerifyAuditProof(log, leaf ^ 2, tree_size, proof, root_hash, leaf_hash))
234       << "proof passed verification with wrong leaf index";
235   EXPECT_FALSE(
236       VerifyAuditProof(log, leaf, tree_size * 2, proof, root_hash, leaf_hash))
237       << "proof passed verification with wrong tree height";
238   EXPECT_FALSE(VerifyAuditProof(log, leaf / 2, tree_size / 2, proof, root_hash,
239                                 leaf_hash))
240       << "proof passed verification with wrong leaf index and tree height";
241   EXPECT_FALSE(
242       VerifyAuditProof(log, leaf, tree_size / 2, proof, root_hash, leaf_hash))
243       << "proof passed verification with wrong tree height";
244   EXPECT_FALSE(VerifyAuditProof(log, leaf, tree_size, proof, GetEmptyTreeHash(),
245                                 leaf_hash))
246       << "proof passed verification with wrong root hash";
247 
248   std::vector<std::string> wrong_proof;
249 
250   // Modify a single element on the proof.
251   for (size_t j = 0; j < proof.size(); ++j) {
252     wrong_proof = proof;
253     wrong_proof[j] = GetEmptyTreeHash();
254     EXPECT_FALSE(VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash,
255                                   leaf_hash))
256         << "proof passed verification with one wrong node (node " << j << ")";
257   }
258 
259   wrong_proof = proof;
260   wrong_proof.emplace_back();
261   EXPECT_FALSE(
262       VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash, leaf_hash))
263       << "proof passed verification with an empty node appended";
264 
265   wrong_proof.back() = root_hash;
266   EXPECT_FALSE(
267       VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash, leaf_hash))
268       << "proof passed verification with an incorrect node appended";
269   wrong_proof.pop_back();
270 
271   if (!wrong_proof.empty()) {
272     wrong_proof.pop_back();
273     EXPECT_FALSE(VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash,
274                                   leaf_hash))
275         << "proof passed verification with the last node missing";
276   }
277 
278   wrong_proof.clear();
279   wrong_proof.emplace_back();
280   wrong_proof.insert(wrong_proof.end(), proof.begin(), proof.end());
281   EXPECT_FALSE(
282       VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash, leaf_hash))
283       << "proof passed verification with an empty node prepended";
284 
285   wrong_proof[0] = root_hash;
286   EXPECT_FALSE(
287       VerifyAuditProof(log, leaf, tree_size, wrong_proof, root_hash, leaf_hash))
288       << "proof passed verification with an incorrect node prepended";
289 }
290 
291 // Given a consistency proof between two snapshots of the tree, asserts that it
292 // verifies and no other combination of tree sizes and proof nodes verifies.
CheckVerifyConsistencyProof(const CTLogVerifier & log,int old_tree_size,int new_tree_size,const std::string & old_root,const std::string & new_root,const std::vector<std::string> & proof)293 void CheckVerifyConsistencyProof(const CTLogVerifier& log,
294                                  int old_tree_size,
295                                  int new_tree_size,
296                                  const std::string& old_root,
297                                  const std::string& new_root,
298                                  const std::vector<std::string>& proof) {
299   // Verify the original consistency proof.
300   EXPECT_TRUE(VerifyConsistencyProof(log, old_tree_size, old_root,
301                                      new_tree_size, new_root, proof))
302       << "proof between trees of size " << old_tree_size << " and "
303       << new_tree_size << " did not pass verification";
304 
305   if (proof.empty()) {
306     // For simplicity test only non-trivial proofs that have old_root !=
307     // new_root
308     // old_tree_size != 0 and old_tree_size != new_tree_size.
309     return;
310   }
311 
312   // Wrong tree size: The proof checking code should not accept as a valid proof
313   // a proof for a tree size different than the original size it was produced
314   // for. Test that this is not the case for off-by-one changes.
315   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size - 1, old_root,
316                                       new_tree_size, new_root, proof))
317       << "proof passed verification with old tree size - 1";
318   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size + 1, old_root,
319                                       new_tree_size, new_root, proof))
320       << "proof passed verification with old tree size + 1";
321   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size ^ 2, old_root,
322                                       new_tree_size, new_root, proof))
323       << "proof passed verification with old tree size ^ 2";
324 
325   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
326                                       new_tree_size * 2, new_root, proof))
327       << "proof passed verification with new tree height + 1";
328   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
329                                       new_tree_size / 2, new_root, proof))
330       << "proof passed verification with new tree height - 1";
331 
332   const std::string wrong_root("WrongRoot");
333   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
334                                       new_tree_size, wrong_root, proof))
335       << "proof passed verification with wrong old root";
336   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, wrong_root,
337                                       new_tree_size, new_root, proof))
338       << "proof passed verification with wrong new root";
339   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, new_root,
340                                       new_tree_size, old_root, proof))
341       << "proof passed verification with old and new root swapped";
342 
343   // Variations of wrong proofs, all of which should be rejected.
344   std::vector<std::string> wrong_proof;
345   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
346                                       new_tree_size, new_root, wrong_proof))
347       << "empty proof passed verification";
348 
349   // Modify a single element in the proof.
350   for (size_t j = 0; j < proof.size(); ++j) {
351     wrong_proof = proof;
352     wrong_proof[j] = GetEmptyTreeHash();
353     EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
354                                         new_tree_size, new_root, wrong_proof))
355         << "proof passed verification with incorrect node (node " << j << ")";
356   }
357 
358   wrong_proof = proof;
359   wrong_proof.emplace_back();
360   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
361                                       new_tree_size, new_root, wrong_proof))
362       << "proof passed verification with empty node appended";
363 
364   wrong_proof.back() = proof.back();
365   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
366                                       new_tree_size, new_root, wrong_proof))
367       << "proof passed verification with last node duplicated";
368   wrong_proof.pop_back();
369 
370   wrong_proof.pop_back();
371   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
372                                       new_tree_size, new_root, wrong_proof))
373       << "proof passed verification with last node missing";
374 
375   wrong_proof.clear();
376   wrong_proof.emplace_back();
377   wrong_proof.insert(wrong_proof.end(), proof.begin(), proof.end());
378   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
379                                       new_tree_size, new_root, wrong_proof))
380       << "proof passed verification with empty node prepended";
381 
382   wrong_proof[0] = proof[0];
383   EXPECT_FALSE(VerifyConsistencyProof(log, old_tree_size, old_root,
384                                       new_tree_size, new_root, wrong_proof))
385       << "proof passed verification with first node duplicated";
386 }
387 
TEST_F(CTLogVerifierTest,VerifiesCertSCT)388 TEST_F(CTLogVerifierTest, VerifiesCertSCT) {
389   ct::SignedEntryData cert_entry;
390   ct::GetX509CertSignedEntry(&cert_entry);
391 
392   scoped_refptr<ct::SignedCertificateTimestamp> cert_sct;
393   ct::GetX509CertSCT(&cert_sct);
394 
395   EXPECT_TRUE(log_->Verify(cert_entry, *cert_sct.get()));
396 }
397 
TEST_F(CTLogVerifierTest,VerifiesPrecertSCT)398 TEST_F(CTLogVerifierTest, VerifiesPrecertSCT) {
399   ct::SignedEntryData precert_entry;
400   ct::GetPrecertSignedEntry(&precert_entry);
401 
402   scoped_refptr<ct::SignedCertificateTimestamp> precert_sct;
403   ct::GetPrecertSCT(&precert_sct);
404 
405   EXPECT_TRUE(log_->Verify(precert_entry, *precert_sct.get()));
406 }
407 
TEST_F(CTLogVerifierTest,FailsInvalidTimestamp)408 TEST_F(CTLogVerifierTest, FailsInvalidTimestamp) {
409   ct::SignedEntryData cert_entry;
410   ct::GetX509CertSignedEntry(&cert_entry);
411 
412   scoped_refptr<ct::SignedCertificateTimestamp> cert_sct;
413   ct::GetX509CertSCT(&cert_sct);
414 
415   // Mangle the timestamp, so that it should fail signature validation.
416   cert_sct->timestamp = base::Time::Now();
417 
418   EXPECT_FALSE(log_->Verify(cert_entry, *cert_sct.get()));
419 }
420 
TEST_F(CTLogVerifierTest,FailsInvalidLogID)421 TEST_F(CTLogVerifierTest, FailsInvalidLogID) {
422   ct::SignedEntryData cert_entry;
423   ct::GetX509CertSignedEntry(&cert_entry);
424 
425   scoped_refptr<ct::SignedCertificateTimestamp> cert_sct;
426   ct::GetX509CertSCT(&cert_sct);
427 
428   // Mangle the log ID, which should cause it to match a different log before
429   // attempting signature validation.
430   cert_sct->log_id.assign(cert_sct->log_id.size(), '\0');
431 
432   EXPECT_FALSE(log_->Verify(cert_entry, *cert_sct.get()));
433 }
434 
TEST_F(CTLogVerifierTest,VerifiesValidSTH)435 TEST_F(CTLogVerifierTest, VerifiesValidSTH) {
436   ct::SignedTreeHead sth;
437   ASSERT_TRUE(ct::GetSampleSignedTreeHead(&sth));
438   EXPECT_TRUE(log_->VerifySignedTreeHead(sth));
439 }
440 
TEST_F(CTLogVerifierTest,DoesNotVerifyInvalidSTH)441 TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidSTH) {
442   ct::SignedTreeHead sth;
443   ASSERT_TRUE(ct::GetSampleSignedTreeHead(&sth));
444   sth.sha256_root_hash[0] = '\x0';
445   EXPECT_FALSE(log_->VerifySignedTreeHead(sth));
446 }
447 
TEST_F(CTLogVerifierTest,VerifiesValidEmptySTH)448 TEST_F(CTLogVerifierTest, VerifiesValidEmptySTH) {
449   ct::SignedTreeHead sth;
450   ASSERT_TRUE(ct::GetSampleEmptySignedTreeHead(&sth));
451   EXPECT_TRUE(log_->VerifySignedTreeHead(sth));
452 }
453 
TEST_F(CTLogVerifierTest,DoesNotVerifyInvalidEmptySTH)454 TEST_F(CTLogVerifierTest, DoesNotVerifyInvalidEmptySTH) {
455   ct::SignedTreeHead sth;
456   ASSERT_TRUE(ct::GetBadEmptySignedTreeHead(&sth));
457   EXPECT_FALSE(log_->VerifySignedTreeHead(sth));
458 }
459 
460 // Test that excess data after the public key is rejected.
TEST_F(CTLogVerifierTest,ExcessDataInPublicKey)461 TEST_F(CTLogVerifierTest, ExcessDataInPublicKey) {
462   std::string key = ct::GetTestPublicKey();
463   key += "extra";
464 
465   scoped_refptr<const CTLogVerifier> log =
466       CTLogVerifier::Create(key, "testlog");
467   EXPECT_FALSE(log);
468 }
469 
TEST_F(CTLogVerifierTest,VerifiesConsistencyProofEdgeCases_EmptyProof)470 TEST_F(CTLogVerifierTest, VerifiesConsistencyProofEdgeCases_EmptyProof) {
471   std::vector<std::string> empty_proof;
472   std::string old_root(GetEmptyTreeHash()), new_root(GetEmptyTreeHash());
473 
474   // Tree snapshots that are always consistent, because the proofs are either
475   // from an empty tree to a non-empty one or for trees of the same size.
476   EXPECT_TRUE(
477       VerifyConsistencyProof(*log_, 0, old_root, 0, new_root, empty_proof));
478   EXPECT_TRUE(
479       VerifyConsistencyProof(*log_, 0, old_root, 1, new_root, empty_proof));
480   EXPECT_TRUE(
481       VerifyConsistencyProof(*log_, 1, old_root, 1, new_root, empty_proof));
482 
483   // Invalid consistency proofs.
484   // Time travel to the past.
485   EXPECT_FALSE(
486       VerifyConsistencyProof(*log_, 1, old_root, 0, new_root, empty_proof));
487   EXPECT_FALSE(
488       VerifyConsistencyProof(*log_, 2, old_root, 1, new_root, empty_proof));
489   // Proof between two trees of different size can never be empty.
490   EXPECT_FALSE(
491       VerifyConsistencyProof(*log_, 1, old_root, 2, new_root, empty_proof));
492 }
493 
TEST_F(CTLogVerifierTest,VerifiesConsistencyProofEdgeCases_MismatchingRoots)494 TEST_F(CTLogVerifierTest, VerifiesConsistencyProofEdgeCases_MismatchingRoots) {
495   const std::string old_root(GetEmptyTreeHash());
496   std::string new_root;
497   std::vector<std::string> empty_proof;
498 
499   // Roots don't match.
500   EXPECT_FALSE(
501       VerifyConsistencyProof(*log_, 0, old_root, 0, new_root, empty_proof));
502   EXPECT_FALSE(
503       VerifyConsistencyProof(*log_, 1, old_root, 1, new_root, empty_proof));
504 }
505 
TEST_F(CTLogVerifierTest,VerifiesConsistencyProofEdgeCases_MatchingRootsNonEmptyProof)506 TEST_F(CTLogVerifierTest,
507        VerifiesConsistencyProofEdgeCases_MatchingRootsNonEmptyProof) {
508   const std::string empty_tree_hash(GetEmptyTreeHash());
509 
510   std::vector<std::string> proof;
511   proof.push_back(empty_tree_hash);
512 
513   // Roots match and the tree size is either the same or the old tree size is 0,
514   // but the proof is not empty (the verification code should not accept
515   // proofs with redundant nodes in this case).
516   proof.push_back(empty_tree_hash);
517   EXPECT_FALSE(VerifyConsistencyProof(*log_, 0, empty_tree_hash, 0,
518                                       empty_tree_hash, proof));
519   EXPECT_FALSE(VerifyConsistencyProof(*log_, 0, empty_tree_hash, 1,
520                                       empty_tree_hash, proof));
521   EXPECT_FALSE(VerifyConsistencyProof(*log_, 1, empty_tree_hash, 1,
522                                       empty_tree_hash, proof));
523 }
524 
525 class CTLogVerifierConsistencyProofTest
526     : public CTLogVerifierTest,
527       public ::testing::WithParamInterface<size_t /* proof index */> {};
528 
529 // Checks that a sample set of valid consistency proofs verify successfully.
TEST_P(CTLogVerifierConsistencyProofTest,VerifiesValidConsistencyProof)530 TEST_P(CTLogVerifierConsistencyProofTest, VerifiesValidConsistencyProof) {
531   const ConsistencyProofTestVector& test_vector =
532       kConsistencyProofs[GetParam()];
533   const std::vector<std::string> proof = GetProof(test_vector);
534 
535   const char* const old_root = kRootHashes[test_vector.old_tree_size - 1];
536   const char* const new_root = kRootHashes[test_vector.new_tree_size - 1];
537   CheckVerifyConsistencyProof(*log_, test_vector.old_tree_size,
538                               test_vector.new_tree_size, HexToBytes(old_root),
539                               HexToBytes(new_root), proof);
540 }
541 
542 INSTANTIATE_TEST_SUITE_P(KnownGoodProofs,
543                          CTLogVerifierConsistencyProofTest,
544                          ::testing::Range(size_t(0),
545                                           std::size(kConsistencyProofs)));
546 
547 class CTLogVerifierAuditProofTest
548     : public CTLogVerifierTest,
549       public ::testing::WithParamInterface<size_t /* proof index */> {};
550 
551 // Checks that a sample set of valid audit proofs verify successfully.
TEST_P(CTLogVerifierAuditProofTest,VerifiesValidAuditProofs)552 TEST_P(CTLogVerifierAuditProofTest, VerifiesValidAuditProofs) {
553   const AuditProofTestVector& test_vector = kAuditProofs[GetParam()];
554   const std::vector<std::string> proof = GetProof(test_vector);
555 
556   const char* const root_hash = kRootHashes[test_vector.tree_size - 1];
557   CheckVerifyAuditProof(*log_, test_vector.leaf, test_vector.tree_size, proof,
558                         HexToBytes(root_hash),
559                         HexToBytes(kLeafHashes[test_vector.leaf]));
560 }
561 
562 INSTANTIATE_TEST_SUITE_P(KnownGoodProofs,
563                          CTLogVerifierAuditProofTest,
564                          ::testing::Range(size_t(0), std::size(kAuditProofs)));
565 
TEST_F(CTLogVerifierTest,VerifiesAuditProofEdgeCases_InvalidLeafIndex)566 TEST_F(CTLogVerifierTest, VerifiesAuditProofEdgeCases_InvalidLeafIndex) {
567   std::vector<std::string> proof;
568   EXPECT_FALSE(
569       VerifyAuditProof(*log_, 1, 0, proof, std::string(), std::string()));
570   EXPECT_FALSE(
571       VerifyAuditProof(*log_, 2, 1, proof, std::string(), std::string()));
572 
573   const std::string empty_hash = GetEmptyTreeHash();
574   EXPECT_FALSE(VerifyAuditProof(*log_, 1, 0, proof, empty_hash, std::string()));
575   EXPECT_FALSE(VerifyAuditProof(*log_, 2, 1, proof, empty_hash, std::string()));
576 }
577 
578 // Functions that implement algorithms from RFC6962 necessary for constructing
579 // Merkle trees and proofs. This allows tests to generate a variety of trees
580 // for exhaustive testing.
581 namespace rfc6962 {
582 
583 // Calculates the hash of a leaf in a Merkle tree, given its content.
584 // See RFC6962, section 2.1.
HashLeaf(const std::string & leaf)585 std::string HashLeaf(const std::string& leaf) {
586   const char kLeafPrefix[] = {'\x00'};
587 
588   SHA256HashValue sha256;
589   memset(sha256.data, 0, sizeof(sha256.data));
590 
591   std::unique_ptr<crypto::SecureHash> hash(
592       crypto::SecureHash::Create(crypto::SecureHash::SHA256));
593   hash->Update(kLeafPrefix, 1);
594   hash->Update(leaf.data(), leaf.size());
595   hash->Finish(sha256.data, sizeof(sha256.data));
596 
597   return std::string(reinterpret_cast<const char*>(sha256.data),
598                      sizeof(sha256.data));
599 }
600 
601 // Calculates the root hash of a Merkle tree, given its leaf data and size.
602 // See RFC6962, section 2.1.
HashTree(std::string leaves[],size_t tree_size)603 std::string HashTree(std::string leaves[], size_t tree_size) {
604   if (tree_size == 0)
605     return GetEmptyTreeHash();
606   if (tree_size == 1)
607     return HashLeaf(leaves[0]);
608 
609   // Find the index of the last leaf in the left sub-tree.
610   const size_t split = CalculateNearestPowerOfTwo(tree_size);
611 
612   // Hash the left and right sub-trees, then hash the results.
613   return ct::internal::HashNodes(HashTree(leaves, split),
614                                  HashTree(&leaves[split], tree_size - split));
615 }
616 
617 // Returns a Merkle audit proof for the leaf with index |leaf_index|.
618 // The tree consists of |leaves[0]| to |leaves[tree_size-1]|.
619 // If |leaf_index| is >= |tree_size|, an empty proof will be returned.
620 // See RFC6962, section 2.1.1, for more details.
CreateAuditProof(std::string leaves[],size_t tree_size,size_t leaf_index)621 std::vector<std::string> CreateAuditProof(std::string leaves[],
622                                           size_t tree_size,
623                                           size_t leaf_index) {
624   std::vector<std::string> proof;
625   if (leaf_index >= tree_size)
626     return proof;
627   if (tree_size == 1)
628     return proof;
629 
630   // Find the index of the first leaf in the right sub-tree.
631   const size_t split = CalculateNearestPowerOfTwo(tree_size);
632 
633   // Recurse down the correct branch of the tree (left or right) to reach the
634   // leaf with |leaf_index|. Add the hash of the branch not taken at each step
635   // on the way up to build the proof.
636   if (leaf_index < split) {
637     proof = CreateAuditProof(leaves, split, leaf_index);
638     proof.push_back(HashTree(&leaves[split], tree_size - split));
639   } else {
640     proof =
641         CreateAuditProof(&leaves[split], tree_size - split, leaf_index - split);
642     proof.push_back(HashTree(leaves, split));
643   }
644 
645   return proof;
646 }
647 
648 // Returns a Merkle consistency proof between two Merkle trees.
649 // The old tree contains |leaves[0]| to |leaves[old_tree_size-1]|.
650 // The new tree contains |leaves[0]| to |leaves[new_tree_size-1]|.
651 // Call with |contains_old_tree| = true.
652 // See RFC6962, section 2.1.2, for more details.
CreateConsistencyProof(std::string leaves[],size_t new_tree_size,size_t old_tree_size,bool contains_old_tree=true)653 std::vector<std::string> CreateConsistencyProof(std::string leaves[],
654                                                 size_t new_tree_size,
655                                                 size_t old_tree_size,
656                                                 bool contains_old_tree = true) {
657   std::vector<std::string> proof;
658   if (old_tree_size == 0 || old_tree_size > new_tree_size)
659     return proof;
660   if (old_tree_size == new_tree_size) {
661     // Consistency proof for two equal subtrees is empty.
662     if (!contains_old_tree) {
663       // Record the hash of this subtree unless it's the root for which
664       // the proof was originally requested. (This happens when the old tree is
665       // balanced).
666       proof.push_back(HashTree(leaves, old_tree_size));
667     }
668     return proof;
669   }
670 
671   // Find the index of the last leaf in the left sub-tree.
672   const size_t split = CalculateNearestPowerOfTwo(new_tree_size);
673 
674   if (old_tree_size <= split) {
675     // Root of the old tree is in the left subtree of the new tree.
676     // Prove that the left subtrees are consistent.
677     proof =
678         CreateConsistencyProof(leaves, split, old_tree_size, contains_old_tree);
679     // Record the hash of the right subtree (only present in the new tree).
680     proof.push_back(HashTree(&leaves[split], new_tree_size - split));
681   } else {
682     // The old tree root is at the same level as the new tree root.
683     // Prove that the right subtrees are consistent. The right subtree
684     // doesn't contain the root of the old tree, so set contains_old_tree =
685     // false.
686     proof = CreateConsistencyProof(&leaves[split], new_tree_size - split,
687                                    old_tree_size - split,
688                                    /* contains_old_tree = */ false);
689     // Record the hash of the left subtree (equal in both trees).
690     proof.push_back(HashTree(leaves, split));
691   }
692   return proof;
693 }
694 
695 }  // namespace rfc6962
696 
697 class CTLogVerifierTestUsingGenerator
698     : public CTLogVerifierTest,
699       public ::testing::WithParamInterface<size_t /* tree_size */> {};
700 
701 // Checks that valid consistency proofs for a range of generated Merkle trees
702 // verify successfully.
TEST_P(CTLogVerifierTestUsingGenerator,VerifiesValidConsistencyProof)703 TEST_P(CTLogVerifierTestUsingGenerator, VerifiesValidConsistencyProof) {
704   const size_t tree_size = GetParam();
705 
706   std::vector<std::string> tree_leaves(tree_size);
707   for (size_t i = 0; i < tree_size; ++i)
708     tree_leaves[i].push_back(static_cast<char>(i));
709 
710   const std::string tree_root =
711       rfc6962::HashTree(tree_leaves.data(), tree_size);
712 
713   // Check consistency proofs for every sub-tree.
714   for (size_t old_tree_size = 0; old_tree_size <= tree_size; ++old_tree_size) {
715     SCOPED_TRACE(old_tree_size);
716     const std::string old_tree_root =
717         rfc6962::HashTree(tree_leaves.data(), old_tree_size);
718     const std::vector<std::string> proof = rfc6962::CreateConsistencyProof(
719         tree_leaves.data(), tree_size, old_tree_size);
720     // Checks that the consistency proof verifies only with the correct tree
721     // sizes and root hashes.
722     CheckVerifyConsistencyProof(*log_, old_tree_size, tree_size, old_tree_root,
723                                 tree_root, proof);
724   }
725 }
726 
727 // Checks that valid audit proofs for a range of generated Merkle trees verify
728 // successfully.
TEST_P(CTLogVerifierTestUsingGenerator,VerifiesValidAuditProofs)729 TEST_P(CTLogVerifierTestUsingGenerator, VerifiesValidAuditProofs) {
730   const size_t tree_size = GetParam();
731 
732   std::vector<std::string> tree_leaves(tree_size);
733   for (size_t i = 0; i < tree_size; ++i)
734     tree_leaves[i].push_back(static_cast<char>(i));
735 
736   const std::string root = rfc6962::HashTree(tree_leaves.data(), tree_size);
737 
738   // Check audit proofs for every leaf in the tree.
739   for (size_t leaf = 0; leaf < tree_size; ++leaf) {
740     SCOPED_TRACE(leaf);
741     std::vector<std::string> proof =
742         rfc6962::CreateAuditProof(tree_leaves.data(), tree_size, leaf);
743     // Checks that the audit proof verifies only for this leaf data, index,
744     // hash, tree size and root hash.
745     CheckVerifyAuditProof(*log_, leaf, tree_size, proof, root,
746                           rfc6962::HashLeaf(tree_leaves[leaf]));
747   }
748 }
749 
750 // Test verification of consistency proofs and audit proofs for all tree sizes
751 // from 0 to 128.
752 INSTANTIATE_TEST_SUITE_P(RangeOfTreeSizes,
753                          CTLogVerifierTestUsingGenerator,
754                          testing::Range(size_t(0), size_t(129)));
755 
756 }  // namespace
757 
758 }  // namespace net
759