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

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: Rebase 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 "base/bind.h"
8 #include "base/location.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/threading/thread_task_runner_handle.h"
13 #include "base/time/time.h"
14 #include "components/base32/base32.h"
15 #include "crypto/sha2.h"
16 #include "net/base/net_errors.h"
17 #include "net/cert/merkle_audit_proof.h"
18 #include "net/dns/dns_client.h"
19 #include "net/dns/dns_protocol.h"
20 #include "net/dns/dns_response.h"
21 #include "net/dns/dns_transaction.h"
22 #include "net/dns/record_parsed.h"
23 #include "net/dns/record_rdata.h"
24
25 namespace certificate_transparency {
26
27 LogDnsClient::LogDnsClient(std::unique_ptr<net::DnsClient> dns_client,
28 const net::BoundNetLog& net_log)
29 : dns_client_(std::move(dns_client)),
30 net_log_(net_log),
31 weak_ptr_factory_(this) {
32 CHECK(dns_client_);
33 }
34
35 LogDnsClient::~LogDnsClient() {}
36
37 void LogDnsClient::QueryLeafIndex(base::StringPiece domain_for_log,
38 base::StringPiece leaf_hash,
39 const LeafIndexCallback& callback) {
40 if (leaf_hash.size() != crypto::kSHA256Length) {
41 base::ThreadTaskRunnerHandle::Get()->PostTask(
42 FROM_HERE, base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, 0));
43 return;
44 }
45
46 std::string encoded_leaf_hash =
47 base32::Base32Encode(leaf_hash, base32::Base32EncodePolicy::OMIT_PADDING);
48 DCHECK_EQ(encoded_leaf_hash.size(), 52u);
49 std::string qname = encoded_leaf_hash;
Eran Messeri 2016/07/01 09:08:00 Nit: Use StringPrintf
Rob Percival 2016/07/01 16:01:01 I've opted for using std::ostringstream instead, a
50 qname += ".hash.";
51 domain_for_log.AppendToString(&qname);
52 qname += ".";
53
54 net::DnsTransactionFactory* factory = dns_client_->GetTransactionFactory();
55 if (factory == nullptr) {
56 base::ThreadTaskRunnerHandle::Get()->PostTask(
57 FROM_HERE,
58 base::Bind(callback, net::Error::ERR_NAME_RESOLUTION_FAILED, 0));
59 return;
60 }
61
62 net::DnsTransactionFactory::CallbackType transaction_callback = base::Bind(
63 &LogDnsClient::QueryLeafIndexComplete, weak_ptr_factory_.GetWeakPtr());
64
65 std::unique_ptr<net::DnsTransaction> dns_transaction =
66 factory->CreateTransaction(qname, net::dns_protocol::kTypeTXT,
67 transaction_callback, net_log_);
68
69 dns_transaction->Start();
70 leaf_index_queries_.push_back({std::move(dns_transaction), callback});
71 }
72
73 // The performance of this could be improved by sending all of the expected
74 // queries up front. Each response can contain a maximum of 7 audit path nodes,
75 // so for an audit proof of size 20, it could send 3 queries (for nodes 0-6,
76 // 7-13 and 14-19) immediately. Currently, it sends only the first and then,
77 // based on the number of nodes received, sends the next query. The complexity
78 // of the code would increase though, as it would need to detect gaps in the
79 // audit proof caused by the server not responding with the anticipated number
80 // of nodes. Ownership of the proof would need to change, as it would be shared
81 // between simultaneous DNS transactions.
82 void LogDnsClient::QueryAuditProof(base::StringPiece domain_for_log,
83 uint64_t leaf_index,
84 uint64_t tree_size,
85 const AuditProofCallback& callback) {
86 if (leaf_index >= tree_size) {
87 base::ThreadTaskRunnerHandle::Get()->PostTask(
88 FROM_HERE,
89 base::Bind(callback, net::Error::ERR_INVALID_ARGUMENT, nullptr));
90 return;
91 }
92
93 std::unique_ptr<net::ct::MerkleAuditProof> proof(
94 new net::ct::MerkleAuditProof);
95 proof->leaf_index = leaf_index;
96
97 QueryAuditProofNodes(std::move(proof), domain_for_log, leaf_index, tree_size,
Eran Messeri 2016/07/01 09:07:59 You could drop the leaf_index from the parameters
Rob Percival 2016/07/01 16:01:01 Done.
98 0, callback);
Eran Messeri 2016/07/01 09:08:00 comment what the 0 stands for, e.g.: 0 /* first pr
Rob Percival 2016/07/01 16:01:01 Done.
99 }
100
101 void LogDnsClient::QueryLeafIndexComplete(net::DnsTransaction* transaction,
102 int net_error,
103 const net::DnsResponse* response) {
104 auto query_iterator =
105 std::find_if(leaf_index_queries_.begin(), leaf_index_queries_.end(),
106 [transaction](const Query<LeafIndexCallback>& query) {
107 return query.transaction.get() == transaction;
108 });
109 if (query_iterator == leaf_index_queries_.end()) {
110 NOTREACHED();
111 return;
112 }
113 const Query<LeafIndexCallback> query = std::move(*query_iterator);
114 leaf_index_queries_.erase(query_iterator);
115
116 if (net_error != net::OK) {
117 base::ThreadTaskRunnerHandle::Get()->PostTask(
118 FROM_HERE, base::Bind(query.callback, net_error, 0));
119 return;
120 }
121
122 if (response == nullptr) {
Eran Messeri 2016/07/01 09:08:00 Nit: You could move this condition up (to line 115
Rob Percival 2016/07/01 16:01:01 That could hide a more specific net_error though.
123 base::ThreadTaskRunnerHandle::Get()->PostTask(
124 FROM_HERE, base::Bind(query.callback, net::ERR_INVALID_RESPONSE, 0));
125 return;
126 }
127
128 uint64_t leaf_index;
129 if (!ParseLeafIndex(*response, &leaf_index)) {
130 base::ThreadTaskRunnerHandle::Get()->PostTask(
131 FROM_HERE,
132 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, 0));
133 return;
134 }
135
136 base::ThreadTaskRunnerHandle::Get()->PostTask(
137 FROM_HERE, base::Bind(query.callback, net::OK, leaf_index));
138 }
139
140 void LogDnsClient::QueryAuditProofNodes(
141 std::unique_ptr<net::ct::MerkleAuditProof> proof,
142 base::StringPiece domain_for_log,
143 uint64_t leaf_index,
144 uint64_t tree_size,
145 uint64_t node_index,
146 const AuditProofCallback& callback) {
147 CHECK(proof);
148 CHECK_LT(leaf_index, tree_size);
149 CHECK_LT(node_index,
150 net::ct::CalculateAuditPathLength(leaf_index, tree_size));
151
152 std::string qname = base::Uint64ToString(node_index);
153 qname += ".";
154 qname += base::Uint64ToString(leaf_index);
155 qname += ".";
156 qname += base::Uint64ToString(tree_size);
157 qname += ".tree.";
158 domain_for_log.AppendToString(&qname);
159 qname += ".";
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, leaf_index, tree_size);
173
174 std::unique_ptr<net::DnsTransaction> dns_transaction =
175 factory->CreateTransaction(qname, 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 leaf_index,
185 uint64_t tree_size,
186 net::DnsTransaction* transaction,
187 int net_error,
188 const net::DnsResponse* response) {
189 auto query_iterator =
190 std::find_if(audit_proof_queries_.begin(), audit_proof_queries_.end(),
191 [transaction](const Query<AuditProofCallback>& query) {
192 return query.transaction.get() == transaction;
193 });
194
195 if (query_iterator == audit_proof_queries_.end()) {
196 NOTREACHED();
197 return;
198 }
199 const Query<AuditProofCallback> query = std::move(*query_iterator);
200 audit_proof_queries_.erase(query_iterator);
201
202 if (net_error != net::OK) {
203 base::ThreadTaskRunnerHandle::Get()->PostTask(
204 FROM_HERE, base::Bind(query.callback, net_error, nullptr));
205 return;
206 }
207
208 if (response == nullptr) {
209 base::ThreadTaskRunnerHandle::Get()->PostTask(
210 FROM_HERE,
211 base::Bind(query.callback, net::ERR_INVALID_RESPONSE, nullptr));
212 return;
213 }
214
215 const uint64_t audit_path_length =
216 net::ct::CalculateAuditPathLength(leaf_index, tree_size);
217 proof->nodes.reserve(audit_path_length);
218
219 if (!ParseAuditPath(*response, proof.get())) {
220 base::ThreadTaskRunnerHandle::Get()->PostTask(
221 FROM_HERE,
222 base::Bind(query.callback, net::ERR_DNS_MALFORMED_RESPONSE, nullptr));
223 return;
224 }
225
226 const uint64_t audit_path_nodes_received = proof->nodes.size();
227 if (audit_path_nodes_received < audit_path_length) {
228 QueryAuditProofNodes(std::move(proof), domain_for_log, leaf_index,
229 tree_size, audit_path_nodes_received, query.callback);
230 return;
231 }
232
233 base::ThreadTaskRunnerHandle::Get()->PostTask(
234 FROM_HERE,
235 base::Bind(query.callback, net::OK, base::Passed(std::move(proof))));
236 }
237
238 bool LogDnsClient::ParseTxtResponse(const net::DnsResponse& response,
239 std::string* txt) {
240 DCHECK(txt);
241
242 net::DnsRecordParser parser = response.Parser();
243 // We don't care about the creation time, since we're going to throw
244 // |parsed_record| away as soon as we've extracted the payload, so provide
245 // the "null" time.
246 auto parsed_record = net::RecordParsed::CreateFrom(&parser, base::Time());
247 if (parsed_record == nullptr)
248 return false;
249
250 auto txt_record = parsed_record->rdata<net::TxtRecordRdata>();
251 if (txt_record == nullptr)
252 return false;
253
254 *txt = base::JoinString(txt_record->texts(), "");
255 return true;
256 }
257
258 bool LogDnsClient::ParseLeafIndex(const net::DnsResponse& response,
259 uint64_t* index) {
260 std::string index_str;
261 if (!ParseTxtResponse(response, &index_str))
262 return false;
263
264 return base::StringToUint64(index_str, index);
265 }
266
267 bool LogDnsClient::ParseAuditPath(const net::DnsResponse& response,
268 net::ct::MerkleAuditProof* proof) {
269 std::string audit_path;
270 if (!ParseTxtResponse(response, &audit_path))
Eran Messeri 2016/07/01 09:07:59 Also return false if the audit_path is of length 0
Rob Percival 2016/07/01 16:01:01 Done.
271 return false;
272
273 for (size_t i = 0; i < audit_path.size(); i += crypto::kSHA256Length) {
274 std::string node = audit_path.substr(i, crypto::kSHA256Length);
275
276 if (node.size() == crypto::kSHA256Length)
Eran Messeri 2016/07/01 09:08:00 You could check beforehand: if (audit_path.size()
Rob Percival 2016/07/01 16:01:01 Done.
277 proof->nodes.push_back(node);
278 else
279 return false;
280 }
281
282 return true;
283 }
284
285 } // 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