in tls/s2n_cipher_suites.c [1216:1360]
static int s2n_set_cipher_as_server(struct s2n_connection *conn, uint8_t *wire, uint32_t count, uint32_t cipher_suite_len)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(conn->secure);
uint8_t renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV };
struct s2n_cipher_suite *higher_vers_match = NULL;
struct s2n_cipher_suite *non_chacha20_match = NULL;
/* RFC 7507 - If client is attempting to negotiate a TLS Version that is lower than the highest supported server
* version, and the client cipher list contains TLS_FALLBACK_SCSV, then the server must abort the connection since
* TLS_FALLBACK_SCSV should only be present when the client previously failed to negotiate a higher TLS version.
*/
if (conn->client_protocol_version < conn->server_protocol_version) {
uint8_t fallback_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_FALLBACK_SCSV };
if (s2n_wire_ciphers_contain(fallback_scsv, wire, count, cipher_suite_len)) {
POSIX_BAIL(S2N_ERR_FALLBACK_DETECTED);
}
}
if (s2n_wire_ciphers_contain(renegotiation_info_scsv, wire, count, cipher_suite_len)) {
/** For renegotiation handshakes:
*= https://www.rfc-editor.org/rfc/rfc5746#3.7
*# o When a ClientHello is received, the server MUST verify that it
*# does not contain the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If
*# the SCSV is present, the server MUST abort the handshake.
*/
POSIX_ENSURE(!s2n_handshake_is_renegotiation(conn), S2N_ERR_BAD_MESSAGE);
/** For initial handshakes:
*= https://www.rfc-editor.org/rfc/rfc5746#3.6
*# o When a ClientHello is received, the server MUST check if it
*# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does,
*# set the secure_renegotiation flag to TRUE.
*/
conn->secure_renegotiation = 1;
}
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);
bool try_chacha20_boosting = s2n_result_is_ok(s2n_validate_chacha20_boosting(cipher_preferences, wire, cipher_suite_len));
/*
* s2n only respects server preference order and chooses the server's
* most preferred mutually supported cipher suite.
*
* If chacha20 boosting is enabled, we prefer chacha20 cipher suites over all
* other cipher suites.
*
* If no mutually supported cipher suites are found, we choose one with a version
* too high for the current connection (higher_vers_match).
*/
for (size_t i = 0; i < cipher_preferences->count; i++) {
const uint8_t *ours = cipher_preferences->suites[i]->iana_value;
if (s2n_wire_ciphers_contain(ours, wire, count, cipher_suite_len)) {
/* We have a match */
struct s2n_cipher_suite *match = cipher_preferences->suites[i];
/* Never use TLS1.3 ciphers on a pre-TLS1.3 connection, and vice versa */
if ((conn->actual_protocol_version >= S2N_TLS13) != (match->minimum_required_tls_version >= S2N_TLS13)) {
continue;
}
/* If connection is for SSLv3, use SSLv3 version of suites */
if (conn->actual_protocol_version == S2N_SSLv3) {
match = match->sslv3_cipher_suite;
}
/* Skip the suite if we don't have an available implementation */
if (!match->available) {
continue;
}
/* Make sure the cipher is valid for available certs */
if (s2n_is_cipher_suite_valid_for_auth(conn, match) != S2N_SUCCESS) {
continue;
}
/* If the kex is not supported continue to the next candidate */
bool kex_supported = false;
POSIX_GUARD_RESULT(s2n_kex_supported(match, conn, &kex_supported));
if (!kex_supported) {
continue;
}
/* If the kex is not configured correctly continue to the next candidate */
if (s2n_result_is_error(s2n_configure_kex(match, conn))) {
continue;
}
/**
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.11
*# The server MUST ensure that it selects a compatible PSK
*# (if any) and cipher suite.
**/
if (conn->psk_params.chosen_psk != NULL) {
if (match->prf_alg != conn->psk_params.chosen_psk->hmac_alg) {
continue;
}
}
/* Don't immediately choose a cipher the connection shouldn't be able to support */
if (conn->actual_protocol_version < match->minimum_required_tls_version) {
if (!higher_vers_match) {
higher_vers_match = match;
}
continue;
}
/* The server and client have chacha20 boosting support enabled AND the server identified a negotiable match */
if (try_chacha20_boosting) {
if (s2n_cipher_suite_uses_chacha20_alg(match)) {
conn->secure->cipher_suite = match;
return S2N_SUCCESS;
}
/* Save the valid non-chacha20 match in case no valid chacha20 match is found */
if (!non_chacha20_match) {
non_chacha20_match = match;
}
continue;
}
conn->secure->cipher_suite = match;
return S2N_SUCCESS;
}
}
if (non_chacha20_match) {
conn->secure->cipher_suite = non_chacha20_match;
return S2N_SUCCESS;
}
/* Settle for a cipher with a higher required proto version, if it was set */
if (higher_vers_match) {
conn->secure->cipher_suite = higher_vers_match;
return S2N_SUCCESS;
}
POSIX_BAIL(S2N_ERR_CIPHER_NOT_SUPPORTED);
}