Skip to content
Open
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
44 changes: 43 additions & 1 deletion tests/unit/s2n_ktls_io_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ int main(int argc, char **argv)
{
BEGIN_TEST();

const uint8_t test_record_type = 43;
const uint8_t test_record_type = TLS_APPLICATION_DATA;
/* test data */
uint8_t test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
struct s2n_blob test_data_blob = { 0 };
Expand Down Expand Up @@ -562,6 +562,48 @@ int main(int argc, char **argv)
};
};

/* Test: s2n_ktls_recvmsg rejects invalid record types
*
* Iterates all 256 possible byte values for the TLS ContentType field
* delivered via kTLS ancillary data. Only alert (21), handshake (22),
* and application_data (23) are valid post-handshake record types per
* RFC 8446 Section 5. All others must be rejected with S2N_ERR_BAD_MESSAGE.
*/
{
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free);
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair));

for (uint16_t record_type_val = 0; record_type_val <= UINT8_MAX; record_type_val++) {
uint8_t injected_record_type = (uint8_t) record_type_val;
bool is_valid = (injected_record_type >= TLS_ALERT
&& injected_record_type <= TLS_APPLICATION_DATA);

/* Inject a record with the test record_type into the client's receive buffer via the mock stuffer IO. */
struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND };
s2n_blocked_status blocked = S2N_NOT_BLOCKED;
size_t bytes_written = 0;
EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, injected_record_type, &msg_iov, 1, &blocked, &bytes_written));
EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND);

uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 };
uint8_t recv_record_type = 0;
size_t bytes_read = 0;

if (is_valid) {
EXPECT_OK(s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read));
EXPECT_EQUAL(recv_record_type, injected_record_type);
EXPECT_EQUAL(bytes_read, S2N_TEST_TO_SEND);
} else {
EXPECT_ERROR_WITH_ERRNO(
s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type,
recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read),
S2N_ERR_BAD_MESSAGE);
}
}
};

/* Test s2n_ktls_send */
{
const size_t test_iov_lens[] = { 10, 0, 1, 5, 100, 100, 10 };
Expand Down
27 changes: 0 additions & 27 deletions tests/unit/s2n_self_talk_ktls_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -347,33 +347,6 @@ int main(int argc, char **argv)
EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read);
}

/* Test: s2n_recv with interleaved control messages */
{
/* TLS1.3 and TLS1.2 have different expectations for control messages */
EXPECT_EQUAL(reader->actual_protocol_version, S2N_TLS12);

const uint8_t test_record_type = TLS_CHANGE_CIPHER_SPEC;
uint8_t control_record_data[] = "control record data";
struct s2n_blob control_record = { 0 };
EXPECT_SUCCESS(s2n_blob_init(&control_record, control_record_data,
sizeof(control_record_data)));

for (size_t i = 0; i < 5; i++) {
EXPECT_OK(s2n_record_write(writer, test_record_type, &control_record));
EXPECT_SUCCESS(s2n_flush(writer, &blocked));

int written = s2n_send(writer, test_data, sizeof(test_data), &blocked);
EXPECT_EQUAL(written, sizeof(test_data));

uint8_t buffer[sizeof(test_data)] = { 0 };
int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked);
EXPECT_EQUAL(read, sizeof(test_data));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);

EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read);
}
};

/* Test: s2n_recv with incorrectly encrypted application data
*
* This test closes the connection so should be the last test to use
Expand Down
19 changes: 19 additions & 0 deletions tls/s2n_ktls_io.c
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,25 @@ S2N_RESULT s2n_ktls_recvmsg(void *io_context, uint8_t *record_type, uint8_t *buf

RESULT_GUARD(s2n_ktls_get_control_data(&msg, S2N_TLS_GET_RECORD_TYPE, record_type));

/* Validate that record_type is a legitimate post-handshake TLS ContentType.
*
* kTLS is only enabled after the handshake completes, so the only valid
* record types on this path are:
* - TLS_ALERT (21): close_notify or error alerts
* - TLS_HANDSHAKE (22): post-handshake messages (e.g., KeyUpdate)
* - TLS_APPLICATION_DATA (23): normal data
*
* TLS_CHANGE_CIPHER_SPEC (20) is explicitly excluded because CCS is only
* valid before the peer's Finished message is received (RFC 8446 Section 5,
* paragraph 3), and kTLS is never active during the handshake.
*
* "If a TLS implementation receives an unexpected record type, it MUST
* terminate the connection with an 'unexpected_message' alert."
* - RFC 8446 Section 5
*/
RESULT_ENSURE(*record_type >= TLS_ALERT, S2N_ERR_BAD_MESSAGE);
RESULT_ENSURE(*record_type <= TLS_APPLICATION_DATA, S2N_ERR_BAD_MESSAGE);

*blocked = S2N_NOT_BLOCKED;
*bytes_read = result;
return S2N_RESULT_OK;
Expand Down
Loading