diff --git a/src/tls.c b/src/tls.c index e060476f4b0..df592f9b2ca 100644 --- a/src/tls.c +++ b/src/tls.c @@ -5004,21 +5004,386 @@ int TLSX_IsGroupSupported(int namedGroup, int side) define HAVE_ECC. Alternatively use FFDHE for DH cipher suites. #endif -static int TLSX_SupportedCurve_New(SupportedCurve** curve, word16 name, +/* The groups this build offers, most preferred first. + * TLSX_DefaultGroupAllowed() applies the runtime conditions. */ +static const word16 preferredGroup[] = { + /* Sort by strength, but prefer non-experimental PQ/T hybrid groups */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_PQC_HYBRIDS) + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519) && \ + ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519MLKEM768, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 384 + WOLFSSL_SECP384R1MLKEM1024, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 256 + WOLFSSL_SECP256R1MLKEM768, + #endif +#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && !WOLFSSL_NO_ML_KEM && + * WOLFSSL_PQC_HYBRIDS */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_1024) && \ + !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) + WOLFSSL_ML_KEM_1024, +#endif +#if defined(HAVE_ECC) && (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + !defined(NO_ECC_SECP) && ECC_MIN_KEY_SZ <= 521 + WOLFSSL_ECC_SECP521R1, +#endif +#if defined(HAVE_ECC) && (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && \ + defined(HAVE_ECC_BRAINPOOL) && ECC_MIN_KEY_SZ <= 512 + WOLFSSL_ECC_BRAINPOOLP512R1TLS13, + WOLFSSL_ECC_BRAINPOOLP512R1, +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_768) && \ + !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) + WOLFSSL_ML_KEM_768, +#endif +#if defined(HAVE_ECC) && (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 384 + #ifndef NO_ECC_SECP + WOLFSSL_ECC_SECP384R1, + #endif + #ifdef HAVE_ECC_BRAINPOOL + WOLFSSL_ECC_BRAINPOOLP384R1TLS13, + WOLFSSL_ECC_BRAINPOOLP384R1, + #endif +#endif +#if !defined(HAVE_FIPS) && defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + WOLFSSL_ECC_X448, +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_512) && \ + !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) + WOLFSSL_ML_KEM_512, +#endif +#if defined(HAVE_ECC) && (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 256 + #ifndef NO_ECC_SECP + WOLFSSL_ECC_SECP256R1, + #endif + #ifdef HAVE_ECC_KOBLITZ + WOLFSSL_ECC_SECP256K1, + #endif + #ifdef HAVE_ECC_BRAINPOOL + WOLFSSL_ECC_BRAINPOOLP256R1TLS13, + WOLFSSL_ECC_BRAINPOOLP256R1, + #endif + #if !defined(HAVE_FIPS) && defined(WOLFSSL_SM2) + WOLFSSL_ECC_SM2P256V1, + #endif +#endif +#if !defined(HAVE_FIPS) && defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_ECC_X25519, +#endif +#if defined(HAVE_ECC) && (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 224 + #ifndef NO_ECC_SECP + WOLFSSL_ECC_SECP224R1, + #endif + #ifdef HAVE_ECC_KOBLITZ + WOLFSSL_ECC_SECP224K1, + #endif +#endif +#if !defined(HAVE_FIPS) && defined(HAVE_ECC) + #if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 192 + #ifndef NO_ECC_SECP + WOLFSSL_ECC_SECP192R1, + #endif + #ifdef HAVE_ECC_KOBLITZ + WOLFSSL_ECC_SECP192K1, + #endif + #endif + #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 160 + #ifndef NO_ECC_SECP + WOLFSSL_ECC_SECP160R1, + #endif + #ifdef HAVE_ECC_SECPR2 + WOLFSSL_ECC_SECP160R2, + #endif + #ifdef HAVE_ECC_KOBLITZ + WOLFSSL_ECC_SECP160K1, + #endif + #endif +#endif /* !HAVE_FIPS && HAVE_ECC */ +#if defined(HAVE_FFDHE_8192) + WOLFSSL_FFDHE_8192, +#endif +#if defined(HAVE_FFDHE_6144) + WOLFSSL_FFDHE_6144, +#endif +#if defined(HAVE_FFDHE_4096) + WOLFSSL_FFDHE_4096, +#endif +#if defined(HAVE_FFDHE_3072) + WOLFSSL_FFDHE_3072, +#endif +#if defined(HAVE_FFDHE_2048) + WOLFSSL_FFDHE_2048, +#endif +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_EXTRA_PQC_HYBRIDS) + #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ + (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 521 + WOLFSSL_SECP521R1MLKEM1024, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 384 + WOLFSSL_SECP384R1MLKEM768, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) && \ + ECC_MIN_KEY_SZ <= 448 + WOLFSSL_X448MLKEM768, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + ECC_MIN_KEY_SZ <= 256 + WOLFSSL_SECP256R1MLKEM512, + #endif + #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519) && \ + ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519MLKEM512, + #endif +#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && !WOLFSSL_NO_ML_KEM && + * WOLFSSL_EXTRA_PQC_HYBRIDS */ +#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ + defined(WOLFSSL_MLKEM_KYBER) + #ifdef WOLFSSL_KYBER1024 + WOLFSSL_KYBER_LEVEL5, + #if defined(HAVE_ECC) && (defined(HAVE_ECC521) || \ + defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521 + WOLFSSL_P521_KYBER_LEVEL5, + #endif + #endif + #ifdef WOLFSSL_KYBER768 + WOLFSSL_KYBER_LEVEL3, + #if defined(HAVE_ECC) && (defined(HAVE_ECC384) || \ + defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 + WOLFSSL_P384_KYBER_LEVEL3, + #endif + #if defined(HAVE_ECC) && (!defined(NO_ECC256) || \ + defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_P256_KYBER_LEVEL3, + #endif + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_KYBER_LEVEL3, + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + WOLFSSL_X448_KYBER_LEVEL3, + #endif + #endif + #ifdef WOLFSSL_KYBER512 + WOLFSSL_KYBER_LEVEL1, + #if defined(HAVE_ECC) && (!defined(NO_ECC256) || \ + defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_P256_KYBER_LEVEL1, + #endif + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_KYBER_LEVEL1, + #endif + #endif +#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && WOLFSSL_MLKEM_KYBER */ + WOLFSSL_NAMED_GROUP_INVALID +}; + +#define PREFERRED_GROUP_SZ \ + ((sizeof(preferredGroup)/sizeof(*preferredGroup)) - 1) + /* -1 for the invalid group */ + +/* Runtime conditions on a group from preferredGroup[]. + * + * ssl The SSL/TLS object. + * name The group to check. + * returns 1 when the group is offered, 0 otherwise. + */ +static int TLSX_DefaultGroupAllowed(const WOLFSSL* ssl, word16 name) +{ + if (WOLFSSL_NAMED_GROUP_IS_PQC(name) || + WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(name)) { + #ifdef WOLFSSL_HAVE_MLKEM + return IsAtLeastTLSv1_3(ssl->version) && + TLSX_IsMlKemGroupSupported(ssl->options.side); + #else + return 0; + #endif + } + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(name)) { + #ifdef NO_DH + return 0; + #else + word32 sz = 0; + + switch (name) { + case WOLFSSL_FFDHE_2048: sz = 2048 / 8; break; + case WOLFSSL_FFDHE_3072: sz = 3072 / 8; break; + case WOLFSSL_FFDHE_4096: sz = 4096 / 8; break; + case WOLFSSL_FFDHE_6144: sz = 6144 / 8; break; + case WOLFSSL_FFDHE_8192: sz = 8192 / 8; break; + default: break; + } + return sz >= ssl->options.minDhKeySz && sz <= ssl->options.maxDhKeySz; + #endif + } + switch (name) { + /* RFC 8734 codepoints are TLS 1.3 only. */ + case WOLFSSL_ECC_BRAINPOOLP256R1TLS13: + case WOLFSSL_ECC_BRAINPOOLP384R1TLS13: + case WOLFSSL_ECC_BRAINPOOLP512R1TLS13: + return IsAtLeastTLSv1_3(ssl->version); + /* RFC 7027 codepoints, also offered by TLS 1.3 when it may fall back + * to TLS 1.2. */ + case WOLFSSL_ECC_BRAINPOOLP256R1: + case WOLFSSL_ECC_BRAINPOOLP384R1: + case WOLFSSL_ECC_BRAINPOOLP512R1: + return !IsAtLeastTLSv1_3(ssl->version) || + (ssl->options.downgrade && + (ssl->options.minDowngrade <= TLSv1_2_MINOR || + ssl->options.minDowngrade <= DTLSv1_2_MINOR)); + default: + return 1; + } +} + +/* Next group this ssl offers, most preferred first. + * + * ssl The SSL/TLS object. + * idx Iteration state. Set to 0 before the first call. + * returns the group, or WOLFSSL_NAMED_GROUP_INVALID when there are no more. + */ +static word16 TLSX_NextSupportedGroup(const WOLFSSL* ssl, word16* idx) +{ +#ifdef WOLFSSL_TLS13 +#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) + /* Resuming offers only the group of the session. */ + if (ssl->options.resuming && ssl->session->namedGroup != 0) { + if ((*idx)++ == 0) { + return ssl->session->namedGroup; + } + return WOLFSSL_NAMED_GROUP_INVALID; + } +#endif + if (ssl->numGroups != 0) { + if (*idx < ssl->numGroups) { + return ssl->group[(*idx)++]; + } + return WOLFSSL_NAMED_GROUP_INVALID; + } +#endif + while (*idx < PREFERRED_GROUP_SZ) { + word16 name = preferredGroup[(*idx)++]; + + if (TLSX_DefaultGroupAllowed(ssl, name)) + return name; + } + return WOLFSSL_NAMED_GROUP_INVALID; +} + +#if defined(WOLFSSL_ML_KEM_USE_OLD_IDS) && defined(WOLFSSL_EXTRA_PQC_HYBRIDS) + /* TLSX_UseSupportedCurve() adds an old codepoint after three hybrids. */ + #define SUPPORTED_CURVES_OLD_ID_CNT 3 +#else + #define SUPPORTED_CURVES_OLD_ID_CNT 0 +#endif +/* Names the first allocation holds, enough for any default or set_groups() + * list; the list doubles from here. */ +#define SUPPORTED_CURVES_INIT_CNT \ + ((((size_t)WOLFSSL_MAX_GROUP_COUNT > PREFERRED_GROUP_SZ) ? \ + (size_t)WOLFSSL_MAX_GROUP_COUNT : PREFERRED_GROUP_SZ) + \ + SUPPORTED_CURVES_OLD_ID_CNT) +/* Most names the extension can encode: both its lengths are a word16. */ +#define SUPPORTED_CURVES_MAX_CNT ((0xFFFFU - OPAQUE16_LEN) / OPAQUE16_LEN) +wc_static_assert(SUPPORTED_CURVES_MAX_CNT < 0xFFFFU); +wc_static_assert(OPAQUE16_LEN + SUPPORTED_CURVES_MAX_CNT * OPAQUE16_LEN <= + 0xFFFFU); + +/* Make room for the requested number of curve names. + * + * curves The list to grow, allocated when NULL. Updated on reallocation. + * cnt The number of names the list has to hold. + * heap The heap used for allocation. + * returns 0 on success, otherwise failure. + */ +static int TLSX_SupportedCurve_Grow(SupportedCurves** curves, word16 cnt, + void* heap) +{ + SupportedCurves* list = *curves; + SupportedCurves* newList; + word32 cap = (word32)SUPPORTED_CURVES_INIT_CNT; + word32 sz; + word16 count = 0; + + if (list != NULL) { + if (cnt <= list->cap) + return 0; + count = list->count; + } + if (cnt > SUPPORTED_CURVES_MAX_CNT) + return BAD_FUNC_ARG; + + while (cap < cnt) + cap *= 2; + /* The doubling can overshoot the most the extension can encode. */ + if (cap > SUPPORTED_CURVES_MAX_CNT) + cap = SUPPORTED_CURVES_MAX_CNT; + + sz = (word32)(sizeof(SupportedCurves) + cap * sizeof(word16)); + if (list == NULL) { + newList = (SupportedCurves*)XMALLOC(sz, heap, DYNAMIC_TYPE_TLSX); + } + else { + #ifdef XREALLOC + newList = (SupportedCurves*)XREALLOC(list, sz, heap, DYNAMIC_TYPE_TLSX); + #else + newList = (SupportedCurves*)XMALLOC(sz, heap, DYNAMIC_TYPE_TLSX); + if (newList != NULL) { + XMEMCPY(newList->name, list->name, count * sizeof(word16)); + XFREE(list, heap, DYNAMIC_TYPE_TLSX); + } + #endif + } + if (newList == NULL) + return MEMORY_E; + + newList->count = count; + newList->cap = (word16)cap; + + *curves = newList; + + return 0; +} + +/* Allocate a list holding one name. + * + * curves Receives the new list; BAD_FUNC_ARG when it already holds one. + * name The first name to store. + * heap The heap used for allocation. + * returns 0 on success, otherwise failure. + */ +static int TLSX_SupportedCurve_New(SupportedCurves** curves, word16 name, void* heap) { - if (curve == NULL) - return BAD_FUNC_ARG; + SupportedCurves* list = NULL; + int ret; - (void)heap; + if ((curves == NULL) || (*curves != NULL)) + return BAD_FUNC_ARG; - *curve = (SupportedCurve*)XMALLOC(sizeof(SupportedCurve), heap, - DYNAMIC_TYPE_TLSX); - if (*curve == NULL) - return MEMORY_E; + ret = TLSX_SupportedCurve_Grow(&list, 1, heap); + if (ret != 0) + return ret; - (*curve)->name = name; - (*curve)->next = NULL; + list->name[list->count++] = name; + *curves = list; return 0; } @@ -5095,14 +5460,10 @@ static int TLSX_PointFormat_New(PointFormats** formats, byte format, return 0; } -static void TLSX_SupportedCurve_FreeAll(SupportedCurve* list, void* heap) +static void TLSX_SupportedCurve_FreeAll(SupportedCurves* list, void* heap) { - SupportedCurve* curve; - - while ((curve = list)) { - list = curve->next; - XFREE(curve, heap, DYNAMIC_TYPE_TLSX); - } + if (list != NULL) + XFREE(list, heap, DYNAMIC_TYPE_TLSX); (void)heap; } @@ -5113,26 +5474,30 @@ static void TLSX_PointFormat_FreeAll(PointFormats* list, void* heap) (void)heap; } -static int TLSX_SupportedCurve_Append(SupportedCurve* list, word16 name, +static int TLSX_SupportedCurve_Append(SupportedCurves** curves, word16 name, void* heap) { - int ret = WC_NO_ERR_TRACE(BAD_FUNC_ARG); - - while (list) { - if (list->name == name) { - ret = 0; /* curve already in use */ - break; - } + SupportedCurves* list; + int ret; + word16 i; - if (list->next == NULL) { - ret = TLSX_SupportedCurve_New(&list->next, name, heap); - break; - } + if ((curves == NULL) || (*curves == NULL)) + return BAD_FUNC_ARG; - list = list->next; + list = *curves; + for (i = 0; i < list->count; i++) { + if (list->name[i] == name) + return 0; /* curve already in use */ } - return ret; + ret = TLSX_SupportedCurve_Grow(curves, list->count + 1, heap); + if (ret != 0) + return ret; + + list = *curves; + list->name[list->count++] = name; + + return 0; } static int TLSX_PointFormat_Append(PointFormats** formats, byte format, @@ -5308,15 +5673,12 @@ static void TLSX_PointFormat_ValidateResponse(WOLFSSL* ssl, byte* semaphore) #if !defined(NO_WOLFSSL_CLIENT) || defined(WOLFSSL_TLS13) -static word16 TLSX_SupportedCurve_GetSize(SupportedCurve* list) +static word16 TLSX_SupportedCurve_GetSize(SupportedCurves* list) { - SupportedCurve* curve; word16 length = OPAQUE16_LEN; /* list length */ - while ((curve = list)) { - list = curve->next; - length += OPAQUE16_LEN; /* curve length */ - } + if (list != NULL) + length += list->count * OPAQUE16_LEN; return length; } @@ -5335,14 +5697,16 @@ static word16 TLSX_PointFormat_GetSize(PointFormats* list) #if !defined(NO_WOLFSSL_CLIENT) || defined(WOLFSSL_TLS13) -static word16 TLSX_SupportedCurve_Write(SupportedCurve* list, byte* output) +static word16 TLSX_SupportedCurve_Write(SupportedCurves* list, byte* output) { word16 offset = OPAQUE16_LEN; + word16 i; - while (list) { - c16toa(list->name, output + offset); - offset += OPAQUE16_LEN; - list = list->next; + if (list != NULL) { + for (i = 0; i < list->count; i++) { + c16toa(list->name[i], output + offset); + offset += OPAQUE16_LEN; + } } c16toa(offset - OPAQUE16_LEN, output); /* writing list length */ @@ -5423,7 +5787,7 @@ int TLSX_SupportedCurve_Parse(const WOLFSSL* ssl, const byte* input, WOLFSSL_NAMED_GROUP_IS_FFDHE(name)) { TLSX* ext = TLSX_Find(*extensions, TLSX_SUPPORTED_GROUPS); if (ext == NULL) { - SupportedCurve* curve = NULL; + SupportedCurves* curve = NULL; ret = TLSX_SupportedCurve_New(&curve, name, ssl->heap); if (ret == 0) { ret = TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, @@ -5433,8 +5797,11 @@ int TLSX_SupportedCurve_Parse(const WOLFSSL* ssl, const byte* input, } } else { - ret = TLSX_SupportedCurve_Append( - (SupportedCurve*)ext->data, name, ssl->heap); + SupportedCurves* curves = (SupportedCurves*)ext->data; + + ret = TLSX_SupportedCurve_Append(&curves, name, + ssl->heap); + ext->data = curves; } if (ret != 0) break; @@ -5452,48 +5819,58 @@ int TLSX_SupportedCurve_Parse(const WOLFSSL* ssl, const byte* input, } } else { - /* Find the intersection with what the user has set */ - SupportedCurve* commonCurves = NULL; + /* Our groups that the peer also offers, in the peer's order. */ + SupportedCurves* curves = (SupportedCurves*)extension->data; + SupportedCurves* common = NULL; + word16 count = (curves != NULL) ? curves->count : 0; +#if !defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_SERVER) + int ffdhe = 0; +#endif + word16 i; + for (; offset < length; offset += OPAQUE16_LEN) { - SupportedCurve* foundCurve = (SupportedCurve*)extension->data; - ato16(input + offset, &name); + int keep; - while (foundCurve != NULL && foundCurve->name != name) - foundCurve = foundCurve->next; + ato16(input + offset, &name); - if (foundCurve != NULL) { - ret = commonCurves == NULL ? - TLSX_SupportedCurve_New(&commonCurves, name, ssl->heap) : - TLSX_SupportedCurve_Append(commonCurves, name, ssl->heap); - if (ret != 0) + for (i = 0; i < count; i++) { + if (curves->name[i] == name) break; } + keep = (i < count); #if !defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \ !defined(NO_WOLFSSL_SERVER) - /* RFC 7919 Section 4 (see comment above). */ - else if (isRequest && WOLFSSL_NAMED_GROUP_IS_FFDHE(name) && + /* RFC 7919 Section 4: any FFDHE codepoint offered restricts DHE + * to named groups; keep the first unsupported one to record it. */ + if (!keep && !ffdhe && isRequest && + WOLFSSL_NAMED_GROUP_IS_FFDHE(name) && !TLSX_IsGroupSupported(name, ssl->options.side)) { - ret = commonCurves == NULL ? - TLSX_SupportedCurve_New(&commonCurves, name, ssl->heap) : - TLSX_SupportedCurve_Append(commonCurves, name, ssl->heap); - if (ret != 0) - break; + keep = 1; + ffdhe = 1; } #endif /* !NO_DH && !WOLFSSL_NO_TLS12 && !NO_WOLFSSL_SERVER */ + if (!keep) + continue; + + if (common == NULL) { + ret = TLSX_SupportedCurve_Grow(&common, count + 1, ssl->heap); + } + if (ret == 0) + ret = TLSX_SupportedCurve_Append(&common, name, ssl->heap); + if (ret != 0) + break; } /* If no common curves return error. In TLS 1.3 we can still try to save * this by using HRR. */ - if (ret == 0 && commonCurves == NULL && - !IsAtLeastTLSv1_3(ssl->version)) + if (ret == 0 && common == NULL && !IsAtLeastTLSv1_3(ssl->version)) ret = ECC_CURVE_ERROR; if (ret == 0) { - /* Now swap out the curves in the extension */ - TLSX_SupportedCurve_FreeAll((SupportedCurve*)extension->data, - ssl->heap); - extension->data = commonCurves; - commonCurves = NULL; + TLSX_SupportedCurve_FreeAll(curves, ssl->heap); + extension->data = common; + common = NULL; } - TLSX_SupportedCurve_FreeAll(commonCurves, ssl->heap); + TLSX_SupportedCurve_FreeAll(common, ssl->heap); } return ret; @@ -5517,13 +5894,27 @@ int TLSX_SupportedCurve_CheckPriority(WOLFSSL* ssl) TLSX* priority = NULL; TLSX* ext = NULL; word16 name; - SupportedCurve* curve; + word16 idx = 0; + word16 i; + SupportedCurves* curves; + SupportedCurves* tmp; extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); /* May be doing PSK with no key exchange. */ if (extension == NULL) return 0; + name = TLSX_NextSupportedGroup(ssl, &idx); + if (name == WOLFSSL_NAMED_GROUP_INVALID) + return 0; + + /* Nothing to say when the client offers the group we prefer. */ + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { + if (curves->name[i] == name) + return 0; + } + ret = TLSX_PopulateSupportedGroups(ssl, &priority); if (ret != WOLFSSL_SUCCESS) { TLSX_FreeAll(priority, ssl->heap); @@ -5537,25 +5928,12 @@ int TLSX_SupportedCurve_CheckPriority(WOLFSSL* ssl) return 0; } - curve = (SupportedCurve*)ext->data; - name = curve->name; - - curve = (SupportedCurve*)extension->data; - while (curve != NULL) { - if (curve->name == name) - break; - curve = curve->next; - } - - if (curve == NULL) { - /* Couldn't find the preferred group in client list. */ - extension->resp = 1; + extension->resp = 1; - /* Send server list back and free client list. */ - curve = (SupportedCurve*)extension->data; - extension->data = ext->data; - ext->data = curve; - } + /* Send server list back and free client list. */ + tmp = (SupportedCurves*)extension->data; + extension->data = ext->data; + ext->data = tmp; TLSX_FreeAll(priority, ssl->heap); @@ -5567,73 +5945,74 @@ int TLSX_SupportedCurve_CheckPriority(WOLFSSL* ssl) #if !defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) #ifdef HAVE_FFDHE #ifdef HAVE_PUBLIC_FFDHE -static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, - SupportedCurve* serverGroup) +static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurves* clientGroups) { int ret = 0; - SupportedCurve* group; + word16 idx = 0; + word16 name; + word16 i; const DhParams* params = NULL; + int found = 0; - for (; serverGroup != NULL; serverGroup = serverGroup->next) { - if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(serverGroup->name)) + while ((name = TLSX_NextSupportedGroup(ssl, &idx)) != + WOLFSSL_NAMED_GROUP_INVALID) { + if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(name)) continue; - for (group = clientGroup; group != NULL; group = group->next) { - if (serverGroup->name != group->name) - continue; + for (i = 0; i < clientGroups->count; i++) { + if (clientGroups->name[i] == name) + break; + } + if (i == clientGroups->count) + continue; - switch (serverGroup->name) { - #ifdef HAVE_FFDHE_2048 - case WOLFSSL_FFDHE_2048: - params = wc_Dh_ffdhe2048_Get(); - break; - #endif - #ifdef HAVE_FFDHE_3072 - case WOLFSSL_FFDHE_3072: - params = wc_Dh_ffdhe3072_Get(); - break; - #endif - #ifdef HAVE_FFDHE_4096 - case WOLFSSL_FFDHE_4096: - params = wc_Dh_ffdhe4096_Get(); - break; - #endif - #ifdef HAVE_FFDHE_6144 - case WOLFSSL_FFDHE_6144: - params = wc_Dh_ffdhe6144_Get(); - break; - #endif - #ifdef HAVE_FFDHE_8192 - case WOLFSSL_FFDHE_8192: - params = wc_Dh_ffdhe8192_Get(); - break; - #endif - default: - break; - } - if (params == NULL) { - ret = BAD_FUNC_ARG; + switch (name) { + #ifdef HAVE_FFDHE_2048 + case WOLFSSL_FFDHE_2048: + params = wc_Dh_ffdhe2048_Get(); break; - } - if (params->p_len >= ssl->options.minDhKeySz && - params->p_len <= ssl->options.maxDhKeySz) { + #endif + #ifdef HAVE_FFDHE_3072 + case WOLFSSL_FFDHE_3072: + params = wc_Dh_ffdhe3072_Get(); + break; + #endif + #ifdef HAVE_FFDHE_4096 + case WOLFSSL_FFDHE_4096: + params = wc_Dh_ffdhe4096_Get(); + break; + #endif + #ifdef HAVE_FFDHE_6144 + case WOLFSSL_FFDHE_6144: + params = wc_Dh_ffdhe6144_Get(); + break; + #endif + #ifdef HAVE_FFDHE_8192 + case WOLFSSL_FFDHE_8192: + params = wc_Dh_ffdhe8192_Get(); + break; + #endif + default: break; - } } - - if (ret != 0) + if (params == NULL) { + ret = BAD_FUNC_ARG; break; - if ((group != NULL) && (serverGroup->name == group->name)) + } + if (params->p_len >= ssl->options.minDhKeySz && + params->p_len <= ssl->options.maxDhKeySz) { + found = 1; break; + } } - if ((ret == 0) && (serverGroup != NULL) && (params != NULL)) { + if ((ret == 0) && found) { ssl->buffers.serverDH_P.buffer = (unsigned char *)params->p; ssl->buffers.serverDH_P.length = params->p_len; ssl->buffers.serverDH_G.buffer = (unsigned char *)params->g; ssl->buffers.serverDH_G.length = params->g_len; - ssl->namedGroup = serverGroup->name; + ssl->namedGroup = name; #if !defined(WOLFSSL_OLD_PRIME_CHECK) && \ !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) ssl->options.dhDoKeyTest = 0; @@ -5644,46 +6023,45 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, return ret; } #else -static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, - SupportedCurve* serverGroup) +static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurves* clientGroups) { int ret = 0; - SupportedCurve* group; + word16 idx = 0; + word16 name; + word16 i; word32 p_len; + int found = 0; - for (; serverGroup != NULL; serverGroup = serverGroup->next) { - if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(serverGroup->name)) + while ((name = TLSX_NextSupportedGroup(ssl, &idx)) != + WOLFSSL_NAMED_GROUP_INVALID) { + if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(name)) continue; - for (group = clientGroup; group != NULL; group = group->next) { - if (serverGroup->name != group->name) - continue; - - ret = wc_DhGetNamedKeyParamSize(serverGroup->name, &p_len, NULL, NULL); - if (ret == 0) { - if (p_len == 0) { - ret = BAD_FUNC_ARG; - break; - } - if (p_len >= ssl->options.minDhKeySz && - p_len <= ssl->options.maxDhKeySz) { - break; - } - } + for (i = 0; i < clientGroups->count; i++) { + if (clientGroups->name[i] == name) + break; } + if (i == clientGroups->count) + continue; + ret = wc_DhGetNamedKeyParamSize(name, &p_len, NULL, NULL); + if ((ret == 0) && (p_len == 0)) + ret = BAD_FUNC_ARG; if (ret != 0) break; - if ((group != NULL) && (serverGroup->name == group->name)) + if (p_len >= ssl->options.minDhKeySz && + p_len <= ssl->options.maxDhKeySz) { + found = 1; break; + } } - if ((ret == 0) && (serverGroup != NULL)) { + if ((ret == 0) && found) { word32 pSz, gSz; ssl->buffers.serverDH_P.buffer = NULL; ssl->buffers.serverDH_G.buffer = NULL; - ret = wc_DhGetNamedKeyParamSize(serverGroup->name, &pSz, &gSz, NULL); + ret = wc_DhGetNamedKeyParamSize(name, &pSz, &gSz, NULL); if (ret == 0) { ssl->buffers.serverDH_P.buffer = (byte*)XMALLOC(pSz, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); @@ -5701,7 +6079,7 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, ssl->buffers.serverDH_G.length = gSz; } if (ret == 0) { - ret = wc_DhCopyNamedKey(serverGroup->name, + ret = wc_DhCopyNamedKey(name, ssl->buffers.serverDH_P.buffer, &pSz, ssl->buffers.serverDH_G.buffer, &gSz, NULL, NULL); @@ -5709,7 +6087,7 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, if (ret == 0) { ssl->buffers.weOwnDH = 1; - ssl->namedGroup = serverGroup->name; + ssl->namedGroup = name; #if !defined(WOLFSSL_OLD_PRIME_CHECK) && \ !defined(HAVE_FIPS) && !defined(HAVE_SELFTEST) ssl->options.dhDoKeyTest = 0; @@ -5746,22 +6124,18 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl) { int ret = 0; -#ifdef HAVE_FFDHE - TLSX* priority = NULL; - TLSX* ext = NULL; -#endif TLSX* extension; - SupportedCurve* clientGroup; - SupportedCurve* group; + SupportedCurves* clientGroups; + word16 i; int found = 0; extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); /* May be doing PSK with no key exchange. */ if (extension == NULL) return 0; - clientGroup = (SupportedCurve*)extension->data; - for (group = clientGroup; group != NULL; group = group->next) { - if (WOLFSSL_NAMED_GROUP_IS_FFDHE(group->name)) { + clientGroups = (SupportedCurves*)extension->data; + for (i = 0; (clientGroups != NULL) && (i < clientGroups->count); i++) { + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(clientGroups->name[i])) { found = 1; break; } @@ -5783,22 +6157,7 @@ int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl) ssl->options.haveDH = 0; #ifdef HAVE_FFDHE - ret = TLSX_PopulateSupportedGroups(ssl, &priority); - if (ret == WOLFSSL_SUCCESS) { - SupportedCurve* serverGroup; - - ext = TLSX_Find(priority, TLSX_SUPPORTED_GROUPS); - if (ext == NULL) { - WOLFSSL_MSG("Could not find supported groups extension"); - ret = 0; - } - else { - serverGroup = (SupportedCurve*)ext->data; - ret = tlsx_ffdhe_find_group(ssl, clientGroup, serverGroup); - } - } - - TLSX_FreeAll(priority, ssl->heap); + ret = tlsx_ffdhe_find_group(ssl, clientGroups); #endif /* HAVE_FFDHE */ return ret; @@ -5815,17 +6174,17 @@ int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl) int TLSX_SupportedCurve_IsSupported(WOLFSSL* ssl, word16 name) { TLSX* extension; - SupportedCurve* curve; + SupportedCurves* curves; + word16 i; extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); if (extension == NULL) return 0; - curve = (SupportedCurve*)extension->data; - while (curve != NULL) { - if (curve->name == name) + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { + if (curves->name[i] == name) return 1; - curve = curve->next; } return 0; @@ -5841,18 +6200,18 @@ int TLSX_SupportedCurve_IsSupported(WOLFSSL* ssl, word16 name) int TLSX_SupportedCurve_Preferred(WOLFSSL* ssl, int checkSupported) { TLSX* extension; - SupportedCurve* curve; + SupportedCurves* curves; + word16 i; extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); if (extension == NULL) return BAD_FUNC_ARG; - curve = (SupportedCurve*)extension->data; - while (curve != NULL) { + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { if (!checkSupported || - TLSX_IsGroupSupported(curve->name, ssl->options.side)) - return curve->name; - curve = curve->next; + TLSX_IsGroupSupported(curves->name[i], ssl->options.side)) + return curves->name[i]; } return BAD_FUNC_ARG; @@ -5907,18 +6266,20 @@ static int TLSX_PointFormat_Parse(WOLFSSL* ssl, const byte* input, #if defined(HAVE_ECC) || defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) int TLSX_ValidateSupportedCurves(const WOLFSSL* ssl, byte first, byte second, word32* ecdhCurveOID) { - TLSX* extension = NULL; - SupportedCurve* curve = NULL; - word32 oid = 0; - word32 defOid = 0; - word32 defSz = 80; /* Maximum known curve size is 66. */ - word32 nextOid = 0; - word32 nextSz = 80; /* Maximum known curve size is 66. */ - word32 currOid = ssl->ecdhCurveOID; - int ephmSuite = 0; - word16 octets = 0; /* according to 'ecc_set_type ecc_sets[];' */ - int key = 0; /* validate key */ - int foundCurve = 0; /* Found at least one supported curve */ + TLSX* extension = NULL; + SupportedCurves* curves = NULL; + word16 curveName = 0; + word16 i = 0; + word32 oid = 0; + word32 defOid = 0; + word32 defSz = 80; /* Maximum known curve size is 66. */ + word32 nextOid = 0; + word32 nextSz = 80; /* Maximum known curve size is 66. */ + word32 currOid = ssl->ecdhCurveOID; + int ephmSuite = 0; + word16 octets = 0; /* per 'ecc_set_type ecc_sets[];' */ + int key = 0; /* validate key */ + int foundCurve = 0; /* Found at least one supported curve */ (void)oid; @@ -5940,19 +6301,19 @@ int TLSX_ValidateSupportedCurves(const WOLFSSL* ssl, byte first, byte second, if (!extension) return 1; /* no suite restriction */ - for (curve = (SupportedCurve*)extension->data; - curve && !key; - curve = curve->next) { + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count) && !key; i++) { + curveName = curves->name[i]; #ifdef OPENSSL_EXTRA /* skip if name is not in supported ECC range * or disabled by user */ - if (wolfSSL_curve_is_disabled(ssl, curve->name)) + if (wolfSSL_curve_is_disabled(ssl, curveName)) continue; #endif /* find supported curve */ - switch (curve->name) { + switch (curveName) { #ifdef HAVE_ECC #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 160 #ifndef NO_ECC_SECP @@ -6266,29 +6627,35 @@ int TLSX_ValidateSupportedCurves(const WOLFSSL* ssl, byte first, byte second, int TLSX_SupportedCurve_Copy(TLSX* src, TLSX** dst, void* heap) { TLSX* extension; + SupportedCurves* curves = NULL; + SupportedCurves* copy; + word32 sz; int ret; extension = TLSX_Find(src, TLSX_SUPPORTED_GROUPS); - if (extension != NULL) { - SupportedCurve* curve; - for (curve = (SupportedCurve*)extension->data; curve != NULL; - curve = curve->next) { - /* Copying an already validated list - don't drop a group based on - * the side, so accept when either side has the crypto support. */ - ret = TLSX_UseSupportedCurve(dst, curve->name, heap, - WOLFSSL_NEITHER_END); - if (ret != WOLFSSL_SUCCESS) - return MEMORY_E; - } - } + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + if ((curves == NULL) || (curves->count == 0)) + return 0; - return 0; + sz = (word32)(sizeof(SupportedCurves) + curves->count * sizeof(word16)); + copy = (SupportedCurves*)XMALLOC(sz, heap, DYNAMIC_TYPE_TLSX); + if (copy == NULL) + return MEMORY_E; + XMEMCPY(copy, curves, sz); + copy->cap = copy->count; + + ret = TLSX_Push(dst, TLSX_SUPPORTED_GROUPS, copy, heap); + if (ret != 0) + XFREE(copy, heap, DYNAMIC_TYPE_TLSX); + + return ret; } int TLSX_UseSupportedCurve(TLSX** extensions, word16 name, void* heap, int side) { TLSX* extension = NULL; - SupportedCurve* curve = NULL; + SupportedCurves* curves = NULL; int ret; if (extensions == NULL) { @@ -6302,35 +6669,37 @@ int TLSX_UseSupportedCurve(TLSX** extensions, word16 name, void* heap, int side) extension = TLSX_Find(*extensions, TLSX_SUPPORTED_GROUPS); if (!extension) { - ret = TLSX_SupportedCurve_New(&curve, name, heap); + ret = TLSX_SupportedCurve_New(&curves, name, heap); if (ret != 0) return ret; - ret = TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, curve, heap); + ret = TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, curves, heap); if (ret != 0) { - XFREE(curve, heap, DYNAMIC_TYPE_TLSX); + XFREE(curves, heap, DYNAMIC_TYPE_TLSX); return ret; } } else { - ret = TLSX_SupportedCurve_Append((SupportedCurve*)extension->data, name, - heap); + curves = (SupportedCurves*)extension->data; + ret = TLSX_SupportedCurve_Append(&curves, name, heap); + extension->data = curves; if (ret != 0) return ret; #if defined(WOLFSSL_ML_KEM_USE_OLD_IDS) && \ defined (WOLFSSL_EXTRA_PQC_HYBRIDS) if (name == WOLFSSL_SECP256R1MLKEM512) { - ret = TLSX_SupportedCurve_Append((SupportedCurve*)extension->data, + ret = TLSX_SupportedCurve_Append(&curves, WOLFSSL_P256_ML_KEM_512_OLD, heap); } else if (name == WOLFSSL_SECP384R1MLKEM768) { - ret = TLSX_SupportedCurve_Append((SupportedCurve*)extension->data, + ret = TLSX_SupportedCurve_Append(&curves, WOLFSSL_P384_ML_KEM_768_OLD, heap); } else if (name == WOLFSSL_SECP521R1MLKEM1024) { - ret = TLSX_SupportedCurve_Append((SupportedCurve*)extension->data, + ret = TLSX_SupportedCurve_Append(&curves, WOLFSSL_P521_ML_KEM_1024_OLD, heap); } + extension->data = curves; if (ret != 0) { return ret; } @@ -11008,8 +11377,9 @@ static int TLSX_SupportedGroups_Find(const WOLFSSL* ssl, word16 name, TLSX* extensions) { #ifdef HAVE_SUPPORTED_CURVES - TLSX* extension; - SupportedCurve* curve = NULL; + TLSX* extension; + SupportedCurves* curves = NULL; + word16 i; if ((extension = TLSX_Find(extensions, TLSX_SUPPORTED_GROUPS)) == NULL) { if ((extension = TLSX_Find(ssl->ctx->extensions, @@ -11018,8 +11388,9 @@ static int TLSX_SupportedGroups_Find(const WOLFSSL* ssl, word16 name, } } - for (curve = (SupportedCurve*)extension->data; curve; curve = curve->next) { - if (curve->name == name) + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { + if (curves->name[i] == name) return 1; } #endif @@ -11624,334 +11995,136 @@ int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, XFREE(keyShareEntry->pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); keyShareEntry->pubKey = ciphertext; - keyShareEntry->pubKeyLen = ecc_kse->pubKeyLen + ctSz; - ciphertext = NULL; - - /* Set namedGroup so wolfSSL_get_curve_name() can function properly on - * the server side. */ - ssl->namedGroup = keyShareEntry->group; - } - else -#ifdef WOLFSSL_ASYNC_CRYPT - if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) -#endif - { - /* Clear the pre master secret buffer to prevent leaking any - * intermediate keys in the error case. Do not use preMasterSz - * here as it may already been set to the ECC shared secret size, - * which would be too small due to the PQC offset case. */ - ForceZero(ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ); - } - - TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); - TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); - XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); - return ret; -} -#endif /* WOLFSSL_HAVE_MLKEM && !WOLFSSL_MLKEM_NO_ENCAPSULATE */ - -/* Use the data to create a new key share object in the extensions. - * - * ssl The SSL/TLS object. - * group The named group. - * len The length of the public key data. - * data The public key data. - * kse The new key share entry object. - * returns 0 on success and other values indicate failure. - */ -int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, - KeyShareEntry **kse, TLSX** extensions) -{ - int ret = 0; - TLSX* extension; - KeyShareEntry* keyShareEntry = NULL; - - /* Find the KeyShare extension if it exists. */ - extension = TLSX_Find(*extensions, TLSX_KEY_SHARE); - if (extension == NULL) { - /* Push new KeyShare extension. */ - ret = TLSX_Push(extensions, TLSX_KEY_SHARE, NULL, ssl->heap); - if (ret != 0) - return ret; - - extension = TLSX_Find(*extensions, TLSX_KEY_SHARE); - if (extension == NULL) - return MEMORY_E; - } - extension->resp = 0; - - /* Try to find the key share entry with this group. */ - keyShareEntry = (KeyShareEntry*)extension->data; - while (keyShareEntry != NULL) { - #if defined(WOLFSSL_ML_KEM_USE_OLD_IDS) && \ - defined (WOLFSSL_EXTRA_PQC_HYBRIDS) - if ((group == WOLFSSL_P256_ML_KEM_512_OLD && - keyShareEntry->group == WOLFSSL_SECP256R1MLKEM512) || - (group == WOLFSSL_P384_ML_KEM_768_OLD && - keyShareEntry->group == WOLFSSL_SECP384R1MLKEM768) || - (group == WOLFSSL_P521_ML_KEM_1024_OLD && - keyShareEntry->group == WOLFSSL_SECP521R1MLKEM1024)) { - keyShareEntry->group = group; - break; - } - else - #endif /* WOLFSSL_ML_KEM_USE_OLD_IDS && WOLFSSL_EXTRA_PQC_HYBRIDS */ - if (keyShareEntry->group == group) - break; - keyShareEntry = keyShareEntry->next; - } - - /* Create a new key share entry if not found. */ - if (keyShareEntry == NULL) { - ret = TLSX_KeyShare_New((KeyShareEntry**)&extension->data, group, - ssl->heap, &keyShareEntry); - if (ret != 0) - return ret; - } - - if (data != NULL) { - /* Store the peer data in the key share object. */ - XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); - keyShareEntry->ke = data; - keyShareEntry->keLen = len; + keyShareEntry->pubKeyLen = ecc_kse->pubKeyLen + ctSz; + ciphertext = NULL; + + /* Set namedGroup so wolfSSL_get_curve_name() can function properly on + * the server side. */ + ssl->namedGroup = keyShareEntry->group; } - else { - /* Generate a key pair. Casting to non-const since changes inside are - * minimal but would require an extensive redesign to refactor. Also - * this path shouldn't be taken when parsing a ClientHello in stateless - * mode. */ - ret = TLSX_KeyShare_GenKey((WOLFSSL*)ssl, keyShareEntry); - if (ret != 0) - return ret; + else +#ifdef WOLFSSL_ASYNC_CRYPT + if (ret != WC_NO_ERR_TRACE(WC_PENDING_E)) +#endif + { + /* Clear the pre master secret buffer to prevent leaking any + * intermediate keys in the error case. Do not use preMasterSz + * here as it may already been set to the ECC shared secret size, + * which would be too small due to the PQC offset case. */ + ForceZero(ssl->arrays->preMasterSecret, MAX_PREMASTER_SZ); } - if (kse != NULL) - *kse = keyShareEntry; - - return 0; + TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); + XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); + return ret; } +#endif /* WOLFSSL_HAVE_MLKEM && !WOLFSSL_MLKEM_NO_ENCAPSULATE */ -/* Set an empty Key Share extension. +/* Use the data to create a new key share object in the extensions. * - * ssl The SSL/TLS object. + * ssl The SSL/TLS object. + * group The named group. + * len The length of the public key data. + * data The public key data. + * kse The new key share entry object. * returns 0 on success and other values indicate failure. */ -int TLSX_KeyShare_Empty(WOLFSSL* ssl) +int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, + KeyShareEntry **kse, TLSX** extensions) { - int ret = 0; - TLSX* extension; + int ret = 0; + TLSX* extension; + KeyShareEntry* keyShareEntry = NULL; /* Find the KeyShare extension if it exists. */ - extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); + extension = TLSX_Find(*extensions, TLSX_KEY_SHARE); if (extension == NULL) { /* Push new KeyShare extension. */ - ret = TLSX_Push(&ssl->extensions, TLSX_KEY_SHARE, NULL, ssl->heap); - } - else if (extension->data != NULL) { - TLSX_KeyShare_FreeAll((KeyShareEntry*)extension->data, ssl->heap); - extension->data = NULL; - } + ret = TLSX_Push(extensions, TLSX_KEY_SHARE, NULL, ssl->heap); + if (ret != 0) + return ret; - return ret; -} + extension = TLSX_Find(*extensions, TLSX_KEY_SHARE); + if (extension == NULL) + return MEMORY_E; + } + extension->resp = 0; -/* Compile-time gating must stay aligned with TLSX_PopulateSupportedGroups(). - * Runtime-only conditions in that function (TLS 1.3 version check, FFDHE - * key-size bounds, session-resumption short-circuit, downgrade-aware - * Brainpool TLS 1.2 selection) are intentionally not represented here. */ -static const word16 preferredGroup[] = { - /* Sort by strength, but prefer non-experimental PQ/T hybrid groups */ -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_PQC_HYBRIDS) - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519) && \ - ECC_MIN_KEY_SZ <= 256 - WOLFSSL_X25519MLKEM768, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 384 - WOLFSSL_SECP384R1MLKEM1024, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - WOLFSSL_SECP256R1MLKEM768, - #endif -#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && !WOLFSSL_NO_ML_KEM && - * WOLFSSL_PQC_HYBRIDS */ -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_1024) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - WOLFSSL_ML_KEM_1024, -#endif -#if defined(HAVE_ECC) && (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ - !defined(NO_ECC_SECP) && ECC_MIN_KEY_SZ <= 521 - WOLFSSL_ECC_SECP521R1, -#endif -#if defined(HAVE_ECC) && (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && \ - defined(HAVE_ECC_BRAINPOOL) && ECC_MIN_KEY_SZ <= 512 - WOLFSSL_ECC_BRAINPOOLP512R1TLS13, - WOLFSSL_ECC_BRAINPOOLP512R1, -#endif -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_768) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - WOLFSSL_ML_KEM_768, -#endif -#if defined(HAVE_ECC) && (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 384 - #ifndef NO_ECC_SECP - WOLFSSL_ECC_SECP384R1, - #endif - #ifdef HAVE_ECC_BRAINPOOL - WOLFSSL_ECC_BRAINPOOLP384R1TLS13, - WOLFSSL_ECC_BRAINPOOLP384R1, - #endif -#endif -#if !defined(HAVE_FIPS) && defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 - WOLFSSL_ECC_X448, -#endif -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_512) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - WOLFSSL_ML_KEM_512, -#endif -#if defined(HAVE_ECC) && (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - #ifndef NO_ECC_SECP - WOLFSSL_ECC_SECP256R1, - #endif - #ifdef HAVE_ECC_KOBLITZ - WOLFSSL_ECC_SECP256K1, - #endif - #ifdef HAVE_ECC_BRAINPOOL - WOLFSSL_ECC_BRAINPOOLP256R1TLS13, - WOLFSSL_ECC_BRAINPOOLP256R1, - #endif - #if !defined(HAVE_FIPS) && defined(WOLFSSL_SM2) - WOLFSSL_ECC_SM2P256V1, - #endif -#endif -#if !defined(HAVE_FIPS) && defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - WOLFSSL_ECC_X25519, -#endif -#if defined(HAVE_ECC) && (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 224 - #ifndef NO_ECC_SECP - WOLFSSL_ECC_SECP224R1, - #endif - #ifdef HAVE_ECC_KOBLITZ - WOLFSSL_ECC_SECP224K1, - #endif -#endif -#if !defined(HAVE_FIPS) && defined(HAVE_ECC) - #if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 192 - #ifndef NO_ECC_SECP - WOLFSSL_ECC_SECP192R1, - #endif - #ifdef HAVE_ECC_KOBLITZ - WOLFSSL_ECC_SECP192K1, - #endif - #endif - #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 160 - #ifndef NO_ECC_SECP - WOLFSSL_ECC_SECP160R1, - #endif - #ifdef HAVE_ECC_SECPR2 - WOLFSSL_ECC_SECP160R2, - #endif - #ifdef HAVE_ECC_KOBLITZ - WOLFSSL_ECC_SECP160K1, - #endif - #endif -#endif /* !HAVE_FIPS && HAVE_ECC */ -#if defined(HAVE_FFDHE_8192) - WOLFSSL_FFDHE_8192, -#endif -#if defined(HAVE_FFDHE_6144) - WOLFSSL_FFDHE_6144, -#endif -#if defined(HAVE_FFDHE_4096) - WOLFSSL_FFDHE_4096, -#endif -#if defined(HAVE_FFDHE_3072) - WOLFSSL_FFDHE_3072, -#endif -#if defined(HAVE_FFDHE_2048) - WOLFSSL_FFDHE_2048, -#endif -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_EXTRA_PQC_HYBRIDS) - #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 521 - WOLFSSL_SECP521R1MLKEM1024, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 384 - WOLFSSL_SECP384R1MLKEM768, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) && \ - ECC_MIN_KEY_SZ <= 448 - WOLFSSL_X448MLKEM768, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC) && \ - (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - WOLFSSL_SECP256R1MLKEM512, - #endif - #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519) && \ - ECC_MIN_KEY_SZ <= 256 - WOLFSSL_X25519MLKEM512, - #endif -#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && !WOLFSSL_NO_ML_KEM && - * WOLFSSL_EXTRA_PQC_HYBRIDS */ -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - defined(WOLFSSL_MLKEM_KYBER) - #ifdef WOLFSSL_KYBER1024 - WOLFSSL_KYBER_LEVEL5, - #if defined(HAVE_ECC) && (defined(HAVE_ECC521) || \ - defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521 - WOLFSSL_P521_KYBER_LEVEL5, - #endif - #endif - #ifdef WOLFSSL_KYBER768 - WOLFSSL_KYBER_LEVEL3, - #if defined(HAVE_ECC) && (defined(HAVE_ECC384) || \ - defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 - WOLFSSL_P384_KYBER_LEVEL3, - #endif - #if defined(HAVE_ECC) && (!defined(NO_ECC256) || \ - defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 - WOLFSSL_P256_KYBER_LEVEL3, - #endif - #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - WOLFSSL_X25519_KYBER_LEVEL3, - #endif - #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 - WOLFSSL_X448_KYBER_LEVEL3, - #endif - #endif - #ifdef WOLFSSL_KYBER512 - WOLFSSL_KYBER_LEVEL1, - #if defined(HAVE_ECC) && (!defined(NO_ECC256) || \ - defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 - WOLFSSL_P256_KYBER_LEVEL1, - #endif - #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - WOLFSSL_X25519_KYBER_LEVEL1, - #endif - #endif -#endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_MLKEM && WOLFSSL_MLKEM_KYBER */ - WOLFSSL_NAMED_GROUP_INVALID -}; + /* Try to find the key share entry with this group. */ + keyShareEntry = (KeyShareEntry*)extension->data; + while (keyShareEntry != NULL) { + #if defined(WOLFSSL_ML_KEM_USE_OLD_IDS) && \ + defined (WOLFSSL_EXTRA_PQC_HYBRIDS) + if ((group == WOLFSSL_P256_ML_KEM_512_OLD && + keyShareEntry->group == WOLFSSL_SECP256R1MLKEM512) || + (group == WOLFSSL_P384_ML_KEM_768_OLD && + keyShareEntry->group == WOLFSSL_SECP384R1MLKEM768) || + (group == WOLFSSL_P521_ML_KEM_1024_OLD && + keyShareEntry->group == WOLFSSL_SECP521R1MLKEM1024)) { + keyShareEntry->group = group; + break; + } + else + #endif /* WOLFSSL_ML_KEM_USE_OLD_IDS && WOLFSSL_EXTRA_PQC_HYBRIDS */ + if (keyShareEntry->group == group) + break; + keyShareEntry = keyShareEntry->next; + } -#define PREFERRED_GROUP_SZ \ - ((sizeof(preferredGroup)/sizeof(*preferredGroup)) - 1) - /* -1 for the invalid group */ + /* Create a new key share entry if not found. */ + if (keyShareEntry == NULL) { + ret = TLSX_KeyShare_New((KeyShareEntry**)&extension->data, group, + ssl->heap, &keyShareEntry); + if (ret != 0) + return ret; + } + + if (data != NULL) { + /* Store the peer data in the key share object. */ + XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + keyShareEntry->ke = data; + keyShareEntry->keLen = len; + } + else { + /* Generate a key pair. Casting to non-const since changes inside are + * minimal but would require an extensive redesign to refactor. Also + * this path shouldn't be taken when parsing a ClientHello in stateless + * mode. */ + ret = TLSX_KeyShare_GenKey((WOLFSSL*)ssl, keyShareEntry); + if (ret != 0) + return ret; + } + + if (kse != NULL) + *kse = keyShareEntry; + + return 0; +} + +/* Set an empty Key Share extension. + * + * ssl The SSL/TLS object. + * returns 0 on success and other values indicate failure. + */ +int TLSX_KeyShare_Empty(WOLFSSL* ssl) +{ + int ret = 0; + TLSX* extension; + + /* Find the KeyShare extension if it exists. */ + extension = TLSX_Find(ssl->extensions, TLSX_KEY_SHARE); + if (extension == NULL) { + /* Push new KeyShare extension. */ + ret = TLSX_Push(&ssl->extensions, TLSX_KEY_SHARE, NULL, ssl->heap); + } + else if (extension->data != NULL) { + TLSX_KeyShare_FreeAll((KeyShareEntry*)extension->data, ssl->heap); + extension->data = NULL; + } + + return ret; +} /* One past the worst rank TLSX_KeyShare_GroupRank() can return. It ranks * against ssl->group[] when the user set a list and against preferredGroup[] @@ -12072,43 +12245,41 @@ int TLSX_KeyShare_SetSupported(const WOLFSSL* ssl, TLSX** extensions) { int ret; #ifdef HAVE_SUPPORTED_CURVES - TLSX* extension; - SupportedCurve* curve = NULL; - SupportedCurve* preferredCurve = NULL; - word16 name = WOLFSSL_NAMED_GROUP_INVALID; - KeyShareEntry* kse = NULL; - int preferredRank = WOLFSSL_WORST_GROUP_RANK; - int rank; + TLSX* extension; + SupportedCurves* curves = NULL; + word16 name = WOLFSSL_NAMED_GROUP_INVALID; + word16 i; + KeyShareEntry* kse = NULL; + int preferredRank = WOLFSSL_WORST_GROUP_RANK; + int rank; extension = TLSX_Find(*extensions, TLSX_SUPPORTED_GROUPS); if (extension != NULL) - curve = (SupportedCurve*)extension->data; - for (; curve != NULL; curve = curve->next) { + curves = (SupportedCurves*)extension->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { /* Use server's preference order. Common group was found but key share * was missing */ - if (!TLSX_IsGroupSupported(curve->name, ssl->options.side)) + if (!TLSX_IsGroupSupported(curves->name[i], ssl->options.side)) continue; - if (wolfSSL_curve_is_disabled(ssl, curve->name)) + if (wolfSSL_curve_is_disabled(ssl, curves->name[i])) continue; - rank = TLSX_KeyShare_GroupRank(ssl, curve->name); + rank = TLSX_KeyShare_GroupRank(ssl, curves->name[i]); if (rank == -1) continue; if (rank < preferredRank) { - preferredCurve = curve; + name = curves->name[i]; preferredRank = rank; } } - curve = preferredCurve; - if (curve == NULL) { + if (name == WOLFSSL_NAMED_GROUP_INVALID) { /* No mutual group exists. An HRR may request only a group the * client advertised in supported_groups, so it cannot recover. * RFC 8446 4.2.1. */ WOLFSSL_ERROR_VERBOSE(KEY_SHARE_ERROR); return KEY_SHARE_ERROR; } - name = curve->name; #ifdef WOLFSSL_ASYNC_CRYPT /* Check the old key share data list. */ @@ -15665,7 +15836,7 @@ void TLSX_FreeAll(TLSX* list, void* heap) case TLSX_SUPPORTED_GROUPS: WOLFSSL_MSG("Supported Groups extension free"); - EC_FREE_ALL((SupportedCurve*)extension->data, heap); + EC_FREE_ALL((SupportedCurves*)extension->data, heap); break; case TLSX_EC_POINT_FORMATS: @@ -15908,7 +16079,7 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType, break; case TLSX_SUPPORTED_GROUPS: - length += EC_GET_SIZE((SupportedCurve*)extension->data); + length += EC_GET_SIZE((SupportedCurves*)extension->data); break; case TLSX_EC_POINT_FORMATS: @@ -16179,7 +16350,7 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, case TLSX_SUPPORTED_GROUPS: WOLFSSL_MSG("Supported Groups extension to write"); - offset += EC_WRITE((SupportedCurve*)extension->data, + offset += EC_WRITE((SupportedCurves*)extension->data, output + offset); break; @@ -16425,414 +16596,19 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore, /* Populates the default supported groups / curves */ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) { - int ret = WOLFSSL_SUCCESS; -#ifdef WOLFSSL_TLS13 -#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK) - if (ssl->options.resuming && ssl->session->namedGroup != 0) { - return TLSX_UseSupportedCurve(extensions, ssl->session->namedGroup, - ssl->heap, ssl->options.side); - } -#endif - - if (ssl->numGroups != 0) { - int i; - for (i = 0; i < ssl->numGroups; i++) { - ret = TLSX_UseSupportedCurve(extensions, ssl->group[i], ssl->heap, - ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - return WOLFSSL_SUCCESS; - } -#endif /* WOLFSSL_TLS13 */ - -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT) && \ - !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_PQC_HYBRIDS) - /* Prefer non-experimental PQ/T hybrid groups (only for TLS 1.3) */ - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519) && \ - ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519MLKEM768, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 384 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_SECP384R1MLKEM1024, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_SECP256R1MLKEM768, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - } -#endif - -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_1024) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ML_KEM_1024, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } -#endif - -#if defined(HAVE_ECC) - /* list in order by strength, since not all servers choose by strength */ - #if (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP521R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #endif - #if (defined(HAVE_ECC512) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 512 - #ifdef HAVE_ECC_BRAINPOOL - if (IsAtLeastTLSv1_3(ssl->version)) { - /* TLS 1.3 BrainpoolP512 curve */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP512R1TLS13, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - - /* If TLS 1.2 is allowed, also add the TLS 1.2 curve */ - if (ssl->options.downgrade && - (ssl->options.minDowngrade <= TLSv1_2_MINOR || - ssl->options.minDowngrade <= DTLSv1_2_MINOR)) { - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP512R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - } - else { - /* TLS 1.2 only */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP512R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - #endif - #endif -#endif - -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_768) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ML_KEM_768, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } -#endif - -#if defined(HAVE_ECC) - #if (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP384R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_BRAINPOOL - if (IsAtLeastTLSv1_3(ssl->version)) { - /* TLS 1.3 BrainpoolP384 curve */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP384R1TLS13, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - - /* If TLS 1.2 is allowed, also add the TLS 1.2 curve */ - if (ssl->options.downgrade && - (ssl->options.minDowngrade <= TLSv1_2_MINOR || - ssl->options.minDowngrade <= DTLSv1_2_MINOR)) { - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP384R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - } - else { - /* TLS 1.2 only */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP384R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - #endif - #endif -#endif /* HAVE_ECC */ - -#ifndef HAVE_FIPS - #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_X448, ssl->heap, - ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif -#endif /* HAVE_FIPS */ - -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && !defined(WOLFSSL_NO_ML_KEM_512) && \ - !defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE) - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ML_KEM_512, ssl->heap, - ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } -#endif - -#if defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) - #if (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP256R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_KOBLITZ - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP256K1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_BRAINPOOL - if (IsAtLeastTLSv1_3(ssl->version)) { - /* TLS 1.3 BrainpoolP256 curve */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP256R1TLS13, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - - /* If TLS 1.2 is allowed, also add the TLS 1.2 curve */ - if (ssl->options.downgrade && - (ssl->options.minDowngrade <= TLSv1_2_MINOR || - ssl->options.minDowngrade <= DTLSv1_2_MINOR)) { - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP256R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - } - else { - /* TLS 1.2 only */ - ret = TLSX_UseSupportedCurve(extensions, - WOLFSSL_ECC_BRAINPOOLP256R1, ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - } - #endif - #if !defined(HAVE_FIPS) && defined(WOLFSSL_SM2) - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SM2P256V1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #endif -#endif /* HAVE_ECC */ - -#ifndef HAVE_FIPS - #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_X25519, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif -#endif /* HAVE_FIPS */ - -#if defined(HAVE_ECC) && defined(HAVE_SUPPORTED_CURVES) - #if (defined(HAVE_ECC224) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 224 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP224R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_KOBLITZ - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP224K1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #endif - - #ifndef HAVE_FIPS - #if (defined(HAVE_ECC192) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 192 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP192R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_KOBLITZ - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP192K1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #endif - #if (defined(HAVE_ECC160) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 160 - #ifndef NO_ECC_SECP - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP160R1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_SECPR2 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP160R2, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #ifdef HAVE_ECC_KOBLITZ - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ECC_SECP160K1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; - #endif - #endif - #endif /* HAVE_FIPS */ -#endif /* HAVE_ECC */ - -#ifndef NO_DH - /* Add FFDHE supported groups. */ - #ifdef HAVE_FFDHE_8192 - if (8192/8 >= ssl->options.minDhKeySz && - 8192/8 <= ssl->options.maxDhKeySz) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_FFDHE_8192, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - #endif - #ifdef HAVE_FFDHE_6144 - if (6144/8 >= ssl->options.minDhKeySz && - 6144/8 <= ssl->options.maxDhKeySz) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_FFDHE_6144, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - #endif - #ifdef HAVE_FFDHE_4096 - if (4096/8 >= ssl->options.minDhKeySz && - 4096/8 <= ssl->options.maxDhKeySz) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_FFDHE_4096, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - #endif - #ifdef HAVE_FFDHE_3072 - if (3072/8 >= ssl->options.minDhKeySz && - 3072/8 <= ssl->options.maxDhKeySz) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_FFDHE_3072, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - #endif - #ifdef HAVE_FFDHE_2048 - if (2048/8 >= ssl->options.minDhKeySz && - 2048/8 <= ssl->options.maxDhKeySz) { - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_FFDHE_2048, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) - return ret; - } - #endif -#endif - -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - !defined(WOLFSSL_NO_ML_KEM) && defined(WOLFSSL_EXTRA_PQC_HYBRIDS) - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { -#if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 521 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_SECP521R1MLKEM1024, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC) && \ - (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 384 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_SECP384R1MLKEM768, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) && \ - ECC_MIN_KEY_SZ <= 448 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448MLKEM768, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC) && \ - (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_SECP256R1MLKEM512, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519) && \ - ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519MLKEM512, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif - } -#endif + word16 idx = 0; + word16 name; + int ret; -#if defined(WOLFSSL_TLS13) && defined(WOLFSSL_HAVE_MLKEM) && \ - defined(WOLFSSL_MLKEM_KYBER) - if (IsAtLeastTLSv1_3(ssl->version) && - TLSX_IsMlKemGroupSupported(ssl->options.side)) { -#ifdef WOLFSSL_KYBER1024 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_KYBER_LEVEL5, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#if defined(HAVE_ECC) && (defined(HAVE_ECC521) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 521 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P521_KYBER_LEVEL5, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#endif -#ifdef WOLFSSL_KYBER768 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_KYBER_LEVEL3, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#if defined(HAVE_ECC) && (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 384 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_KYBER_LEVEL3, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if defined(HAVE_ECC) && (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_KYBER_LEVEL3, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL3, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448_KYBER_LEVEL3, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#endif -#ifdef WOLFSSL_KYBER512 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_KYBER_LEVEL1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#if defined(HAVE_ECC) && (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ - ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_KYBER_LEVEL1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 - ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL1, - ssl->heap, ssl->options.side); - if (ret != WOLFSSL_SUCCESS) return ret; -#endif -#endif + while ((name = TLSX_NextSupportedGroup(ssl, &idx)) != + WOLFSSL_NAMED_GROUP_INVALID) { + ret = TLSX_UseSupportedCurve(extensions, name, ssl->heap, + ssl->options.side); + if (ret != WOLFSSL_SUCCESS) + return ret; } -#endif - (void)ssl; - (void)extensions; - - return ret; + return WOLFSSL_SUCCESS; } #endif /* HAVE_SUPPORTED_CURVES */ diff --git a/tests/api.c b/tests/api.c index f0da14b6d3e..37880e8c9b2 100644 --- a/tests/api.c +++ b/tests/api.c @@ -47156,6 +47156,12 @@ TEST_CASE testCases[] = { TEST_DECL(test_TLSX_SignatureAlgorithmsCert_parse), TEST_DECL(test_TLSX_PointFormat_list), TEST_DECL(test_TLSX_MaxFragment_value), + TEST_DECL(test_TLSX_SupportedCurve_append), + TEST_DECL(test_TLSX_SupportedCurve_grow), + TEST_DECL(test_TLSX_SupportedCurve_intersection), + TEST_DECL(test_TLSX_SupportedCurve_check_priority), + TEST_DECL(test_TLSX_SupportedFFDHE_Set), + TEST_DECL(test_TLSX_SupportedGroups_default_filter), TEST_DECL(test_TLSX_PointFormat_uncompressed_required), TEST_DECL(test_wolfSSL_CTX_add_client_custom_ext), TEST_DECL(test_wolfSSL_custom_ext_handshake), diff --git a/tests/api/test_tls_ext.c b/tests/api/test_tls_ext.c index a4eefb08ee2..257281c2735 100644 --- a/tests/api/test_tls_ext.c +++ b/tests/api/test_tls_ext.c @@ -3424,6 +3424,577 @@ int test_TLSX_MaxFragment_value(void) return EXPECT_RESULT(); } +/* Appending keeps every group the build accepts, in order, and drops a + * repeat. */ +int test_TLSX_SupportedCurve_append(void) +{ + EXPECT_DECLS; +#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_TLS_EXTENSIONS) + /* Every standalone group the API knows; a build rejects the rest. */ + static const word16 known[] = { + WOLFSSL_ECC_SECP160K1, WOLFSSL_ECC_SECP160R1, WOLFSSL_ECC_SECP160R2, + WOLFSSL_ECC_SECP192K1, WOLFSSL_ECC_SECP192R1, WOLFSSL_ECC_SECP224K1, + WOLFSSL_ECC_SECP224R1, WOLFSSL_ECC_SECP256K1, WOLFSSL_ECC_SECP256R1, + WOLFSSL_ECC_SECP384R1, WOLFSSL_ECC_SECP521R1, + WOLFSSL_ECC_BRAINPOOLP256R1, WOLFSSL_ECC_BRAINPOOLP384R1, + WOLFSSL_ECC_BRAINPOOLP512R1, WOLFSSL_ECC_SM2P256V1, + WOLFSSL_ECC_X25519, WOLFSSL_ECC_X448, + WOLFSSL_FFDHE_2048, WOLFSSL_FFDHE_3072, WOLFSSL_FFDHE_4096, + WOLFSSL_FFDHE_6144, WOLFSSL_FFDHE_8192, + WOLFSSL_ML_KEM_512, WOLFSSL_ML_KEM_768, WOLFSSL_ML_KEM_1024, + WOLFSSL_KYBER_LEVEL1, WOLFSSL_KYBER_LEVEL3, WOLFSSL_KYBER_LEVEL5 + }; + word16 added[sizeof(known) / sizeof(known[0])]; + word16 addedCnt = 0; + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + TLSX* extension = NULL; + SupportedCurves* curves = NULL; + word16 i; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + + for (i = 0; (ssl != NULL) && (i < sizeof(known) / sizeof(known[0])); i++) { + if (wolfSSL_UseSupportedCurve(ssl, known[i]) == WOLFSSL_SUCCESS) + added[addedCnt++] = known[i]; + } + ExpectIntGT(addedCnt, 0); + /* A repeat is dropped rather than appended. */ + if (addedCnt > 0) { + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, added[0]), + WOLFSSL_SUCCESS); + } + + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, (int)addedCnt); + ExpectIntLE(curves == NULL ? 1 : (int)curves->count, + curves == NULL ? 0 : (int)curves->cap); + for (i = 0; (curves != NULL) && (i < addedCnt); i++) { + ExpectIntEQ(curves->name[i], added[i]); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* Growing past the initial capacity, driven through the RFC 7919 rule: an + * FFDHE codepoint is kept even when this build has no such group. */ +int test_TLSX_SupportedCurve_grow(void) +{ + EXPECT_DECLS; +#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_TLS_EXTENSIONS) && \ + !defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_SERVER) + /* FFDHE range (256..511) codepoints no build implements, so every one + * of them takes the keep path. */ + #define TEST_GROW_CNT 128 + #define TEST_GROW_FIRST 0x0140 + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + Suites* suites = NULL; + TLSX* extension = NULL; + SupportedCurves* curves = NULL; + byte peerList[6 + TEST_GROW_CNT * 2]; + const byte oneName[] = { 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0x01, 0x40 }; + int initCap = -1; + word16 i; + + /* supported_groups (0x000a), extension length, list length, names. */ + peerList[0] = 0x00; + peerList[1] = 0x0a; + peerList[2] = (byte)(((2 + TEST_GROW_CNT * 2) >> 8) & 0xff); + peerList[3] = (byte)((2 + TEST_GROW_CNT * 2) & 0xff); + peerList[4] = (byte)(((TEST_GROW_CNT * 2) >> 8) & 0xff); + peerList[5] = (byte)((TEST_GROW_CNT * 2) & 0xff); + for (i = 0; i < TEST_GROW_CNT; i++) { + peerList[6 + i * 2] = (byte)(((TEST_GROW_FIRST + i) >> 8) & 0xff); + peerList[7 + i * 2] = (byte)((TEST_GROW_FIRST + i) & 0xff); + } + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, oneName, (word16)sizeof(oneName), + client_hello, suites), 0); + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + if (curves != NULL) + initCap = (int)curves->cap; + ExpectIntGT(initCap, 0); + wolfSSL_free(ssl); + ssl = NULL; + extension = NULL; + curves = NULL; + + /* No list of our own, so the peer's names are the ones stored. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, peerList, (word16)sizeof(peerList), + client_hello, suites), 0); + + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, TEST_GROW_CNT); + ExpectIntGE(curves == NULL ? -1 : (int)curves->cap, TEST_GROW_CNT); + ExpectIntGT(curves == NULL ? -1 : (int)curves->cap, initCap); + for (i = 0; (curves != NULL) && (i < TEST_GROW_CNT); i++) { + ExpectIntEQ(curves->name[i], TEST_GROW_FIRST + i); + } + + wolfSSL_free(ssl); + wolfSSL_CTX_free(ctx); + #undef TEST_GROW_CNT + #undef TEST_GROW_FIRST +#endif + return EXPECT_RESULT(); +} + +/* The intersection a server keeps when it has its own list: common groups in + * the peer's order, repeats and unknown groups dropped. */ +int test_TLSX_SupportedCurve_intersection(void) +{ + EXPECT_DECLS; +#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_TLS) && \ + defined(HAVE_SUPPORTED_CURVES) && !defined(WOLFSSL_NO_TLS12) && \ + defined(HAVE_ECC) && !defined(NO_ECC_SECP) && ECC_MIN_KEY_SZ <= 256 && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && \ + (defined(HAVE_ECC384) || defined(HAVE_ALL_CURVES)) && \ + (!defined(NO_WOLFSSL_SERVER) || (defined(WOLFSSL_TLS13) && \ + !defined(WOLFSSL_NO_SERVER_GROUPS_EXT))) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + Suites* suites = NULL; + TLSX* extension = NULL; + SupportedCurves* curves = NULL; + /* supported_groups: secp384r1 (0x0018), an unknown 0xeeee, secp384r1 + * again, secp256r1 (0x0017) */ + const byte peerList[] = { 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, + 0x00, 0x18, 0xee, 0xee, 0x00, 0x18, 0x00, 0x17 }; + /* supported_groups (0x000a), ext len 0x0004, list len 0x0002, 0xeeee */ + const byte noCommon[] = { 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0xee, 0xee }; +#ifdef WOLFSSL_TLS13 + /* The same plus a key_share (0x0033) with no shares, which TLS 1.3 + * requires alongside supported_groups. */ + const byte noCommon13[] = { 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, 0xee, 0xee, + 0x00, 0x33, 0x00, 0x02, 0x00, 0x00 }; +#endif + /* FFDHE codepoints this build lacks, interleaved with common groups. */ + const byte ffdheMix[] = { 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, + 0x00, 0x18, 0x01, 0x03, 0x00, 0x17, 0x01, 0x04 }; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + /* Our own list, least preferred group of the two last. */ + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP384R1), + WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, peerList, (word16)sizeof(peerList), + client_hello, suites), 0); + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + /* Both common groups kept, in the peer's order, the repeat dropped. */ + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, 2); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], WOLFSSL_ECC_SECP384R1); + ExpectIntEQ(curves == NULL ? 0 : curves->name[1], WOLFSSL_ECC_SECP256R1); + wolfSSL_free(ssl); + ssl = NULL; + + /* No group in common is fatal before TLS 1.3, which has no HRR. */ + extension = NULL; + curves = NULL; + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, noCommon, (word16)sizeof(noCommon), + client_hello, suites), + WC_NO_ERR_TRACE(ECC_CURVE_ERROR)); + wolfSSL_free(ssl); + ssl = NULL; + + /* RFC 7919 Section 4: the first FFDHE codepoint this build lacks is kept, + * in the peer's order. */ + extension = NULL; + curves = NULL; + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP384R1), + WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, ffdheMix, (word16)sizeof(ffdheMix), + client_hello, suites), 0); + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); +#if !defined(NO_DH) && !defined(WOLFSSL_NO_TLS12) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(HAVE_FFDHE_6144) + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, 3); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], WOLFSSL_ECC_SECP384R1); + ExpectIntEQ(curves == NULL ? 0 : curves->name[1], WOLFSSL_FFDHE_6144); + ExpectIntEQ(curves == NULL ? 0 : curves->name[2], WOLFSSL_ECC_SECP256R1); +#else + /* Otherwise the FFDHE entry depends on the build, but both ECC groups + * are kept and secp384r1 leads. */ + ExpectIntGE(curves == NULL ? -1 : (int)curves->count, 2); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], WOLFSSL_ECC_SECP384R1); + if (curves != NULL) { + word16 j; + for (j = 0; j < curves->count; j++) { + if (curves->name[j] == WOLFSSL_ECC_SECP256R1) + break; + } + ExpectIntLT(j, curves->count); + } +#endif + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); + ctx = NULL; + +#ifdef WOLFSSL_TLS13 + /* TLS 1.3 can answer with an HRR, so no common group is not an error. */ + extension = NULL; + curves = NULL; + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method())); + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, noCommon13, (word16)sizeof(noCommon13), + client_hello, suites), 0); + if (ssl != NULL) + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + ExpectNotNull(extension); + if (extension != NULL) + ExpectNull(extension->data); + wolfSSL_free(ssl); + ssl = NULL; + wolfSSL_CTX_free(ctx); +#endif +#endif + return EXPECT_RESULT(); +} + +/* The server keeps the first group in its own preference order that the client + * also offered, within the configured DH bounds. */ +int test_TLSX_SupportedFFDHE_Set(void) +{ + EXPECT_DECLS; +#if !defined(NO_TLS) && !defined(NO_DH) && !defined(NO_WOLFSSL_SERVER) && \ + !defined(WOLFSSL_NO_TLS12) && defined(HAVE_SUPPORTED_CURVES) && \ + defined(HAVE_TLS_EXTENSIONS) && defined(HAVE_FFDHE) && \ + defined(HAVE_FFDHE_2048) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && !defined(NO_RSA) && \ + WOLFSSL_MIN_DHKEY_BITS <= 2048 && WOLFSSL_MAX_DHKEY_BITS >= 2048 + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + Suites* suites = NULL; + /* supported_groups (0x000a), ext len 0x0004, list len 0x0002, + * ffdhe2048 (0x0100) */ + const byte ffdheOnly[] = { 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, + 0x01, 0x00 }; + /* The same extension carrying secp256r1 (0x0017) instead. */ + const byte noFfdhe[] = { 0x00, 0x0a, 0x00, 0x04, 0x00, 0x02, + 0x00, 0x17 }; +#if defined(HAVE_FFDHE_3072) && WOLFSSL_MAX_DHKEY_BITS >= 3072 + /* ffdhe2048 then ffdhe3072 (0x0101). */ + const byte ffdheBoth[] = { 0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, + 0x01, 0x00, 0x01, 0x01 }; +#ifdef WOLFSSL_TLS13 + int setGroups[] = { WOLFSSL_FFDHE_2048, WOLFSSL_FFDHE_3072 }; +#endif +#endif + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_2_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, svrCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, svrKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + + /* The one group both sides have is selected. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, ffdheOnly, (word16)sizeof(ffdheOnly), + client_hello, suites), 0); + ExpectIntEQ(TLSX_SupportedFFDHE_Set(ssl), 0); + if (ssl != NULL) { + ExpectIntEQ(ssl->namedGroup, WOLFSSL_FFDHE_2048); + ExpectIntEQ(ssl->options.haveDH, 1); + ExpectIntEQ((int)ssl->buffers.serverDH_P.length, 2048 / 8); + ExpectNotNull(ssl->buffers.serverDH_G.buffer); + /* Without public FFDHE parameters the group's P and G are copied. */ +#ifdef HAVE_PUBLIC_FFDHE + ExpectIntEQ(ssl->buffers.weOwnDH, 0); +#else + ExpectIntEQ(ssl->buffers.weOwnDH, 1); +#endif + } + wolfSSL_free(ssl); + ssl = NULL; + suites = NULL; + + /* A floor above the only common group selects nothing, and that is + * success. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_SetMinDhKey_Sz(ssl, 8192), WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, ffdheOnly, (word16)sizeof(ffdheOnly), + client_hello, suites), 0); + ExpectIntEQ(TLSX_SupportedFFDHE_Set(ssl), 0); + if (ssl != NULL) { + ExpectIntEQ(ssl->options.haveDH, 0); + ExpectNull(ssl->buffers.serverDH_P.buffer); + } + wolfSSL_free(ssl); + ssl = NULL; + suites = NULL; + +#if defined(HAVE_FFDHE_3072) && WOLFSSL_MAX_DHKEY_BITS >= 3072 + /* The server's order decides: 3072 wins though the client lists it + * second. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, ffdheBoth, (word16)sizeof(ffdheBoth), + client_hello, suites), 0); + ExpectIntEQ(TLSX_SupportedFFDHE_Set(ssl), 0); + if (ssl != NULL) + ExpectIntEQ(ssl->namedGroup, WOLFSSL_FFDHE_3072); + wolfSSL_free(ssl); + ssl = NULL; + suites = NULL; + +#ifdef WOLFSSL_TLS13 + /* A list from wolfSSL_set_groups() replaces the default order. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + ExpectIntEQ(wolfSSL_set_groups(ssl, setGroups, + (int)(sizeof(setGroups) / sizeof(setGroups[0]))), + WOLFSSL_SUCCESS); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, ffdheBoth, (word16)sizeof(ffdheBoth), + client_hello, suites), 0); + ExpectIntEQ(TLSX_SupportedFFDHE_Set(ssl), 0); + if (ssl != NULL) + ExpectIntEQ(ssl->namedGroup, WOLFSSL_FFDHE_2048); + wolfSSL_free(ssl); + ssl = NULL; + suites = NULL; +#endif +#endif + + /* No FFDHE codepoint on offer: nothing touches the DH state. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) + suites = (Suites*)WOLFSSL_SUITES(ssl); + ExpectIntEQ(TLSX_Parse(ssl, noFfdhe, (word16)sizeof(noFfdhe), + client_hello, suites), 0); + ExpectIntEQ(TLSX_SupportedFFDHE_Set(ssl), 0); + if (ssl != NULL) { + ExpectIntEQ(ssl->options.haveDH, 0); + ExpectIntEQ(ssl->namedGroup, 0); + } + wolfSSL_free(ssl); + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +/* When the client does not offer the group this build prefers, the server swaps + * its own list in and marks it for sending back in EncryptedExtensions. */ +int test_TLSX_SupportedCurve_check_priority(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_SERVER_GROUPS_EXT) && \ + !defined(NO_WOLFSSL_SERVER) && !defined(NO_CERTS) && \ + !defined(NO_FILESYSTEM) && \ + !defined(NO_TLS) && defined(HAVE_SUPPORTED_CURVES) && \ + defined(HAVE_ECC) && !defined(NO_ECC_SECP) && ECC_MIN_KEY_SZ <= 256 && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) + WOLFSSL_CTX* ctx = NULL; + WOLFSSL* ssl = NULL; + TLSX* extension = NULL; + SupportedCurves* curves = NULL; + word16 preferred = 0; + + ExpectNotNull(ctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method())); + ExpectIntEQ(wolfSSL_CTX_use_certificate_file(ctx, eccCertFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + ExpectIntEQ(wolfSSL_CTX_use_PrivateKey_file(ctx, eccKeyFile, + CERT_FILETYPE), WOLFSSL_SUCCESS); + + /* The client offers only a private-use codepoint, so our own list goes + * back. */ + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + } + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + /* The public API rejects an unsupported name, so store it directly. */ + if (curves != NULL) + curves->name[0] = 0xeeee; + ExpectIntEQ(TLSX_SupportedCurve_CheckPriority(ssl), 0); + if (extension != NULL) + ExpectIntEQ(extension->resp, 1); + if ((extension != NULL) && (extension->data != NULL)) { + curves = (SupportedCurves*)extension->data; + /* Our own list replaced the client's, so the codepoint is gone. */ + ExpectIntGT((int)curves->count, 0); + if (curves->count > 0) + preferred = curves->name[0]; + ExpectIntNE(preferred, 0xeeee); + } + wolfSSL_free(ssl); + ssl = NULL; + + /* The client now offers the preferred group, so nothing is sent back. */ + extension = NULL; + curves = NULL; + ExpectNotNull(ssl = wolfSSL_new(ctx)); + if (ssl != NULL) { + ExpectIntEQ(wolfSSL_UseSupportedCurve(ssl, WOLFSSL_ECC_SECP256R1), + WOLFSSL_SUCCESS); + extension = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + } + ExpectNotNull(extension); + if (extension != NULL) + curves = (SupportedCurves*)extension->data; + ExpectNotNull(curves); + if ((curves != NULL) && (preferred != 0)) + curves->name[0] = preferred; + ExpectIntEQ(TLSX_SupportedCurve_CheckPriority(ssl), 0); + if (extension != NULL) + ExpectIntEQ(extension->resp, 0); + if ((extension != NULL) && (extension->data != NULL)) { + curves = (SupportedCurves*)extension->data; + ExpectIntEQ((int)curves->count, 1); + ExpectIntEQ(curves->name[0], preferred); + } + wolfSSL_free(ssl); + ssl = NULL; + + wolfSSL_CTX_free(ctx); +#endif + return EXPECT_RESULT(); +} + +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC_BRAINPOOL) && \ + defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 +static int test_tls_ext_group_count(WOLFSSL* ssl, word16 name) +{ + TLSX* ext = TLSX_Find(ssl->extensions, TLSX_SUPPORTED_GROUPS); + SupportedCurves* curves = NULL; + int cnt = 0; + word16 i; + + if (ext != NULL) + curves = (SupportedCurves*)ext->data; + for (i = 0; (curves != NULL) && (i < curves->count); i++) { + if (curves->name[i] == name) + cnt++; + } + return cnt; +} +#endif + +/* The default list follows the version: RFC 7027 brainpool groups for TLS 1.2, + * RFC 8734 ones for TLS 1.3, both when TLS 1.3 may fall back to TLS 1.2, and + * no ML-KEM groups for TLS 1.2. */ +int test_TLSX_SupportedGroups_default_filter(void) +{ + EXPECT_DECLS; +#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && \ + defined(HAVE_SUPPORTED_CURVES) && defined(HAVE_ECC_BRAINPOOL) && \ + defined(WOLFSSL_TLS13) && !defined(WOLFSSL_NO_TLS12) && \ + (!defined(NO_ECC256) || defined(HAVE_ALL_CURVES)) && ECC_MIN_KEY_SZ <= 256 + const method_provider methods[] = { wolfTLSv1_2_client_method, + wolfTLSv1_3_client_method, wolfSSLv23_client_method }; + const int want12[] = { 1, 0, 1 }; + const int want13[] = { 0, 1, 1 }; + int m; + + for (m = 0; m < (int)(sizeof(methods) / sizeof(methods[0])); m++) { + struct test_memio_ctx test_ctx; + WOLFSSL_CTX* ctx_c = NULL; + WOLFSSL_CTX* ctx_s = NULL; + WOLFSSL* ssl_c = NULL; + WOLFSSL* ssl_s = NULL; + + XMEMSET(&test_ctx, 0, sizeof(test_ctx)); + ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, + &ssl_s, methods[m], wolfSSLv23_server_method), 0); + ExpectIntEQ(wolfSSL_connect(ssl_c), -1); + ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ); + if (ssl_c != NULL) { + ExpectIntEQ(test_tls_ext_group_count(ssl_c, + WOLFSSL_ECC_BRAINPOOLP256R1), want12[m]); + ExpectIntEQ(test_tls_ext_group_count(ssl_c, + WOLFSSL_ECC_BRAINPOOLP256R1TLS13), want13[m]); + if (want13[m] == 0) { + ExpectIntEQ(test_tls_ext_group_count(ssl_c, + WOLFSSL_ML_KEM_768), 0); + ExpectIntEQ(test_tls_ext_group_count(ssl_c, + WOLFSSL_X25519MLKEM768), 0); + ExpectIntEQ(test_tls_ext_group_count(ssl_c, + WOLFSSL_SECP256R1MLKEM768), 0); + } + } + + wolfSSL_free(ssl_c); + wolfSSL_free(ssl_s); + wolfSSL_CTX_free(ctx_c); + wolfSSL_CTX_free(ctx_s); + } +#endif + return EXPECT_RESULT(); +} + /* RFC 8422 Section 5.1.2: a client that sends the ec_point_formats extension * MUST include the uncompressed (0) point format. When the uncompressed format * is omitted the server records this (ssl->options.peerNoUncompPF) during diff --git a/tests/api/test_tls_ext.h b/tests/api/test_tls_ext.h index 3ba806a662b..2a647eb5660 100644 --- a/tests/api/test_tls_ext.h +++ b/tests/api/test_tls_ext.h @@ -70,6 +70,12 @@ int test_TLSX_SupportedCurve_empty_or_unsupported(void); int test_TLSX_SignatureAlgorithmsCert_parse(void); int test_TLSX_PointFormat_list(void); int test_TLSX_MaxFragment_value(void); +int test_TLSX_SupportedCurve_append(void); +int test_TLSX_SupportedCurve_grow(void); +int test_TLSX_SupportedCurve_intersection(void); +int test_TLSX_SupportedCurve_check_priority(void); +int test_TLSX_SupportedFFDHE_Set(void); +int test_TLSX_SupportedGroups_default_filter(void); int test_TLSX_PointFormat_uncompressed_required(void); int test_wolfSSL_CTX_add_client_custom_ext(void); int test_wolfSSL_custom_ext_handshake(void); diff --git a/tests/api/test_tls_parse.c b/tests/api/test_tls_parse.c index 619461048c3..dde73199be7 100644 --- a/tests/api/test_tls_parse.c +++ b/tests/api/test_tls_parse.c @@ -141,6 +141,14 @@ static void* tls_parse_fail_realloc(void* ptr, size_t size) { return realloc(ptr, size); } + +TEST_TLS_PARSE_UNUSED +static void* tls_parse_null_realloc(void* ptr, size_t size) +{ + (void)ptr; + (void)size; + return NULL; +} #endif /* WOLFSSL_TEST_STATIC_BUILD && !NO_TLS */ #if defined(WOLFSSL_TEST_STATIC_BUILD) && defined(WOLFSSL_TLS13) && \ @@ -153,15 +161,17 @@ TEST_TLS_PARSE_UNUSED static int test_tls_parse_push_curve(TLSX** extensions, WOLFSSL* ssl, word16 name) { - SupportedCurve* curve = (SupportedCurve*)XMALLOC(sizeof(SupportedCurve), - ssl->heap, DYNAMIC_TYPE_TLSX); - if (curve == NULL) + SupportedCurves* curves = (SupportedCurves*)XMALLOC( + sizeof(SupportedCurves) + sizeof(word16), ssl->heap, + DYNAMIC_TYPE_TLSX); + if (curves == NULL) return WC_NO_ERR_TRACE(MEMORY_E); - curve->name = name; - curve->next = NULL; - if (TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, curve, ssl->heap) != 0) { + curves->count = 1; + curves->cap = 1; + curves->name[0] = name; + if (TLSX_Push(extensions, TLSX_SUPPORTED_GROUPS, curves, ssl->heap) != 0) { /* TLSX_New failed before it stored curve, so the entry is still ours. */ - XFREE(curve, ssl->heap, DYNAMIC_TYPE_TLSX); + XFREE(curves, ssl->heap, DYNAMIC_TYPE_TLSX); return WC_NO_ERR_TRACE(MEMORY_E); } return 0; @@ -2329,14 +2339,68 @@ int test_TLSX_SupportedGroups_parse(void) extensions = NULL; #ifdef WOLFSSL_TEST_STATIC_BUILD - /* Offered group matches the local restriction, but recording it - * in the intersection list is forced to fail: a non-zero ret - * reaches the "no common curve" check below without commonCurves - * ever becoming non-NULL through the normal path. */ + /* New lists come from malloc and do not depend on realloc(NULL). */ + { + wolfSSL_Malloc_cb prevM = NULL; + wolfSSL_Free_cb prevF = NULL; + wolfSSL_Realloc_cb prevR = NULL; + + ExpectIntEQ(wolfSSL_GetAllocators(&prevM, &prevF, &prevR), 0); + ExpectIntEQ(wolfSSL_SetAllocators(tls_parse_fail_malloc, + tls_parse_fail_free, tls_parse_null_realloc), 0); + ExpectIntEQ(TLSX_UseSupportedCurve(&extensions, + WOLFSSL_ECC_SECP256R1, ssl->heap, + ssl->options.side), WOLFSSL_SUCCESS); + (void)wolfSSL_SetAllocators(prevM, prevF, prevR); + TLSX_FreeAll(extensions, NULL); + extensions = NULL; + } + + /* A failed grow leaves the list in the extension as it was. */ + { + wolfSSL_Malloc_cb prevM = NULL; + wolfSSL_Free_cb prevF = NULL; + wolfSSL_Realloc_cb prevR = NULL; + TLSX* ext = NULL; + SupportedCurves* before = NULL; + SupportedCurves* curves = NULL; + + ExpectIntEQ(test_tls_parse_push_curve(&extensions, ssl, + WOLFSSL_ECC_SECP256R1), 0); + ExpectNotNull(ext = TLSX_Find(extensions, + TLSX_SUPPORTED_GROUPS)); + if (ext != NULL) + before = (SupportedCurves*)ext->data; + + ExpectIntEQ(wolfSSL_GetAllocators(&prevM, &prevF, &prevR), 0); + ExpectIntEQ(wolfSSL_SetAllocators(tls_parse_fail_malloc, + tls_parse_fail_free, tls_parse_null_realloc), 0); + tls_parse_alloc_seen = 0; + tls_parse_fail_after = 0; + ExpectIntEQ(TLSX_UseSupportedCurve(&extensions, + WOLFSSL_ECC_SECP384R1, ssl->heap, ssl->options.side), + WC_NO_ERR_TRACE(MEMORY_E)); + tls_parse_fail_after = -1; + (void)wolfSSL_SetAllocators(prevM, prevF, prevR); + + if (ext != NULL) + curves = (SupportedCurves*)ext->data; + ExpectPtrEq(curves, before); + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, 1); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], + WOLFSSL_ECC_SECP256R1); + TLSX_FreeAll(extensions, NULL); + extensions = NULL; + } + + /* The intersection is one allocation; when it fails the list in the + * extension is left as it was. */ { wolfSSL_Malloc_cb prevM = NULL; wolfSSL_Free_cb prevF = NULL; wolfSSL_Realloc_cb prevR = NULL; + TLSX* ext = NULL; + SupportedCurves* curves = NULL; ExpectIntEQ(TLSX_UseSupportedCurve(&extensions, WOLFSSL_ECC_SECP256R1, ssl->heap, @@ -2351,10 +2415,106 @@ int test_TLSX_SupportedGroups_parse(void) ExpectIntEQ(TLSX_SupportedCurve_Parse(ssl, goodBody, (word16)sizeof(goodBody), 1, &extensions), WC_NO_ERR_TRACE(MEMORY_E)); + ExpectIntEQ(tls_parse_alloc_seen, 1); + + ExpectNotNull(ext = TLSX_Find(extensions, + TLSX_SUPPORTED_GROUPS)); + if (ext != NULL) + curves = (SupportedCurves*)ext->data; + ExpectNotNull(curves); + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, 1); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], + WOLFSSL_ECC_SECP256R1); + + tls_parse_alloc_seen = 0; + tls_parse_fail_after = 1; + ExpectIntEQ(TLSX_SupportedCurve_Parse(ssl, goodBody, + (word16)sizeof(goodBody), 1, &extensions), 0); + ExpectIntEQ(tls_parse_alloc_seen, 1); tls_parse_fail_after = -1; (void)wolfSSL_SetAllocators(prevM, prevF, prevR); TLSX_FreeAll(extensions, NULL); + extensions = NULL; + } + + /* Only the first FFDHE codepoint this build lacks is kept, in the + * peer's order. */ + { + /* secp256r1, then two FFDHE codepoints no build implements. */ + const byte ffdheTwo[] = { 0x00, 0x06, 0x00, 0x17, 0x01, 0x20, + 0x01, 0x21 }; + TLSX* ext = NULL; + SupportedCurves* curves = NULL; + + ExpectIntEQ(test_tls_parse_push_curve(&extensions, ssl, + WOLFSSL_ECC_SECP256R1), 0); + ExpectIntEQ(TLSX_SupportedCurve_Parse(ssl, ffdheTwo, + (word16)sizeof(ffdheTwo), 1, &extensions), 0); + ExpectNotNull(ext = TLSX_Find(extensions, + TLSX_SUPPORTED_GROUPS)); + if (ext != NULL) + curves = (SupportedCurves*)ext->data; + ExpectNotNull(curves); + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, 2); + ExpectIntEQ(curves == NULL ? 0 : curves->name[0], + WOLFSSL_ECC_SECP256R1); + ExpectIntEQ(curves == NULL ? 0 : curves->name[1], 0x0120); + TLSX_FreeAll(extensions, NULL); + extensions = NULL; + } + + /* After an HRR the second ClientHello is intersected with the first + * one's list, which can be longer than WOLFSSL_MAX_GROUP_COUNT. */ + { + #define TEST_LONG_CNT (WOLFSSL_MAX_GROUP_COUNT + 2) + byte peer[OPAQUE16_LEN + TEST_LONG_CNT * OPAQUE16_LEN]; + TLSX* ext = NULL; + SupportedCurves* curves = NULL; + word16 name; + word16 i; + int ret; + + curves = (SupportedCurves*)XMALLOC(sizeof(SupportedCurves) + + TEST_LONG_CNT * sizeof(word16), ssl->heap, + DYNAMIC_TYPE_TLSX); + ExpectNotNull(curves); + if (curves != NULL) { + curves->count = TEST_LONG_CNT; + curves->cap = TEST_LONG_CNT; + for (i = 0; i < TEST_LONG_CNT; i++) + curves->name[i] = (word16)(0x0a00 + i); + ret = TLSX_Push(&extensions, TLSX_SUPPORTED_GROUPS, curves, + ssl->heap); + if (ret != 0) + XFREE(curves, ssl->heap, DYNAMIC_TYPE_TLSX); + ExpectIntEQ(ret, 0); + } + + peer[0] = (byte)((TEST_LONG_CNT * OPAQUE16_LEN) >> 8); + peer[1] = (byte)(TEST_LONG_CNT * OPAQUE16_LEN); + for (i = 0; i < TEST_LONG_CNT; i++) { + name = (word16)(0x0a00 + TEST_LONG_CNT - 1 - i); + peer[OPAQUE16_LEN + i * OPAQUE16_LEN] = (byte)(name >> 8); + peer[OPAQUE16_LEN + i * OPAQUE16_LEN + 1] = (byte)name; + } + + ExpectIntEQ(TLSX_SupportedCurve_Parse(ssl, peer, + (word16)sizeof(peer), 1, &extensions), 0); + ExpectNotNull(ext = TLSX_Find(extensions, + TLSX_SUPPORTED_GROUPS)); + curves = NULL; + if (ext != NULL) + curves = (SupportedCurves*)ext->data; + ExpectNotNull(curves); + ExpectIntEQ(curves == NULL ? -1 : (int)curves->count, + TEST_LONG_CNT); + for (i = 0; (curves != NULL) && (i < curves->count); i++) { + ExpectIntEQ(curves->name[i], 0x0a00 + TEST_LONG_CNT - 1 - i); + } + TLSX_FreeAll(extensions, NULL); + extensions = NULL; + #undef TEST_LONG_CNT } #endif } @@ -2380,6 +2540,13 @@ int test_TLSX_SupportedGroups_parse(void) WOLFSSL_SUCCESS); ExpectIntEQ(TLSX_SupportedCurve_Parse(ssl, noMatch261, (word16)sizeof(noMatch261), 0, &extensions), 0); + { + TLSX* ext = TLSX_Find(extensions, TLSX_SUPPORTED_GROUPS); + + ExpectNotNull(ext); + if (ext != NULL) + ExpectNull(ext->data); + } TLSX_FreeAll(extensions, NULL); } wolfSSL_free(ssl); diff --git a/wolfssl/internal.h b/wolfssl/internal.h index a6333cd5653..bf1b9fd6a66 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -3916,10 +3916,16 @@ WOLFSSL_LOCAL void TLSX_CertificateAuthorities_FreeAll( /** Supported Elliptic Curves - RFC 4492 (session 4) */ #ifdef HAVE_SUPPORTED_CURVES -typedef struct SupportedCurve { - word16 name; /* Curve Names */ - struct SupportedCurve* next; /* List Behavior */ -} SupportedCurve; +typedef struct SupportedCurves { + word16 count; /* Curve names in use */ + word16 cap; /* Curve names allocated */ + /* Ignore "nonstandard extension used : zero-sized array in struct/union" + * MSVC warning */ + #ifdef _MSC_VER + #pragma warning(disable: 4200) + #endif + word16 name[WC_FLEXIBLE_ARRAY_SIZE]; /* Curve names */ +} SupportedCurves; typedef struct PointFormats { byte count; /* Formats in use */ @@ -3948,8 +3954,13 @@ WOLFSSL_LOCAL int TLSX_IsGroupSupported(int namedGroup, int side); #ifndef NO_WOLFSSL_SERVER WOLFSSL_LOCAL int TLSX_ValidateSupportedCurves(const WOLFSSL* ssl, byte first, byte second, word32* ecdhCurveOID); -WOLFSSL_LOCAL int TLSX_SupportedCurve_CheckPriority(WOLFSSL* ssl); -WOLFSSL_LOCAL int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl); +#ifdef WOLFSSL_API_PREFIX_MAP + #define TLSX_SupportedCurve_CheckPriority \ + wolfSSL_TLSX_SupportedCurve_CheckPriority + #define TLSX_SupportedFFDHE_Set wolfSSL_TLSX_SupportedFFDHE_Set +#endif +WOLFSSL_TEST_VIS int TLSX_SupportedCurve_CheckPriority(WOLFSSL* ssl); +WOLFSSL_TEST_VIS int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl); #endif WOLFSSL_LOCAL int TLSX_SupportedCurve_IsSupported(WOLFSSL* ssl, word16 name); WOLFSSL_LOCAL int TLSX_SupportedCurve_Preferred(WOLFSSL* ssl,