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

Add API to retrieve the supported groups for a security policy #4273

Merged
merged 5 commits into from
Nov 13, 2023
Merged
Show file tree
Hide file tree
Changes from 3 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
25 changes: 25 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -3391,6 +3391,31 @@ S2N_API int s2n_offered_early_data_reject(struct s2n_offered_early_data *early_d
*/
S2N_API int s2n_offered_early_data_accept(struct s2n_offered_early_data *early_data);

/**
* Retrieves the list of supported groups configured by the security policy associated with `config`.
*
* The retrieved list of groups will contain all of the supported groups for a security policy that are compatible
* with the build of s2n-tls. For instance, PQ kem groups that are not supported by the linked libcrypto will not
* be written. Otherwise, all of the supported groups configured for the security policy will be written.
*
* IANA values for each of the supported groups are written to the provided `groups` array, and `groups_count` is
* set to the number of written 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 supported groups configured by the security policy, this function will error.
*
* Note that this API retrieves the supported groups from a local security policy. To instead get the supported
maddeleine marked this conversation as resolved.
Show resolved Hide resolved
* groups sent by the client, use the `s2n_client_hello_get_supported_groups()` API.
goatgoose marked this conversation as resolved.
Show resolved Hide resolved
*
* @param config A pointer to the s2n_config object from which the supported groups will be retrieved.
* @param groups The array to populate with the supported groups.
* @param groups_count_max The maximum number of supported groups that can fit in the `groups` array.
* @param groups_count Set to the number of supported groups written to `groups`.
* @returns S2N_SUCCESS on success. S2N_FAILURE on failure.
*/
S2N_API int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max,
uint16_t *groups_count);

#ifdef __cplusplus
}
#endif
108 changes: 108 additions & 0 deletions tests/unit/s2n_config_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@

#include "api/s2n.h"
#include "crypto/s2n_fips.h"
#include "pq-crypto/s2n_pq.h"
#include "s2n_test.h"
#include "testlib/s2n_testlib.h"
#include "tls/extensions/s2n_client_supported_groups.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_internal.h"
#include "tls/s2n_record.h"
#include "tls/s2n_security_policies.h"
#include "tls/s2n_tls13.h"
#include "unstable/npn.h"

#define S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT 30

static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context,
struct s2n_offered_psk_list *psk_identity_list)
{
Expand Down Expand Up @@ -978,5 +982,109 @@ int main(int argc, char **argv)
}
}

/* Test s2n_config_get_supported_groups */
{
/* Safety */
{
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
EXPECT_NOT_NULL(config);
uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 };
uint16_t supported_groups_count = 0;

int ret = s2n_config_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups),
maddeleine marked this conversation as resolved.
Show resolved Hide resolved
&supported_groups_count);
EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL);
EXPECT_EQUAL(supported_groups_count, 0);

ret = s2n_config_get_supported_groups(config, NULL, s2n_array_len(supported_groups), &supported_groups_count);
EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL);
EXPECT_EQUAL(supported_groups_count, 0);

ret = s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups), NULL);
EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL);
EXPECT_EQUAL(supported_groups_count, 0);
}

/* Error if the provided supported groups array is too small */
{
/* Test a policy with and without PQ kem groups */
const char *policies[] = {
"20170210",
"PQ-TLS-1-2-2023-10-07",
};

for (size_t i = 0; i < s2n_array_len(policies); i++) {
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
EXPECT_NOT_NULL(config);
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, policies[i]));

uint32_t policy_groups_count = 0;
EXPECT_OK(s2n_kem_preferences_groups_available(config->security_policy->kem_preferences,
&policy_groups_count));
policy_groups_count += config->security_policy->ecc_preferences->count;
EXPECT_TRUE(policy_groups_count > 0);

uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 };
uint16_t supported_groups_count = 11;
for (size_t invalid_count = 0; invalid_count < policy_groups_count; invalid_count++) {
int ret = s2n_config_get_supported_groups(config, supported_groups, invalid_count,
&supported_groups_count);
EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE);
EXPECT_EQUAL(supported_groups_count, 0);
}

EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, policy_groups_count,
&supported_groups_count));
EXPECT_EQUAL(supported_groups_count, policy_groups_count);
}
}

