/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0 * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include "tls/s2n_ktls.h" #include "crypto/s2n_ktls_crypto.h" #include "tls/s2n_prf.h" #include "tls/s2n_tls.h" /* Used for overriding setsockopt calls in testing */ s2n_setsockopt_fn s2n_setsockopt = setsockopt; S2N_RESULT s2n_ktls_set_setsockopt_cb(s2n_setsockopt_fn cb) { RESULT_ENSURE(s2n_in_test(), S2N_ERR_NOT_IN_TEST); s2n_setsockopt = cb; return S2N_RESULT_OK; } bool s2n_ktls_is_supported_on_platform() { #if defined(S2N_KTLS_SUPPORTED) return true; #else return false; #endif } static int s2n_ktls_disabled_read(void *io_context, uint8_t *buf, uint32_t len) { POSIX_BAIL(S2N_ERR_IO); } static S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) { RESULT_ENSURE_REF(conn); const struct s2n_config *config = conn->config; RESULT_ENSURE_REF(config); RESULT_ENSURE(s2n_ktls_is_supported_on_platform(), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); /* kTLS enable should only be called once the handshake has completed. */ RESULT_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); /* TODO support TLS 1.3 * * TLS 1.3 support requires sending the KeyUpdate message when the cryptographic * key usage limits are met. However, this is currently only possible by applying a * kernel patch to support this functionality. */ RESULT_ENSURE(conn->actual_protocol_version == S2N_TLS12, S2N_ERR_KTLS_UNSUPPORTED_CONN); /* Check if the cipher supports kTLS */ const struct s2n_cipher *cipher = NULL; RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher)); RESULT_ENSURE_REF(cipher); RESULT_ENSURE(cipher->set_ktls_info, S2N_ERR_KTLS_UNSUPPORTED_CONN); /* Renegotiation requires updating the keys, which kTLS doesn't currently support. * * Setting the renegotiation callback doesn't guarantee that a client will * attempt to renegotiate. The callback can also be used to send warning alerts * signaling that renegotiation was rejected. However, we can provide applications * with a clearer signal earlier by preventing them from enabling ktls on a * connection that MIGHT require renegotiation. We can relax this restriction * later if necessary. */ bool may_receive_hello_request = s2n_result_is_ok(s2n_client_hello_request_validate(conn)); bool may_renegotiate = may_receive_hello_request && config->renegotiate_request_cb; RESULT_ENSURE(!may_renegotiate, S2N_ERR_KTLS_RENEG); /* kTLS I/O functionality is managed by s2n-tls. kTLS cannot be enabled if the * application sets custom I/O (managed_send_io == false means application has * set custom I/O). */ switch (ktls_mode) { case S2N_KTLS_MODE_SEND: RESULT_ENSURE(conn->managed_send_io, S2N_ERR_KTLS_MANAGED_IO); /* The output stuffer should be empty before enabling kTLS. */ RESULT_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); break; case S2N_KTLS_MODE_RECV: RESULT_ENSURE(conn->managed_recv_io, S2N_ERR_KTLS_MANAGED_IO); /* The input stuffer should be empty before enabling kTLS. */ RESULT_ENSURE(s2n_stuffer_data_available(&conn->in) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); break; default: RESULT_BAIL(S2N_ERR_SAFETY); break; } return S2N_RESULT_OK; } /* Enabling kTLS preserves the original *io_context; making this functions * safe to call even after kTLS has been enabled on the connection. * * Retrieving fd assumes that the connection is using socket IO and has the * send_io_context set. While kTLS overrides IO and essentially disables * the socket conn->send function callback, it doesn't modify the * send_io_context. */ S2N_RESULT s2n_ktls_get_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd) { RESULT_ENSURE_REF(conn); RESULT_ENSURE_REF(fd); if (ktls_mode == S2N_KTLS_MODE_RECV) { RESULT_GUARD_POSIX(s2n_connection_get_read_fd(conn, fd)); } else if (ktls_mode == S2N_KTLS_MODE_SEND) { RESULT_GUARD_POSIX(s2n_connection_get_write_fd(conn, fd)); } return S2N_RESULT_OK; } static S2N_RESULT s2n_ktls_get_io_mode(s2n_ktls_mode ktls_mode, int *tls_tx_rx_mode) { RESULT_ENSURE_REF(tls_tx_rx_mode); if (ktls_mode == S2N_KTLS_MODE_SEND) { *tls_tx_rx_mode = S2N_TLS_TX; } else { *tls_tx_rx_mode = S2N_TLS_RX; } return S2N_RESULT_OK; } static S2N_RESULT s2n_ktls_crypto_info_init(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, struct s2n_ktls_crypto_info *crypto_info) { RESULT_ENSURE_REF(conn); struct s2n_crypto_parameters *secure = conn->secure; RESULT_ENSURE_REF(secure); /* In order to avoid storing the encryption keys on the connection, we instead * regenerate them when required by ktls. * * s2n_key_material also includes an IV, but we should use the IV stored * on the connection instead. Some record algorithms (like CBC) mutate the * "implicit_iv" when writing records, so the IV may change after generation. */ struct s2n_key_material key_material = { 0 }; RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material)); bool is_sending_key = (ktls_mode == S2N_KTLS_MODE_SEND); s2n_mode key_mode = (is_sending_key) ? conn->mode : S2N_PEER_MODE(conn->mode); struct s2n_ktls_crypto_info_inputs inputs = { 0 }; if (key_mode == S2N_CLIENT) { inputs.key = key_material.client_key; RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv, secure->client_implicit_iv, sizeof(secure->client_implicit_iv))); RESULT_GUARD_POSIX(s2n_blob_init(&inputs.seq, secure->client_sequence_number, sizeof(secure->client_sequence_number))); } else { inputs.key = key_material.server_key; RESULT_GUARD_POSIX(s2n_blob_init(&inputs.iv, secure->server_implicit_iv, sizeof(secure->server_implicit_iv))); RESULT_GUARD_POSIX(s2n_blob_init(&inputs.seq, secure->server_sequence_number, sizeof(secure->server_sequence_number))); } const struct s2n_cipher *cipher = NULL; RESULT_GUARD(s2n_connection_get_secure_cipher(conn, &cipher)); RESULT_ENSURE_REF(cipher); RESULT_ENSURE_REF(cipher->set_ktls_info); RESULT_GUARD(cipher->set_ktls_info(&inputs, crypto_info)); return S2N_RESULT_OK; } /* This method intentionally returns void because it may NOT perform any fallible * operations. See s2n_connection_ktls_enable. */ void s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) { if (conn == NULL) { return; } if (ktls_mode == S2N_KTLS_MODE_SEND) { conn->ktls_send_enabled = true; conn->send = s2n_ktls_send_cb; } else { conn->ktls_recv_enabled = true; conn->recv = s2n_ktls_disabled_read; } } static S2N_RESULT s2n_connection_ktls_enable(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) { RESULT_ENSURE_REF(conn); RESULT_GUARD(s2n_ktls_validate(conn, ktls_mode)); int fd = 0; RESULT_GUARD(s2n_ktls_get_file_descriptor(conn, ktls_mode, &fd)); /* This call doesn't actually enable ktls or modify the IO behavior of the socket. * Instead, this is just a prerequisite for calling setsockopt with SOL_TLS. * * We intentionally ignore the result of this call. It may fail because ktls * is not supported, but it might also fail because ktls has already been enabled * for the socket. If SOL_TLS isn't enabled on the socket, our next call to * setsockopt with SOL_TLS will also fail, and we DO check that result. */ s2n_setsockopt(fd, S2N_SOL_TCP, S2N_TCP_ULP, S2N_TLS_ULP_NAME, S2N_TLS_ULP_NAME_SIZE); int tls_tx_rx_mode = 0; RESULT_GUARD(s2n_ktls_get_io_mode(ktls_mode, &tls_tx_rx_mode)); struct s2n_ktls_crypto_info crypto_info = { 0 }; RESULT_GUARD(s2n_ktls_crypto_info_init(conn, ktls_mode, &crypto_info)); /* If this call succeeds, then ktls is enabled for that io mode and will be offloaded */ int ret = s2n_setsockopt(fd, S2N_SOL_TLS, tls_tx_rx_mode, crypto_info.value.data, crypto_info.value.size); RESULT_ENSURE(ret == 0, S2N_ERR_KTLS_ENABLE); /* At this point, ktls is enabled on the socket for the requested IO mode. * No further fallible operations may be performed, or else the caller may * incorrectly assume that enabling ktls failed and they should therefore * fall back to using application layer TLS. * * That means no calls to RESULT_ENSURE, RESULT_GUARD, etc. after this point. */ s2n_ktls_configure_connection(conn, ktls_mode); return S2N_RESULT_OK; } int s2n_connection_ktls_enable_send(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); /* If already enabled then return success */ if (conn->ktls_send_enabled) { return S2N_SUCCESS; } POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_SEND)); return S2N_SUCCESS; } int s2n_connection_ktls_enable_recv(struct s2n_connection *conn) { POSIX_ENSURE_REF(conn); /* If already enabled then return success */ if (conn->ktls_recv_enabled) { return S2N_SUCCESS; } POSIX_GUARD_RESULT(s2n_connection_ktls_enable(conn, S2N_KTLS_MODE_RECV)); return S2N_SUCCESS; }