static int s2n_set_cipher_as_server()

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);
}