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"
8 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram.h"
10 #include "net/base/net_errors.h"
11 #include "net/base/net_log.h"
12 #include "net/cert/ct_log_verifier.h"
13 #include "net/cert/ct_objects_extractor.h"
14 #include "net/cert/ct_serialization.h"
15 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
16 #include "net/cert/ct_verify_result.h"
17 #include "net/cert/sct_status_flags.h"
18 #include "net/cert/x509_certificate.h"
24 // Record SCT verification status. This metric would help detecting presence
25 // of unknown CT logs as well as bad deployments (invalid SCTs).
26 void LogSCTStatusToUMA(ct::SCTVerifyStatus status
) {
27 UMA_HISTOGRAM_ENUMERATION(
28 "Net.CertificateTransparency.SCTStatus", status
, ct::SCT_STATUS_MAX
);
31 // Record SCT origin enum. This metric measure the popularity
32 // of the various channels of providing SCTs for a certificate.
33 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin
) {
34 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
36 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX
);
39 // Count the number of SCTs that were available for each SSL connection
40 // (including SCTs embedded in the certificate).
41 // This metric would allow measuring:
42 // * Of all SSL connections, how many had SCTs available for validation.
43 // * When SCTs are available, how many are available per connection.
44 void LogNumSCTsToUMA(const ct::CTVerifyResult
& result
) {
45 UMA_HISTOGRAM_CUSTOM_COUNTS("Net.CertificateTransparency.SCTsPerConnection",
46 result
.invalid_scts
.size() +
47 result
.verified_scts
.size() +
48 result
.unknown_logs_scts
.size(),
56 MultiLogCTVerifier::MultiLogCTVerifier() { }
58 MultiLogCTVerifier::~MultiLogCTVerifier() { }
60 void MultiLogCTVerifier::AddLog(scoped_ptr
<CTLogVerifier
> log_verifier
) {
65 linked_ptr
<CTLogVerifier
> log(log_verifier
.release());
66 logs_
[log
->key_id()] = log
;
69 int MultiLogCTVerifier::Verify(
70 X509Certificate
* cert
,
71 const std::string
& stapled_ocsp_response
,
72 const std::string
& sct_list_from_tls_extension
,
73 ct::CTVerifyResult
* result
,
74 const BoundNetLog
& net_log
) {
78 result
->verified_scts
.clear();
79 result
->invalid_scts
.clear();
80 result
->unknown_logs_scts
.clear();
82 bool has_verified_scts
= false;
84 std::string embedded_scts
;
85 if (!cert
->GetIntermediateCertificates().empty() &&
86 ct::ExtractEmbeddedSCTList(
87 cert
->os_cert_handle(),
89 ct::LogEntry precert_entry
;
92 ct::GetPrecertLogEntry(
93 cert
->os_cert_handle(),
94 cert
->GetIntermediateCertificates().front(),
99 ct::SignedCertificateTimestamp::SCT_EMBEDDED
,
103 std::string sct_list_from_ocsp
;
104 if (!stapled_ocsp_response
.empty() &&
105 !cert
->GetIntermediateCertificates().empty()) {
106 ct::ExtractSCTListFromOCSPResponse(
107 cert
->GetIntermediateCertificates().front(), cert
->serial_number(),
108 stapled_ocsp_response
, &sct_list_from_ocsp
);
111 // Log to Net Log, after extracting SCTs but before possibly failing on
112 // X.509 entry creation.
113 NetLog::ParametersCallback net_log_callback
=
114 base::Bind(&NetLogRawSignedCertificateTimestampCallback
,
115 &embedded_scts
, &sct_list_from_ocsp
, &sct_list_from_tls_extension
);
118 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED
,
121 ct::LogEntry x509_entry
;
122 if (ct::GetX509LogEntry(cert
->os_cert_handle(), &x509_entry
)) {
123 has_verified_scts
|= VerifySCTs(
126 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE
,
129 has_verified_scts
|= VerifySCTs(
130 sct_list_from_tls_extension
,
132 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION
,
136 NetLog::ParametersCallback net_log_checked_callback
=
137 base::Bind(&NetLogSignedCertificateTimestampCallback
, result
);
140 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED
,
141 net_log_checked_callback
);
143 LogNumSCTsToUMA(*result
);
145 if (has_verified_scts
)
148 return ERR_CT_NO_SCTS_VERIFIED_OK
;
151 bool MultiLogCTVerifier::VerifySCTs(
152 const std::string
& encoded_sct_list
,
153 const ct::LogEntry
& expected_entry
,
154 ct::SignedCertificateTimestamp::Origin origin
,
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
, result
);
185 bool MultiLogCTVerifier::VerifySingleSCT(
186 scoped_refptr
<ct::SignedCertificateTimestamp
> sct
,
187 const ct::LogEntry
& expected_entry
,
188 ct::CTVerifyResult
* result
) {
190 // Assume this SCT is untrusted until proven otherwise.
191 IDToLogMap::iterator 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
)) {
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
);