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.
4
5 #include "net/cert/multi_log_ct_verifier.h"
6
7 #include <vector>
8
9 #include "base/bind.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"
21
22 namespace net {
23
24 namespace {
25
26 // Record SCT verification status. This metric would help detecting presence
27 // of unknown CT logs as well as bad deployments (invalid SCTs).
LogSCTStatusToUMA(ct::SCTVerifyStatus status)28 void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
29 UMA_HISTOGRAM_ENUMERATION(
30 "Net.CertificateTransparency.SCTStatus", status, ct::SCT_STATUS_MAX);
31 }
32
33 // Record SCT origin enum. This metric measure the popularity
34 // of the various channels of providing SCTs for a certificate.
LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin)35 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
36 UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
37 origin,
38 ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
39 }
40
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.
LogNumSCTsToUMA(const ct::CTVerifyResult & result)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(),
51 1,
52 10,
53 11);
54 }
55
56 } // namespace
57
MultiLogCTVerifier()58 MultiLogCTVerifier::MultiLogCTVerifier() { }
59
~MultiLogCTVerifier()60 MultiLogCTVerifier::~MultiLogCTVerifier() { }
61
AddLog(scoped_ptr<CTLogVerifier> log_verifier)62 void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) {
63 DCHECK(log_verifier);
64 if (!log_verifier)
65 return;
66
67 linked_ptr<CTLogVerifier> log(log_verifier.release());
68 logs_[log->key_id()] = log;
69 }
70
AddLogs(ScopedVector<CTLogVerifier> log_verifiers)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;
78 }
79
80 // Ownership of the pointers in |log_verifiers| is transferred to |logs_|
81 log_verifiers.weak_clear();
82 }
83
Verify(X509Certificate * cert,const std::string & stapled_ocsp_response,const std::string & sct_list_from_tls_extension,ct::CTVerifyResult * result,const BoundNetLog & net_log)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) {
90 DCHECK(cert);
91 DCHECK(result);
92
93 result->verified_scts.clear();
94 result->invalid_scts.clear();
95 result->unknown_logs_scts.clear();
96
97 bool has_verified_scts = false;
98
99 std::string embedded_scts;
100 if (!cert->GetIntermediateCertificates().empty() &&
101 ct::ExtractEmbeddedSCTList(
102 cert->os_cert_handle(),
103 &embedded_scts)) {
104 ct::LogEntry precert_entry;
105
106 has_verified_scts =
107 ct::GetPrecertLogEntry(
108 cert->os_cert_handle(),
109 cert->GetIntermediateCertificates().front(),
110 &precert_entry) &&
111 VerifySCTs(
112 embedded_scts,
113 precert_entry,
114 ct::SignedCertificateTimestamp::SCT_EMBEDDED,
115 result);
116 }
117
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);
124 }
125
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);
131
132 net_log.AddEvent(
133 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED,
134 net_log_callback);
135
136 ct::LogEntry x509_entry;
137 if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) {
138 has_verified_scts |= VerifySCTs(
139 sct_list_from_ocsp,
140 x509_entry,
141 ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
142 result);
143
144 has_verified_scts |= VerifySCTs(
145 sct_list_from_tls_extension,
146 x509_entry,
147 ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
148 result);
149 }
150
151 NetLog::ParametersCallback net_log_checked_callback =
152 base::Bind(&NetLogSignedCertificateTimestampCallback, result);
153
154 net_log.AddEvent(
155 NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
156 net_log_checked_callback);
157
158 LogNumSCTsToUMA(*result);
159
160 if (has_verified_scts)
161 return OK;
162
163 return ERR_CT_NO_SCTS_VERIFIED_OK;
164 }
165
VerifySCTs(const std::string & encoded_sct_list,const ct::LogEntry & expected_entry,ct::SignedCertificateTimestamp::Origin origin,ct::CTVerifyResult * result)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) {
171 if (logs_.empty())
172 return false;
173
174 base::StringPiece temp(encoded_sct_list);
175 std::vector<base::StringPiece> sct_list;
176
177 if (!ct::DecodeSCTList(&temp, &sct_list))
178 return false;
179
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);
185
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?
190 continue;
191 }
192 decoded_sct->origin = origin;
193
194 verified |= VerifySingleSCT(decoded_sct, expected_entry, result);
195 }
196
197 return verified;
198 }
199
VerifySingleSCT(scoped_refptr<ct::SignedCertificateTimestamp> sct,const ct::LogEntry & expected_entry,ct::CTVerifyResult * result)200 bool MultiLogCTVerifier::VerifySingleSCT(
201 scoped_refptr<ct::SignedCertificateTimestamp> sct,
202 const ct::LogEntry& expected_entry,
203 ct::CTVerifyResult* result) {
204
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);
211 return false;
212 }
213
214 sct->log_description = it->second->description();
215
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);
220 return false;
221 }
222
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);
228 return false;
229 }
230
231 LogSCTStatusToUMA(ct::SCT_STATUS_OK);
232 result->verified_scts.push_back(sct);
233 return true;
234 }
235
236 } // namespace net
237