Skip to content

Commit

Permalink
set keys
Browse files Browse the repository at this point in the history
  • Loading branch information
toidiu committed Jun 23, 2023
1 parent 607a74b commit b1d343f
Show file tree
Hide file tree
Showing 8 changed files with 361 additions and 33 deletions.
1 change: 1 addition & 0 deletions error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,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_KTLS_DISABLED_FOR_TEST, "Some kTLS operations have been disabled for testing") \
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 @@ -312,6 +312,7 @@ typedef enum {
S2N_ERR_KTLS_UNSUPPORTED_PLATFORM,
S2N_ERR_KTLS_UNSUPPORTED_CONN,
S2N_ERR_KTLS_ULP,
S2N_ERR_KTLS_ENABLE_CRYPTO,
S2N_ERR_KTLS_DISABLED_FOR_TEST,
S2N_ERR_ATOMIC,
S2N_ERR_T_USAGE_END,
Expand Down
155 changes: 155 additions & 0 deletions tests/unit/s2n_ktls_set_keys_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
/*
* 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 "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/s2n_ktls.h"

S2N_RESULT s2n_disable_ktls_socket_config_for_testing(void);
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 s2n_tls12_crypto_info_aes_gcm_128 *crypto_info, int *tls_mode);
S2N_RESULT s2n_ktls_set_keys(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, struct s2n_key_material *key_material);

S2N_RESULT helper_generate_test_data(struct s2n_blob *test_data)
{
struct s2n_stuffer test_data_stuffer = { 0 };
RESULT_GUARD_POSIX(s2n_stuffer_init(&test_data_stuffer, test_data));
for (int i = 0; i < S2N_MAX_KEY_BLOCK_LEN; i++) {
RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&test_data_stuffer, i));
}
return S2N_RESULT_OK;
}

int main(int argc, char **argv)
{
BEGIN_TEST();

EXPECT_OK(s2n_disable_ktls_socket_config_for_testing());

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(helper_generate_test_data(&test_data_blob));

if (s2n_ktls_is_supported_on_platform()) {
/* s2n_ktls_create_aes128_gcm_crypto_info */
{
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, "default"));
EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));
EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

/* setup IO */
struct s2n_test_io_pair io_pair = { 0 };
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);

/* 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));

POSIX_ENSURE_EQ(key_material.client_key.size, S2N_TLS_AES_128_GCM_KEY_LEN);
POSIX_ENSURE_EQ(key_material.server_key.size, S2N_TLS_AES_128_GCM_KEY_LEN);

int tls_tx_rx_mode = 0;
int ktls_mode = S2N_KTLS_MODE_SEND;
struct s2n_tls12_crypto_info_aes_gcm_128 crypto_info = { 0 };

/* server should send with its own keys */
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info, &tls_tx_rx_mode));
EXPECT_EQUAL(memcmp(key_material.server_key.data, crypto_info.key, key_material.server_key.size), 0);
EXPECT_EQUAL(tls_tx_rx_mode, S2N_TLS_TX);
/* client should send with its own keys */
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(client_conn, ktls_mode, &key_material, &crypto_info, &tls_tx_rx_mode));
EXPECT_EQUAL(tls_tx_rx_mode, S2N_TLS_TX);
EXPECT_EQUAL(memcmp(key_material.client_key.data, crypto_info.key, key_material.client_key.size), 0);

ktls_mode = S2N_KTLS_MODE_RECV;
/* server should recv with its peers keys */
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(server_conn, ktls_mode, &key_material, &crypto_info, &tls_tx_rx_mode));
EXPECT_EQUAL(tls_tx_rx_mode, S2N_TLS_RX);
EXPECT_EQUAL(memcmp(key_material.client_key.data, crypto_info.key, key_material.client_key.size), 0);
/* client should recv with its peers keys */
EXPECT_OK(s2n_ktls_init_aes128_gcm_crypto_info(client_conn, ktls_mode, &key_material, &crypto_info, &tls_tx_rx_mode));
EXPECT_EQUAL(tls_tx_rx_mode, S2N_TLS_RX);
EXPECT_EQUAL(memcmp(key_material.server_key.data, crypto_info.key, key_material.server_key.size), 0);

EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
};

/* s2n_ktls_set_keys */
{
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, "default"));
EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config));
EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config));

/* setup IO */
struct s2n_test_io_pair io_pair = { 0 };
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair));

EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12);

/* 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));

/* call set keys with send mode */
int ktls_mode = S2N_KTLS_MODE_SEND;
EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_keys(server_conn, ktls_mode, &key_material), S2N_ERR_KTLS_DISABLED_FOR_TEST);

/* call set keys with recv mode */
ktls_mode = S2N_KTLS_MODE_RECV;
EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_keys(server_conn, ktls_mode, &key_material), S2N_ERR_KTLS_DISABLED_FOR_TEST);

EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
}
}

