Skip to content

Commit

Permalink
ktls: receive app data (#4201)
Browse files Browse the repository at this point in the history
  • Loading branch information
lrstewart authored Sep 18, 2023
1 parent e15bf7f commit 3758f4b
Show file tree
Hide file tree
Showing 6 changed files with 536 additions and 38 deletions.
44 changes: 44 additions & 0 deletions tests/unit/s2n_ktls_io_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1139,6 +1139,50 @@ int main(int argc, char **argv)
uint8_t *read = s2n_stuffer_raw_read(&conn->in, small_frag_len);
EXPECT_BYTEARRAY_EQUAL(read, test_data, small_frag_len);
};

/* Test: Receive drains conn->in before calling recvmsg again */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

/* Write half the test data into conn->in */
const size_t offset = sizeof(test_data) / 2;
EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, test_data, offset));

/* Write the other half into a new record */
size_t written = 0;
struct iovec offset_iovec = {
.iov_base = test_data + offset,
.iov_len = sizeof(test_data) - offset,
};
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &offset_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, offset_iovec.iov_len);

uint8_t record_type = 0;
uint8_t *read = NULL;

/* Verify that our first read returns conn->in, not the new record */
EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type));
EXPECT_EQUAL(record_type, TLS_APPLICATION_DATA);
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), offset);
read = s2n_stuffer_raw_read(&conn->in, offset);
EXPECT_BYTEARRAY_EQUAL(read, test_data, offset);

EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in));

/* Verify a second read returns the new record */
EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type));
EXPECT_EQUAL(record_type, TLS_ALERT);
EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), offset_iovec.iov_len);
read = s2n_stuffer_raw_read(&conn->in, offset_iovec.iov_len);
EXPECT_BYTEARRAY_EQUAL(read, offset_iovec.iov_base, offset_iovec.iov_len);
};
};

END_TEST();
Expand Down
314 changes: 313 additions & 1 deletion tests/unit/s2n_recv_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,11 @@
*/

#include "api/s2n.h"
#include "api/unstable/renegotiate.h"
#include "s2n_test.h"
#include "testlib/s2n_ktls_test_utils.h"
#include "testlib/s2n_testlib.h"
#include "utils/s2n_random.h"

bool s2n_custom_recv_fn_called = false;

Expand All @@ -30,6 +33,22 @@ int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t
return result;
}

static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg)
{
POSIX_ENSURE_REF(io_context);
return *(ssize_t *) io_context;
}

static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context,
s2n_renegotiate_response *response)
{
POSIX_ENSURE_REF(context);
size_t *count = (size_t *) context;
(*count)++;
*response = S2N_RENEGOTIATE_IGNORE;
return S2N_SUCCESS;
}

int main(int argc, char **argv)
{
BEGIN_TEST();
Expand Down Expand Up @@ -340,7 +359,300 @@ int main(int argc, char **argv)
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
}
};

/* Test with ktls */
{
uint8_t test_data[100] = { 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));

const struct iovec test_iovec = {
.iov_base = test_data,
.iov_len = sizeof(test_data),
};

s2n_blocked_status blocked = S2N_NOT_BLOCKED;

/* Test: receive all requested application data */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(test_data));

uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_EQUAL(read, written);
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, read);
};

/* Test: receive partial application data */
{
const size_t partial_size = sizeof(test_data) / 2;
struct iovec partial_iovec = test_iovec;
partial_iovec.iov_len = partial_size;

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&partial_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, partial_size);

uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_EQUAL(read, written);
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, read);
};

/* Test: drain buffered application data */
{
const size_t partial_size = sizeof(test_data) / 2;

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(test_data));

/* The first read doesn't read all the available data */
uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, partial_size, &blocked);
EXPECT_EQUAL(read, partial_size);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);
EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size);
EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1);

/* The second read drains the remaining data */
const size_t remaining = sizeof(test_data) - partial_size;
read = s2n_recv(conn, output + read, remaining, &blocked);
EXPECT_EQUAL(read, remaining);
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data));
EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1);
};

/* Test: receive blocks */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));

uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);
};

/* Test: receive indicates end-of-data */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

ssize_t ret_val = 0;
EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val));

uint8_t output[10] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);

/* Error fatal but not blinded */
EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED));
EXPECT_EQUAL(s2n_connection_get_delay(conn), 0);
};

/* Test: receive alert */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(test_data));

uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT);
EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ);

/* Error fatal but not blinded */
EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED));
EXPECT_EQUAL(s2n_connection_get_delay(conn), 0);
};

/* Test: receive handshake message */
{
DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(reneg_config);

size_t reneg_request_count = 0;
EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config,
s2n_test_reneg_req_cb, &reneg_request_count));

uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST };
const struct iovec hello_request_iovec = {
.iov_base = hello_request,
.iov_len = sizeof(hello_request),
};

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config));
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));
conn->secure_renegotiation = true;

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

size_t written = 0;

/* Send the handshake message */
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE,
&hello_request_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(hello_request));

/* Also send some application data */
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&test_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, sizeof(test_data));

/* Verify that we received the application data */
uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_EQUAL(read, sizeof(test_data));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, read);

/* Verify that we received and processed the handshake message */
EXPECT_EQUAL(reneg_request_count, 1);
};

/* Test: Multirecord mode */
{
DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(),
s2n_config_ptr_free);
EXPECT_NOT_NULL(multi_config);
EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true));

/* Test: receive all requested application data */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config));
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

/* Write a lot of very small records */
struct iovec offset_iovec = { 0 };
for (size_t offset = 0; offset < sizeof(test_data); offset++) {
offset_iovec.iov_base = test_data + offset;
offset_iovec.iov_len = 1;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&offset_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, 1);
}

/* Receive all the data from the many small records */
uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_EQUAL(read, sizeof(test_data));
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data));
};

/* Test: receive partial application data */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config));
EXPECT_OK(s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV));

DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 },
s2n_ktls_io_stuffer_pair_free);
EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair));
struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in;

/* Write a lot of very small records, but don't write the full
* expected test data size. */
const size_t partial_size = sizeof(test_data) / 2;
struct iovec offset_iovec = { 0 };
for (size_t offset = 0; offset < partial_size; offset++) {
offset_iovec.iov_base = test_data + offset;
offset_iovec.iov_len = 1;

size_t written = 0;
EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA,
&offset_iovec, 1, &blocked, &written));
EXPECT_EQUAL(written, 1);
}

/* Receive the partial data */
uint8_t output[sizeof(test_data)] = { 0 };
int read = s2n_recv(conn, output, sizeof(output), &blocked);
EXPECT_EQUAL(read, partial_size);
EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED);
EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size);
};
};
};

END_TEST();
}
Loading

0 comments on commit 3758f4b

Please sign in to comment.