LCOV - code coverage report
Current view: top level - tls - s2n_auth_selection.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 108 120 90.0 %
Date: 2026-08-22 07:27:53 Functions: 8 8 100.0 %
Branches: 86 180 47.8 %

           Branch data     Line data    Source code
       1                 :            : /*
       2                 :            :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3                 :            :  *
       4                 :            :  * Licensed under the Apache License, Version 2.0 (the "License").
       5                 :            :  * You may not use this file except in compliance with the License.
       6                 :            :  * A copy of the License is located at
       7                 :            :  *
       8                 :            :  *  http://aws.amazon.com/apache2.0
       9                 :            :  *
      10                 :            :  * or in the "license" file accompanying this file. This file is distributed
      11                 :            :  * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
      12                 :            :  * express or implied. See the License for the specific language governing
      13                 :            :  * permissions and limitations under the License.
      14                 :            :  */
      15                 :            : 
      16                 :            : #include "tls/s2n_auth_selection.h"
      17                 :            : 
      18                 :            : #include "crypto/s2n_certificate.h"
      19                 :            : #include "crypto/s2n_signature.h"
      20                 :            : #include "tls/s2n_cipher_suites.h"
      21                 :            : #include "tls/s2n_kex.h"
      22                 :            : #include "tls/s2n_signature_algorithms.h"
      23                 :            : #include "utils/s2n_safety.h"
      24                 :            : 
      25                 :            : /* This module should contain any logic related to choosing a valid combination of
      26                 :            :  * signature algorithm, authentication method, and certificate to use for authentication.
      27                 :            :  *
      28                 :            :  * We choose our auth methods by:
      29                 :            :  * 1. Finding a cipher suite with an auth method that we have valid certs for. In TLS1.3,
      30                 :            :  *    this is a no-op -- cipher suites do not specify an auth method.
      31                 :            :  * 2. Choosing a signature algorithm that matches both the auth method (if set) and the
      32                 :            :  *    available certs.
      33                 :            :  * 3. Selecting the cert that matches the chosen signature algorithm.
      34                 :            :  *
      35                 :            :  * This is a break from the original s2n pre-TLS1.3 flow, when we could choose certs and
      36                 :            :  * ciphers at the same time. Our cipher suites differentiate between "RSA" and "ECDSA",
      37                 :            :  * but not between "RSA" and "RSA-PSS". To make that decision, we need to wait until
      38                 :            :  * we've chosen a signature algorithm. This allows us to use RSA-PSS with existing
      39                 :            :  * TLS1.2 cipher suites.
      40                 :            :  */
      41                 :            : 
      42                 :            : int s2n_get_auth_method_for_cert_type(s2n_pkey_type cert_type, s2n_authentication_method *auth_method)
      43                 :      14399 : {
      44         [ -  + ]:      14399 :     switch (cert_type) {
      45         [ +  + ]:       8504 :         case S2N_PKEY_TYPE_RSA:
      46         [ +  + ]:      10184 :         case S2N_PKEY_TYPE_RSA_PSS:
      47                 :      10184 :             *auth_method = S2N_AUTHENTICATION_RSA;
      48                 :      10184 :             return S2N_SUCCESS;
      49         [ +  + ]:       2563 :         case S2N_PKEY_TYPE_ECDSA:
      50                 :       2563 :             *auth_method = S2N_AUTHENTICATION_ECDSA;
      51                 :       2563 :             return S2N_SUCCESS;
      52         [ +  + ]:       1650 :         case S2N_PKEY_TYPE_MLDSA:
      53         [ +  + ]:       1652 :         case S2N_PKEY_TYPE_UNKNOWN:
      54         [ -  + ]:       1652 :         case S2N_PKEY_TYPE_SENTINEL:
      55         [ +  - ]:       1652 :             POSIX_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
      56                 :      14399 :     }
      57         [ #  # ]:          0 :     POSIX_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
      58                 :          0 : }
      59                 :            : 
      60                 :            : static int s2n_is_sig_alg_valid_for_cipher_suite(s2n_signature_algorithm sig_alg, struct s2n_cipher_suite *cipher_suite)
      61                 :       6960 : {
      62 [ -  + ][ #  # ]:       6960 :     POSIX_ENSURE_REF(cipher_suite);
      63                 :            : 
      64                 :       6960 :     s2n_pkey_type cert_type_for_sig_alg = S2N_PKEY_TYPE_UNKNOWN;
      65         [ -  + ]:       6960 :     POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_alg, &cert_type_for_sig_alg));
      66                 :            : 
      67                 :            :     /* Non-ephemeral key exchange methods require encryption, and RSA-PSS certificates
      68                 :            :      * do not support encryption.
      69                 :            :      *
      70                 :            :      * Therefore, if a cipher suite uses a non-ephemeral kex, then any signature
      71                 :            :      * algorithm that requires RSA-PSS certificates is not valid.
      72                 :            :      */
      73                 :       6960 :     const struct s2n_kex *kex = cipher_suite->key_exchange_alg;
      74 [ -  + ][ #  # ]:       6960 :     POSIX_ENSURE_REF(kex);
      75         [ +  + ]:       6960 :     if (!kex->is_ephemeral) {
      76 [ +  + ][ +  - ]:       1209 :         POSIX_ENSURE_NE(cert_type_for_sig_alg, S2N_PKEY_TYPE_RSA_PSS);
      77                 :       1209 :     }
      78                 :            : 
      79                 :            :     /* If a cipher suite includes an auth method, then the signature algorithm
      80                 :            :      * must match that auth method.
      81                 :            :      */
      82         [ +  + ]:       6959 :     if (cipher_suite->auth_method != S2N_AUTHENTICATION_METHOD_SENTINEL) {
      83                 :       2596 :         s2n_authentication_method auth_method_for_sig_alg = 0;
      84         [ -  + ]:       2596 :         POSIX_GUARD(s2n_get_auth_method_for_cert_type(cert_type_for_sig_alg, &auth_method_for_sig_alg));
      85 [ +  + ][ +  - ]:       2596 :         POSIX_ENSURE_EQ(cipher_suite->auth_method, auth_method_for_sig_alg);
      86                 :       2596 :     }
      87                 :            : 
      88                 :       6414 :     return S2N_SUCCESS;
      89                 :       6959 : }
      90                 :            : 
      91                 :            : static int s2n_certs_exist_for_sig_scheme(struct s2n_connection *conn, const struct s2n_signature_scheme *sig_scheme)
      92                 :      31743 : {
      93 [ -  + ][ #  # ]:      31743 :     POSIX_ENSURE_REF(sig_scheme);
      94                 :            : 
      95                 :      31743 :     s2n_pkey_type cert_type = S2N_PKEY_TYPE_UNKNOWN;
      96         [ -  + ]:      31743 :     POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_scheme->sig_alg, &cert_type));
      97                 :            :     /* A valid cert must exist for the authentication method. */
      98                 :      31743 :     struct s2n_cert_chain_and_key *cert = s2n_get_compatible_cert_chain_and_key(conn, cert_type);
      99 [ +  + ][ +  - ]:      31743 :     POSIX_ENSURE_REF(cert);
     100                 :            : 
     101                 :            :     /* In TLS 1.3, signature scheme may further restrict the certs */
     102         [ +  + ]:       8139 :     if (conn->actual_protocol_version >= S2N_TLS13) {
     103 [ +  + ][ +  + ]:       5438 :         if (cert_type == S2N_PKEY_TYPE_RSA || cert_type == S2N_PKEY_TYPE_RSA_PSS) {
     104                 :            :             /* a RSA cert is valid for any corresponding RSA signature scheme. For
     105                 :            :              * example an rsae cert can be used for both rsa_pss_rsae_sha256
     106                 :            :              * and rsa_pss_rsae_sha384 */
     107                 :       3310 :             return S2N_SUCCESS;
     108         [ +  - ]:       3310 :         } else if (cert_type == S2N_PKEY_TYPE_ECDSA) {
     109                 :            :             /* TLS 1.3 ECDSA signatures schemes e.g. ecdsa_secp384r1_sha384 also specify
     110                 :            :              * a curve. We must make sure that the certificate has the correct curve */
     111 [ -  + ][ #  # ]:       2128 :             POSIX_ENSURE_REF(cert->private_key);
     112 [ #  # ][ -  + ]:       2128 :             POSIX_ENSURE_REF(cert->cert_chain);
     113 [ #  # ][ -  + ]:       2128 :             POSIX_ENSURE_REF(cert->cert_chain->head);
     114 [ -  + ][ #  # ]:       2128 :             POSIX_ENSURE_EQ(cert->cert_chain->head->pkey_type, S2N_PKEY_TYPE_ECDSA);
     115 [ +  + ][ +  - ]:       2128 :             POSIX_ENSURE_EQ(cert->cert_chain->head->info.public_key_nid, sig_scheme->signature_curve->libcrypto_nid);
     116         [ #  # ]:       2128 :         } else if (cert_type == S2N_PKEY_TYPE_MLDSA) {
     117                 :            :             /* ML-DSA signatures e.g. mldsa65 include a parameter set (65) which must match
     118                 :            :              * the cert. public_key_nid holds the specific ML-DSA NID (NID_MLDSA44/65/87)
     119                 :            :              * populated via EVP_PKEY_pqdsa_get_type() at cert-load time. */
     120 [ #  # ][ #  # ]:          0 :             POSIX_ENSURE_REF(cert->private_key);
     121 [ #  # ][ #  # ]:          0 :             POSIX_ENSURE_REF(cert->cert_chain);
     122 [ #  # ][ #  # ]:          0 :             POSIX_ENSURE_REF(cert->cert_chain->head);
     123 [ #  # ][ #  # ]:          0 :             POSIX_ENSURE_EQ(cert->cert_chain->head->pkey_type, S2N_PKEY_TYPE_MLDSA);
     124 [ #  # ][ #  # ]:          0 :             POSIX_ENSURE_EQ(cert->cert_chain->head->info.public_key_nid, sig_scheme->libcrypto_nid);
     125                 :          0 :         } else {
     126                 :            :             /* We expect any future signature schemes to also have these restrictions
     127                 :            :              * so we concretely fail here until they are properly handled */
     128         [ #  # ]:          0 :             POSIX_BAIL(S2N_ERR_UNIMPLEMENTED);
     129                 :          0 :         }
     130                 :       5438 :     }
     131                 :            : 
     132                 :       3826 :     return S2N_SUCCESS;
     133                 :       8139 : }
     134                 :            : 
     135                 :            : static int s2n_certs_exist_for_auth_method(struct s2n_connection *conn, s2n_authentication_method auth_method)
     136                 :       9659 : {
     137         [ +  + ]:       9659 :     if (auth_method == S2N_AUTHENTICATION_METHOD_SENTINEL) {
     138                 :       5454 :         return S2N_SUCCESS;
     139                 :       5454 :     }
     140                 :            : 
     141                 :       4205 :     s2n_authentication_method auth_method_for_cert_type = 0;
     142         [ +  - ]:       9511 :     for (int i = 0; i < S2N_CERT_TYPE_COUNT; i++) {
     143         [ +  + ]:       9511 :         POSIX_GUARD(s2n_get_auth_method_for_cert_type(i, &auth_method_for_cert_type));
     144                 :            : 
     145         [ +  + ]:       7861 :         if (auth_method != auth_method_for_cert_type) {
     146                 :       2975 :             continue;
     147                 :       2975 :         }
     148                 :            : 
     149         [ +  + ]:       4886 :         if (s2n_get_compatible_cert_chain_and_key(conn, i) != NULL) {
     150                 :       2555 :             return S2N_SUCCESS;
     151                 :       2555 :         }
     152                 :       4886 :     }
     153         [ #  # ]:          0 :     POSIX_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
     154                 :          0 : }
     155                 :            : 
     156                 :            : /* TLS1.3 ciphers are always valid, as they don't include an auth method.
     157                 :            :  *
     158                 :            :  * A pre-TLS1.3 cipher suite is valid if:
     159                 :            :  * - At least one compatible cert is configured
     160                 :            :  *
     161                 :            :  * This method is called by the server when choosing a cipher suite.
     162                 :            :  */
     163                 :            : int s2n_is_cipher_suite_valid_for_auth(struct s2n_connection *conn, struct s2n_cipher_suite *cipher_suite)
     164                 :       9659 : {
     165 [ -  + ][ #  # ]:       9659 :     POSIX_ENSURE_REF(cipher_suite);
     166                 :            : 
     167         [ +  + ]:       9659 :     POSIX_GUARD(s2n_certs_exist_for_auth_method(conn, cipher_suite->auth_method));
     168                 :            : 
     169                 :       8009 :     return S2N_SUCCESS;
     170                 :       9659 : }
     171                 :            : 
     172                 :            : /* A signature algorithm is valid if:
     173                 :            :  * - At least one compatible cert is configured.
     174                 :            :  * - The signature algorithm is allowed by the cipher suite's auth method
     175                 :            :  *   (if running as a pre-TLS1.3 server).
     176                 :            :  *
     177                 :            :  * This method is called by the both server and client when choosing a signature algorithm.
     178                 :            :  */
     179                 :            : int s2n_is_sig_scheme_valid_for_auth(struct s2n_connection *conn, const struct s2n_signature_scheme *sig_scheme)
     180                 :      31743 : {
     181 [ -  + ][ #  # ]:      31743 :     POSIX_ENSURE_REF(conn);
     182 [ #  # ][ -  + ]:      31743 :     POSIX_ENSURE_REF(conn->secure);
     183 [ -  + ][ #  # ]:      31743 :     POSIX_ENSURE_REF(sig_scheme);
     184                 :            : 
     185                 :      31743 :     struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite;
     186 [ -  + ][ #  # ]:      31743 :     POSIX_ENSURE_REF(cipher_suite);
     187                 :            : 
     188         [ +  + ]:      31743 :     POSIX_GUARD(s2n_certs_exist_for_sig_scheme(conn, sig_scheme));
     189                 :            : 
     190                 :            :     /* For the client side, signature algorithm does not need to match the cipher suite. */
     191         [ +  + ]:       7136 :     if (conn->mode == S2N_SERVER) {
     192         [ +  + ]:       6960 :         POSIX_GUARD(s2n_is_sig_alg_valid_for_cipher_suite(sig_scheme->sig_alg, cipher_suite));
     193                 :       6960 :     }
     194                 :       6590 :     return S2N_SUCCESS;
     195                 :       7136 : }
     196                 :            : 
     197                 :            : /* A cert is valid if:
     198                 :            :  * - The configured cipher suite's auth method (if present) supports the cert.
     199                 :            :  *
     200                 :            :  * We could also verify that at least one of our supported sig algs
     201                 :            :  * supports the cert, but that seems unnecessary. If we don't have a valid
     202                 :            :  * sig alg, we'll fail on CertVerify.
     203                 :            :  *
     204                 :            :  * This method is called by the client when receiving the server's cert.
     205                 :            :  */
     206                 :            : int s2n_is_cert_type_valid_for_auth(struct s2n_connection *conn, s2n_pkey_type cert_type)
     207                 :       4837 : {
     208 [ -  + ][ #  # ]:       4837 :     POSIX_ENSURE_REF(conn);
     209 [ -  + ][ #  # ]:       4837 :     POSIX_ENSURE_REF(conn->secure);
     210 [ -  + ][ #  # ]:       4837 :     POSIX_ENSURE_REF(conn->secure->cipher_suite);
     211                 :       4837 :     s2n_authentication_method conn_auth_method = conn->secure->cipher_suite->auth_method;
     212                 :            : 
     213                 :            :     /* TLS1.3 cipher suites do not specify an auth type */
     214         [ +  + ]:       4837 :     if (conn_auth_method == S2N_AUTHENTICATION_METHOD_SENTINEL) {
     215                 :       2550 :         return S2N_SUCCESS;
     216                 :       2550 :     }
     217                 :            : 
     218                 :       2287 :     s2n_authentication_method cert_auth_method = 0;
     219         [ +  + ]:       2287 :     POSIX_GUARD(s2n_get_auth_method_for_cert_type(cert_type, &cert_auth_method));
     220 [ +  + ][ +  - ]:       2285 :     POSIX_ENSURE(cert_auth_method == conn_auth_method, S2N_ERR_CERT_TYPE_UNSUPPORTED);
     221                 :       2282 :     return S2N_SUCCESS;
     222                 :       2285 : }
     223                 :            : 
     224                 :            : /* Choose the cert associated with our configured signature algorithm.
     225                 :            :  *
     226                 :            :  * This method is called by the server after configuring its cipher suite and sig algs.
     227                 :            :  */
     228                 :            : int s2n_select_certs_for_server_auth(struct s2n_connection *conn, struct s2n_cert_chain_and_key **chosen_certs)
     229                 :       6821 : {
     230 [ -  + ][ #  # ]:       6821 :     POSIX_ENSURE_REF(conn);
     231                 :       6821 :     const struct s2n_signature_scheme *sig_scheme = conn->handshake_params.server_cert_sig_scheme;
     232 [ -  + ][ #  # ]:       6821 :     POSIX_ENSURE_REF(sig_scheme);
     233                 :       6821 :     s2n_signature_algorithm sig_alg = sig_scheme->sig_alg;
     234                 :            : 
     235                 :       6821 :     s2n_pkey_type cert_type = S2N_PKEY_TYPE_UNKNOWN;
     236         [ +  + ]:       6821 :     if (sig_scheme == &s2n_null_sig_scheme) {
     237                 :            :         /* Only RSA auth (+ RSA kex) supports no signature scheme */
     238                 :        341 :         cert_type = S2N_PKEY_TYPE_RSA;
     239                 :       6480 :     } else {
     240         [ -  + ]:       6480 :         POSIX_GUARD_RESULT(s2n_signature_algorithm_get_pkey_type(sig_alg, &cert_type));
     241                 :       6480 :     }
     242                 :            : 
     243                 :       6821 :     *chosen_certs = s2n_get_compatible_cert_chain_and_key(conn, cert_type);
     244 [ +  + ][ +  - ]:       6821 :     S2N_ERROR_IF(*chosen_certs == NULL, S2N_ERR_CERT_TYPE_UNSUPPORTED);
     245                 :            : 
     246                 :       6815 :     return S2N_SUCCESS;
     247                 :       6821 : }

Generated by: LCOV version 1.14