OLD | NEW |
| (Empty) |
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/quic/crypto/proof_verifier_chromium.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/callback_helpers.h" | |
10 #include "base/compiler_specific.h" | |
11 #include "base/logging.h" | |
12 #include "base/metrics/histogram.h" | |
13 #include "base/profiler/scoped_tracker.h" | |
14 #include "base/stl_util.h" | |
15 #include "base/strings/stringprintf.h" | |
16 #include "crypto/signature_verifier.h" | |
17 #include "net/base/net_errors.h" | |
18 #include "net/base/net_log.h" | |
19 #include "net/cert/asn1_util.h" | |
20 #include "net/cert/cert_status_flags.h" | |
21 #include "net/cert/cert_verifier.h" | |
22 #include "net/cert/cert_verify_result.h" | |
23 #include "net/cert/single_request_cert_verifier.h" | |
24 #include "net/cert/x509_certificate.h" | |
25 #include "net/cert/x509_util.h" | |
26 #include "net/http/transport_security_state.h" | |
27 #include "net/quic/crypto/crypto_protocol.h" | |
28 #include "net/ssl/ssl_config_service.h" | |
29 | |
30 using base::StringPiece; | |
31 using base::StringPrintf; | |
32 using std::string; | |
33 using std::vector; | |
34 | |
35 namespace net { | |
36 | |
37 ProofVerifyDetails* ProofVerifyDetailsChromium::Clone() const { | |
38 ProofVerifyDetailsChromium* other = new ProofVerifyDetailsChromium; | |
39 other->cert_verify_result = cert_verify_result; | |
40 return other; | |
41 } | |
42 | |
43 // A Job handles the verification of a single proof. It is owned by the | |
44 // ProofVerifier. If the verification can not complete synchronously, it | |
45 // will notify the ProofVerifier upon completion. | |
46 class ProofVerifierChromium::Job { | |
47 public: | |
48 Job(ProofVerifierChromium* proof_verifier, | |
49 CertVerifier* cert_verifier, | |
50 TransportSecurityState* transport_security_state, | |
51 const BoundNetLog& net_log); | |
52 | |
53 // Starts the proof verification. If |QUIC_PENDING| is returned, then | |
54 // |callback| will be invoked asynchronously when the verification completes. | |
55 QuicAsyncStatus VerifyProof(const std::string& hostname, | |
56 const std::string& server_config, | |
57 const std::vector<std::string>& certs, | |
58 const std::string& signature, | |
59 std::string* error_details, | |
60 scoped_ptr<ProofVerifyDetails>* verify_details, | |
61 ProofVerifierCallback* callback); | |
62 | |
63 private: | |
64 enum State { | |
65 STATE_NONE, | |
66 STATE_VERIFY_CERT, | |
67 STATE_VERIFY_CERT_COMPLETE, | |
68 }; | |
69 | |
70 int DoLoop(int last_io_result); | |
71 void OnIOComplete(int result); | |
72 int DoVerifyCert(int result); | |
73 int DoVerifyCertComplete(int result); | |
74 | |
75 bool VerifySignature(const std::string& signed_data, | |
76 const std::string& signature, | |
77 const std::string& cert); | |
78 | |
79 // Proof verifier to notify when this jobs completes. | |
80 ProofVerifierChromium* proof_verifier_; | |
81 | |
82 // The underlying verifier used for verifying certificates. | |
83 scoped_ptr<SingleRequestCertVerifier> verifier_; | |
84 | |
85 TransportSecurityState* transport_security_state_; | |
86 | |
87 // |hostname| specifies the hostname for which |certs| is a valid chain. | |
88 std::string hostname_; | |
89 | |
90 scoped_ptr<ProofVerifierCallback> callback_; | |
91 scoped_ptr<ProofVerifyDetailsChromium> verify_details_; | |
92 std::string error_details_; | |
93 | |
94 // X509Certificate from a chain of DER encoded certificates. | |
95 scoped_refptr<X509Certificate> cert_; | |
96 | |
97 State next_state_; | |
98 | |
99 BoundNetLog net_log_; | |
100 | |
101 DISALLOW_COPY_AND_ASSIGN(Job); | |
102 }; | |
103 | |
104 ProofVerifierChromium::Job::Job( | |
105 ProofVerifierChromium* proof_verifier, | |
106 CertVerifier* cert_verifier, | |
107 TransportSecurityState* transport_security_state, | |
108 const BoundNetLog& net_log) | |
109 : proof_verifier_(proof_verifier), | |
110 verifier_(new SingleRequestCertVerifier(cert_verifier)), | |
111 transport_security_state_(transport_security_state), | |
112 next_state_(STATE_NONE), | |
113 net_log_(net_log) { | |
114 } | |
115 | |
116 QuicAsyncStatus ProofVerifierChromium::Job::VerifyProof( | |
117 const string& hostname, | |
118 const string& server_config, | |
119 const vector<string>& certs, | |
120 const string& signature, | |
121 std::string* error_details, | |
122 scoped_ptr<ProofVerifyDetails>* verify_details, | |
123 ProofVerifierCallback* callback) { | |
124 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
125 tracked_objects::ScopedTracker tracking_profile( | |
126 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
127 "422516 ProofVerifierChromium::Job::VerifyProof")); | |
128 | |
129 DCHECK(error_details); | |
130 DCHECK(verify_details); | |
131 DCHECK(callback); | |
132 | |
133 error_details->clear(); | |
134 | |
135 if (STATE_NONE != next_state_) { | |
136 *error_details = "Certificate is already set and VerifyProof has begun"; | |
137 DLOG(DFATAL) << *error_details; | |
138 return QUIC_FAILURE; | |
139 } | |
140 | |
141 verify_details_.reset(new ProofVerifyDetailsChromium); | |
142 | |
143 if (certs.empty()) { | |
144 *error_details = "Failed to create certificate chain. Certs are empty."; | |
145 DLOG(WARNING) << *error_details; | |
146 verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; | |
147 *verify_details = verify_details_.Pass(); | |
148 return QUIC_FAILURE; | |
149 } | |
150 | |
151 // Convert certs to X509Certificate. | |
152 vector<StringPiece> cert_pieces(certs.size()); | |
153 for (unsigned i = 0; i < certs.size(); i++) { | |
154 cert_pieces[i] = base::StringPiece(certs[i]); | |
155 } | |
156 cert_ = X509Certificate::CreateFromDERCertChain(cert_pieces); | |
157 if (!cert_.get()) { | |
158 *error_details = "Failed to create certificate chain"; | |
159 DLOG(WARNING) << *error_details; | |
160 verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; | |
161 *verify_details = verify_details_.Pass(); | |
162 return QUIC_FAILURE; | |
163 } | |
164 | |
165 // We call VerifySignature first to avoid copying of server_config and | |
166 // signature. | |
167 if (!VerifySignature(server_config, signature, certs[0])) { | |
168 *error_details = "Failed to verify signature of server config"; | |
169 DLOG(WARNING) << *error_details; | |
170 verify_details_->cert_verify_result.cert_status = CERT_STATUS_INVALID; | |
171 *verify_details = verify_details_.Pass(); | |
172 return QUIC_FAILURE; | |
173 } | |
174 | |
175 hostname_ = hostname; | |
176 | |
177 next_state_ = STATE_VERIFY_CERT; | |
178 switch (DoLoop(OK)) { | |
179 case OK: | |
180 *verify_details = verify_details_.Pass(); | |
181 return QUIC_SUCCESS; | |
182 case ERR_IO_PENDING: | |
183 callback_.reset(callback); | |
184 return QUIC_PENDING; | |
185 default: | |
186 *error_details = error_details_; | |
187 *verify_details = verify_details_.Pass(); | |
188 return QUIC_FAILURE; | |
189 } | |
190 } | |
191 | |
192 int ProofVerifierChromium::Job::DoLoop(int last_result) { | |
193 int rv = last_result; | |
194 do { | |
195 State state = next_state_; | |
196 next_state_ = STATE_NONE; | |
197 switch (state) { | |
198 case STATE_VERIFY_CERT: | |
199 DCHECK(rv == OK); | |
200 rv = DoVerifyCert(rv); | |
201 break; | |
202 case STATE_VERIFY_CERT_COMPLETE: | |
203 rv = DoVerifyCertComplete(rv); | |
204 break; | |
205 case STATE_NONE: | |
206 default: | |
207 rv = ERR_UNEXPECTED; | |
208 LOG(DFATAL) << "unexpected state " << state; | |
209 break; | |
210 } | |
211 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); | |
212 return rv; | |
213 } | |
214 | |
215 void ProofVerifierChromium::Job::OnIOComplete(int result) { | |
216 int rv = DoLoop(result); | |
217 if (rv != ERR_IO_PENDING) { | |
218 scoped_ptr<ProofVerifierCallback> callback(callback_.Pass()); | |
219 // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium. | |
220 scoped_ptr<ProofVerifyDetails> verify_details(verify_details_.Pass()); | |
221 callback->Run(rv == OK, error_details_, &verify_details); | |
222 // Will delete |this|. | |
223 proof_verifier_->OnJobComplete(this); | |
224 } | |
225 } | |
226 | |
227 int ProofVerifierChromium::Job::DoVerifyCert(int result) { | |
228 next_state_ = STATE_VERIFY_CERT_COMPLETE; | |
229 | |
230 int flags = 0; | |
231 return verifier_->Verify( | |
232 cert_.get(), | |
233 hostname_, | |
234 flags, | |
235 SSLConfigService::GetCRLSet().get(), | |
236 &verify_details_->cert_verify_result, | |
237 base::Bind(&ProofVerifierChromium::Job::OnIOComplete, | |
238 base::Unretained(this)), | |
239 net_log_); | |
240 } | |
241 | |
242 int ProofVerifierChromium::Job::DoVerifyCertComplete(int result) { | |
243 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
244 tracked_objects::ScopedTracker tracking_profile( | |
245 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
246 "422516 ProofVerifierChromium::Job::DoVerifyCertComplete")); | |
247 | |
248 verifier_.reset(); | |
249 | |
250 const CertVerifyResult& cert_verify_result = | |
251 verify_details_->cert_verify_result; | |
252 const CertStatus cert_status = cert_verify_result.cert_status; | |
253 if (transport_security_state_ && | |
254 (result == OK || | |
255 (IsCertificateError(result) && IsCertStatusMinorError(cert_status))) && | |
256 !transport_security_state_->CheckPublicKeyPins( | |
257 hostname_, | |
258 cert_verify_result.is_issued_by_known_root, | |
259 cert_verify_result.public_key_hashes, | |
260 &verify_details_->pinning_failure_log)) { | |
261 result = ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN; | |
262 } | |
263 | |
264 scoped_refptr<ct::EVCertsWhitelist> ev_whitelist = | |
265 SSLConfigService::GetEVCertsWhitelist(); | |
266 if ((cert_status & CERT_STATUS_IS_EV) && ev_whitelist.get() && | |
267 ev_whitelist->IsValid()) { | |
268 const SHA256HashValue fingerprint( | |
269 X509Certificate::CalculateFingerprint256(cert_->os_cert_handle())); | |
270 | |
271 UMA_HISTOGRAM_BOOLEAN( | |
272 "Net.SSL_EVCertificateInWhitelist", | |
273 ev_whitelist->ContainsCertificateHash( | |
274 std::string(reinterpret_cast<const char*>(fingerprint.data), 8))); | |
275 } | |
276 | |
277 if (result != OK) { | |
278 std::string error_string = ErrorToString(result); | |
279 error_details_ = StringPrintf("Failed to verify certificate chain: %s", | |
280 error_string.c_str()); | |
281 DLOG(WARNING) << error_details_; | |
282 } | |
283 | |
284 // Exit DoLoop and return the result to the caller to VerifyProof. | |
285 DCHECK_EQ(STATE_NONE, next_state_); | |
286 return result; | |
287 } | |
288 | |
289 bool ProofVerifierChromium::Job::VerifySignature(const string& signed_data, | |
290 const string& signature, | |
291 const string& cert) { | |
292 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
293 tracked_objects::ScopedTracker tracking_profile( | |
294 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
295 "422516 ProofVerifierChromium::Job::VerifySignature")); | |
296 | |
297 StringPiece spki; | |
298 if (!asn1::ExtractSPKIFromDERCert(cert, &spki)) { | |
299 DLOG(WARNING) << "ExtractSPKIFromDERCert failed"; | |
300 return false; | |
301 } | |
302 | |
303 crypto::SignatureVerifier verifier; | |
304 | |
305 size_t size_bits; | |
306 X509Certificate::PublicKeyType type; | |
307 X509Certificate::GetPublicKeyInfo(cert_->os_cert_handle(), &size_bits, | |
308 &type); | |
309 if (type == X509Certificate::kPublicKeyTypeRSA) { | |
310 crypto::SignatureVerifier::HashAlgorithm hash_alg = | |
311 crypto::SignatureVerifier::SHA256; | |
312 crypto::SignatureVerifier::HashAlgorithm mask_hash_alg = hash_alg; | |
313 unsigned int hash_len = 32; // 32 is the length of a SHA-256 hash. | |
314 | |
315 bool ok = verifier.VerifyInitRSAPSS( | |
316 hash_alg, mask_hash_alg, hash_len, | |
317 reinterpret_cast<const uint8*>(signature.data()), signature.size(), | |
318 reinterpret_cast<const uint8*>(spki.data()), spki.size()); | |
319 if (!ok) { | |
320 DLOG(WARNING) << "VerifyInitRSAPSS failed"; | |
321 return false; | |
322 } | |
323 } else if (type == X509Certificate::kPublicKeyTypeECDSA) { | |
324 // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT. | |
325 // RFC 5758: | |
326 // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2) | |
327 // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 } | |
328 // ... | |
329 // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or | |
330 // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field | |
331 // as an AlgorithmIdentifier, the encoding MUST omit the parameters | |
332 // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one | |
333 // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with- | |
334 // SHA384, or ecdsa-with-SHA512. | |
335 // See also RFC 5480, Appendix A. | |
336 static const uint8 kECDSAWithSHA256AlgorithmID[] = { | |
337 0x30, 0x0a, | |
338 0x06, 0x08, | |
339 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02, | |
340 }; | |
341 | |
342 if (!verifier.VerifyInit( | |
343 kECDSAWithSHA256AlgorithmID, sizeof(kECDSAWithSHA256AlgorithmID), | |
344 reinterpret_cast<const uint8*>(signature.data()), | |
345 signature.size(), | |
346 reinterpret_cast<const uint8*>(spki.data()), | |
347 spki.size())) { | |
348 DLOG(WARNING) << "VerifyInit failed"; | |
349 return false; | |
350 } | |
351 } else { | |
352 LOG(ERROR) << "Unsupported public key type " << type; | |
353 return false; | |
354 } | |
355 | |
356 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(kProofSignatureLabel), | |
357 sizeof(kProofSignatureLabel)); | |
358 verifier.VerifyUpdate(reinterpret_cast<const uint8*>(signed_data.data()), | |
359 signed_data.size()); | |
360 | |
361 if (!verifier.VerifyFinal()) { | |
362 DLOG(WARNING) << "VerifyFinal failed"; | |
363 return false; | |
364 } | |
365 | |
366 DVLOG(1) << "VerifyFinal success"; | |
367 return true; | |
368 } | |
369 | |
370 ProofVerifierChromium::ProofVerifierChromium( | |
371 CertVerifier* cert_verifier, | |
372 TransportSecurityState* transport_security_state) | |
373 : cert_verifier_(cert_verifier), | |
374 transport_security_state_(transport_security_state) { | |
375 } | |
376 | |
377 ProofVerifierChromium::~ProofVerifierChromium() { | |
378 STLDeleteElements(&active_jobs_); | |
379 } | |
380 | |
381 QuicAsyncStatus ProofVerifierChromium::VerifyProof( | |
382 const std::string& hostname, | |
383 const std::string& server_config, | |
384 const std::vector<std::string>& certs, | |
385 const std::string& signature, | |
386 const ProofVerifyContext* verify_context, | |
387 std::string* error_details, | |
388 scoped_ptr<ProofVerifyDetails>* verify_details, | |
389 ProofVerifierCallback* callback) { | |
390 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed. | |
391 tracked_objects::ScopedTracker tracking_profile( | |
392 FROM_HERE_WITH_EXPLICIT_FUNCTION( | |
393 "422516 ProofVerifierChromium::VerifyProof")); | |
394 | |
395 if (!verify_context) { | |
396 *error_details = "Missing context"; | |
397 return QUIC_FAILURE; | |
398 } | |
399 const ProofVerifyContextChromium* chromium_context = | |
400 reinterpret_cast<const ProofVerifyContextChromium*>(verify_context); | |
401 scoped_ptr<Job> job(new Job(this, | |
402 cert_verifier_, | |
403 transport_security_state_, | |
404 chromium_context->net_log)); | |
405 QuicAsyncStatus status = job->VerifyProof(hostname, server_config, certs, | |
406 signature, error_details, | |
407 verify_details, callback); | |
408 if (status == QUIC_PENDING) { | |
409 active_jobs_.insert(job.release()); | |
410 } | |
411 return status; | |
412 } | |
413 | |
414 void ProofVerifierChromium::OnJobComplete(Job* job) { | |
415 active_jobs_.erase(job); | |
416 delete job; | |
417 } | |
418 | |
419 } // namespace net | |
OLD | NEW |