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