Chromium Code Reviews| 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 |