Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "net/cert/internal/path_builder.h" | |
| 6 | |
| 7 #include "base/base_paths.h" | |
| 8 #include "base/cancelable_callback.h" | |
| 9 #include "base/files/file_util.h" | |
| 10 #include "base/location.h" | |
| 11 #include "base/path_service.h" | |
| 12 #include "base/threading/thread_task_runner_handle.h" | |
| 13 #include "net/base/net_errors.h" | |
| 14 #include "net/base/test_completion_callback.h" | |
| 15 #include "net/cert/internal/cert_issuer_source_static.h" | |
| 16 #include "net/cert/internal/parsed_certificate.h" | |
| 17 #include "net/cert/internal/signature_policy.h" | |
| 18 #include "net/cert/internal/test_helpers.h" | |
| 19 #include "net/cert/internal/trust_store.h" | |
| 20 #include "net/cert/internal/verify_certificate_chain.h" | |
| 21 #include "net/cert/pem_tokenizer.h" | |
| 22 #include "net/der/input.h" | |
| 23 #include "net/test/cert_test_util.h" | |
| 24 #include "net/test/test_certificate_data.h" | |
| 25 #include "testing/gmock/include/gmock/gmock.h" | |
| 26 #include "testing/gtest/include/gtest/gtest.h" | |
| 27 | |
| 28 namespace net { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 using ::testing::_; | |
| 33 using ::testing::Invoke; | |
| 34 using ::testing::SaveArg; | |
| 35 using ::testing::StrictMock; | |
| 36 using ::testing::SetArgPointee; | |
| 37 using ::testing::Return; | |
| 38 | |
| 39 // AsyncCertIssuerSourceStatic always returns its certs asynchronously. | |
| 40 class AsyncCertIssuerSourceStatic : public CertIssuerSource { | |
| 41 public: | |
| 42 class StaticAsyncRequest : public Request { | |
| 43 public: | |
| 44 StaticAsyncRequest(const IssuerCallback& issuers_callback, | |
| 45 ParsedCertificateList&& issuers) | |
|
eroman
2016/07/01 23:49:29
side-comment: Seems legit, although not sure if st
mattm
2016/07/02 02:21:51
seems to be: https://chromium-cpp.appspot.com/ (R
| |
| 46 : cancelable_closure_(base::Bind(&StaticAsyncRequest::RunCallback, | |
| 47 base::Unretained(this))), | |
| 48 issuers_callback_(issuers_callback) { | |
| 49 issuers_.swap(issuers); | |
| 50 issuers_iter_ = issuers_.begin(); | |
| 51 } | |
| 52 ~StaticAsyncRequest() override {} | |
| 53 | |
| 54 CompletionStatus GetNext( | |
| 55 scoped_refptr<ParsedCertificate>* out_cert) override { | |
| 56 if (issuers_iter_ == issuers_.end()) | |
| 57 *out_cert = nullptr; | |
| 58 else | |
| 59 *out_cert = std::move(*issuers_iter_++); | |
| 60 return CompletionStatus::SYNC; | |
| 61 } | |
| 62 | |
| 63 base::Closure callback() { return cancelable_closure_.callback(); } | |
| 64 | |
| 65 private: | |
| 66 void RunCallback() { issuers_callback_.Run(this); } | |
| 67 | |
| 68 base::CancelableClosure cancelable_closure_; | |
| 69 IssuerCallback issuers_callback_; | |
| 70 ParsedCertificateList issuers_; | |
| 71 ParsedCertificateList::iterator issuers_iter_; | |
| 72 | |
| 73 DISALLOW_COPY_AND_ASSIGN(StaticAsyncRequest); | |
| 74 }; | |
| 75 | |
| 76 ~AsyncCertIssuerSourceStatic() override {} | |
| 77 | |
| 78 void AddCert(scoped_refptr<ParsedCertificate> cert) { | |
| 79 static_cert_issuer_source_.AddCert(std::move(cert)); | |
| 80 } | |
| 81 | |
| 82 void SyncGetIssuersOf(const ParsedCertificate* cert, | |
| 83 ParsedCertificateList* issuers) override {} | |
| 84 void AsyncGetIssuersOf(const ParsedCertificate* cert, | |
| 85 const IssuerCallback& issuers_callback, | |
| 86 std::unique_ptr<Request>* out_req) override { | |
| 87 num_async_gets_++; | |
| 88 ParsedCertificateList issuers; | |
| 89 static_cert_issuer_source_.SyncGetIssuersOf(cert, &issuers); | |
| 90 std::unique_ptr<StaticAsyncRequest> req( | |
| 91 new StaticAsyncRequest(issuers_callback, std::move(issuers))); | |
| 92 base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, req->callback()); | |
| 93 *out_req = std::move(req); | |
| 94 } | |
| 95 int num_async_gets() const { return num_async_gets_; } | |
| 96 | |
| 97 private: | |
| 98 CertIssuerSourceStatic static_cert_issuer_source_; | |
| 99 | |
| 100 int num_async_gets_ = 0; | |
| 101 }; | |
| 102 | |
| 103 // Reads a data file from the unit-test data. | |
| 104 std::string ReadTestFileToString(const std::string& file_name) { | |
| 105 // Compute the full path, relative to the src/ directory. | |
| 106 base::FilePath src_root; | |
| 107 PathService::Get(base::DIR_SOURCE_ROOT, &src_root); | |
| 108 base::FilePath filepath = src_root.AppendASCII(file_name); | |
| 109 | |
| 110 // Read the full contents of the file. | |
| 111 std::string file_data; | |
| 112 if (!base::ReadFileToString(filepath, &file_data)) { | |
| 113 ADD_FAILURE() << "Couldn't read file: " << filepath.value(); | |
| 114 return std::string(); | |
| 115 } | |
| 116 | |
| 117 return file_data; | |
| 118 } | |
| 119 | |
| 120 // Reads a verify_certificate_chain_unittest-style test case from |file_name|. | |
| 121 // Test cases are comprised of a certificate chain, trust store, a timestamp to | |
| 122 // validate at, and the expected result of verification (though the expected | |
| 123 // result is ignored here). | |
| 124 void ReadVerifyCertChainTestFromFile(const std::string& file_name, | |
| 125 std::vector<std::string>* chain, | |
|
eroman
2016/07/01 23:49:29
Should we randomize the order of the intermediates
mattm
2016/07/02 02:21:51
Probably not actual randomization (since even if i
| |
| 126 scoped_refptr<ParsedCertificate>* root, | |
| 127 der::GeneralizedTime* time) { | |
| 128 chain->clear(); | |
| 129 | |
| 130 std::string file_data = ReadTestFileToString(file_name); | |
| 131 | |
| 132 std::vector<std::string> pem_headers; | |
| 133 | |
| 134 const char kCertificateHeader[] = "CERTIFICATE"; | |
| 135 const char kTrustedCertificateHeader[] = "TRUSTED_CERTIFICATE"; | |
| 136 const char kTimeHeader[] = "TIME"; | |
| 137 | |
| 138 pem_headers.push_back(kCertificateHeader); | |
| 139 pem_headers.push_back(kTrustedCertificateHeader); | |
| 140 pem_headers.push_back(kTimeHeader); | |
| 141 | |
| 142 bool has_time = false; | |
| 143 | |
| 144 PEMTokenizer pem_tokenizer(file_data, pem_headers); | |
| 145 while (pem_tokenizer.GetNext()) { | |
| 146 const std::string& block_type = pem_tokenizer.block_type(); | |
| 147 const std::string& block_data = pem_tokenizer.data(); | |
| 148 | |
| 149 if (block_type == kCertificateHeader) { | |
| 150 chain->push_back(block_data); | |
| 151 } else if (block_type == kTrustedCertificateHeader) { | |
| 152 *root = ParsedCertificate::CreateFromCertificateCopy(block_data, {}); | |
| 153 ASSERT_TRUE(*root); | |
| 154 } else if (block_type == kTimeHeader) { | |
| 155 ASSERT_FALSE(has_time) << "Duplicate " << kTimeHeader; | |
| 156 has_time = true; | |
| 157 ASSERT_TRUE(der::ParseUTCTime(der::Input(&block_data), time)); | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 ASSERT_TRUE(has_time); | |
| 162 } | |
| 163 | |
| 164 ::testing::AssertionResult ReadTestPem(const std::string& file_name, | |
| 165 const std::string& block_name, | |
| 166 std::string* result) { | |
| 167 const PemBlockMapping mappings[] = { | |
| 168 {block_name.c_str(), result}, | |
| 169 }; | |
| 170 | |
| 171 return ReadTestDataFromPemFile(file_name, mappings); | |
| 172 } | |
| 173 | |
| 174 ::testing::AssertionResult ReadTestCert( | |
| 175 const std::string& file_name, | |
| 176 scoped_refptr<ParsedCertificate>* result) { | |
| 177 std::string der; | |
| 178 ::testing::AssertionResult r = ReadTestPem( | |
| 179 "net/data/ssl/certificates/" + file_name, "CERTIFICATE", &der); | |
| 180 if (!r) | |
| 181 return r; | |
| 182 *result = ParsedCertificate::CreateFromCertificateCopy(der, {}); | |
| 183 if (!*result) | |
| 184 return ::testing::AssertionFailure() << "CreateFromCertificateCopy failed"; | |
| 185 return ::testing::AssertionSuccess(); | |
| 186 } | |
| 187 | |
| 188 // Run the path builder, and wait for async completion if necessary. The return | |
| 189 // value signifies whether the path builder completed synchronously or | |
| 190 // asynchronously, not that RunPathBuilder itself is asynchronous. | |
| 191 CompletionStatus RunPathBuilder(CertPathBuilder* path_builder) { | |
| 192 TestClosure callback; | |
| 193 CompletionStatus rv = path_builder->Run(callback.closure()); | |
| 194 | |
| 195 if (rv == CompletionStatus::ASYNC) { | |
| 196 DVLOG(1) << "waiting for async completion..."; | |
| 197 callback.WaitForResult(); | |
| 198 DVLOG(1) << "async completed."; | |
| 199 } | |
| 200 return rv; | |
| 201 } | |
| 202 | |
| 203 class PathBuilderMultiRootTest : public ::testing::Test { | |
| 204 public: | |
| 205 PathBuilderMultiRootTest() : signature_policy_(1024) {} | |
| 206 | |
| 207 void SetUp() override { | |
| 208 ASSERT_TRUE(ReadTestCert("multi-root-A-by-B.pem", &a_by_b_)); | |
| 209 ASSERT_TRUE(ReadTestCert("multi-root-B-by-C.pem", &b_by_c_)); | |
| 210 ASSERT_TRUE(ReadTestCert("multi-root-B-by-F.pem", &b_by_f_)); | |
| 211 ASSERT_TRUE(ReadTestCert("multi-root-C-by-D.pem", &c_by_d_)); | |
| 212 ASSERT_TRUE(ReadTestCert("multi-root-C-by-E.pem", &c_by_e_)); | |
| 213 ASSERT_TRUE(ReadTestCert("multi-root-D-by-D.pem", &d_by_d_)); | |
| 214 ASSERT_TRUE(ReadTestCert("multi-root-E-by-E.pem", &e_by_e_)); | |
| 215 ASSERT_TRUE(ReadTestCert("multi-root-F-by-E.pem", &f_by_e_)); | |
| 216 } | |
| 217 | |
| 218 protected: | |
| 219 scoped_refptr<ParsedCertificate> a_by_b_, b_by_c_, b_by_f_, c_by_d_, c_by_e_, | |
| 220 d_by_d_, e_by_e_, f_by_e_; | |
| 221 | |
| 222 SimpleSignaturePolicy signature_policy_; | |
| 223 der::GeneralizedTime time_ = {2016, 4, 11, 0, 0, 0}; | |
| 224 }; | |
| 225 | |
| 226 // If the target cert is a trust anchor, it should verify and should not include | |
| 227 // anything else in the path. | |
| 228 TEST_F(PathBuilderMultiRootTest, TargetIsTrustAnchor) { | |
| 229 TrustStore trust_store; | |
| 230 trust_store.AddTrustedCertificate(a_by_b_); | |
| 231 trust_store.AddTrustedCertificate(b_by_f_); | |
| 232 | |
| 233 CertPathBuilder::Result result; | |
| 234 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 235 &result); | |
| 236 | |
| 237 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 238 | |
| 239 EXPECT_EQ(OK, result.error()); | |
| 240 EXPECT_EQ(1U, result.paths[result.best_result_index]->path.size()); | |
| 241 EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]); | |
| 242 } | |
| 243 | |
| 244 // If the target cert is directly issued by a trust anchor, it should verify | |
| 245 // without any intermediate certs being provided. | |
| 246 TEST_F(PathBuilderMultiRootTest, TargetDirectlySignedByTrustAnchor) { | |
| 247 TrustStore trust_store; | |
| 248 trust_store.AddTrustedCertificate(b_by_f_); | |
| 249 | |
| 250 CertPathBuilder::Result result; | |
| 251 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 252 &result); | |
| 253 | |
| 254 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 255 | |
| 256 EXPECT_EQ(OK, result.error()); | |
| 257 EXPECT_EQ(2U, result.paths[result.best_result_index]->path.size()); | |
| 258 EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]); | |
| 259 EXPECT_EQ(b_by_f_, result.paths[result.best_result_index]->path[1]); | |
| 260 } | |
| 261 | |
| 262 // Test that async cert queries are not made if the path can be successfully | |
| 263 // built with synchronously available certs. | |
| 264 TEST_F(PathBuilderMultiRootTest, TriesSyncFirst) { | |
| 265 TrustStore trust_store; | |
| 266 trust_store.AddTrustedCertificate(e_by_e_); | |
| 267 | |
| 268 CertIssuerSourceStatic sync_certs; | |
| 269 sync_certs.AddCert(a_by_b_); | |
|
eroman
2016/07/01 23:49:29
Does this have an effect on path building, or just
mattm
2016/07/02 02:21:51
No effect. I don't remember if that was on purpose
| |
| 270 sync_certs.AddCert(b_by_f_); | |
| 271 sync_certs.AddCert(f_by_e_); | |
| 272 | |
| 273 AsyncCertIssuerSourceStatic async_certs; | |
| 274 async_certs.AddCert(b_by_c_); | |
|
eroman
2016/07/01 23:49:29
What would happen if b_by_c were returned synchron
mattm
2016/07/02 02:21:51
Yes.
| |
| 275 async_certs.AddCert(c_by_e_); | |
| 276 | |
| 277 CertPathBuilder::Result result; | |
| 278 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 279 &result); | |
| 280 path_builder.AddCertIssuerSource(&async_certs); | |
| 281 path_builder.AddCertIssuerSource(&sync_certs); | |
| 282 | |
| 283 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 284 | |
| 285 EXPECT_EQ(OK, result.error()); | |
| 286 EXPECT_EQ(0, async_certs.num_async_gets()); | |
| 287 } | |
| 288 | |
| 289 // Test that async cert queries are not made if no callback is provided. | |
| 290 TEST_F(PathBuilderMultiRootTest, SychronousOnlyMode) { | |
| 291 TrustStore trust_store; | |
| 292 trust_store.AddTrustedCertificate(e_by_e_); | |
| 293 | |
| 294 CertIssuerSourceStatic sync_certs; | |
| 295 sync_certs.AddCert(f_by_e_); | |
| 296 | |
| 297 AsyncCertIssuerSourceStatic async_certs; | |
| 298 async_certs.AddCert(b_by_f_); | |
| 299 | |
| 300 CertPathBuilder::Result result; | |
| 301 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 302 &result); | |
| 303 path_builder.AddCertIssuerSource(&async_certs); | |
| 304 path_builder.AddCertIssuerSource(&sync_certs); | |
| 305 | |
| 306 EXPECT_EQ(CompletionStatus::SYNC, path_builder.Run(base::Closure())); | |
| 307 | |
| 308 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error()); | |
| 309 EXPECT_EQ(0, async_certs.num_async_gets()); | |
| 310 } | |
| 311 | |
| 312 // If async queries are needed, all async sources will be queried | |
| 313 // simultaneously. | |
| 314 TEST_F(PathBuilderMultiRootTest, TestAsyncSimultaneous) { | |
| 315 TrustStore trust_store; | |
| 316 trust_store.AddTrustedCertificate(e_by_e_); | |
| 317 | |
| 318 CertIssuerSourceStatic sync_certs; | |
| 319 sync_certs.AddCert(a_by_b_); | |
| 320 sync_certs.AddCert(b_by_c_); | |
| 321 sync_certs.AddCert(b_by_f_); | |
| 322 | |
| 323 AsyncCertIssuerSourceStatic async_certs1; | |
| 324 async_certs1.AddCert(c_by_e_); | |
| 325 | |
| 326 AsyncCertIssuerSourceStatic async_certs2; | |
| 327 async_certs2.AddCert(f_by_e_); | |
| 328 | |
| 329 CertPathBuilder::Result result; | |
| 330 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 331 &result); | |
| 332 path_builder.AddCertIssuerSource(&async_certs1); | |
| 333 path_builder.AddCertIssuerSource(&async_certs2); | |
| 334 path_builder.AddCertIssuerSource(&sync_certs); | |
| 335 | |
| 336 EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder)); | |
| 337 | |
| 338 EXPECT_EQ(OK, result.error()); | |
| 339 EXPECT_EQ(1, async_certs1.num_async_gets()); | |
| 340 EXPECT_EQ(1, async_certs2.num_async_gets()); | |
| 341 } | |
| 342 | |
| 343 // Test that PathBuilder does not generate longer paths than necessary if one of | |
| 344 // the supplied certs is itself a trust anchor. | |
| 345 TEST_F(PathBuilderMultiRootTest, TestLongChain) { | |
| 346 // Both D(D) and C(D) are trusted roots. | |
| 347 TrustStore trust_store; | |
| 348 trust_store.AddTrustedCertificate(d_by_d_); | |
| 349 trust_store.AddTrustedCertificate(c_by_d_); | |
| 350 | |
| 351 // Certs A(B), B(C), and C(D) are all supplied. | |
| 352 CertIssuerSourceStatic sync_certs; | |
| 353 sync_certs.AddCert(a_by_b_); | |
| 354 sync_certs.AddCert(b_by_c_); | |
| 355 sync_certs.AddCert(c_by_d_); | |
| 356 | |
| 357 CertPathBuilder::Result result; | |
| 358 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 359 &result); | |
| 360 path_builder.AddCertIssuerSource(&sync_certs); | |
| 361 | |
| 362 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 363 | |
| 364 EXPECT_EQ(OK, result.error()); | |
| 365 | |
| 366 // The result path should be A(B) <- B(C) <- C(D) | |
| 367 // not the longer but also valid A(B) <- B(C) <- C(D) <- D(D) | |
| 368 EXPECT_EQ(3U, result.paths[result.best_result_index]->path.size()); | |
| 369 } | |
| 370 | |
| 371 // Test that PathBuilder will backtrack and try a different path if the first | |
| 372 // one doesn't work out. | |
| 373 TEST_F(PathBuilderMultiRootTest, TestBacktracking) { | |
| 374 // Only D(D) is a trusted root. | |
| 375 TrustStore trust_store; | |
| 376 trust_store.AddTrustedCertificate(d_by_d_); | |
| 377 | |
| 378 // Certs A(B), B(F), and F(E) are supplied synchronously, thus the path | |
| 379 // A(B) <- B(F) <- F(E) should be built first, though it won't verify. | |
| 380 CertIssuerSourceStatic sync_certs; | |
| 381 sync_certs.AddCert(a_by_b_); | |
| 382 sync_certs.AddCert(b_by_f_); | |
| 383 sync_certs.AddCert(f_by_e_); | |
| 384 | |
| 385 // Certs B(C), and C(D) are supplied asynchronously, so the path | |
| 386 // A(B) <- B(C) <- C(D) <- D(D) should be tried second. | |
| 387 AsyncCertIssuerSourceStatic async_certs; | |
| 388 async_certs.AddCert(b_by_c_); | |
| 389 async_certs.AddCert(c_by_d_); | |
| 390 | |
| 391 CertPathBuilder::Result result; | |
| 392 CertPathBuilder path_builder(a_by_b_, &trust_store, &signature_policy_, time_, | |
| 393 &result); | |
| 394 path_builder.AddCertIssuerSource(&sync_certs); | |
| 395 path_builder.AddCertIssuerSource(&async_certs); | |
| 396 | |
| 397 EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder)); | |
| 398 | |
| 399 EXPECT_EQ(OK, result.error()); | |
| 400 | |
| 401 // The result path should be A(B) <- B(C) <- C(D) <- D(D) | |
| 402 ASSERT_EQ(4U, result.paths[result.best_result_index]->path.size()); | |
| 403 EXPECT_EQ(a_by_b_, result.paths[result.best_result_index]->path[0]); | |
| 404 EXPECT_EQ(b_by_c_, result.paths[result.best_result_index]->path[1]); | |
| 405 EXPECT_EQ(c_by_d_, result.paths[result.best_result_index]->path[2]); | |
| 406 EXPECT_EQ(d_by_d_, result.paths[result.best_result_index]->path[3]); | |
| 407 } | |
| 408 | |
| 409 class PathBuilderKeyRolloverTest : public ::testing::Test { | |
| 410 public: | |
| 411 PathBuilderKeyRolloverTest() : signature_policy_(1024) {} | |
| 412 | |
| 413 void SetUp() override { | |
| 414 std::vector<std::string> path; | |
| 415 | |
| 416 ReadVerifyCertChainTestFromFile( | |
| 417 "net/data/verify_certificate_chain_unittest/key-rollover-oldchain.pem", | |
| 418 &path, &oldroot_, &time_); | |
| 419 ASSERT_EQ(2U, path.size()); | |
| 420 target_ = ParsedCertificate::CreateFromCertificateCopy(path[0], {}); | |
| 421 oldintermediary_ = | |
| 422 ParsedCertificate::CreateFromCertificateCopy(path[1], {}); | |
| 423 ASSERT_TRUE(target_); | |
| 424 ASSERT_TRUE(oldintermediary_); | |
| 425 | |
| 426 ReadVerifyCertChainTestFromFile( | |
| 427 "net/data/verify_certificate_chain_unittest/" | |
| 428 "key-rollover-longrolloverchain.pem", | |
| 429 &path, &oldroot_, &time_); | |
| 430 ASSERT_EQ(4U, path.size()); | |
| 431 newintermediary_ = | |
| 432 ParsedCertificate::CreateFromCertificateCopy(path[1], {}); | |
| 433 newroot_ = ParsedCertificate::CreateFromCertificateCopy(path[2], {}); | |
| 434 newrootrollover_ = | |
| 435 ParsedCertificate::CreateFromCertificateCopy(path[3], {}); | |
| 436 ASSERT_TRUE(newintermediary_); | |
| 437 ASSERT_TRUE(newroot_); | |
| 438 ASSERT_TRUE(newrootrollover_); | |
| 439 } | |
| 440 | |
| 441 protected: | |
| 442 // oldroot-------->newrootrollover newroot | |
| 443 // | | | | |
| 444 // v v v | |
| 445 // oldintermediary newintermediary | |
| 446 // | | | |
| 447 // +------------+-------------+ | |
| 448 // | | |
| 449 // v | |
| 450 // target | |
| 451 scoped_refptr<ParsedCertificate> target_, oldintermediary_, newintermediary_, | |
|
eroman
2016/07/01 23:49:29
nit: one per line
mattm
2016/07/02 02:21:51
Done.
| |
| 452 oldroot_, newroot_, newrootrollover_; | |
| 453 | |
| 454 SimpleSignaturePolicy signature_policy_; | |
| 455 der::GeneralizedTime time_; | |
| 456 }; | |
| 457 | |
| 458 // Tests that if only the old root cert is trusted, the path builder can build a | |
| 459 // path through the new intermediate and rollover cert to the old root. | |
| 460 TEST_F(PathBuilderKeyRolloverTest, TestRolloverOnlyOldRootTrusted) { | |
| 461 // Only oldroot is trusted. | |
| 462 TrustStore trust_store; | |
| 463 trust_store.AddTrustedCertificate(oldroot_); | |
| 464 | |
| 465 // Old intermediary cert is not provided, so the pathbuilder will need to go | |
|
eroman
2016/07/01 23:49:29
Not sure if one is more correct than another,
but
mattm
2016/07/02 02:21:51
Done.
| |
| 466 // through the rollover cert. | |
| 467 CertIssuerSourceStatic sync_certs; | |
| 468 sync_certs.AddCert(newintermediary_); | |
| 469 sync_certs.AddCert(newrootrollover_); | |
| 470 | |
| 471 CertPathBuilder::Result result; | |
| 472 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 473 &result); | |
| 474 path_builder.AddCertIssuerSource(&sync_certs); | |
| 475 | |
| 476 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 477 | |
| 478 EXPECT_EQ(OK, result.error()); | |
| 479 | |
| 480 // Path builder will first attempt: target <- newintermediary <- oldroot | |
| 481 // but it will fail since newintermediary is signed by newroot. | |
| 482 ASSERT_EQ(2U, result.paths.size()); | |
| 483 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 484 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 485 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 486 EXPECT_EQ(newintermediary_, result.paths[0]->path[1]); | |
| 487 EXPECT_EQ(oldroot_, result.paths[0]->path[2]); | |
| 488 | |
| 489 // Path builder will next attempt: | |
| 490 // target <- newintermediary <- newrootrollover <- oldroot | |
| 491 // which will succeed. | |
| 492 EXPECT_EQ(1U, result.best_result_index); | |
| 493 EXPECT_EQ(OK, result.paths[1]->error); | |
| 494 ASSERT_EQ(4U, result.paths[1]->path.size()); | |
| 495 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 496 EXPECT_EQ(newintermediary_, result.paths[1]->path[1]); | |
| 497 EXPECT_EQ(newrootrollover_, result.paths[1]->path[2]); | |
| 498 EXPECT_EQ(oldroot_, result.paths[1]->path[3]); | |
| 499 } | |
| 500 | |
| 501 // Tests that if both old and new roots are trusted it can build a path through | |
| 502 // either. | |
| 503 // TODO(mattm): Once prioritization is implemented, it should test that it | |
| 504 // always builds the path through the new intermediary and new root. | |
| 505 TEST_F(PathBuilderKeyRolloverTest, TestRolloverBothRootsTrusted) { | |
| 506 // Both oldroot and newroot are trusted. | |
| 507 TrustStore trust_store; | |
| 508 trust_store.AddTrustedCertificate(oldroot_); | |
| 509 trust_store.AddTrustedCertificate(newroot_); | |
| 510 | |
| 511 // Both old and new intermediates + rollover cert are provided. | |
| 512 CertIssuerSourceStatic sync_certs; | |
| 513 sync_certs.AddCert(oldintermediary_); | |
| 514 sync_certs.AddCert(newintermediary_); | |
| 515 sync_certs.AddCert(newrootrollover_); | |
| 516 | |
| 517 CertPathBuilder::Result result; | |
| 518 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 519 &result); | |
| 520 path_builder.AddCertIssuerSource(&sync_certs); | |
| 521 | |
| 522 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 523 | |
| 524 EXPECT_EQ(OK, result.error()); | |
| 525 | |
| 526 // Path builder willattempt one of: | |
| 527 // target <- oldintermediary <- oldroot | |
| 528 // target <- newintermediary <- newroot | |
| 529 // either will succeed. | |
| 530 ASSERT_EQ(1U, result.paths.size()); | |
| 531 EXPECT_EQ(OK, result.paths[0]->error); | |
| 532 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 533 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 534 if (result.paths[0]->path[1] != newintermediary_) { | |
| 535 DVLOG(1) << "USED OLD"; | |
| 536 EXPECT_EQ(oldintermediary_, result.paths[0]->path[1]); | |
| 537 EXPECT_EQ(oldroot_, result.paths[0]->path[2]); | |
| 538 } else { | |
| 539 DVLOG(1) << "USED NEW"; | |
| 540 EXPECT_EQ(newintermediary_, result.paths[0]->path[1]); | |
| 541 EXPECT_EQ(newroot_, result.paths[0]->path[2]); | |
| 542 } | |
| 543 } | |
| 544 | |
| 545 // Tests that multiple trust root matches on a single path will be considered. | |
| 546 // Both roots have the same subject but different keys. Only one of them will | |
| 547 // verify. | |
| 548 TEST_F(PathBuilderKeyRolloverTest, TestMultipleRootMatchesOnlyOneWorks) { | |
| 549 // Both newroot and oldroot are trusted. | |
| 550 TrustStore trust_store; | |
| 551 // Note: The test assumes newroot will be tried before oldroot. | |
| 552 // Currently this depends on the order the roots are added. | |
| 553 trust_store.AddTrustedCertificate(newroot_); | |
| 554 trust_store.AddTrustedCertificate(oldroot_); | |
| 555 | |
| 556 // Only oldintermediary is supplied, so the path with newroot should fail, | |
| 557 // oldroot should succeed. | |
| 558 CertIssuerSourceStatic sync_certs; | |
| 559 sync_certs.AddCert(oldintermediary_); | |
| 560 | |
| 561 CertPathBuilder::Result result; | |
| 562 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 563 &result); | |
| 564 path_builder.AddCertIssuerSource(&sync_certs); | |
| 565 | |
| 566 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 567 | |
| 568 EXPECT_EQ(OK, result.error()); | |
| 569 ASSERT_EQ(2U, result.paths.size()); | |
| 570 | |
| 571 // Path builder will first attempt: target <- oldintermediary <- newroot | |
| 572 // but it will fail since oldintermediary is signed by oldroot. | |
| 573 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 574 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 575 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 576 EXPECT_EQ(oldintermediary_, result.paths[0]->path[1]); | |
| 577 EXPECT_EQ(newroot_, result.paths[0]->path[2]); | |
| 578 | |
| 579 // Path builder will next attempt: | |
| 580 // target <- old intermediary <- oldroot | |
| 581 // which should succeed. | |
| 582 EXPECT_EQ(OK, result.paths[1]->error); | |
| 583 ASSERT_EQ(3U, result.paths[1]->path.size()); | |
| 584 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 585 EXPECT_EQ(oldintermediary_, result.paths[1]->path[1]); | |
| 586 EXPECT_EQ(oldroot_, result.paths[1]->path[2]); | |
| 587 } | |
| 588 | |
| 589 // Tests that the path builder doesn't build longer than necessary paths. | |
| 590 TEST_F(PathBuilderKeyRolloverTest, TestRolloverLongChain) { | |
| 591 // Only oldroot is trusted. | |
| 592 TrustStore trust_store; | |
| 593 trust_store.AddTrustedCertificate(oldroot_); | |
| 594 | |
| 595 // New intermediate and new root are provided synchronously. | |
| 596 CertIssuerSourceStatic sync_certs; | |
| 597 sync_certs.AddCert(newintermediary_); | |
| 598 sync_certs.AddCert(newroot_); | |
| 599 | |
| 600 // Rollover cert is only provided asynchronously. This will force the | |
| 601 // pathbuilder to first try building a longer than necessary path. | |
| 602 AsyncCertIssuerSourceStatic async_certs; | |
| 603 async_certs.AddCert(newrootrollover_); | |
| 604 | |
| 605 CertPathBuilder::Result result; | |
| 606 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 607 &result); | |
| 608 path_builder.AddCertIssuerSource(&sync_certs); | |
| 609 path_builder.AddCertIssuerSource(&async_certs); | |
| 610 | |
| 611 EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder)); | |
| 612 | |
| 613 EXPECT_EQ(OK, result.error()); | |
| 614 ASSERT_EQ(3U, result.paths.size()); | |
| 615 | |
| 616 // Path builder will first attempt: target <- newintermediary <- oldroot | |
| 617 // but it will fail since newintermediary is signed by newroot. | |
| 618 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 619 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 620 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 621 EXPECT_EQ(newintermediary_, result.paths[0]->path[1]); | |
| 622 EXPECT_EQ(oldroot_, result.paths[0]->path[2]); | |
| 623 | |
| 624 // Path builder will next attempt: | |
| 625 // target <- newintermediary <- newroot <- oldroot | |
| 626 // but it will fail since newroot is self-signed. | |
| 627 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[1]->error); | |
| 628 ASSERT_EQ(4U, result.paths[1]->path.size()); | |
| 629 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 630 EXPECT_EQ(newintermediary_, result.paths[1]->path[1]); | |
| 631 EXPECT_EQ(newroot_, result.paths[1]->path[2]); | |
| 632 EXPECT_EQ(oldroot_, result.paths[1]->path[3]); | |
| 633 | |
| 634 // Path builder will skip: | |
| 635 // target <- newintermediary <- newroot <- newrootrollover <- ... | |
| 636 // Since newroot and newrootrollover have the same Name+SAN+SPKI. | |
| 637 | |
| 638 // Finally path builder will use: | |
| 639 // target <- newintermediary <- newrootrollover <- oldroot | |
| 640 EXPECT_EQ(2U, result.best_result_index); | |
| 641 EXPECT_EQ(OK, result.paths[2]->error); | |
| 642 ASSERT_EQ(4U, result.paths[2]->path.size()); | |
| 643 EXPECT_EQ(target_, result.paths[2]->path[0]); | |
| 644 EXPECT_EQ(newintermediary_, result.paths[2]->path[1]); | |
| 645 EXPECT_EQ(newrootrollover_, result.paths[2]->path[2]); | |
| 646 EXPECT_EQ(oldroot_, result.paths[2]->path[3]); | |
| 647 } | |
| 648 | |
| 649 // If the target cert is a trust root, that alone is a valid path. | |
| 650 TEST_F(PathBuilderKeyRolloverTest, TestEndEntityIsTrustRoot) { | |
| 651 // Trust newintermediary. | |
| 652 TrustStore trust_store; | |
| 653 trust_store.AddTrustedCertificate(newintermediary_); | |
| 654 | |
| 655 CertPathBuilder::Result result; | |
| 656 // Newintermediary is also the target cert. | |
| 657 CertPathBuilder path_builder(newintermediary_, &trust_store, | |
| 658 &signature_policy_, time_, &result); | |
| 659 | |
| 660 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 661 | |
| 662 EXPECT_EQ(OK, result.error()); | |
| 663 | |
| 664 ASSERT_EQ(1U, result.paths.size()); | |
| 665 EXPECT_EQ(OK, result.paths[0]->error); | |
| 666 ASSERT_EQ(1U, result.paths[0]->path.size()); | |
| 667 EXPECT_EQ(newintermediary_, result.paths[0]->path[0]); | |
| 668 } | |
| 669 | |
| 670 // If target has same Name+SAN+SPKI as a necessary intermediate, test if a path | |
| 671 // can still be built. | |
| 672 // Since LoopChecker will prevent the intermediate from being included, this | |
| 673 // currently does NOT verify. This case shouldn't occur in the web PKI. | |
| 674 TEST_F(PathBuilderKeyRolloverTest, | |
| 675 TestEndEntityHasSameNameAndSpkiAsIntermediate) { | |
| 676 // Trust oldroot. | |
| 677 TrustStore trust_store; | |
| 678 trust_store.AddTrustedCertificate(oldroot_); | |
| 679 | |
| 680 // New root rollover is provided synchronously. | |
| 681 CertIssuerSourceStatic sync_certs; | |
| 682 sync_certs.AddCert(newrootrollover_); | |
| 683 | |
| 684 CertPathBuilder::Result result; | |
| 685 // Newroot is the target cert. | |
| 686 CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_, | |
| 687 time_, &result); | |
| 688 path_builder.AddCertIssuerSource(&sync_certs); | |
| 689 | |
| 690 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 691 | |
| 692 // This could actually be OK, but CertPathBuilder does not build the | |
| 693 // newroot <- newrootrollover <- oldroot path. | |
| 694 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error()); | |
| 695 } | |
| 696 | |
| 697 // If target has same Name+SAN+SPKI as the trust root, test that a (trivial) | |
| 698 // path can still be built. | |
| 699 TEST_F(PathBuilderKeyRolloverTest, | |
| 700 TestEndEntityHasSameNameAndSpkiAsTrustAnchor) { | |
| 701 // Trust newrootrollover. | |
| 702 TrustStore trust_store; | |
| 703 trust_store.AddTrustedCertificate(newrootrollover_); | |
| 704 | |
| 705 CertPathBuilder::Result result; | |
| 706 // Newroot is the target cert. | |
| 707 CertPathBuilder path_builder(newroot_, &trust_store, &signature_policy_, | |
| 708 time_, &result); | |
| 709 | |
| 710 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 711 | |
| 712 EXPECT_EQ(OK, result.error()); | |
| 713 | |
| 714 ASSERT_FALSE(result.paths.empty()); | |
| 715 const CertPathBuilder::ResultPath* best_result = | |
| 716 result.paths[result.best_result_index].get(); | |
| 717 | |
| 718 // Newroot has same name+SPKI as newrootrollover, thus the path is valid and | |
| 719 // only contains newroot. | |
| 720 EXPECT_EQ(OK, best_result->error); | |
| 721 ASSERT_EQ(1U, best_result->path.size()); | |
| 722 EXPECT_EQ(newroot_, best_result->path[0]); | |
| 723 } | |
| 724 | |
| 725 // Test that PathBuilder will not try the same path twice if multiple | |
| 726 // CertIssuerSources provide the same certificate. | |
| 727 TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediates) { | |
| 728 // Create a separate copy of oldintermediary. | |
| 729 scoped_refptr<ParsedCertificate> oldintermediary_dupe( | |
| 730 ParsedCertificate::CreateFromCertificateCopy( | |
| 731 oldintermediary_->der_cert().AsStringPiece(), {})); | |
| 732 | |
| 733 // Only newroot is a trusted root. | |
| 734 TrustStore trust_store; | |
| 735 trust_store.AddTrustedCertificate(newroot_); | |
| 736 | |
| 737 // The oldintermediary is supplied synchronously by |sync_certs1| and | |
| 738 // another copy of oldintermediary is supplied synchronously by |sync_certs2|. | |
| 739 // The path target <- oldintermediary <- newroot should be built first, | |
| 740 // though it won't verify. It should not be attempted again even though | |
| 741 // oldintermediary was supplied twice. | |
| 742 CertIssuerSourceStatic sync_certs1; | |
| 743 sync_certs1.AddCert(oldintermediary_); | |
| 744 CertIssuerSourceStatic sync_certs2; | |
| 745 sync_certs2.AddCert(oldintermediary_dupe); | |
| 746 | |
| 747 // The newintermediary is supplied asynchronously, so the path | |
| 748 // target <- newintermediary <- newroot should be tried second. | |
| 749 AsyncCertIssuerSourceStatic async_certs; | |
| 750 async_certs.AddCert(newintermediary_); | |
| 751 | |
| 752 CertPathBuilder::Result result; | |
| 753 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 754 &result); | |
| 755 path_builder.AddCertIssuerSource(&sync_certs1); | |
| 756 path_builder.AddCertIssuerSource(&sync_certs2); | |
| 757 path_builder.AddCertIssuerSource(&async_certs); | |
| 758 | |
| 759 EXPECT_EQ(CompletionStatus::ASYNC, RunPathBuilder(&path_builder)); | |
| 760 | |
| 761 EXPECT_EQ(OK, result.error()); | |
| 762 ASSERT_EQ(2U, result.paths.size()); | |
| 763 | |
| 764 // Path builder will first attempt: target <- oldintermediary <- newroot | |
| 765 // but it will fail since oldintermediary is signed by oldroot. | |
| 766 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 767 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 768 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 769 // Compare the DER instead of ParsedCertificate pointer, don't care which copy | |
| 770 // of oldintermediary was used in the path. | |
| 771 EXPECT_EQ(oldintermediary_->der_cert(), result.paths[0]->path[1]->der_cert()); | |
| 772 EXPECT_EQ(newroot_, result.paths[0]->path[2]); | |
| 773 | |
| 774 // Path builder will next attempt: target <- newintermediary <- newroot | |
| 775 // which will succeed. | |
| 776 EXPECT_EQ(1U, result.best_result_index); | |
| 777 EXPECT_EQ(OK, result.paths[1]->error); | |
| 778 ASSERT_EQ(3U, result.paths[1]->path.size()); | |
| 779 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 780 EXPECT_EQ(newintermediary_, result.paths[1]->path[1]); | |
| 781 EXPECT_EQ(newroot_, result.paths[1]->path[2]); | |
| 782 } | |
| 783 | |
| 784 // Test that PathBuilder will not try the same path twice if the same cert is | |
| 785 // presented via a CertIssuerSources and a TrustAnchor. | |
| 786 TEST_F(PathBuilderKeyRolloverTest, TestDuplicateIntermediateAndRoot) { | |
| 787 // Create a separate copy of newroot. | |
| 788 scoped_refptr<ParsedCertificate> newroot_dupe( | |
| 789 ParsedCertificate::CreateFromCertificateCopy( | |
| 790 newroot_->der_cert().AsStringPiece(), {})); | |
| 791 | |
| 792 // Only newroot is a trusted root. | |
| 793 TrustStore trust_store; | |
| 794 trust_store.AddTrustedCertificate(newroot_); | |
| 795 | |
| 796 // The oldintermediary and newroot are supplied synchronously by |sync_certs|. | |
| 797 CertIssuerSourceStatic sync_certs; | |
| 798 sync_certs.AddCert(oldintermediary_); | |
| 799 sync_certs.AddCert(newroot_dupe); | |
| 800 | |
| 801 CertPathBuilder::Result result; | |
| 802 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 803 &result); | |
| 804 path_builder.AddCertIssuerSource(&sync_certs); | |
| 805 | |
| 806 EXPECT_EQ(CompletionStatus::SYNC, RunPathBuilder(&path_builder)); | |
| 807 | |
| 808 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.error()); | |
| 809 ASSERT_EQ(1U, result.paths.size()); | |
| 810 | |
| 811 // Path builder attempt: target <- oldintermediary <- newroot | |
| 812 // but it will fail since oldintermediary is signed by oldroot. | |
| 813 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 814 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 815 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 816 EXPECT_EQ(oldintermediary_, result.paths[0]->path[1]); | |
| 817 // Compare the DER instead of ParsedCertificate pointer, don't care which copy | |
| 818 // of newroot was used in the path. | |
| 819 EXPECT_EQ(newroot_->der_cert(), result.paths[0]->path[2]->der_cert()); | |
| 820 } | |
| 821 | |
| 822 class MockCertIssuerSourceRequest : public CertIssuerSource::Request { | |
| 823 public: | |
| 824 MOCK_METHOD1(GetNext, CompletionStatus(scoped_refptr<ParsedCertificate>*)); | |
| 825 }; | |
| 826 | |
| 827 class MockCertIssuerSource : public CertIssuerSource { | |
| 828 public: | |
| 829 MOCK_METHOD2(SyncGetIssuersOf, | |
| 830 void(const ParsedCertificate*, ParsedCertificateList*)); | |
| 831 MOCK_METHOD3(AsyncGetIssuersOf, | |
| 832 void(const ParsedCertificate*, | |
| 833 const IssuerCallback&, | |
| 834 std::unique_ptr<Request>*)); | |
| 835 }; | |
| 836 | |
| 837 // Helper class to pass the Request to the PathBuilder when it calls | |
| 838 // AsyncGetIssuersOf. (GoogleMock has a ByMove helper, but it apparently can | |
| 839 // only be used with Return, not SetArgPointee.) | |
| 840 class CertIssuerSourceRequestMover { | |
| 841 public: | |
| 842 CertIssuerSourceRequestMover(std::unique_ptr<CertIssuerSource::Request> req) | |
| 843 : request_(std::move(req)) {} | |
| 844 void MoveIt(const ParsedCertificate* cert, | |
| 845 const CertIssuerSource::IssuerCallback& issuers_callback, | |
| 846 std::unique_ptr<CertIssuerSource::Request>* out_req) { | |
| 847 *out_req = std::move(request_); | |
| 848 } | |
| 849 | |
| 850 private: | |
| 851 std::unique_ptr<CertIssuerSource::Request> request_; | |
| 852 }; | |
| 853 | |
| 854 // Test that a single CertIssuerSource returning multiple async batches of | |
| 855 // issuers is handled correctly. Due to the StrictMocks, it also tests that path | |
| 856 // builder does not request issuers of certs that it shouldn't. | |
| 857 TEST_F(PathBuilderKeyRolloverTest, TestMultipleAsyncCallbacksFromSingleSource) { | |
| 858 StrictMock<MockCertIssuerSource> cert_issuer_source; | |
| 859 | |
| 860 // Only newroot is a trusted root. | |
| 861 TrustStore trust_store; | |
| 862 trust_store.AddTrustedCertificate(newroot_); | |
| 863 | |
| 864 CertPathBuilder::Result result; | |
| 865 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 866 &result); | |
| 867 path_builder.AddCertIssuerSource(&cert_issuer_source); | |
| 868 | |
| 869 CertIssuerSource::IssuerCallback target_issuers_callback; | |
| 870 // Create the mock CertIssuerSource::Request... | |
| 871 std::unique_ptr<StrictMock<MockCertIssuerSourceRequest>> | |
| 872 target_issuers_req_owner(new StrictMock<MockCertIssuerSourceRequest>()); | |
| 873 // Keep a raw pointer to the Request... | |
| 874 StrictMock<MockCertIssuerSourceRequest>* target_issuers_req = | |
| 875 target_issuers_req_owner.get(); | |
| 876 // Setup helper class to pass ownership of the Request to the PathBuilder when | |
| 877 // it calls AsyncGetIssuersOf. | |
| 878 CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner)); | |
| 879 { | |
| 880 ::testing::InSequence s; | |
| 881 EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _)); | |
| 882 EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _, _)) | |
| 883 .WillOnce( | |
| 884 DoAll(SaveArg<1>(&target_issuers_callback), | |
| 885 Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt))); | |
| 886 } | |
| 887 | |
| 888 TestClosure callback; | |
| 889 CompletionStatus rv = path_builder.Run(callback.closure()); | |
| 890 ASSERT_EQ(CompletionStatus::ASYNC, rv); | |
| 891 | |
| 892 ASSERT_FALSE(target_issuers_callback.is_null()); | |
| 893 | |
| 894 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 895 | |
| 896 // First async batch: return oldintermediary_. | |
| 897 EXPECT_CALL(*target_issuers_req, GetNext(_)) | |
| 898 .WillOnce(DoAll(SetArgPointee<0>(oldintermediary_), | |
| 899 Return(CompletionStatus::SYNC))) | |
| 900 .WillOnce( | |
| 901 DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC))); | |
| 902 { | |
| 903 ::testing::InSequence s; | |
| 904 // oldintermediary_ does not create a valid path, so both sync and async | |
| 905 // lookups are expected. | |
| 906 EXPECT_CALL(cert_issuer_source, | |
| 907 SyncGetIssuersOf(oldintermediary_.get(), _)); | |
| 908 EXPECT_CALL(cert_issuer_source, | |
| 909 AsyncGetIssuersOf(oldintermediary_.get(), _, _)); | |
| 910 } | |
| 911 target_issuers_callback.Run(target_issuers_req); | |
| 912 ::testing::Mock::VerifyAndClearExpectations(target_issuers_req); | |
| 913 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 914 | |
| 915 // Second async batch: return newintermediary_. | |
| 916 EXPECT_CALL(*target_issuers_req, GetNext(_)) | |
| 917 .WillOnce(DoAll(SetArgPointee<0>(newintermediary_), | |
| 918 Return(CompletionStatus::SYNC))) | |
| 919 .WillOnce( | |
| 920 DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC))); | |
| 921 // newroot_ is in the trust store, so this path will be completed | |
| 922 // synchronously. AsyncGetIssuersOf will not be called on newintermediary_. | |
| 923 EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediary_.get(), _)); | |
| 924 target_issuers_callback.Run(target_issuers_req); | |
| 925 // Note that VerifyAndClearExpectations(target_issuers_req) is not called | |
| 926 // here. PathBuilder could have destroyed it already, so just let the | |
| 927 // expectations get checked by the destructor. | |
| 928 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 929 | |
| 930 // Ensure pathbuilder finished and filled result. | |
| 931 callback.WaitForResult(); | |
| 932 | |
| 933 EXPECT_EQ(OK, result.error()); | |
| 934 ASSERT_EQ(2U, result.paths.size()); | |
| 935 | |
| 936 // Path builder first attempts: target <- oldintermediary <- newroot | |
| 937 // but it will fail since oldintermediary is signed by oldroot. | |
| 938 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 939 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 940 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 941 EXPECT_EQ(oldintermediary_, result.paths[0]->path[1]); | |
| 942 EXPECT_EQ(newroot_, result.paths[0]->path[2]); | |
| 943 | |
| 944 // After the second batch of async results, path builder will attempt: | |
| 945 // target <- newintermediary <- newroot which will succeed. | |
| 946 EXPECT_EQ(OK, result.paths[1]->error); | |
| 947 ASSERT_EQ(3U, result.paths[1]->path.size()); | |
| 948 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 949 EXPECT_EQ(newintermediary_, result.paths[1]->path[1]); | |
| 950 EXPECT_EQ(newroot_, result.paths[1]->path[2]); | |
| 951 } | |
| 952 | |
| 953 // Test that PathBuilder will not try the same path twice if CertIssuerSources | |
| 954 // asynchronously provide the same certificate multiple times. | |
| 955 TEST_F(PathBuilderKeyRolloverTest, TestDuplicateAsyncIntermediates) { | |
| 956 StrictMock<MockCertIssuerSource> cert_issuer_source; | |
| 957 | |
| 958 // Only newroot is a trusted root. | |
| 959 TrustStore trust_store; | |
| 960 trust_store.AddTrustedCertificate(newroot_); | |
| 961 | |
| 962 CertPathBuilder::Result result; | |
| 963 CertPathBuilder path_builder(target_, &trust_store, &signature_policy_, time_, | |
| 964 &result); | |
| 965 path_builder.AddCertIssuerSource(&cert_issuer_source); | |
| 966 | |
| 967 CertIssuerSource::IssuerCallback target_issuers_callback; | |
| 968 // Create the mock CertIssuerSource::Request... | |
| 969 std::unique_ptr<StrictMock<MockCertIssuerSourceRequest>> | |
| 970 target_issuers_req_owner(new StrictMock<MockCertIssuerSourceRequest>()); | |
| 971 // Keep a raw pointer to the Request... | |
| 972 StrictMock<MockCertIssuerSourceRequest>* target_issuers_req = | |
| 973 target_issuers_req_owner.get(); | |
| 974 // Setup helper class to pass ownership of the Request to the PathBuilder when | |
| 975 // it calls AsyncGetIssuersOf. | |
| 976 CertIssuerSourceRequestMover req_mover(std::move(target_issuers_req_owner)); | |
| 977 { | |
| 978 ::testing::InSequence s; | |
| 979 EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(target_.get(), _)); | |
| 980 EXPECT_CALL(cert_issuer_source, AsyncGetIssuersOf(target_.get(), _, _)) | |
| 981 .WillOnce( | |
| 982 DoAll(SaveArg<1>(&target_issuers_callback), | |
| 983 Invoke(&req_mover, &CertIssuerSourceRequestMover::MoveIt))); | |
| 984 } | |
| 985 | |
| 986 TestClosure callback; | |
| 987 CompletionStatus rv = path_builder.Run(callback.closure()); | |
| 988 ASSERT_EQ(CompletionStatus::ASYNC, rv); | |
| 989 | |
| 990 ASSERT_FALSE(target_issuers_callback.is_null()); | |
| 991 | |
| 992 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 993 | |
| 994 // First async batch: return oldintermediary_. | |
| 995 EXPECT_CALL(*target_issuers_req, GetNext(_)) | |
| 996 .WillOnce(DoAll(SetArgPointee<0>(oldintermediary_), | |
| 997 Return(CompletionStatus::SYNC))) | |
| 998 .WillOnce( | |
| 999 DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC))); | |
| 1000 { | |
| 1001 ::testing::InSequence s; | |
| 1002 // oldintermediary_ does not create a valid path, so both sync and async | |
| 1003 // lookups are expected. | |
| 1004 EXPECT_CALL(cert_issuer_source, | |
| 1005 SyncGetIssuersOf(oldintermediary_.get(), _)); | |
| 1006 EXPECT_CALL(cert_issuer_source, | |
| 1007 AsyncGetIssuersOf(oldintermediary_.get(), _, _)); | |
| 1008 } | |
| 1009 target_issuers_callback.Run(target_issuers_req); | |
| 1010 ::testing::Mock::VerifyAndClearExpectations(target_issuers_req); | |
| 1011 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 1012 | |
| 1013 // Second async batch: return a different copy of oldintermediary_ again. | |
| 1014 scoped_refptr<ParsedCertificate> oldintermediary_dupe( | |
| 1015 ParsedCertificate::CreateFromCertificateCopy( | |
| 1016 oldintermediary_->der_cert().AsStringPiece(), {})); | |
| 1017 EXPECT_CALL(*target_issuers_req, GetNext(_)) | |
| 1018 .WillOnce(DoAll(SetArgPointee<0>(oldintermediary_dupe), | |
| 1019 Return(CompletionStatus::SYNC))) | |
| 1020 .WillOnce( | |
| 1021 DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC))); | |
| 1022 target_issuers_callback.Run(target_issuers_req); | |
| 1023 // oldintermediary was already processed above, it should not generate any | |
| 1024 // more requests. | |
| 1025 ::testing::Mock::VerifyAndClearExpectations(target_issuers_req); | |
| 1026 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 1027 | |
| 1028 // Third async batch: return newintermediary_. | |
| 1029 EXPECT_CALL(*target_issuers_req, GetNext(_)) | |
| 1030 .WillOnce(DoAll(SetArgPointee<0>(newintermediary_), | |
| 1031 Return(CompletionStatus::SYNC))) | |
| 1032 .WillOnce( | |
| 1033 DoAll(SetArgPointee<0>(nullptr), Return(CompletionStatus::ASYNC))); | |
| 1034 // newroot_ is in the trust store, so this path will be completed | |
| 1035 // synchronously. AsyncGetIssuersOf will not be called on newintermediary_. | |
| 1036 EXPECT_CALL(cert_issuer_source, SyncGetIssuersOf(newintermediary_.get(), _)); | |
| 1037 target_issuers_callback.Run(target_issuers_req); | |
| 1038 // Note that VerifyAndClearExpectations(target_issuers_req) is not called | |
| 1039 // here. PathBuilder could have destroyed it already, so just let the | |
| 1040 // expectations get checked by the destructor. | |
| 1041 ::testing::Mock::VerifyAndClearExpectations(&cert_issuer_source); | |
| 1042 | |
| 1043 // Ensure pathbuilder finished and filled result. | |
| 1044 callback.WaitForResult(); | |
| 1045 | |
| 1046 EXPECT_EQ(OK, result.error()); | |
| 1047 ASSERT_EQ(2U, result.paths.size()); | |
| 1048 | |
| 1049 // Path builder first attempts: target <- oldintermediary <- newroot | |
| 1050 // but it will fail since oldintermediary is signed by oldroot. | |
| 1051 EXPECT_EQ(ERR_CERT_AUTHORITY_INVALID, result.paths[0]->error); | |
| 1052 ASSERT_EQ(3U, result.paths[0]->path.size()); | |
| 1053 EXPECT_EQ(target_, result.paths[0]->path[0]); | |
| 1054 EXPECT_EQ(oldintermediary_, result.paths[0]->path[1]); | |
| 1055 EXPECT_EQ(newroot_, result.paths[0]->path[2]); | |
| 1056 | |
| 1057 // The second async result does not generate any path. | |
| 1058 | |
| 1059 // After the third batch of async results, path builder will attempt: | |
| 1060 // target <- newintermediary <- newroot which will succeed. | |
| 1061 EXPECT_EQ(OK, result.paths[1]->error); | |
| 1062 ASSERT_EQ(3U, result.paths[1]->path.size()); | |
| 1063 EXPECT_EQ(target_, result.paths[1]->path[0]); | |
| 1064 EXPECT_EQ(newintermediary_, result.paths[1]->path[1]); | |
| 1065 EXPECT_EQ(newroot_, result.paths[1]->path[2]); | |
| 1066 } | |
| 1067 | |
| 1068 } // namespace | |
| 1069 | |
| 1070 } // namespace net | |
| OLD | NEW |