Add Data-less Zero-RTT support.
This adds support on the server and client to accept data-less early
data. The server will still fail to parse early data with any
contents, so this should remain disabled.
BUG=76
Change-Id: Id85d192d8e0360b8de4b6971511b5e8a0e8012f7
Reviewed-on: https://boringssl-review.googlesource.com/12921
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/ssl/tls13_server.c b/ssl/tls13_server.c
index 9c8d1a1..4854002 100644
--- a/ssl/tls13_server.c
+++ b/ssl/tls13_server.c
@@ -43,6 +43,8 @@
state_send_server_certificate_verify,
state_complete_server_certificate_verify,
state_send_server_finished,
+ state_read_second_client_flight,
+ state_process_end_of_early_data,
state_process_client_certificate,
state_process_client_certificate_verify,
state_process_channel_id,
@@ -289,6 +291,19 @@
/* Carry over authentication information from the previous handshake into
* a fresh session. */
hs->new_session = SSL_SESSION_dup(session, SSL_SESSION_DUP_AUTH_ONLY);
+
+ if (/* Early data must be acceptable for this ticket. */
+ ssl->ctx->enable_early_data &&
+ session->ticket_max_early_data != 0 &&
+ /* The client must have offered early data. */
+ hs->early_data_offered &&
+ /* The negotiated ALPN must match the one in the ticket. */
+ ssl->s3->alpn_selected_len == session->early_alpn_len &&
+ OPENSSL_memcmp(ssl->s3->alpn_selected, session->early_alpn,
+ ssl->s3->alpn_selected_len) == 0) {
+ ssl->early_data_accepted = 1;
+ }
+
SSL_SESSION_free(session);
if (hs->new_session == NULL) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
@@ -323,6 +338,7 @@
}
}
+ /* Store the initial negotiated ALPN in the session. */
if (ssl->s3->alpn_selected != NULL) {
hs->new_session->early_alpn =
BUF_memdup(ssl->s3->alpn_selected, ssl->s3->alpn_selected_len);
@@ -351,12 +367,22 @@
return ssl_hs_error;
}
+ if (ssl->early_data_accepted) {
+ if (!tls13_derive_early_secrets(hs)) {
+ return ssl_hs_error;
+ }
+ } else if (hs->early_data_offered) {
+ ssl->s3->skip_early_data = 1;
+ }
+
ssl->method->received_flight(ssl);
/* Resolve ECDHE and incorporate it into the secret. */
int need_retry;
if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
if (need_retry) {
+ ssl->early_data_accepted = 0;
+ ssl->s3->skip_early_data = 1;
hs->tls13_state = state_send_hello_retry_request;
return ssl_hs_ok;
}
@@ -440,8 +466,6 @@
/* Derive and enable the handshake traffic secrets. */
if (!tls13_derive_handshake_secrets(hs) ||
- !tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
- hs->hash_len) ||
!tls13_set_traffic_key(ssl, evp_aead_seal, hs->server_handshake_secret,
hs->hash_len)) {
goto err;
@@ -543,8 +567,33 @@
return ssl_hs_error;
}
+ hs->tls13_state = state_read_second_client_flight;
+ return ssl_hs_flush;
+}
+
+static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) {
+ SSL *const ssl = hs->ssl;
+ if (ssl->early_data_accepted) {
+ if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->early_traffic_secret,
+ hs->hash_len)) {
+ return ssl_hs_error;
+ }
+ hs->tls13_state = state_process_end_of_early_data;
+ return ssl_hs_read_end_of_early_data;
+ }
+
+ hs->tls13_state = state_process_end_of_early_data;
+ return ssl_hs_ok;
+}
+
+static enum ssl_hs_wait_t do_process_end_of_early_data(SSL_HANDSHAKE *hs) {
+ SSL *const ssl = hs->ssl;
+ if (!tls13_set_traffic_key(ssl, evp_aead_open, hs->client_handshake_secret,
+ hs->hash_len)) {
+ return ssl_hs_error;
+ }
hs->tls13_state = state_process_client_certificate;
- return ssl_hs_flush_and_read_message;
+ return ssl_hs_read_message;
}
static enum ssl_hs_wait_t do_process_client_certificate(SSL_HANDSHAKE *hs) {
@@ -720,10 +769,16 @@
break;
case state_complete_server_certificate_verify:
ret = do_send_server_certificate_verify(hs, 0 /* complete */);
- break;
+ break;
case state_send_server_finished:
ret = do_send_server_finished(hs);
break;
+ case state_read_second_client_flight:
+ ret = do_read_second_client_flight(hs);
+ break;
+ case state_process_end_of_early_data:
+ ret = do_process_end_of_early_data(hs);
+ break;
case state_process_client_certificate:
ret = do_process_client_certificate(hs);
break;