OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/cert/ct_log_verifier.h" | 5 #include "net/cert/ct_log_verifier.h" |
6 | 6 |
7 #include <stdint.h> | 7 #include <stdint.h> |
8 | 8 |
9 #include <memory> | 9 #include <memory> |
10 #include <string> | 10 #include <string> |
11 #include <vector> | 11 #include <vector> |
12 | 12 |
13 #include "base/macros.h" | |
13 #include "base/strings/string_number_conversions.h" | 14 #include "base/strings/string_number_conversions.h" |
14 #include "base/time/time.h" | 15 #include "base/time/time.h" |
15 #include "crypto/secure_hash.h" | 16 #include "crypto/secure_hash.h" |
16 #include "net/base/hash_value.h" | 17 #include "net/base/hash_value.h" |
17 #include "net/cert/ct_log_verifier_util.h" | 18 #include "net/cert/ct_log_verifier_util.h" |
19 #include "net/cert/merkle_audit_proof.h" | |
18 #include "net/cert/merkle_consistency_proof.h" | 20 #include "net/cert/merkle_consistency_proof.h" |
19 #include "net/cert/signed_certificate_timestamp.h" | 21 #include "net/cert/signed_certificate_timestamp.h" |
20 #include "net/cert/signed_tree_head.h" | 22 #include "net/cert/signed_tree_head.h" |
21 #include "net/test/ct_test_util.h" | 23 #include "net/test/ct_test_util.h" |
22 #include "testing/gtest/include/gtest/gtest.h" | 24 #include "testing/gtest/include/gtest/gtest.h" |
23 | 25 |
24 namespace net { | 26 namespace net { |
25 | 27 |
26 namespace { | 28 namespace { |
27 | 29 |
(...skipping 18 matching lines...) Expand all Loading... | |
46 size_t proof_length; | 48 size_t proof_length; |
47 const char* const proof[3]; | 49 const char* const proof[3]; |
48 }; | 50 }; |
49 | 51 |
50 // All test data replicated from | 52 // All test data replicated from |
51 // https://github.com/google/certificate-transparency/blob/c41b090ecc14ddd6b3531 dc7e5ce36b21e253fdd/cpp/merkletree/merkle_tree_test.cc | 53 // https://github.com/google/certificate-transparency/blob/c41b090ecc14ddd6b3531 dc7e5ce36b21e253fdd/cpp/merkletree/merkle_tree_test.cc |
52 // A hash of the empty string. | 54 // A hash of the empty string. |
53 const char* const kSHA256EmptyTreeHash = | 55 const char* const kSHA256EmptyTreeHash = |
54 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; | 56 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"; |
55 | 57 |
58 // Hashes of the leaf data for the sample tree (8 leaves). | |
59 const char* const kSHA256LeafHashes[8] = { | |
60 "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", | |
61 "96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", | |
62 "0298d122906dcfc10892cb53a73992fc5b9f493ea4c9badb27b791b4127a7fe7", | |
63 "07506a85fd9dd2f120eb694f86011e5bb4662e5c415a62917033d4a9624487e7", | |
64 "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", | |
65 "4271a26be0d8a84f0bd54c8c302e7cb3a3b5d1fa6780a40bcce2873477dab658", | |
66 "b08693ec2e721597130641e8211e7eedccb4c26413963eee6c1e2ed16ffb1a5f", | |
67 "46f6ffadd3d06a09ff3c5860d2755c8b9819db7df44251788c7d8e3180de8eb1"}; | |
68 | |
56 // Root hashes from building the sample tree of size 8 leaf-by-leaf. | 69 // Root hashes from building the sample tree of size 8 leaf-by-leaf. |
57 // The first entry is the root at size 0, the last is the root at size 8. | 70 // The first entry is the root at size 0, the last is the root at size 8. |
58 const char* const kSHA256Roots[8] = { | 71 const char* const kSHA256Roots[8] = { |
59 "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", | 72 "6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", |
60 "fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", | 73 "fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", |
61 "aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", | 74 "aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", |
62 "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", | 75 "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", |
63 "4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", | 76 "4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", |
64 "76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", | 77 "76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", |
65 "ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", | 78 "ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", |
(...skipping 21 matching lines...) Expand all Loading... | |
87 "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", | 100 "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", |
88 "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"}}, | 101 "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"}}, |
89 // Consistency proof between tree of size 2 and tree of size 5, with 2 | 102 // Consistency proof between tree of size 2 and tree of size 5, with 2 |
90 // nodes in the proof. | 103 // nodes in the proof. |
91 {2, | 104 {2, |
92 5, | 105 5, |
93 2, | 106 2, |
94 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", | 107 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", |
95 "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", ""}}}; | 108 "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", ""}}}; |
96 | 109 |
110 // A single audit proof. Contains the leaf index (leaf), tree size (snapshot), | |
111 // the length of the proof (proof_length) and at most 3 proof nodes (all test | |
112 // proofs will be for a tree of size 8). | |
113 struct PathTestVector { | |
114 uint64_t leaf; | |
115 uint64_t snapshot; | |
116 size_t path_length; | |
117 const char* const path[3]; | |
118 }; | |
119 | |
120 // A collection of audit proofs for various leaves and sub-trees of the tree | |
121 // defined by |kSHA256Roots|. | |
122 const PathTestVector kSHA256Paths[6] = { | |
123 {0, 0, 0, {"", "", ""}}, | |
124 {1, 1, 0, {"", "", ""}}, | |
125 {1, | |
126 8, | |
127 3, | |
128 {"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", | |
129 "5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", | |
130 "6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4"}}, | |
131 {6, | |
132 8, | |
133 3, | |
134 {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", | |
135 "ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", | |
136 "d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7"}}, | |
137 {3, | |
138 3, | |
139 1, | |
140 {"fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", "", | |
141 ""}}, | |
142 {2, | |
143 5, | |
144 3, | |
145 {"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", | |
146 "5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", | |
147 "bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b"}}}; | |
148 | |
97 // Decodes a hexadecimal string into the binary data it represents. | 149 // Decodes a hexadecimal string into the binary data it represents. |
98 std::string HexToBytes(const std::string& hex_data) { | 150 std::string HexToBytes(const std::string& hex_data) { |
99 std::vector<uint8_t> output; | 151 std::vector<uint8_t> output; |
100 std::string result; | 152 std::string result; |
101 if (base::HexStringToBytes(hex_data, &output)) | 153 if (base::HexStringToBytes(hex_data, &output)) |
102 result.assign(output.begin(), output.end()); | 154 result.assign(output.begin(), output.end()); |
103 return result; | 155 return result; |
104 } | 156 } |
105 | 157 |
106 const std::string& GetEmptyTreeHash() { | 158 const std::string& GetEmptyTreeHash() { |
107 static const std::string empty_hash = HexToBytes(kSHA256EmptyTreeHash); | 159 static const std::string empty_hash = HexToBytes(kSHA256EmptyTreeHash); |
108 return empty_hash; | 160 return empty_hash; |
109 } | 161 } |
110 | 162 |
111 // Creates a ct::MerkleConsistencyProof and returns the result of | 163 // Creates a ct::MerkleConsistencyProof and returns the result of |
112 // calling log->VerifyConsistencyProof with that proof and snapshots. | 164 // calling log->VerifyConsistencyProof with that proof and snapshots. |
113 bool VerifyConsistencyProof(scoped_refptr<const CTLogVerifier> log, | 165 bool VerifyConsistencyProof(scoped_refptr<const CTLogVerifier> log, |
114 uint64_t old_tree_size, | 166 uint64_t old_tree_size, |
115 const std::string& old_tree_root, | 167 const std::string& old_tree_root, |
116 uint64_t new_tree_size, | 168 uint64_t new_tree_size, |
117 const std::string& new_tree_root, | 169 const std::string& new_tree_root, |
118 const std::vector<std::string>& proof) { | 170 const std::vector<std::string>& proof) { |
119 return log->VerifyConsistencyProof( | 171 return log->VerifyConsistencyProof( |
120 ct::MerkleConsistencyProof(log->key_id(), proof, old_tree_size, | 172 ct::MerkleConsistencyProof(log->key_id(), proof, old_tree_size, |
121 new_tree_size), | 173 new_tree_size), |
122 old_tree_root, new_tree_root); | 174 old_tree_root, new_tree_root); |
123 } | 175 } |
124 | 176 |
177 bool VerifyAuditProof(scoped_refptr<const CTLogVerifier> log, | |
Ryan Sleevi
2016/08/25 18:56:24
STYLE: https://chromium.googlesource.com/chromium/
Rob Percival
2016/10/03 17:39:26
Done.
| |
178 uint64_t leaf, | |
179 uint64_t tree_size, | |
180 const std::vector<std::string>& proof, | |
181 const std::string& tree_root, | |
182 const std::string& leaf_hash) { | |
183 // Test vectors use a 1-based leaf index, but CTLogVerifier uses a 0-based | |
184 // index. We need to map |leaf| to a 0-based index but there is no mapping for | |
185 // 0, so reject it. Subtract 1 from any other value. | |
186 if (leaf == 0) | |
187 return false; | |
188 return log->VerifyAuditProof(ct::MerkleAuditProof(leaf - 1, tree_size, proof), | |
189 tree_root, leaf_hash); | |
190 } | |
191 | |
125 class CTLogVerifierTest : public ::testing::Test { | 192 class CTLogVerifierTest : public ::testing::Test { |
126 public: | 193 public: |
127 CTLogVerifierTest() {} | 194 CTLogVerifierTest() {} |
128 | 195 |
129 void SetUp() override { | 196 void SetUp() override { |
130 log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog", | 197 log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog", |
131 "https://ct.example.com", "ct.example.com"); | 198 "https://ct.example.com", "ct.example.com"); |
132 | 199 |
133 ASSERT_TRUE(log_); | 200 ASSERT_TRUE(log_); |
134 ASSERT_EQ(ct::GetTestPublicKeyId(), log_->key_id()); | 201 ASSERT_EQ(ct::GetTestPublicKeyId(), log_->key_id()); |
135 ASSERT_EQ("ct.example.com", log_->dns_domain()); | 202 ASSERT_EQ("ct.example.com", log_->dns_domain()); |
136 } | 203 } |
137 | 204 |
205 // Given an audit proof for a leaf in the tree, asserts that it verifies and | |
206 // no other combination of leaves, snapshots and proof nodes verifies. | |
207 void VerifierCheck(int leaf, | |
208 int tree_size, | |
209 const std::vector<std::string>& path, | |
210 const std::string& root, | |
211 const std::string& leaf_hash) { | |
212 // Verify the original path. | |
213 EXPECT_TRUE(VerifyAuditProof(log_, leaf, tree_size, path, root, leaf_hash)); | |
214 | |
215 // Wrong leaf index. | |
216 EXPECT_FALSE( | |
217 VerifyAuditProof(log_, leaf - 1, tree_size, path, root, leaf_hash)); | |
218 EXPECT_FALSE( | |
219 VerifyAuditProof(log_, leaf + 1, tree_size, path, root, leaf_hash)); | |
220 EXPECT_FALSE( | |
221 VerifyAuditProof(log_, leaf ^ 2, tree_size, path, root, leaf_hash)); | |
222 | |
223 // Wrong tree height. | |
224 EXPECT_FALSE( | |
225 VerifyAuditProof(log_, leaf, tree_size * 2, path, root, leaf_hash)); | |
226 EXPECT_FALSE( | |
227 VerifyAuditProof(log_, leaf, tree_size / 2, path, root, leaf_hash)); | |
228 | |
229 // Wrong root. | |
230 EXPECT_FALSE(VerifyAuditProof(log_, leaf, tree_size, path, | |
231 GetEmptyTreeHash(), leaf_hash)); | |
232 | |
233 // Wrong paths. | |
234 std::vector<std::string> wrong_path; | |
235 | |
236 // Modify a single element on the path. | |
237 for (size_t j = 0; j < path.size(); ++j) { | |
238 wrong_path = path; | |
239 wrong_path[j] = HexToBytes(kSHA256EmptyTreeHash); | |
240 EXPECT_FALSE( | |
241 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
242 } | |
243 | |
244 // Add garbage at the end of the path. | |
245 wrong_path = path; | |
246 wrong_path.push_back(std::string()); | |
247 EXPECT_FALSE( | |
248 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
249 wrong_path.pop_back(); | |
250 | |
251 wrong_path.push_back(root); | |
Ryan Sleevi
2016/08/25 18:56:24
PERFORMANCE: Since you were concerned about perf i
Rob Percival
2016/10/03 17:39:25
Done.
| |
252 EXPECT_FALSE( | |
253 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
254 wrong_path.pop_back(); | |
255 | |
256 // Remove a node from the end. | |
257 if (!wrong_path.empty()) { | |
258 wrong_path.pop_back(); | |
259 EXPECT_FALSE( | |
260 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
261 } | |
262 | |
263 // Add garbage in the beginning of the path. | |
264 wrong_path.clear(); | |
265 wrong_path.push_back(std::string()); | |
266 wrong_path.insert(wrong_path.end(), path.begin(), path.end()); | |
267 EXPECT_FALSE( | |
268 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
269 | |
270 wrong_path[0] = root; | |
271 EXPECT_FALSE( | |
272 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
273 } | |
274 | |
138 // Given a consistency proof between two snapshots of the tree, asserts that | 275 // Given a consistency proof between two snapshots of the tree, asserts that |
139 // it verifies and no other combination of snapshots and proof nodes verifies. | 276 // it verifies and no other combination of snapshots and proof nodes verifies. |
140 void VerifierConsistencyCheck(int snapshot1, | 277 void VerifierConsistencyCheck(int snapshot1, |
141 int snapshot2, | 278 int snapshot2, |
142 const std::string& root1, | 279 const std::string& root1, |
143 const std::string& root2, | 280 const std::string& root2, |
144 const std::vector<std::string>& proof) { | 281 const std::vector<std::string>& proof) { |
145 // Verify the original consistency proof. | 282 // Verify the original consistency proof. |
146 EXPECT_TRUE( | 283 EXPECT_TRUE( |
147 VerifyConsistencyProof(log_, snapshot1, root1, snapshot2, root2, proof)) | 284 VerifyConsistencyProof(log_, snapshot1, root1, snapshot2, root2, proof)) |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
354 // proofs with redundant nodes in this case). | 491 // proofs with redundant nodes in this case). |
355 proof.push_back(empty_tree_hash); | 492 proof.push_back(empty_tree_hash); |
356 EXPECT_FALSE(VerifyConsistencyProof(log_, 0, empty_tree_hash, 0, | 493 EXPECT_FALSE(VerifyConsistencyProof(log_, 0, empty_tree_hash, 0, |
357 empty_tree_hash, proof)); | 494 empty_tree_hash, proof)); |
358 EXPECT_FALSE(VerifyConsistencyProof(log_, 0, empty_tree_hash, 1, | 495 EXPECT_FALSE(VerifyConsistencyProof(log_, 0, empty_tree_hash, 1, |
359 empty_tree_hash, proof)); | 496 empty_tree_hash, proof)); |
360 EXPECT_FALSE(VerifyConsistencyProof(log_, 1, empty_tree_hash, 1, | 497 EXPECT_FALSE(VerifyConsistencyProof(log_, 1, empty_tree_hash, 1, |
361 empty_tree_hash, proof)); | 498 empty_tree_hash, proof)); |
362 } | 499 } |
363 | 500 |
364 TEST_F(CTLogVerifierTest, VerifiesValidConsistencyProofs) { | 501 class CTLogVerifierConsistencyProofTest |
502 : public CTLogVerifierTest, | |
503 public ::testing::WithParamInterface<size_t> {}; | |
Ryan Sleevi
2016/08/25 18:56:24
<size_t /* tree_size */> ?
Rob Percival
2016/10/03 17:39:25
Done.
| |
504 | |
505 TEST_P(CTLogVerifierConsistencyProofTest, VerifiesValidConsistencyProof) { | |
506 const size_t i = GetParam(); | |
365 std::vector<std::string> proof; | 507 std::vector<std::string> proof; |
366 std::string root1, root2; | 508 for (size_t j = 0; j < kSHA256Proofs[i].proof_length; ++j) { |
509 proof.push_back(HexToBytes(kSHA256Proofs[i].proof[j])); | |
510 } | |
511 const uint64_t snapshot1 = kSHA256Proofs[i].snapshot1; | |
512 const uint64_t snapshot2 = kSHA256Proofs[i].snapshot2; | |
513 const char* const old_root = kSHA256Roots[snapshot1 - 1]; | |
514 const char* const new_root = kSHA256Roots[snapshot2 - 1]; | |
515 VerifierConsistencyCheck(snapshot1, snapshot2, HexToBytes(old_root), | |
516 HexToBytes(new_root), proof); | |
517 } | |
367 | 518 |
368 // Known good proofs. | 519 INSTANTIATE_TEST_CASE_P(KnownGoodProofs, |
369 for (size_t i = 0; i < arraysize(kSHA256Proofs); ++i) { | 520 CTLogVerifierConsistencyProofTest, |
370 SCOPED_TRACE(i); | 521 ::testing::Range(0ul, arraysize(kSHA256Proofs))); |
371 proof.clear(); | |
372 for (size_t j = 0; j < kSHA256Proofs[i].proof_length; ++j) { | |
373 const char* const v = kSHA256Proofs[i].proof[j]; | |
374 proof.push_back(HexToBytes(v)); | |
375 } | |
376 const uint64_t snapshot1 = kSHA256Proofs[i].snapshot1; | |
377 const uint64_t snapshot2 = kSHA256Proofs[i].snapshot2; | |
378 const char* const old_root = kSHA256Roots[snapshot1 - 1]; | |
379 const char* const new_root = kSHA256Roots[snapshot2 - 1]; | |
380 VerifierConsistencyCheck(snapshot1, snapshot2, HexToBytes(old_root), | |
381 HexToBytes(new_root), proof); | |
382 } | |
383 } | |
384 | 522 |
385 const char kLeafPrefix[] = {'\x00'}; | 523 const char kLeafPrefix[] = {'\x00'}; |
386 | 524 |
387 // Reference implementation of RFC6962. | 525 // Reference implementation of RFC6962. |
388 // This allows generation of arbitrary-sized Merkle trees and consistency | 526 // This allows generation of arbitrary-sized Merkle trees and consistency |
389 // proofs between them for testing the consistency proof validation | 527 // proofs between them for testing the consistency proof validation |
390 // code. | 528 // code. |
391 class TreeHasher { | 529 class TreeHasher { |
392 public: | 530 public: |
393 static std::string HashLeaf(const std::string& leaf) { | 531 static std::string HashLeaf(const std::string& leaf) { |
(...skipping 20 matching lines...) Expand all Loading... | |
414 if (input_size == 1) | 552 if (input_size == 1) |
415 return TreeHasher::HashLeaf(inputs[0]); | 553 return TreeHasher::HashLeaf(inputs[0]); |
416 | 554 |
417 const uint64_t split = CalculateNearestPowerOfTwo(input_size); | 555 const uint64_t split = CalculateNearestPowerOfTwo(input_size); |
418 | 556 |
419 return ct::internal::HashNodes( | 557 return ct::internal::HashNodes( |
420 ReferenceMerkleTreeHash(&inputs[0], split), | 558 ReferenceMerkleTreeHash(&inputs[0], split), |
421 ReferenceMerkleTreeHash(&inputs[split], input_size - split)); | 559 ReferenceMerkleTreeHash(&inputs[split], input_size - split)); |
422 } | 560 } |
423 | 561 |
562 // Reference implementation of Merkle paths. Path from leaf to root, | |
563 // excluding the leaf and root themselves. Returns an audit proof for the tree | |
564 // leaf with index |leaf| (1-based). The tree is designated by |inputs|. | |
Ryan Sleevi
2016/08/25 18:56:24
I'm completely struggling to understand what this
Rob Percival
2016/10/03 17:39:26
Don't worry about "piling on", I didn't even write
| |
565 std::vector<std::string> ReferenceMerklePath(std::string* inputs, | |
Ryan Sleevi
2016/08/25 18:56:24
DESIGN: Do you need to pass as std::string? Or wou
Rob Percival
2016/10/03 17:39:26
Each element of |inputs| can be an arbitrary strin
| |
566 uint64_t input_size, | |
567 uint64_t leaf) { | |
Ryan Sleevi
2016/08/25 18:56:24
STYLE: These should be size_t, not uint64_t - http
Rob Percival
2016/10/03 17:39:25
Done.
| |
568 std::vector<std::string> path; | |
569 if (leaf > input_size || leaf == 0) | |
570 return path; | |
571 | |
572 if (input_size == 1) | |
573 return path; | |
574 | |
575 const uint64_t split = CalculateNearestPowerOfTwo(input_size); | |
576 | |
577 std::vector<std::string> subpath; | |
578 if (leaf <= split) { | |
579 subpath = ReferenceMerklePath(&inputs[0], split, leaf); | |
580 path.insert(path.end(), subpath.begin(), subpath.end()); | |
581 path.push_back(ReferenceMerkleTreeHash(&inputs[split], input_size - split)); | |
582 } else { | |
583 subpath = | |
584 ReferenceMerklePath(&inputs[split], input_size - split, leaf - split); | |
585 path.insert(path.end(), subpath.begin(), subpath.end()); | |
586 path.push_back(ReferenceMerkleTreeHash(&inputs[0], split)); | |
587 } | |
Ryan Sleevi
2016/08/25 18:56:24
You should add more documentation here as to expla
Rob Percival
2016/10/03 17:39:25
Done.
| |
588 | |
589 return path; | |
590 } | |
591 | |
424 // Reference implementation of snapshot consistency. Returns a | 592 // Reference implementation of snapshot consistency. Returns a |
425 // consistency proof between two snapshots of the tree designated | 593 // consistency proof between two snapshots of the tree designated |
426 // by |inputs|. | 594 // by |inputs|. |
427 // Call with have_root1 = true. | 595 // Call with have_root1 = true. |
428 std::vector<std::string> ReferenceSnapshotConsistency(std::string* inputs, | 596 std::vector<std::string> ReferenceSnapshotConsistency(std::string* inputs, |
429 uint64_t snapshot2, | 597 uint64_t snapshot2, |
430 uint64_t snapshot1, | 598 uint64_t snapshot1, |
431 bool have_root1) { | 599 bool have_root1) { |
432 std::vector<std::string> proof; | 600 std::vector<std::string> proof; |
433 if (snapshot1 == 0 || snapshot1 > snapshot2) | 601 if (snapshot1 == 0 || snapshot1 > snapshot2) |
(...skipping 27 matching lines...) Expand all Loading... | |
461 // doesn't contain the root of snapshot1, so set have_root1 = false. | 629 // doesn't contain the root of snapshot1, so set have_root1 = false. |
462 subproof = ReferenceSnapshotConsistency(&inputs[split], snapshot2 - split, | 630 subproof = ReferenceSnapshotConsistency(&inputs[split], snapshot2 - split, |
463 snapshot1 - split, false); | 631 snapshot1 - split, false); |
464 proof.insert(proof.end(), subproof.begin(), subproof.end()); | 632 proof.insert(proof.end(), subproof.begin(), subproof.end()); |
465 // Record the hash of the left subtree (equal in both trees). | 633 // Record the hash of the left subtree (equal in both trees). |
466 proof.push_back(ReferenceMerkleTreeHash(&inputs[0], split)); | 634 proof.push_back(ReferenceMerkleTreeHash(&inputs[0], split)); |
467 } | 635 } |
468 return proof; | 636 return proof; |
469 } | 637 } |
470 | 638 |
639 TEST_F(CTLogVerifierTest, VerifiesAuditProofEdgeCases_InvalidLeafIndex) { | |
640 std::vector<std::string> path; | |
641 EXPECT_FALSE( | |
642 VerifyAuditProof(log_, 1, 0, path, std::string(), std::string())); | |
643 EXPECT_FALSE( | |
644 VerifyAuditProof(log_, 2, 1, path, std::string(), std::string())); | |
645 | |
646 const std::string empty_hash = HexToBytes(kSHA256EmptyTreeHash); | |
647 EXPECT_FALSE(VerifyAuditProof(log_, 1, 0, path, empty_hash, std::string())); | |
648 EXPECT_FALSE(VerifyAuditProof(log_, 2, 1, path, empty_hash, std::string())); | |
649 } | |
650 | |
651 class CTLogVerifierAuditProofTest | |
652 : public CTLogVerifierTest, | |
653 public ::testing::WithParamInterface<size_t> {}; | |
654 | |
655 TEST_P(CTLogVerifierAuditProofTest, VerifiesValidAuditProofs) { | |
656 size_t i = GetParam(); | |
657 // Construct the path. | |
658 std::vector<std::string> path; | |
659 for (size_t j = 0; j < kSHA256Paths[i].path_length; ++j) | |
660 path.push_back(HexToBytes(kSHA256Paths[i].path[j])); | |
661 const char* const root_hash = kSHA256Roots[kSHA256Paths[i].snapshot - 1]; | |
662 VerifierCheck(kSHA256Paths[i].leaf, kSHA256Paths[i].snapshot, path, | |
663 HexToBytes(root_hash), | |
664 HexToBytes(kSHA256LeafHashes[kSHA256Paths[i].leaf - 1])); | |
665 } | |
666 | |
667 // kSHA256Paths[0] is an invalid path, so only use elements 1-5. | |
668 INSTANTIATE_TEST_CASE_P(KnownGoodPaths, | |
669 CTLogVerifierAuditProofTest, | |
670 ::testing::Range(1ul, arraysize(kSHA256Paths))); | |
671 | |
471 class CTLogVerifierTestUsingReferenceGenerator | 672 class CTLogVerifierTestUsingReferenceGenerator |
472 : public CTLogVerifierTest, | 673 : public CTLogVerifierTest, |
473 public ::testing::WithParamInterface<uint64_t> {}; | 674 public ::testing::WithParamInterface<uint64_t> {}; |
474 | 675 |
475 const uint64_t kReferenceTreeSize = 256; | 676 const uint64_t kReferenceTreeSize = 256; |
476 | 677 |
477 // Tests that every possible valid consistency proof for a tree of a given size | |
478 // verifies correctly. Also checks that invalid variations of each proof fail to | |
479 // verify (see VerifierConsistencyCheck). | |
480 TEST_P(CTLogVerifierTestUsingReferenceGenerator, | 678 TEST_P(CTLogVerifierTestUsingReferenceGenerator, |
481 VerifiesValidConsistencyProof) { | 679 VerifiesValidConsistencyProof) { |
482 std::vector<std::string> data; | 680 std::vector<std::string> data; |
483 for (uint64_t i = 0; i < kReferenceTreeSize; ++i) | 681 for (uint64_t i = 0; i < kReferenceTreeSize; ++i) |
484 data.push_back(std::string(1, static_cast<char>(i))); | 682 data.emplace_back(1, static_cast<char>(i)); |
485 | 683 |
486 const uint64_t tree_size = GetParam(); | 684 const uint64_t tree_size = GetParam(); |
487 const std::string tree_root = ReferenceMerkleTreeHash(data.data(), tree_size); | 685 const std::string tree_root = ReferenceMerkleTreeHash(data.data(), tree_size); |
488 | 686 |
489 for (uint64_t snapshot = 1; snapshot <= tree_size; ++snapshot) { | 687 for (uint64_t snapshot = 1; snapshot <= tree_size; ++snapshot) { |
490 SCOPED_TRACE(snapshot); | 688 SCOPED_TRACE(snapshot); |
491 const std::string snapshot_root = | 689 const std::string snapshot_root = |
492 ReferenceMerkleTreeHash(data.data(), snapshot); | 690 ReferenceMerkleTreeHash(data.data(), snapshot); |
493 const std::vector<std::string> proof = | 691 const std::vector<std::string> proof = |
494 ReferenceSnapshotConsistency(data.data(), tree_size, snapshot, true); | 692 ReferenceSnapshotConsistency(data.data(), tree_size, snapshot, true); |
495 VerifierConsistencyCheck(snapshot, tree_size, snapshot_root, tree_root, | 693 VerifierConsistencyCheck(snapshot, tree_size, snapshot_root, tree_root, |
496 proof); | 694 proof); |
497 } | 695 } |
498 } | 696 } |
499 | 697 |
500 // Test verification of consistency proofs between all tree sizes from 1 to 128. | 698 TEST_P(CTLogVerifierTestUsingReferenceGenerator, VerifiesValidAuditProofs) { |
501 INSTANTIATE_TEST_CASE_P(RangeOfTreeSizesAndSnapshots, | 699 std::vector<std::string> data; |
700 for (uint64_t i = 0; i < kReferenceTreeSize; ++i) | |
701 data.emplace_back(1, static_cast<char>(i)); | |
702 | |
703 // More tests with reference path generator. | |
704 const uint64_t tree_size = GetParam(); | |
705 const std::string root = ReferenceMerkleTreeHash(data.data(), tree_size); | |
706 // Repeat for each leaf in range. | |
707 for (uint64_t leaf = 1; leaf <= tree_size; ++leaf) { | |
708 SCOPED_TRACE(leaf); | |
709 std::vector<std::string> path = | |
710 ReferenceMerklePath(data.data(), tree_size, leaf); | |
711 VerifierCheck(leaf, tree_size, path, root, | |
712 TreeHasher::HashLeaf(data[leaf - 1])); | |
713 } | |
714 } | |
715 | |
716 // Test verification of consistency proofs and audit proofs for all tree sizes | |
717 // from 1 to |kReferenceTreeSize / 2|. | |
718 INSTANTIATE_TEST_CASE_P(RangeOfTreeSizes, | |
502 CTLogVerifierTestUsingReferenceGenerator, | 719 CTLogVerifierTestUsingReferenceGenerator, |
503 testing::Range(UINT64_C(1), | 720 testing::Range(UINT64_C(1), |
504 (kReferenceTreeSize / 2) + 1)); | 721 (kReferenceTreeSize / 2) + 1)); |
505 | 722 |
506 } // namespace | 723 } // namespace |
507 | 724 |
508 } // namespace net | 725 } // namespace net |
OLD | NEW |