| // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "net/base/cert_verifier.h" |
| |
| #include "base/callback.h" |
| #include "base/file_path.h" |
| #include "base/stringprintf.h" |
| #include "net/base/cert_test_util.h" |
| #include "net/base/net_errors.h" |
| #include "net/base/test_completion_callback.h" |
| #include "net/base/x509_certificate.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace net { |
| |
| class TestTimeService : public CertVerifier::TimeService { |
| public: |
| // CertVerifier::TimeService methods: |
| virtual base::Time Now() { return current_time_; } |
| |
| void set_current_time(base::Time now) { current_time_ = now; } |
| |
| private: |
| base::Time current_time_; |
| }; |
| |
| class CertVerifierTest : public testing::Test { |
| }; |
| |
| class ExplodingCallback : public CallbackRunner<Tuple1<int> > { |
| public: |
| virtual void RunWithParams(const Tuple1<int>& params) { |
| FAIL(); |
| } |
| }; |
| |
| // Tests a cache hit, which should results in synchronous completion. |
| TEST_F(CertVerifierTest, CacheHit) { |
| TestTimeService* time_service = new TestTimeService; |
| base::Time current_time = base::Time::Now(); |
| time_service->set_current_time(current_time); |
| CertVerifier verifier(time_service); |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| TestCompletionCallback callback; |
| CertVerifier::RequestHandle request_handle; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(1u, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| // Synchronous completion. |
| ASSERT_NE(ERR_IO_PENDING, error); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_TRUE(request_handle == NULL); |
| ASSERT_EQ(2u, verifier.requests()); |
| ASSERT_EQ(1u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| } |
| |
| // Tests an inflight join. |
| TEST_F(CertVerifierTest, InflightJoin) { |
| TestTimeService* time_service = new TestTimeService; |
| base::Time current_time = base::Time::Now(); |
| time_service->set_current_time(current_time); |
| CertVerifier verifier(time_service); |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| TestCompletionCallback callback; |
| CertVerifier::RequestHandle request_handle; |
| CertVerifyResult verify_result2; |
| TestCompletionCallback callback2; |
| CertVerifier::RequestHandle request_handle2; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result2, |
| &callback2, &request_handle2); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle2 != NULL); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| error = callback2.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(2u, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(1u, verifier.inflight_joins()); |
| } |
| |
| // Tests cache entry expiration. |
| TEST_F(CertVerifierTest, ExpiredCacheEntry) { |
| TestTimeService* time_service = new TestTimeService; |
| base::Time current_time = base::Time::Now(); |
| time_service->set_current_time(current_time); |
| CertVerifier verifier(time_service); |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| TestCompletionCallback callback; |
| CertVerifier::RequestHandle request_handle; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(1u, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| |
| // Before expiration, should have a cache hit. |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| // Synchronous completion. |
| ASSERT_NE(ERR_IO_PENDING, error); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_TRUE(request_handle == NULL); |
| ASSERT_EQ(2u, verifier.requests()); |
| ASSERT_EQ(1u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| |
| // After expiration, should not have a cache hit. |
| ASSERT_EQ(1u, verifier.GetCacheSize()); |
| current_time += base::TimeDelta::FromMinutes(60); |
| time_service->set_current_time(current_time); |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| ASSERT_EQ(0u, verifier.GetCacheSize()); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(3u, verifier.requests()); |
| ASSERT_EQ(1u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| } |
| |
| // Tests a full cache. |
| TEST_F(CertVerifierTest, FullCache) { |
| TestTimeService* time_service = new TestTimeService; |
| base::Time current_time = base::Time::Now(); |
| time_service->set_current_time(current_time); |
| CertVerifier verifier(time_service); |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| TestCompletionCallback callback; |
| CertVerifier::RequestHandle request_handle; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(1u, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| |
| const unsigned kCacheSize = 256; |
| |
| for (unsigned i = 0; i < kCacheSize; i++) { |
| std::string hostname = base::StringPrintf("www%d.example.com", i + 1); |
| error = verifier.Verify(google_cert, hostname, 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = callback.WaitForResult(); |
| ASSERT_TRUE(IsCertificateError(error)); |
| } |
| ASSERT_EQ(kCacheSize + 1, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| |
| ASSERT_EQ(kCacheSize, verifier.GetCacheSize()); |
| current_time += base::TimeDelta::FromMinutes(60); |
| time_service->set_current_time(current_time); |
| error = verifier.Verify(google_cert, "www999.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| ASSERT_EQ(kCacheSize, verifier.GetCacheSize()); |
| error = callback.WaitForResult(); |
| ASSERT_EQ(1u, verifier.GetCacheSize()); |
| ASSERT_TRUE(IsCertificateError(error)); |
| ASSERT_EQ(kCacheSize + 2, verifier.requests()); |
| ASSERT_EQ(0u, verifier.cache_hits()); |
| ASSERT_EQ(0u, verifier.inflight_joins()); |
| } |
| |
| // Tests that the callback of a canceled request is never made. |
| TEST_F(CertVerifierTest, CancelRequest) { |
| CertVerifier verifier; |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| ExplodingCallback exploding_callback; |
| CertVerifier::RequestHandle request_handle; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &exploding_callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| verifier.CancelRequest(request_handle); |
| |
| // Issue a few more requests to the worker pool and wait for their |
| // completion, so that the task of the canceled request (which runs on a |
| // worker thread) is likely to complete by the end of this test. |
| TestCompletionCallback callback; |
| for (int i = 0; i < 5; ++i) { |
| error = verifier.Verify(google_cert, "www2.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| error = callback.WaitForResult(); |
| verifier.ClearCache(); |
| } |
| } |
| |
| // Tests that a canceled request is not leaked. |
| TEST_F(CertVerifierTest, CancelRequestThenQuit) { |
| CertVerifier verifier; |
| |
| FilePath certs_dir = GetTestCertsDirectory(); |
| scoped_refptr<X509Certificate> google_cert( |
| ImportCertFromFile(certs_dir, "google.single.der")); |
| ASSERT_NE(static_cast<X509Certificate*>(NULL), google_cert); |
| |
| int error; |
| CertVerifyResult verify_result; |
| TestCompletionCallback callback; |
| CertVerifier::RequestHandle request_handle; |
| |
| error = verifier.Verify(google_cert, "www.example.com", 0, &verify_result, |
| &callback, &request_handle); |
| ASSERT_EQ(ERR_IO_PENDING, error); |
| ASSERT_TRUE(request_handle != NULL); |
| verifier.CancelRequest(request_handle); |
| // Destroy |verifier| by going out of scope. |
| } |
| |
| } // namespace net |