END_TEST();
}
11 changes: 11 additions & 0 deletions tests/unit/s2n_ktls_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,17 @@
S2N_RESULT s2n_ktls_retrieve_file_descriptor(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, int *fd);
S2N_RESULT s2n_disable_ktls_socket_config_for_testing(void);

/* set kTLS un-supported cipher */
struct s2n_cipher temp_unsupported_cipher = {
.ktls_supported = false,
};
struct s2n_record_algorithm temp_unsupported_record_alg = {
.cipher = &temp_unsupported_cipher,
};
struct s2n_cipher_suite temp_unsupported_cipher_suite = {
.record_alg = &temp_unsupported_record_alg,
};

int main(int argc, char **argv)
{
BEGIN_TEST();
Expand Down
156 changes: 136 additions & 20 deletions tls/s2n_ktls.c
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ bool s2n_ktls_is_supported_on_platform()
#endif
}

S2N_RESULT s2n_ktls_validate_connection(struct s2n_connection *conn)
S2N_RESULT s2n_ktls_validate(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->secure);
Expand All @@ -58,13 +58,6 @@ S2N_RESULT s2n_ktls_validate_connection(struct s2n_connection *conn)
/* Check if the cipher supports kTLS */
RESULT_ENSURE(cipher->ktls_supported, S2N_ERR_KTLS_UNSUPPORTED_CONN);

return S2N_RESULT_OK;
}

S2N_RESULT s2n_ktls_validate_socket_mode(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
{
RESULT_ENSURE_REF(conn);

/* 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).
Expand Down Expand Up @@ -103,6 +96,120 @@ S2N_RESULT s2n_ktls_retrieve_file_descriptor(struct s2n_connection *conn, s2n_kt
return S2N_RESULT_OK;
}

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 s2n_tls12_crypto_info_aes_gcm_128 *crypto_info, int *tls_tx_rx_mode)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->client);
RESULT_ENSURE_REF(conn->server);
RESULT_ENSURE_REF(key_material);
RESULT_ENSURE_REF(crypto_info);
RESULT_ENSURE_REF(tls_tx_rx_mode);

/* TODO once other ciphers and protocols are supported, check that the
* negotiated cipher is AES_128_GCM
*
* This would involve adding a unique identifier to s2n_cipher.
*/

/* set tls mode */
if (ktls_mode == S2N_KTLS_MODE_SEND) { /* TX */
*tls_tx_rx_mode = S2N_TLS_TX;
} else { /* RX */
*tls_tx_rx_mode = S2N_TLS_RX;
}

/* set values based on mode of operation */
uint8_t *implicit_iv, *sequence_number = NULL;
struct s2n_blob *key = NULL;

if ((conn->mode == S2N_SERVER && ktls_mode == S2N_KTLS_MODE_SEND) /* server sending */
|| (conn->mode == S2N_CLIENT && ktls_mode == S2N_KTLS_MODE_RECV) /* client receiving */
) {
/* if server is sending or client is receiving then use server key material */
key = &key_material->server_key;
implicit_iv = conn->server->server_implicit_iv;
sequence_number = conn->server->server_sequence_number;
} else {
key = &key_material->client_key;
implicit_iv = conn->client->client_implicit_iv;
sequence_number = conn->client->client_sequence_number;
}

RESULT_ENSURE_REF(key->data);
RESULT_ENSURE_EQ(key->size, S2N_TLS_CIPHER_AES_GCM_128_KEY_SIZE);

/* set values on crypto_info */
crypto_info->info.cipher_type = S2N_TLS_CIPHER_AES_GCM_128;
crypto_info->info.version = S2N_TLS_1_2_VERSION;
RESULT_CHECKED_MEMCPY(crypto_info->salt, implicit_iv, S2N_TLS_CIPHER_AES_GCM_128_SALT_SIZE);
RESULT_CHECKED_MEMCPY(crypto_info->rec_seq, sequence_number, S2N_TLS_CIPHER_AES_GCM_128_REC_SEQ_SIZE);
RESULT_CHECKED_MEMCPY(crypto_info->key, key->data, S2N_TLS_CIPHER_AES_GCM_128_KEY_SIZE);
RESULT_CHECKED_MEMCPY(crypto_info->iv, implicit_iv, S2N_TLS_CIPHER_AES_GCM_128_IV_SIZE);

/* TODO used for debugging remove before PR */
if (false) {
for (int i = 0; i < 4; i++) {
printf("ktls TX salt---------- %x \n", crypto_info->salt[i]);
}
for (int i = 0; i < 8; i++) {
printf("ktls TX implicit_iv---------- %x \n", crypto_info->iv[i]);
}
for (int i = 0; i < 8; i++) {
printf("ktls TX seq---------- %x \n", crypto_info->rec_seq[i]);
}
for (int i = 0; i < 16; i++) {
printf("ktls TX key---------- %x \n", crypto_info->key[i]);
}
/* ================== */
/* { */
/* printf("\n ktls client ===== \n"); */
/* for (int i = 0; i < client_traffic_key->size; i++) { */
/* printf("%x", client_traffic_key->data[i]); */
/* } */
/* printf("\n ktls ===== \n"); */
/* } */
/* { */
/* printf("\n ktls server ===== \n"); */
/* for (int i = 0; i < server_traffic_key->size; i++) { */
/* printf("%x", server_traffic_key->data[i]); */
/* } */
/* printf("\n ktls server ===== \n"); */
/* } */
}

return S2N_RESULT_OK;
}

S2N_RESULT s2n_ktls_set_keys(struct s2n_connection *conn, s2n_ktls_mode ktls_mode, struct s2n_key_material *key_material)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(key_material);

RESULT_GUARD(s2n_ktls_validate(conn, ktls_mode));

int fd;
RESULT_GUARD(s2n_ktls_retrieve_file_descriptor(conn, ktls_mode, &fd));

int tls_tx_rx_mode = 0;
/* TODO Only AES_128_GCM for TLS 1.2 is supported at the moment. */
struct s2n_tls12_crypto_info_aes_gcm_128 crypto_info = { 0 };
RESULT_GUARD(s2n_ktls_init_aes128_gcm_crypto_info(conn, ktls_mode, key_material, &crypto_info, &tls_tx_rx_mode));

/* Calls to setsockopt require a real socket, which is not used in unit tests. */
RESULT_ENSURE(!disable_ktls_socket_config_for_testing, S2N_ERR_KTLS_DISABLED_FOR_TEST);

#if defined(__linux__)
/* set keys */
int ret_val = setsockopt(fd, S2N_SOL_TLS, tls_tx_rx_mode, &crypto_info, sizeof(crypto_info));
if (ret_val < 0) {
RESULT_BAIL(S2N_ERR_KTLS_ENABLE_CRYPTO);
}
#endif

return S2N_RESULT_OK;
}

