| Index: net/cert/cert_verifier_cache_persister_unittest.cc
|
| diff --git a/net/cert/cert_verifier_cache_persister_unittest.cc b/net/cert/cert_verifier_cache_persister_unittest.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..8c1046cdd1d3db683462ba9bd2d35b5e9556965a
|
| --- /dev/null
|
| +++ b/net/cert/cert_verifier_cache_persister_unittest.cc
|
| @@ -0,0 +1,276 @@
|
| +// Copyright (c) 2016 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/cert/cert_verifier_cache_persister.h"
|
| +
|
| +#include "base/files/file_path.h"
|
| +#include "base/memory/ref_counted.h"
|
| +#include "base/memory/scoped_ptr.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/base/test_completion_callback.h"
|
| +#include "net/base/test_data_directory.h"
|
| +#include "net/cert/cert_trust_anchor_provider.h"
|
| +#include "net/cert/cert_verify_proc.h"
|
| +#include "net/cert/cert_verify_result.h"
|
| +#include "net/cert/multi_threaded_cert_verifier.h"
|
| +#include "net/cert/x509_certificate.h"
|
| +#include "net/log/net_log.h"
|
| +#include "net/test/cert_test_util.h"
|
| +#include "testing/gmock/include/gmock/gmock.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace net {
|
| +
|
| +namespace {
|
| +
|
| +class MockCertVerifyProc : public CertVerifyProc {
|
| + public:
|
| + MockCertVerifyProc() {}
|
| +
|
| + private:
|
| + ~MockCertVerifyProc() override {}
|
| +
|
| + // CertVerifyProc implementation
|
| + bool SupportsAdditionalTrustAnchors() const override { return false; }
|
| + bool SupportsOCSPStapling() const override { return false; }
|
| +
|
| + int VerifyInternal(X509Certificate* cert,
|
| + const std::string& hostname,
|
| + const std::string& ocsp_response,
|
| + int flags,
|
| + CRLSet* crl_set,
|
| + const CertificateList& additional_trust_anchors,
|
| + CertVerifyResult* verify_result) override {
|
| + verify_result->Reset();
|
| + verify_result->verified_cert = cert;
|
| + verify_result->cert_status = CERT_STATUS_COMMON_NAME_INVALID;
|
| + return ERR_CERT_COMMON_NAME_INVALID;
|
| + }
|
| +};
|
| +
|
| +bool HashValueVectorEqual(const HashValueVector& a, const HashValueVector& b) {
|
| + size_t size = a.size();
|
| +
|
| + if (size != b.size())
|
| + return false;
|
| +
|
| + for (size_t i = 0; i < size; ++i) {
|
| + if (!a[i].Equals(b[i]))
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +class CertVerifierCachePersisterTest : public ::testing::Test {
|
| + public:
|
| + CertVerifierCachePersisterTest()
|
| + : verifier_(new MockCertVerifyProc()), persister_(&verifier_) {}
|
| + ~CertVerifierCachePersisterTest() override {}
|
| +
|
| + protected:
|
| + MultiThreadedCertVerifier verifier_;
|
| + CertVerifierCachePersister persister_;
|
| +};
|
| +
|
| +TEST_F(CertVerifierCachePersisterTest, PersistCache) {
|
| + base::FilePath certs_dir = GetTestCertsDirectory();
|
| + scoped_refptr<X509Certificate> test_cert(
|
| + ImportCertFromFile(certs_dir, "ok_cert.pem"));
|
| + ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert.get());
|
| +
|
| + std::string example_hostname("www.example.com");
|
| + std::string ocsp_response;
|
| + int flags = 0;
|
| + CRLSet* crl_set = NULL;
|
| + int error;
|
| + CertVerifyResult verify_result;
|
| + TestCompletionCallback callback;
|
| + scoped_ptr<CertVerifier::Request> request;
|
| +
|
| + error = verifier_.Verify(test_cert.get(), example_hostname, ocsp_response,
|
| + flags, crl_set, &verify_result, callback.callback(),
|
| + &request, BoundNetLog());
|
| + ASSERT_EQ(ERR_IO_PENDING, error);
|
| + EXPECT_TRUE(request);
|
| + error = callback.WaitForResult();
|
| + ASSERT_TRUE(IsCertificateError(error));
|
| + ASSERT_EQ(1u, verifier_.requests());
|
| + ASSERT_EQ(0u, verifier_.cache_hits());
|
| + ASSERT_EQ(0u, verifier_.inflight_joins());
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + CertVerifyResult verify_result_serialized;
|
| + error = verifier_.Verify(test_cert.get(), example_hostname, ocsp_response,
|
| + flags, crl_set, &verify_result_serialized,
|
| + callback.callback(), &request, BoundNetLog());
|
| + // Synchronous completion.
|
| + ASSERT_NE(ERR_IO_PENDING, error);
|
| + ASSERT_TRUE(IsCertificateError(error));
|
| + ASSERT_FALSE(request);
|
| + ASSERT_EQ(2u, verifier_.requests());
|
| + ASSERT_EQ(1u, verifier_.cache_hits());
|
| + ASSERT_EQ(0u, verifier_.inflight_joins());
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + std::string data;
|
| + persister_.SerializeCache(&data);
|
| + EXPECT_FALSE(data.empty());
|
| +
|
| + // Clear the cache.
|
| + verifier_.ClearCache();
|
| + ASSERT_EQ(0u, verifier_.GetCacheSize());
|
| +
|
| + // Restore the cache data
|
| + EXPECT_TRUE(persister_.LoadCache(data));
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + CertVerifyResult verify_result_deserialized;
|
| + error = verifier_.Verify(test_cert.get(), example_hostname, ocsp_response,
|
| + flags, crl_set, &verify_result_deserialized,
|
| + callback.callback(), &request, BoundNetLog());
|
| + // Synchronous completion.
|
| + ASSERT_NE(ERR_IO_PENDING, error);
|
| + ASSERT_TRUE(IsCertificateError(error));
|
| + ASSERT_FALSE(request);
|
| + ASSERT_EQ(3u, verifier_.requests());
|
| + ASSERT_EQ(2u, verifier_.cache_hits());
|
| + ASSERT_EQ(0u, verifier_.inflight_joins());
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + // Verify the result after deserialized is same as the result before
|
| + // searilization.
|
| + ASSERT_EQ(verify_result_serialized.cert_status,
|
| + verify_result_deserialized.cert_status);
|
| + ASSERT_EQ(verify_result_serialized.has_md2,
|
| + verify_result_deserialized.has_md2);
|
| + ASSERT_EQ(verify_result_serialized.has_md4,
|
| + verify_result_deserialized.has_md4);
|
| + ASSERT_EQ(verify_result_serialized.has_md5,
|
| + verify_result_deserialized.has_md5);
|
| + ASSERT_EQ(verify_result_serialized.has_sha1,
|
| + verify_result_deserialized.has_sha1);
|
| + ASSERT_EQ(verify_result_serialized.has_sha1_leaf,
|
| + verify_result_deserialized.has_sha1_leaf);
|
| + ASSERT_EQ(verify_result_serialized.is_issued_by_known_root,
|
| + verify_result_deserialized.is_issued_by_known_root);
|
| + ASSERT_EQ(verify_result_serialized.is_issued_by_additional_trust_anchor,
|
| + verify_result_deserialized.is_issued_by_additional_trust_anchor);
|
| + ASSERT_EQ(verify_result_serialized.has_md2,
|
| + verify_result_deserialized.has_md2);
|
| + ASSERT_EQ(verify_result_serialized.common_name_fallback_used,
|
| + verify_result_deserialized.common_name_fallback_used);
|
| + EXPECT_TRUE(verify_result_serialized.verified_cert.get()->Equals(
|
| + verify_result_deserialized.verified_cert.get()));
|
| + EXPECT_TRUE(
|
| + HashValueVectorEqual(verify_result_serialized.public_key_hashes,
|
| + verify_result_deserialized.public_key_hashes));
|
| +
|
| + // Verify that cache by using the CertVerifierCacheIterator.
|
| + MultiThreadedCertVerifier::CertVerifierCacheIterator iterator(verifier_);
|
| + EXPECT_TRUE(iterator.HasNext());
|
| + ASSERT_EQ(example_hostname, iterator.hostname());
|
| + ASSERT_EQ(flags, iterator.flags());
|
| + ASSERT_EQ(error, iterator.error());
|
| + const CertVerifyResult& result = iterator.result();
|
| + ASSERT_EQ(verify_result_serialized.cert_status, result.cert_status);
|
| + ASSERT_EQ(verify_result_serialized.has_md2, result.has_md2);
|
| + ASSERT_EQ(verify_result_serialized.has_md4, result.has_md4);
|
| + ASSERT_EQ(verify_result_serialized.has_md5, result.has_md5);
|
| + ASSERT_EQ(verify_result_serialized.has_sha1, result.has_sha1);
|
| + ASSERT_EQ(verify_result_serialized.has_sha1_leaf, result.has_sha1_leaf);
|
| + ASSERT_EQ(verify_result_serialized.is_issued_by_known_root,
|
| + result.is_issued_by_known_root);
|
| + ASSERT_EQ(verify_result_serialized.is_issued_by_additional_trust_anchor,
|
| + result.is_issued_by_additional_trust_anchor);
|
| + ASSERT_EQ(verify_result_serialized.has_md2, result.has_md2);
|
| + ASSERT_EQ(verify_result_serialized.common_name_fallback_used,
|
| + result.common_name_fallback_used);
|
| + EXPECT_TRUE(HashValueVectorEqual(verify_result_serialized.public_key_hashes,
|
| + result.public_key_hashes));
|
| + EXPECT_TRUE(verify_result_serialized.verified_cert.get()->Equals(
|
| + result.verified_cert.get()));
|
| +
|
| + iterator.Advance();
|
| + EXPECT_FALSE(iterator.HasNext());
|
| +}
|
| +
|
| +TEST_F(CertVerifierCachePersisterTest, PersistCacheExpiredEntry) {
|
| + base::FilePath certs_dir = GetTestCertsDirectory();
|
| + scoped_refptr<X509Certificate> test_cert(
|
| + ImportCertFromFile(certs_dir, "ok_cert.pem"));
|
| + ASSERT_NE(static_cast<X509Certificate*>(NULL), test_cert.get());
|
| +
|
| + std::string example_hostname("www.example.com");
|
| + std::string ocsp_response;
|
| + int flags = 0;
|
| + CRLSet* crl_set = NULL;
|
| + int error;
|
| + CertVerifyResult verify_result;
|
| + TestCompletionCallback callback;
|
| + scoped_ptr<CertVerifier::Request> request;
|
| +
|
| + // Verify that there is no cache hit.
|
| + error = verifier_.Verify(test_cert.get(), example_hostname, ocsp_response,
|
| + flags, crl_set, &verify_result, callback.callback(),
|
| + &request, BoundNetLog());
|
| + ASSERT_EQ(ERR_IO_PENDING, error);
|
| + EXPECT_TRUE(request);
|
| + error = callback.WaitForResult();
|
| + ASSERT_TRUE(IsCertificateError(error));
|
| + ASSERT_EQ(1u, verifier_.requests());
|
| + ASSERT_EQ(0u, verifier_.cache_hits());
|
| + ASSERT_EQ(0u, verifier_.inflight_joins());
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + error = verifier_.Verify(test_cert.get(), example_hostname, ocsp_response,
|
| + flags, crl_set, &verify_result, callback.callback(),
|
| + &request, BoundNetLog());
|
| + // Synchronous completion and verify that there is a cache hit.
|
| + ASSERT_NE(ERR_IO_PENDING, error);
|
| + ASSERT_TRUE(IsCertificateError(error));
|
| + ASSERT_FALSE(request);
|
| + ASSERT_EQ(2u, verifier_.requests());
|
| + ASSERT_EQ(1u, verifier_.cache_hits()); // cache hit.
|
| + ASSERT_EQ(0u, verifier_.inflight_joins());
|
| + ASSERT_EQ(1u, verifier_.GetCacheSize());
|
| +
|
| + // Test cache expiration, by adding expired data.
|
| + const CertificateList empty_cert_list;
|
| + const CertificateList& additional_trust_anchors =
|
| + verifier_.trust_anchor_provider_
|
| + ? verifier_.trust_anchor_provider_->GetAdditionalTrustAnchors()
|
| + : empty_cert_list;
|
| + const MultiThreadedCertVerifier::RequestParams key(
|
| + test_cert->fingerprint(), test_cert->ca_fingerprint(), example_hostname,
|
| + ocsp_response, flags, additional_trust_anchors);
|
| + const MultiThreadedCertVerifier::CertVerifierCache::value_type* cached_entry =
|
| + verifier_.cache_.Get(key, MultiThreadedCertVerifier::CacheValidityPeriod(
|
| + base::Time::Now()));
|
| + // TODO(rtenneti): use |kTTLSecs| instead of hardcoded 1800ms.
|
| + base::Time start_time =
|
| + base::Time::Now() - base::TimeDelta::FromSeconds(3600);
|
| + verifier_.cache_.Put(
|
| + key, *cached_entry,
|
| + MultiThreadedCertVerifier::CacheValidityPeriod(start_time),
|
| + MultiThreadedCertVerifier::CacheValidityPeriod(
|
| + start_time, start_time + base::TimeDelta::FromSeconds(1800)));
|
| +
|
| + // Persist the data.
|
| + std::string data;
|
| + persister_.SerializeCache(&data);
|
| + EXPECT_FALSE(data.empty());
|
| +
|
| + // Clear the cache.
|
| + verifier_.ClearCache();
|
| + ASSERT_EQ(0u, verifier_.GetCacheSize());
|
| +
|
| + // Shouldn't restore expired entries.
|
| + EXPECT_TRUE(persister_.LoadCache(data));
|
| + ASSERT_EQ(0u, verifier_.GetCacheSize());
|
| +}
|
| +
|
| +} // namespace net
|
|
|