| Index: net/socket/ssl_client_socket_openssl.cc
|
| diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
|
| index f2c9cbc664cc4a856916a2b8f2d871950e526894..5489ead7e36861501d492ac80037a7361e452859 100644
|
| --- a/net/socket/ssl_client_socket_openssl.cc
|
| +++ b/net/socket/ssl_client_socket_openssl.cc
|
| @@ -17,11 +17,14 @@
|
| #include "base/bind.h"
|
| #include "base/callback_helpers.h"
|
| #include "base/environment.h"
|
| +#include "base/lazy_instance.h"
|
| #include "base/memory/singleton.h"
|
| #include "base/metrics/histogram_macros.h"
|
| #include "base/profiler/scoped_tracker.h"
|
| +#include "base/stl_util.h"
|
| #include "base/strings/string_piece.h"
|
| #include "base/synchronization/lock.h"
|
| +#include "base/threading/sequenced_worker_pool.h"
|
| #include "base/threading/thread_local.h"
|
| #include "base/values.h"
|
| #include "crypto/ec_private_key.h"
|
| @@ -42,6 +45,7 @@
|
| #include "net/ssl/ssl_connection_status_flags.h"
|
| #include "net/ssl/ssl_failure_state.h"
|
| #include "net/ssl/ssl_info.h"
|
| +#include "net/ssl/ssl_private_key.h"
|
|
|
| #if defined(OS_WIN)
|
| #include "base/win/windows_version.h"
|
| @@ -50,7 +54,7 @@
|
| #if defined(USE_OPENSSL_CERTS)
|
| #include "net/ssl/openssl_client_key_store.h"
|
| #else
|
| -#include "net/ssl/openssl_platform_key.h"
|
| +#include "net/ssl/ssl_platform_key.h"
|
| #endif
|
|
|
| namespace net {
|
| @@ -68,7 +72,7 @@ namespace {
|
|
|
| // This constant can be any non-negative/non-zero value (eg: it does not
|
| // overlap with any value of the net::Error range, including net::OK).
|
| -const int kNoPendingReadResult = 1;
|
| +const int kNoPendingResult = 1;
|
|
|
| // If a client doesn't have a list of protocols that it supports, but
|
| // the server supports NPN, choosing "http/1.1" is the best answer.
|
| @@ -145,6 +149,55 @@ int LogErrorCallback(const char* str, size_t len, void* context) {
|
| return 1;
|
| }
|
|
|
| +bool EVP_MDToPrivateKeyHash(const EVP_MD* md, SSLPrivateKey::Hash* hash) {
|
| + switch (EVP_MD_type(md)) {
|
| + case NID_md5_sha1:
|
| + *hash = SSLPrivateKey::Hash::MD5_SHA1;
|
| + return true;
|
| + case NID_sha1:
|
| + *hash = SSLPrivateKey::Hash::SHA1;
|
| + return true;
|
| + case NID_sha256:
|
| + *hash = SSLPrivateKey::Hash::SHA256;
|
| + return true;
|
| + case NID_sha384:
|
| + *hash = SSLPrivateKey::Hash::SHA384;
|
| + return true;
|
| + case NID_sha512:
|
| + *hash = SSLPrivateKey::Hash::SHA512;
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +#if !defined(USE_OPENSSL_CERTS)
|
| +class PlatformKeyTaskRunner {
|
| + public:
|
| + PlatformKeyTaskRunner() {
|
| + // Serialize all the private key operations on a single background
|
| + // thread to avoid problems with buggy smartcards.
|
| + worker_pool_ = new base::SequencedWorkerPool(1, "Platform Key Thread");
|
| + task_runner_ = worker_pool_->GetSequencedTaskRunnerWithShutdownBehavior(
|
| + worker_pool_->GetSequenceToken(),
|
| + base::SequencedWorkerPool::CONTINUE_ON_SHUTDOWN);
|
| + }
|
| +
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner() {
|
| + return task_runner_;
|
| + }
|
| +
|
| + private:
|
| + scoped_refptr<base::SequencedWorkerPool> worker_pool_;
|
| + scoped_refptr<base::SequencedTaskRunner> task_runner_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(PlatformKeyTaskRunner);
|
| +};
|
| +
|
| +base::LazyInstance<PlatformKeyTaskRunner>::Leaky g_platform_key_task_runner =
|
| + LAZY_INSTANCE_INITIALIZER;
|
| +#endif // !USE_OPENSSL_CERTS
|
| +
|
| } // namespace
|
|
|
| class SSLClientSocketOpenSSL::SSLContext {
|
| @@ -165,6 +218,8 @@ class SSLClientSocketOpenSSL::SSLContext {
|
| return SSL_set_ex_data(ssl, ssl_socket_data_index_, socket) != 0;
|
| }
|
|
|
| + static const SSL_PRIVATE_KEY_METHOD kPrivateKeyMethod;
|
| +
|
| private:
|
| friend struct DefaultSingletonTraits<SSLContext>;
|
|
|
| @@ -236,6 +291,42 @@ class SSLClientSocketOpenSSL::SSLContext {
|
| return socket->NewSessionCallback(session);
|
| }
|
|
|
| + static int PrivateKeyTypeCallback(SSL* ssl) {
|
| + SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
|
| + return socket->PrivateKeyTypeCallback();
|
| + }
|
| +
|
| + static int PrivateKeySupportsDigestCallback(SSL* ssl, const EVP_MD* md) {
|
| + SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
|
| + return socket->PrivateKeySupportsDigestCallback(md);
|
| + }
|
| +
|
| + static size_t PrivateKeyMaxSignatureLenCallback(SSL* ssl) {
|
| + SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
|
| + return socket->PrivateKeyMaxSignatureLenCallback();
|
| + }
|
| +
|
| + static ssl_private_key_result_t PrivateKeySignCallback(SSL* ssl,
|
| + uint8_t* out,
|
| + size_t* out_len,
|
| + size_t max_out,
|
| + const EVP_MD* md,
|
| + const uint8_t* in,
|
| + size_t in_len) {
|
| + SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
|
| + return socket->PrivateKeySignCallback(out, out_len, max_out, md, in,
|
| + in_len);
|
| + }
|
| +
|
| + static ssl_private_key_result_t PrivateKeySignCompleteCallback(
|
| + SSL* ssl,
|
| + uint8_t* out,
|
| + size_t* out_len,
|
| + size_t max_out) {
|
| + SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
|
| + return socket->PrivateKeySignCompleteCallback(out, out_len, max_out);
|
| + }
|
| +
|
| // This is the index used with SSL_get_ex_data to retrieve the owner
|
| // SSLClientSocketOpenSSL object from an SSL instance.
|
| int ssl_socket_data_index_;
|
| @@ -250,6 +341,15 @@ class SSLClientSocketOpenSSL::SSLContext {
|
| SSLClientSessionCacheOpenSSL session_cache_;
|
| };
|
|
|
| +const SSL_PRIVATE_KEY_METHOD
|
| + SSLClientSocketOpenSSL::SSLContext::kPrivateKeyMethod = {
|
| + &SSLClientSocketOpenSSL::SSLContext::PrivateKeyTypeCallback,
|
| + &SSLClientSocketOpenSSL::SSLContext::PrivateKeySupportsDigestCallback,
|
| + &SSLClientSocketOpenSSL::SSLContext::PrivateKeyMaxSignatureLenCallback,
|
| + &SSLClientSocketOpenSSL::SSLContext::PrivateKeySignCallback,
|
| + &SSLClientSocketOpenSSL::SSLContext::PrivateKeySignCompleteCallback,
|
| +};
|
| +
|
| // PeerCertificateChain is a helper object which extracts the certificate
|
| // chain, as given by the server, from an OpenSSL socket and performs the needed
|
| // resource management. The first element of the chain is the leaf certificate
|
| @@ -350,7 +450,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
|
| const SSLClientSocketContext& context)
|
| : transport_send_busy_(false),
|
| transport_recv_busy_(false),
|
| - pending_read_error_(kNoPendingReadResult),
|
| + pending_read_error_(kNoPendingResult),
|
| pending_read_ssl_error_(SSL_ERROR_NONE),
|
| transport_read_error_(OK),
|
| transport_write_error_(OK),
|
| @@ -372,6 +472,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
|
| session_pending_(false),
|
| certificate_verified_(false),
|
| ssl_failure_state_(SSL_FAILURE_NONE),
|
| + signature_result_(kNoPendingResult),
|
| transport_security_state_(context.transport_security_state),
|
| policy_enforcer_(context.cert_policy_enforcer),
|
| net_log_(transport_->socket()->NetLog()),
|
| @@ -493,7 +594,7 @@ void SSLClientSocketOpenSSL::Disconnect() {
|
| user_write_buf_ = NULL;
|
| user_write_buf_len_ = 0;
|
|
|
| - pending_read_error_ = kNoPendingReadResult;
|
| + pending_read_error_ = kNoPendingResult;
|
| pending_read_ssl_error_ = SSL_ERROR_NONE;
|
| pending_read_error_info_ = OpenSSLErrorInfo();
|
|
|
| @@ -516,6 +617,10 @@ void SSLClientSocketOpenSSL::Disconnect() {
|
| certificate_verified_ = false;
|
| channel_id_request_.Cancel();
|
| ssl_failure_state_ = SSL_FAILURE_NONE;
|
| +
|
| + private_key_.reset();
|
| + signature_result_ = kNoPendingResult;
|
| + signature_.clear();
|
| }
|
|
|
| bool SSLClientSocketOpenSSL::IsConnected() const {
|
| @@ -937,6 +1042,12 @@ int SSLClientSocketOpenSSL::DoHandshake() {
|
| !ssl_config_.send_client_cert) {
|
| return ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| }
|
| + if (ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
|
| + DCHECK(private_key_);
|
| + DCHECK_NE(kNoPendingResult, signature_result_);
|
| + GotoState(STATE_HANDSHAKE);
|
| + return ERR_IO_PENDING;
|
| + }
|
|
|
| OpenSSLErrorInfo error_info;
|
| net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer, &error_info);
|
| @@ -1254,32 +1365,9 @@ void SSLClientSocketOpenSSL::OnSendComplete(int result) {
|
| return;
|
| }
|
|
|
| - // OnSendComplete may need to call DoPayloadRead while the renegotiation
|
| - // handshake is in progress.
|
| - int rv_read = ERR_IO_PENDING;
|
| - int rv_write = ERR_IO_PENDING;
|
| - bool network_moved;
|
| - do {
|
| - if (user_read_buf_.get())
|
| - rv_read = DoPayloadRead();
|
| - if (user_write_buf_.get())
|
| - rv_write = DoPayloadWrite();
|
| - network_moved = DoTransportIO();
|
| - } while (rv_read == ERR_IO_PENDING && rv_write == ERR_IO_PENDING &&
|
| - (user_read_buf_.get() || user_write_buf_.get()) && network_moved);
|
| -
|
| - // Performing the Read callback may cause |this| to be deleted. If this
|
| - // happens, the Write callback should not be invoked. Guard against this by
|
| - // holding a WeakPtr to |this| and ensuring it's still valid.
|
| - base::WeakPtr<SSLClientSocketOpenSSL> guard(weak_factory_.GetWeakPtr());
|
| - if (user_read_buf_.get() && rv_read != ERR_IO_PENDING)
|
| - DoReadCallback(rv_read);
|
| -
|
| - if (!guard.get())
|
| - return;
|
| -
|
| - if (user_write_buf_.get() && rv_write != ERR_IO_PENDING)
|
| - DoWriteCallback(rv_write);
|
| + // During a renegotiation, a Read call may also be blocked on a transport
|
| + // write, so retry both operations.
|
| + PumpReadWriteEvents();
|
| }
|
|
|
| void SSLClientSocketOpenSSL::OnRecvComplete(int result) {
|
| @@ -1377,9 +1465,9 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
|
| DCHECK(user_read_buf_.get());
|
|
|
| int rv;
|
| - if (pending_read_error_ != kNoPendingReadResult) {
|
| + if (pending_read_error_ != kNoPendingResult) {
|
| rv = pending_read_error_;
|
| - pending_read_error_ = kNoPendingReadResult;
|
| + pending_read_error_ = kNoPendingResult;
|
| if (rv == 0) {
|
| net_log_.AddByteTransferEvent(NetLog::TYPE_SSL_SOCKET_BYTES_RECEIVED,
|
| rv, user_read_buf_->data());
|
| @@ -1421,6 +1509,11 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
|
| } else if (pending_read_ssl_error_ == SSL_ERROR_WANT_X509_LOOKUP &&
|
| !ssl_config_.send_client_cert) {
|
| pending_read_error_ = ERR_SSL_CLIENT_AUTH_CERT_NEEDED;
|
| + } else if (pending_read_ssl_error_ ==
|
| + SSL_ERROR_WANT_PRIVATE_KEY_OPERATION) {
|
| + DCHECK(private_key_);
|
| + DCHECK_NE(kNoPendingResult, signature_result_);
|
| + pending_read_error_ = ERR_IO_PENDING;
|
| } else {
|
| pending_read_error_ = MapOpenSSLErrorWithDetails(
|
| pending_read_ssl_error_, err_tracer, &pending_read_error_info_);
|
| @@ -1445,12 +1538,12 @@ int SSLClientSocketOpenSSL::DoPayloadRead() {
|
| // call to DoPayloadRead(), and thus it is important to check SSL_read() on
|
| // subsequent invocations to see if a complete record may now be read.
|
| if (pending_read_error_ == ERR_IO_PENDING)
|
| - pending_read_error_ = kNoPendingReadResult;
|
| + pending_read_error_ = kNoPendingResult;
|
| } else {
|
| // No bytes were returned. Return the pending read error immediately.
|
| - DCHECK_NE(kNoPendingReadResult, pending_read_error_);
|
| + DCHECK_NE(kNoPendingResult, pending_read_error_);
|
| rv = pending_read_error_;
|
| - pending_read_error_ = kNoPendingReadResult;
|
| + pending_read_error_ = kNoPendingResult;
|
| }
|
|
|
| if (rv >= 0) {
|
| @@ -1478,6 +1571,8 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() {
|
| }
|
|
|
| int ssl_error = SSL_get_error(ssl_, rv);
|
| + if (ssl_error == SSL_ERROR_WANT_PRIVATE_KEY_OPERATION)
|
| + return ERR_IO_PENDING;
|
| OpenSSLErrorInfo error_info;
|
| int net_error = MapOpenSSLErrorWithDetails(ssl_error, err_tracer,
|
| &error_info);
|
| @@ -1490,6 +1585,33 @@ int SSLClientSocketOpenSSL::DoPayloadWrite() {
|
| return net_error;
|
| }
|
|
|
| +void SSLClientSocketOpenSSL::PumpReadWriteEvents() {
|
| + int rv_read = ERR_IO_PENDING;
|
| + int rv_write = ERR_IO_PENDING;
|
| + bool network_moved;
|
| + do {
|
| + if (user_read_buf_.get())
|
| + rv_read = DoPayloadRead();
|
| + if (user_write_buf_.get())
|
| + rv_write = DoPayloadWrite();
|
| + network_moved = DoTransportIO();
|
| + } while (rv_read == ERR_IO_PENDING && rv_write == ERR_IO_PENDING &&
|
| + (user_read_buf_.get() || user_write_buf_.get()) && network_moved);
|
| +
|
| + // Performing the Read callback may cause |this| to be deleted. If this
|
| + // happens, the Write callback should not be invoked. Guard against this by
|
| + // holding a WeakPtr to |this| and ensuring it's still valid.
|
| + base::WeakPtr<SSLClientSocketOpenSSL> guard(weak_factory_.GetWeakPtr());
|
| + if (user_read_buf_.get() && rv_read != ERR_IO_PENDING)
|
| + DoReadCallback(rv_read);
|
| +
|
| + if (!guard.get())
|
| + return;
|
| +
|
| + if (user_write_buf_.get() && rv_write != ERR_IO_PENDING)
|
| + DoWriteCallback(rv_write);
|
| +}
|
| +
|
| int SSLClientSocketOpenSSL::BufferSend(void) {
|
| if (transport_send_busy_)
|
| return ERR_IO_PENDING;
|
| @@ -1673,18 +1795,18 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) {
|
| return -1;
|
| }
|
|
|
| - // TODO(davidben): With Linux client auth support, this should be
|
| - // conditioned on OS_ANDROID and then, with https://crbug.com/394131,
|
| - // removed altogether. OpenSSLClientKeyStore is mostly an artifact of the
|
| - // net/ client auth API lacking a private key handle.
|
| + if (!SSL_use_certificate(ssl_, leaf_x509.get()) ||
|
| + !SSL_set1_chain(ssl_, chain.get())) {
|
| + LOG(WARNING) << "Failed to set client certificate";
|
| + return -1;
|
| + }
|
| +
|
| #if defined(USE_OPENSSL_CERTS)
|
| + // TODO(davidben): Move Android to the SSLPrivateKey codepath and disable
|
| + // client auth on NaCl altogether.
|
| crypto::ScopedEVP_PKEY privkey =
|
| OpenSSLClientKeyStore::GetInstance()->FetchClientCertPrivateKey(
|
| ssl_config_.client_cert.get());
|
| -#else // !defined(USE_OPENSSL_CERTS)
|
| - crypto::ScopedEVP_PKEY privkey =
|
| - FetchClientCertPrivateKey(ssl_config_.client_cert.get());
|
| -#endif // defined(USE_OPENSSL_CERTS)
|
| if (!privkey) {
|
| // Could not find the private key. Fail the handshake and surface an
|
| // appropriate error to the caller.
|
| @@ -1692,13 +1814,26 @@ int SSLClientSocketOpenSSL::ClientCertRequestCallback(SSL* ssl) {
|
| OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY);
|
| return -1;
|
| }
|
| -
|
| - if (!SSL_use_certificate(ssl_, leaf_x509.get()) ||
|
| - !SSL_use_PrivateKey(ssl_, privkey.get()) ||
|
| - !SSL_set1_chain(ssl_, chain.get())) {
|
| - LOG(WARNING) << "Failed to set client certificate";
|
| + if (!SSL_use_PrivateKey(ssl_, privkey.get())) {
|
| + LOG(WARNING) << "Failed to set private key";
|
| return -1;
|
| }
|
| +#else // !USE_OPENSSL_CERTS
|
| + // TODO(davidben): Lift this call up to the embedder so we can actually test
|
| + // this code. https://crbug.com/394131
|
| + private_key_ = FetchClientCertPrivateKey(
|
| + ssl_config_.client_cert.get(),
|
| + g_platform_key_task_runner.Get().task_runner());
|
| + if (!private_key_) {
|
| + // Could not find the private key. Fail the handshake and surface an
|
| + // appropriate error to the caller.
|
| + LOG(WARNING) << "Client cert found without private key";
|
| + OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY);
|
| + return -1;
|
| + }
|
| +
|
| + SSL_set_private_key_method(ssl_, &SSLContext::kPrivateKeyMethod);
|
| +#endif // USE_OPENSSL_CERTS
|
|
|
| int cert_count = 1 + sk_X509_num(chain.get());
|
| net_log_.AddEvent(NetLog::TYPE_SSL_CLIENT_CERT_PROVIDED,
|
| @@ -1924,4 +2059,98 @@ bool SSLClientSocketOpenSSL::IsRenegotiationAllowed() const {
|
| return false;
|
| }
|
|
|
| +int SSLClientSocketOpenSSL::PrivateKeyTypeCallback() {
|
| + switch (private_key_->GetType()) {
|
| + case SSLPrivateKey::Type::RSA:
|
| + return EVP_PKEY_RSA;
|
| + case SSLPrivateKey::Type::ECDSA:
|
| + return EVP_PKEY_EC;
|
| + }
|
| + NOTREACHED();
|
| + return EVP_PKEY_NONE;
|
| +}
|
| +
|
| +int SSLClientSocketOpenSSL::PrivateKeySupportsDigestCallback(const EVP_MD* md) {
|
| + SSLPrivateKey::Hash hash;
|
| + return EVP_MDToPrivateKeyHash(md, &hash) && private_key_->SupportsHash(hash);
|
| +}
|
| +
|
| +size_t SSLClientSocketOpenSSL::PrivateKeyMaxSignatureLenCallback() {
|
| + return private_key_->GetMaxSignatureLengthInBytes();
|
| +}
|
| +
|
| +ssl_private_key_result_t SSLClientSocketOpenSSL::PrivateKeySignCallback(
|
| + uint8_t* out,
|
| + size_t* out_len,
|
| + size_t max_out,
|
| + const EVP_MD* md,
|
| + const uint8_t* in,
|
| + size_t in_len) {
|
| + DCHECK_EQ(kNoPendingResult, signature_result_);
|
| + DCHECK(signature_.empty());
|
| + DCHECK(private_key_);
|
| +
|
| + net_log_.BeginEvent(NetLog::TYPE_SSL_PRIVATE_KEY_OPERATION);
|
| +
|
| + SSLPrivateKey::Hash hash;
|
| + if (!EVP_MDToPrivateKeyHash(md, &hash)) {
|
| + OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
|
| + return ssl_private_key_failure;
|
| + }
|
| +
|
| + signature_result_ = ERR_IO_PENDING;
|
| + private_key_->SignDigest(
|
| + hash, base::StringPiece(reinterpret_cast<const char*>(in), in_len),
|
| + base::Bind(&SSLClientSocketOpenSSL::OnPrivateKeySignComplete,
|
| + weak_factory_.GetWeakPtr()));
|
| + return ssl_private_key_retry;
|
| +}
|
| +
|
| +ssl_private_key_result_t SSLClientSocketOpenSSL::PrivateKeySignCompleteCallback(
|
| + uint8_t* out,
|
| + size_t* out_len,
|
| + size_t max_out) {
|
| + DCHECK_NE(kNoPendingResult, signature_result_);
|
| + DCHECK(private_key_);
|
| +
|
| + if (signature_result_ == ERR_IO_PENDING)
|
| + return ssl_private_key_retry;
|
| + if (signature_result_ != OK) {
|
| + OpenSSLPutNetError(FROM_HERE, signature_result_);
|
| + return ssl_private_key_failure;
|
| + }
|
| + if (signature_.size() > max_out) {
|
| + OpenSSLPutNetError(FROM_HERE, ERR_SSL_CLIENT_AUTH_SIGNATURE_FAILED);
|
| + return ssl_private_key_failure;
|
| + }
|
| + memcpy(out, vector_as_array(&signature_), signature_.size());
|
| + *out_len = signature_.size();
|
| + signature_.clear();
|
| + return ssl_private_key_success;
|
| +}
|
| +
|
| +void SSLClientSocketOpenSSL::OnPrivateKeySignComplete(
|
| + Error error,
|
| + const std::vector<uint8_t>& signature) {
|
| + DCHECK_EQ(ERR_IO_PENDING, signature_result_);
|
| + DCHECK(signature_.empty());
|
| + DCHECK(private_key_);
|
| +
|
| + net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_PRIVATE_KEY_OPERATION,
|
| + error);
|
| +
|
| + signature_result_ = error;
|
| + if (signature_result_ == OK)
|
| + signature_ = signature;
|
| +
|
| + if (next_handshake_state_ == STATE_HANDSHAKE) {
|
| + OnHandshakeIOComplete(signature_result_);
|
| + return;
|
| + }
|
| +
|
| + // During a renegotiation, either Read or Write calls may be blocked on an
|
| + // asynchronous private key operation.
|
| + PumpReadWriteEvents();
|
| +}
|
| +
|
| } // namespace net
|
|
|