• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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