static S2N_RESULT s2n_ktls_configure_socket(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
{
RESULT_ENSURE_REF(conn);
Expand Down Expand Up @@ -141,19 +248,26 @@ static S2N_RESULT s2n_ktls_configure_socket(struct s2n_connection *conn, s2n_ktl
return S2N_RESULT_OK;
}

S2N_RESULT s2n_ktls_configure_connection(struct s2n_connection *conn, s2n_ktls_mode ktls_mode)
{
RESULT_ENSURE_REF(conn);

struct s2n_key_material key_material = { 0 };
RESULT_GUARD(s2n_prf_generate_key_material(conn, &key_material));

/* config ktls socket */
RESULT_GUARD(s2n_ktls_set_keys(conn, ktls_mode, &key_material));

return S2N_RESULT_OK;
}

/*
* Since kTLS is an optimization, it is possible to continue operation
* by using userspace TLS if kTLS is not supported. Upon successfully
* enabling kTLS, we set connection->ktls_send_enabled (and recv) to true.
*
* For this reason we categorize kTLS errors into recoverable and
* un-recoverable and handle them appropriately:
*
* - Errors related to the socket configuration are considered recoverable
* since kTLS related `setsockopt` operations are non-destructive.
*
* - Errors related to connection configuration are considered
* un-recoverable since s2n_connection state is unknown.
* kTLS configuration errors are recoverable since calls to setsockopt are
* non-destructive and its possible to fallback to userspace.
*/
int s2n_connection_ktls_enable_send(struct s2n_connection *conn)
{
Expand All @@ -163,11 +277,12 @@ int s2n_connection_ktls_enable_send(struct s2n_connection *conn)
POSIX_BAIL(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM);
}

POSIX_GUARD_RESULT(s2n_ktls_validate_connection(conn));
POSIX_GUARD_RESULT(s2n_ktls_validate_socket_mode(conn, S2N_KTLS_MODE_SEND));
POSIX_GUARD_RESULT(s2n_ktls_validate(conn, S2N_KTLS_MODE_SEND));

POSIX_GUARD_RESULT(s2n_ktls_configure_socket(conn, S2N_KTLS_MODE_SEND));
POSIX_GUARD_RESULT(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_SEND));

conn->ktls_send_enabled = true;
return S2N_SUCCESS;
}

Expand All @@ -179,11 +294,12 @@ int s2n_connection_ktls_enable_recv(struct s2n_connection *conn)
POSIX_BAIL(S2N_ERR_KTLS_UNSUPPORTED_PLATFORM);
}

POSIX_GUARD_RESULT(s2n_ktls_validate_connection(conn));
POSIX_GUARD_RESULT(s2n_ktls_validate_socket_mode(conn, S2N_KTLS_MODE_RECV));
POSIX_GUARD_RESULT(s2n_ktls_validate(conn, S2N_KTLS_MODE_RECV));

POSIX_GUARD_RESULT(s2n_ktls_configure_socket(conn, S2N_KTLS_MODE_RECV));
POSIX_GUARD_RESULT(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

conn->ktls_recv_enabled = true;
return S2N_SUCCESS;
}

Expand Down
Loading

0 comments on commit b1d343f

Please sign in to comment.