Hand back ECDHE split handshakes after the first server message.
This changes the contract for split handshakes such that on the
receiving side, the connection is to be driven until it returns
|SSL_ERROR_HANDBACK|, rather than until SSL_do_handshake() returns
success.
Change-Id: Idd1ebfbd943d88474d7c934f4c0ae757ff3c0f37
Reviewed-on: https://boringssl-review.googlesource.com/26864
Commit-Queue: Matt Braithwaite <mab@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index b19d443..dd73c83 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -93,17 +93,17 @@
if (CBS_len(&transcript) != 0) {
s3->hs->transcript.Update(transcript);
s3->is_v2_hello = true;
- ssl_do_msg_callback(ssl, 0 /* read */, 0 /* V2ClientHello */, transcript);
}
+ ssl->handback = true;
return true;
}
bool SSL_serialize_handback(const SSL *ssl, CBB *out) {
if (!ssl->server ||
- !ssl->s3->initial_handshake_complete ||
- ssl->method->is_dtls ||
- ssl->version < TLS1_VERSION) {
+ (ssl->s3->hs->state != state12_finish_server_handshake &&
+ ssl->s3->hs->state != state12_read_client_certificate) ||
+ ssl->method->is_dtls || ssl->version < TLS1_VERSION) {
return false;
}
@@ -115,14 +115,22 @@
size_t iv_len = 0;
const uint8_t *read_iv = nullptr, *write_iv = nullptr;
- if (ssl->version == TLS1_VERSION &&
- SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
- (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) ||
- !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) {
- return false;
+ Span<const uint8_t> transcript;
+ if (ssl->s3->hs->state == state12_finish_server_handshake) {
+ if (ssl->version == TLS1_VERSION &&
+ SSL_CIPHER_is_block_cipher(s3->aead_read_ctx->cipher()) &&
+ (!s3->aead_read_ctx->GetIV(&read_iv, &iv_len) ||
+ !s3->aead_write_ctx->GetIV(&write_iv, &iv_len))) {
+ return false;
+ }
+ } else {
+ transcript = s3->hs->transcript.buffer();
}
- CBB seq;
+ // TODO(mab): make sure everything is serialized.
+ CBB seq, key_share;
+ SSL_SESSION *session =
+ s3->session_reused ? ssl->session : s3->hs->new_session.get();
if (!CBB_add_asn1(out, &seq, CBS_ASN1_SEQUENCE) ||
!CBB_add_asn1_uint64(&seq, kHandbackVersion) ||
!CBB_add_asn1_uint64(&seq, ssl->version) ||
@@ -142,7 +150,7 @@
!CBB_add_asn1_bool(&seq, s3->session_reused) ||
!CBB_add_asn1_bool(&seq, s3->send_connection_binding) ||
!CBB_add_asn1_bool(&seq, s3->tlsext_channel_id_valid) ||
- !ssl_session_serialize(s3->established_session.get(), &seq) ||
+ !ssl_session_serialize(session, &seq) ||
!CBB_add_asn1_octet_string(&seq, s3->next_proto_negotiated.data(),
s3->next_proto_negotiated.size()) ||
!CBB_add_asn1_octet_string(&seq, s3->alpn_selected.data(),
@@ -158,11 +166,22 @@
!CBB_add_asn1_bool(&seq, ssl->quiet_shutdown) ||
!CBB_add_asn1_bool(&seq, ssl->tlsext_channel_id_enabled) ||
!CBB_add_asn1_bool(&seq, ssl->retain_only_sha256_of_client_certs) ||
- !CBB_flush(out)) {
+ !CBB_add_asn1_bool(&seq, ssl->token_binding_negotiated) ||
+ !CBB_add_asn1_uint64(&seq, ssl->negotiated_token_binding_param) ||
+ !CBB_add_asn1_bool(&seq, s3->hs->next_proto_neg_seen) ||
+ !CBB_add_asn1_bool(&seq, s3->hs->cert_request) ||
+ !CBB_add_asn1_bool(&seq, s3->hs->extended_master_secret) ||
+ !CBB_add_asn1_bool(&seq, s3->hs->ticket_expected) ||
+ !CBB_add_asn1_uint64(&seq, SSL_CIPHER_get_id(s3->hs->new_cipher)) ||
+ !CBB_add_asn1_octet_string(&seq, transcript.data(), transcript.size()) ||
+ !CBB_add_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) {
return false;
}
-
- return true;
+ if (ssl->s3->hs->state == state12_read_client_certificate &&
+ !s3->hs->key_share->Serialize(&key_share)) {
+ return false;
+ }
+ return CBB_flush(out);
}
bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback) {
@@ -173,11 +192,16 @@
SSL3_STATE *const s3 = ssl->s3;
uint64_t handback_version, version, conf_max_version, conf_min_version,
- max_send_fragment, options, mode, max_cert_list;
+ max_send_fragment, options, mode, max_cert_list,
+ negotiated_token_binding_param, cipher;
+
CBS seq, read_seq, write_seq, server_rand, client_rand, read_iv, write_iv,
- next_proto, alpn, hostname, channel_id;
- int session_reused, send_connection_binding, channel_id_valid,
- quiet_shutdown, channel_id_enabled, retain_only_sha256;
+ next_proto, alpn, hostname, channel_id, transcript, key_share;
+ int session_reused, send_connection_binding, channel_id_valid, quiet_shutdown,
+ channel_id_enabled, retain_only_sha256, cert_request,
+ extended_master_secret, ticket_expected, token_binding_negotiated,
+ next_proto_neg_seen;
+ SSL_SESSION *session = nullptr;
CBS handback_cbs(handback);
if (!CBS_get_asn1(&handback_cbs, &seq, CBS_ASN1_SEQUENCE) ||
@@ -210,11 +234,19 @@
return false;
}
- s3->established_session =
- SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
+ s3->hs = ssl_handshake_new(ssl);
+ if (session_reused) {
+ ssl->session =
+ SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool)
+ .release();
+ session = ssl->session;
+ } else {
+ s3->hs->new_session =
+ SSL_SESSION_parse(&seq, ssl->ctx->x509_method, ssl->ctx->pool);
+ session = s3->hs->new_session.get();
+ }
- if (!s3->established_session ||
- !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) ||
+ if (!session || !CBS_get_asn1(&seq, &next_proto, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&seq, &alpn, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&seq, &hostname, CBS_ASN1_OCTETSTRING) ||
!CBS_get_asn1(&seq, &channel_id, CBS_ASN1_OCTETSTRING) ||
@@ -226,7 +258,22 @@
!CBS_get_asn1_uint64(&seq, &max_cert_list) ||
!CBS_get_asn1_bool(&seq, &quiet_shutdown) ||
!CBS_get_asn1_bool(&seq, &channel_id_enabled) ||
- !CBS_get_asn1_bool(&seq, &retain_only_sha256)) {
+ !CBS_get_asn1_bool(&seq, &retain_only_sha256) ||
+ !CBS_get_asn1_bool(&seq, &token_binding_negotiated) ||
+ !CBS_get_asn1_uint64(&seq, &negotiated_token_binding_param) ||
+ !CBS_get_asn1_bool(&seq, &next_proto_neg_seen) ||
+ !CBS_get_asn1_bool(&seq, &cert_request) ||
+ !CBS_get_asn1_bool(&seq, &extended_master_secret) ||
+ !CBS_get_asn1_bool(&seq, &ticket_expected) ||
+ !CBS_get_asn1_uint64(&seq, &cipher)) {
+ return false;
+ }
+ if ((s3->hs->new_cipher =
+ SSL_get_cipher_by_value(static_cast<uint16_t>(cipher))) == nullptr) {
+ return false;
+ }
+ if (!CBS_get_asn1(&seq, &transcript, CBS_ASN1_OCTETSTRING) ||
+ !CBS_get_asn1(&seq, &key_share, CBS_ASN1_SEQUENCE)) {
return false;
}
@@ -240,9 +287,9 @@
ssl->mode = mode;
ssl->max_cert_list = max_cert_list;
- s3->hs.reset();
s3->have_version = true;
- s3->initial_handshake_complete = true;
+ s3->hs->state = CBS_len(&transcript) == 0 ? state12_finish_server_handshake
+ : state12_read_client_certificate;
s3->session_reused = session_reused;
s3->send_connection_binding = send_connection_binding;
s3->tlsext_channel_id_valid = channel_id_valid;
@@ -263,23 +310,44 @@
ssl->quiet_shutdown = quiet_shutdown;
ssl->tlsext_channel_id_enabled = channel_id_enabled;
ssl->retain_only_sha256_of_client_certs = retain_only_sha256;
+ ssl->token_binding_negotiated = token_binding_negotiated;
+ ssl->negotiated_token_binding_param =
+ static_cast<uint8_t>(negotiated_token_binding_param);
+ s3->hs->next_proto_neg_seen = next_proto_neg_seen;
+ s3->hs->wait = ssl_hs_flush;
+ s3->hs->extended_master_secret = extended_master_secret;
+ s3->hs->ticket_expected = ticket_expected;
+ s3->aead_write_ctx->SetVersionIfNullCipher(ssl->version);
+ s3->hs->cert_request = cert_request;
- Array<uint8_t> key_block;
- if (!tls1_configure_aead(ssl, evp_aead_open, &key_block,
- s3->established_session->cipher, read_iv) ||
- !tls1_configure_aead(ssl, evp_aead_seal, &key_block,
- s3->established_session->cipher, write_iv)) {
- return false;
+ if (s3->hs->state == state12_finish_server_handshake) {
+ Array<uint8_t> key_block;
+ if (!tls1_configure_aead(ssl, evp_aead_open, &key_block, session->cipher,
+ read_iv) ||
+ !tls1_configure_aead(ssl, evp_aead_seal, &key_block, session->cipher,
+ write_iv)) {
+ return false;
+ }
+
+ if (!CBS_copy_bytes(&read_seq, s3->read_sequence,
+ sizeof(s3->read_sequence)) ||
+ !CBS_copy_bytes(&write_seq, s3->write_sequence,
+ sizeof(s3->write_sequence))) {
+ return false;
+ }
+ } else {
+ if (!s3->hs->transcript.Init() ||
+ !s3->hs->transcript.InitHash(ssl_protocol_version(ssl),
+ s3->hs->new_cipher) ||
+ !s3->hs->transcript.Update(transcript)) {
+ return false;
+ }
+ if ((s3->hs->key_share = SSLKeyShare::Create(&key_share)) == nullptr) {
+ return false;
+ }
}
- if (!CBS_copy_bytes(&read_seq, s3->read_sequence,
- sizeof(s3->read_sequence)) ||
- !CBS_copy_bytes(&write_seq, s3->write_sequence,
- sizeof(s3->write_sequence))) {
- return false;
- }
-
- return true;
+ return CBS_len(&seq) == 0;
}
} // namespace bssl