Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Side by Side Diff: components/certificate_transparency/log_dns_client.cc

Issue 2066553002: Certificate Transparency DNS log client (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@mock_dns_responses
Patch Set: Revert using MerkleAuditProof::tree_size Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "components/certificate_transparency/log_dns_client.h"
6
7 #include <sstream>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/threading/thread_task_runner_handle.h"
15 #include "base/time/time.h"
16 #include "components/base32/base32.h"
17 #include "crypto/sha2.h"
18 #include "net/base/net_errors.h"
19 #include "net/cert/merkle_audit_proof.h"
20 #include "net/dns/dns_client.h"
21 #include "net/dns/dns_protocol.h"
22 #include "net/dns/dns_response.h"
23 #include "net/dns/dns_transaction.h"
24 #include "net/dns/record_parsed.h"
25 #include "net/dns/record_rdata.h"
26
27 namespace certificate_transparency {
28
29 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client,
30 const net::BoundNetLog& net_log)
31 : dns_client_(std::move(dns_client)),
32 net_log_(net_log),
33 weak_ptr_factory_(this) {
34 CHECK(dns_client_);
35 }
36
37 LogDnsClient::~LogDnsClient() {}
38
39 void LogDnsClient::QueryLeafIndex(base::StringPiece domain_for_log,
40 base::StringPiece leaf_hash,
41 const LeafIndexCallback& callback) {
42 if (domain_for_log.empty() || leaf_hash.size() != crypto::kSHA256Length) {
43 base::ThreadTaskRunnerHandle::Get()->PostTask(
44 FROM_HERE, base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, 0));
45 return;
46 }
47
48 std::string encoded_leaf_hash =
49 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING);
50 DCHECK_EQ(encoded_leaf_hash.size(), 52u);
51
52 std::ostringstream qname;
Eran Messeri 2016/07/05 15:28:12 Nit (here and below): You could define and fill qn
Rob Percival 2016/07/05 15:51:30 Done.
53 qname << encoded_leaf_hash << ".hash." << domain_for_log << ".";
54
55 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory();
56 if (factory == nullptr) {
57 base::ThreadTaskRunnerHandle::Get()->PostTask(
58 FROM_HERE,
59 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, 0));
60 return;
61 }
62
63 net::DnsTransactionFactory::CallbackType transaction_callback = base::Bind(
64 &LogDnsClient::QueryLeafIndexComplete, weak_ptr_factory_.GetWeakPtr());
65
66 std::unique_ptr<net::DnsTransaction> dns_transaction =
67 factory->CreateTransaction(qname.str(), net::dns_protocol::kTypeTXT,
68 transaction_callback, net_log_);
69
70 dns_transaction->Start();
71 leaf_index_queries_.push_back({std::move(dns_transaction), callback});
72 }
73
74 // The performance of this could be improved by sending all of the expected
75 // queries up front. Each response can contain a maximum of 7 audit path nodes,
76 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6,
77 // 7-13 and 14-19) immediately. Currently, it sends only the first and then,
78 // based on the number of nodes received, sends the next query. The complexity
79 // of the code would increase though, as it would need to detect gaps in the
80 // audit proof caused by the server not responding with the anticipated number
81 // of nodes. Ownership of the proof would need to change, as it would be shared
82 // between simultaneous DNS transactions.
83 void LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log,
84 uint64_t leaf_index,
85 uint64_t tree_size,
86 const AuditProofCallback& callback) {
87 if (domain_for_log.empty() || leaf_index >= tree_size) {
88 base::ThreadTaskRunnerHandle::Get()->PostTask(
89 FROM_HERE,
90 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr));
91 return;
92 }
93
94 std::unique_ptr<net::ct::MerkleAuditProof> proof(
95 new net::ct::MerkleAuditProof);
96 proof->leaf_index = leaf_index;
97 // TODO: Once a "tree_size" field is added to MerkleAuditProof, pass
98 // |tree_size| to QueryAuditProofNodes using that.
99
100 // Query for the first batch of audit proof nodes (i.e. starting from 0).
101 QueryAuditProofNodes(std::move(proof), domain_for_log, tree_size, 0,
102 callback);
103 }
104
105 void LogDnsClient::QueryLeafIndexComplete(net::DnsTransaction* transaction,
106 int net_error,
107 const net::DnsResponse* response) {
108 auto query_iterator =
109 std::find_if(leaf_index_queries_.begin(), leaf_index_queries_.end(),
110 [transaction](const Query<LeafIndexCallback>& query) {
111 return query.transaction.get() == transaction;
112 });
113 if (query_iterator == leaf_index_queries_.end()) {
114 NOTREACHED();
115 return;
116 }
117 const Query<LeafIndexCallback> query = std::move(*query_iterator);
118 leaf_index_queries_.erase(query_iterator);
119
120 // If we've received no response but no net::error either (shouldn't happen),
121 // report the response as invalid.
122 if (response == nullptr && net_error == net::OK) {
123 net_error = net::ERR_INVALID_RESPONSE;
124 }
125
126 if (net_error != net::OK) {
127 base::ThreadTaskRunnerHandle::Get()->PostTask(
128 FROM_HERE, base::Bind(query.callback, net_error, 0));
129 return;
130 }
131
132 uint64_t leaf_index;
133 if (!ParseLeafIndex(*response, &leaf_index)) {
134 base::ThreadTaskRunnerHandle::Get()->PostTask(
135 FROM_HERE,
136 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, 0));
137 return;
138 }
139
140 base::ThreadTaskRunnerHandle::Get()->PostTask(
141 FROM_HERE, base::Bind(query.callback, net::OK, leaf_index));
142 }
143
144 void LogDnsClient::QueryAuditProofNodes(
145 std::unique_ptr<net::ct::MerkleAuditProof> proof,
146 base::StringPiece domain_for_log,
147 uint64_t tree_size,
148 uint64_t node_index,
149 const AuditProofCallback& callback) {
150 // Preconditions that should be guaranteed internally by this class.
151 DCHECK(proof);
152 DCHECK(!domain_for_log.empty());
153 DCHECK_LT(proof->leaf_index, tree_size);
154 DCHECK_LT(node_index,
155 net::ct::CalculateAuditPathLength(proof->leaf_index, tree_size));
156
157 std::ostringstream qname;
158 qname << node_index << "." << proof->leaf_index << "." << tree_size
159 << ".tree." << domain_for_log << ".";
160
161 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory();
162 if (factory == nullptr) {
163 base::ThreadTaskRunnerHandle::Get()->PostTask(
164 FROM_HERE,
165 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, nullptr));
166 return;
167 }
168
169 net::DnsTransactionFactory::CallbackType transaction_callback =
170 base::Bind(&LogDnsClient::QueryAuditProofNodesComplete,
171 weak_ptr_factory_.GetWeakPtr(), base::Passed(std::move(proof)),
172 domain_for_log, tree_size);
173
174 std::unique_ptr<net::DnsTransaction> dns_transaction =
175 factory->CreateTransaction(qname.str(), net::dns_protocol::kTypeTXT,
176 transaction_callback, net_log_);
177 dns_transaction->Start();
178 audit_proof_queries_.push_back({std::move(dns_transaction), callback});
179 }
180
181 void LogDnsClient::QueryAuditProofNodesComplete(
182 std::unique_ptr<net::ct::MerkleAuditProof> proof,
183 base::StringPiece domain_for_log,
184 uint64_t tree_size,
185 net::DnsTransaction* transaction,
186 int net_error,
187 const net::DnsResponse* response) {
188 // Preconditions that should be guaranteed internally by this class.
189 DCHECK(proof);
190 DCHECK(!domain_for_log.empty());
191
192 auto query_iterator =
193 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(),
194 [transaction](const Query<AuditProofCallback>& query) {
195 return query.transaction.get() == transaction;
196 });
197
198 if (query_iterator == audit_proof_queries_.end()) {
199 NOTREACHED();
200 return;
201 }
202 const Query<AuditProofCallback> query = std::move(*query_iterator);
203 audit_proof_queries_.erase(query_iterator);
204
205 // If we've received no response but no net::error either (shouldn't happen),
206 // report the response as invalid.
207 if (response == nullptr && net_error == net::OK) {
208 net_error = net::ERR_INVALID_RESPONSE;
209 }
210
211 if (net_error != net::OK) {
212 base::ThreadTaskRunnerHandle::Get()->PostTask(
213 FROM_HERE, base::Bind(query.callback, net_error, nullptr));
214 return;
215 }
216
217 const uint64_t audit_path_length =
218 net::ct::CalculateAuditPathLength(proof->leaf_index, tree_size);
219 proof->nodes.reserve(audit_path_length);
220
221 if (!ParseAuditPath(*response, proof.get())) {
222 base::ThreadTaskRunnerHandle::Get()->PostTask(
223 FROM_HERE,
224 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, nullptr));
225 return;
226 }
227
228 const uint64_t audit_path_nodes_received = proof->nodes.size();
229 if (audit_path_nodes_received < audit_path_length) {
230 QueryAuditProofNodes(std::move(proof), domain_for_log, tree_size,
231 audit_path_nodes_received, query.callback);
232 return;
233 }
234
235 base::ThreadTaskRunnerHandle::Get()->PostTask(
236 FROM_HERE,
237 base::Bind(query.callback, net::OK, base::Passed(std::move(proof))));
238 }
239
240 bool LogDnsClient::ParseTxtResponse(const net::DnsResponse& response,
241 std::string* txt) {
242 DCHECK(txt);
243
244 net::DnsRecordParser parser = response.Parser();
245 // We don't care about the creation time, since we're going to throw
246 // |parsed_record| away as soon as we've extracted the payload, so provide
247 // the "null" time.
248 auto parsed_record = net::RecordParsed::CreateFrom(&parser, base::Time());
249 if (parsed_record == nullptr)
250 return false;
251
252 auto txt_record = parsed_record->rdata<net::TxtRecordRdata>();
253 if (txt_record == nullptr)
254 return false;
255
256 *txt = base::JoinString(txt_record->texts(), "");
257 return true;
258 }
259
260 bool LogDnsClient::ParseLeafIndex(const net::DnsResponse& response,
261 uint64_t* index) {
262 DCHECK(index);
263
264 std::string index_str;
265 if (!ParseTxtResponse(response, &index_str))
266 return false;
267
268 return base::StringToUint64(index_str, index);
269 }
270
271 bool LogDnsClient::ParseAuditPath(const net::DnsResponse& response,
272 net::ct::MerkleAuditProof* proof) {
273 DCHECK(proof);
274
275 std::string audit_path;
276 if (!ParseTxtResponse(response, &audit_path))
277 return false;
278 // If empty or not a multiple of the node size, it is considered invalid.
279 // It's important to consider empty audit paths as invalid, as otherwise an
280 // infinite loop could occur if the server consistently returned empty
281 // responses.
282 if (audit_path.empty() || audit_path.size() % crypto::kSHA256Length != 0)
283 return false;
284
285 for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) {
286 proof->nodes.push_back(audit_path.substr(i, crypto::kSHA256Length));
287 }
288
289 return true;
290 }
291
292 } // namespace certificate_transparency
OLDNEW
« no previous file with comments | « components/certificate_transparency/log_dns_client.h ('k') | components/certificate_transparency/log_dns_client_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698