Skip to content

Commit

Permalink
Add API to retrieve the supported groups for a security policy (aws#4273
Browse files Browse the repository at this point in the history
)
  • Loading branch information
goatgoose committed Nov 13, 2023
1 parent 1a58649 commit b82a5d4
Show file tree
Hide file tree
Showing 3 changed files with 179 additions and 0 deletions.
28 changes: 28 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -3391,6 +3391,34 @@ 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. This API
* can be used with the s2n_client_hello_get_supported_groups() API as a means of comparing compatibility between
* a client and server.
*
* 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 only the groups from a security policy that are available to negotiate via the
* supported groups extension, and does not return TLS 1.2 PQ kem groups that are negotiated in the supported PQ
* kem parameters extension.
*
* @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),
&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);
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. */
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,
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++) {
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;
}

0 comments on commit b82a5d4

Please sign in to comment.