OLD | NEW |
| (Empty) |
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). | |
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. | |
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. | |
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 | |
58 MultiLogCTVerifier::MultiLogCTVerifier() { } | |
59 | |
60 MultiLogCTVerifier::~MultiLogCTVerifier() { } | |
61 | |
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 | |
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 | |
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 | |
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 | |
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 | |
OLD | NEW |