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.
5 #include "net/quic/crypto/proof_verifier_chromium.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_macros.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/host_port_pair.h"
18 #include "net/base/net_errors.h"
19 #include "net/cert/asn1_util.h"
20 #include "net/cert/cert_policy_enforcer.h"
21 #include "net/cert/cert_status_flags.h"
22 #include "net/cert/cert_verifier.h"
23 #include "net/cert/cert_verify_result.h"
24 #include "net/cert/ct_verify_result.h"
25 #include "net/cert/x509_certificate.h"
26 #include "net/cert/x509_util.h"
27 #include "net/http/transport_security_state.h"
28 #include "net/log/net_log.h"
29 #include "net/quic/crypto/crypto_protocol.h"
30 #include "net/ssl/ssl_config_service.h"
32 using base::StringPiece
;
33 using base::StringPrintf
;
39 ProofVerifyDetails
* ProofVerifyDetailsChromium::Clone() const {
40 ProofVerifyDetailsChromium
* other
= new ProofVerifyDetailsChromium
;
41 other
->cert_verify_result
= cert_verify_result
;
45 // A Job handles the verification of a single proof. It is owned by the
46 // ProofVerifier. If the verification can not complete synchronously, it
47 // will notify the ProofVerifier upon completion.
48 class ProofVerifierChromium::Job
{
50 Job(ProofVerifierChromium
* proof_verifier
,
51 CertVerifier
* cert_verifier
,
52 CertPolicyEnforcer
* cert_policy_enforcer
,
53 TransportSecurityState
* transport_security_state
,
54 int cert_verify_flags
,
55 const BoundNetLog
& net_log
);
57 // Starts the proof verification. If |QUIC_PENDING| is returned, then
58 // |callback| will be invoked asynchronously when the verification completes.
59 QuicAsyncStatus
VerifyProof(const std::string
& hostname
,
60 const std::string
& server_config
,
61 const std::vector
<std::string
>& certs
,
62 const std::string
& signature
,
63 std::string
* error_details
,
64 scoped_ptr
<ProofVerifyDetails
>* verify_details
,
65 ProofVerifierCallback
* callback
);
71 STATE_VERIFY_CERT_COMPLETE
,
74 int DoLoop(int last_io_result
);
75 void OnIOComplete(int result
);
76 int DoVerifyCert(int result
);
77 int DoVerifyCertComplete(int result
);
79 bool VerifySignature(const std::string
& signed_data
,
80 const std::string
& signature
,
81 const std::string
& cert
);
83 // Proof verifier to notify when this jobs completes.
84 ProofVerifierChromium
* proof_verifier_
;
86 // The underlying verifier used for verifying certificates.
87 CertVerifier
* verifier_
;
88 scoped_ptr
<CertVerifier::Request
> cert_verifier_request_
;
90 CertPolicyEnforcer
* policy_enforcer_
;
92 TransportSecurityState
* transport_security_state_
;
94 // |hostname| specifies the hostname for which |certs| is a valid chain.
95 std::string hostname_
;
97 scoped_ptr
<ProofVerifierCallback
> callback_
;
98 scoped_ptr
<ProofVerifyDetailsChromium
> verify_details_
;
99 std::string error_details_
;
101 // X509Certificate from a chain of DER encoded certificates.
102 scoped_refptr
<X509Certificate
> cert_
;
104 // |cert_verify_flags| is bitwise OR'd of CertVerifier::VerifyFlags and it is
105 // passed to CertVerifier::Verify.
106 int cert_verify_flags_
;
110 BoundNetLog net_log_
;
112 DISALLOW_COPY_AND_ASSIGN(Job
);
115 ProofVerifierChromium::Job::Job(
116 ProofVerifierChromium
* proof_verifier
,
117 CertVerifier
* cert_verifier
,
118 CertPolicyEnforcer
* cert_policy_enforcer
,
119 TransportSecurityState
* transport_security_state
,
120 int cert_verify_flags
,
121 const BoundNetLog
& net_log
)
122 : proof_verifier_(proof_verifier
),
123 verifier_(cert_verifier
),
124 policy_enforcer_(cert_policy_enforcer
),
125 transport_security_state_(transport_security_state
),
126 cert_verify_flags_(cert_verify_flags
),
127 next_state_(STATE_NONE
),
130 QuicAsyncStatus
ProofVerifierChromium::Job::VerifyProof(
131 const string
& hostname
,
132 const string
& server_config
,
133 const vector
<string
>& certs
,
134 const string
& signature
,
135 std::string
* error_details
,
136 scoped_ptr
<ProofVerifyDetails
>* verify_details
,
137 ProofVerifierCallback
* callback
) {
138 DCHECK(error_details
);
139 DCHECK(verify_details
);
142 error_details
->clear();
144 if (STATE_NONE
!= next_state_
) {
145 *error_details
= "Certificate is already set and VerifyProof has begun";
146 DLOG(DFATAL
) << *error_details
;
150 verify_details_
.reset(new ProofVerifyDetailsChromium
);
153 *error_details
= "Failed to create certificate chain. Certs are empty.";
154 DLOG(WARNING
) << *error_details
;
155 verify_details_
->cert_verify_result
.cert_status
= CERT_STATUS_INVALID
;
156 *verify_details
= verify_details_
.Pass();
160 // Convert certs to X509Certificate.
161 vector
<StringPiece
> cert_pieces(certs
.size());
162 for (unsigned i
= 0; i
< certs
.size(); i
++) {
163 cert_pieces
[i
] = base::StringPiece(certs
[i
]);
165 cert_
= X509Certificate::CreateFromDERCertChain(cert_pieces
);
167 *error_details
= "Failed to create certificate chain";
168 DLOG(WARNING
) << *error_details
;
169 verify_details_
->cert_verify_result
.cert_status
= CERT_STATUS_INVALID
;
170 *verify_details
= verify_details_
.Pass();
174 // We call VerifySignature first to avoid copying of server_config and
176 if (!VerifySignature(server_config
, signature
, certs
[0])) {
177 *error_details
= "Failed to verify signature of server config";
178 DLOG(WARNING
) << *error_details
;
179 verify_details_
->cert_verify_result
.cert_status
= CERT_STATUS_INVALID
;
180 *verify_details
= verify_details_
.Pass();
184 hostname_
= hostname
;
186 next_state_
= STATE_VERIFY_CERT
;
187 switch (DoLoop(OK
)) {
189 *verify_details
= verify_details_
.Pass();
192 callback_
.reset(callback
);
195 *error_details
= error_details_
;
196 *verify_details
= verify_details_
.Pass();
201 int ProofVerifierChromium::Job::DoLoop(int last_result
) {
202 int rv
= last_result
;
204 State state
= next_state_
;
205 next_state_
= STATE_NONE
;
207 case STATE_VERIFY_CERT
:
209 rv
= DoVerifyCert(rv
);
211 case STATE_VERIFY_CERT_COMPLETE
:
212 rv
= DoVerifyCertComplete(rv
);
217 LOG(DFATAL
) << "unexpected state " << state
;
220 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
224 void ProofVerifierChromium::Job::OnIOComplete(int result
) {
225 int rv
= DoLoop(result
);
226 if (rv
!= ERR_IO_PENDING
) {
227 scoped_ptr
<ProofVerifierCallback
> callback(callback_
.Pass());
228 // Callback expects ProofVerifyDetails not ProofVerifyDetailsChromium.
229 scoped_ptr
<ProofVerifyDetails
> verify_details(verify_details_
.Pass());
230 callback
->Run(rv
== OK
, error_details_
, &verify_details
);
231 // Will delete |this|.
232 proof_verifier_
->OnJobComplete(this);
236 int ProofVerifierChromium::Job::DoVerifyCert(int result
) {
237 next_state_
= STATE_VERIFY_CERT_COMPLETE
;
239 return verifier_
->Verify(
240 cert_
.get(), hostname_
, std::string(), cert_verify_flags_
,
241 SSLConfigService::GetCRLSet().get(), &verify_details_
->cert_verify_result
,
242 base::Bind(&ProofVerifierChromium::Job::OnIOComplete
,
243 base::Unretained(this)),
244 &cert_verifier_request_
, net_log_
);
247 int ProofVerifierChromium::Job::DoVerifyCertComplete(int result
) {
248 cert_verifier_request_
.reset();
250 const CertVerifyResult
& cert_verify_result
=
251 verify_details_
->cert_verify_result
;
252 const CertStatus cert_status
= cert_verify_result
.cert_status
;
253 if (result
== OK
&& policy_enforcer_
&&
254 (cert_verify_result
.cert_status
& CERT_STATUS_IS_EV
)) {
255 // QUIC does not support OCSP stapling or the CT TLS extension; as a
256 // result, CT can never be verified, thus the result is always empty.
257 ct::CTVerifyResult empty_ct_result
;
258 if (!policy_enforcer_
->DoesConformToCTEVPolicy(
259 cert_verify_result
.verified_cert
.get(),
260 SSLConfigService::GetEVCertsWhitelist().get(), empty_ct_result
,
262 verify_details_
->cert_verify_result
.cert_status
|=
263 CERT_STATUS_CT_COMPLIANCE_FAILED
;
264 verify_details_
->cert_verify_result
.cert_status
&= ~CERT_STATUS_IS_EV
;
268 // TODO(estark): replace 0 below with the port of the connection.
269 if (transport_security_state_
&&
271 (IsCertificateError(result
) && IsCertStatusMinorError(cert_status
))) &&
272 !transport_security_state_
->CheckPublicKeyPins(
273 HostPortPair(hostname_
, 0),
274 cert_verify_result
.is_issued_by_known_root
,
275 cert_verify_result
.public_key_hashes
, cert_
.get(),
276 cert_verify_result
.verified_cert
.get(),
277 TransportSecurityState::ENABLE_PIN_REPORTS
,
278 &verify_details_
->pinning_failure_log
)) {
279 result
= ERR_SSL_PINNED_KEY_NOT_IN_CERT_CHAIN
;
283 std::string error_string
= ErrorToString(result
);
284 error_details_
= StringPrintf("Failed to verify certificate chain: %s",
285 error_string
.c_str());
286 DLOG(WARNING
) << error_details_
;
289 // Exit DoLoop and return the result to the caller to VerifyProof.
290 DCHECK_EQ(STATE_NONE
, next_state_
);
294 bool ProofVerifierChromium::Job::VerifySignature(const string
& signed_data
,
295 const string
& signature
,
296 const string
& cert
) {
297 // TODO(rtenneti): Remove ScopedTracker below once crbug.com/422516 is fixed.
298 tracked_objects::ScopedTracker
tracking_profile(
299 FROM_HERE_WITH_EXPLICIT_FUNCTION(
300 "422516 ProofVerifierChromium::Job::VerifySignature"));
303 if (!asn1::ExtractSPKIFromDERCert(cert
, &spki
)) {
304 DLOG(WARNING
) << "ExtractSPKIFromDERCert failed";
308 crypto::SignatureVerifier verifier
;
311 X509Certificate::PublicKeyType type
;
312 X509Certificate::GetPublicKeyInfo(cert_
->os_cert_handle(), &size_bits
,
314 if (type
== X509Certificate::kPublicKeyTypeRSA
) {
315 crypto::SignatureVerifier::HashAlgorithm hash_alg
=
316 crypto::SignatureVerifier::SHA256
;
317 crypto::SignatureVerifier::HashAlgorithm mask_hash_alg
= hash_alg
;
318 unsigned int hash_len
= 32; // 32 is the length of a SHA-256 hash.
320 bool ok
= verifier
.VerifyInitRSAPSS(
321 hash_alg
, mask_hash_alg
, hash_len
,
322 reinterpret_cast<const uint8
*>(signature
.data()), signature
.size(),
323 reinterpret_cast<const uint8
*>(spki
.data()), spki
.size());
325 DLOG(WARNING
) << "VerifyInitRSAPSS failed";
328 } else if (type
== X509Certificate::kPublicKeyTypeECDSA
) {
329 // This is the algorithm ID for ECDSA with SHA-256. Parameters are ABSENT.
331 // ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
332 // us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
334 // When the ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-SHA384, or
335 // ecdsa-with-SHA512 algorithm identifier appears in the algorithm field
336 // as an AlgorithmIdentifier, the encoding MUST omit the parameters
337 // field. That is, the AlgorithmIdentifier SHALL be a SEQUENCE of one
338 // component, the OID ecdsa-with-SHA224, ecdsa-with-SHA256, ecdsa-with-
339 // SHA384, or ecdsa-with-SHA512.
340 // See also RFC 5480, Appendix A.
341 static const uint8 kECDSAWithSHA256AlgorithmID
[] = {
344 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x04, 0x03, 0x02,
347 if (!verifier
.VerifyInit(
348 kECDSAWithSHA256AlgorithmID
, sizeof(kECDSAWithSHA256AlgorithmID
),
349 reinterpret_cast<const uint8
*>(signature
.data()),
351 reinterpret_cast<const uint8
*>(spki
.data()),
353 DLOG(WARNING
) << "VerifyInit failed";
357 LOG(ERROR
) << "Unsupported public key type " << type
;
361 verifier
.VerifyUpdate(reinterpret_cast<const uint8
*>(kProofSignatureLabel
),
362 sizeof(kProofSignatureLabel
));
363 verifier
.VerifyUpdate(reinterpret_cast<const uint8
*>(signed_data
.data()),
366 if (!verifier
.VerifyFinal()) {
367 DLOG(WARNING
) << "VerifyFinal failed";
371 DVLOG(1) << "VerifyFinal success";
375 ProofVerifierChromium::ProofVerifierChromium(
376 CertVerifier
* cert_verifier
,
377 CertPolicyEnforcer
* cert_policy_enforcer
,
378 TransportSecurityState
* transport_security_state
)
379 : cert_verifier_(cert_verifier
),
380 cert_policy_enforcer_(cert_policy_enforcer
),
381 transport_security_state_(transport_security_state
) {}
383 ProofVerifierChromium::~ProofVerifierChromium() {
384 STLDeleteElements(&active_jobs_
);
387 QuicAsyncStatus
ProofVerifierChromium::VerifyProof(
388 const std::string
& hostname
,
389 const std::string
& server_config
,
390 const std::vector
<std::string
>& certs
,
391 const std::string
& signature
,
392 const ProofVerifyContext
* verify_context
,
393 std::string
* error_details
,
394 scoped_ptr
<ProofVerifyDetails
>* verify_details
,
395 ProofVerifierCallback
* callback
) {
396 if (!verify_context
) {
397 *error_details
= "Missing context";
400 const ProofVerifyContextChromium
* chromium_context
=
401 reinterpret_cast<const ProofVerifyContextChromium
*>(verify_context
);
402 scoped_ptr
<Job
> job(new Job(
403 this, cert_verifier_
, cert_policy_enforcer_
, transport_security_state_
,
404 chromium_context
->cert_verify_flags
, chromium_context
->net_log
));
405 QuicAsyncStatus status
=
406 job
->VerifyProof(hostname
, server_config
, certs
, signature
, error_details
,
407 verify_details
, callback
);
408 if (status
== QUIC_PENDING
) {
409 active_jobs_
.insert(job
.release());
414 void ProofVerifierChromium::OnJobComplete(Job
* job
) {
415 active_jobs_
.erase(job
);