in tls/s2n_client_hello.c [213:354]
static S2N_RESULT s2n_client_hello_verify_for_retry(struct s2n_connection *conn,
struct s2n_client_hello *old_ch, struct s2n_client_hello *new_ch,
uint8_t *previous_client_random)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(old_ch);
RESULT_ENSURE_REF(new_ch);
RESULT_ENSURE_REF(previous_client_random);
if (!s2n_is_hello_retry_handshake(conn)) {
return S2N_RESULT_OK;
}
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2
*# The client will also send a
*# ClientHello when the server has responded to its ClientHello with a
*# HelloRetryRequest. In that case, the client MUST send the same
*# ClientHello without modification, except as follows:
*
* All of the exceptions that follow are extensions.
*/
RESULT_ENSURE(old_ch->legacy_version == new_ch->legacy_version, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(old_ch->compression_methods.size == new_ch->compression_methods.size, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(s2n_constant_time_equals(old_ch->compression_methods.data, new_ch->compression_methods.data,
new_ch->compression_methods.size),
S2N_ERR_BAD_MESSAGE);
/* Some clients are not compliant with TLS 1.3 RFC, and send mismatching values in their second
* ClientHello. For increased compatibility, these checks are skipped outside of tests. The
* checks are still included in tests to ensure the s2n-tls client remains compliant.
*/
if (s2n_in_test()) {
/* In the past, the s2n-tls client updated the client random in the second ClientHello
* which is not allowed by RFC8446: https://github.com/aws/s2n-tls/pull/3311. Although the
* issue was addressed, its existence means that old versions of the s2n-tls client will
* fail this validation.
*/
RESULT_ENSURE(s2n_constant_time_equals(
previous_client_random,
conn->handshake_params.client_random,
S2N_TLS_RANDOM_DATA_LEN),
S2N_ERR_BAD_MESSAGE);
/* Some clients have been found to send a mismatching legacy session ID. */
RESULT_ENSURE(old_ch->session_id.size == new_ch->session_id.size, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(s2n_constant_time_equals(old_ch->session_id.data, new_ch->session_id.data,
new_ch->session_id.size),
S2N_ERR_BAD_MESSAGE);
/* Some clients have been found to send a mismatching cipher suite list. */
RESULT_ENSURE(old_ch->cipher_suites.size == new_ch->cipher_suites.size, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(s2n_constant_time_equals(old_ch->cipher_suites.data, new_ch->cipher_suites.data,
new_ch->cipher_suites.size),
S2N_ERR_BAD_MESSAGE);
}
/*
* Now enforce that the extensions also exactly match,
* except for the exceptions described in the RFC.
*/
for (size_t i = 0; i < s2n_array_len(s2n_supported_extensions); i++) {
s2n_parsed_extension *old_extension = &old_ch->extensions.parsed_extensions[i];
uint32_t old_size = old_extension->extension.size;
s2n_parsed_extension *new_extension = &new_ch->extensions.parsed_extensions[i];
uint32_t new_size = new_extension->extension.size;
/* The extension type is only set if the extension is present.
* Look for a non-zero-length extension.
*/
uint16_t extension_type = 0;
if (old_size != 0) {
extension_type = old_extension->extension_type;
} else if (new_size != 0) {
extension_type = new_extension->extension_type;
} else {
continue;
}
switch (extension_type) {
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2
*# - If a "key_share" extension was supplied in the HelloRetryRequest,
*# replacing the list of shares with a list containing a single
*# KeyShareEntry from the indicated group.
*/
case TLS_EXTENSION_KEY_SHARE:
/* Handled when parsing the key share extension */
break;
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2
*# - Removing the "early_data" extension (Section 4.2.10) if one was
*# present. Early data is not permitted after a HelloRetryRequest.
*/
case TLS_EXTENSION_EARLY_DATA:
RESULT_ENSURE(new_size == 0, S2N_ERR_BAD_MESSAGE);
break;
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2
*# - Including a "cookie" extension if one was provided in the
*# HelloRetryRequest.
*/
case TLS_EXTENSION_COOKIE:
/* Handled when parsing the cookie extension */
break;
/*
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.1.2
*# - Updating the "pre_shared_key" extension if present by recomputing
*# the "obfuscated_ticket_age" and binder values and (optionally)
*# removing any PSKs which are incompatible with the server's
*# indicated cipher suite.
*/
case TLS_EXTENSION_PRE_SHARED_KEY:
/* Handled when parsing the psk extension */
break;
/* Some clients have been found to send mismatching supported versions in their second
* ClientHello. The extension isn't compared byte-for-byte for increased compatibility
* with these clients.
*/
case TLS_EXTENSION_SUPPORTED_VERSIONS:
/* Additional HRR validation for the supported versions extension is performed when
* parsing the extension.
*/
break;
/*
* No more exceptions.
* All other extensions must match.
*/
default:
RESULT_ENSURE(old_size == new_size, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(s2n_constant_time_equals(
new_extension->extension.data,
old_extension->extension.data,
old_size),
S2N_ERR_BAD_MESSAGE);
}
}
return S2N_RESULT_OK;
}