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"
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"
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",
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(),
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
) {
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
) {
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(),
96 ct::LogEntry precert_entry
;
99 ct::GetPrecertLogEntry(cert
->os_cert_handle(),
100 cert
->GetIntermediateCertificates().front(),
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
);
121 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED
,
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
);
139 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED
,
140 net_log_checked_callback
);
142 LogNumSCTsToUMA(*result
);
144 if (has_verified_scts
)
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
) {
159 base::StringPiece
temp(encoded_sct_list
);
160 std::vector
<base::StringPiece
> sct_list
;
162 if (!ct::DecodeSCTList(&temp
, &sct_list
))
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?
177 decoded_sct
->origin
= origin
;
179 verified
|= VerifySingleSCT(decoded_sct
, expected_entry
, cert
, result
);
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
);
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
);
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
);
216 LogSCTStatusToUMA(ct::SCT_STATUS_OK
);
217 result
->verified_scts
.push_back(sct
);
219 observer_
->OnSCTVerified(cert
, sct
.get());