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.h"
12 #include "net/base/net_errors.h"
13 #include "net/base/net_log.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"
26 // Record SCT verification status. This metric would help detecting presence
27 // of unknown CT logs as well as bad deployments (invalid SCTs).
28 void LogSCTStatusToUMA(ct::SCTVerifyStatus status
) {
29 UMA_HISTOGRAM_ENUMERATION(
30 "Net.CertificateTransparency.SCTStatus", status
, ct::SCT_STATUS_MAX
);
33 // Record SCT origin enum. This metric measure the popularity
34 // of the various channels of providing SCTs for a certificate.
35 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin
) {
36 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
38 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX
);
41 // Count the number of SCTs that were available for each SSL connection
42 // (including SCTs embedded in the certificate).
43 // This metric would allow measuring:
44 // * Of all SSL connections, how many had SCTs available for validation.
45 // * When SCTs are available, how many are available per connection.
46 void LogNumSCTsToUMA(const ct::CTVerifyResult
& result
) {
47 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection",
48 result
.invalid_scts
.size() +
49 result
.verified_scts
.size() +
50 result
.unknown_logs_scts
.size(),
58 MultiLogCTVerifier::MultiLogCTVerifier() { }
60 MultiLogCTVerifier::~MultiLogCTVerifier() { }
62 void MultiLogCTVerifier::AddLog(scoped_ptr
<CTLogVerifier
> log_verifier
) {
67 linked_ptr
<CTLogVerifier
> log(log_verifier
.release());
68 logs_
[log
->key_id()] = log
;
71 void MultiLogCTVerifier::AddLogs(
72 ScopedVector
<CTLogVerifier
> log_verifiers
) {
73 for (ScopedVector
<CTLogVerifier
>::iterator it
=
74 log_verifiers
.begin(); it
!= log_verifiers
.end(); ++it
) {
75 linked_ptr
<CTLogVerifier
> log(*it
);
76 VLOG(1) << "Adding CT log: " << log
->description();
77 logs_
[log
->key_id()] = log
;
80 // Ownership of the pointers in |log_verifiers| is transferred to |logs_|
81 log_verifiers
.weak_clear();
84 int MultiLogCTVerifier::Verify(
85 X509Certificate
* cert
,
86 const std::string
& stapled_ocsp_response
,
87 const std::string
& sct_list_from_tls_extension
,
88 ct::CTVerifyResult
* result
,
89 const BoundNetLog
& net_log
) {
93 result
->verified_scts
.clear();
94 result
->invalid_scts
.clear();
95 result
->unknown_logs_scts
.clear();
97 bool has_verified_scts
= false;
99 std::string embedded_scts
;
100 if (!cert
->GetIntermediateCertificates().empty() &&
101 ct::ExtractEmbeddedSCTList(
102 cert
->os_cert_handle(),
104 ct::LogEntry precert_entry
;
107 ct::GetPrecertLogEntry(
108 cert
->os_cert_handle(),
109 cert
->GetIntermediateCertificates().front(),
114 ct::SignedCertificateTimestamp::SCT_EMBEDDED
,
118 std::string sct_list_from_ocsp
;
119 if (!stapled_ocsp_response
.empty() &&
120 !cert
->GetIntermediateCertificates().empty()) {
121 ct::ExtractSCTListFromOCSPResponse(
122 cert
->GetIntermediateCertificates().front(), cert
->serial_number(),
123 stapled_ocsp_response
, &sct_list_from_ocsp
);
126 // Log to Net Log, after extracting SCTs but before possibly failing on
127 // X.509 entry creation.
128 NetLog::ParametersCallback net_log_callback
=
129 base::Bind(&NetLogRawSignedCertificateTimestampCallback
,
130 &embedded_scts
, &sct_list_from_ocsp
, &sct_list_from_tls_extension
);
133 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED
,
136 ct::LogEntry x509_entry
;
137 if (ct::GetX509LogEntry(cert
->os_cert_handle(), &x509_entry
)) {
138 has_verified_scts
|= VerifySCTs(
141 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE
,
144 has_verified_scts
|= VerifySCTs(
145 sct_list_from_tls_extension
,
147 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION
,
151 NetLog::ParametersCallback net_log_checked_callback
=
152 base::Bind(&NetLogSignedCertificateTimestampCallback
, result
);
155 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED
,
156 net_log_checked_callback
);
158 LogNumSCTsToUMA(*result
);
160 if (has_verified_scts
)
163 return ERR_CT_NO_SCTS_VERIFIED_OK
;
166 bool MultiLogCTVerifier::VerifySCTs(
167 const std::string
& encoded_sct_list
,
168 const ct::LogEntry
& expected_entry
,
169 ct::SignedCertificateTimestamp::Origin origin
,
170 ct::CTVerifyResult
* result
) {
174 base::StringPiece
temp(encoded_sct_list
);
175 std::vector
<base::StringPiece
> sct_list
;
177 if (!ct::DecodeSCTList(&temp
, &sct_list
))
180 bool verified
= false;
181 for (std::vector
<base::StringPiece
>::const_iterator it
= sct_list
.begin();
182 it
!= sct_list
.end(); ++it
) {
183 base::StringPiece
encoded_sct(*it
);
184 LogSCTOriginToUMA(origin
);
186 scoped_refptr
<ct::SignedCertificateTimestamp
> decoded_sct
;
187 if (!DecodeSignedCertificateTimestamp(&encoded_sct
, &decoded_sct
)) {
188 LogSCTStatusToUMA(ct::SCT_STATUS_NONE
);
189 // XXX(rsleevi): Should we really just skip over bad SCTs?
192 decoded_sct
->origin
= origin
;
194 verified
|= VerifySingleSCT(decoded_sct
, expected_entry
, result
);
200 bool MultiLogCTVerifier::VerifySingleSCT(
201 scoped_refptr
<ct::SignedCertificateTimestamp
> sct
,
202 const ct::LogEntry
& expected_entry
,
203 ct::CTVerifyResult
* result
) {
205 // Assume this SCT is untrusted until proven otherwise.
206 IDToLogMap::iterator it
= logs_
.find(sct
->log_id
);
207 if (it
== logs_
.end()) {
208 DVLOG(1) << "SCT does not match any known log.";
209 result
->unknown_logs_scts
.push_back(sct
);
210 LogSCTStatusToUMA(ct::SCT_STATUS_LOG_UNKNOWN
);
214 sct
->log_description
= it
->second
->description();
216 if (!it
->second
->Verify(expected_entry
, *sct
.get())) {
217 DVLOG(1) << "Unable to verify SCT signature.";
218 result
->invalid_scts
.push_back(sct
);
219 LogSCTStatusToUMA(ct::SCT_STATUS_INVALID
);
223 // SCT verified ok, just make sure the timestamp is legitimate.
224 if (sct
->timestamp
> base::Time::Now()) {
225 DVLOG(1) << "SCT is from the future!";
226 result
->invalid_scts
.push_back(sct
);
227 LogSCTStatusToUMA(ct::SCT_STATUS_INVALID
);
231 LogSCTStatusToUMA(ct::SCT_STATUS_OK
);
232 result
->verified_scts
.push_back(sct
);