Move the early_data_{offered,reason} logic out of extension callbacks.
ECH requires that we construct two ClientHellos. That means our
add_clienthello callbacks will need to be called multiple times and
should be const. (They already are called multiple times for
HelloRetryRequest, but we currently thread that through the callbacks a
bit. With ECH, I think we need to make them pure serialization.)
Bug: 275
Change-Id: I11f8195fd2ec4b8639f0a2af01a24d4974445580
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/47984
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index fbf0ef5..07da255 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -374,6 +374,54 @@
return true;
}
+// should_offer_early_data returns |ssl_early_data_accepted| if |hs| should
+// offer early data, and some other reason code otherwise.
+static ssl_early_data_reason_t should_offer_early_data(
+ const SSL_HANDSHAKE *hs) {
+ const SSL *const ssl = hs->ssl;
+ assert(!ssl->server);
+ if (!ssl->enable_early_data) {
+ return ssl_early_data_disabled;
+ }
+
+ if (hs->max_version < TLS1_3_VERSION) {
+ // We discard inapplicable sessions, so this is redundant with the session
+ // checks below, but reporting that TLS 1.3 was disabled is more useful.
+ return ssl_early_data_protocol_version;
+ }
+
+ if (ssl->session == nullptr) {
+ return ssl_early_data_no_session_offered;
+ }
+
+ if (ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
+ ssl->session->ticket_max_early_data == 0) {
+ return ssl_early_data_unsupported_for_session;
+ }
+
+ if (!ssl->session->early_alpn.empty()) {
+ if (!ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn)) {
+ // Avoid reporting a confusing value in |SSL_get0_alpn_selected|.
+ return ssl_early_data_alpn_mismatch;
+ }
+
+ // If the previous connection negotiated ALPS, only offer 0-RTT when the
+ // local are settings are consistent with what we'd offer for this
+ // connection.
+ if (ssl->session->has_application_settings) {
+ Span<const uint8_t> settings;
+ if (!ssl_get_local_application_settings(hs, &settings,
+ ssl->session->early_alpn) ||
+ settings != ssl->session->local_application_settings) {
+ return ssl_early_data_alps_mismatch;
+ }
+ }
+ }
+
+ // Early data has not yet been accepted, but we use it as a success code.
+ return ssl_early_data_accepted;
+}
+
static enum ssl_hs_wait_t do_start_connect(SSL_HANDSHAKE *hs) {
SSL *const ssl = hs->ssl;
@@ -436,6 +484,13 @@
}
}
+ ssl_early_data_reason_t reason = should_offer_early_data(hs);
+ if (reason != ssl_early_data_accepted) {
+ ssl->s3->early_data_reason = reason;
+ } else {
+ hs->early_data_offered = true;
+ }
+
if (!ssl_write_client_hello(hs)) {
return ssl_hs_error;
}