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 "base/bind.h"
8#include "base/callback_helpers.h"
9#include "net/base/net_errors.h"
10#include "net/base/net_log.h"
11#include "net/cert/ct_log_verifier.h"
12#include "net/cert/ct_objects_extractor.h"
13#include "net/cert/ct_serialization.h"
14#include "net/cert/ct_signed_certificate_timestamp_log_param.h"
15#include "net/cert/ct_verify_result.h"
16#include "net/cert/x509_certificate.h"
17
18namespace net {
19
20MultiLogCTVerifier::MultiLogCTVerifier() { }
21
22MultiLogCTVerifier::~MultiLogCTVerifier() { }
23
24void MultiLogCTVerifier::AddLog(scoped_ptr<CTLogVerifier> log_verifier) {
25  DCHECK(log_verifier);
26  if (!log_verifier)
27    return;
28
29  linked_ptr<CTLogVerifier> log(log_verifier.release());
30  logs_[log->key_id()] = log;
31}
32
33int MultiLogCTVerifier::Verify(
34    X509Certificate* cert,
35    const std::string& stapled_ocsp_response,
36    const std::string& sct_list_from_tls_extension,
37    ct::CTVerifyResult* result,
38    const BoundNetLog& net_log)  {
39  DCHECK(cert);
40  DCHECK(result);
41
42  result->verified_scts.clear();
43  result->invalid_scts.clear();
44  result->unknown_logs_scts.clear();
45
46  bool has_verified_scts = false;
47
48  std::string embedded_scts;
49  if (!cert->GetIntermediateCertificates().empty() &&
50      ct::ExtractEmbeddedSCTList(
51          cert->os_cert_handle(),
52          &embedded_scts)) {
53    ct::LogEntry precert_entry;
54
55    has_verified_scts =
56        ct::GetPrecertLogEntry(
57            cert->os_cert_handle(),
58            cert->GetIntermediateCertificates().front(),
59            &precert_entry) &&
60        VerifySCTs(
61            embedded_scts,
62            precert_entry,
63            ct::SignedCertificateTimestamp::SCT_EMBEDDED,
64            result);
65  }
66
67  std::string sct_list_from_ocsp;
68  if (!stapled_ocsp_response.empty() &&
69      !cert->GetIntermediateCertificates().empty()) {
70    ct::ExtractSCTListFromOCSPResponse(
71        cert->GetIntermediateCertificates().front(), cert->serial_number(),
72        stapled_ocsp_response, &sct_list_from_ocsp);
73  }
74
75  // Log to Net Log, after extracting SCTs but before possibly failing on
76  // X.509 entry creation.
77  NetLog::ParametersCallback net_log_callback =
78      base::Bind(&NetLogRawSignedCertificateTimestampCallback,
79          &embedded_scts, &sct_list_from_ocsp, &sct_list_from_tls_extension);
80
81  net_log.AddEvent(
82      NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_RECEIVED,
83      net_log_callback);
84
85  ct::LogEntry x509_entry;
86  if (ct::GetX509LogEntry(cert->os_cert_handle(), &x509_entry)) {
87    has_verified_scts |= VerifySCTs(
88        sct_list_from_ocsp,
89        x509_entry,
90        ct::SignedCertificateTimestamp::SCT_FROM_OCSP_RESPONSE,
91        result);
92
93    has_verified_scts |= VerifySCTs(
94        sct_list_from_tls_extension,
95        x509_entry,
96        ct::SignedCertificateTimestamp::SCT_FROM_TLS_EXTENSION,
97        result);
98  }
99
100  NetLog::ParametersCallback net_log_checked_callback =
101      base::Bind(&NetLogSignedCertificateTimestampCallback, result);
102
103  net_log.AddEvent(
104      NetLog::TYPE_SIGNED_CERTIFICATE_TIMESTAMPS_CHECKED,
105      net_log_checked_callback);
106
107  if (has_verified_scts)
108    return OK;
109
110  return ERR_CT_NO_SCTS_VERIFIED_OK;
111}
112
113bool MultiLogCTVerifier::VerifySCTs(
114    const std::string& encoded_sct_list,
115    const ct::LogEntry& expected_entry,
116    ct::SignedCertificateTimestamp::Origin origin,
117    ct::CTVerifyResult* result) {
118  if (logs_.empty())
119    return false;
120
121  base::StringPiece temp(encoded_sct_list);
122  std::vector<base::StringPiece> sct_list;
123
124  if (!ct::DecodeSCTList(&temp, &sct_list))
125    return false;
126
127  bool verified = false;
128  for (std::vector<base::StringPiece>::const_iterator it = sct_list.begin();
129       it != sct_list.end(); ++it) {
130    base::StringPiece encoded_sct(*it);
131
132    scoped_refptr<ct::SignedCertificateTimestamp> decoded_sct;
133    if (!DecodeSignedCertificateTimestamp(&encoded_sct, &decoded_sct)) {
134      // XXX(rsleevi): Should we really just skip over bad SCTs?
135      continue;
136    }
137    decoded_sct->origin = origin;
138
139    verified |= VerifySingleSCT(decoded_sct, expected_entry, result);
140  }
141
142  return verified;
143}
144
145bool MultiLogCTVerifier::VerifySingleSCT(
146    scoped_refptr<ct::SignedCertificateTimestamp> sct,
147    const ct::LogEntry& expected_entry,
148    ct::CTVerifyResult* result) {
149
150  // Assume this SCT is untrusted until proven otherwise.
151  IDToLogMap::iterator it = logs_.find(sct->log_id);
152  if (it == logs_.end()) {
153    DVLOG(1) << "SCT does not match any known log.";
154    result->unknown_logs_scts.push_back(sct);
155    return false;
156  }
157
158  sct->log_description = it->second->description();
159
160  if (!it->second->Verify(expected_entry, *sct)) {
161    DVLOG(1) << "Unable to verify SCT signature.";
162    result->invalid_scts.push_back(sct);
163    return false;
164  }
165
166  // SCT verified ok, just make sure the timestamp is legitimate.
167  if (sct->timestamp > base::Time::Now()) {
168    DVLOG(1) << "SCT is from the future!";
169    result->invalid_scts.push_back(sct);
170    return false;
171  }
172
173  result->verified_scts.push_back(sct);
174  return true;
175}
176
177} // namespace net
178