Skip to content

Commit

Permalink
ktls: set keys on socket and enable ktls (aws#4071)
Browse files Browse the repository at this point in the history
  • Loading branch information
toidiu committed Jul 26, 2023
1 parent 403d5e6 commit b0b253e
Show file tree
Hide file tree
Showing 10 changed files with 470 additions and 73 deletions.
1 change: 1 addition & 0 deletions error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM, "kTLS is unsupported on this platform") \
ERR_ENTRY(S2N_ERR_KTLS_UNSUPPORTED_CONN, "kTLS is unsupported for this connection") \
ERR_ENTRY(S2N_ERR_KTLS_ULP, "An error occurred when attempting to configure the socket for kTLS. Ensure the 'tls' kernel module is enabled.") \
ERR_ENTRY(S2N_ERR_KTLS_ENABLE_CRYPTO, "An error occurred when attempting to enable kTLS on socket.") \
ERR_ENTRY(S2N_ERR_ATOMIC, "Atomic operations in this environment would require locking") \
/* clang-format on */

Expand Down
1 change: 1 addition & 0 deletions error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ typedef enum {
S2N_ERR_KTLS_UNSUPPORTED_PLATFORM,
S2N_ERR_KTLS_UNSUPPORTED_CONN,
S2N_ERR_KTLS_ULP,
S2N_ERR_KTLS_ENABLE_CRYPTO,
S2N_ERR_ATOMIC,
S2N_ERR_T_USAGE_END,
} s2n_error;
Expand Down
30 changes: 30 additions & 0 deletions tests/features/S2N_KTLS_SUPPORTED.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* 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.
*/

/* Gate kTLS support only to Linux. Add other platforms once they have been tested. */
#if defined(__linux__)
#include <linux/tls.h>
#endif

int main()
{
/* Struct defined when kTLS support was added to linux
* https://github.com/torvalds/linux/blob/3c4d7559159bfe1e3b94df3a657b2cda3a34e218/include/uapi/linux/tls.h
*/
struct tls12_crypto_info_aes_gcm_128 aes_crypto_info;
struct tls_crypto_info crypto_info;

return 0;
}
Empty file.
268 changes: 234 additions & 34 deletions tests/unit/s2n_ktls_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,126 @@

#include "tls/s2n_ktls.h"

#include "error/s2n_errno.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "utils/s2n_random.h"
#include "utils/s2n_socket.h"

S2N_RESULT s2n_ktls_retrieve_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd);
#define S2N_TEST_SEND_FD 66
#define S2N_TEST_RECV_FD 55

/* set kTLS supported cipher */
struct s2n_cipher ktls_temp_supported_cipher = {
.ktls_supported = true,
};
struct s2n_record_algorithm ktls_temp_supported_record_alg = {
.cipher = &ktls_temp_supported_cipher,
};
struct s2n_cipher_suite ktls_temp_supported_cipher_suite = {
.record_alg = &ktls_temp_supported_record_alg,
};
#if defined(S2N_KTLS_SUPPORTED)
/* It's difficult to test this method via s2n_connection_ktls_enable_send/recv because
* the key_material is populated via prf, which by definition produces "pseudo-random"
* output */
S2N_RESULT s2n_ktls_init_aes128_gcm_crypto_info(struct s2n_connection *conn, s2n_ktls_mode ktls_mode,
struct s2n_key_material *key_material, struct tls12_crypto_info_aes_gcm_128 *crypto_info);
#endif

static int s2n_test_setsockopt_noop(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
return S2N_SUCCESS;
}

static int s2n_test_setsockopt_eexist_error(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
errno = EEXIST;
return S2N_FAILURE;
}

static int s2n_test_setsockopt_error(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
errno = EINVAL;
return S2N_FAILURE;
}

static int s2n_test_setsockopt_tx(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
POSIX_ENSURE_EQ(fd, S2N_TEST_SEND_FD);

if (level == S2N_SOL_TLS) {
POSIX_ENSURE_EQ(optname, S2N_TLS_TX);
#if defined(S2N_KTLS_SUPPORTED)
POSIX_ENSURE_EQ(optlen, sizeof(struct tls12_crypto_info_aes_gcm_128));
#endif
} else if (level == S2N_SOL_TCP) {
POSIX_ENSURE_EQ(optname, S2N_TCP_ULP);
POSIX_ENSURE_EQ(optlen, S2N_TLS_ULP_NAME_SIZE);
} else {
POSIX_BAIL(S2N_ERR_SAFETY);
}
return S2N_SUCCESS;
}

