Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1133)

Unified Diff: net/socket/ssl_client_socket_openssl.cc

Issue 1360633002: Implement Token Binding negotiation TLS extension (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@test-server-flags
Patch Set: rebase Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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 c3af8b84d83e62340efb535f64a5e2d93da2ac86..bcf23eaf9c63c1411a4d982b13035e1bcc484b67 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -9,7 +9,9 @@
#include <errno.h>
#include <openssl/bio.h>
+#include <openssl/bytestring.h>
#include <openssl/err.h>
+#include <openssl/evp.h>
#include <openssl/mem.h>
#include <openssl/ssl.h>
#include <string.h>
@@ -255,6 +257,10 @@ class SSLClientSocketOpenSSL::SSLContext {
SSL_CTX_set_keylog_bio(ssl_ctx_.get(), bio);
}
}
+
+ if (!TokenBindingExtension::RegisterCallbacks(ssl_ctx_.get())) {
+ NOTREACHED();
+ }
}
static int ClientCertRequestCallback(SSL* ssl, void* arg) {
@@ -541,6 +547,11 @@ int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
return rv;
}
+ // Set params for TokenBindingExtension object.
+ if (IsTokenBindingEnabled(ssl_config_, channel_id_service_)) {
+ token_binding_extension_.SetParams(ssl_config_.token_binding_params);
+ }
+
// Set SSL to client mode. Handshake happens in the loop below.
SSL_set_connect_state(ssl_);
@@ -704,6 +715,7 @@ bool SSLClientSocketOpenSSL::GetSSLInfo(SSLInfo* ssl_info) {
ssl_info->client_cert_sent =
ssl_config_.send_client_cert && ssl_config_.client_cert.get();
ssl_info->channel_id_sent = channel_id_sent_;
+ ssl_info->token_binding_negotiated = token_binding_extension_.WasNegotiated();
ssl_info->pinning_failure_log = pinning_failure_log_;
AddSCTInfoToSSLInfo(ssl_info);
@@ -1102,6 +1114,18 @@ int SSLClientSocketOpenSSL::DoHandshakeComplete(int result) {
return ERR_SSL_FALLBACK_BEYOND_MINIMUM_VERSION;
}
+ // Check that if token binding was negotiated, then extended master secret
+ // must also be negotiated.
+ if (token_binding_extension_.WasNegotiated() &&
+ !ssl_->session->extended_master_secret) {
+ return ERR_SSL_PROTOCOL_ERROR;
+ }
+
+ if (token_binding_extension_.WasNegotiated() && !channel_id_key_) {
+ GotoState(STATE_TOKEN_BINDING_LOOKUP);
+ return OK;
+ }
+
// SSL handshake is completed. If NPN wasn't negotiated, see if ALPN was.
if (npn_status_ == kNextProtoUnsupported) {
const uint8_t* alpn_proto = NULL;
@@ -1118,6 +1142,8 @@ int SSLClientSocketOpenSSL::DoHandshakeComplete(int result) {
RecordChannelIDSupport(channel_id_service_, channel_id_sent_,
ssl_config_.channel_id_enabled,
crypto::ECPrivateKey::IsSupported());
+ RecordTokenBindingSupport(ssl_config_, channel_id_service_,
+ token_binding_extension_.WasNegotiated());
// Only record OCSP histograms if OCSP was requested.
if (ssl_config_.signed_cert_timestamps_enabled ||
@@ -1180,6 +1206,31 @@ int SSLClientSocketOpenSSL::DoChannelIDLookupComplete(int result) {
return OK;
}
+int SSLClientSocketOpenSSL::DoTokenBindingLookup() {
+ net_log_.BeginEvent(NetLog::TYPE_SSL_GET_TOKEN_BINDING_KEY);
+ GotoState(STATE_TOKEN_BINDING_LOOKUP_COMPLETE);
+ return channel_id_service_->GetOrCreateChannelID(
+ host_and_port_.host(), &channel_id_key_,
+ base::Bind(&SSLClientSocketOpenSSL::OnHandshakeIOComplete,
+ base::Unretained(this)),
+ &channel_id_request_);
davidben 2015/10/01 16:15:17 What does this do? Do we even use this key for any
nharper 2015/10/01 19:12:23 We use this key once at the SSL layer to generate
+}
+
+int SSLClientSocketOpenSSL::DoTokenBindingLookupComplete(int result) {
+ if (!channel_id_key_) {
+ LOG(ERROR) << "Failed to import Channel ID.";
+ result = ERR_CHANNEL_ID_IMPORT_FAILED;
+ }
+
+ net_log_.EndEventWithNetErrorCode(NetLog::TYPE_SSL_GET_TOKEN_BINDING_KEY,
+ result);
+ if (result < 0)
+ return result;
+
+ GotoState(STATE_HANDSHAKE_COMPLETE);
+ return OK;
+}
+
int SSLClientSocketOpenSSL::DoVerifyCert(int result) {
DCHECK(!server_cert_chain_->empty());
DCHECK(start_cert_verification_time_.is_null());
@@ -1410,6 +1461,13 @@ int SSLClientSocketOpenSSL::DoHandshakeLoop(int last_io_result) {
case STATE_CHANNEL_ID_LOOKUP_COMPLETE:
rv = DoChannelIDLookupComplete(rv);
break;
+ case STATE_TOKEN_BINDING_LOOKUP:
+ DCHECK_EQ(OK, rv);
+ rv = DoTokenBindingLookup();
+ break;
+ case STATE_TOKEN_BINDING_LOOKUP_COMPLETE:
+ rv = DoTokenBindingLookupComplete(rv);
+ break;
case STATE_VERIFY_CERT:
DCHECK_EQ(OK, rv);
rv = DoVerifyCert(rv);
@@ -2161,4 +2219,164 @@ void SSLClientSocketOpenSSL::OnPrivateKeySignComplete(
PumpReadWriteEvents();
}
+SSLClientSocketOpenSSL::TokenBindingExtension::TokenBindingExtension()
+ : negotiated_(false), negotiated_param_(TokenBindingParam(0)) {}
+
+SSLClientSocketOpenSSL::TokenBindingExtension::~TokenBindingExtension() {}
+
+bool SSLClientSocketOpenSSL::TokenBindingExtension::WasNegotiated() const {
+ return negotiated_;
+}
+
+TokenBindingParam
+SSLClientSocketOpenSSL::TokenBindingExtension::NegotiationResult() const {
+ return negotiated_param_;
+};
+
+void SSLClientSocketOpenSSL::TokenBindingExtension::SetParams(
+ const std::vector<TokenBindingParam>& params) {
+ supported_params_.clear();
+ for (TokenBindingParam param : params) {
+ supported_params_.push_back(param);
+ }
+};
+
+// static
+bool SSLClientSocketOpenSSL::TokenBindingExtension::RegisterCallbacks(
+ SSL_CTX* ssl_ctx) {
+ return SSL_CTX_add_client_custom_ext(
+ ssl_ctx, kExtNum, &TokenBindingExtension::ClientAddCallback,
+ &TokenBindingExtension::ClientFreeCallback, nullptr,
+ &TokenBindingExtension::ClientParseCallback, nullptr);
+}
+
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientAdd(
+ const uint8_t** out,
+ size_t* out_len,
+ int* out_alert_value) {
+ if (supported_params_.empty()) {
+ return 0;
+ }
+ CBB output, parameters_list;
+ int retval = -1;
+ if (!CBB_init(&output, 7))
+ goto end;
+ if (!CBB_add_u8(&output, kProtocolVersionMajor) ||
+ !CBB_add_u8(&output, kProtocolVersionMinor) ||
+ !CBB_add_u8_length_prefixed(&output, &parameters_list)) {
+ goto end;
+ }
+ for (size_t i = 0; i < supported_params_.size(); ++i) {
+ if (!CBB_add_u8(&parameters_list, supported_params_[i])) {
+ goto end;
+ }
+ }
+ if (!CBB_finish(&output, const_cast<uint8_t**>(out), out_len))
+ goto end;
+
+ retval = 1;
+
+end:
+ CBB_cleanup(&output);
+ if (retval == -1)
+ *out_alert_value = SSL_AD_INTERNAL_ERROR;
+ return retval;
+}
+
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientParse(
+ const uint8_t* contents,
+ size_t contents_len,
+ int* out_alert_value) {
+ CBS extension;
+ CBS_init(&extension, contents, contents_len);
+
+ CBS parameters_list;
+ uint8_t version_major, version_minor, param;
+ if (!CBS_get_u8(&extension, &version_major) ||
+ !CBS_get_u8(&extension, &version_minor) ||
+ version_major != kProtocolVersionMajor ||
+ version_minor != kProtocolVersionMinor ||
+ !CBS_get_u8_length_prefixed(&extension, &parameters_list) ||
+ !CBS_get_u8(&parameters_list, &param) || CBS_len(&extension) > 0) {
+ *out_alert_value = SSL_AD_DECODE_ERROR;
+ return 0;
+ }
+ bool valid_param = false;
+ for (size_t i = 0; i < supported_params_.size(); ++i) {
+ if (param == supported_params_[i]) {
+ negotiated_param_ = TokenBindingParam(param);
+ valid_param = true;
+ break;
+ }
+ }
+
+ // The server-negotiated version must be less than or equal to our version.
+ if (version_major > kProtocolVersionMajor ||
+ (version_minor > kProtocolVersionMinor &&
+ version_major == kProtocolVersionMajor)) {
+ *out_alert_value = SSL_AD_ILLEGAL_PARAMETER;
+ return 0;
+ }
+
+ // Although the server returns the negotiated key param in a list, it must
+ // only send one element in that list.
+ if (CBS_len(&parameters_list) > 0 || !valid_param) {
+ *out_alert_value = SSL_AD_ILLEGAL_PARAMETER;
+ return 0;
+ }
+
+ // If the version the server negotiated is older than we support, don't fail
+ // parsing the extension, but also don't set |negotiated_|.
+ if (version_major < kMinProtocolVersionMajor ||
+ (version_minor < kMinProtocolVersionMinor &&
+ version_major == kMinProtocolVersionMajor)) {
+ return 1;
+ }
+
+ negotiated_ = true;
+ return 1;
+}
+
+// static
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientAddCallback(
+ SSL* ssl,
+ unsigned int extension_value,
+ const uint8_t** out,
+ size_t* out_len,
+ int* out_alert_value,
+ void* add_arg) {
+ DCHECK(extension_value == kExtNum);
+ SSLClientSocketOpenSSL* socket =
+ SSLClientSocketOpenSSL::SSLContext::GetInstance()->GetClientSocketFromSSL(
+ ssl);
+ return socket->token_binding_extension_.ClientAdd(out, out_len,
+ out_alert_value);
+}
+
+// static
+void SSLClientSocketOpenSSL::TokenBindingExtension::ClientFreeCallback(
+ SSL* ssl,
+ unsigned extension_value,
+ const uint8_t* out,
+ void* add_arg) {
+ DCHECK(extension_value == kExtNum);
+ OPENSSL_free(const_cast<unsigned char*>(out));
+}
+
+// static
+int SSLClientSocketOpenSSL::TokenBindingExtension::ClientParseCallback(
+ SSL* ssl,
+ unsigned int extension_value,
+ const uint8_t* contents,
+ size_t contents_len,
+ int* out_alert_value,
+ void* parse_arg) {
+ DCHECK(extension_value == kExtNum);
+ SSLClientSocketOpenSSL* socket =
+ SSLClientSocketOpenSSL::SSLContext::GetInstance()->GetClientSocketFromSSL(
+ ssl);
+ return socket->token_binding_extension_.ClientParse(contents, contents_len,
+ out_alert_value);
+}
+
} // namespace net

Powered by Google App Engine
This is Rietveld 408576698