#include "net/cert/caching_cert_verifier.h"
#include <utility>
#include "base/functional/bind.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "net/base/net_errors.h"
#include "net/base/url_util.h"
namespace net {
namespace {
const unsigned kMaxCacheEntries = 256;
const unsigned kTTLSecs = 1800;
}
CachingCertVerifier::CachingCertVerifier(std::unique_ptr<CertVerifier> verifier)
: verifier_(std::move(verifier)), cache_(kMaxCacheEntries) {
verifier_->AddObserver(this);
CertDatabase::GetInstance()->AddObserver(this);
}
CachingCertVerifier::~CachingCertVerifier() {
CertDatabase::GetInstance()->RemoveObserver(this);
verifier_->RemoveObserver(this);
}
int CachingCertVerifier::Verify(const CertVerifier::RequestParams& params,
CertVerifyResult* verify_result,
CompletionOnceCallback callback,
std::unique_ptr<Request>* out_req,
const NetLogWithSource& net_log) {
out_req->reset();
requests_++;
const CertVerificationCache::value_type* cached_entry =
cache_.Get(params, CacheValidityPeriod(base::Time::Now()));
UMA_HISTOGRAM_BOOLEAN("Net.CachingCertVerifier.CacheHit",
cached_entry != nullptr);
if (IsGoogleHost(params.hostname())) {
if (IsGoogleHostWithAlpnH3(params.hostname())) {
UMA_HISTOGRAM_BOOLEAN("Net.CachingCertVerifier.CacheHit.GoogleWithAlpnH3",
cached_entry != nullptr);
}
UMA_HISTOGRAM_BOOLEAN("Net.CachingCertVerifier.CacheHit.Google",
cached_entry != nullptr);
}
if (cached_entry) {
++cache_hits_;
*verify_result = cached_entry->result;
return cached_entry->error;
}
base::Time start_time = base::Time::Now();
base::TimeTicks start_time_ticks = base::TimeTicks::Now();
CompletionOnceCallback caching_callback =
base::BindOnce(&CachingCertVerifier::OnRequestFinished,
base::Unretained(this), config_id_, params, start_time,
start_time_ticks, std::move(callback), verify_result);
int result = verifier_->Verify(params, verify_result,
std::move(caching_callback), out_req, net_log);
if (result != ERR_IO_PENDING) {
base::TimeDelta verify_time = base::TimeTicks::Now() - start_time_ticks;
UMA_HISTOGRAM_CUSTOM_TIMES(
"Net.CachingCertVerifier.Sync.UncachedVerifyTime", verify_time,
base::Milliseconds(1), base::Minutes(10), 100);
if (IsGoogleHost(params.hostname())) {
if (IsGoogleHostWithAlpnH3(params.hostname())) {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Net.CachingCertVerifier.Sync.UncachedVerifyTime.GoogleWithAlpnH3",
verify_time, base::Milliseconds(1), base::Minutes(10), 100);
}
UMA_HISTOGRAM_CUSTOM_TIMES(
"Net.CachingCertVerifier.Sync.UncachedVerifyTime.Google", verify_time,
base::Milliseconds(1), base::Minutes(10), 100);
}
AddResultToCache(config_id_, params, start_time, *verify_result, result);
}
return result;
}
void CachingCertVerifier::Verify2QwacBinding(
const std::string& binding,
const std::string& hostname,
const scoped_refptr<X509Certificate>& tls_cert,
base::OnceCallback<void(const scoped_refptr<X509Certificate>&)> callback,
const NetLogWithSource& net_log) {
verifier_->Verify2QwacBinding(binding, hostname, tls_cert,
std::move(callback), net_log);
}
void CachingCertVerifier::SetConfig(const CertVerifier::Config& config) {
verifier_->SetConfig(config);
config_id_++;
ClearCache();
}
void CachingCertVerifier::AddObserver(CertVerifier::Observer* observer) {
verifier_->AddObserver(observer);
}
void CachingCertVerifier::RemoveObserver(CertVerifier::Observer* observer) {
verifier_->RemoveObserver(observer);
}
CachingCertVerifier::CachedResult::CachedResult() = default;
CachingCertVerifier::CachedResult::~CachedResult() = default;
CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(base::Time now)
: verification_time(now), expiration_time(now) {}
CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
base::Time now,
base::Time expiration)
: verification_time(now), expiration_time(expiration) {}
bool CachingCertVerifier::CacheExpirationFunctor::operator()(
const CacheValidityPeriod& now,
const CacheValidityPeriod& expiration) const {
DCHECK(now.verification_time == now.expiration_time);
return now.verification_time >= expiration.verification_time &&
now.verification_time < expiration.expiration_time;
}
void CachingCertVerifier::OnRequestFinished(uint32_t config_id,
const RequestParams& params,
base::Time start_time,
base::TimeTicks start_time_ticks,
CompletionOnceCallback callback,
CertVerifyResult* verify_result,
int error) {
base::TimeDelta verify_time = base::TimeTicks::Now() - start_time_ticks;
UMA_HISTOGRAM_CUSTOM_TIMES("Net.CachingCertVerifier.Async.UncachedVerifyTime",
verify_time, base::Milliseconds(1),
base::Minutes(10), 100);
if (IsGoogleHost(params.hostname())) {
if (IsGoogleHostWithAlpnH3(params.hostname())) {
UMA_HISTOGRAM_CUSTOM_TIMES(
"Net.CachingCertVerifier.Async.UncachedVerifyTime.GoogleWithAlpnH3",
verify_time, base::Milliseconds(1), base::Minutes(10), 100);
}
UMA_HISTOGRAM_CUSTOM_TIMES(
"Net.CachingCertVerifier.Async.UncachedVerifyTime.Google", verify_time,
base::Milliseconds(1), base::Minutes(10), 100);
}
AddResultToCache(config_id, params, start_time, *verify_result, error);
std::move(callback).Run(error);
}
void CachingCertVerifier::AddResultToCache(
uint32_t config_id,
const RequestParams& params,
base::Time start_time,
const CertVerifyResult& verify_result,
int error) {
if (config_id != config_id_)
return;
CachedResult cached_result;
cached_result.error = error;
cached_result.result = verify_result;
cache_.Put(
params, cached_result, CacheValidityPeriod(start_time),
CacheValidityPeriod(start_time, start_time + base::Seconds(kTTLSecs)));
}
void CachingCertVerifier::OnCertVerifierChanged() {
config_id_++;
ClearCache();
}
void CachingCertVerifier::OnTrustStoreChanged() {
config_id_++;
ClearCache();
}
void CachingCertVerifier::ClearCache() {
cache_.Clear();
}
size_t CachingCertVerifier::GetCacheSize() const {
return cache_.size();
}
}