/* The groups produced by s2n_config_get_supported_groups should match the groups produced
* by a connection that's configured to send its entire list of supported groups
*/
for (size_t policy_idx = 0; security_policy_selection[policy_idx].version != NULL; policy_idx++) {
const struct s2n_security_policy *security_policy = security_policy_selection[policy_idx].security_policy;
EXPECT_NOT_NULL(security_policy);

uint32_t expected_groups_count = 0;
EXPECT_OK(s2n_kem_preferences_groups_available(security_policy->kem_preferences, &expected_groups_count));
expected_groups_count += security_policy->ecc_preferences->count;

DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
EXPECT_NOT_NULL(config);
config->security_policy = security_policy;

uint16_t supported_groups[S2N_TEST_MAX_SUPPORTED_GROUPS_COUNT] = { 0 };
uint16_t supported_groups_count = 0;
EXPECT_SUCCESS(s2n_config_get_supported_groups(config, supported_groups, s2n_array_len(supported_groups),
&supported_groups_count));
EXPECT_EQUAL(supported_groups_count, expected_groups_count);

DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
maddeleine marked this conversation as resolved.
Show resolved Hide resolved
EXPECT_NOT_NULL(conn);
EXPECT_SUCCESS(s2n_connection_set_config(conn, config));
/* PQ kem groups aren't sent in the supported groups extension before TLS 1.3. */
maddeleine marked this conversation as resolved.
Show resolved Hide resolved
conn->actual_protocol_version = S2N_TLS13;

DEFER_CLEANUP(struct s2n_stuffer extension_stuffer = { 0 }, s2n_stuffer_free);
EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_stuffer, 0));
EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &extension_stuffer));

uint16_t extension_groups_count = 0;
EXPECT_OK(s2n_supported_groups_parse_count(&extension_stuffer, &extension_groups_count));
EXPECT_EQUAL(extension_groups_count, expected_groups_count);

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.
*/
uint16_t group_iana = 0;
POSIX_GUARD(s2n_stuffer_read_uint16(&extension_stuffer, &group_iana));

EXPECT_EQUAL(group_iana, supported_groups[i]);
}
}
}

END_TEST();
}
43 changes: 43 additions & 0 deletions tls/s2n_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "crypto/s2n_fips.h"
#include "crypto/s2n_hkdf.h"
#include "error/s2n_errno.h"
#include "pq-crypto/s2n_pq.h"
#include "tls/s2n_cipher_preferences.h"
#include "tls/s2n_internal.h"
#include "tls/s2n_ktls.h"
Expand Down Expand Up @@ -1138,3 +1139,45 @@ int s2n_config_set_cert_validation_cb(struct s2n_config *config, s2n_cert_valida

return S2N_SUCCESS;
}

int s2n_config_get_supported_groups(struct s2n_config *config, uint16_t *groups, uint16_t groups_count_max,
maddeleine marked this conversation as resolved.
Show resolved Hide resolved
uint16_t *groups_count_out)
{
POSIX_ENSURE_REF(groups_count_out);
*groups_count_out = 0;
POSIX_ENSURE_REF(config);
POSIX_ENSURE_REF(groups);

const struct s2n_security_policy *security_policy = config->security_policy;
POSIX_ENSURE_REF(security_policy);
const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences;
POSIX_ENSURE_REF(kem_preferences);
const struct s2n_ecc_preferences *ecc_preferences = security_policy->ecc_preferences;
POSIX_ENSURE_REF(ecc_preferences);

uint16_t groups_count = 0;
for (uint8_t i = 0; i < kem_preferences->tls13_kem_group_count; i++) {
goatgoose marked this conversation as resolved.
Show resolved Hide resolved
const struct s2n_kem_group *kem_group = kem_preferences->tls13_kem_groups[i];
POSIX_ENSURE_REF(kem_group);
if (!s2n_kem_group_is_available(kem_group)) {
continue;
}

POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE);
groups[groups_count] = kem_group->iana_id;
groups_count += 1;
}

for (uint8_t i = 0; i < ecc_preferences->count; i++) {
const struct s2n_ecc_named_curve *ecc_curve = ecc_preferences->ecc_curves[i];
POSIX_ENSURE_REF(ecc_curve);

POSIX_ENSURE(groups_count < groups_count_max, S2N_ERR_INSUFFICIENT_MEM_SIZE);
groups[groups_count] = ecc_curve->iana_id;
groups_count += 1;
}

*groups_count_out = groups_count;

return S2N_SUCCESS;
}
Loading