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 <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 |