static int s2n_test_setsockopt_rx(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
POSIX_ENSURE_EQ(fd, S2N_TEST_RECV_FD);

if (level == S2N_SOL_TLS) {
POSIX_ENSURE_EQ(optname, S2N_TLS_RX);
#if defined(S2N_KTLS_SUPPORTED)
POSIX_ENSURE_EQ(optlen, sizeof(struct tls12_crypto_info_aes_gcm_128));
#endif
} else if (level == S2N_SOL_TCP) {
POSIX_ENSURE_EQ(optname, S2N_TCP_ULP);
POSIX_ENSURE_EQ(optlen, S2N_TLS_ULP_NAME_SIZE);
} else {
POSIX_BAIL(S2N_ERR_SAFETY);
}
return S2N_SUCCESS;
}

S2N_RESULT s2n_test_configure_connection_for_ktls(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);

RESULT_GUARD(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_noop));

/* config I/O */
RESULT_GUARD_POSIX(s2n_connection_set_write_fd(conn, 1));
RESULT_GUARD_POSIX(s2n_connection_set_read_fd(conn, 1));
RESULT_GUARD_POSIX(s2n_connection_set_write_fd(conn, S2N_TEST_SEND_FD));
RESULT_GUARD_POSIX(s2n_connection_set_read_fd(conn, S2N_TEST_RECV_FD));
conn->ktls_send_enabled = false;
conn->ktls_recv_enabled = false;

/* configure connection so that the handshake is complete */
conn->secure->cipher_suite = &ktls_temp_supported_cipher_suite;
/* set kTLS supported cipher */
conn->secure->cipher_suite = &s2n_rsa_with_aes_128_gcm_sha256;
conn->actual_protocol_version = S2N_TLS12;
/* configure connection so that the handshake is complete */
RESULT_GUARD(s2n_skip_handshake(conn));

return S2N_RESULT_OK;
}

S2N_RESULT s2n_test_generate_fake_crypto_params(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);

struct s2n_blob test_data_blob = { 0 };

struct s2n_crypto_parameters *server_param = conn->server;
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, server_param->server_implicit_iv, sizeof(server_param->server_implicit_iv)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, server_param->server_sequence_number, sizeof(server_param->server_sequence_number)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, server_param->client_implicit_iv, sizeof(server_param->client_implicit_iv)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, server_param->client_sequence_number, sizeof(server_param->client_sequence_number)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));

struct s2n_crypto_parameters *client_param = conn->client;
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, client_param->server_implicit_iv, sizeof(client_param->server_implicit_iv)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, client_param->server_sequence_number, sizeof(client_param->server_sequence_number)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, client_param->client_implicit_iv, sizeof(client_param->client_implicit_iv)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));
RESULT_GUARD_POSIX(s2n_blob_init(&test_data_blob, client_param->client_sequence_number, sizeof(client_param->client_sequence_number)));
RESULT_GUARD(s2n_get_public_random_data(&test_data_blob));

return S2N_RESULT_OK;
}

int main(int argc, char **argv)
{
BEGIN_TEST();
Expand Down Expand Up @@ -82,37 +168,47 @@ int main(int argc, char **argv)
EXPECT_FALSE(cipher.ktls_supported);
};

/* Test s2n_ktls_retrieve_file_descriptor */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
int write_fd_orig = 1;
int read_fd_orig = 2;
int fd_ret = 0;

EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, write_fd_orig));
EXPECT_OK(s2n_ktls_retrieve_file_descriptor(server_conn, S2N_KTLS_MODE_SEND, &fd_ret));
EXPECT_EQUAL(write_fd_orig, fd_ret);

EXPECT_SUCCESS(s2n_connection_set_read_fd(server_conn, read_fd_orig));
EXPECT_OK(s2n_ktls_retrieve_file_descriptor(server_conn, S2N_KTLS_MODE_RECV, &fd_ret));
EXPECT_EQUAL(read_fd_orig, fd_ret);
};

/* Test s2n_connection_ktls_enable_recv/send */
{
/* Success case */
/* enable TX/RX */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn));

EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_tx));
EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn));
EXPECT_TRUE(server_conn->ktls_send_enabled);

EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_rx));
EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn));
EXPECT_TRUE(server_conn->ktls_recv_enabled);
};

/* handle setsockopt error */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn));
EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_eexist_error));

/* do expect an error when trying to set keys on the socket */
EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_ENABLE_CRYPTO);
EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_ENABLE_CRYPTO);
};

