LCOV - code coverage report
Current view: top level - tls/extensions - s2n_client_supported_versions.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 78 81 96.3 %
Date: 2025-08-14 07:26:07 Functions: 5 6 83.3 %
Branches: 51 80 63.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/extensions/s2n_client_supported_versions.h"
      17                 :            : 
      18                 :            : #include <stdint.h>
      19                 :            : #include <sys/param.h>
      20                 :            : 
      21                 :            : #include "tls/extensions/s2n_supported_versions.h"
      22                 :            : #include "tls/s2n_alerts.h"
      23                 :            : #include "tls/s2n_cipher_preferences.h"
      24                 :            : #include "tls/s2n_tls.h"
      25                 :            : #include "tls/s2n_tls_parameters.h"
      26                 :            : #include "utils/s2n_safety.h"
      27                 :            : 
      28                 :            : /**
      29                 :            :  * Specified in https://tools.ietf.org/html/rfc8446#section-4.2.1
      30                 :            :  *
      31                 :            :  * "The "supported_versions" extension is used by the client to indicate
      32                 :            :  * which versions of TLS it supports and by the server to indicate which
      33                 :            :  * version it is using. The extension contains a list of supported
      34                 :            :  * versions in preference order, with the most preferred version first."
      35                 :            :  *
      36                 :            :  * Structure:
      37                 :            :  * Extension type (2 bytes)
      38                 :            :  * Extension size (2 bytes)
      39                 :            :  * Version list length (1 byte)
      40                 :            :  * Version list (number of versions * 2 bytes)
      41                 :            :  *
      42                 :            :  * Note: We assume in these functions that the supported version numbers
      43                 :            :  * are consecutive. This is true because S2N does not support SSLv2, and
      44                 :            :  * is already an assumption made in the old client hello version handling.
      45                 :            :  **/
      46                 :            : 
      47                 :            : static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out);
      48                 :            : static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *in);
      49                 :            : 
      50                 :            : const s2n_extension_type s2n_client_supported_versions_extension = {
      51                 :            :     .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS,
      52                 :            :     .is_response = false,
      53                 :            :     .send = s2n_client_supported_versions_send,
      54                 :            :     .recv = s2n_client_supported_versions_recv,
      55                 :            :     .should_send = s2n_extension_send_if_tls13_connection,
      56                 :            :     .if_missing = s2n_extension_noop_if_missing,
      57                 :            : };
      58                 :            : 
      59                 :            : static int s2n_client_supported_versions_send(struct s2n_connection *conn, struct s2n_stuffer *out)
      60                 :       5311 : {
      61                 :       5311 :     uint8_t highest_supported_version = conn->client_protocol_version;
      62                 :       5311 :     uint8_t minimum_supported_version = s2n_unknown_protocol_version;
      63         [ -  + ]:       5311 :     POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version));
      64 [ -  + ][ #  # ]:       5311 :     POSIX_ENSURE(highest_supported_version >= minimum_supported_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
      65                 :            : 
      66                 :       5311 :     uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1;
      67         [ -  + ]:       5311 :     POSIX_GUARD(s2n_stuffer_write_uint8(out, version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN));
      68                 :            : 
      69         [ +  + ]:      19820 :     for (uint8_t i = highest_supported_version; i >= minimum_supported_version; i--) {
      70         [ -  + ]:      14509 :         POSIX_GUARD(s2n_stuffer_write_uint8(out, i / 10));
      71         [ -  + ]:      14509 :         POSIX_GUARD(s2n_stuffer_write_uint8(out, i % 10));
      72                 :      14509 :     }
      73                 :            : 
      74                 :       5311 :     return S2N_SUCCESS;
      75                 :       5311 : }
      76                 :            : 
      77                 :            : int s2n_extensions_client_supported_versions_process(struct s2n_connection *conn, struct s2n_stuffer *extension,
      78                 :            :         uint8_t *client_protocol_version_out, uint8_t *actual_protocol_version_out)
      79                 :       4991 : {
      80                 :       4991 :     uint8_t highest_supported_version = conn->server_protocol_version;
      81                 :       4991 :     uint8_t minimum_supported_version = s2n_unknown_protocol_version;
      82         [ -  + ]:       4991 :     POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version));
      83                 :            : 
      84                 :       4991 :     uint8_t size_of_version_list = 0;
      85         [ -  + ]:       4991 :     POSIX_GUARD(s2n_stuffer_read_uint8(extension, &size_of_version_list));
      86 [ +  + ][ +  - ]:       4991 :     S2N_ERROR_IF(size_of_version_list != s2n_stuffer_data_available(extension), S2N_ERR_BAD_MESSAGE);
      87 [ +  + ][ +  - ]:       4984 :     S2N_ERROR_IF(size_of_version_list % S2N_TLS_PROTOCOL_VERSION_LEN != 0, S2N_ERR_BAD_MESSAGE);
      88                 :            : 
      89                 :       4983 :     uint8_t client_protocol_version = s2n_unknown_protocol_version;
      90                 :       4983 :     uint8_t actual_protocol_version = s2n_unknown_protocol_version;
      91                 :            : 
      92         [ +  + ]:      17837 :     for (int i = 0; i < size_of_version_list; i += S2N_TLS_PROTOCOL_VERSION_LEN) {
      93                 :      12854 :         uint8_t client_version_parts[S2N_TLS_PROTOCOL_VERSION_LEN];
      94         [ -  + ]:      12854 :         POSIX_GUARD(s2n_stuffer_read_bytes(extension, client_version_parts, S2N_TLS_PROTOCOL_VERSION_LEN));
      95                 :            : 
      96                 :            :         /* If the client version is outside of our supported versions, then ignore the value.
      97                 :            :          * S2N does not support SSLv2 except for upgrading connections. Since this extension is
      98                 :            :          * a TLS1.3 extension, we will skip any SSLv2 values. */
      99 [ +  + ][ +  + ]:      12854 :         if (client_version_parts[0] != 3 || client_version_parts[1] > 4) {
     100                 :         35 :             continue;
     101                 :         35 :         }
     102                 :            : 
     103                 :      12819 :         uint16_t client_version = (client_version_parts[0] * 10) + client_version_parts[1];
     104                 :            : 
     105                 :      12819 :         client_protocol_version = MAX(client_version, client_protocol_version);
     106                 :            : 
     107         [ +  + ]:      12819 :         if (client_version > highest_supported_version) {
     108                 :          4 :             continue;
     109                 :          4 :         }
     110                 :            : 
     111         [ +  + ]:      12815 :         if (client_version < minimum_supported_version) {
     112                 :       2461 :             continue;
     113                 :       2461 :         }
     114                 :            : 
     115                 :            :         /* We ignore the client's preferred order and instead choose
     116                 :            :          * the highest version that both client and server support. */
     117                 :      10354 :         actual_protocol_version = MAX(client_version, actual_protocol_version);
     118                 :      10354 :     }
     119                 :            : 
     120                 :       4983 :     *client_protocol_version_out = client_protocol_version;
     121                 :       4983 :     *actual_protocol_version_out = actual_protocol_version;
     122                 :            : 
     123                 :       4983 :     return S2N_SUCCESS;
     124                 :       4983 : }
     125                 :            : 
     126                 :            : static S2N_RESULT s2n_client_supported_versions_recv_impl(struct s2n_connection *conn, struct s2n_stuffer *extension)
     127                 :       4963 : {
     128 [ -  + ][ #  # ]:       4963 :     RESULT_ENSURE_REF(conn);
     129 [ -  + ][ #  # ]:       4963 :     RESULT_ENSURE_REF(extension);
     130                 :            : 
     131         [ +  + ]:       4963 :     RESULT_GUARD_POSIX(s2n_extensions_client_supported_versions_process(conn, extension, &conn->client_protocol_version,
     132                 :       4959 :             &conn->actual_protocol_version));
     133                 :            : 
     134 [ +  + ][ +  - ]:       4959 :     RESULT_ENSURE(conn->client_protocol_version != s2n_unknown_protocol_version, S2N_ERR_UNKNOWN_PROTOCOL_VERSION);
     135 [ +  + ][ +  - ]:       4957 :     RESULT_ENSURE(conn->actual_protocol_version != s2n_unknown_protocol_version, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
     136                 :            : 
     137                 :       4953 :     return S2N_RESULT_OK;
     138                 :       4957 : }
     139                 :            : 
     140                 :            : static int s2n_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
     141                 :       5361 : {
     142                 :            :     /* For backwards compatibility, the supported versions extension isn't used for protocol
     143                 :            :      * version selection if the server doesn't support TLS 1.3. This ensures that TLS 1.2 servers
     144                 :            :      * experience no behavior change due to processing the TLS 1.3 extension. See
     145                 :            :      * https://github.com/aws/s2n-tls/issues/4240.
     146                 :            :      *
     147                 :            :      *= https://www.rfc-editor.org/rfc/rfc8446#section-4.2.1
     148                 :            :      *= type=exception
     149                 :            :      *= reason=The client hello legacy version is used for version selection on TLS 1.2 servers for backwards compatibility
     150                 :            :      *# If this extension is present in the ClientHello, servers MUST NOT use
     151                 :            :      *# the ClientHello.legacy_version value for version negotiation and MUST
     152                 :            :      *# use only the "supported_versions" extension to determine client
     153                 :            :      *# preferences.
     154                 :            :      */
     155         [ +  + ]:       5361 :     if (s2n_connection_get_protocol_version(conn) < S2N_TLS13) {
     156                 :        398 :         return S2N_SUCCESS;
     157                 :        398 :     }
     158                 :            : 
     159                 :            :     /* A TLS 1.3 state machine flag is used to determine if a HelloRetryRequest is negotiated. A
     160                 :            :      * protocol version of TLS 1.3 must be set in order to query the TLS 1.3 state machine, so
     161                 :            :      * it must be queried before the protocol version is potentially reset due to processing the
     162                 :            :      * extension.
     163                 :            :      */
     164                 :       4963 :     bool is_hrr_handshake = s2n_is_hello_retry_handshake(conn);
     165                 :            : 
     166                 :       4963 :     s2n_result result = s2n_client_supported_versions_recv_impl(conn, extension);
     167         [ +  + ]:       4963 :     if (s2n_result_is_error(result)) {
     168                 :         10 :         conn->client_protocol_version = s2n_unknown_protocol_version;
     169                 :         10 :         conn->actual_protocol_version = s2n_unknown_protocol_version;
     170                 :            : 
     171                 :         10 :         s2n_queue_reader_unsupported_protocol_version_alert(conn);
     172 [ -  + ][ #  # ]:         10 :         POSIX_ENSURE(s2n_errno != S2N_ERR_SAFETY, S2N_ERR_BAD_MESSAGE);
     173                 :         10 :     }
     174         [ +  + ]:       4963 :     POSIX_GUARD_RESULT(result);
     175                 :            : 
     176                 :            :     /* When the supported versions extension is received in a ClientHello sent in response to a
     177                 :            :      * HelloRetryRequest, ensure that TLS 1.3 is selected as the protocol version.
     178                 :            :      */
     179 [ +  + ][ +  - ]:       4953 :     if (is_hrr_handshake && conn->handshake.message_number > 0) {
     180 [ +  + ][ +  - ]:        633 :         POSIX_ENSURE(conn->client_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
     181 [ #  # ][ -  + ]:        629 :         POSIX_ENSURE(conn->actual_protocol_version == S2N_TLS13, S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED);
     182                 :        629 :     }
     183                 :            : 
     184                 :       4949 :     return S2N_SUCCESS;
     185                 :       4953 : }
     186                 :            : 
     187                 :            : /* Old-style extension functions -- remove after extensions refactor is complete */
     188                 :            : 
     189                 :            : int s2n_extensions_client_supported_versions_size(struct s2n_connection *conn)
     190                 :          1 : {
     191                 :          1 :     uint8_t minimum_supported_version = s2n_unknown_protocol_version;
     192         [ -  + ]:          1 :     POSIX_GUARD_RESULT(s2n_connection_get_minimum_supported_version(conn, &minimum_supported_version));
     193                 :          1 :     uint8_t highest_supported_version = conn->client_protocol_version;
     194                 :            : 
     195                 :          1 :     uint8_t version_list_length = highest_supported_version - minimum_supported_version + 1;
     196                 :            : 
     197                 :          1 :     return version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 5;
     198                 :          1 : }
     199                 :            : 
     200                 :            : /* still used in fuzz test */
     201                 :            : int s2n_extensions_client_supported_versions_recv(struct s2n_connection *conn, struct s2n_stuffer *extension)
     202                 :          0 : {
     203                 :          0 :     return s2n_extension_recv(&s2n_client_supported_versions_extension, conn, extension);
     204                 :          0 : }

Generated by: LCOV version 1.14