Skip to content

Commit

Permalink
Add API to retrieve parsed supported groups (#4216)
Browse files Browse the repository at this point in the history
  • Loading branch information
goatgoose committed Sep 23, 2023
1 parent 7318609 commit 5e232a6
Show file tree
Hide file tree
Showing 8 changed files with 519 additions and 7 deletions.
25 changes: 25 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -1530,6 +1530,31 @@ S2N_API extern int s2n_client_hello_get_session_id_length(struct s2n_client_hell
*/
S2N_API extern int s2n_client_hello_get_session_id(struct s2n_client_hello *ch, uint8_t *out, uint32_t *out_length, uint32_t max_length);

/**
* Retrieves the supported groups received from the client in the supported groups extension.
*
* IANA values for each of the received supported groups are written to the provided `groups`
* array, and `groups_count` is set to the number of received supported groups.
*
* `groups_count_max` should be set to the maximum capacity of the `groups` array. If
* `groups_count_max` is less than the number of received supported groups, this function will
* error. To determine how large `groups` should be in advance, use
* `s2n_client_hello_get_extension_length()` with the S2N_EXTENSION_SUPPORTED_GROUPS extension
* type, and divide the value by 2.
*
* If no supported groups extension was received from the peer, or the received supported groups
* extension is malformed, this function will error.
*
* @param ch A pointer to the ClientHello. Can be retrieved from a connection via
* `s2n_connection_get_client_hello()`.
* @param groups The array to populate with the received supported groups.
* @param groups_count_max The maximum number of supported groups that can fit in the `groups` array.
* @param groups_count Returns the number of received supported groups.
* @returns S2N_SUCCESS on success. S2N_FAILURE on failure.
*/
S2N_API extern int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups,
uint16_t groups_count_max, uint16_t *groups_count);

/**
* Sets the file descriptor for a s2n connection.
*
Expand Down
1 change: 1 addition & 0 deletions error/s2n_errno.c
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ static const char *no_such_error = "Internal s2n error";
ERR_ENTRY(S2N_ERR_SEND_SIZE, "Retried s2n_send() size is invalid") \
ERR_ENTRY(S2N_ERR_CORK_SET_ON_UNMANAGED, "Attempt to set connection cork management on unmanaged IO") \
ERR_ENTRY(S2N_ERR_UNRECOGNIZED_EXTENSION, "TLS extension not recognized") \
ERR_ENTRY(S2N_ERR_EXTENSION_NOT_RECEIVED, "The TLS extension was not received") \
ERR_ENTRY(S2N_ERR_INVALID_SCT_LIST, "SCT list is invalid") \
ERR_ENTRY(S2N_ERR_INVALID_OCSP_RESPONSE, "OCSP response is invalid") \
ERR_ENTRY(S2N_ERR_UPDATING_EXTENSION, "Updating extension data failed") \
Expand Down
1 change: 1 addition & 0 deletions error/s2n_errno.h
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ typedef enum {
S2N_ERR_SEND_SIZE,
S2N_ERR_CORK_SET_ON_UNMANAGED,
S2N_ERR_UNRECOGNIZED_EXTENSION,
S2N_ERR_EXTENSION_NOT_RECEIVED,
S2N_ERR_INVALID_SCT_LIST,
S2N_ERR_INVALID_OCSP_RESPONSE,
S2N_ERR_UPDATING_EXTENSION,
Expand Down
388 changes: 388 additions & 0 deletions tests/unit/s2n_client_hello_get_supported_groups_test.c

Large diffs are not rendered by default.

45 changes: 44 additions & 1 deletion tests/unit/s2n_client_hello_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,49 @@ int main(int argc, char **argv)
EXPECT_SUCCESS(s2n_connection_free(conn));
};

/* Test s2n_client_hello_has_extension with a zero-length extension */
for (int send_sct = 0; send_sct <= 1; send_sct++) {
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key));

/* The SCT extension is zero-length. */
if (send_sct) {
EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST));
}

DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(client, config));

DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
EXPECT_SUCCESS(s2n_connection_set_config(server, config));

EXPECT_SUCCESS(s2n_client_hello_send(client));
EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io,
s2n_stuffer_data_available(&client->handshake.io)));
EXPECT_SUCCESS(s2n_client_hello_recv(server));

struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server);
EXPECT_NOT_NULL(client_hello);

s2n_parsed_extension *sct_extension = NULL;
int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions,
&sct_extension);

if (send_sct) {
/* Ensure that the extension was received. */
EXPECT_SUCCESS(ret);
POSIX_ENSURE_REF(sct_extension);

/* Ensure that the extension is zero-length. */
EXPECT_EQUAL(sct_extension->extension.size, 0);
} else {
/* The extension shouldn't have been received because it wasn't requested. */
EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED);
}
}

/* Test s2n_client_hello_get_raw_extension */
{
uint8_t data[] = {
Expand Down Expand Up @@ -1129,7 +1172,7 @@ int main(int argc, char **argv)
EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0);
EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len));
EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0);
EXPECT_EQUAL(s2n_errno, S2N_ERR_NULL);
EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED);
free(ext_data);
ext_data = NULL;

Expand Down
28 changes: 23 additions & 5 deletions tls/extensions/s2n_client_supported_groups.c
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,25 @@ static int s2n_client_supported_groups_send(struct s2n_connection *conn, struct
return S2N_SUCCESS;
}

