Add initial support for 0-RTT with QUIC.
This adapts our existing API for QUIC, although I'm not entirely
convinced the shape of it fits as it does with TCP. Things that needed
to be changed:
- There is a slight ordering issue on the server with HRR and releasing
the 0-RTT keys to QUIC.
- Remove EndOfEarlyData.
- At the early return point for the server, QUIC needs to have installed
the client traffic secrets earlier.
- The maximum early data value is a constant in QUIC.
- QUIC never installs early secrets at the TLS level. (In particular,
this avoids nuisances with do_send_second_client_hello's null cipher
not updating the encryption level.)
- The read/write secrets for 0-RTT keys were mixed up.
As the QUIC tests are getting a bit unwieldy, I tidied them up a bit.
This CL does *not* handle the QUIC transport parameters or HTTP/3
server SETTINGS frame interactions with 0-RTT. That will be done in a
separate CL.
I suspect if we ever implement DTLS 1.3, we'll find ourselves wanting to
align some of the QUIC bits here with DTLS and perhaps refine the
handshake/transport abstractions a bit.
Bug: 221
Change-Id: I61f701d7241dbc99e5dbf57ae6c283e10b85b049
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/37145
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index f1891cf..a52a49c 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -146,7 +146,10 @@
}
session->ticket_age_add_valid = true;
if (ssl->enable_early_data) {
- session->ticket_max_early_data = kMaxEarlyDataAccepted;
+ // QUIC does not use the max_early_data_size parameter and always sets it
+ // to a fixed value. See draft-ietf-quic-tls-22, section 4.5.
+ session->ticket_max_early_data =
+ ssl->quic_method != nullptr ? 0xffffffff : kMaxEarlyDataAccepted;
}
static_assert(kNumTickets < 256, "Too many tickets");
@@ -442,7 +445,7 @@
}
if (ssl->s3->early_data_accepted) {
- if (!tls13_derive_early_secrets(hs)) {
+ if (!tls13_derive_early_secret(hs)) {
return ssl_hs_error;
}
} else if (hs->early_data_offered) {
@@ -468,6 +471,15 @@
return ssl_hs_error;
}
+ // Note we defer releasing the early traffic secret to QUIC until after ECDHE
+ // is resolved. The early traffic secret should be derived before the key
+ // schedule incorporates ECDHE, but doing so may reject 0-RTT. To avoid
+ // confusing the caller, we split derivation and releasing the secret to QUIC.
+ if (ssl->s3->early_data_accepted &&
+ !tls13_set_early_secret_for_quic(hs)) {
+ return ssl_hs_error;
+ }
+
ssl->method->next_message(ssl);
hs->tls13_state = state_send_server_hello;
return ssl_hs_ok;
@@ -731,7 +743,8 @@
// Finished early. See RFC 8446, section 4.6.1.
static const uint8_t kEndOfEarlyData[4] = {SSL3_MT_END_OF_EARLY_DATA, 0,
0, 0};
- if (!hs->transcript.Update(kEndOfEarlyData)) {
+ if (ssl->quic_method == nullptr &&
+ !hs->transcript.Update(kEndOfEarlyData)) {
OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
return ssl_hs_error;
}
@@ -772,7 +785,9 @@
static enum ssl_hs_wait_t do_read_second_client_flight(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
if (ssl->s3->early_data_accepted) {
- if (!tls13_set_traffic_key(ssl, ssl_encryption_early_data, evp_aead_open,
+ // QUIC never receives handshake messages under 0-RTT keys.
+ if (ssl->quic_method == nullptr &&
+ !tls13_set_traffic_key(ssl, ssl_encryption_early_data, evp_aead_open,
hs->early_traffic_secret())) {
return ssl_hs_error;
}
@@ -780,6 +795,19 @@
hs->can_early_read = true;
hs->in_early_data = true;
}
+
+ // QUIC doesn't use an EndOfEarlyData message (draft-ietf-quic-tls-22,
+ // section 8.3), so we switch to client_handshake_secret before the early
+ // return.
+ if (ssl->quic_method != nullptr) {
+ if (!tls13_set_traffic_key(ssl, ssl_encryption_handshake, evp_aead_open,
+ hs->client_handshake_secret())) {
+ return ssl_hs_error;
+ }
+ hs->tls13_state = state_read_client_certificate;
+ return ssl->s3->early_data_accepted ? ssl_hs_early_return : ssl_hs_ok;
+ }
+
hs->tls13_state = state_process_end_of_early_data;
return ssl->s3->early_data_accepted ? ssl_hs_read_end_of_early_data
: ssl_hs_ok;