/* handle setsockopt EEXIST error from TCP_ULP call */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn));
EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_error));

/* do expect an error when trying to set keys on the socket */
EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_ULP);
EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_ULP);
};

/* Noop if kTLS is already enabled */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
Expand Down Expand Up @@ -224,7 +320,111 @@ int main(int argc, char **argv)
server_conn->managed_recv_io = true;
EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn));
};
}
};

/* Test s2n_ktls_init_aes128_gcm_crypto_info */
{
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn));

/* prepare test data */
uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 };
struct s2n_blob test_data_blob = { 0 };
EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data)));
EXPECT_OK(s2n_get_public_random_data(&test_data_blob));

/* copy test data to key_material */
struct s2n_key_material key_material = { 0 };
EXPECT_OK(s2n_key_material_init(&key_material, server_conn));
POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, s2n_array_len(key_material.key_block));

#if defined(S2N_KTLS_SUPPORTED)
struct tls12_crypto_info_aes_gcm_128 crypto_info = { 0 };

/* generate test data for crypto params: implicit_iv and sequence_number */
EXPECT_OK(s2n_test_generate_fake_crypto_params(server_conn));
struct s2n_crypto_parameters *server_param = server_conn->server;

/* Test crypto_info for happy case */
{
/* server should send with its own keys */
int ktls_mode = S2N_KTLS_MODE_SEND;
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info));
EXPECT_EQUAL(memcmp(crypto_info.key, key_material.server_key.data, key_material.server_key.size), 0);
EXPECT_EQUAL(memcmp(crypto_info.iv, server_param->server_implicit_iv, TLS_CIPHER_AES_GCM_128_IV_SIZE), 0);
EXPECT_EQUAL(memcmp(crypto_info.salt, server_param->server_implicit_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE), 0);
EXPECT_EQUAL(memcmp(crypto_info.rec_seq, server_param->server_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE), 0);

/* server should recv with its peer's keys */
ktls_mode = S2N_KTLS_MODE_RECV;
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info));
EXPECT_EQUAL(memcmp(crypto_info.key, key_material.client_key.data, key_material.client_key.size), 0);
EXPECT_EQUAL(memcmp(crypto_info.iv, server_param->client_implicit_iv, TLS_CIPHER_AES_GCM_128_IV_SIZE), 0);
EXPECT_EQUAL(memcmp(crypto_info.salt, server_param->client_implicit_iv, TLS_CIPHER_AES_GCM_128_SALT_SIZE), 0);
EXPECT_EQUAL(memcmp(crypto_info.rec_seq, server_param->client_sequence_number, TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE), 0);

EXPECT_EQUAL(crypto_info.info.cipher_type, TLS_CIPHER_AES_GCM_128);
EXPECT_EQUAL(crypto_info.info.version, TLS_1_2_VERSION);
};

/* Test crypto_info for invalid cases */
{
int ktls_mode = S2N_KTLS_MODE_SEND;
server_conn->actual_protocol_version = S2N_TLS13;
EXPECT_ERROR_WITH_ERRNO(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info),
S2N_ERR_KTLS_UNSUPPORTED_CONN);

/* restore protocol version */
server_conn->actual_protocol_version = S2N_TLS12;
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info));

server_conn->secure->cipher_suite = &s2n_rsa_with_aes_256_gcm_sha384;
EXPECT_ERROR_WITH_ERRNO(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info),
S2N_ERR_KTLS_UNSUPPORTED_CONN);
};
#endif
};

/* selftalk: Success case with a real TLS1.2 negotiated server and client */
{
DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key,
s2n_cert_chain_and_key_ptr_free);
DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(),
s2n_config_ptr_free);

/* setup config */
EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key,
S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY));
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key));
EXPECT_SUCCESS(s2n_config_disable_x509_verification(config));
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210"));
EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_noop));
EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));
EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

/* setup IO and negotiate */
DEFER_CLEANUP(struct s2n_test_io_pair test_io_pair = { 0 },
s2n_io_pair_close);
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&test_io_pair));
EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &test_io_pair));
EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);

/* enable kTLS send */
EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn));
EXPECT_TRUE(server_conn->ktls_send_enabled);
EXPECT_NOT_EQUAL(server_conn->send, s2n_socket_write);

/* enable kTLS recv */
EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn));
EXPECT_TRUE(server_conn->ktls_recv_enabled);
EXPECT_NOT_EQUAL(server_conn->recv, s2n_socket_read);
};

END_TEST();
}
Loading

0 comments on commit b0b253e

Please sign in to comment.