LCOV - code coverage report
Current view: top level - tls - s2n_ktls.c (source / functions) Hit Total Coverage
Test: unit_test_coverage.info Lines: 143 151 94.7 %
Date: 2025-08-15 07:28:39 Functions: 11 12 91.7 %
Branches: 109 194 56.2 %

           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_ktls.h"
      17                 :            : 
      18                 :            : #include "crypto/s2n_ktls_crypto.h"
      19                 :            : #include "tls/s2n_prf.h"
      20                 :            : #include "tls/s2n_tls.h"
      21                 :            : #include "tls/s2n_tls13_key_schedule.h"
      22                 :            : 
      23                 :            : /* Used for overriding setsockopt calls in testing */
      24                 :            : s2n_setsockopt_fn s2n_setsockopt = setsockopt;
      25                 :            : 
      26                 :            : S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb)
      27                 :         24 : {
      28 [ -  + ][ #  # ]:         24 :     RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST);
      29                 :         24 :     s2n_setsockopt = cb;
      30                 :         24 :     return S2N_RESULT_OK;
      31                 :         24 : }
      32                 :            : 
      33                 :            : bool s2n_ktls_is_supported_on_platform()
      34                 :         39 : {
      35                 :         39 : #if defined(S2N_KTLS_SUPPORTED)
      36                 :         39 :     return true;
      37                 :            : #else
      38                 :            :     return false;
      39                 :            : #endif
      40                 :         39 : }
      41                 :            : 
      42                 :            : static int s2n_ktls_disabled_read(void *io_context, uint8_t *buf, uint32_t len)
      43                 :          0 : {
      44         [ #  # ]:          0 :     POSIX_BAIL(S2N_ERR_IO);
      45                 :          0 : }
      46                 :            : 
      47                 :            : static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
      48                 :         37 : {
      49 [ #  # ][ -  + ]:         37 :     RESULT_ENSURE_REF(conn);
      50                 :         37 :     const struct s2n_config *config = conn->config;
      51 [ #  # ][ -  + ]:         37 :     RESULT_ENSURE_REF(config);
      52                 :            : 
      53 [ -  + ][ #  # ]:         37 :     RESULT_ENSURE(s2n_ktls_is_supported_on_platform(), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM);
      54                 :            : 
      55                 :            :     /* kTLS enable should only be called once the handshake has completed. */
      56 [ +  + ][ +  - ]:         37 :     RESULT_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE);
      57                 :            : 
      58                 :            :     /* kTLS uses the prf_space to recalculate the keys, but the prf_space may be
      59                 :            :      * freed by s2n_connection_free_handshake to reduce the connection size.
      60                 :            :      * Explicitly check for prf_space here to avoid a confusing S2N_ERR_NULL later.
      61                 :            :      */
      62 [ -  + ][ #  # ]:         35 :     RESULT_ENSURE(conn->prf_space, S2N_ERR_INVALID_STATE);
      63                 :            : 
      64                 :            :     /* For now, only allow TlS1.3 if explicitly enabled.
      65                 :            :      *
      66                 :            :      * TLS1.3 is potentially more dangerous to enable than TLS1.2, since the kernel
      67                 :            :      * does not currently support updating TLS keys and therefore will fail if
      68                 :            :      * KeyUpdate messages are encountered.
      69                 :            :      */
      70         [ +  + ]:         35 :     bool version_supported = (conn->actual_protocol_version == S2N_TLS12)
      71 [ +  + ][ +  - ]:         35 :             || (conn->config->ktls_tls13_enabled && conn->actual_protocol_version == S2N_TLS13);
      72 [ +  - ][ +  + ]:         35 :     RESULT_ENSURE(version_supported, S2N_ERR_KTLS_UNSUPPORTED_CONN);
      73                 :            : 
      74                 :            :     /* Check if the cipher supports kTLS */
      75                 :         32 :     const struct s2n_cipher *cipher = NULL;
      76         [ -  + ]:         32 :     RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher));
      77 [ -  + ][ #  # ]:         32 :     RESULT_ENSURE_REF(cipher);
      78 [ +  - ][ +  + ]:         32 :     RESULT_ENSURE(cipher->set_ktls_info, S2N_ERR_KTLS_UNSUPPORTED_CONN);
      79                 :            : 
      80                 :            :     /* Renegotiation requires updating the keys, which kTLS doesn't currently support.
      81                 :            :      *
      82                 :            :      * Setting the renegotiation callback doesn't guarantee that a client will
      83                 :            :      * attempt to renegotiate. The callback can also be used to send warning alerts
      84                 :            :      * signaling that renegotiation was rejected. However, we can provide applications
      85                 :            :      * with a clearer signal earlier by preventing them from enabling ktls on a
      86                 :            :      * connection that MIGHT require renegotiation. We can relax this restriction
      87                 :            :      * later if necessary.
      88                 :            :      */
      89                 :         30 :     bool may_receive_hello_request = s2n_result_is_ok(s2n_client_hello_request_validate(conn));
      90 [ +  + ][ +  + ]:         30 :     bool may_renegotiate = may_receive_hello_request && config->renegotiate_request_cb;
      91 [ +  - ][ +  + ]:         30 :     RESULT_ENSURE(!may_renegotiate, S2N_ERR_KTLS_RENEG);
      92                 :            : 
      93                 :            :     /* Prevent kTLS from being enabled on connections that might be serialized.
      94                 :            :      *
      95                 :            :      * The socket takes over tracking sequence numbers when kTLS is enabled.
      96                 :            :      * We would need to call getsockopt to retrieve the current sequence numbers for
      97                 :            :      * serialization. This would complicate the serialization implementation so
      98                 :            :      * for now, do not support kTLS with serialization.
      99                 :            :      */
     100 [ +  + ][ +  - ]:         29 :     RESULT_ENSURE(config->serialized_connection_version == S2N_SERIALIZED_CONN_NONE,
     101                 :         28 :             S2N_ERR_KTLS_UNSUPPORTED_CONN);
     102                 :            : 
     103                 :            :     /* kTLS I/O functionality is managed by s2n-tls. kTLS cannot be enabled if the
     104                 :            :      * application sets custom I/O (managed_send_io == false means application has
     105                 :            :      * set custom I/O).
     106                 :            :      */
     107                 :         28 :     switch (ktls_mode) {
     108         [ +  + ]:         12 :         case S2N_KTLS_MODE_SEND:
     109 [ +  + ][ +  - ]:         12 :             RESULT_ENSURE(conn->managed_send_io, S2N_ERR_KTLS_MANAGED_IO);
     110                 :            :             /* The output stuffer should be empty before enabling kTLS. */
     111 [ +  + ][ +  - ]:         11 :             RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->out), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING);
     112                 :         10 :             break;
     113         [ +  + ]:         16 :         case S2N_KTLS_MODE_RECV:
     114 [ +  + ][ +  - ]:         16 :             RESULT_ENSURE(conn->managed_recv_io, S2N_ERR_KTLS_MANAGED_IO);
     115                 :            :             /* The input stuffers should be empty before enabling kTLS. */
     116 [ +  - ][ +  + ]:         15 :             RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->header_in), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING);
     117 [ +  + ][ +  - ]:         14 :             RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->in), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING);
     118 [ +  + ][ +  - ]:         13 :             RESULT_ENSURE(s2n_stuffer_is_consumed(&conn->buffer_in), S2N_ERR_KTLS_UNSUPPORTED_CONN);
     119                 :         12 :             break;
     120         [ -  + ]:         12 :         default:
     121         [ #  # ]:          0 :             RESULT_BAIL(S2N_ERR_SAFETY);
     122                 :          0 :             break;
     123                 :         28 :     }
     124                 :            : 
     125                 :         22 :     return S2N_RESULT_OK;
     126                 :         28 : }
     127                 :            : 
     128                 :            : /* Enabling kTLS preserves the original *io_context; making this functions
     129                 :            :  * safe to call even after kTLS has been enabled on the connection.
     130                 :            :  *
     131                 :            :  * Retrieving fd assumes that the connection is using socket IO and has the
     132                 :            :  * send_io_context set. While kTLS overrides IO and essentially disables
     133                 :            :  * the socket conn->send function callback, it doesn't modify the
     134                 :            :  * send_io_context. */
     135                 :            : S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd)
     136                 :       1506 : {
     137 [ -  + ][ #  # ]:       1506 :     RESULT_ENSURE_REF(conn);
     138 [ -  + ][ #  # ]:       1506 :     RESULT_ENSURE_REF(fd);
     139                 :            : 
     140         [ +  + ]:       1506 :     if (ktls_mode == S2N_KTLS_MODE_RECV) {
     141         [ -  + ]:         12 :         RESULT_GUARD_POSIX(s2n_connection_get_read_fd(conn, fd));
     142         [ +  - ]:       1494 :     } else if (ktls_mode == S2N_KTLS_MODE_SEND) {
     143         [ -  + ]:       1494 :         RESULT_GUARD_POSIX(s2n_connection_get_write_fd(conn, fd));
     144                 :       1494 :     }
     145                 :       1506 :     return S2N_RESULT_OK;
     146                 :       1506 : }
     147                 :            : 
     148                 :            : static S2N_RESULT s2n_ktls_get_io_mode(s2n_ktls_mode ktls_mode, int *tls_tx_rx_mode)
     149                 :         22 : {
     150 [ -  + ][ #  # ]:         22 :     RESULT_ENSURE_REF(tls_tx_rx_mode);
     151                 :            : 
     152         [ +  + ]:         22 :     if (ktls_mode == S2N_KTLS_MODE_SEND) {
     153                 :         10 :         *tls_tx_rx_mode = S2N_TLS_TX;
     154                 :         12 :     } else {
     155                 :         12 :         *tls_tx_rx_mode = S2N_TLS_RX;
     156                 :         12 :     }
     157                 :         22 :     return S2N_RESULT_OK;
     158                 :         22 : }
     159                 :            : 
     160                 :            : static S2N_RESULT s2n_ktls_crypto_info_init(struct s2n_connection *conn, s2n_ktls_mode ktls_mode,
     161                 :            :         struct s2n_ktls_crypto_info *crypto_info)
     162                 :         22 : {
     163 [ -  + ][ #  # ]:         22 :     RESULT_ENSURE_REF(conn);
     164                 :         22 :     struct s2n_crypto_parameters *secure = conn->secure;
     165 [ -  + ][ #  # ]:         22 :     RESULT_ENSURE_REF(secure);
     166                 :            : 
     167                 :            :     /* In order to avoid storing the encryption keys on the connection, we instead
     168                 :            :      * regenerate them when required by ktls.
     169                 :            :      *
     170                 :            :      * s2n_key_material also includes an IV, but we should use the IV stored
     171                 :            :      * on the connection instead. Some record algorithms (like CBC) mutate the
     172                 :            :      * "implicit_iv" when writing records, so the IV may change after generation.
     173                 :            :      */
     174                 :         22 :     struct s2n_key_material key_material = { 0 };
     175                 :            : 
     176                 :         22 :     bool is_sending_key = (ktls_mode == S2N_KTLS_MODE_SEND);
     177         [ +  + ]:         22 :     s2n_mode key_mode = (is_sending_key) ? conn->mode : S2N_PEER_MODE(conn->mode);
     178                 :            : 
     179                 :         22 :     switch (conn->actual_protocol_version) {
     180         [ +  + ]:         21 :         case S2N_TLS12:
     181         [ -  + ]:         21 :             RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material));
     182                 :         21 :             break;
     183         [ +  + ]:         21 :         case S2N_TLS13:
     184         [ -  + ]:          1 :             RESULT_GUARD(s2n_tls13_key_schedule_generate_key_material(
     185                 :          1 :                     conn, key_mode, &key_material));
     186                 :          1 :             break;
     187         [ -  + ]:          1 :         default:
     188         [ #  # ]:          0 :             RESULT_BAIL(S2N_ERR_KTLS_UNSUPPORTED_CONN);
     189                 :         22 :     }
     190                 :            : 
     191                 :         22 :     struct s2n_ktls_crypto_info_inputs inputs = { 0 };
     192         [ +  + ]:         22 :     if (key_mode == S2N_CLIENT) {
     193                 :         11 :         inputs.key = key_material.client_key;
     194         [ -  + ]:         11 :         RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv,
     195                 :         11 :                 secure->client_implicit_iv, sizeof(secure->client_implicit_iv)));
     196                 :         11 :     } else {
     197                 :         11 :         inputs.key = key_material.server_key;
     198         [ -  + ]:         11 :         RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv,
     199                 :         11 :                 secure->server_implicit_iv, sizeof(secure->server_implicit_iv)));
     200                 :         11 :     }
     201         [ -  + ]:         22 :     RESULT_GUARD(s2n_connection_get_sequence_number(conn, key_mode, &inputs.seq));
     202                 :            : 
     203                 :         22 :     const struct s2n_cipher *cipher = NULL;
     204         [ -  + ]:         22 :     RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher));
     205 [ -  + ][ #  # ]:         22 :     RESULT_ENSURE_REF(cipher);
     206 [ #  # ][ -  + ]:         22 :     RESULT_ENSURE_REF(cipher->set_ktls_info);
     207         [ -  + ]:         22 :     RESULT_GUARD(cipher->set_ktls_info(&inputs, crypto_info));
     208                 :         22 :     return S2N_RESULT_OK;
     209                 :         22 : }
     210                 :            : 
     211                 :            : /* This method intentionally returns void because it may NOT perform any fallible
     212                 :            :  * operations. See s2n_connection_ktls_enable.
     213                 :            :  */
     214                 :            : void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
     215                 :         41 : {
     216         [ -  + ]:         41 :     if (conn == NULL) {
     217                 :          0 :         return;
     218                 :          0 :     }
     219         [ +  + ]:         41 :     if (ktls_mode == S2N_KTLS_MODE_SEND) {
     220                 :         16 :         conn->ktls_send_enabled = true;
     221                 :         16 :         conn->send = s2n_ktls_send_cb;
     222                 :         25 :     } else {
     223                 :         25 :         conn->ktls_recv_enabled = true;
     224                 :         25 :         conn->recv = s2n_ktls_disabled_read;
     225                 :         25 :     }
     226                 :         41 : }
     227                 :            : 
     228                 :            : static S2N_RESULT s2n_connection_ktls_enable(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
     229                 :         37 : {
     230 [ #  # ][ -  + ]:         37 :     RESULT_ENSURE_REF(conn);
     231         [ +  + ]:         37 :     RESULT_GUARD(s2n_ktls_validate(conn, ktls_mode));
     232                 :            : 
     233                 :         22 :     int fd = 0;
     234         [ -  + ]:         22 :     RESULT_GUARD(s2n_ktls_get_file_descriptor(conn, ktls_mode, &fd));
     235                 :            : 
     236                 :            :     /* This call doesn't actually enable ktls or modify the IO behavior of the socket.
     237                 :            :      * Instead, this is just a prerequisite for calling setsockopt with SOL_TLS.
     238                 :            :      *
     239                 :            :      * We intentionally ignore the result of this call. It may fail because ktls
     240                 :            :      * is not supported, but it might also fail because ktls has already been enabled
     241                 :            :      * for the socket. If SOL_TLS isn't enabled on the socket, our next call to
     242                 :            :      * setsockopt with SOL_TLS will also fail, and we DO check that result.
     243                 :            :      */
     244                 :         22 :     s2n_setsockopt(fd, S2N_SOL_TCP, S2N_TCP_ULP, S2N_TLS_ULP_NAME, S2N_TLS_ULP_NAME_SIZE);
     245                 :            : 
     246                 :         22 :     int tls_tx_rx_mode = 0;
     247         [ -  + ]:         22 :     RESULT_GUARD(s2n_ktls_get_io_mode(ktls_mode, &tls_tx_rx_mode));
     248                 :            : 
     249                 :         22 :     struct s2n_ktls_crypto_info crypto_info = { 0 };
     250         [ -  + ]:         22 :     RESULT_GUARD(s2n_ktls_crypto_info_init(conn, ktls_mode, &crypto_info));
     251                 :            : 
     252                 :            :     /* If this call succeeds, then ktls is enabled for that io mode and will be offloaded */
     253                 :         22 :     int ret = s2n_setsockopt(fd, S2N_SOL_TLS, tls_tx_rx_mode, crypto_info.value.data, crypto_info.value.size);
     254 [ +  - ][ +  + ]:         22 :     RESULT_ENSURE(ret == 0, S2N_ERR_KTLS_ENABLE);
     255                 :            : 
     256                 :            :     /* At this point, ktls is enabled on the socket for the requested IO mode.
     257                 :            :      * No further fallible operations may be performed, or else the caller may
     258                 :            :      * incorrectly assume that enabling ktls failed and they should therefore
     259                 :            :      * fall back to using application layer TLS.
     260                 :            :      *
     261                 :            :      * That means no calls to RESULT_ENSURE, RESULT_GUARD, etc. after this point.
     262                 :            :      */
     263                 :            : 
     264                 :         18 :     s2n_ktls_configure_connection(conn, ktls_mode);
     265                 :         18 :     return S2N_RESULT_OK;
     266                 :         22 : }
     267                 :            : 
     268                 :            : int s2n_connection_ktls_enable_send(struct s2n_connection *conn)
     269                 :         18 : {
     270 [ -  + ][ #  # ]:         18 :     POSIX_ENSURE_REF(conn);
     271                 :            : 
     272                 :            :     /* If already enabled then return success */
     273         [ +  + ]:         18 :     if (conn->ktls_send_enabled) {
     274                 :          1 :         return S2N_SUCCESS;
     275                 :          1 :     }
     276                 :            : 
     277         [ +  + ]:         17 :     POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_SEND));
     278                 :          8 :     return S2N_SUCCESS;
     279                 :         17 : }
     280                 :            : 
     281                 :            : int s2n_connection_ktls_enable_recv(struct s2n_connection *conn)
     282                 :         21 : {
     283 [ #  # ][ -  + ]:         21 :     POSIX_ENSURE_REF(conn);
     284                 :            : 
     285                 :            :     /* If already enabled then return success */
     286         [ +  + ]:         21 :     if (conn->ktls_recv_enabled) {
     287                 :          1 :         return S2N_SUCCESS;
     288                 :          1 :     }
     289                 :            : 
     290         [ +  + ]:         20 :     POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_RECV));
     291                 :         10 :     return S2N_SUCCESS;
     292                 :         20 : }
     293                 :            : 
     294                 :            : int s2n_config_ktls_enable_unsafe_tls13(struct s2n_config *config)
     295                 :          5 : {
     296 [ +  + ][ +  - ]:          5 :     POSIX_ENSURE_REF(config);
     297                 :          4 :     config->ktls_tls13_enabled = true;
     298                 :          4 :     return S2N_SUCCESS;
     299                 :          5 : }

Generated by: LCOV version 1.14