Index: net/http/http_network_transaction.cc |
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc |
index 423cb8708081ac52043a65c74520a86e0ae01eb1..b7121c5cd4ded88ad64059ed99b778517584a952 100644 |
--- a/net/http/http_network_transaction.cc |
+++ b/net/http/http_network_transaction.cc |
@@ -8,6 +8,7 @@ |
#include <utility> |
#include <vector> |
+#include "base/base64url.h" |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
#include "base/compiler_specific.h" |
@@ -62,6 +63,7 @@ |
#include "net/ssl/ssl_cert_request_info.h" |
#include "net/ssl/ssl_connection_status_flags.h" |
#include "net/ssl/ssl_private_key.h" |
+#include "net/ssl/token_binding.h" |
#include "url/gurl.h" |
#include "url/url_canon.h" |
@@ -201,6 +203,11 @@ int HttpNetworkTransaction::Start(const HttpRequestInfo* request_info, |
if (request_->privacy_mode == PRIVACY_MODE_ENABLED) |
server_ssl_config_.channel_id_enabled = false; |
+ if (session_->params().enable_token_binding && |
+ session_->params().channel_id_service) { |
+ server_ssl_config_.token_binding_params.push_back(TB_PARAM_ECDSAP256); |
+ } |
+ |
next_state_ = STATE_NOTIFY_BEFORE_CREATE_STREAM; |
int rv = DoLoop(OK); |
if (rv == ERR_IO_PENDING) |
@@ -639,6 +646,42 @@ bool HttpNetworkTransaction::IsSecureRequest() const { |
return request_->url.SchemeIsCryptographic(); |
} |
+bool HttpNetworkTransaction::IsTokenBindingEnabled() const { |
+ if (!IsSecureRequest()) |
+ return false; |
+ SSLInfo ssl_info; |
+ stream_->GetSSLInfo(&ssl_info); |
+ return ssl_info.token_binding_negotiated && |
+ ssl_info.token_binding_key_param == TB_PARAM_ECDSAP256 && |
+ session_->params().channel_id_service; |
+} |
+ |
+void HttpNetworkTransaction::RecordTokenBindingSupport() const { |
+ // This enum is used for an UMA histogram - do not change or re-use values. |
+ enum { |
+ DISABLED = 0, |
+ CLIENT_ONLY = 1, |
+ CLIENT_AND_SERVER = 2, |
+ CLIENT_NO_CHANNEL_ID_SERVICE = 3, |
+ TOKEN_BINDING_SUPPORT_MAX |
+ } supported; |
+ if (!IsSecureRequest()) |
+ return; |
+ SSLInfo ssl_info; |
+ stream_->GetSSLInfo(&ssl_info); |
+ if (!session_->params().enable_token_binding) { |
+ supported = DISABLED; |
+ } else if (!session_->params().channel_id_service) { |
+ supported = CLIENT_NO_CHANNEL_ID_SERVICE; |
+ } else if (ssl_info.token_binding_negotiated) { |
+ supported = CLIENT_AND_SERVER; |
+ } else { |
+ supported = CLIENT_ONLY; |
+ } |
+ UMA_HISTOGRAM_ENUMERATION("Net.TokenBinding.Support", supported, |
+ TOKEN_BINDING_SUPPORT_MAX); |
+} |
+ |
bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const { |
return (proxy_info_.is_http() || proxy_info_.is_https() || |
proxy_info_.is_quic()) && |
@@ -701,6 +744,13 @@ int HttpNetworkTransaction::DoLoop(int result) { |
case STATE_GENERATE_SERVER_AUTH_TOKEN_COMPLETE: |
rv = DoGenerateServerAuthTokenComplete(rv); |
break; |
+ case STATE_GET_TOKEN_BINDING_KEY: |
+ DCHECK_EQ(OK, rv); |
+ rv = DoGetTokenBindingKey(); |
+ break; |
+ case STATE_GET_TOKEN_BINDING_KEY_COMPLETE: |
+ rv = DoGetTokenBindingKeyComplete(rv); |
+ break; |
case STATE_INIT_REQUEST_BODY: |
DCHECK_EQ(OK, rv); |
rv = DoInitRequestBody(); |
@@ -916,11 +966,34 @@ int HttpNetworkTransaction::DoGenerateServerAuthToken() { |
int HttpNetworkTransaction::DoGenerateServerAuthTokenComplete(int rv) { |
DCHECK_NE(ERR_IO_PENDING, rv); |
if (rv == OK) |
- next_state_ = STATE_INIT_REQUEST_BODY; |
+ next_state_ = STATE_GET_TOKEN_BINDING_KEY; |
return rv; |
} |
-void HttpNetworkTransaction::BuildRequestHeaders( |
+int HttpNetworkTransaction::DoGetTokenBindingKey() { |
+ next_state_ = STATE_GET_TOKEN_BINDING_KEY_COMPLETE; |
+ if (!IsTokenBindingEnabled()) |
+ return OK; |
+ |
+ net_log_.BeginEvent(NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY); |
+ ChannelIDService* channel_id_service = session_->params().channel_id_service; |
+ return channel_id_service->GetOrCreateChannelID( |
+ request_->url.host(), &token_binding_key_, io_callback_, |
+ &token_binding_request_); |
+} |
+ |
+int HttpNetworkTransaction::DoGetTokenBindingKeyComplete(int rv) { |
+ DCHECK_NE(ERR_IO_PENDING, rv); |
+ next_state_ = STATE_INIT_REQUEST_BODY; |
+ if (!IsTokenBindingEnabled()) |
+ return OK; |
+ |
+ net_log_.EndEventWithNetErrorCode( |
+ NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY, rv); |
+ return rv; |
+} |
+ |
+int HttpNetworkTransaction::BuildRequestHeaders( |
bool using_http_proxy_without_tunnel) { |
request_headers_.SetHeader(HttpRequestHeaders::kHost, |
GetHostAndOptionalPort(request_->url)); |
@@ -953,6 +1026,16 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); |
} |
+ RecordTokenBindingSupport(); |
+ if (token_binding_key_) { |
+ std::string token_binding_header; |
+ int rv = BuildTokenBindingHeader(&token_binding_header); |
+ if (rv != OK) |
+ return rv; |
+ request_headers_.SetHeader(HttpRequestHeaders::kTokenBinding, |
+ token_binding_header); |
+ } |
+ |
// Honor load flags that impact proxy caches. |
if (request_->load_flags & LOAD_BYPASS_CACHE) { |
request_headers_.SetHeader(HttpRequestHeaders::kPragma, "no-cache"); |
@@ -977,6 +1060,29 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
response_.did_use_http_auth = |
request_headers_.HasHeader(HttpRequestHeaders::kAuthorization) || |
request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization); |
+ return OK; |
+} |
+ |
+int HttpNetworkTransaction::BuildTokenBindingHeader(std::string* out) { |
+ std::vector<uint8_t> signed_ekm; |
+ int rv = stream_->GetSignedEKMForTokenBinding(token_binding_key_.get(), |
+ &signed_ekm); |
+ if (rv != OK) |
+ return rv; |
+ std::string provided_token_binding; |
+ rv = BuildProvidedTokenBinding(token_binding_key_.get(), signed_ekm, |
+ &provided_token_binding); |
+ if (rv != OK) |
+ return rv; |
+ std::vector<base::StringPiece> token_bindings; |
+ token_bindings.push_back(provided_token_binding); |
+ std::string header; |
+ rv = BuildTokenBindingMessageFromTokenBindings(token_bindings, &header); |
+ if (rv != OK) |
+ return rv; |
+ base::Base64UrlEncode(header, base::Base64UrlEncodePolicy::INCLUDE_PADDING, |
+ out); |
+ return OK; |
} |
int HttpNetworkTransaction::DoInitRequestBody() { |
@@ -1001,7 +1107,7 @@ int HttpNetworkTransaction::DoBuildRequest() { |
// we have proxy info available. |
if (request_headers_.IsEmpty()) { |
bool using_http_proxy_without_tunnel = UsingHttpProxyWithoutTunnel(); |
- BuildRequestHeaders(using_http_proxy_without_tunnel); |
+ return BuildRequestHeaders(using_http_proxy_without_tunnel); |
} |
return OK; |
@@ -1486,6 +1592,7 @@ void HttpNetworkTransaction::ResetStateForAuthRestart() { |
remote_endpoint_ = IPEndPoint(); |
net_error_details_.quic_broken = false; |
net_error_details_.quic_connection_error = QUIC_NO_ERROR; |
+ token_binding_key_.reset(); |
} |
void HttpNetworkTransaction::CacheNetErrorDetailsAndResetStream() { |