diff --git a/tests/unit/s2n_ktls_test.c b/tests/unit/s2n_ktls_test.c new file mode 100644 index 00000000000..87c7277efaf --- /dev/null +++ b/tests/unit/s2n_ktls_test.c @@ -0,0 +1,188 @@ +/* + * 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 + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_record.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const struct s2n_security_policy *default_security_policy, *tls13_security_policy, *fips_security_policy; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); + + char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); + char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); + + /* config default kTLS mode */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->ktls_mode_requested, S2N_KTLS_MODE_DISABLED); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DUPLEX)); + }; + + /* request config kTLS mode + * + * Requesting Duplex means the Send and Recv are also requested + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_ktls_enable(config, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DUPLEX)); + + EXPECT_SUCCESS(s2n_config_ktls_enable(config, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DUPLEX)); + + EXPECT_SUCCESS(s2n_config_ktls_enable(config, S2N_KTLS_MODE_DUPLEX)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_RECV)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DUPLEX)); + + EXPECT_SUCCESS(s2n_config_ktls_enable(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DISABLED)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_config_is_ktls_requested(config, S2N_KTLS_MODE_DUPLEX)); + }; + + /* connection default kTLS mode */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + EXPECT_TRUE(conn->managed_send_io); + EXPECT_TRUE(conn->managed_recv_io); + + EXPECT_EQUAL(conn->ktls_mode_enabled, S2N_KTLS_MODE_DISABLED); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + }; + + /* enable kTLS modes + * + * Enabling TX and RX modes is additive and equals Duplex + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + EXPECT_TRUE(conn->managed_send_io); + EXPECT_TRUE(conn->managed_recv_io); + + EXPECT_EQUAL(conn->ktls_mode_enabled, S2N_KTLS_MODE_DISABLED); + + EXPECT_OK(s2n_connection_mark_ktls_enabled(conn, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + + EXPECT_OK(s2n_connection_mark_ktls_enabled(conn, S2N_KTLS_MODE_SEND)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + }; + + /* enable kTLS duplex */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + EXPECT_TRUE(conn->managed_send_io); + EXPECT_TRUE(conn->managed_recv_io); + + EXPECT_EQUAL(conn->ktls_mode_enabled, S2N_KTLS_MODE_DISABLED); + + EXPECT_OK(s2n_connection_mark_ktls_enabled(conn, S2N_KTLS_MODE_DUPLEX)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + }; + + /* DISALLOW kTLS disable on connection + * + * kTLS cannot be disabled once it has been enabled + * + * Note: This behavior might change if we introduce a kernel patch which + * support user space fallback from kTLS. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + EXPECT_TRUE(conn->managed_send_io); + EXPECT_TRUE(conn->managed_recv_io); + + EXPECT_EQUAL(conn->ktls_mode_enabled, S2N_KTLS_MODE_DISABLED); + + EXPECT_OK(s2n_connection_mark_ktls_enabled(conn, S2N_KTLS_MODE_DUPLEX)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + + EXPECT_OK(s2n_connection_mark_ktls_enabled(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_FALSE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DISABLED)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_SEND)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_RECV)); + EXPECT_TRUE(s2n_connection_matches_ktls_mode(conn, S2N_KTLS_MODE_DUPLEX)); + }; + + END_TEST(); +} diff --git a/tls/s2n_config.c b/tls/s2n_config.c index 6d6d2c1b4f1..5f9d1083459 100644 --- a/tls/s2n_config.c +++ b/tls/s2n_config.c @@ -13,6 +13,8 @@ * permissions and limitations under the License. */ +#include "tls/s2n_config.h" + #include #include @@ -22,6 +24,7 @@ #include "error/s2n_errno.h" #include "tls/s2n_cipher_preferences.h" #include "tls/s2n_internal.h" +#include "tls/s2n_ktls.h" #include "tls/s2n_security_policies.h" #include "tls/s2n_tls13.h" #include "utils/s2n_blob.h" @@ -1067,3 +1070,26 @@ int s2n_config_set_recv_multi_record(struct s2n_config *config, bool enabled) return S2N_SUCCESS; } + +/* Indicates if the connection should attempt to enable kTLS. */ +int s2n_config_ktls_enable(struct s2n_config *config, s2n_ktls_mode ktls_mode) +{ + POSIX_ENSURE_REF(config); + + config->ktls_mode_requested = ktls_mode; + return S2N_SUCCESS; +} + +/* Enabling Duplex mode means that both Send and Recv are also requested. */ +bool s2n_config_is_ktls_requested(struct s2n_config *config, s2n_ktls_mode ktls_mode) +{ + POSIX_ENSURE_REF(config); + + if (ktls_mode == S2N_KTLS_MODE_DUPLEX) { + return config->ktls_mode_requested == S2N_KTLS_MODE_DUPLEX; + } + if (ktls_mode == S2N_KTLS_MODE_DISABLED) { + return config->ktls_mode_requested == S2N_KTLS_MODE_DISABLED; + } + return config->ktls_mode_requested & ktls_mode; +} diff --git a/tls/s2n_config.h b/tls/s2n_config.h index 96969be9710..75014bcb72f 100644 --- a/tls/s2n_config.h +++ b/tls/s2n_config.h @@ -19,6 +19,7 @@ #include "crypto/s2n_certificate.h" #include "crypto/s2n_dhe.h" #include "tls/s2n_crl.h" +#include "tls/s2n_ktls.h" #include "tls/s2n_psk.h" #include "tls/s2n_renegotiate.h" #include "tls/s2n_resume.h" @@ -175,6 +176,11 @@ struct s2n_config { void *renegotiate_request_ctx; s2n_renegotiate_request_cb renegotiate_request_cb; + + /* Depending on OS and configuration it is possible to use kTLS. + * + * This option indicates if connections should attempt to use kTLS. */ + s2n_ktls_mode ktls_mode_requested; }; S2N_CLEANUP_RESULT s2n_config_ptr_free(struct s2n_config **config); @@ -190,3 +196,4 @@ void s2n_wipe_static_configs(void); extern struct s2n_cert_chain_and_key *s2n_config_get_single_default_cert(struct s2n_config *config); int s2n_config_get_num_default_certs(struct s2n_config *config); S2N_RESULT s2n_config_wall_clock(struct s2n_config *config, uint64_t *output); +bool s2n_config_is_ktls_requested(struct s2n_config *config, s2n_ktls_mode ktls_mode); diff --git a/tls/s2n_connection.c b/tls/s2n_connection.c index d599be86597..5ddd988005b 100644 --- a/tls/s2n_connection.c +++ b/tls/s2n_connection.c @@ -37,6 +37,7 @@ #include "tls/s2n_handshake.h" #include "tls/s2n_internal.h" #include "tls/s2n_kem.h" +#include "tls/s2n_ktls.h" #include "tls/s2n_prf.h" #include "tls/s2n_record.h" #include "tls/s2n_resume.h" @@ -1517,3 +1518,41 @@ S2N_RESULT s2n_connection_dynamic_free_in_buffer(struct s2n_connection *conn) return S2N_RESULT_OK; } + +/* Marks s2n_ktls_mode enabled for the connection. + * + * Note: currently, kTLS cannot be disabled once enabled. + */ +S2N_RESULT s2n_connection_mark_ktls_enabled(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. */ + if ((ktls_mode == S2N_KTLS_MODE_SEND || ktls_mode == S2N_KTLS_MODE_DUPLEX) && !conn->managed_send_io) { + return S2N_RESULT_ERROR; + } + if ((ktls_mode == S2N_KTLS_MODE_RECV || ktls_mode == S2N_KTLS_MODE_DUPLEX) && !conn->managed_recv_io) { + return S2N_RESULT_ERROR; + } + + conn->ktls_mode_enabled |= ktls_mode; + + return S2N_RESULT_OK; +} + +/* Indicates if the mode matches the connection's current kTLS configuration. + * + * Enabling Duplex mode means that both Send and Recv are also enabled. */ +bool s2n_connection_matches_ktls_mode(struct s2n_connection *conn, s2n_ktls_mode ktls_mode) +{ + POSIX_ENSURE_REF(conn); + + if (ktls_mode == S2N_KTLS_MODE_DUPLEX) { + return conn->ktls_mode_enabled == S2N_KTLS_MODE_DUPLEX; + } + if (ktls_mode == S2N_KTLS_MODE_DISABLED) { + return conn->ktls_mode_enabled == S2N_KTLS_MODE_DISABLED; + } + return conn->ktls_mode_enabled & ktls_mode; +} diff --git a/tls/s2n_connection.h b/tls/s2n_connection.h index 555efe0af51..ed034b3a36a 100644 --- a/tls/s2n_connection.h +++ b/tls/s2n_connection.h @@ -282,8 +282,8 @@ struct s2n_connection { */ uint16_t max_outgoing_fragment_length; - /* The number of bytes to send before changing the record size. - * If this value > 0 then dynamic TLS record size is enabled. Otherwise, the feature is disabled (default). + /* The number of bytes to send before changing the record size. + * If this value > 0 then dynamic TLS record size is enabled. Otherwise, the feature is disabled (default). */ uint32_t dynamic_record_resize_threshold; @@ -385,6 +385,9 @@ struct s2n_connection { uint32_t server_keying_material_lifetime; struct s2n_post_handshake post_handshake; + + /* Marks if kTLS has been enabled for this connection. */ + s2n_ktls_mode ktls_mode_enabled; }; S2N_CLEANUP_RESULT s2n_connection_ptr_free(struct s2n_connection **s2n_connection); @@ -417,3 +420,4 @@ int s2n_connection_get_client_cert_chain(struct s2n_connection *conn, uint8_t ** int s2n_connection_get_peer_cert_chain(const struct s2n_connection *conn, struct s2n_cert_chain_and_key *cert_chain_and_key); uint8_t s2n_connection_get_protocol_version(const struct s2n_connection *conn); S2N_RESULT s2n_connection_set_max_fragment_length(struct s2n_connection *conn, uint16_t length); +S2N_RESULT s2n_connection_mark_ktls_enabled(struct s2n_connection *conn, s2n_ktls_mode mode); diff --git a/tls/s2n_ktls.c b/tls/s2n_ktls.c new file mode 100644 index 00000000000..94c9507ea9f --- /dev/null +++ b/tls/s2n_ktls.c @@ -0,0 +1,26 @@ +/* + * 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_ktls.h" + +/* + * TODO implement + */ +S2N_RESULT s2n_ktls_enable(struct s2n_connection *conn, s2n_ktls_mode mode) +{ + /* TODO perform managed_send_io and managed_recv_io checks */ + + return S2N_RESULT_OK; +} diff --git a/tls/s2n_ktls.h b/tls/s2n_ktls.h new file mode 100644 index 00000000000..b7fe61c1804 --- /dev/null +++ b/tls/s2n_ktls.h @@ -0,0 +1,52 @@ +/* + * 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. + */ + +#pragma once + +#include + +#include "utils/s2n_result.h" + +/* --- unstable API --- + * + * These will eventually be moved to unstable/ktls.h once kTLS is implemented + */ + +/* A set of kTLS configurations representing the combination of sending + * and receiving. + * + * s2n assumes specific binary representation for the following modes: + * + * S2N_KTLS_MODE_SEND | S2N_KTLS_MODE_RECV = S2N_KTLS_MODE_DUPLEX + * 0b01 | 0b10 = 0b11 + */ +typedef enum { + /* Disable kTLS. */ + S2N_KTLS_MODE_DISABLED = 0, + /* Enable kTLS for the send socket. */ + S2N_KTLS_MODE_SEND = 1, + /* Enable kTLS for the recv socket. */ + S2N_KTLS_MODE_RECV = 2, + /* Enable kTLS for both rx and tx socket. */ + S2N_KTLS_MODE_DUPLEX = 3, +} s2n_ktls_mode; + +int s2n_config_ktls_enable(struct s2n_config *config, s2n_ktls_mode ktls_mode); + +bool s2n_connection_matches_ktls_mode(struct s2n_connection *conn, s2n_ktls_mode ktls_mode); + +/* --- unstable API --- */ + +S2N_RESULT s2n_ktls_enable(struct s2n_connection *conn, s2n_ktls_mode mode);