Add SSL_get_early_data_reason.
This is to help servers diagnose 0-RTT rejects. (QUIC has a similar
feature, and this will help determine if we need to adjust the ticket
age skew.)
Bug: 113
Change-Id: Icc7e5df326b5fa82e744605021b1205298efba6a
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35885
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/tls13_server.cc b/ssl/tls13_server.cc
index 13a89c0..82c9689 100644
--- a/ssl/tls13_server.cc
+++ b/ssl/tls13_server.cc
@@ -307,16 +307,15 @@
static enum ssl_ticket_aead_result_t select_session(
SSL_HANDSHAKE *hs, uint8_t *out_alert, UniquePtr<SSL_SESSION> *out_session,
- int32_t *out_ticket_age_skew, const SSLMessage &msg,
- const SSL_CLIENT_HELLO *client_hello) {
+ int32_t *out_ticket_age_skew, bool *out_offered_ticket,
+ const SSLMessage &msg, const SSL_CLIENT_HELLO *client_hello) {
SSL *const ssl = hs->ssl;
- *out_session = NULL;
+ *out_session = nullptr;
- // Decode the ticket if we agreed on a PSK key exchange mode.
CBS pre_shared_key;
- if (!hs->accept_psk_mode ||
- !ssl_client_hello_get_extension(client_hello, &pre_shared_key,
- TLSEXT_TYPE_pre_shared_key)) {
+ *out_offered_ticket = ssl_client_hello_get_extension(
+ client_hello, &pre_shared_key, TLSEXT_TYPE_pre_shared_key);
+ if (!*out_offered_ticket) {
return ssl_ticket_aead_ignore_ticket;
}
@@ -337,6 +336,11 @@
return ssl_ticket_aead_error;
}
+ // If the peer did not offer psk_dhe, ignore the resumption.
+ if (!hs->accept_psk_mode) {
+ return ssl_ticket_aead_ignore_ticket;
+ }
+
// TLS 1.3 session tickets are renewed separately as part of the
// NewSessionTicket.
bool unused_renew;
@@ -406,10 +410,18 @@
uint8_t alert = SSL_AD_DECODE_ERROR;
UniquePtr<SSL_SESSION> session;
- switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew, msg,
- &client_hello)) {
+ bool offered_ticket = false;
+ switch (select_session(hs, &alert, &session, &ssl->s3->ticket_age_skew,
+ &offered_ticket, msg, &client_hello)) {
case ssl_ticket_aead_ignore_ticket:
assert(!session);
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
+ } else if (!offered_ticket) {
+ ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
+ } else {
+ ssl->s3->early_data_reason = ssl_early_data_session_not_resumed;
+ }
if (!ssl_get_new_session(hs, 1 /* server */)) {
ssl_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_INTERNAL_ERROR);
return ssl_hs_error;
@@ -422,17 +434,23 @@
hs->new_session =
SSL_SESSION_dup(session.get(), SSL_SESSION_DUP_AUTH_ONLY);
- if (ssl->enable_early_data &&
- // Early data must be acceptable for this ticket.
- session->ticket_max_early_data != 0 &&
- // The client must have offered early data.
- hs->early_data_offered &&
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
+ } else if (session->ticket_max_early_data == 0) {
+ ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
+ } else if (!hs->early_data_offered) {
+ ssl->s3->early_data_reason = ssl_early_data_peer_declined;
+ } else if (ssl->s3->channel_id_valid) {
// Channel ID is incompatible with 0-RTT.
- !ssl->s3->channel_id_valid &&
- // If Token Binding is negotiated, reject 0-RTT.
- !ssl->s3->token_binding_negotiated &&
- // The negotiated ALPN must match the one in the ticket.
- MakeConstSpan(ssl->s3->alpn_selected) == session->early_alpn) {
+ ssl->s3->early_data_reason = ssl_early_data_channel_id;
+ } else if (ssl->s3->token_binding_negotiated) {
+ // Token Binding is incompatible with 0-RTT.
+ ssl->s3->early_data_reason = ssl_early_data_token_binding;
+ } else if (MakeConstSpan(ssl->s3->alpn_selected) != session->early_alpn) {
+ // The negotiated ALPN must match the one in the ticket.
+ ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
+ } else {
+ ssl->s3->early_data_reason = ssl_early_data_accepted;
ssl->s3->early_data_accepted = true;
}
@@ -499,7 +517,10 @@
bool need_retry;
if (!resolve_ecdhe_secret(hs, &need_retry, &client_hello)) {
if (need_retry) {
- ssl->s3->early_data_accepted = false;
+ if (ssl->s3->early_data_accepted) {
+ ssl->s3->early_data_reason = ssl_early_data_hello_retry_request;
+ ssl->s3->early_data_accepted = false;
+ }
ssl->s3->skip_early_data = true;
ssl->method->next_message(ssl);
if (!hs->transcript.UpdateForHelloRetryRequest()) {