Index: net/http/http_network_transaction.cc |
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc |
index 8a8e7421e4bbd5d3e8f5390f3fd5f601c176b45d..90098eda064a823f948a8802079a1c765e4cd14e 100644 |
--- a/net/http/http_network_transaction.cc |
+++ b/net/http/http_network_transaction.cc |
@@ -7,6 +7,7 @@ |
#include <set> |
#include <vector> |
+#include "base/base64.h" |
#include "base/bind.h" |
#include "base/bind_helpers.h" |
#include "base/compiler_specific.h" |
@@ -60,6 +61,7 @@ |
#include "net/spdy/spdy_session_pool.h" |
#include "net/ssl/ssl_cert_request_info.h" |
#include "net/ssl/ssl_connection_status_flags.h" |
+#include "net/ssl/token_binding.h" |
#include "url/gurl.h" |
#include "url/url_canon.h" |
@@ -198,6 +200,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) |
@@ -614,6 +621,48 @@ bool HttpNetworkTransaction::IsSecureRequest() const { |
return request_->url.SchemeIsCryptographic(); |
} |
+bool HttpNetworkTransaction::IsTokenBindingEnabled() const { |
+ if (!IsSecureRequest()) |
+ return false; |
+ SSLInfo ssl_info; |
+ stream_->GetSSLInfo(&ssl_info); |
+ if (!ssl_info.token_binding_negotiated) |
+ return false; |
+ if (ssl_info.token_binding_key_param != TB_PARAM_ECDSAP256) |
+ return false; |
+ if (!session_->params().channel_id_service) |
+ return false; |
+ return true; |
+} |
+ |
+void HttpNetworkTransaction::RecordTokenBindingSupport() const { |
+ enum { |
+ DISABLED = 0, |
+ CLIENT_ONLY = 1, |
+ CLIENT_AND_SERVER = 2, |
+ CLIENT_NO_CHANNEL_ID_SERVICE = 3, |
+ UNSUPPORTED_KEY_PARAM = 4, |
+ 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_key_param != TB_PARAM_ECDSAP256) { |
+ supported = UNSUPPORTED_KEY_PARAM; |
davidben
2015/11/18 20:49:00
This can never happen, no? You'll only negotiate t
nharper
2015/12/04 01:42:19
My thought here was if the server can negotiate to
|
+ } else if (ssl_info.token_binding_negotiated) { |
+ supported = CLIENT_AND_SERVER; |
+ } else { |
+ supported = CLIENT_ONLY; |
+ } |
+ UMA_HISTOGRAM_ENUMERATION("TokenBinding.Support", supported, |
+ TOKEN_BINDING_SUPPORT_MAX); |
+} |
+ |
bool HttpNetworkTransaction::UsingHttpProxyWithoutTunnel() const { |
return (proxy_info_.is_http() || proxy_info_.is_https() || |
proxy_info_.is_quic()) && |
@@ -676,6 +725,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(); |
@@ -891,7 +947,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; |
+} |
+ |
+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_, |
+ base::Bind(&HttpNetworkTransaction::OnIOComplete, base::Unretained(this)), |
davidben
2015/11/18 20:49:00
io_callback_
nharper
2015/12/04 01:42:19
Done.
|
+ &token_binding_request_); |
+} |
+ |
+int HttpNetworkTransaction::DoGetTokenBindingKeyComplete(int rv) { |
+ DCHECK_NE(ERR_IO_PENDING, rv); |
+ next_state_ = STATE_INIT_REQUEST_BODY; |
+ if (!IsTokenBindingEnabled()) |
+ return OK; |
+ |
+ if (rv == OK && !token_binding_key_) |
+ rv = ERR_CHANNEL_ID_IMPORT_FAILED; |
davidben
2015/11/18 20:49:00
Is this possible?
nharper
2015/12/04 01:42:19
I can't see how this would be possible (GetOrCreat
|
+ |
+ net_log_.EndEventWithNetErrorCode( |
+ NetLog::TYPE_HTTP_TRANSACTION_GET_TOKEN_BINDING_KEY, rv); |
return rv; |
} |
@@ -928,6 +1011,15 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
request_headers_.SetHeader(HttpRequestHeaders::kContentLength, "0"); |
} |
+ RecordTokenBindingSupport(); |
+ if (token_binding_key_) { |
davidben
2015/11/18 20:49:00
An HttpNetworkSession may run through a request mu
nharper
2015/12/04 01:42:19
Done.
|
+ std::string token_binding_header = BuildTokenBindingHeader(); |
+ if (token_binding_header != "") { |
+ 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"); |
@@ -954,6 +1046,25 @@ void HttpNetworkTransaction::BuildRequestHeaders( |
request_headers_.HasHeader(HttpRequestHeaders::kProxyAuthorization); |
} |
+std::string HttpNetworkTransaction::BuildTokenBindingHeader() { |
+ std::string provided_token_binding; |
+ int rv = stream_->GetProvidedTokenBindingWithKey(token_binding_key_, |
+ &provided_token_binding); |
+ if (rv != OK) |
+ return ""; |
davidben
2015/11/18 20:49:00
If this or below fails, it should probably be fata
nharper
2015/12/04 01:42:19
Done.
|
+ std::vector<std::string> token_bindings; |
+ token_bindings.push_back(provided_token_binding); |
+ std::string header; |
+ rv = BuildTokenBindingMessageFromTokenBindings(token_bindings, &header); |
+ if (rv != OK) |
+ return ""; |
+ std::string base64_header; |
+ base::Base64Encode(header, &base64_header); |
+ base::ReplaceChars(base64_header, "+", "-", &base64_header); |
+ base::ReplaceChars(base64_header, "/", "_", &base64_header); |
davidben
2015/11/18 20:49:00
base/base64url.h
nharper
2015/12/04 01:42:19
Done.
|
+ return base64_header; |
+} |
+ |
int HttpNetworkTransaction::DoInitRequestBody() { |
next_state_ = STATE_INIT_REQUEST_BODY_COMPLETE; |
int rv = OK; |