diff options
Diffstat (limited to 'src/ssl/t1_lib.cc')
-rw-r--r-- | src/ssl/t1_lib.cc | 200 |
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++) { |