in tls/s2n_signature_algorithms.c [177:317]
S2N_RESULT s2n_signature_algorithm_select(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->secure);
struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite;
RESULT_ENSURE_REF(cipher_suite);
const struct s2n_signature_scheme **chosen_sig_scheme = NULL;
if (conn->mode == S2N_CLIENT) {
chosen_sig_scheme = &conn->handshake_params.client_cert_sig_scheme;
} else {
chosen_sig_scheme = &conn->handshake_params.server_cert_sig_scheme;
}
/* Before TLS1.2, signature algorithms were fixed instead of negotiated */
if (conn->actual_protocol_version < S2N_TLS12) {
RESULT_GUARD(s2n_signature_algorithms_get_legacy_default(conn, conn->mode, chosen_sig_scheme));
return S2N_RESULT_OK;
}
const struct s2n_signature_preferences *signature_preferences = NULL;
RESULT_GUARD_POSIX(s2n_connection_get_signature_preferences(conn, &signature_preferences));
RESULT_ENSURE_REF(signature_preferences);
const struct s2n_signature_scheme *fallback_candidate = NULL;
/* We use local preference order, not peer preference order, so we iterate
* over the local preferences instead of over the options offered by the peer.
*/
for (size_t i = 0; i < signature_preferences->count; i++) {
const struct s2n_signature_scheme *candidate = signature_preferences->signature_schemes[i];
/* Validates that a signature is valid to choose,
* including that it's allowed by the current protocol version.
*/
if (!s2n_signature_scheme_is_valid_for_recv(conn, candidate)) {
continue;
}
if (s2n_is_sig_scheme_valid_for_auth(conn, candidate) != S2N_SUCCESS) {
continue;
}
/* s2n-tls first attempts to choose a signature algorithm offered by the peer.
* However, if that is not possible, we will attempt to continue the handshake
* anyway with an algorithm not offered by the peer. This fallback behavior
* is allowed by the RFC for TLS1.3 servers and partially allowed for TLS1.2
* servers that don't receive the signature_algorithms extension, but is
* otherwise an intentional deviation from the RFC.
*
* TLS1.3 servers:
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.3
*# If the CertificateVerify message is sent by a server, the signature
*# algorithm MUST be one offered in the client's "signature_algorithms"
*# extension unless no valid certificate chain can be produced without
*# unsupported algorithms
*
* TLS1.3 clients:
*= https://www.rfc-editor.org/rfc/rfc8446#section-4.4.3
*= type=exception
*= reason=Compatibility with hypothetical faulty peers
*# If sent by a client, the signature algorithm used in the signature
*# MUST be one of those present in the supported_signature_algorithms
*# field of the "signature_algorithms" extension in the
*# CertificateRequest message.
*
* TLS1.2 servers:
*= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.3
*= type=exception
*= reason=Compatibility with known faulty peers
*# If the client has offered the "signature_algorithms" extension, the
*# signature algorithm and hash algorithm MUST be a pair listed in that
*# extension.
*
* TLS1.2 clients:
*= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.8
*= type=exception
*= reason=Compatibility with hypothetical faulty peers
*# The hash and signature algorithms used in the signature MUST be
*# one of those present in the supported_signature_algorithms field
*# of the CertificateRequest message.
*/
bool is_peer_supported = s2n_signature_algorithm_is_supported_by_peer(
conn, candidate->iana_value);
if (is_peer_supported) {
*chosen_sig_scheme = candidate;
return S2N_RESULT_OK;
}
/**
*= https://www.rfc-editor.org/rfc/rfc5246#section-7.4.1.4.1
*# If the client does not send the signature_algorithms extension, the
*# server MUST do the following:
*#
*# - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA,
*# DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had
*# sent the value {sha1,rsa}.
*#
*# - If the negotiated key exchange algorithm is one of (DHE_DSS,
*# DH_DSS), behave as if the client had sent the value {sha1,dsa}.
*#
*# - If the negotiated key exchange algorithm is one of (ECDH_ECDSA,
*# ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}.
*
* The default scheme for DSA is not used because s2n-tls does not support DSA certificates.
*
* These defaults are only relevant for TLS1.2, since TLS1.3 does not allow SHA1.
*/
bool is_default = (candidate == &s2n_ecdsa_sha1 || candidate == &s2n_rsa_pkcs1_sha1);
/* If we ultimately cannot choose any algorithm offered by the peer,
* we will attempt negotiation with an algorithm not offered by the peer.
*
* The TLS1.2 RFC specifies default algorithms for use when no signature_algorithms
* extension is sent-- see the definition of is_default above.
*
* s2n-tls has encountered clients in the wild that support the TLS1.2
* default algorithms but do not include them in their signature_algorithms
* extension, likely due to a misreading of the RFC. So s2n-tls attempts
* to use the TLS1.2 defaults even when the client sends the signature_algorithms
* extension, and always treats them as the most preferred fallback option.
*
* If the TLS1.2 defaults are not possible-- for example, because TLS1.3
* or the security policy forbids SHA1-- we fallback to our own most
* preferred algorithm. In most cases a correctly implemented peer will reject
* this fallback, but the only alternative is to kill the connection here.
*/
if (is_default) {
fallback_candidate = candidate;
} else if (fallback_candidate == NULL) {
fallback_candidate = candidate;
}
}
if (fallback_candidate) {
*chosen_sig_scheme = fallback_candidate;
} else {
RESULT_BAIL(S2N_ERR_NO_VALID_SIGNATURE_SCHEME);
}
return S2N_RESULT_OK;
}