Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ktls: ktls interface and building blocks #3707

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
188 changes: 188 additions & 0 deletions tests/unit/s2n_ktls_test.c
Original file line number Diff line number Diff line change
@@ -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 <stdlib.h>

#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();
}
26 changes: 26 additions & 0 deletions tls/s2n_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
* permissions and limitations under the License.
*/

#include "tls/s2n_config.h"

#include <strings.h>
#include <time.h>

Expand All @@ -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"
Expand Down Expand Up @@ -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. */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really sure this comment is accurate. The function enables ktls, it doesn't indicate anything.

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay maybe I don't fully understand the purpose of this function, but if it's to tell the user which mode is set, why not just:

int s2n_config_get_ktls_mode(struct s2n_config *config, s2n_ktls_mode *ktls_mode)
{
    POSIX_ENSURE_REF(config);
    *ktls_mode = config->ktls_mode_requested;
    return S2N_SUCCESS;
}

}
7 changes: 7 additions & 0 deletions tls/s2n_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -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);
39 changes: 39 additions & 0 deletions tls/s2n_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am confused. Are both of these functions that you've added APIs? Why is only one declared in connection.h?

{
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;
}
8 changes: 6 additions & 2 deletions tls/s2n_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
26 changes: 26 additions & 0 deletions tls/s2n_ktls.c
Original file line number Diff line number Diff line change
@@ -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 */
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this file at this point in the project? Seems like there's nothing in here that we need yet. What purpose is this function serving?


return S2N_RESULT_OK;
}
Loading