Remove linux_chromium_gn_dbg from the chromium CQ.
[chromium-blink-merge.git] / net / cert / multi_log_ct_verifier.cc
blob7e421b2980b1f6943970478d9854109262448f20
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
3 // found in the LICENSE file.
5 #include "net/cert/multi_log_ct_verifier.h"
7 #include <vector>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/metrics/histogram_macros.h"
12 #include "base/values.h"
13 #include "net/base/net_errors.h"
14 #include "net/cert/ct_log_verifier.h"
15 #include "net/cert/ct_objects_extractor.h"
16 #include "net/cert/ct_serialization.h"
17 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
18 #include "net/cert/ct_verify_result.h"
19 #include "net/cert/sct_status_flags.h"
20 #include "net/cert/x509_certificate.h"
21 #include "net/log/net_log.h"
23 namespace net {
25 namespace {
27 // Record SCT verification status. This metric would help detecting presence
28 // of unknown CT logs as well as bad deployments (invalid SCTs).
29 void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
30 UMA_HISTOGRAM_ENUMERATION(
31 "Net.CertificateTransparency.SCTStatus", status, ct::SCT_STATUS_MAX);
34 // Record SCT origin enum. This metric measure the popularity
35 // of the various channels of providing SCTs for a certificate.
36 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
37 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
38 origin,
39 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
42 // Count the number of SCTs that were available for each SSL connection
43 // (including SCTs embedded in the certificate).
44 // This metric would allow measuring:
45 // * Of all SSL connections, how many had SCTs available for validation.
46 // * When SCTs are available, how many are available per connection.
47 void LogNumSCTsToUMA(const ct::CTVerifyResult& result) {
48 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection",
49 result.invalid_scts.size() +
50 result.verified_scts.size() +
51 result.unknown_logs_scts.size(),
53 10,
54 11);
57 } // namespace
59 MultiLogCTVerifier::MultiLogCTVerifier() : observer_(nullptr) {
62 MultiLogCTVerifier::~MultiLogCTVerifier() { }
64 void MultiLogCTVerifier::AddLogs(
65 const std::vector<scoped_refptr<CTLogVerifier>>& log_verifiers) {
66 for (const auto& log_verifier : log_verifiers) {
67 VLOG(1) << "Adding CT log: " << log_verifier->description();
68 logs_[log_verifier->key_id()] = log_verifier;
72 void MultiLogCTVerifier::SetObserver(Observer* observer) {
73 observer_ = observer;
76 int MultiLogCTVerifier::Verify(
77 X509Certificate* cert,
78 const std::string& stapled_ocsp_response,
79 const std::string& sct_list_from_tls_extension,
80 ct::CTVerifyResult* result,
81 const BoundNetLog& net_log) {
82 DCHECK(cert);
83 DCHECK(result);
85 result->verified_scts.clear();
86 result->invalid_scts.clear();
87 result->unknown_logs_scts.clear();
89 bool has_verified_scts = false;
91 std::string embedded_scts;
92 if (!cert->GetIntermediateCertificates().empty() &&
93 ct::ExtractEmbeddedSCTList(
94 cert->os_cert_handle(),
95 &embedded_scts)) {
96 ct::LogEntry precert_entry;
98 has_verified_scts =
99 ct::GetPrecertLogEntry(cert->os_cert_handle(),
100 cert->GetIntermediateCertificates().front(),
101 &precert_entry) &&
102 VerifySCTs(embedded_scts, precert_entry,
103 ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert, result);
106 std::string sct_list_from_ocsp;
107 if (!stapled_ocsp_response.empty() &&
108 !cert->GetIntermediateCertificates().empty()) {
109 ct::ExtractSCTListFromOCSPResponse(
110 cert->GetIntermediateCertificates().front(), cert->serial_number(),
111 stapled_ocsp_response, &sct_list_from_ocsp);
114 // Log to Net Log, after extracting SCTs but before possibly failing on
115 // X.509 entry creation.
116 NetLog::ParametersCallback net_log_callback =
117 base::Bind(&NetLogRawSignedCertificateTimestampCallback,
118 &embedded_scts, &sct_list_from_ocsp, &sct_list_from_tls_extension);
120 net_log.AddEvent(
121 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED,
122 net_log_callback);
124 ct::LogEntry x509_entry;
125 if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) {
126 has_verified_scts |= VerifySCTs(
127 sct_list_from_ocsp, x509_entry,
128 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert, result);
130 has_verified_scts |= VerifySCTs(
131 sct_list_from_tls_extension, x509_entry,
132 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert, result);
135 NetLog::ParametersCallback net_log_checked_callback =
136 base::Bind(&NetLogSignedCertificateTimestampCallback, result);
138 net_log.AddEvent(
139 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
140 net_log_checked_callback);
142 LogNumSCTsToUMA(*result);
144 if (has_verified_scts)
145 return OK;
147 return ERR_CT_NO_SCTS_VERIFIED_OK;
150 bool MultiLogCTVerifier::VerifySCTs(
151 const std::string& encoded_sct_list,
152 const ct::LogEntry& expected_entry,
153 ct::SignedCertificateTimestamp::Origin origin,
154 X509Certificate* cert,
155 ct::CTVerifyResult* result) {
156 if (logs_.empty())
157 return false;
159 base::StringPiece temp(encoded_sct_list);
160 std::vector<base::StringPiece> sct_list;
162 if (!ct::DecodeSCTList(&temp, &sct_list))
163 return false;
165 bool verified = false;
166 for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
167 it != sct_list.end(); ++it) {
168 base::StringPiece encoded_sct(*it);
169 LogSCTOriginToUMA(origin);
171 scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
172 if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
173 LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
174 // XXX(rsleevi): Should we really just skip over bad SCTs?
175 continue;
177 decoded_sct->origin = origin;
179 verified |= VerifySingleSCT(decoded_sct, expected_entry, cert, result);
182 return verified;
185 bool MultiLogCTVerifier::VerifySingleSCT(
186 scoped_refptr<ct::SignedCertificateTimestamp> sct,
187 const ct::LogEntry& expected_entry,
188 X509Certificate* cert,
189 ct::CTVerifyResult* result) {
190 // Assume this SCT is untrusted until proven otherwise.
191 const auto& it = logs_.find(sct->log_id);
192 if (it == logs_.end()) {
193 DVLOG(1) << "SCT does not match any known log.";
194 result->unknown_logs_scts.push_back(sct);
195 LogSCTStatusToUMA(ct::SCT_STATUS_LOG_UNKNOWN);
196 return false;
199 sct->log_description = it->second->description();
201 if (!it->second->Verify(expected_entry, *sct.get())) {
202 DVLOG(1) << "Unable to verify SCT signature.";
203 result->invalid_scts.push_back(sct);
204 LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
205 return false;
208 // SCT verified ok, just make sure the timestamp is legitimate.
209 if (sct->timestamp > base::Time::Now()) {
210 DVLOG(1) << "SCT is from the future!";
211 result->invalid_scts.push_back(sct);
212 LogSCTStatusToUMA(ct::SCT_STATUS_INVALID);
213 return false;
216 LogSCTStatusToUMA(ct::SCT_STATUS_OK);
217 result->verified_scts.push_back(sct);
218 if (observer_)
219 observer_->OnSCTVerified(cert, sct.get());
220 return true;
223 } // namespace net