123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798 |
- From 5ed597eb28c408c5968e6dfb839880ba5fa17ba1 Mon Sep 17 00:00:00 2001
- From: Daiki Ueno <ueno@gnu.org>
- Date: Fri, 6 Dec 2024 09:53:18 +0900
- Subject: [PATCH] groups: represent hybrid groups with an array of IDs
- Previously, the supported_groups array contained externally defined
- elements, which is legitimate in C99 but caused error with Clang:
- groups.c:93:2: error: initializer element is not a compile-time constant
- group_x25519,
- ^~~~~~~~~~~~
- This reworks the array definition of indirection through group
- IDs (gnutls_group_t, i.e., integer).
- This also makes pqc-hybrid-kx test more exhaustive.
- Signed-off-by: Daiki Ueno <ueno@gnu.org>
- Upstream: https://gitlab.com/gnutls/gnutls/-/commit/9cc9d5556d258d23a399abfe45715773e719d134
- Signed-off-by: Brandon Maier <brandon.maier@collins.com>
- ---
- lib/algorithms.h | 7 ++
- lib/algorithms/groups.c | 161 ++++++++++++++++++++------------
- lib/ext/key_share.c | 81 ++++++++++++----
- lib/ext/supported_groups.c | 45 +++++----
- lib/gnutls_int.h | 8 +-
- lib/includes/gnutls/gnutls.h.in | 4 +-
- lib/priority.c | 25 ++---
- lib/session.c | 6 +-
- tests/pqc-hybrid-kx.sh | 101 +++++++++++++++++---
- 9 files changed, 315 insertions(+), 123 deletions(-)
- diff --git a/lib/algorithms.h b/lib/algorithms.h
- index 2e1b694c6..c4af571ce 100644
- --- a/lib/algorithms.h
- +++ b/lib/algorithms.h
- @@ -55,6 +55,9 @@
- #define IS_KEM(x) \
- (((x) == GNUTLS_PK_MLKEM768) || ((x) == GNUTLS_PK_EXP_KYBER768))
-
- +
- +#define IS_GROUP_HYBRID(group) ((group)->ids[0] != GNUTLS_GROUP_INVALID)
- +
- #define SIG_SEM_PRE_TLS12 (1 << 1)
- #define SIG_SEM_TLS13 (1 << 2)
- #define SIG_SEM_DEFAULT (SIG_SEM_PRE_TLS12 | SIG_SEM_TLS13)
- @@ -493,6 +496,10 @@ const gnutls_group_entry_st *_gnutls_tls_id_to_group(unsigned num);
- const gnutls_group_entry_st *_gnutls_id_to_group(unsigned id);
- gnutls_group_t _gnutls_group_get_id(const char *name);
-
- +int _gnutls_group_expand(
- + const gnutls_group_entry_st *group,
- + const gnutls_group_entry_st *subgroups[MAX_HYBRID_GROUPS + 1]);
- +
- gnutls_ecc_curve_t _gnutls_ecc_bits_to_curve(gnutls_pk_algorithm_t pk,
- int bits);
- #define MAX_ECC_CURVE_SIZE 66
- diff --git a/lib/algorithms/groups.c b/lib/algorithms/groups.c
- index 88d0cf630..2fbe7b8ec 100644
- --- a/lib/algorithms/groups.c
- +++ b/lib/algorithms/groups.c
- @@ -30,30 +30,6 @@
- /* Supported ECC curves
- */
-
- -#ifdef HAVE_LIBOQS
- -static const gnutls_group_entry_st group_mlkem768 = {
- - .name = "MLKEM768",
- - .id = GNUTLS_GROUP_INVALID,
- - .curve = GNUTLS_ECC_CURVE_INVALID,
- - .pk = GNUTLS_PK_MLKEM768,
- -};
- -
- -static const gnutls_group_entry_st group_kyber768 = {
- - .name = "KYBER768",
- - .id = GNUTLS_GROUP_INVALID,
- - .curve = GNUTLS_ECC_CURVE_INVALID,
- - .pk = GNUTLS_PK_EXP_KYBER768,
- -};
- -#endif
- -
- -static const gnutls_group_entry_st group_x25519 = {
- - .name = "X25519",
- - .id = GNUTLS_GROUP_X25519,
- - .curve = GNUTLS_ECC_CURVE_X25519,
- - .tls_id = 29,
- - .pk = GNUTLS_PK_ECDH_X25519,
- -};
- -
- static const gnutls_group_entry_st supported_groups[] = {
- {
- .name = "SECP192R1",
- @@ -90,7 +66,13 @@ static const gnutls_group_entry_st supported_groups[] = {
- .tls_id = 25,
- .pk = GNUTLS_PK_ECDSA,
- },
- - group_x25519,
- + {
- + .name = "X25519",
- + .id = GNUTLS_GROUP_X25519,
- + .curve = GNUTLS_ECC_CURVE_X25519,
- + .tls_id = 29,
- + .pk = GNUTLS_PK_ECDH_X25519,
- + },
- #ifdef ENABLE_GOST
- /* draft-smyshlyaev-tls12-gost-suites-06, Section 6 */
- {
- @@ -191,24 +173,33 @@ static const gnutls_group_entry_st supported_groups[] = {
- .tls_id = 0x104 },
- #endif
- #ifdef HAVE_LIBOQS
- + {
- + .name = "MLKEM768",
- + .id = GNUTLS_GROUP_EXP_MLKEM768,
- + .pk = GNUTLS_PK_MLKEM768,
- + /* absense of .tls_id means that this group alone cannot be used in TLS */
- + },
- + {
- + .name = "KYBER768",
- + .id = GNUTLS_GROUP_EXP_KYBER768,
- + .pk = GNUTLS_PK_EXP_KYBER768,
- + /* absense of .tls_id means that this group alone cannot be used in TLS */
- + },
- { .name = "SECP256R1-MLKEM768",
- .id = GNUTLS_GROUP_EXP_SECP256R1_MLKEM768,
- - .curve = GNUTLS_ECC_CURVE_SECP256R1,
- - .pk = GNUTLS_PK_ECDSA,
- - .tls_id = 0x11EB,
- - .next = &group_mlkem768 },
- + .ids = { GNUTLS_GROUP_SECP256R1, GNUTLS_GROUP_EXP_MLKEM768,
- + GNUTLS_GROUP_INVALID },
- + .tls_id = 0x11EB },
- { .name = "X25519-MLKEM768",
- .id = GNUTLS_GROUP_EXP_X25519_MLKEM768,
- - .curve = GNUTLS_ECC_CURVE_INVALID,
- - .pk = GNUTLS_PK_MLKEM768,
- - .tls_id = 0x11EC,
- - .next = &group_x25519 },
- + .ids = { GNUTLS_GROUP_EXP_MLKEM768, GNUTLS_GROUP_X25519,
- + GNUTLS_GROUP_INVALID },
- + .tls_id = 0x11EC },
- { .name = "X25519-KYBER768",
- .id = GNUTLS_GROUP_EXP_X25519_KYBER768,
- - .curve = GNUTLS_ECC_CURVE_X25519,
- - .pk = GNUTLS_PK_ECDH_X25519,
- - .tls_id = 0x6399,
- - .next = &group_kyber768 },
- + .ids = { GNUTLS_GROUP_X25519, GNUTLS_GROUP_EXP_KYBER768,
- + GNUTLS_GROUP_INVALID },
- + .tls_id = 0x6399 },
- #endif
- { 0, 0, 0 }
- };
- @@ -221,14 +212,46 @@ static const gnutls_group_entry_st supported_groups[] = {
- } \
- }
-
- +static inline const gnutls_group_entry_st *group_to_entry(gnutls_group_t group)
- +{
- + if (group == 0)
- + return NULL;
- +
- + GNUTLS_GROUP_LOOP(if (p->id == group) { return p; });
- +
- + return NULL;
- +}
- +
- +static inline bool
- +group_is_supported_standalone(const gnutls_group_entry_st *group)
- +{
- + return group->pk != 0 && _gnutls_pk_exists(group->pk) &&
- + (group->curve == 0 ||
- + _gnutls_ecc_curve_is_supported(group->curve));
- +}
- +
- +static inline bool group_is_supported(const gnutls_group_entry_st *group)
- +{
- + if (!IS_GROUP_HYBRID(group))
- + return group_is_supported_standalone(group);
- +
- + for (size_t i = 0;
- + i < MAX_HYBRID_GROUPS && group->ids[i] != GNUTLS_GROUP_INVALID;
- + i++) {
- + const gnutls_group_entry_st *p = group_to_entry(group->ids[i]);
- + if (!p || !group_is_supported_standalone(p))
- + return false;
- + }
- +
- + return true;
- +}
- +
- /* Returns the TLS id of the given curve
- */
- const gnutls_group_entry_st *_gnutls_tls_id_to_group(unsigned num)
- {
- GNUTLS_GROUP_LOOP(
- - if (p->tls_id == num &&
- - (p->curve == 0 ||
- - _gnutls_ecc_curve_is_supported(p->curve))) { return p; });
- + if (p->tls_id == num && group_is_supported(p)) { return p; });
-
- return NULL;
- }
- @@ -239,10 +262,7 @@ const gnutls_group_entry_st *_gnutls_id_to_group(unsigned id)
- return NULL;
-
- GNUTLS_GROUP_LOOP(
- - if (p->id == id && (p->curve == 0 ||
- - _gnutls_ecc_curve_is_supported(p->curve))) {
- - return p;
- - });
- + if (p->id == id && group_is_supported(p)) { return p; });
-
- return NULL;
- }
- @@ -261,27 +281,17 @@ const gnutls_group_entry_st *_gnutls_id_to_group(unsigned id)
- **/
- const gnutls_group_t *gnutls_group_list(void)
- {
- - static gnutls_group_t groups[MAX_ALGOS] = { 0 };
- + static gnutls_group_t groups[MAX_ALGOS + 1] = { 0 };
-
- if (groups[0] == 0) {
- - int i = 0;
- + size_t i = 0;
-
- - const gnutls_group_entry_st *p;
- -
- - for (p = supported_groups; p->name != NULL; p++) {
- - const gnutls_group_entry_st *pp;
- -
- - for (pp = p; pp != NULL; pp = pp->next) {
- - if ((pp->curve != 0 &&
- - !_gnutls_ecc_curve_is_supported(
- - pp->curve)) ||
- - (pp->pk != 0 && !_gnutls_pk_exists(pp->pk)))
- - break;
- - }
- - if (pp == NULL)
- + for (const gnutls_group_entry_st *p = supported_groups;
- + p->name != NULL; p++) {
- + if (group_is_supported(p))
- groups[i++] = p->id;
- }
- - groups[i++] = 0;
- + groups[i++] = GNUTLS_GROUP_INVALID;
- }
-
- return groups;
- @@ -344,3 +354,34 @@ const char *gnutls_group_get_name(gnutls_group_t group)
-
- return NULL;
- }
- +
- +/* Expand GROUP into hybrid SUBGROUPS if any, otherwise an array
- + * containing the GROUP itself. The result will be written to
- + * SUBGROUPS, which will be NUL-terminated.
- + */
- +int _gnutls_group_expand(
- + const gnutls_group_entry_st *group,
- + const gnutls_group_entry_st *subgroups[MAX_HYBRID_GROUPS + 1])
- +{
- + size_t pos = 0;
- +
- + if (IS_GROUP_HYBRID(group)) {
- + for (size_t i = 0; i < MAX_HYBRID_GROUPS &&
- + group->ids[i] != GNUTLS_GROUP_INVALID;
- + i++) {
- + const gnutls_group_entry_st *p =
- + group_to_entry(group->ids[i]);
- + /* This shouldn't happen, as GROUP is assumed
- + * to be supported before calling this
- + * function. */
- + if (unlikely(!p))
- + return gnutls_assert_val(
- + GNUTLS_E_INTERNAL_ERROR);
- + subgroups[pos++] = p;
- + }
- + } else {
- + subgroups[pos++] = group;
- + }
- + subgroups[pos] = NULL;
- + return 0;
- +}
- diff --git a/lib/ext/key_share.c b/lib/ext/key_share.c
- index 574521157..8fbe2d2bd 100644
- --- a/lib/ext/key_share.c
- +++ b/lib/ext/key_share.c
- @@ -232,6 +232,9 @@ static int client_gen_key_share(gnutls_session_t session,
- gnutls_buffer_st *extdata)
- {
- unsigned int length_pos;
- + const gnutls_group_entry_st *groups[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- int ret;
-
- _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session,
- @@ -247,8 +250,12 @@ static int client_gen_key_share(gnutls_session_t session,
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- - for (const gnutls_group_entry_st *p = group; p != NULL; p = p->next) {
- - ret = client_gen_key_share_single(session, p, extdata);
- + ret = _gnutls_group_expand(group, groups);
- + if (ret < 0)
- + return gnutls_assert_val(ret);
- +
- + for (size_t i = 0; groups[i]; i++) {
- + ret = client_gen_key_share_single(session, groups[i], extdata);
- if (ret < 0)
- return gnutls_assert_val(ret);
- }
- @@ -345,6 +352,9 @@ static int server_gen_key_share(gnutls_session_t session,
- gnutls_buffer_st *extdata)
- {
- unsigned int length_pos;
- + const gnutls_group_entry_st *groups[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- int ret;
-
- _gnutls_handshake_log("EXT[%p]: sending key share for %s\n", session,
- @@ -360,8 +370,12 @@ static int server_gen_key_share(gnutls_session_t session,
- if (ret < 0)
- return gnutls_assert_val(ret);
-
- - for (const gnutls_group_entry_st *p = group; p != NULL; p = p->next) {
- - ret = server_gen_key_share_single(session, p, extdata);
- + ret = _gnutls_group_expand(group, groups);
- + if (ret < 0)
- + return gnutls_assert_val(ret);
- +
- + for (size_t i = 0; groups[i]; i++) {
- + ret = server_gen_key_share_single(session, groups[i], extdata);
- if (ret < 0)
- return gnutls_assert_val(ret);
- }
- @@ -594,13 +608,19 @@ static int server_use_key_share(gnutls_session_t session,
- const uint8_t *data, size_t data_size)
- {
- gnutls_buffer_st buffer;
- + const gnutls_group_entry_st *groups[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- + int ret;
-
- _gnutls_ro_buffer_init(&buffer, data, data_size);
-
- - for (const gnutls_group_entry_st *p = group; p != NULL; p = p->next) {
- - int ret;
- + ret = _gnutls_group_expand(group, groups);
- + if (ret < 0)
- + return gnutls_assert_val(ret);
-
- - ret = server_use_key_share_single(session, p, &buffer);
- + for (size_t i = 0; groups[i]; i++) {
- + ret = server_use_key_share_single(session, groups[i], &buffer);
- if (ret < 0)
- return gnutls_assert_val(ret);
- }
- @@ -775,13 +795,19 @@ static int client_use_key_share(gnutls_session_t session,
- const uint8_t *data, size_t data_size)
- {
- gnutls_buffer_st buffer;
- + const gnutls_group_entry_st *groups[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- + int ret;
-
- _gnutls_ro_buffer_init(&buffer, data, data_size);
-
- - for (const gnutls_group_entry_st *p = group; p != NULL; p = p->next) {
- - int ret;
- + ret = _gnutls_group_expand(group, groups);
- + if (ret < 0)
- + return gnutls_assert_val(ret);
-
- - ret = client_use_key_share_single(session, p, &buffer);
- + for (size_t i = 0; groups[i]; i++) {
- + ret = client_use_key_share_single(session, groups[i], &buffer);
- if (ret < 0)
- return gnutls_assert_val(ret);
- }
- @@ -958,18 +984,39 @@ static int key_share_recv_params(gnutls_session_t session, const uint8_t *data,
- return 0;
- }
-
- +static inline bool pk_types_overlap_single(const gnutls_group_entry_st *a,
- + const gnutls_group_entry_st *b)
- +{
- + return a->pk == b->pk || (IS_ECDHX(a->pk) && IS_ECDHX(b->pk)) ||
- + (IS_KEM(a->pk) && IS_KEM(b->pk));
- +}
- +
- static inline bool pk_types_overlap(const gnutls_group_entry_st *a,
- const gnutls_group_entry_st *b)
- {
- - const gnutls_group_entry_st *pa;
- + const gnutls_group_entry_st *sa[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- + const gnutls_group_entry_st *sb[MAX_HYBRID_GROUPS + 1] = {
- + NULL,
- + };
- + int ret;
- +
- + ret = _gnutls_group_expand(a, sa);
- + if (ret < 0) {
- + gnutls_assert();
- + return false;
- + }
-
- - for (pa = a; pa != NULL; pa = pa->next) {
- - const gnutls_group_entry_st *pb;
- + ret = _gnutls_group_expand(b, sb);
- + if (ret < 0) {
- + gnutls_assert();
- + return false;
- + }
-
- - for (pb = b; pb != NULL; pb = pb->next) {
- - if (pa->pk == pb->pk ||
- - (IS_ECDHX(pa->pk) && IS_ECDHX(pb->pk)) ||
- - (IS_KEM(pa->pk) && IS_KEM(pb->pk)))
- + for (size_t i = 0; sa[i]; i++) {
- + for (size_t j = 0; sb[j]; j++) {
- + if (pk_types_overlap_single(sa[i], sb[j]))
- return true;
- }
- }
- diff --git a/lib/ext/supported_groups.c b/lib/ext/supported_groups.c
- index 254ec4882..4c31d2f8f 100644
- --- a/lib/ext/supported_groups.c
- +++ b/lib/ext/supported_groups.c
- @@ -106,9 +106,9 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- unsigned min_dh;
- unsigned j;
- int serv_ec_idx, serv_dh_idx,
- - serv_kem_idx; /* index in server's priority listing */
- + serv_hybrid_idx; /* index in server's priority listing */
- int cli_ec_pos, cli_dh_pos,
- - cli_kem_pos; /* position in listing sent by client */
- + cli_hybrid_pos; /* position in listing sent by client */
-
- if (session->security_parameters.entity == GNUTLS_CLIENT) {
- /* A client shouldn't receive this extension in TLS1.2. It is
- @@ -134,8 +134,8 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- /* we figure what is the minimum DH allowed for this session, if any */
- min_dh = get_min_dh(session);
-
- - serv_ec_idx = serv_dh_idx = serv_kem_idx = -1;
- - cli_ec_pos = cli_dh_pos = cli_kem_pos = -1;
- + serv_ec_idx = serv_dh_idx = serv_hybrid_idx = -1;
- + cli_ec_pos = cli_dh_pos = cli_hybrid_pos = -1;
-
- /* This extension is being processed prior to a ciphersuite being selected,
- * so we cannot rely on ciphersuite information. */
- @@ -180,14 +180,15 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- break;
- serv_ec_idx = j;
- cli_ec_pos = i;
- - } else if (IS_KEM(group->pk)) {
- - if (serv_kem_idx !=
- + } else if (IS_GROUP_HYBRID(
- + group)) {
- + if (serv_hybrid_idx !=
- -1 &&
- (int)j >
- - serv_kem_idx)
- + serv_hybrid_idx)
- break;
- - serv_kem_idx = j;
- - cli_kem_pos = i;
- + serv_hybrid_idx = j;
- + cli_hybrid_pos = i;
- }
- } else {
- if (group->pk == GNUTLS_PK_DH) {
- @@ -200,11 +201,13 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- break;
- cli_ec_pos = i;
- serv_ec_idx = j;
- - } else if (IS_KEM(group->pk)) {
- - if (cli_kem_pos != -1)
- + } else if (IS_GROUP_HYBRID(
- + group)) {
- + if (cli_hybrid_pos !=
- + -1)
- break;
- - cli_kem_pos = i;
- - serv_kem_idx = j;
- + cli_hybrid_pos = i;
- + serv_hybrid_idx = j;
- }
- }
- break;
- @@ -212,7 +215,7 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- }
- }
-
- - /* serv_{dh,ec,kem}_idx contain the index of the groups we want to use.
- + /* serv_{dh,ec,hybrid}_idx contain the index of the groups we want to use.
- */
- if (serv_dh_idx != -1) {
- session->internals.cand_dh_group =
- @@ -236,18 +239,20 @@ static int _gnutls_supported_groups_recv_params(gnutls_session_t session,
- }
- }
-
- - /* KEM can only be used in TLS 1.3, where no separation from
- - * ECDH and DH, and thus only cand_group is set here.
- + /* PQC hybrid key exchange groups can only be used in
- + * TLS 1.3, where no distinction between ECDH and DH
- + * in the group definitions, and thus only cand_group
- + * is set here.
- */
- - if (serv_kem_idx != -1) {
- + if (serv_hybrid_idx != -1) {
- if (session->internals.cand_group == NULL ||
- (session->internals.priorities->server_precedence &&
- - serv_kem_idx < MIN(serv_ec_idx, serv_dh_idx)) ||
- + serv_hybrid_idx < MIN(serv_ec_idx, serv_dh_idx)) ||
- (!session->internals.priorities->server_precedence &&
- - cli_kem_pos < MIN(cli_ec_pos, cli_dh_pos))) {
- + cli_hybrid_pos < MIN(cli_ec_pos, cli_dh_pos))) {
- session->internals.cand_group =
- session->internals.priorities->groups
- - .entry[serv_kem_idx];
- + .entry[serv_hybrid_idx];
- }
- }
-
- diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
- index fb2cacb54..01ef59729 100644
- --- a/lib/gnutls_int.h
- +++ b/lib/gnutls_int.h
- @@ -756,6 +756,8 @@ typedef struct gnutls_cipher_suite_entry_st {
- gnutls_mac_algorithm_t prf;
- } gnutls_cipher_suite_entry_st;
-
- +#define MAX_HYBRID_GROUPS 2
- +
- typedef struct gnutls_group_entry_st {
- const char *name;
- gnutls_group_t id;
- @@ -765,8 +767,12 @@ typedef struct gnutls_group_entry_st {
- const unsigned *q_bits;
- gnutls_ecc_curve_t curve;
- gnutls_pk_algorithm_t pk;
- + gnutls_group_t ids[MAX_HYBRID_GROUPS + 1]; /* IDs of subgroups
- + * comprising a
- + * hybrid group,
- + * terminated with
- + * GNUTLS_GROUP_INVALID */
- unsigned tls_id; /* The RFC4492 namedCurve ID or TLS 1.3 group ID */
- - const struct gnutls_group_entry_st *next;
- } gnutls_group_entry_st;
-
- #define GNUTLS_MAC_FLAG_PREIMAGE_INSECURE \
- diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in
- index 8b3bb5213..1e44fdd91 100644
- --- a/lib/includes/gnutls/gnutls.h.in
- +++ b/lib/includes/gnutls/gnutls.h.in
- @@ -1147,8 +1147,10 @@ typedef enum {
- GNUTLS_GROUP_EXP_X25519_KYBER768 = 512,
- GNUTLS_GROUP_EXP_SECP256R1_MLKEM768 = 513,
- GNUTLS_GROUP_EXP_X25519_MLKEM768 = 514,
- + GNUTLS_GROUP_EXP_KYBER768 = 515,
- + GNUTLS_GROUP_EXP_MLKEM768 = 516,
- GNUTLS_GROUP_EXP_MIN = GNUTLS_GROUP_EXP_X25519_KYBER768,
- - GNUTLS_GROUP_EXP_MAX = GNUTLS_GROUP_EXP_X25519_MLKEM768
- + GNUTLS_GROUP_EXP_MAX = GNUTLS_GROUP_EXP_MLKEM768
- } gnutls_group_t;
-
- /* macros to allow specifying a specific curve in gnutls_privkey_generate()
- diff --git a/lib/priority.c b/lib/priority.c
- index ac4ff2d8c..479dbccd6 100644
- --- a/lib/priority.c
- +++ b/lib/priority.c
- @@ -2566,7 +2566,7 @@ static void add_dh(gnutls_priority_t priority_cache)
- }
- }
-
- -static void add_kem(gnutls_priority_t priority_cache)
- +static void add_hybrid(gnutls_priority_t priority_cache)
- {
- const gnutls_group_entry_st *ge;
- unsigned i;
- @@ -2579,7 +2579,7 @@ static void add_kem(gnutls_priority_t priority_cache)
- sizeof(priority_cache->groups.entry) /
- sizeof(priority_cache->groups.entry[0])) {
- /* do not add groups which do not correspond to enabled ciphersuites */
- - if (!IS_KEM(ge->pk))
- + if (!IS_GROUP_HYBRID(ge))
- continue;
- priority_cache->groups
- .entry[priority_cache->groups.size++] = ge;
- @@ -2598,7 +2598,7 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
- const gnutls_sign_entry_st *se;
- unsigned have_ec = 0;
- unsigned have_dh = 0;
- - unsigned have_kem = 0;
- + unsigned have_hybrid = 0;
- unsigned tls_sig_sem = 0;
- const version_entry_st *tlsmax = NULL, *vers;
- const version_entry_st *dtlsmax = NULL;
- @@ -2807,9 +2807,9 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
- priority_cache->cs.entry[priority_cache->cs.size++] =
- ce;
-
- - if (!have_kem) {
- - have_kem = 1;
- - add_kem(priority_cache);
- + if (!have_hybrid) {
- + have_hybrid = 1;
- + add_hybrid(priority_cache);
- }
- }
- }
- @@ -2851,8 +2851,8 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
- }
- }
-
- - if (have_tls13 && (!have_ec || !have_dh || !have_kem)) {
- - /* scan groups to determine have_{ec,dh,kem} */
- + if (have_tls13 && (!have_ec || !have_dh || !have_hybrid)) {
- + /* scan groups to determine have_{ec,dh,hybrid} */
- for (i = 0; i < priority_cache->_supported_ecc.num_priorities;
- i++) {
- const gnutls_group_entry_st *ge;
- @@ -2865,12 +2865,13 @@ static int set_ciphersuite_list(gnutls_priority_t priority_cache)
- } else if (ge->prime && !have_dh) {
- add_dh(priority_cache);
- have_dh = 1;
- - } else if (IS_KEM(ge->pk) && !have_kem) {
- - add_kem(priority_cache);
- - have_kem = 1;
- + } else if (IS_GROUP_HYBRID(ge) &&
- + !have_hybrid) {
- + add_hybrid(priority_cache);
- + have_hybrid = 1;
- }
-
- - if (have_dh && have_ec && have_kem)
- + if (have_dh && have_ec && have_hybrid)
- break;
- }
- }
- diff --git a/lib/session.c b/lib/session.c
- index a9049a464..7fcbe4fb4 100644
- --- a/lib/session.c
- +++ b/lib/session.c
- @@ -415,7 +415,11 @@ char *gnutls_session_get_desc(gnutls_session_t session)
- snprintf(kx_name, sizeof(kx_name), "(PSK)");
- }
- } else if (group && sign_str) {
- - if (group->curve)
- + if (IS_GROUP_HYBRID(group))
- + snprintf(kx_name, sizeof(kx_name),
- + "(HYBRID-%s)-(%s)", group_name,
- + sign_str);
- + else if (group->curve)
- snprintf(kx_name, sizeof(kx_name),
- "(ECDHE-%s)-(%s)", group_name,
- sign_str);
- diff --git a/tests/pqc-hybrid-kx.sh b/tests/pqc-hybrid-kx.sh
- index da936cf04..4984cd4b4 100644
- --- a/tests/pqc-hybrid-kx.sh
- +++ b/tests/pqc-hybrid-kx.sh
- @@ -33,34 +33,113 @@
-
- . "${srcdir}/scripts/common.sh"
-
- +# First check any mismatch in the gnutls-cli --list
- if ! "${CLI}" --list | grep '^Groups: .*GROUP-X25519-KYBER768.*' >/dev/null; then
- if "${CLI}" --list | grep '^Public Key Systems: .*KYBER768.*' >/dev/null; then
- - fail "KYBER768 is in Public Key Systems, while GROUP-X25519-KYBER768 is NOT in Groups"
- + fail '' 'KYBER768 is in Public Key Systems, while GROUP-X25519-KYBER768 is NOT in Groups'
- fi
- - exit 77
- else
- if ! "${CLI}" --list | grep '^Public Key Systems: .*KYBER768.*' >/dev/null; then
- - fail "KYBER768 is NOT in Public Key Systems, while GROUP-X25519-KYBER768 is in Groups"
- + fail '' 'KYBER768 is NOT in Public Key Systems, while GROUP-X25519-KYBER768 is in Groups'
- + fi
- +fi
- +
- +if ! "${CLI}" --list | grep '^Groups: .*GROUP-\(SECP256R1\|X25519\)-MLKEM768.*' >/dev/null; then
- + if "${CLI}" --list | grep '^Public Key Systems: .*ML-KEM-768.*' >/dev/null; then
- + fail '' 'ML-KEM-768 is in Public Key Systems, while GROUP-SECP256R1-MLKEM768 or GROUP-X25519-MLKEM768 is NOT in Groups'
- + fi
- +else
- + if ! "${CLI}" --list | grep '^Public Key Systems: .*ML-KEM-768.*' >/dev/null; then
- + fail '' 'ML-KEM-768 is NOT in Public Key Systems, while GROUP-SECP256R1-MLKEM768 or GROUP-X25519-MLKEM768 is in Groups'
- fi
- fi
-
- +# If none of those hybrid groups is supported, skip the test
- +if ! "${CLI}" --list | grep '^Groups: .*GROUP-\(X25519-KYBER768\|SECP256R1-MLKEM768\|X25519-MLKEM768\).*' >/dev/null; then
- + exit 77
- +fi
- +
- testdir=`create_testdir pqc-hybrid-kx`
-
- KEY="$srcdir/../doc/credentials/x509/key-ecc.pem"
- CERT="$srcdir/../doc/credentials/x509/cert-ecc.pem"
- CACERT="$srcdir/../doc/credentials/x509/ca.pem"
-
- -eval "${GETPORT}"
- -launch_server --echo --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509keyfile="$KEY" --x509certfile="$CERT"
- -PID=$!
- -wait_server ${PID}
- +# Test all supported hybrid groups
- +for group in X25519-KYBER768 SECP256R1-MLKEM768 X25519-MLKEM768; do
- + if ! "${CLI}" --list | grep "^Groups: .*GROUP-$group.*" >/dev/null; then
- + echo "$group is not supported, skipping" >&2
- + continue
- + fi
- +
- + eval "${GETPORT}"
- + launch_server --echo --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509keyfile="$KEY" --x509certfile="$CERT"
- + PID=$!
- + wait_server ${PID}
- +
- + ${VALGRIND} "${CLI}" -p "${PORT}" localhost --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
- + kill ${PID}
- + wait
- +
- + grep -- "- Description: (TLS1.3-X.509)-(HYBRID-$group)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)" "$testdir/cli.log" || { echo "unexpected handshake description"; cat "$testdir/cli.log"; exit 1; }
- +done
- +
- +# KEM based groups cannot be used standalone
- +for group in KYBER768 MLKEM768; do
- + if ! "${CLI}" --list | grep "^Groups: .*GROUP-$group.*" >/dev/null; then
- + "$group is not supported, skipping"
- + continue
- + fi
- +
- + eval "${GETPORT}"
- + launch_server --echo --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509keyfile="$KEY" --x509certfile="$CERT"
- + PID=$!
- + wait_server ${PID}
- +
- + ${VALGRIND} "${CLI}" -p "${PORT}" localhost --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
- + rc=$?
- + kill ${PID}
- + wait
- +
- + if test $rc -eq 0; then
- + fail '' 'Handshake succeeded with a standalone KEM group'
- + fi
- +done
- +
- +# Check if disabling a curve will also disables hybrid groups with it
- +cat <<_EOF_ > "$testdir/test.config"
- +[overrides]
- +
- +disabled-curve = x25519
- +_EOF_
- +
- +for group in X25519-KYBER768 SECP256R1-MLKEM768 X25519-MLKEM768; do
- + if ! "${CLI}" --list | grep "^Groups: .*GROUP-$group.*" >/dev/null; then
- + echo "$group is not supported, skipping" >&2
- + continue
- + fi
-
- -${VALGRIND} "${CLI}" -p "${PORT}" localhost --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
- + eval "${GETPORT}"
- + GNUTLS_SYSTEM_PRIORITY_FILE="$testdir/test.config" launch_server --echo --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509keyfile="$KEY" --x509certfile="$CERT"
- + PID=$!
- + wait_server ${PID}
-
- -kill ${PID}
- -wait
- + ${VALGRIND} "${CLI}" -p "${PORT}" localhost --priority "NORMAL:-GROUP-ALL:+GROUP-$group" --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
- + rc=$?
- + kill ${PID}
- + wait
-
- -grep -- '- Description: (TLS1.3-X.509)-(ECDHE-X25519-KYBER768)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)' "$testdir/cli.log" || { echo "unexpected handshake description"; exit 1; }
- + case "$group" in
- + X25519*)
- + if test $rc -eq 0; then
- + fail '' 'Handshake succeeded with a hybrid group with X25519'
- + fi
- + ;;
- + *)
- + grep -- "- Description: (TLS1.3-X.509)-(HYBRID-$group)-(ECDSA-SECP256R1-SHA256)-(AES-256-GCM)" "$testdir/cli.log" || { echo "unexpected handshake description"; cat "$testdir/cli.log"; exit 1; }
- + ;;
- + esac
- +done
-
- rm -rf "$testdir"
- exit 0
- --
- 2.47.1
|