summaryrefslogtreecommitdiff
path: root/src/ssl/t1_lib.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/ssl/t1_lib.cc')
-rw-r--r--src/ssl/t1_lib.cc200
1 files changed, 140 insertions, 60 deletions
diff --git a/src/ssl/t1_lib.cc b/src/ssl/t1_lib.cc
index 87f18889..c1c41a8a 100644
--- a/src/ssl/t1_lib.cc
+++ b/src/ssl/t1_lib.cc
@@ -199,6 +199,10 @@ static bool tls1_check_duplicate_extensions(const CBS *cbs) {
return true;
}
+static bool is_post_quantum_group(uint16_t id) {
+ return id == SSL_CURVE_CECPQ2 || id == SSL_CURVE_CECPQ2b;
+}
+
bool ssl_client_hello_init(const SSL *ssl, SSL_CLIENT_HELLO *out,
const SSLMessage &msg) {
OPENSSL_memset(out, 0, sizeof(*out));
@@ -325,10 +329,10 @@ bool tls1_get_shared_group(SSL_HANDSHAKE *hs, uint16_t *out_group_id) {
for (uint16_t pref_group : pref) {
for (uint16_t supp_group : supp) {
if (pref_group == supp_group &&
- // CECPQ2 doesn't fit in the u8-length-prefixed ECPoint field in TLS
- // 1.2 and below.
+ // CECPQ2(b) doesn't fit in the u8-length-prefixed ECPoint field in
+ // TLS 1.2 and below.
(ssl_protocol_version(ssl) >= TLS1_3_VERSION ||
- pref_group != SSL_CURVE_CECPQ2)) {
+ !is_post_quantum_group(pref_group))) {
*out_group_id = pref_group;
return true;
}
@@ -390,9 +394,9 @@ bool tls1_set_curves_list(Array<uint16_t> *out_group_ids, const char *curves) {
}
bool tls1_check_group_id(const SSL_HANDSHAKE *hs, uint16_t group_id) {
- if (group_id == SSL_CURVE_CECPQ2 &&
+ if (is_post_quantum_group(group_id) &&
ssl_protocol_version(hs->ssl) < TLS1_3_VERSION) {
- // CECPQ2 requires TLS 1.3.
+ // CECPQ2(b) requires TLS 1.3.
return false;
}
@@ -629,45 +633,7 @@ static bool ext_sni_parse_serverhello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
static bool ext_sni_parse_clienthello(SSL_HANDSHAKE *hs, uint8_t *out_alert,
CBS *contents) {
- SSL *const ssl = hs->ssl;
- if (contents == NULL) {
- return true;
- }
-
- CBS server_name_list, host_name;
- uint8_t name_type;
- if (!CBS_get_u16_length_prefixed(contents, &server_name_list) ||
- !CBS_get_u8(&server_name_list, &name_type) ||
- // Although the server_name extension was intended to be extensible to
- // new name types and multiple names, OpenSSL 1.0.x had a bug which meant
- // different name types will cause an error. Further, RFC 4366 originally
- // defined syntax inextensibly. RFC 6066 corrected this mistake, but
- // adding new name types is no longer feasible.
- //
- // Act as if the extensibility does not exist to simplify parsing.
- !CBS_get_u16_length_prefixed(&server_name_list, &host_name) ||
- CBS_len(&server_name_list) != 0 ||
- CBS_len(contents) != 0) {
- return false;
- }
-
- if (name_type != TLSEXT_NAMETYPE_host_name ||
- CBS_len(&host_name) == 0 ||
- CBS_len(&host_name) > TLSEXT_MAXLEN_host_name ||
- CBS_contains_zero_byte(&host_name)) {
- *out_alert = SSL_AD_UNRECOGNIZED_NAME;
- return false;
- }
-
- // Copy the hostname as a string.
- char *raw = nullptr;
- if (!CBS_strdup(&host_name, &raw)) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- return false;
- }
- ssl->s3->hostname.reset(raw);
-
- hs->should_ack_sni = true;
+ // SNI has already been parsed earlier in the handshake. See |extract_sni|.
return true;
}
@@ -1790,7 +1756,7 @@ static bool ext_ec_point_add_extension(SSL_HANDSHAKE *hs, CBB *out) {
}
static bool ext_ec_point_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
- // The point format extension is unneccessary in TLS 1.3.
+ // The point format extension is unnecessary in TLS 1.3.
if (hs->min_version >= TLS1_3_VERSION) {
return true;
}
@@ -2057,20 +2023,46 @@ static bool ext_psk_key_exchange_modes_parse_clienthello(SSL_HANDSHAKE *hs,
static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
SSL *const ssl = hs->ssl;
- if (!ssl->enable_early_data ||
- // Session must be 0-RTT capable.
- ssl->session == nullptr ||
- ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
- ssl->session->ticket_max_early_data == 0 ||
- // The second ClientHello never offers early data.
- hs->received_hello_retry_request ||
- // In case ALPN preferences changed since this session was established,
- // avoid reporting a confusing value in |SSL_get0_alpn_selected|.
- (!ssl->session->early_alpn.empty() &&
- !ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn))) {
+ // The second ClientHello never offers early data, and we must have already
+ // filled in |early_data_reason| by this point.
+ if (hs->received_hello_retry_request) {
+ assert(ssl->s3->early_data_reason != ssl_early_data_unknown);
+ return true;
+ }
+
+ if (!ssl->enable_early_data) {
+ ssl->s3->early_data_reason = ssl_early_data_disabled;
return true;
}
+ if (hs->max_version < TLS1_3_VERSION) {
+ // We discard inapplicable sessions, so this is redundant with the session
+ // checks below, but we check give a more useful reason.
+ ssl->s3->early_data_reason = ssl_early_data_protocol_version;
+ return true;
+ }
+
+ if (ssl->session == nullptr) {
+ ssl->s3->early_data_reason = ssl_early_data_no_session_offered;
+ return true;
+ }
+
+ if (ssl_session_protocol_version(ssl->session.get()) < TLS1_3_VERSION ||
+ ssl->session->ticket_max_early_data == 0) {
+ ssl->s3->early_data_reason = ssl_early_data_unsupported_for_session;
+ return true;
+ }
+
+ // In case ALPN preferences changed since this session was established, avoid
+ // reporting a confusing value in |SSL_get0_alpn_selected| and sending early
+ // data we know will be rejected.
+ if (!ssl->session->early_alpn.empty() &&
+ !ssl_is_alpn_protocol_allowed(hs, ssl->session->early_alpn)) {
+ ssl->s3->early_data_reason = ssl_early_data_alpn_mismatch;
+ return true;
+ }
+
+ // |early_data_reason| will be filled in later when the server responds.
hs->early_data_offered = true;
if (!CBB_add_u16(out, TLSEXT_TYPE_early_data) ||
@@ -2083,12 +2075,27 @@ static bool ext_early_data_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
}
static bool ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
- uint8_t *out_alert, CBS *contents) {
+ uint8_t *out_alert,
+ CBS *contents) {
SSL *const ssl = hs->ssl;
if (contents == NULL) {
+ if (hs->early_data_offered && !hs->received_hello_retry_request) {
+ ssl->s3->early_data_reason = ssl->s3->session_reused
+ ? ssl_early_data_peer_declined
+ : ssl_early_data_session_not_resumed;
+ } else {
+ // We already filled in |early_data_reason| when declining to offer 0-RTT
+ // or handling the implicit HelloRetryRequest reject.
+ assert(ssl->s3->early_data_reason != ssl_early_data_unknown);
+ }
return true;
}
+ // If we received an HRR, the second ClientHello never offers early data, so
+ // the extensions logic will automatically reject early data extensions as
+ // unsolicited. This covered by the ServerAcceptsEarlyDataOnHRR test.
+ assert(!hs->received_hello_retry_request);
+
if (CBS_len(contents) != 0) {
*out_alert = SSL_AD_DECODE_ERROR;
return false;
@@ -2100,6 +2107,7 @@ static bool ext_early_data_parse_serverhello(SSL_HANDSHAKE *hs,
return false;
}
+ ssl->s3->early_data_reason = ssl_early_data_accepted;
ssl->s3->early_data_accepted = true;
return true;
}
@@ -2186,8 +2194,8 @@ static bool ext_key_share_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
group_id = groups[0];
- if (group_id == SSL_CURVE_CECPQ2 && groups.size() >= 2) {
- // CECPQ2 is not sent as the only initial key share. We'll include the
+ if (is_post_quantum_group(group_id) && groups.size() >= 2) {
+ // CECPQ2(b) is not sent as the only initial key share. We'll include the
// 2nd preference group too to avoid round-trips.
second_group_id = groups[1];
assert(second_group_id != group_id);
@@ -2424,7 +2432,7 @@ static bool ext_supported_groups_add_clienthello(SSL_HANDSHAKE *hs, CBB *out) {
}
for (uint16_t group : tls1_get_grouplist(hs)) {
- if (group == SSL_CURVE_CECPQ2 &&
+ if (is_post_quantum_group(group) &&
hs->max_version < TLS1_3_VERSION) {
continue;
}
@@ -2840,6 +2848,67 @@ static bool cert_compression_add_serverhello(SSL_HANDSHAKE *hs, CBB *out) {
return true;
}
+
+// Post-quantum experiment signal
+//
+// This extension may be used in order to identify a control group for
+// experimenting with post-quantum key exchange algorithms.
+
+static bool ext_pq_experiment_signal_add_clienthello(SSL_HANDSHAKE *hs,
+ CBB *out) {
+ if (hs->ssl->ctx->pq_experiment_signal &&
+ (!CBB_add_u16(out, TLSEXT_TYPE_pq_experiment_signal) ||
+ !CBB_add_u16(out, 0))) {
+ return false;
+ }
+
+ return true;
+}
+
+static bool ext_pq_experiment_signal_parse_serverhello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ if (!hs->ssl->ctx->pq_experiment_signal || CBS_len(contents) != 0) {
+ return false;
+ }
+
+ hs->ssl->s3->pq_experiment_signal_seen = true;
+ return true;
+}
+
+static bool ext_pq_experiment_signal_parse_clienthello(SSL_HANDSHAKE *hs,
+ uint8_t *out_alert,
+ CBS *contents) {
+ if (contents == nullptr) {
+ return true;
+ }
+
+ if (CBS_len(contents) != 0) {
+ return false;
+ }
+
+ if (hs->ssl->ctx->pq_experiment_signal) {
+ hs->ssl->s3->pq_experiment_signal_seen = true;
+ }
+
+ return true;
+}
+
+static bool ext_pq_experiment_signal_add_serverhello(SSL_HANDSHAKE *hs,
+ CBB *out) {
+ if (hs->ssl->s3->pq_experiment_signal_seen &&
+ (!CBB_add_u16(out, TLSEXT_TYPE_pq_experiment_signal) ||
+ !CBB_add_u16(out, 0))) {
+ return false;
+ }
+
+ return true;
+}
+
// kExtensions contains all the supported extensions.
static const struct tls_extension kExtensions[] = {
{
@@ -3028,6 +3097,14 @@ static const struct tls_extension kExtensions[] = {
ext_delegated_credential_parse_clienthello,
dont_add_serverhello,
},
+ {
+ TLSEXT_TYPE_pq_experiment_signal,
+ NULL,
+ ext_pq_experiment_signal_add_clienthello,
+ ext_pq_experiment_signal_parse_serverhello,
+ ext_pq_experiment_signal_parse_clienthello,
+ ext_pq_experiment_signal_add_serverhello,
+ },
};
#define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -3061,6 +3138,9 @@ bool ssl_add_clienthello_tlsext(SSL_HANDSHAKE *hs, CBB *out,
return false;
}
+ // Note we may send multiple ClientHellos for DTLS HelloVerifyRequest and TLS
+ // 1.3 HelloRetryRequest. For the latter, the extensions may change, so it is
+ // important to reset this value.
hs->extensions.sent = 0;
for (size_t i = 0; i < kNumExtensions; i++) {