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/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/values.h"
12 #include "net/base/net_errors.h"
13 #include "net/cert/ct_log_verifier.h"
14 #include "net/cert/ct_objects_extractor.h"
15 #include "net/cert/ct_serialization.h"
16 #include "net/cert/ct_signed_certificate_timestamp_log_param.h"
17 #include "net/cert/sct_status_flags.h"
18 #include "net/cert/signed_certificate_timestamp_and_status.h"
19 #include "net/cert/x509_certificate.h"
20 #include "net/log/net_log_event_type.h"
21 #include "net/log/net_log_with_source.h"
22 
23 namespace net {
24 
25 namespace {
26 
27 // Record SCT verification status. This metric would help detecting presence
28 // of unknown CT logs as well as bad deployments (invalid SCTs).
LogSCTStatusToUMA(ct::SCTVerifyStatus status)29 void LogSCTStatusToUMA(ct::SCTVerifyStatus status) {
30   // Note SCT_STATUS_MAX + 1 is passed to the UMA_HISTOGRAM_ENUMERATION as that
31   // macro requires the values to be strictly less than the boundary value,
32   // and SCT_STATUS_MAX is the last valid value of the SCTVerifyStatus enum
33   // (since that enum is used for IPC as well).
34   UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTStatus", status,
35                             ct::SCT_STATUS_MAX + 1);
36 }
37 
38 // Record SCT origin enum. This metric measure the popularity
39 // of the various channels of providing SCTs for a certificate.
LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin)40 void LogSCTOriginToUMA(ct::SignedCertificateTimestamp::Origin origin) {
41   UMA_HISTOGRAM_ENUMERATION("Net.CertificateTransparency.SCTOrigin",
42                             origin,
43                             ct::SignedCertificateTimestamp::SCT_ORIGIN_MAX);
44 }
45 
AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct,ct::SCTVerifyStatus status,SignedCertificateTimestampAndStatusList * sct_list)46 void AddSCTAndLogStatus(scoped_refptr<ct::SignedCertificateTimestamp> sct,
47                         ct::SCTVerifyStatus status,
48                         SignedCertificateTimestampAndStatusList* sct_list) {
49   LogSCTStatusToUMA(status);
50   sct_list->push_back(SignedCertificateTimestampAndStatus(sct, status));
51 }
52 
53 }  // namespace
54 
MultiLogCTVerifier()55 MultiLogCTVerifier::MultiLogCTVerifier() {}
56 
57 MultiLogCTVerifier::~MultiLogCTVerifier() = default;
58 
AddLogs(const std::vector<scoped_refptr<const CTLogVerifier>> & log_verifiers)59 void MultiLogCTVerifier::AddLogs(
60     const std::vector<scoped_refptr<const CTLogVerifier>>& log_verifiers) {
61   for (const auto& log_verifier : log_verifiers) {
62     VLOG(1) << "Adding CT log: " << log_verifier->description();
63     logs_[log_verifier->key_id()] = log_verifier;
64   }
65 }
66 
Verify(base::StringPiece hostname,X509Certificate * cert,base::StringPiece stapled_ocsp_response,base::StringPiece sct_list_from_tls_extension,SignedCertificateTimestampAndStatusList * output_scts,const NetLogWithSource & net_log)67 void MultiLogCTVerifier::Verify(
68     base::StringPiece hostname,
69     X509Certificate* cert,
70     base::StringPiece stapled_ocsp_response,
71     base::StringPiece sct_list_from_tls_extension,
72     SignedCertificateTimestampAndStatusList* output_scts,
73     const NetLogWithSource& net_log) {
74   DCHECK(cert);
75   DCHECK(output_scts);
76 
77   base::TimeTicks start = base::TimeTicks::Now();
78 
79   output_scts->clear();
80 
81   std::string embedded_scts;
82   if (!cert->intermediate_buffers().empty() &&
83       ct::ExtractEmbeddedSCTList(cert->cert_buffer(), &embedded_scts)) {
84     ct::SignedEntryData precert_entry;
85 
86     if (ct::GetPrecertSignedEntry(cert->cert_buffer(),
87                                   cert->intermediate_buffers().front().get(),
88                                   &precert_entry)) {
89       VerifySCTs(hostname, embedded_scts, precert_entry,
90                  ct::SignedCertificateTimestamp::SCT_EMBEDDED, cert,
91                  output_scts);
92     }
93   }
94 
95   std::string sct_list_from_ocsp;
96   if (!stapled_ocsp_response.empty() && !cert->intermediate_buffers().empty()) {
97     ct::ExtractSCTListFromOCSPResponse(
98         cert->intermediate_buffers().front().get(), cert->serial_number(),
99         stapled_ocsp_response, &sct_list_from_ocsp);
100   }
101 
102   // Log to Net Log, after extracting SCTs but before possibly failing on
103   // X.509 entry creation.
104   net_log.AddEvent(
105       NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED, [&] {
106         return NetLogRawSignedCertificateTimestampParams(
107             embedded_scts, sct_list_from_ocsp, sct_list_from_tls_extension);
108       });
109 
110   ct::SignedEntryData x509_entry;
111   if (ct::GetX509SignedEntry(cert->cert_buffer(), &x509_entry)) {
112     VerifySCTs(hostname, sct_list_from_ocsp, x509_entry,
113                ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE, cert,
114                output_scts);
115 
116     VerifySCTs(hostname, sct_list_from_tls_extension, x509_entry,
117                ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION, cert,
118                output_scts);
119   }
120 
121   // Only log the verification time if SCTs were provided.
122   if (!output_scts->empty()) {
123     base::TimeDelta verify_time = base::TimeTicks::Now() - start;
124     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
125         "Net.CertificateTransparency.SCT.VerificationTime", verify_time,
126         base::TimeDelta::FromMicroseconds(1),
127         base::TimeDelta::FromMilliseconds(100), 50);
128   }
129 
130   net_log.AddEvent(NetLogEventType::SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED, [&] {
131     return NetLogSignedCertificateTimestampParams(output_scts);
132   });
133 }
134 
VerifySCTs(base::StringPiece hostname,base::StringPiece encoded_sct_list,const ct::SignedEntryData & expected_entry,ct::SignedCertificateTimestamp::Origin origin,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)135 void MultiLogCTVerifier::VerifySCTs(
136     base::StringPiece hostname,
137     base::StringPiece encoded_sct_list,
138     const ct::SignedEntryData& expected_entry,
139     ct::SignedCertificateTimestamp::Origin origin,
140     X509Certificate* cert,
141     SignedCertificateTimestampAndStatusList* output_scts) {
142   if (logs_.empty())
143     return;
144 
145   std::vector<base::StringPiece> sct_list;
146 
147   if (!ct::DecodeSCTList(encoded_sct_list, &sct_list))
148     return;
149 
150   for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
151        it != sct_list.end(); ++it) {
152     base::StringPiece encoded_sct(*it);
153     LogSCTOriginToUMA(origin);
154 
155     scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
156     if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
157       LogSCTStatusToUMA(ct::SCT_STATUS_NONE);
158       continue;
159     }
160     decoded_sct->origin = origin;
161 
162     base::TimeTicks start = base::TimeTicks::Now();
163     VerifySingleSCT(hostname, decoded_sct, expected_entry, cert, output_scts);
164     base::TimeDelta verify_time = base::TimeTicks::Now() - start;
165     UMA_HISTOGRAM_CUSTOM_MICROSECONDS_TIMES(
166         "Net.CertificateTransparency.SCT.SingleVerificationTime", verify_time,
167         base::TimeDelta::FromMicroseconds(1),
168         base::TimeDelta::FromMilliseconds(100), 50);
169   }
170 }
171 
VerifySingleSCT(base::StringPiece hostname,scoped_refptr<ct::SignedCertificateTimestamp> sct,const ct::SignedEntryData & expected_entry,X509Certificate * cert,SignedCertificateTimestampAndStatusList * output_scts)172 bool MultiLogCTVerifier::VerifySingleSCT(
173     base::StringPiece hostname,
174     scoped_refptr<ct::SignedCertificateTimestamp> sct,
175     const ct::SignedEntryData& expected_entry,
176     X509Certificate* cert,
177     SignedCertificateTimestampAndStatusList* output_scts) {
178   // Assume this SCT is untrusted until proven otherwise.
179   const auto& it = logs_.find(sct->log_id);
180   if (it == logs_.end()) {
181     DVLOG(1) << "SCT does not match any known log.";
182     AddSCTAndLogStatus(sct, ct::SCT_STATUS_LOG_UNKNOWN, output_scts);
183     return false;
184   }
185 
186   sct->log_description = it->second->description();
187 
188   if (!it->second->Verify(expected_entry, *sct.get())) {
189     DVLOG(1) << "Unable to verify SCT signature.";
190     AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_SIGNATURE, output_scts);
191     return false;
192   }
193 
194   // SCT verified ok, just make sure the timestamp is legitimate.
195   if (sct->timestamp > base::Time::Now()) {
196     DVLOG(1) << "SCT is from the future!";
197     AddSCTAndLogStatus(sct, ct::SCT_STATUS_INVALID_TIMESTAMP, output_scts);
198     return false;
199   }
200 
201   AddSCTAndLogStatus(sct, ct::SCT_STATUS_OK, output_scts);
202   return true;
203 }
204 
205 } // namespace net
206