S2N_RESULT s2n_supported_groups_parse_count(struct s2n_stuffer *extension, uint16_t *count)
{
RESULT_ENSURE_REF(count);
*count = 0;
RESULT_ENSURE_REF(extension);

uint16_t supported_groups_list_size = 0;
RESULT_GUARD_POSIX(s2n_stuffer_read_uint16(extension, &supported_groups_list_size));

RESULT_ENSURE(supported_groups_list_size <= s2n_stuffer_data_available(extension),
S2N_ERR_INVALID_PARSED_EXTENSIONS);
RESULT_ENSURE(supported_groups_list_size % S2N_SUPPORTED_GROUP_SIZE == 0,
S2N_ERR_INVALID_PARSED_EXTENSIONS);

*count = supported_groups_list_size / S2N_SUPPORTED_GROUP_SIZE;

return S2N_RESULT_OK;
}

/* Populates the appropriate index of either the mutually_supported_curves or
* mutually_supported_kem_groups array based on the received IANA ID. Will
* ignore unrecognized IANA IDs (and return success). */
Expand Down Expand Up @@ -165,15 +184,14 @@ static int s2n_client_supported_groups_recv(struct s2n_connection *conn, struct
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(extension);

uint16_t size_of_all;
POSIX_GUARD(s2n_stuffer_read_uint16(extension, &size_of_all));
if (size_of_all > s2n_stuffer_data_available(extension) || (size_of_all % sizeof(uint16_t))) {
uint16_t supported_groups_count = 0;
if (s2n_result_is_error(s2n_supported_groups_parse_count(extension, &supported_groups_count))) {
/* Malformed length, ignore the extension */
return S2N_SUCCESS;
}

for (size_t i = 0; i < (size_of_all / sizeof(uint16_t)); i++) {
uint16_t iana_id;
for (size_t i = 0; i < supported_groups_count; i++) {
uint16_t iana_id = 0;
POSIX_GUARD(s2n_stuffer_read_uint16(extension, &iana_id));
POSIX_GUARD(s2n_client_supported_groups_recv_iana_id(conn, iana_id));
}
Expand Down
4 changes: 4 additions & 0 deletions tls/extensions/s2n_client_supported_groups.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@
#include "tls/extensions/s2n_extension_type.h"
#include "tls/s2n_connection.h"

#define S2N_SUPPORTED_GROUP_SIZE 2

extern const s2n_extension_type s2n_client_supported_groups_extension;
bool s2n_extension_should_send_if_ecc_enabled(struct s2n_connection *conn);

S2N_RESULT s2n_supported_groups_parse_count(struct s2n_stuffer *extension, uint16_t *count);

/* Old-style extension functions -- remove after extensions refactor is complete */
int s2n_recv_client_supported_groups(struct s2n_connection *conn, struct s2n_stuffer *extension);
34 changes: 33 additions & 1 deletion tls/s2n_client_hello.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "crypto/s2n_rsa_signing.h"
#include "error/s2n_errno.h"
#include "stuffer/s2n_stuffer.h"
#include "tls/extensions/s2n_client_supported_groups.h"
#include "tls/extensions/s2n_extension_list.h"
#include "tls/extensions/s2n_server_key_share.h"
#include "tls/s2n_alerts.h"
Expand Down Expand Up @@ -863,7 +864,7 @@ int s2n_client_hello_get_parsed_extension(s2n_tls_extension_type extension_type,
POSIX_GUARD(s2n_extension_supported_iana_value_to_id(extension_type, &extension_type_id));

s2n_parsed_extension *found_parsed_extension = &parsed_extension_list->parsed_extensions[extension_type_id];
POSIX_ENSURE_REF(found_parsed_extension->extension.data);
POSIX_ENSURE(found_parsed_extension->extension.data, S2N_ERR_EXTENSION_NOT_RECEIVED);
POSIX_ENSURE(found_parsed_extension->extension_type == extension_type, S2N_ERR_INVALID_PARSED_EXTENSIONS);

*parsed_extension = found_parsed_extension;
Expand Down Expand Up @@ -971,3 +972,34 @@ int s2n_client_hello_has_extension(struct s2n_client_hello *ch, uint16_t extensi
}
return S2N_SUCCESS;
}

int s2n_client_hello_get_supported_groups(struct s2n_client_hello *ch, uint16_t *groups,
uint16_t groups_count_max, uint16_t *groups_count_out)
{
POSIX_ENSURE_REF(groups_count_out);
*groups_count_out = 0;
POSIX_ENSURE_REF(ch);
POSIX_ENSURE_REF(groups);

s2n_parsed_extension *supported_groups_extension = NULL;
POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_SUPPORTED_GROUPS, &ch->extensions, &supported_groups_extension));
POSIX_ENSURE_REF(supported_groups_extension);

struct s2n_stuffer extension_stuffer = { 0 };
POSIX_GUARD(s2n_stuffer_init_written(&extension_stuffer, &supported_groups_extension->extension));

uint16_t supported_groups_count = 0;
POSIX_GUARD_RESULT(s2n_supported_groups_parse_count(&extension_stuffer, &supported_groups_count));
POSIX_ENSURE(supported_groups_count <= groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE);

for (size_t i = 0; i < supported_groups_count; i++) {
/* s2n_stuffer_read_uint16 is used to read each of the supported groups in network-order
* endianness.
*/
POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &groups[i]));
}

*groups_count_out = supported_groups_count;

return S2N_SUCCESS;
}

0 comments on commit 5e232a6

Please sign in to comment.