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
18 namespace net {
19
MultiLogCTVerifier()20 MultiLogCTVerifier::MultiLogCTVerifier() { }
21
~MultiLogCTVerifier()22 MultiLogCTVerifier::~MultiLogCTVerifier() { }
23
AddLog(scoped_ptr<CTLogVerifier> log_verifier)24 void 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
Verify(X509Certificate * cert,const std::string & stapled_ocsp_response,const std::string & sct_list_from_tls_extension,ct::CTVerifyResult * result,const BoundNetLog & net_log)33 int 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
VerifySCTs(const std::string & encoded_sct_list,const ct::LogEntry & expected_entry,ct::SignedCertificateTimestamp::Origin origin,ct::CTVerifyResult * result)113 bool 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
VerifySingleSCT(scoped_refptr<ct::SignedCertificateTimestamp> sct,const ct::LogEntry & expected_entry,ct::CTVerifyResult * result)145 bool 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