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 <map> | |
9 #include <memory> | 10 #include <memory> |
10 #include <string> | 11 #include <string> |
12 #include <utility> | |
13 #include <vector> | |
11 | 14 |
15 #include "base/command_line.h" | |
16 #include "base/macros.h" | |
12 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
13 #include "base/time/time.h" | 18 #include "base/time/time.h" |
14 #include "crypto/secure_hash.h" | 19 #include "crypto/secure_hash.h" |
15 #include "net/base/hash_value.h" | 20 #include "net/base/hash_value.h" |
16 #include "net/cert/ct_log_verifier_util.h" | 21 #include "net/cert/ct_log_verifier_util.h" |
22 #include "net/cert/merkle_audit_proof.h" | |
17 #include "net/cert/merkle_consistency_proof.h" | 23 #include "net/cert/merkle_consistency_proof.h" |
18 #include "net/cert/signed_certificate_timestamp.h" | 24 #include "net/cert/signed_certificate_timestamp.h" |
19 #include "net/cert/signed_tree_head.h" | 25 #include "net/cert/signed_tree_head.h" |
20 #include "net/test/ct_test_util.h" | 26 #include "net/test/ct_test_util.h" |
21 #include "testing/gtest/include/gtest/gtest.h" | 27 #include "testing/gtest/include/gtest/gtest.h" |
22 | 28 |
23 namespace net { | 29 namespace net { |
24 | 30 |
25 namespace { | 31 namespace { |
26 | 32 |
33 // Disables the node hash cache used to accelerate some of the tests. | |
34 // The cache reduces the duration of some tests by at least 50%, in exchange for | |
35 // significantly higher memory usage. | |
36 const char* kNoHashCacheFlag = "no-hash-cache"; | |
Ryan Sleevi
2016/07/25 19:41:41
This is not a good design for unittests to have ar
Rob Percival
2016/07/26 14:24:29
Ok, I'll drop the flag. It really only exists for
| |
37 | |
27 // Calculate the power of two nearest to, but less than, |n|. | 38 // Calculate the power of two nearest to, but less than, |n|. |
28 // |n| must be at least 2. | 39 // |n| must be at least 2. |
29 uint64_t CalculateNearestPowerOfTwo(uint64_t n) { | 40 uint64_t CalculateNearestPowerOfTwo(uint64_t n) { |
30 DCHECK_GT(n, 1u); | 41 DCHECK_GT(n, 1u); |
31 | 42 |
32 uint64_t ret = UINT64_C(1) << 63; | 43 uint64_t ret = UINT64_C(1) << 63; |
33 while (ret >= n) | 44 while (ret >= n) |
34 ret >>= 1; | 45 ret >>= 1; |
35 | 46 |
36 return ret; | 47 return ret; |
37 } | 48 } |
38 | 49 |
39 // The following structures, TestVector and ProofTestVector are used for | 50 // The following structures, TestVector and ProofTestVector are used for |
40 // definining test proofs later on. | 51 // definining test proofs later on. |
41 | 52 |
42 // A single hash node. | 53 // A single hash node. |
43 struct TestVector { | 54 struct TestVector { |
44 const char* const str; | 55 const char* const str; // hex string |
45 size_t length_bytes; | 56 size_t length_bytes; // number of bytes represented by |str| |
46 }; | 57 }; |
47 | 58 |
48 // A single consistency proof. Contains the old and new tree sizes | 59 // A single consistency proof. Contains the old and new tree sizes |
49 // (snapshot1 and snapshot2), the length of the proof (proof_length) and | 60 // (snapshot1 and snapshot2), the length of the proof (proof_length) and |
50 // at most 3 proof nodes (all test proofs will be for a tree of size 8). | 61 // at most 3 proof nodes (all test proofs will be for a tree of size 8). |
51 struct ProofTestVector { | 62 struct ProofTestVector { |
52 uint64_t snapshot1; | 63 uint64_t snapshot1; |
53 uint64_t snapshot2; | 64 uint64_t snapshot2; |
54 size_t proof_length; | 65 size_t proof_length; |
55 TestVector proof[3]; | 66 TestVector proof[3]; |
56 }; | 67 }; |
57 | 68 |
58 // All test data replicated from | 69 // All test data replicated from |
59 // https://github.com/google/certificate-transparency/blob/c41b090ecc14ddd6b3531 dc7e5ce36b21e253fdd/cpp/merkletree/merkle_tree_test.cc | 70 // https://github.com/google/certificate-transparency/blob/c41b090ecc14ddd6b3531 dc7e5ce36b21e253fdd/cpp/merkletree/merkle_tree_test.cc |
60 // A hash of the empty string. | 71 // A hash of the empty string. |
61 const TestVector kSHA256EmptyTreeHash = { | 72 const TestVector kSHA256EmptyTreeHash = { |
62 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 32}; | 73 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 32}; |
63 | 74 |
64 // Node hashes for a sample tree of size 8 (each element in this array is | 75 // Hashes of the leaf data for the sample tree (8 leaves). |
65 // a node hash, not leaf data; order represents order of the nodes in the tree). | 76 const TestVector kSHA256LeafHashes[8] = { |
77 {"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32}, | |
78 {"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32}, | |
79 {"0298d122906dcfc10892cb53a73992fc5b9f493ea4c9badb27b791b4127a7fe7", 32}, | |
80 {"07506a85fd9dd2f120eb694f86011e5bb4662e5c415a62917033d4a9624487e7", 32}, | |
81 {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32}, | |
82 {"4271a26be0d8a84f0bd54c8c302e7cb3a3b5d1fa6780a40bcce2873477dab658", 32}, | |
83 {"b08693ec2e721597130641e8211e7eedccb4c26413963eee6c1e2ed16ffb1a5f", 32}, | |
84 {"46f6ffadd3d06a09ff3c5860d2755c8b9819db7df44251788c7d8e3180de8eb1", 32}, | |
85 }; | |
86 | |
87 // Incremental roots from building the sample tree of size 8 leaf-by-leaf. | |
88 // The first entry is the root at size 0, the last is the root at size 8. | |
66 const TestVector kSHA256Roots[8] = { | 89 const TestVector kSHA256Roots[8] = { |
67 {"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32}, | 90 {"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32}, |
68 {"fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32}, | 91 {"fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32}, |
69 {"aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", 32}, | 92 {"aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", 32}, |
70 {"d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32}, | 93 {"d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32}, |
71 {"4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", 32}, | 94 {"4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", 32}, |
72 {"76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", 32}, | 95 {"76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", 32}, |
73 {"ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", 32}, | 96 {"ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", 32}, |
74 {"5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", 32}}; | 97 {"5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", 32}}; |
75 | 98 |
76 // A collection of consistency proofs between various sub-trees of the tree | 99 // A collection of consistency proofs between various nodes of the sample tree. |
77 // defined by |kSHA256Roots|. | |
78 const ProofTestVector kSHA256Proofs[4] = { | 100 const ProofTestVector kSHA256Proofs[4] = { |
79 // Empty consistency proof between trees of the same size (1). | 101 // Empty consistency proof between trees of the same size (1). |
80 {1, 1, 0, {{"", 0}, {"", 0}, {"", 0}}}, | 102 {1, 1, 0, {{"", 0}, {"", 0}, {"", 0}}}, |
81 // Consistency proof between tree of size 1 and tree of size 8, with 3 | 103 // Consistency proof between tree of size 1 and tree of size 8, with 3 |
82 // nodes in the proof. | 104 // nodes in the proof. |
83 {1, | 105 {1, |
84 8, | 106 8, |
85 3, | 107 3, |
86 {{"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32}, | 108 {{"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32}, |
87 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, | 109 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, |
(...skipping 10 matching lines...) Expand all Loading... | |
98 32}}}, | 120 32}}}, |
99 // Consistency proof between tree of size 2 and tree of size 5, with 2 | 121 // Consistency proof between tree of size 2 and tree of size 5, with 2 |
100 // nodes in the proof. | 122 // nodes in the proof. |
101 {2, | 123 {2, |
102 5, | 124 5, |
103 2, | 125 2, |
104 {{"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, | 126 {{"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, |
105 {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32}, | 127 {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32}, |
106 {"", 0}}}}; | 128 {"", 0}}}}; |
107 | 129 |
130 // A single audit proof. Contains the leaf index (leaf), tree size (snapshot), | |
131 // the length of the proof (proof_length) and at most 3 proof nodes (all test | |
132 // proofs will be for a tree of size 8). | |
133 struct PathTestVector { | |
134 int leaf; | |
135 int snapshot; | |
136 int path_length; | |
137 TestVector path[3]; | |
138 }; | |
139 | |
140 // A collection of audit proofs for various leaves and sub-trees of the tree | |
141 // defined by |kSHA256Roots|. | |
142 const PathTestVector kSHA256Paths[6] = { | |
143 {0, 0, 0, {{"", 0}, {"", 0}, {"", 0}}}, | |
144 {1, 1, 0, {{"", 0}, {"", 0}, {"", 0}}}, | |
145 {1, | |
146 8, | |
147 3, | |
148 {{"96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32}, | |
149 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, | |
150 {"6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", | |
151 32}}}, | |
152 {6, | |
153 8, | |
154 3, | |
155 {{"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32}, | |
156 {"ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32}, | |
157 {"d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", | |
158 32}}}, | |
159 {3, | |
160 3, | |
161 1, | |
162 {{"fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32}, | |
163 {"", 0}, | |
164 {"", 0}}}, | |
165 {2, | |
166 5, | |
167 3, | |
168 {{"6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32}, | |
169 {"5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32}, | |
170 {"bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", | |
171 32}}}}; | |
172 | |
108 // Decodes a hexadecimal string into the binary data it represents. | 173 // Decodes a hexadecimal string into the binary data it represents. |
109 std::string HexToBytes(const char* hex_data, size_t hex_data_length) { | 174 std::string HexToBytes(const char* hex_data, size_t hex_data_length) { |
110 std::vector<uint8_t> output; | 175 std::vector<uint8_t> output; |
111 std::string result; | 176 std::string result; |
112 std::string hex_data_input(hex_data, hex_data_length); | 177 std::string hex_data_input(hex_data, hex_data_length); |
113 if (base::HexStringToBytes(hex_data, &output)) | 178 if (base::HexStringToBytes(hex_data, &output)) |
114 result.assign(reinterpret_cast<const char*>(&output[0]), output.size()); | 179 result.assign(reinterpret_cast<const char*>(&output[0]), output.size()); |
115 return result; | 180 return result; |
116 } | 181 } |
117 | 182 |
183 std::string HexToBytes(const TestVector& x) { | |
184 std::string bytes = HexToBytes(x.str, x.length_bytes * 2); | |
185 CHECK_EQ(x.length_bytes, bytes.size()); | |
186 return bytes; | |
187 } | |
188 | |
118 std::string GetEmptyTreeHash() { | 189 std::string GetEmptyTreeHash() { |
119 return HexToBytes(kSHA256EmptyTreeHash.str, | 190 return HexToBytes(kSHA256EmptyTreeHash); |
120 kSHA256EmptyTreeHash.length_bytes); | |
121 } | 191 } |
122 | 192 |
123 // Creates a ct::MerkleConsistencyProof and returns the result of | 193 // Creates a ct::MerkleConsistencyProof and returns the result of |
124 // calling log->VerifyConsistencyProof with that proof and snapshots. | 194 // calling log->VerifyConsistencyProof with that proof and snapshots. |
125 bool VerifyConsistencyProof(scoped_refptr<const CTLogVerifier> log, | 195 bool VerifyConsistencyProof(scoped_refptr<const CTLogVerifier> log, |
126 uint64_t old_tree_size, | 196 uint64_t old_tree_size, |
127 const std::string& old_tree_root, | 197 const std::string& old_tree_root, |
128 uint64_t new_tree_size, | 198 uint64_t new_tree_size, |
129 const std::string& new_tree_root, | 199 const std::string& new_tree_root, |
130 const std::vector<std::string>& proof) { | 200 const std::vector<std::string>& proof) { |
131 return log->VerifyConsistencyProof( | 201 return log->VerifyConsistencyProof( |
132 ct::MerkleConsistencyProof(log->key_id(), proof, old_tree_size, | 202 ct::MerkleConsistencyProof(log->key_id(), proof, old_tree_size, |
133 new_tree_size), | 203 new_tree_size), |
134 old_tree_root, new_tree_root); | 204 old_tree_root, new_tree_root); |
135 } | 205 } |
136 | 206 |
207 bool VerifyAuditProof(scoped_refptr<const CTLogVerifier> log, | |
208 uint64_t leaf, | |
209 uint64_t tree_size, | |
210 const std::vector<std::string>& proof, | |
211 const std::string& tree_root, | |
212 const std::string& leaf_hash) { | |
213 // Test vectors use a 1-based leaf index, but our code uses a 0-based index. | |
214 if (leaf == 0) | |
215 return false; | |
216 return log->VerifyAuditProof(ct::MerkleAuditProof(leaf - 1, tree_size, proof), | |
217 tree_root, leaf_hash); | |
218 } | |
219 | |
137 class CTLogVerifierTest : public ::testing::Test { | 220 class CTLogVerifierTest : public ::testing::Test { |
138 public: | 221 public: |
139 CTLogVerifierTest() {} | 222 CTLogVerifierTest() {} |
140 | 223 |
141 void SetUp() override { | 224 void SetUp() override { |
142 log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog", | 225 log_ = CTLogVerifier::Create(ct::GetTestPublicKey(), "testlog", |
143 "https://ct.example.com", "ct.example.com"); | 226 "https://ct.example.com", "ct.example.com"); |
144 | 227 |
145 ASSERT_TRUE(log_); | 228 ASSERT_TRUE(log_); |
146 ASSERT_EQ(ct::GetTestPublicKeyId(), log_->key_id()); | 229 ASSERT_EQ(ct::GetTestPublicKeyId(), log_->key_id()); |
147 ASSERT_EQ("ct.example.com", log_->dns_domain()); | 230 ASSERT_EQ("ct.example.com", log_->dns_domain()); |
148 } | 231 } |
149 | 232 |
233 // Given an audit proof for a leaf in the tree, asserts that it verifies and | |
234 // no other combination of leaves, snapshots and proof nodes verifies. | |
235 void VerifierCheck(int leaf, | |
236 int tree_size, | |
237 const std::vector<std::string>& path, | |
238 const std::string& root, | |
239 const std::string& leaf_hash) { | |
240 // Verify the original path. | |
241 EXPECT_TRUE(VerifyAuditProof(log_, leaf, tree_size, path, root, leaf_hash)); | |
242 | |
243 // Wrong leaf index. | |
244 EXPECT_FALSE( | |
245 VerifyAuditProof(log_, leaf - 1, tree_size, path, root, leaf_hash)); | |
246 EXPECT_FALSE( | |
247 VerifyAuditProof(log_, leaf + 1, tree_size, path, root, leaf_hash)); | |
248 EXPECT_FALSE( | |
249 VerifyAuditProof(log_, leaf ^ 2, tree_size, path, root, leaf_hash)); | |
250 | |
251 // Wrong tree height. | |
252 EXPECT_FALSE( | |
253 VerifyAuditProof(log_, leaf, tree_size * 2, path, root, leaf_hash)); | |
254 EXPECT_FALSE( | |
255 VerifyAuditProof(log_, leaf, tree_size / 2, path, root, leaf_hash)); | |
256 | |
257 // Wrong root. | |
258 EXPECT_FALSE(VerifyAuditProof(log_, leaf, tree_size, path, | |
259 HexToBytes(kSHA256EmptyTreeHash), leaf_hash)); | |
260 | |
261 // Wrong paths. | |
262 std::vector<std::string> wrong_path; | |
263 | |
264 // Modify a single element on the path. | |
265 for (size_t j = 0; j < path.size(); ++j) { | |
266 wrong_path = path; | |
267 wrong_path[j] = HexToBytes(kSHA256EmptyTreeHash); | |
268 EXPECT_FALSE( | |
269 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
270 } | |
271 | |
272 // Add garbage at the end of the path. | |
273 wrong_path = path; | |
274 wrong_path.push_back(std::string()); | |
275 EXPECT_FALSE( | |
276 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
277 wrong_path.pop_back(); | |
278 | |
279 wrong_path.push_back(root); | |
280 EXPECT_FALSE( | |
281 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
282 wrong_path.pop_back(); | |
283 | |
284 // Remove a node from the end. | |
285 if (!wrong_path.empty()) { | |
286 wrong_path.pop_back(); | |
287 EXPECT_FALSE( | |
288 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
289 } | |
290 | |
291 // Add garbage in the beginning of the path. | |
292 wrong_path.clear(); | |
293 wrong_path.push_back(std::string()); | |
294 wrong_path.insert(wrong_path.end(), path.begin(), path.end()); | |
295 EXPECT_FALSE( | |
296 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
297 | |
298 wrong_path[0] = root; | |
299 EXPECT_FALSE( | |
300 VerifyAuditProof(log_, leaf, tree_size, wrong_path, root, leaf_hash)); | |
301 } | |
302 | |
150 // Given a consistency proof between two snapshots of the tree, asserts that | 303 // Given a consistency proof between two snapshots of the tree, asserts that |
151 // it verifies and no other combination of snapshots and proof nodes verifies. | 304 // it verifies and no other combination of snapshots and proof nodes verifies. |
152 void VerifierConsistencyCheck(int snapshot1, | 305 void VerifierConsistencyCheck(int snapshot1, |
153 int snapshot2, | 306 int snapshot2, |
154 const std::string& root1, | 307 const std::string& root1, |
155 const std::string& root2, | 308 const std::string& root2, |
156 const std::vector<std::string>& proof) { | 309 const std::vector<std::string>& proof) { |
157 // Verify the original consistency proof. | 310 // Verify the original consistency proof. |
158 EXPECT_TRUE( | 311 EXPECT_TRUE( |
159 VerifyConsistencyProof(log_, snapshot1, root1, snapshot2, root2, proof)) | 312 VerifyConsistencyProof(log_, snapshot1, root1, snapshot2, root2, proof)) |
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
372 EXPECT_FALSE(VerifyConsistencyProof(log_, 1, empty_tree_hash, 1, | 525 EXPECT_FALSE(VerifyConsistencyProof(log_, 1, empty_tree_hash, 1, |
373 empty_tree_hash, proof)); | 526 empty_tree_hash, proof)); |
374 } | 527 } |
375 | 528 |
376 TEST_F(CTLogVerifierTest, VerifiesValidConsistencyProofs) { | 529 TEST_F(CTLogVerifierTest, VerifiesValidConsistencyProofs) { |
377 std::vector<std::string> proof; | 530 std::vector<std::string> proof; |
378 std::string root1, root2; | 531 std::string root1, root2; |
379 | 532 |
380 // Known good proofs. | 533 // Known good proofs. |
381 for (size_t i = 0; i < arraysize(kSHA256Proofs); ++i) { | 534 for (size_t i = 0; i < arraysize(kSHA256Proofs); ++i) { |
535 SCOPED_TRACE(i); | |
382 proof.clear(); | 536 proof.clear(); |
383 for (size_t j = 0; j < kSHA256Proofs[i].proof_length; ++j) { | 537 for (size_t j = 0; j < kSHA256Proofs[i].proof_length; ++j) { |
384 const TestVector& v = kSHA256Proofs[i].proof[j]; | 538 const TestVector& v = kSHA256Proofs[i].proof[j]; |
385 proof.push_back(HexToBytes(v.str, v.length_bytes)); | 539 proof.push_back(HexToBytes(v)); |
386 } | 540 } |
387 const uint64_t snapshot1 = kSHA256Proofs[i].snapshot1; | 541 const uint64_t snapshot1 = kSHA256Proofs[i].snapshot1; |
388 const uint64_t snapshot2 = kSHA256Proofs[i].snapshot2; | 542 const uint64_t snapshot2 = kSHA256Proofs[i].snapshot2; |
389 const TestVector& old_root = kSHA256Roots[snapshot1 - 1]; | 543 const TestVector& old_root = kSHA256Roots[snapshot1 - 1]; |
390 const TestVector& new_root = kSHA256Roots[snapshot2 - 1]; | 544 const TestVector& new_root = kSHA256Roots[snapshot2 - 1]; |
391 VerifierConsistencyCheck( | 545 VerifierConsistencyCheck(snapshot1, snapshot2, HexToBytes(old_root), |
392 snapshot1, snapshot2, HexToBytes(old_root.str, old_root.length_bytes), | 546 HexToBytes(new_root), proof); |
393 HexToBytes(new_root.str, new_root.length_bytes), proof); | |
394 } | 547 } |
395 } | 548 } |
396 | 549 |
397 const char kLeafPrefix[] = {'\x00'}; | 550 const char kLeafPrefix[] = {'\x00'}; |
398 | 551 |
399 // Reference implementation of RFC6962. | 552 // Reference implementation of RFC6962. |
400 // This allows generation of arbitrary-sized Merkle trees and consistency | 553 // This allows generation of arbitrary-sized Merkle trees and consistency |
401 // proofs between them for testing the consistency proof validation | 554 // proofs between them for testing the consistency proof validation |
402 // code. | 555 // code. |
403 class TreeHasher { | 556 class TreeHasher { |
404 public: | 557 public: |
405 static std::string HashEmpty() { | 558 static std::string HashEmpty() { return HexToBytes(kSHA256EmptyTreeHash); } |
406 return HexToBytes(kSHA256EmptyTreeHash.str, | |
407 kSHA256EmptyTreeHash.length_bytes); | |
408 } | |
409 | 559 |
410 static std::string HashLeaf(const std::string& leaf) { | 560 static std::string HashLeaf(const std::string& leaf) { |
411 SHA256HashValue sha256; | 561 SHA256HashValue sha256; |
412 memset(sha256.data, 0, sizeof(sha256.data)); | 562 memset(sha256.data, 0, sizeof(sha256.data)); |
413 | 563 |
414 std::unique_ptr<crypto::SecureHash> hash( | 564 std::unique_ptr<crypto::SecureHash> hash( |
415 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); | 565 crypto::SecureHash::Create(crypto::SecureHash::SHA256)); |
416 hash->Update(kLeafPrefix, 1); | 566 hash->Update(kLeafPrefix, 1); |
417 hash->Update(leaf.data(), leaf.size()); | 567 hash->Update(leaf.data(), leaf.size()); |
418 hash->Finish(sha256.data, sizeof(sha256.data)); | 568 hash->Finish(sha256.data, sizeof(sha256.data)); |
419 | 569 |
420 return std::string(reinterpret_cast<const char*>(sha256.data), | 570 return std::string(reinterpret_cast<const char*>(sha256.data), |
421 sizeof(sha256.data)); | 571 sizeof(sha256.data)); |
422 } | 572 } |
423 }; | 573 }; |
424 | 574 |
425 // Reference implementation of Merkle hash, for cross-checking. | 575 // Reference implementation of Merkle hash, for cross-checking. |
426 // Recursively calculates the hash of the root given the leaf data | 576 // Recursively calculates the hash of the root given the leaf data |
427 // specified in |inputs|. | 577 // specified in |inputs|. |
428 std::string ReferenceMerkleTreeHash(std::string* inputs, uint64_t input_size) { | 578 std::string ReferenceMerkleTreeHash(std::string* inputs, uint64_t input_size) { |
429 if (!input_size) | 579 // Memoize function - this requires 34 bytes of memory per unique pair of |
430 return TreeHasher::HashEmpty(); | 580 // |inputs| and |input_size|, plus std::map and std::string overhead. |
581 // This results in a ~50% reduction in time required to run the | |
582 // *FromReferenceGenerator tests. | |
583 static std::map<std::pair<std::string*, uint64_t>, std::string> cache; | |
584 // Use of the cache can be disabled using the --no-hash-cache flag, which | |
585 // makes it easy to see how much memory the cache is consuming. | |
586 static bool is_cache_enabled = | |
587 !base::CommandLine::ForCurrentProcess()->HasSwitch(kNoHashCacheFlag); | |
588 | |
589 std::string hash; | |
590 if (is_cache_enabled) { | |
591 auto iter = cache.find(std::make_pair(inputs, input_size)); | |
592 if (iter != cache.end()) | |
593 hash = iter->second; | |
594 } | |
595 | |
596 if (hash.empty()) { | |
Ryan Sleevi
2016/07/25 19:41:41
DESIGN: Do error handling first
if (!hash.empty()
Rob Percival
2016/07/26 14:24:29
That isn't error handling - it's checking whether
| |
597 if (!input_size) { | |
598 hash = TreeHasher::HashEmpty(); | |
599 } else if (input_size == 1) { | |
600 hash = TreeHasher::HashLeaf(inputs[0]); | |
601 } else { | |
602 const uint64_t split = CalculateNearestPowerOfTwo(input_size); | |
603 | |
604 hash = ct::internal::HashNodes( | |
605 ReferenceMerkleTreeHash(&inputs[0], split), | |
606 ReferenceMerkleTreeHash(&inputs[split], input_size - split)); | |
607 } | |
608 | |
609 if (is_cache_enabled) | |
610 cache[std::make_pair(inputs, input_size)] = hash; | |
611 } | |
612 | |
613 return hash; | |
614 } | |
615 | |
616 // Reference implementation of Merkle paths. Path from leaf to root, | |
617 // excluding the leaf and root themselves. Returns an audit proof for the tree | |
618 // leaf with index |leaf| (1-based). The tree is designated by |inputs|. | |
619 std::vector<std::string> ReferenceMerklePath(std::string* inputs, | |
620 uint64_t input_size, | |
621 uint64_t leaf) { | |
622 std::vector<std::string> path; | |
623 if (leaf > input_size || leaf == 0) | |
624 return path; | |
625 | |
431 if (input_size == 1) | 626 if (input_size == 1) |
432 return TreeHasher::HashLeaf(inputs[0]); | 627 return path; |
433 | 628 |
434 const uint64_t split = CalculateNearestPowerOfTwo(input_size); | 629 const uint64_t split = CalculateNearestPowerOfTwo(input_size); |
435 | 630 |
436 return ct::internal::HashNodes( | 631 std::vector<std::string> subpath; |
437 ReferenceMerkleTreeHash(&inputs[0], split), | 632 if (leaf <= split) { |
438 ReferenceMerkleTreeHash(&inputs[split], input_size - split)); | 633 subpath = ReferenceMerklePath(&inputs[0], split, leaf); |
634 path.insert(path.end(), subpath.begin(), subpath.end()); | |
635 path.push_back(ReferenceMerkleTreeHash(&inputs[split], input_size - split)); | |
636 } else { | |
637 subpath = | |
638 ReferenceMerklePath(&inputs[split], input_size - split, leaf - split); | |
639 path.insert(path.end(), subpath.begin(), subpath.end()); | |
640 path.push_back(ReferenceMerkleTreeHash(&inputs[0], split)); | |
641 } | |
642 | |
643 return path; | |
439 } | 644 } |
440 | 645 |
441 // Reference implementation of snapshot consistency. Returns a | 646 // Reference implementation of snapshot consistency. Returns a |
442 // consistency proof between two snapshots of the tree designated | 647 // consistency proof between two snapshots of the tree designated |
443 // by |inputs|. | 648 // by |inputs|. |
444 // Call with have_root1 = true. | 649 // Call with have_root1 = true. |
445 std::vector<std::string> ReferenceSnapshotConsistency(std::string* inputs, | 650 std::vector<std::string> ReferenceSnapshotConsistency(std::string* inputs, |
446 uint64_t snapshot2, | 651 uint64_t snapshot2, |
447 uint64_t snapshot1, | 652 uint64_t snapshot1, |
448 bool have_root1) { | 653 bool have_root1) { |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
496 TEST_F(CTLogVerifierTest, | 701 TEST_F(CTLogVerifierTest, |
497 MAYBE_VerifiesValidConsistencyProofsFromReferenceGenerator) { | 702 MAYBE_VerifiesValidConsistencyProofsFromReferenceGenerator) { |
498 std::vector<std::string> data; | 703 std::vector<std::string> data; |
499 for (int i = 0; i < 256; ++i) | 704 for (int i = 0; i < 256; ++i) |
500 data.push_back(std::string(1, i)); | 705 data.push_back(std::string(1, i)); |
501 | 706 |
502 std::vector<std::string> proof; | 707 std::vector<std::string> proof; |
503 std::string root1, root2; | 708 std::string root1, root2; |
504 // More tests with reference proof generator. | 709 // More tests with reference proof generator. |
505 for (size_t tree_size = 1; tree_size <= data.size() / 2; ++tree_size) { | 710 for (size_t tree_size = 1; tree_size <= data.size() / 2; ++tree_size) { |
711 SCOPED_TRACE(tree_size); | |
506 root2 = ReferenceMerkleTreeHash(data.data(), tree_size); | 712 root2 = ReferenceMerkleTreeHash(data.data(), tree_size); |
507 // Repeat for each snapshot in range. | 713 // Repeat for each snapshot in range. |
508 for (size_t snapshot = 1; snapshot <= tree_size; ++snapshot) { | 714 for (size_t snapshot = 1; snapshot <= tree_size; ++snapshot) { |
715 SCOPED_TRACE(snapshot); | |
509 proof = | 716 proof = |
510 ReferenceSnapshotConsistency(data.data(), tree_size, snapshot, true); | 717 ReferenceSnapshotConsistency(data.data(), tree_size, snapshot, true); |
511 root1 = ReferenceMerkleTreeHash(data.data(), snapshot); | 718 root1 = ReferenceMerkleTreeHash(data.data(), snapshot); |
512 VerifierConsistencyCheck(snapshot, tree_size, root1, root2, proof); | 719 VerifierConsistencyCheck(snapshot, tree_size, root1, root2, proof); |
513 } | 720 } |
514 } | 721 } |
515 } | 722 } |
516 | 723 |
724 TEST_F(CTLogVerifierTest, VerifiesAuditProofEdgeCases_EmptyProof) { | |
725 std::vector<std::string> path; | |
726 // Various invalid paths. | |
727 EXPECT_FALSE( | |
728 VerifyAuditProof(log_, 0, 0, path, std::string(), std::string())); | |
729 EXPECT_FALSE( | |
730 VerifyAuditProof(log_, 0, 1, path, std::string(), std::string())); | |
731 EXPECT_FALSE( | |
732 VerifyAuditProof(log_, 1, 0, path, std::string(), std::string())); | |
733 EXPECT_FALSE( | |
734 VerifyAuditProof(log_, 2, 1, path, std::string(), std::string())); | |
735 | |
736 const std::string empty_hash = HexToBytes(kSHA256EmptyTreeHash); | |
737 EXPECT_FALSE(VerifyAuditProof(log_, 0, 0, path, empty_hash, std::string())); | |
738 EXPECT_FALSE(VerifyAuditProof(log_, 0, 1, path, empty_hash, std::string())); | |
739 EXPECT_FALSE(VerifyAuditProof(log_, 1, 0, path, empty_hash, std::string())); | |
740 EXPECT_FALSE(VerifyAuditProof(log_, 2, 1, path, empty_hash, std::string())); | |
741 } | |
742 | |
743 TEST_F(CTLogVerifierTest, VerifiesValidAuditProofs) { | |
744 std::vector<std::string> path; | |
745 // Known good paths. | |
746 // i = 0 is an invalid path. | |
747 for (int i = 1; i < 6; ++i) { | |
748 SCOPED_TRACE(i); | |
749 // Construct the path. | |
750 path.clear(); | |
751 for (int j = 0; j < kSHA256Paths[i].path_length; ++j) | |
752 path.push_back(HexToBytes(kSHA256Paths[i].path[j])); | |
753 const TestVector& root_hash = kSHA256Roots[kSHA256Paths[i].snapshot - 1]; | |
754 VerifierCheck(kSHA256Paths[i].leaf, kSHA256Paths[i].snapshot, path, | |
755 HexToBytes(root_hash), | |
756 HexToBytes(kSHA256LeafHashes[kSHA256Paths[i].leaf - 1])); | |
757 } | |
758 } | |
759 | |
760 TEST_F(CTLogVerifierTest, VerifiesValidAuditProofsFromReferenceGenerator) { | |
761 std::vector<std::string> data; | |
762 for (size_t i = 0; i < 256; ++i) | |
763 data.emplace_back(1, i); | |
764 | |
765 // More tests with reference path generator. | |
766 std::string root; | |
767 std::vector<std::string> path; | |
768 for (size_t tree_size = 1; tree_size <= data.size() / 2; ++tree_size) { | |
769 SCOPED_TRACE(tree_size); | |
770 // Repeat for each leaf in range. | |
771 for (size_t leaf = 1; leaf <= tree_size; ++leaf) { | |
772 SCOPED_TRACE(leaf); | |
773 path = ReferenceMerklePath(data.data(), tree_size, leaf); | |
774 root = ReferenceMerkleTreeHash(data.data(), tree_size); | |
775 VerifierCheck(leaf, tree_size, path, root, | |
776 TreeHasher::HashLeaf(data[leaf - 1])); | |
777 } | |
778 } | |
779 } | |
780 | |
517 } // namespace | 781 } // namespace |
518 | 782 |
519 } // namespace net | 783 } // namespace net |
OLD | NEW |