in tls/s2n_client_hello.c [709:811]
int s2n_client_hello_send(struct s2n_connection *conn)
{
POSIX_ENSURE_REF(conn);
const struct s2n_security_policy *security_policy = NULL;
POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy));
const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences;
POSIX_ENSURE_REF(cipher_preferences);
if (!s2n_connection_supports_tls13(conn) || !s2n_security_policy_supports_tls13(security_policy)) {
conn->client_protocol_version = MIN(conn->client_protocol_version, S2N_TLS12);
conn->actual_protocol_version = MIN(conn->actual_protocol_version, S2N_TLS12);
}
struct s2n_stuffer *out = &conn->handshake.io;
uint8_t client_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 };
uint8_t reported_protocol_version = MIN(conn->client_protocol_version, S2N_TLS12);
client_protocol_version[0] = reported_protocol_version / 10;
client_protocol_version[1] = reported_protocol_version % 10;
conn->client_hello_version = reported_protocol_version;
POSIX_GUARD(s2n_stuffer_write_bytes(out, client_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN));
struct s2n_blob client_random = { 0 };
POSIX_GUARD(s2n_blob_init(&client_random, conn->handshake_params.client_random, S2N_TLS_RANDOM_DATA_LEN));
if (!s2n_is_hello_retry_handshake(conn)) {
/* Only generate the random data for our first client hello.
* If we retry, we'll reuse the value. */
POSIX_GUARD_RESULT(s2n_get_public_random_data(&client_random));
}
POSIX_GUARD(s2n_stuffer_write(out, &client_random));
POSIX_GUARD_RESULT(s2n_generate_client_session_id(conn));
POSIX_GUARD(s2n_stuffer_write_uint8(out, conn->session_id_len));
if (conn->session_id_len > 0) {
POSIX_GUARD(s2n_stuffer_write_bytes(out, conn->session_id, conn->session_id_len));
}
/* Reserve space for size of the list of available ciphers */
struct s2n_stuffer_reservation available_cipher_suites_size;
POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &available_cipher_suites_size));
/* Now, write the IANA values of every available cipher suite in our list */
struct s2n_cipher_suite *cipher = NULL;
bool tls12_is_possible = false;
for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) {
cipher = cipher_preferences->suites[i];
if (s2n_result_is_error(s2n_cipher_suite_validate_available(conn, cipher))) {
continue;
}
if (cipher->minimum_required_tls_version < S2N_TLS13) {
tls12_is_possible = true;
}
POSIX_GUARD(s2n_stuffer_write_bytes(out, cipher->iana_value, S2N_TLS_CIPHER_SUITE_LEN));
}
/**
* For initial handshakes:
*= https://www.rfc-editor.org/rfc/rfc5746#3.4
*# o The client MUST include either an empty "renegotiation_info"
*# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling
*# cipher suite value in the ClientHello. Including both is NOT
*# RECOMMENDED.
* For maximum backwards compatibility, we choose to use the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite
* rather than the "renegotiation_info" extension.
*
* For renegotiation handshakes:
*= https://www.rfc-editor.org/rfc/rfc5746#3.5
*# The SCSV MUST NOT be included.
*/
if (tls12_is_possible && !s2n_handshake_is_renegotiation(conn)) {
uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV };
POSIX_GUARD(s2n_stuffer_write_bytes(out, renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN));
}
/* Write size of the list of available ciphers */
uint32_t ciphers_size = 0;
POSIX_GUARD(s2n_stuffer_get_vector_size(&available_cipher_suites_size, &ciphers_size));
POSIX_ENSURE(ciphers_size > 0, S2N_ERR_INVALID_CIPHER_PREFERENCES);
POSIX_GUARD(s2n_stuffer_write_reservation(&available_cipher_suites_size, ciphers_size));
/* Zero compression methods */
POSIX_GUARD(s2n_stuffer_write_uint8(out, 1));
POSIX_GUARD(s2n_stuffer_write_uint8(out, 0));
/* Write the extensions */
POSIX_GUARD(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, out));
/* Once the message is complete, finish calculating the PSK binders.
*
* The PSK binders require all the sizes in the ClientHello to be written correctly,
* including the extension size and extension list size, and therefore have
* to be calculated AFTER we finish writing the entire extension list. */
POSIX_GUARD_RESULT(s2n_finish_psk_extension(conn));
/* If early data was not requested as part of the ClientHello, it never will be. */
if (conn->early_data_state == S2N_UNKNOWN_EARLY_DATA_STATE) {
POSIX_GUARD_RESULT(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_NOT_REQUESTED));
}
return S2N_SUCCESS;
}