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
22namespace net {
23
24namespace {
25
26// Record SCT verification status. This metric would help detecting presence
27// of unknown CT logs as well as bad deployments (invalid SCTs).
28void 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.
35void 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.
46void 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
58MultiLogCTVerifier::MultiLogCTVerifier() { }
59
60MultiLogCTVerifier::~MultiLogCTVerifier() { }
61
62void 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
71void 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
84int 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
166bool 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
200bool 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