From 89491c7e366cff23e3dcce9f195c428b743c2610 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 1 Aug 2024 10:01:32 +0200 Subject: [PATCH 1/2] Improvements for PQC hybrid key exchange MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for X25519 and X448 based hybrid PQC + ECC key exchange groups. Furthermore, two new combinations with SECP curves are added to match OQS combinations. This also incorporates the changed order of X25519 and X448 based combinations to place the PQC material before the ECDH material. This is motivated by the necessity to always have material of a FIPS approved algorithm first. Also, codepoints are updated to reflect the latest draft standards for pure ML-KEM and some of the hybrids. With these changes and based on the recent additions to both enable ML-KEM final and draft versions simultaneously, a WolfSSL TLS server is now compatible with all recent browsers that support either the draft version of ML-KEM (Chromium based browsers and Firefox < version 132; only when the draft version is enabled in the build) or the final version already (Firefox > version 132). In the process of extending support, some code and logic cleanup happened. Furthermore, some memory leaks within the hybrid code path have been fixed. Signed-off-by: Tobias Frauenschläger --- examples/benchmark/tls_bench.c | 21 +- examples/client/client.c | 55 +- examples/server/server.c | 55 +- src/internal.c | 51 + src/ssl.c | 156 ++- src/tls.c | 1542 ++++++++++++++++++------- src/tls13.c | 3 +- tests/include.am | 6 +- tests/suites.c | 50 +- tests/test-dtls13-pq-2-frag.conf | 23 - tests/test-dtls13-pq-2.conf | 27 - tests/test-dtls13-pq-frag.conf | 25 +- tests/test-dtls13-pq-hybrid-frag.conf | 131 +++ tests/test-dtls13-pq-hybrid.conf | 51 + tests/test-dtls13-pq.conf | 4 +- tests/test-tls13-pq-2.conf | 59 - tests/test-tls13-pq-hybrid.conf | 149 +++ wolfssl/internal.h | 18 +- wolfssl/ssl.h | 72 +- wolfssl/wolfcrypt/ext_kyber.h | 10 +- 20 files changed, 1865 insertions(+), 643 deletions(-) delete mode 100644 tests/test-dtls13-pq-2-frag.conf delete mode 100644 tests/test-dtls13-pq-2.conf create mode 100644 tests/test-dtls13-pq-hybrid-frag.conf create mode 100644 tests/test-dtls13-pq-hybrid.conf delete mode 100644 tests/test-tls13-pq-2.conf create mode 100644 tests/test-tls13-pq-hybrid.conf diff --git a/examples/benchmark/tls_bench.c b/examples/benchmark/tls_bench.c index 15e053dfdb..e44f164c2c 100644 --- a/examples/benchmark/tls_bench.c +++ b/examples/benchmark/tls_bench.c @@ -296,17 +296,26 @@ static struct group_info groups[] = { { WOLFSSL_ML_KEM_512, "ML_KEM_512" }, { WOLFSSL_ML_KEM_768, "ML_KEM_768" }, { WOLFSSL_ML_KEM_1024, "ML_KEM_1024" }, - { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" }, - { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" }, - { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" }, + { WOLFSSL_P256_ML_KEM_512, "P256_ML_KEM_512" }, + { WOLFSSL_P384_ML_KEM_768, "P384_ML_KEM_768" }, + { WOLFSSL_P256_ML_KEM_768, "P256_ML_KEM_768" }, + { WOLFSSL_P521_ML_KEM_1024, "P521_ML_KEM_1024" }, + { WOLFSSL_P384_ML_KEM_1024, "P384_ML_KEM_1024" }, + { WOLFSSL_X25519_ML_KEM_512, "X25519_ML_KEM_512" }, + { WOLFSSL_X448_ML_KEM_768, "X448_ML_KEM_768" }, + { WOLFSSL_X25519_ML_KEM_768, "X25519_ML_KEM_768" }, #endif #ifdef WOLFSSL_KYBER_ORIGINAL { WOLFSSL_KYBER_LEVEL1, "KYBER_LEVEL1" }, { WOLFSSL_KYBER_LEVEL3, "KYBER_LEVEL3" }, { WOLFSSL_KYBER_LEVEL5, "KYBER_LEVEL5" }, - { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, - { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, - { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, + { WOLFSSL_P256_KYBER_LEVEL1, "P256_KYBER_LEVEL1" }, + { WOLFSSL_P384_KYBER_LEVEL3, "P384_KYBER_LEVEL3" }, + { WOLFSSL_P256_KYBER_LEVEL3, "P256_KYBER_LEVEL3" }, + { WOLFSSL_P521_KYBER_LEVEL5, "P521_KYBER_LEVEL5" }, + { WOLFSSL_X25519_KYBER_LEVEL1, "X25519_KYBER_LEVEL1" }, + { WOLFSSL_X448_KYBER_LEVEL3, "X448_KYBER_LEVEL3" }, + { WOLFSSL_X25519_KYBER_LEVEL3, "X25519_KYBER_LEVEL3" }, #endif #endif { 0, NULL } diff --git a/examples/client/client.c b/examples/client/client.c index 5c2e34053f..dca255c195 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -431,12 +431,36 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, if (XSTRCMP(pqcAlg, "P384_ML_KEM_768") == 0) { group = WOLFSSL_P384_ML_KEM_768; } + else if (XSTRCMP(pqcAlg, "P256_ML_KEM_768") == 0) { + group = WOLFSSL_P256_ML_KEM_768; + } else #endif #ifndef WOLFSSL_NO_ML_KEM_1024 if (XSTRCMP(pqcAlg, "P521_ML_KEM_1024") == 0) { group = WOLFSSL_P521_ML_KEM_1024; } + else if (XSTRCMP(pqcAlg, "P384_ML_KEM_1024") == 0) { + group = WOLFSSL_P384_ML_KEM_1024; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_ML_KEM_512") == 0) { + group = WOLFSSL_X25519_ML_KEM_512; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_ML_KEM_768") == 0) { + group = WOLFSSL_X25519_ML_KEM_768; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) + if (XSTRCMP(pqcAlg, "X448_ML_KEM_768") == 0) { + group = WOLFSSL_X448_ML_KEM_768; + } else #endif #endif /* WOLFSSL_NO_ML_KEM */ @@ -469,6 +493,9 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, if (XSTRCMP(pqcAlg, "P384_KYBER_LEVEL3") == 0) { group = WOLFSSL_P384_KYBER_LEVEL3; } + else if (XSTRCMP(pqcAlg, "P256_KYBER_LEVEL3") == 0) { + group = WOLFSSL_P256_KYBER_LEVEL3; + } else #endif #ifndef WOLFSSL_NO_KYBER1024 @@ -477,6 +504,24 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, } else #endif + #if !defined(WOLFSSL_NO_KYBER512) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_KYBER_LEVEL1") == 0) { + group = WOLFSSL_X25519_KYBER_LEVEL1; + } + else + #endif + #if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_KYBER_LEVEL3") == 0) { + group = WOLFSSL_X25519_KYBER_LEVEL3; + } + else + #endif + #if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE448) + if (XSTRCMP(pqcAlg, "X448_KYBER_LEVEL3") == 0) { + group = WOLFSSL_X448_KYBER_LEVEL3; + } + else + #endif #endif /* WOLFSSL_KYBER_ORIGINAL */ { err_sys("invalid post-quantum KEM specified"); @@ -1378,12 +1423,18 @@ static const char* client_usage_msg[][78] = { #ifndef WOLFSSL_NO_ML_KEM " ML_KEM_512, ML_KEM_768, ML_KEM_1024, P256_ML_KEM_512," "\n" - " P384_ML_KEM_768, P521_ML_KEM_1024\n" + " P384_ML_KEM_768, P256_ML_KEM_768, P521_ML_KEM_1024,\n" + " P384_ML_KEM_1024, X25519_ML_KEM_512, " + "X25519_ML_KEM_768,\n" + " X448_ML_KEM_768\n" #endif #ifdef WOLFSSL_KYBER_ORIGINAL " KYBER_LEVEL1, KYBER_LEVEL3, KYBER_LEVEL5, " "P256_KYBER_LEVEL1,\n" - " P384_KYBER_LEVEL3, P521_KYBER_LEVEL5\n" + " P384_KYBER_LEVEL3, P256_KYBER_LEVEL3, " + "P521_KYBER_LEVEL5,\n" + " X25519_KYBER_LEVEL1, X25519_KYBER_LEVEL3, " + "X448_KYBER_LEVEL3\n" #endif "", /* 69 */ diff --git a/examples/server/server.c b/examples/server/server.c index 83581ee42e..2f093d1619 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -744,12 +744,36 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, if (XSTRCMP(pqcAlg, "P384_ML_KEM_768") == 0) { groups[count] = WOLFSSL_P384_ML_KEM_768; } + else if (XSTRCMP(pqcAlg, "P256_ML_KEM_768") == 0) { + groups[count] = WOLFSSL_P256_ML_KEM_768; + } else #endif #ifndef WOLFSSL_NO_ML_KEM_1024 if (XSTRCMP(pqcAlg, "P521_ML_KEM_1024") == 0) { groups[count] = WOLFSSL_P521_ML_KEM_1024; } + else if (XSTRCMP(pqcAlg, "P384_ML_KEM_1024") == 0) { + groups[count] = WOLFSSL_P384_ML_KEM_1024; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_ML_KEM_512") == 0) { + groups[count] = WOLFSSL_X25519_ML_KEM_512; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_ML_KEM_768") == 0) { + groups[count] = WOLFSSL_X25519_ML_KEM_768; + } + else + #endif + #if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448) + if (XSTRCMP(pqcAlg, "X448_ML_KEM_768") == 0) { + groups[count] = WOLFSSL_X448_ML_KEM_768; + } else #endif #endif /* WOLFSSL_NO_ML_KEM */ @@ -782,6 +806,9 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, if (XSTRCMP(pqcAlg, "P384_KYBER_LEVEL3") == 0) { groups[count] = WOLFSSL_P384_KYBER_LEVEL3; } + else if (XSTRCMP(pqcAlg, "P256_KYBER_LEVEL3") == 0) { + groups[count] = WOLFSSL_P256_KYBER_LEVEL3; + } else #endif #ifndef WOLFSSL_NO_KYBER1024 @@ -790,6 +817,24 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519, } else #endif + #if !defined(WOLFSSL_NO_KYBER512) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_KYBER_LEVEL1") == 0) { + groups[count] = WOLFSSL_X25519_KYBER_LEVEL1; + } + else + #endif + #if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE25519) + if (XSTRCMP(pqcAlg, "X25519_KYBER_LEVEL3") == 0) { + groups[count] = WOLFSSL_X25519_KYBER_LEVEL3; + } + else + #endif + #if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE448) + if (XSTRCMP(pqcAlg, "X448_KYBER_LEVEL3") == 0) { + groups[count] = WOLFSSL_X448_KYBER_LEVEL3; + } + else + #endif #endif { err_sys("invalid post-quantum KEM specified"); @@ -1027,12 +1072,18 @@ static const char* server_usage_msg[][66] = { #ifndef WOLFSSL_NO_ML_KEM " ML_KEM_512, ML_KEM_768, ML_KEM_1024, P256_ML_KEM_512," "\n" - " P384_ML_KEM_768, P521_ML_KEM_1024\n" + " P384_ML_KEM_768, P256_ML_KEM_768, P521_ML_KEM_1024,\n" + " P384_ML_KEM_1024, X25519_ML_KEM_512, " + "X25519_ML_KEM_768,\n" + " X448_ML_KEM_768\n" #endif #ifdef WOLFSSL_KYBER_ORIGINAL " KYBER_LEVEL1, KYBER_LEVEL3, KYBER_LEVEL5, " "P256_KYBER_LEVEL1,\n" - " P384_KYBER_LEVEL3, P521_KYBER_LEVEL5\n" + " P384_KYBER_LEVEL3, P256_KYBER_LEVEL3, " + "P521_KYBER_LEVEL5,\n" + " X25519_KYBER_LEVEL1, X25519_KYBER_LEVEL3, " + "X448_KYBER_LEVEL3\n" #endif "", /* 60 */ diff --git a/src/internal.c b/src/internal.c index c34d94b2a4..b7f90a864a 100644 --- a/src/internal.c +++ b/src/internal.c @@ -35136,6 +35136,57 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, } #endif /* HAVE_ECC */ +#ifdef WOLFSSL_HAVE_KYBER + /* Returns 1 when the given group is a PQC group, 0 otherwise. */ + int NamedGroupIsPqc(int group) + { + switch (group) { + #ifndef WOLFSSL_NO_ML_KEM + case WOLFSSL_ML_KEM_512: + case WOLFSSL_ML_KEM_768: + case WOLFSSL_ML_KEM_1024: + #endif + #ifdef WOLFSSL_KYBER_ORIGINAL + case WOLFSSL_KYBER_LEVEL1: + case WOLFSSL_KYBER_LEVEL3: + case WOLFSSL_KYBER_LEVEL5: + #endif + return 1; + default: + return 0; + } + } + + /* Returns 1 when the given group is a PQC hybrid group, 0 otherwise. */ + int NamedGroupIsPqcHybrid(int group) + { + switch (group) { + #ifndef WOLFSSL_NO_ML_KEM + case WOLFSSL_P256_ML_KEM_768: + case WOLFSSL_X25519_ML_KEM_768: + case WOLFSSL_P384_ML_KEM_1024: + case WOLFSSL_P256_ML_KEM_512: + case WOLFSSL_P384_ML_KEM_768: + case WOLFSSL_P521_ML_KEM_1024: + case WOLFSSL_X25519_ML_KEM_512: + case WOLFSSL_X448_ML_KEM_768: + #endif + #ifdef WOLFSSL_KYBER_ORIGINAL + case WOLFSSL_P256_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL1: + case WOLFSSL_P384_KYBER_LEVEL3: + case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_X25519_KYBER_LEVEL1: + case WOLFSSL_X448_KYBER_LEVEL3: + #endif + return 1; + default: + return 0; + } + } +#endif /* WOLFSSL_HAVE_KYBER */ + int TranslateErrorToAlert(int err) { switch (err) { diff --git a/src/ssl.c b/src/ssl.c index fca23c9b89..c674d7e483 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3601,6 +3601,11 @@ static int isValidCurveGroup(word16 name) case WOLFSSL_P256_ML_KEM_512: case WOLFSSL_P384_ML_KEM_768: case WOLFSSL_P521_ML_KEM_1024: + case WOLFSSL_P384_ML_KEM_1024: + case WOLFSSL_X25519_ML_KEM_512: + case WOLFSSL_X448_ML_KEM_768: + case WOLFSSL_X25519_ML_KEM_768: + case WOLFSSL_P256_ML_KEM_768: #endif #endif /* !WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL @@ -3611,6 +3616,10 @@ static int isValidCurveGroup(word16 name) case WOLFSSL_P256_KYBER_LEVEL1: case WOLFSSL_P384_KYBER_LEVEL3: case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_X25519_KYBER_LEVEL1: + case WOLFSSL_X448_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL3: #endif #endif /* WOLFSSL_KYBER_ORIGINAL */ #endif @@ -15381,66 +15390,97 @@ const char* wolfSSL_get_curve_name(WOLFSSL* ssl) if (IsAtLeastTLSv1_3(ssl->version)) { switch (ssl->namedGroup) { #ifndef WOLFSSL_NO_ML_KEM -#ifdef HAVE_LIBOQS - case WOLFSSL_ML_KEM_512: - return "ML_KEM_512"; - case WOLFSSL_ML_KEM_768: - return "ML_KEM_768"; - case WOLFSSL_ML_KEM_1024: - return "ML_KEM_1024"; - case WOLFSSL_P256_ML_KEM_512: - return "P256_ML_KEM_512"; - case WOLFSSL_P384_ML_KEM_768: - return "P384_ML_KEM_768"; - case WOLFSSL_P521_ML_KEM_1024: - return "P521_ML_KEM_1024"; -#elif defined(WOLFSSL_WC_KYBER) +#if defined(WOLFSSL_WC_KYBER) #ifndef WOLFSSL_NO_ML_KEM_512 case WOLFSSL_ML_KEM_512: return "ML_KEM_512"; case WOLFSSL_P256_ML_KEM_512: return "P256_ML_KEM_512"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_ML_KEM_512: + return "X25519_ML_KEM_512"; + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_768 case WOLFSSL_ML_KEM_768: return "ML_KEM_768"; case WOLFSSL_P384_ML_KEM_768: return "P384_ML_KEM_768"; + case WOLFSSL_P256_ML_KEM_768: + return "P256_ML_KEM_768"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_ML_KEM_768: + return "X25519_ML_KEM_768"; + #endif + #ifdef HAVE_CURVE448 + case WOLFSSL_X448_ML_KEM_768: + return "X448_ML_KEM_768"; + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_1024 case WOLFSSL_ML_KEM_1024: return "ML_KEM_1024"; case WOLFSSL_P521_ML_KEM_1024: return "P521_ML_KEM_1024"; + case WOLFSSL_P384_ML_KEM_1024: + return "P384_ML_KEM_1024"; #endif -#endif -#endif +#elif defined(HAVE_LIBOQS) + case WOLFSSL_ML_KEM_512: + return "ML_KEM_512"; + case WOLFSSL_ML_KEM_768: + return "ML_KEM_768"; + case WOLFSSL_ML_KEM_1024: + return "ML_KEM_1024"; + case WOLFSSL_P256_ML_KEM_512: + return "P256_ML_KEM_512"; + case WOLFSSL_P384_ML_KEM_768: + return "P384_ML_KEM_768"; + case WOLFSSL_P256_ML_KEM_768: + return "P256_ML_KEM_768"; + case WOLFSSL_P521_ML_KEM_1024: + return "P521_ML_KEM_1024"; + case WOLFSSL_P384_ML_KEM_1024: + return "P384_ML_KEM_1024"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_ML_KEM_512: + return "X25519_ML_KEM_512"; + case WOLFSSL_X25519_ML_KEM_768: + return "X25519_ML_KEM_768"; + #endif + #ifdef HAVE_CURVE448 + case WOLFSSL_X448_ML_KEM_768: + return "X448_ML_KEM_768"; + #endif +#endif /* WOLFSSL_WC_KYBER */ +#endif /* WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL -#ifdef HAVE_LIBOQS - case WOLFSSL_KYBER_LEVEL1: - return "KYBER_LEVEL1"; - case WOLFSSL_KYBER_LEVEL3: - return "KYBER_LEVEL3"; - case WOLFSSL_KYBER_LEVEL5: - return "KYBER_LEVEL5"; - case WOLFSSL_P256_KYBER_LEVEL1: - return "P256_KYBER_LEVEL1"; - case WOLFSSL_P384_KYBER_LEVEL3: - return "P384_KYBER_LEVEL3"; - case WOLFSSL_P521_KYBER_LEVEL5: - return "P521_KYBER_LEVEL5"; -#elif defined(WOLFSSL_WC_KYBER) +#if defined(WOLFSSL_WC_KYBER) #ifndef WOLFSSL_NO_KYBER512 case WOLFSSL_KYBER_LEVEL1: return "KYBER_LEVEL1"; case WOLFSSL_P256_KYBER_LEVEL1: return "P256_KYBER_LEVEL1"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_KYBER_LEVEL1: + return "X25519_KYBER_LEVEL1"; + #endif #endif #ifndef WOLFSSL_NO_KYBER768 case WOLFSSL_KYBER_LEVEL3: return "KYBER_LEVEL3"; case WOLFSSL_P384_KYBER_LEVEL3: return "P384_KYBER_LEVEL3"; + case WOLFSSL_P256_KYBER_LEVEL3: + return "P256_KYBER_LEVEL3"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_KYBER_LEVEL3: + return "X25519_KYBER_LEVEL3"; + #endif + #ifdef HAVE_CURVE448 + case WOLFSSL_X448_KYBER_LEVEL3: + return "X448_KYBER_LEVEL3"; + #endif #endif #ifndef WOLFSSL_NO_KYBER1024 case WOLFSSL_KYBER_LEVEL5: @@ -15448,8 +15488,33 @@ const char* wolfSSL_get_curve_name(WOLFSSL* ssl) case WOLFSSL_P521_KYBER_LEVEL5: return "P521_KYBER_LEVEL5"; #endif -#endif -#endif +#elif defined (HAVE_LIBOQS) + case WOLFSSL_KYBER_LEVEL1: + return "KYBER_LEVEL1"; + case WOLFSSL_KYBER_LEVEL3: + return "KYBER_LEVEL3"; + case WOLFSSL_KYBER_LEVEL5: + return "KYBER_LEVEL5"; + case WOLFSSL_P256_KYBER_LEVEL1: + return "P256_KYBER_LEVEL1"; + case WOLFSSL_P384_KYBER_LEVEL3: + return "P384_KYBER_LEVEL3"; + case WOLFSSL_P256_KYBER_LEVEL3: + return "P256_KYBER_LEVEL3"; + case WOLFSSL_P521_KYBER_LEVEL5: + return "P521_KYBER_LEVEL5"; + #ifdef HAVE_CURVE25519 + case WOLFSSL_X25519_KYBER_LEVEL1: + return "X25519_KYBER_LEVEL1"; + case WOLFSSL_X25519_KYBER_LEVEL3: + return "X25519_KYBER_LEVEL3"; + #endif + #ifdef HAVE_CURVE448 + case WOLFSSL_X448_KYBER_LEVEL3: + return "X448_KYBER_LEVEL3"; + #endif +#endif /* WOLFSSL_WC_KYBER */ +#endif /* WOLFSSL_KYBER_ORIGINAL */ } } #endif /* WOLFSSL_TLS13 && WOLFSSL_HAVE_KYBER */ @@ -22934,8 +22999,18 @@ const WOLF_EC_NIST_NAME kNistCurves[] = { WOLFSSL_P256_ML_KEM_512}, {CURVE_NAME("P384_ML_KEM_768"), WOLFSSL_P384_ML_KEM_768, WOLFSSL_P384_ML_KEM_768}, + {CURVE_NAME("P256_ML_KEM_768"), WOLFSSL_P256_ML_KEM_768, + WOLFSSL_P256_ML_KEM_768}, {CURVE_NAME("P521_ML_KEM_1024"), WOLFSSL_P521_ML_KEM_1024, WOLFSSL_P521_ML_KEM_1024}, + {CURVE_NAME("P384_ML_KEM_1024"), WOLFSSL_P384_ML_KEM_1024, + WOLFSSL_P384_ML_KEM_1024}, + {CURVE_NAME("X25519_ML_KEM_512"), WOLFSSL_X25519_ML_KEM_512, + WOLFSSL_X25519_ML_KEM_512}, + {CURVE_NAME("X448_ML_KEM_768"), WOLFSSL_X448_ML_KEM_768, + WOLFSSL_X448_ML_KEM_768}, + {CURVE_NAME("X25519_ML_KEM_768"), WOLFSSL_X25519_ML_KEM_768, + WOLFSSL_X25519_ML_KEM_768}, #endif #endif /* !WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL @@ -22943,9 +23018,20 @@ const WOLF_EC_NIST_NAME kNistCurves[] = { {CURVE_NAME("KYBER_LEVEL3"), WOLFSSL_KYBER_LEVEL3, WOLFSSL_KYBER_LEVEL3}, {CURVE_NAME("KYBER_LEVEL5"), WOLFSSL_KYBER_LEVEL5, WOLFSSL_KYBER_LEVEL5}, #if (defined(WOLFSSL_WC_KYBER) || defined(HAVE_LIBOQS)) && defined(HAVE_ECC) - {CURVE_NAME("P256_KYBER_LEVEL1"), WOLFSSL_P256_KYBER_LEVEL1, WOLFSSL_P256_KYBER_LEVEL1}, - {CURVE_NAME("P384_KYBER_LEVEL3"), WOLFSSL_P384_KYBER_LEVEL3, WOLFSSL_P384_KYBER_LEVEL3}, - {CURVE_NAME("P521_KYBER_LEVEL5"), WOLFSSL_P521_KYBER_LEVEL5, WOLFSSL_P521_KYBER_LEVEL5}, + {CURVE_NAME("P256_KYBER_LEVEL1"), WOLFSSL_P256_KYBER_LEVEL1, + WOLFSSL_P256_KYBER_LEVEL1}, + {CURVE_NAME("P384_KYBER_LEVEL3"), WOLFSSL_P384_KYBER_LEVEL3, + WOLFSSL_P384_KYBER_LEVEL3}, + {CURVE_NAME("P256_KYBER_LEVEL3"), WOLFSSL_P256_KYBER_LEVEL3, + WOLFSSL_P256_KYBER_LEVEL3}, + {CURVE_NAME("P521_KYBER_LEVEL5"), WOLFSSL_P521_KYBER_LEVEL5, + WOLFSSL_P521_KYBER_LEVEL5}, + {CURVE_NAME("X25519_KYBER_LEVEL1"), WOLFSSL_X25519_KYBER_LEVEL1, + WOLFSSL_X25519_KYBER_LEVEL1}, + {CURVE_NAME("X448_KYBER_LEVEL3"), WOLFSSL_X448_KYBER_LEVEL3, + WOLFSSL_X448_KYBER_LEVEL3}, + {CURVE_NAME("X25519_KYBER_LEVEL3"), WOLFSSL_X25519_KYBER_LEVEL3, + WOLFSSL_X25519_KYBER_LEVEL3}, #endif #endif /* WOLFSSL_KYBER_ORIGINAL */ #endif /* WOLFSSL_HAVE_KYBER */ diff --git a/src/tls.c b/src/tls.c index b38d49f531..617f8de7eb 100644 --- a/src/tls.c +++ b/src/tls.c @@ -4920,7 +4920,7 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, const DhParams* params = NULL; for (; serverGroup != NULL; serverGroup = serverGroup->next) { - if (!WOLFSSL_NAMED_GROUP_IS_FFHDE(serverGroup->name)) + if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(serverGroup->name)) continue; for (group = clientGroup; group != NULL; group = group->next) { @@ -4997,7 +4997,7 @@ static int tlsx_ffdhe_find_group(WOLFSSL* ssl, SupportedCurve* clientGroup, word32 p_len; for (; serverGroup != NULL; serverGroup = serverGroup->next) { - if (!WOLFSSL_NAMED_GROUP_IS_FFHDE(serverGroup->name)) + if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(serverGroup->name)) continue; for (group = clientGroup; group != NULL; group = group->next) { @@ -5101,7 +5101,7 @@ int TLSX_SupportedFFDHE_Set(WOLFSSL* ssl) return 0; clientGroup = (SupportedCurve*)extension->data; for (group = clientGroup; group != NULL; group = group->next) { - if (WOLFSSL_NAMED_GROUP_IS_FFHDE(group->name)) { + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(group->name)) { found = 1; break; } @@ -7786,6 +7786,7 @@ static int TLSX_KeyShare_GenX25519Key(WOLFSSL *ssl, KeyShareEntry* kse) if (ret == 0) { /* setting "key" means okay to call wc_curve25519_free */ key = (curve25519_key*)kse->key; + kse->keyLen = CURVE25519_KEYSIZE; #ifdef WOLFSSL_STATIC_EPHEMERAL ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_CURVE25519, kse->key); @@ -7871,6 +7872,7 @@ static int TLSX_KeyShare_GenX448Key(WOLFSSL *ssl, KeyShareEntry* kse) ret = wc_curve448_init((curve448_key*)kse->key); if (ret == 0) { key = (curve448_key*)kse->key; + kse->keyLen = CURVE448_KEY_SIZE; #ifdef WOLFSSL_STATIC_EPHEMERAL ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_CURVE448, kse->key); @@ -8136,62 +8138,6 @@ static int kyber_id2type(int id, int *type) return ret; } -typedef struct PqcHybridMapping { - int hybrid; - int ecc; - int pqc; -} PqcHybridMapping; - -static const PqcHybridMapping pqc_hybrid_mapping[] = { -#ifndef WOLFSSL_NO_ML_KEM - {.hybrid = WOLFSSL_P256_ML_KEM_512, .ecc = WOLFSSL_ECC_SECP256R1, - .pqc = WOLFSSL_ML_KEM_512}, - {.hybrid = WOLFSSL_P384_ML_KEM_768, .ecc = WOLFSSL_ECC_SECP384R1, - .pqc = WOLFSSL_ML_KEM_768}, - {.hybrid = WOLFSSL_P521_ML_KEM_1024, .ecc = WOLFSSL_ECC_SECP521R1, - .pqc = WOLFSSL_ML_KEM_1024}, -#endif -#ifdef WOLFSSL_KYBER_ORIGINAL - {.hybrid = WOLFSSL_P256_KYBER_LEVEL1, .ecc = WOLFSSL_ECC_SECP256R1, - .pqc = WOLFSSL_KYBER_LEVEL1}, - {.hybrid = WOLFSSL_P384_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_SECP384R1, - .pqc = WOLFSSL_KYBER_LEVEL3}, - {.hybrid = WOLFSSL_P521_KYBER_LEVEL5, .ecc = WOLFSSL_ECC_SECP521R1, - .pqc = WOLFSSL_KYBER_LEVEL5}, -#endif - {.hybrid = 0, .ecc = 0, .pqc = 0} -}; - -/* This will map an ecc-pqs hybrid group into its ecc group and pqc kem group. - * If it cannot find a mapping then *pqc is set to group. ecc is optional. */ -static void findEccPqc(int *ecc, int *pqc, int group) -{ - int i; - if (pqc == NULL) { - return; - } - - *pqc = 0; - if (ecc != NULL) { - *ecc = 0; - } - - for (i = 0; pqc_hybrid_mapping[i].hybrid != 0; i++) { - if (pqc_hybrid_mapping[i].hybrid == group) { - *pqc = pqc_hybrid_mapping[i].pqc; - if (ecc != NULL) { - *ecc = pqc_hybrid_mapping[i].ecc; - } - break; - } - } - - if (*pqc == 0) { - /* It is not a hybrid, so maybe its simple. */ - *pqc = group; - } -} - #if defined(WOLFSSL_MLKEM_CACHE_A) && \ !defined(WOLFSSL_TLSX_PQC_MLKEM_STORE_PRIV_KEY) /* Store KyberKey object rather than private key bytes in key share entry. @@ -8205,14 +8151,14 @@ static void findEccPqc(int *ecc, int *pqc, int group) #endif #ifndef WOLFSSL_KYBER_NO_MAKE_KEY -/* Create a key share entry using liboqs parameters group. +/* Create a key share entry using pqc parameters group on the client side. * Generates a key pair. * * ssl The SSL/TLS object. * kse The key share entry object. * returns 0 on success, otherwise failure. */ -static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) +static int TLSX_KeyShare_GenPqcKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) { int ret = 0; int type = 0; @@ -8221,13 +8167,8 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) byte* privKey = NULL; word32 privSz = 0; #else - KyberKey* kem; + KyberKey* kem = NULL; #endif - byte* pubKey = NULL; - KeyShareEntry *ecc_kse = NULL; - int oqs_group = 0; - int ecc_group = 0; - word32 pubSz = 0; /* This gets called twice. Once during parsing of the key share and once * during the population of the extension. No need to do work the second @@ -8236,8 +8177,8 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) return ret; } - findEccPqc(&ecc_group, &oqs_group, kse->group); - ret = kyber_id2type(oqs_group, &type); + /* Get the type of key we need from the key share group. */ + ret = kyber_id2type(kse->group, &type); if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { WOLFSSL_MSG("Invalid Kyber algorithm specified."); ret = BAD_FUNC_ARG; @@ -8255,7 +8196,7 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) ret = wc_KyberKey_PrivateKeySize(kem, &privSz); } if (ret == 0) { - ret = wc_KyberKey_PublicKeySize(kem, &pubSz); + ret = wc_KyberKey_PublicKeySize(kem, &kse->pubKeyLen); } if (ret == 0) { @@ -8267,6 +8208,7 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) } #else if (ret == 0) { + /* Allocate a Kyber key to hold private key. */ kem = (KyberKey*)XMALLOC(sizeof(KyberKey), ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); if (kem == NULL) { @@ -8281,75 +8223,265 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) } } if (ret == 0) { - ret = wc_KyberKey_PublicKeySize(kem, &pubSz); + ret = wc_KyberKey_PublicKeySize(kem, &kse->pubKeyLen); } #endif if (ret == 0) { - ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, - DYNAMIC_TYPE_TLSX); - if (ecc_kse == NULL) { - WOLFSSL_MSG("ecc_kse memory allocation failure"); + kse->pubKey = (byte*)XMALLOC(kse->pubKeyLen, ssl->heap, + DYNAMIC_TYPE_PUBLIC_KEY); + if (kse->pubKey == NULL) { + WOLFSSL_MSG("pubkey memory allocation failure"); ret = MEMORY_ERROR; } } if (ret == 0) { - XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); + ret = wc_KyberKey_MakeKey(kem, ssl->rng); + if (ret != 0) { + WOLFSSL_MSG("Kyber keygen failure"); + } + } + if (ret == 0) { + ret = wc_KyberKey_EncodePublicKey(kem, kse->pubKey, + kse->pubKeyLen); } - if (ret == 0 && ecc_group != 0) { - ecc_kse->group = ecc_group; - ret = TLSX_KeyShare_GenEccKey(ssl, ecc_kse); - /* If fail, no error message, TLSX_KeyShare_GenEccKey will do it. */ +#ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + if (ret == 0) { + ret = wc_KyberKey_EncodePrivateKey(kem, privKey, privSz); + } +#endif + +#ifdef WOLFSSL_DEBUG_TLS + WOLFSSL_MSG("Public Kyber Key"); + WOLFSSL_BUFFER(kse->pubKey, kse->pubKeyLen ); +#endif + + if (ret != 0) { + /* Data owned by key share entry otherwise. */ + wc_KyberKey_Free(kem); + XFREE(kse->pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + kse->pubKey = NULL; + #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + XFREE(privKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + #else + XFREE(kem, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + kse->key = NULL; + #endif + } + else { + #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + wc_KyberKey_Free(kem); + kse->privKey = (byte*)privKey; + kse->privKeyLen = privSz; + #else + kse->key = kem; + #endif + } + + return ret; +} + +/* Structures and objects needed for hybrid key exchanges using both classic + * ECDHE and PQC KEM key material. */ +typedef struct PqcHybridMapping { + int hybrid; + int ecc; + int pqc; + int pqc_first; +} PqcHybridMapping; + +static const PqcHybridMapping pqc_hybrid_mapping[] = { +#ifndef WOLFSSL_NO_ML_KEM + {.hybrid = WOLFSSL_P256_ML_KEM_512, .ecc = WOLFSSL_ECC_SECP256R1, + .pqc = WOLFSSL_ML_KEM_512, .pqc_first = 0}, + {.hybrid = WOLFSSL_P384_ML_KEM_768, .ecc = WOLFSSL_ECC_SECP384R1, + .pqc = WOLFSSL_ML_KEM_768, .pqc_first = 0}, + {.hybrid = WOLFSSL_P256_ML_KEM_768, .ecc = WOLFSSL_ECC_SECP256R1, + .pqc = WOLFSSL_ML_KEM_768, .pqc_first = 0}, + {.hybrid = WOLFSSL_P521_ML_KEM_1024, .ecc = WOLFSSL_ECC_SECP521R1, + .pqc = WOLFSSL_ML_KEM_1024, .pqc_first = 0}, + {.hybrid = WOLFSSL_P384_ML_KEM_1024, .ecc = WOLFSSL_ECC_SECP384R1, + .pqc = WOLFSSL_ML_KEM_1024, .pqc_first = 0}, +#ifdef HAVE_CURVE25519 + {.hybrid = WOLFSSL_X25519_ML_KEM_512, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_ML_KEM_512, .pqc_first = 1}, + {.hybrid = WOLFSSL_X25519_ML_KEM_768, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_ML_KEM_768, .pqc_first = 1}, +#endif +#ifdef HAVE_CURVE448 + {.hybrid = WOLFSSL_X448_ML_KEM_768, .ecc = WOLFSSL_ECC_X448, + .pqc = WOLFSSL_ML_KEM_768, .pqc_first = 1}, +#endif +#endif /* WOLFSSL_NO_ML_KEM */ +#ifdef WOLFSSL_KYBER_ORIGINAL + {.hybrid = WOLFSSL_P256_KYBER_LEVEL1, .ecc = WOLFSSL_ECC_SECP256R1, + .pqc = WOLFSSL_KYBER_LEVEL1, .pqc_first = 0}, + {.hybrid = WOLFSSL_P384_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_SECP384R1, + .pqc = WOLFSSL_KYBER_LEVEL3, .pqc_first = 0}, + {.hybrid = WOLFSSL_P256_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_SECP256R1, + .pqc = WOLFSSL_KYBER_LEVEL3, .pqc_first = 0}, + {.hybrid = WOLFSSL_P521_KYBER_LEVEL5, .ecc = WOLFSSL_ECC_SECP521R1, + .pqc = WOLFSSL_KYBER_LEVEL5, .pqc_first = 0}, +#ifdef HAVE_CURVE25519 + {.hybrid = WOLFSSL_X25519_KYBER_LEVEL1, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_KYBER_LEVEL1, .pqc_first = 0}, + {.hybrid = WOLFSSL_X25519_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_KYBER_LEVEL3, .pqc_first = 0}, +#endif +#ifdef HAVE_CURVE448 + {.hybrid = WOLFSSL_X448_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_X448, + .pqc = WOLFSSL_KYBER_LEVEL3, .pqc_first = 0}, +#endif +#endif /* WOLFSSL_KYBER_ORIGINAL */ + {.hybrid = 0, .ecc = 0, .pqc = 0, .pqc_first = 0} +}; + +/* Map an ecc-pqc hybrid group into its ecc group and pqc kem group. */ +static void findEccPqc(int *ecc, int *pqc, int *pqc_first, int group) +{ + int i; + + if (pqc != NULL) + *pqc = 0; + if (ecc != NULL) + *ecc = 0; + if (pqc_first != NULL) + *pqc_first = 0; + + for (i = 0; pqc_hybrid_mapping[i].hybrid != 0; i++) { + if (pqc_hybrid_mapping[i].hybrid == group) { + if (pqc != NULL) + *pqc = pqc_hybrid_mapping[i].pqc; + if (ecc != NULL) + *ecc = pqc_hybrid_mapping[i].ecc; + if (pqc_first != NULL) + *pqc_first = pqc_hybrid_mapping[i].pqc_first; + break; + } + } +} + +/* Create a key share entry using both ecdhe and pqc parameters groups. + * Generates two key pairs on the client side. + * + * ssl The SSL/TLS object. + * kse The key share entry object. + * returns 0 on success, otherwise failure. + */ +static int TLSX_KeyShare_GenPqcHybridKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) +{ + int ret = 0; + KeyShareEntry *ecc_kse = NULL; + KeyShareEntry *pqc_kse = NULL; + int pqc_group = 0; + int ecc_group = 0; + int pqc_first = 0; + + /* This gets called twice. Once during parsing of the key share and once + * during the population of the extension. No need to do work the second + * time. Just return success if its already been done. */ + if (kse->pubKey != NULL) { + return ret; + } + + /* Determine the ECC and PQC group of the hybrid combination */ + findEccPqc(&ecc_group, &pqc_group, &pqc_first, kse->group); + if (ecc_group == 0 || pqc_group == 0) { + WOLFSSL_MSG("Invalid hybrid group"); + ret = BAD_FUNC_ARG; } if (ret == 0) { - pubKey = (byte*)XMALLOC(ecc_kse->pubKeyLen + pubSz, ssl->heap, - DYNAMIC_TYPE_PUBLIC_KEY); - if (pubKey == NULL) { - WOLFSSL_MSG("pubkey memory allocation failure"); + ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + pqc_kse = (KeyShareEntry*)XMALLOC(sizeof(*pqc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + if (ecc_kse == NULL || pqc_kse == NULL) { + WOLFSSL_MSG("kse memory allocation failure"); ret = MEMORY_ERROR; } } if (ret == 0) { - ret = wc_KyberKey_MakeKey(kem, ssl->rng); - if (ret != 0) { - WOLFSSL_MSG("Kyber keygen failure"); + XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); + XMEMSET(pqc_kse, 0, sizeof(*pqc_kse)); + } + + /* Generate ECC key share part */ + if (ret == 0) { + ecc_kse->group = ecc_group; + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_GenX25519Key(ssl, ecc_kse); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_GenX448Key(ssl, ecc_kse); + } + else + #endif + { + ret = TLSX_KeyShare_GenEccKey(ssl, ecc_kse); } + /* No error message, TLSX_KeyShare_Gen*Key will do it. */ } + + /* Generate PQC key share part */ if (ret == 0) { - ret = wc_KyberKey_EncodePublicKey(kem, pubKey + ecc_kse->pubKeyLen, - pubSz); + pqc_kse->group = pqc_group; + ret = TLSX_KeyShare_GenPqcKeyClient(ssl, pqc_kse); + /* No error message, TLSX_KeyShare_GenPqcKeyClient will do it. */ } -#ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + + /* Allocate memory for combined public key */ if (ret == 0) { - ret = wc_KyberKey_EncodePrivateKey(kem, privKey, privSz); + kse->pubKey = (byte*)XMALLOC(ecc_kse->pubKeyLen + pqc_kse->pubKeyLen, + ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + if (kse->pubKey == NULL) { + WOLFSSL_MSG("pubkey memory allocation failure"); + ret = MEMORY_ERROR; + } } -#endif + + /* Create combined public key. The order of classic/pqc key material is + * indicated by the pqc_first variable. */ if (ret == 0) { - if (ecc_kse->pubKeyLen > 0) - XMEMCPY(pubKey, ecc_kse->pubKey, ecc_kse->pubKeyLen); - kse->pubKey = pubKey; - kse->pubKeyLen = ecc_kse->pubKeyLen + pubSz; - pubKey = NULL; - - /* Note we are saving the OQS private key and ECC private key - * separately. That's because the ECC private key is not simply a - * buffer. Its is an ecc_key struct. Typically do not need the private - * key size, but will need to zero it out upon freeing. */ -#ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ - kse->privKey = privKey; - privKey = NULL; - kse->privKeyLen = privSz; -#else - kse->privKey = (byte*)kem; - kem = NULL; - kse->privKeyLen = sizeof(KyberKey); -#endif + if (pqc_first) { + XMEMCPY(kse->pubKey, pqc_kse->pubKey, pqc_kse->pubKeyLen); + XMEMCPY(kse->pubKey + pqc_kse->pubKeyLen, ecc_kse->pubKey, + ecc_kse->pubKeyLen); + } + else { + XMEMCPY(kse->pubKey, ecc_kse->pubKey, ecc_kse->pubKeyLen); + XMEMCPY(kse->pubKey + ecc_kse->pubKeyLen, pqc_kse->pubKey, + pqc_kse->pubKeyLen); + } + kse->pubKeyLen = ecc_kse->pubKeyLen + pqc_kse->pubKeyLen; + } + /* Store the private keys. + * Note we are saving the PQC private key and ECC private key + * separately. That's because the ECC private key is not simply a + * buffer. Its is an ecc_key struct. */ + if (ret == 0) { + #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + /* PQC private key is an encoded byte array */ + kse->privKey = pqc_kse->privKey; + kse->privKeyLen = pqc_kse->privKeyLen; + pqc_kse->privKey = NULL; + #else + /* PQC private key is a pointer to KyberKey object */ + kse->privKey = (byte*)pqc_kse->key; + kse->privKeyLen = 0; + pqc_kse->key = NULL; + #endif + /* ECC private key is a pointer to ecc_key object */ kse->key = ecc_kse->key; + kse->keyLen = ecc_kse->keyLen; ecc_kse->key = NULL; } @@ -8358,18 +8490,12 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) WOLFSSL_BUFFER(kse->pubKey, kse->pubKeyLen ); #endif - wc_KyberKey_Free(kem); TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); - XFREE(pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); -#ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ - XFREE(privKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); -#else - XFREE(kem, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); -#endif + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); return ret; } -#endif +#endif /* !WOLFSSL_KYBER_NO_MAKE_KEY */ #endif /* WOLFSSL_HAVE_KYBER */ /* Generate a secret/key using the key share entry. @@ -8381,7 +8507,7 @@ int TLSX_KeyShare_GenKey(WOLFSSL *ssl, KeyShareEntry *kse) { int ret; /* Named FFDHE groups have a bit set to identify them. */ - if (WOLFSSL_NAMED_GROUP_IS_FFHDE(kse->group)) + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(kse->group)) ret = TLSX_KeyShare_GenDhKey(ssl, kse); else if (kse->group == WOLFSSL_ECC_X25519) ret = TLSX_KeyShare_GenX25519Key(ssl, kse); @@ -8389,7 +8515,9 @@ int TLSX_KeyShare_GenKey(WOLFSSL *ssl, KeyShareEntry *kse) ret = TLSX_KeyShare_GenX448Key(ssl, kse); #if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_MAKE_KEY) else if (WOLFSSL_NAMED_GROUP_IS_PQC(kse->group)) - ret = TLSX_KeyShare_GenPqcKey(ssl, kse); + ret = TLSX_KeyShare_GenPqcKeyClient(ssl, kse); + else if (WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(kse->group)) + ret = TLSX_KeyShare_GenPqcHybridKeyClient(ssl, kse); #endif else ret = TLSX_KeyShare_GenEccKey(ssl, kse); @@ -8410,7 +8538,7 @@ static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) while ((current = list) != NULL) { list = current->next; - if (WOLFSSL_NAMED_GROUP_IS_FFHDE(current->group)) { + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(current->group)) { #ifndef NO_DH wc_FreeDhKey((DhKey*)current->key); #endif @@ -8427,18 +8555,42 @@ static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) } #ifdef WOLFSSL_HAVE_KYBER else if (WOLFSSL_NAMED_GROUP_IS_PQC(current->group)) { - if (current->key != NULL) { - ForceZero((byte*)current->key, current->keyLen); + #ifdef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + wc_KyberKey_Free((KyberKey*)current->key); + #else + if (current->privKey != NULL) { + ForceZero(current->privKey, current->privKeyLen); } - XFREE(current->pubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY); - current->pubKey = NULL; + #endif + } + else if (WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(current->group)) { + int ecc_group = 0; + findEccPqc(&ecc_group, NULL, NULL, current->group); + + /* Free PQC private key */ + #ifdef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + wc_KyberKey_Free((KyberKey*)current->privKey); + #else if (current->privKey != NULL) { ForceZero(current->privKey, current->privKeyLen); - #ifdef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ - wc_KyberKey_Free((KyberKey*)current->privKey); + } + #endif + + /* Free ECC private key */ + if (ecc_group == WOLFSSL_ECC_X25519) { + #ifdef HAVE_CURVE25519 + wc_curve25519_free((curve25519_key*)current->key); + #endif + } + else if (ecc_group == WOLFSSL_ECC_X448) { + #ifdef HAVE_CURVE448 + wc_curve448_free((curve448_key*)current->key); + #endif + } + else { + #ifdef HAVE_ECC + wc_ecc_free((ecc_key*)current->key); #endif - XFREE(current->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); - current->privKey = NULL; } } #endif @@ -8448,7 +8600,7 @@ static void TLSX_KeyShare_FreeAll(KeyShareEntry* list, void* heap) #endif } XFREE(current->key, heap, DYNAMIC_TYPE_PRIVATE_KEY); - #if !defined(NO_DH) && (!defined(NO_CERTS) || !defined(NO_PSK)) + #if !defined(NO_DH) || defined(WOLFSSL_HAVE_KYBER) XFREE(current->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); #endif XFREE(current->pubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY); @@ -8674,10 +8826,15 @@ static int TLSX_KeyShare_ProcessDh(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) * * ssl The SSL/TLS object. * keyShareEntry The key share entry object to use to calculate shared secret. + * ssOutput The destination buffer for the shared secret. + * ssOutSz The size of the generated shared secret. + * * returns 0 on success and other values indicate failure. */ -static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, - KeyShareEntry* keyShareEntry) +static int TLSX_KeyShare_ProcessX25519_ex(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry, + unsigned char* ssOutput, + word32* ssOutSz) { int ret; @@ -8732,9 +8889,7 @@ static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, if (ret == 0) { #endif ret = wc_curve25519_shared_secret_ex(key, peerX25519Key, - ssl->arrays->preMasterSecret, - &ssl->arrays->preMasterSz, - EC25519_LITTLE_ENDIAN); + ssOutput, ssOutSz, EC25519_LITTLE_ENDIAN); } wc_curve25519_free(peerX25519Key); @@ -8742,9 +8897,13 @@ static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, wc_curve25519_free((curve25519_key*)keyShareEntry->key); XFREE(keyShareEntry->key, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); keyShareEntry->key = NULL; + XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + keyShareEntry->ke = NULL; #else (void)ssl; (void)keyShareEntry; + (void)ssOutput; + (void)ssOutSz; ret = PEER_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); @@ -8753,13 +8912,33 @@ static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, return ret; } +/* Process the X25519 key share extension on the client side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to use to calculate shared secret. + * + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry) +{ + return TLSX_KeyShare_ProcessX25519_ex(ssl, keyShareEntry, + ssl->arrays->preMasterSecret, &ssl->arrays->preMasterSz); +} + /* Process the X448 key share extension on the client side. * * ssl The SSL/TLS object. * keyShareEntry The key share entry object to use to calculate shared secret. + * ssOutput The destination buffer for the shared secret. + * ssOutSz The size of the generated shared secret. + * * returns 0 on success and other values indicate failure. */ -static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) +static int TLSX_KeyShare_ProcessX448_ex(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry, + unsigned char* ssOutput, + word32* ssOutSz) { int ret; @@ -8810,9 +8989,7 @@ static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) ssl->ecdhCurveOID = ECC_X448_OID; ret = wc_curve448_shared_secret_ex(key, peerX448Key, - ssl->arrays->preMasterSecret, - &ssl->arrays->preMasterSz, - EC448_LITTLE_ENDIAN); + ssOutput, ssOutSz, EC448_LITTLE_ENDIAN); } wc_curve448_free(peerX448Key); @@ -8820,9 +8997,13 @@ static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) wc_curve448_free((curve448_key*)keyShareEntry->key); XFREE(keyShareEntry->key, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); keyShareEntry->key = NULL; + XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + keyShareEntry->ke = NULL; #else (void)ssl; (void)keyShareEntry; + (void)ssOutput; + (void)ssOutSz; ret = PEER_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); @@ -8831,13 +9012,31 @@ static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) return ret; } +/* Process the X448 key share extension on the client side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to use to calculate shared secret. + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) +{ + return TLSX_KeyShare_ProcessX448_ex(ssl, keyShareEntry, + ssl->arrays->preMasterSecret, &ssl->arrays->preMasterSz); +} + /* Process the ECC key share extension on the client side. * * ssl The SSL/TLS object. * keyShareEntry The key share entry object to use to calculate shared secret. + * ssOutput The destination buffer for the shared secret. + * ssOutSz The size of the generated shared secret. + * * returns 0 on success and other values indicate failure. */ -static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) +static int TLSX_KeyShare_ProcessEcc_ex(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry, + unsigned char* ssOutput, + word32* ssOutSz) { int ret = 0; #ifdef HAVE_ECC @@ -8937,9 +9136,7 @@ static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) if (ret == 0) { ret = EccSharedSecret(ssl, eccKey, ssl->peerEccKey, keyShareEntry->ke, &keyShareEntry->keLen, - ssl->arrays->preMasterSecret, &ssl->arrays->preMasterSz, - ssl->options.side - ); + ssOutput, ssOutSz, ssl->options.side); #ifdef WOLFSSL_ASYNC_CRYPT if (ret == WC_NO_ERR_TRACE(WC_PENDING_E)) return ret; @@ -8967,6 +9164,8 @@ static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) #else (void)ssl; (void)keyShareEntry; + (void)ssOutput; + (void)ssOutSz; ret = PEER_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); @@ -8975,185 +9174,359 @@ static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) return ret; } +/* Process the ECC key share extension on the client side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to use to calculate shared secret. + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_ProcessEcc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) +{ + return TLSX_KeyShare_ProcessEcc_ex(ssl, keyShareEntry, + ssl->arrays->preMasterSecret, &ssl->arrays->preMasterSz); +} + #if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_DECAPSULATE) /* Process the Kyber key share extension on the client side. * * ssl The SSL/TLS object. * keyShareEntry The key share entry object to use to calculate shared secret. + * ssOutput The destination buffer for the shared secret. + * ssOutSz The size of the generated shared secret. + * * returns 0 on success and other values indicate failure. */ -static int TLSX_KeyShare_ProcessPqc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) +static int TLSX_KeyShare_ProcessPqcClient_ex(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry, + unsigned char* ssOutput, + word32* ssOutSz) { - int ret = 0; - int type; + int ret = 0; + KyberKey* kem = (KyberKey*)keyShareEntry->key; #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ - KyberKey kem[1]; - word32 privSz = 0; -#else - KyberKey* kem; + word32 privSz = 0; #endif - byte* sharedSecret = NULL; - word32 sharedSecretLen = 0; - int oqs_group = 0; - int ecc_group = 0; - ecc_key eccpubkey; - word32 outlen = 0; - word32 ctSz = 0; - word32 ssSz = 0; - - if (keyShareEntry->ke == NULL) { - WOLFSSL_MSG("Invalid OQS algorithm specified."); - return BAD_FUNC_ARG; - } + word32 ctSz = 0; + word32 ssSz = 0; if (ssl->options.side == WOLFSSL_SERVER_END) { /* I am the server, the shared secret has already been generated and - * is in keyShareEntry->ke; copy it to the pre-master secret - * pre-allocated buffer. */ - if (keyShareEntry->keLen > ENCRYPT_LEN) { - WOLFSSL_MSG("shared secret is too long."); - return LENGTH_ERROR; - } - - XMEMCPY(ssl->arrays->preMasterSecret, keyShareEntry->ke, - keyShareEntry->keLen); - ssl->arrays->preMasterSz = keyShareEntry->keLen; - XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_SECRET); - keyShareEntry->ke = NULL; - keyShareEntry->keLen = 0; + * is in ssl->arrays->preMasterSecret, so nothing really to do here. */ return 0; } - /* I am the client, the ciphertext is in keyShareEntry->ke */ - findEccPqc(&ecc_group, &oqs_group, keyShareEntry->group); - - ret = wc_ecc_init_ex(&eccpubkey, ssl->heap, ssl->devId); - if (ret != 0) { - WOLFSSL_MSG("Memory allocation error."); - return MEMORY_E; - } - - ret = kyber_id2type(oqs_group, &type); - if (ret != 0) { - wc_ecc_free(&eccpubkey); - WOLFSSL_MSG("Invalid OQS algorithm specified."); + if (keyShareEntry->ke == NULL) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); return BAD_FUNC_ARG; } + if (ssOutSz == NULL) + return BAD_FUNC_ARG; #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ - ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); - if (ret != 0) { - wc_ecc_free(&eccpubkey); - WOLFSSL_MSG("Error creating Kyber KEM"); - return MEMORY_E; + if (kem == NULL) { + int type = 0; + + /* Allocate a Kyber key to hold private key. */ + kem = (KyberKey*) XMALLOC(sizeof(KyberKey), ssl->heap, + DYNAMIC_TYPE_PRIVATE_KEY); + if (kem == NULL) { + WOLFSSL_MSG("GenPqcKey memory error"); + ret = MEMORY_E; + } + if (ret == 0) { + ret = kyber_id2type(keyShareEntry->group, &type); + } + if (ret != 0) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); + if (ret != 0) { + WOLFSSL_MSG("Error creating Kyber KEM"); + } + } } #else - kem = (KyberKey*)keyShareEntry->privKey; - keyShareEntry->privKey = NULL; + if (kem == NULL || keyShareEntry->privKeyLen != 0) { + WOLFSSL_MSG("Invalid Kyber key."); + ret = BAD_FUNC_ARG; + } #endif if (ret == 0) { ret = wc_KyberKey_SharedSecretSize(kem, &ssSz); } - if (ret == 0) { - sharedSecretLen = ssSz; - switch (ecc_group) { - case WOLFSSL_ECC_SECP256R1: - sharedSecretLen += 32; - outlen = 32; - break; - case WOLFSSL_ECC_SECP384R1: - sharedSecretLen += 48; - outlen = 48; - break; - case WOLFSSL_ECC_SECP521R1: - sharedSecretLen += 66; - outlen = 66; - break; - default: - break; - } - } - if (ret == 0) { - sharedSecret = (byte*)XMALLOC(sharedSecretLen, ssl->heap, - DYNAMIC_TYPE_TLSX); - if (sharedSecret == NULL) { - WOLFSSL_MSG("Memory allocation error."); - ret = MEMORY_E; - } - } if (ret == 0) { ret = wc_KyberKey_CipherTextSize(kem, &ctSz); } + #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ if (ret == 0) { ret = wc_KyberKey_PrivateKeySize(kem, &privSz); } + if (ret == 0 && privSz != keyShareEntry->privKeyLen) { + WOLFSSL_MSG("Invalid private key size."); + ret = BAD_FUNC_ARG; + } if (ret == 0) { ret = wc_KyberKey_DecodePrivateKey(kem, keyShareEntry->privKey, privSz); } #endif + if (ret == 0) { - ret = wc_KyberKey_Decapsulate(kem, sharedSecret + outlen, - keyShareEntry->ke + keyShareEntry->keLen - ctSz, ctSz); + ret = wc_KyberKey_Decapsulate(kem, ssOutput, + keyShareEntry->ke, ctSz); if (ret != 0) { WOLFSSL_MSG("wc_KyberKey decapsulation failure."); ret = BAD_FUNC_ARG; } } + if (ret == 0) { + *ssOutSz = ssSz; + } - if (ecc_group != 0) { - if (ret == 0) { - /* Point is validated by import function. */ - ret = wc_ecc_import_x963(keyShareEntry->ke, - keyShareEntry->keLen - ctSz, - &eccpubkey); - if (ret != 0) { - WOLFSSL_MSG("ECC Public key import error."); - } + wc_KyberKey_Free(kem); + + XFREE(kem, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + keyShareEntry->key = NULL; + + XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + keyShareEntry->ke = NULL; + + return ret; +} + +/* Process the Kyber key share extension on the client side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to use to calculate shared secret. + * + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_ProcessPqcClient(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry) +{ + return TLSX_KeyShare_ProcessPqcClient_ex(ssl, keyShareEntry, + ssl->arrays->preMasterSecret, + &ssl->arrays->preMasterSz); +} + +/* Process the hybrid key share extension on the client side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to use to calculate shared secret. + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry) +{ + int ret = 0; + int pqc_group = 0; + int ecc_group = 0; + int pqc_first = 0; + KeyShareEntry* pqc_kse = NULL; + KeyShareEntry *ecc_kse = NULL; + word32 ctSz = 0; + word32 ssSzPqc = 0; + word32 ssSzEcc = 0; + + if (ssl->options.side == WOLFSSL_SERVER_END) { + /* I am the server, the shared secret has already been generated and + * is in ssl->arrays->preMasterSecret, so nothing really to do here. */ + return 0; + } + + if (keyShareEntry->ke == NULL) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + return BAD_FUNC_ARG; + } + + /* I am the client, both the PQC ciphertext and the ECHD public key are in + * keyShareEntry->ke */ + + /* Determine the ECC and PQC group of the hybrid combination */ + findEccPqc(&ecc_group, &pqc_group, &pqc_first, keyShareEntry->group); + if (ecc_group == 0 || pqc_group == 0) { + WOLFSSL_MSG("Invalid hybrid group"); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + pqc_kse = (KeyShareEntry*)XMALLOC(sizeof(*pqc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + if (ecc_kse == NULL || pqc_kse == NULL) { + WOLFSSL_MSG("kse memory allocation failure"); + ret = MEMORY_ERROR; } + } -#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ - !defined(HAVE_SELFTEST) + /* The ciphertext and shared secret sizes of a KEM are fixed. Hence, we + * decode these sizes to separate the KEM ciphertext from the ECDH public + * key. */ + if (ret == 0) { + #ifndef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + int type; + KyberKey* kem; + #endif + + XMEMSET(pqc_kse, 0, sizeof(*pqc_kse)); + pqc_kse->group = pqc_group; + pqc_kse->privKeyLen = keyShareEntry->privKeyLen; + #ifdef WOLFSSL_TLSX_PQC_MLKEM_STORE_OBJ + pqc_kse->key = keyShareEntry->privKey; + #else + pqc_kse->privKey = keyShareEntry->privKey; + + /* Allocate a Kyber key to hold private key. */ + kem = (KyberKey*) XMALLOC(sizeof(KyberKey), ssl->heap, + DYNAMIC_TYPE_PRIVATE_KEY); + if (kem == NULL) { + WOLFSSL_MSG("GenPqcKey memory error"); + ret = MEMORY_E; + } + if (ret == 0) { + ret = kyber_id2type(pqc_group, &type); + } + if (ret != 0) { + WOLFSSL_MSG("Invalid Kyber algorithm specified."); + ret = BAD_FUNC_ARG; + } if (ret == 0) { - ret = wc_ecc_set_rng((ecc_key *)keyShareEntry->key, ssl->rng); + ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); if (ret != 0) { - WOLFSSL_MSG("Failure to set the ECC private key RNG."); + WOLFSSL_MSG("Error creating Kyber KEM"); } } -#endif + if (ret == 0) { + pqc_kse->key = kem; + } + #endif if (ret == 0) { - PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_shared_secret((ecc_key *)keyShareEntry->key, - &eccpubkey, sharedSecret, &outlen); - PRIVATE_KEY_LOCK(); - if (outlen != sharedSecretLen - ssSz) { - WOLFSSL_MSG("ECC shared secret derivation error."); + ret = wc_KyberKey_SharedSecretSize((KyberKey*)pqc_kse->key, + &ssSzPqc); + } + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize((KyberKey*)pqc_kse->key, + &ctSz); + if (ret == 0 && keyShareEntry->keLen <= ctSz) { + WOLFSSL_MSG("Invalid ciphertext size."); ret = BAD_FUNC_ARG; } } + if (ret == 0) { + pqc_kse->keLen = ctSz; + pqc_kse->ke = (byte*)XMALLOC(pqc_kse->keLen, ssl->heap, + DYNAMIC_TYPE_PUBLIC_KEY); + if (pqc_kse->ke == NULL) { + WOLFSSL_MSG("pqc_kse memory allocation failure"); + ret = MEMORY_ERROR; + } + /* Copy the PQC KEM ciphertext. Depending on the pqc_first flag, + * the KEM ciphertext comes before or after the ECDH public key. */ + if (ret == 0) { + int offset = keyShareEntry->keLen - ctSz; + + if (pqc_first) + offset = 0; + + XMEMCPY(pqc_kse->ke, keyShareEntry->ke + offset, ctSz); + } + } } - if ((ret == 0) && (sharedSecretLen > ENCRYPT_LEN)) { - WOLFSSL_MSG("shared secret is too long."); - ret = LENGTH_ERROR; + + if (ret == 0) { + XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); + ecc_kse->group = ecc_group; + ecc_kse->keLen = keyShareEntry->keLen - ctSz; + ecc_kse->key = keyShareEntry->key; + ecc_kse->ke = (byte*)XMALLOC(ecc_kse->keLen, ssl->heap, + DYNAMIC_TYPE_PUBLIC_KEY); + if (ecc_kse->ke == NULL) { + WOLFSSL_MSG("ecc_kse memory allocation failure"); + ret = MEMORY_ERROR; + } + /* Copy the ECDH public key. Depending on the pqc_first flag, the + * KEM ciphertext comes before or after the ECDH public key. */ + if (ret == 0) { + int offset = 0; + + if (pqc_first) + offset = ctSz; + + XMEMCPY(ecc_kse->ke, keyShareEntry->ke + offset, ecc_kse->keLen); + } } + /* Process ECDH key share part. The generated shared secret is directly + * stored in the ssl->arrays->preMasterSecret buffer. Depending on the + * pqc_first flag, the ECDH shared secret part goes before or after the + * KEM part. */ if (ret == 0) { - /* Copy the shared secret to the pre-master secret pre-allocated - * buffer. */ - XMEMCPY(ssl->arrays->preMasterSecret, sharedSecret, sharedSecretLen); - ssl->arrays->preMasterSz = (word32) sharedSecretLen; + int offset = 0; + + /* Set the ECC size variable to the initial buffer size */ + ssSzEcc = ssl->arrays->preMasterSz; + + if (pqc_first) + offset = ssSzPqc; + + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + offset, &ssSzEcc); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + offset, &ssSzEcc); + } + else + #endif + { + ret = TLSX_KeyShare_ProcessEcc_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + offset, &ssSzEcc); + } } - XFREE(sharedSecret, ssl->heap, DYNAMIC_TYPE_SECRET); + if (ret == 0) { + keyShareEntry->key = ecc_kse->key; + + if ((ret == 0) && ((ssSzEcc + ssSzPqc) > ENCRYPT_LEN)) { + WOLFSSL_MSG("shared secret is too long."); + ret = LENGTH_ERROR; + } + } + + /* Process PQC KEM key share part. Depending on the pqc_first flag, the + * KEM shared secret part goes before or after the ECDH part. */ + if (ret == 0) { + int offset = ssSzEcc; + + if (pqc_first) + offset = 0; + + ret = TLSX_KeyShare_ProcessPqcClient_ex(ssl, pqc_kse, + ssl->arrays->preMasterSecret + offset, &ssSzPqc); + } + + if (ret == 0) { + keyShareEntry->privKey = (byte*)pqc_kse->key; + + ssl->arrays->preMasterSz = ssSzEcc + ssSzPqc; + } + + TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); - wc_ecc_free(&eccpubkey); - wc_KyberKey_Free(kem); return ret; } -#endif /* WOLFSSL_HAVE_KYBER */ +#endif /* WOLFSSL_HAVE_KYBER && !WOLFSSL_KYBER_NO_DECAPSULATE */ /* Process the key share extension on the client side. * @@ -9173,7 +9546,7 @@ static int TLSX_KeyShare_Process(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) ssl->arrays->preMasterSz = ENCRYPT_LEN; /* Use Key Share Data from server. */ - if (WOLFSSL_NAMED_GROUP_IS_FFHDE(keyShareEntry->group)) + if (WOLFSSL_NAMED_GROUP_IS_FFDHE(keyShareEntry->group)) ret = TLSX_KeyShare_ProcessDh(ssl, keyShareEntry); else if (keyShareEntry->group == WOLFSSL_ECC_X25519) ret = TLSX_KeyShare_ProcessX25519(ssl, keyShareEntry); @@ -9181,7 +9554,9 @@ static int TLSX_KeyShare_Process(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) ret = TLSX_KeyShare_ProcessX448(ssl, keyShareEntry); #if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_DECAPSULATE) else if (WOLFSSL_NAMED_GROUP_IS_PQC(keyShareEntry->group)) - ret = TLSX_KeyShare_ProcessPqc(ssl, keyShareEntry); + ret = TLSX_KeyShare_ProcessPqcClient(ssl, keyShareEntry); + else if (WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(keyShareEntry->group)) + ret = TLSX_KeyShare_ProcessPqcHybridClient(ssl, keyShareEntry); #endif else ret = TLSX_KeyShare_ProcessEcc(ssl, keyShareEntry); @@ -9231,10 +9606,17 @@ static int TLSX_KeyShareEntry_Parse(const WOLFSSL* ssl, const byte* input, return BUFFER_ERROR; #ifdef WOLFSSL_HAVE_KYBER - if (WOLFSSL_NAMED_GROUP_IS_PQC(group) && + if ((WOLFSSL_NAMED_GROUP_IS_PQC(group) || + WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) && ssl->options.side == WOLFSSL_SERVER_END) { - /* For KEMs, the public key is not stored. Casting away const because - * we know for KEMs, it will be read-only.*/ + /* When handling a key share containing a KEM public key on the server + * end, we have to perform the encapsulation immediately in order to + * send the resulting ciphertext back to the client in the ServerHello + * message. As the public key is not stored and we do not modify it, we + * don't have to create a copy of it. + * In case of a hybrid key exchange, the ECDH part is also performed + * immediately (to not split the generation of the master secret). + * Hence, we also don't have to store this public key either. */ ke = (byte *)&input[offset]; } else #endif @@ -9464,115 +9846,267 @@ int TLSX_KeyShare_Parse(WOLFSSL* ssl, const byte* input, word16 length, return SANITY_MSG_E; } - return ret; -} - -/* Create a new key share entry and put it into the list. - * - * list The linked list of key share entries. - * group The named group. - * heap The memory to allocate with. - * keyShareEntry The new key share entry object. - * returns 0 on success and other values indicate failure. - */ -static int TLSX_KeyShare_New(KeyShareEntry** list, int group, void *heap, - KeyShareEntry** keyShareEntry) -{ - KeyShareEntry* kse; - KeyShareEntry** next; + return ret; +} + +/* Create a new key share entry and put it into the list. + * + * list The linked list of key share entries. + * group The named group. + * heap The memory to allocate with. + * keyShareEntry The new key share entry object. + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_New(KeyShareEntry** list, int group, void *heap, + KeyShareEntry** keyShareEntry) +{ + KeyShareEntry* kse; + KeyShareEntry** next; + + kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), heap, + DYNAMIC_TYPE_TLSX); + if (kse == NULL) + return MEMORY_E; + + XMEMSET(kse, 0, sizeof(*kse)); + kse->group = (word16)group; + + /* Add it to the back and maintain the links. */ + while (*list != NULL) { + /* Assign to temporary to work around compiler bug found by customer. */ + next = &((*list)->next); + list = next; + } + *list = kse; + *keyShareEntry = kse; + + (void)heap; + + return 0; +} + +#if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_ENCAPSULATE) +/* Process the Kyber key share extension on the server side. + * + * ssl The SSL/TLS object. + * keyShareEntry The key share entry object to be sent to the client. + * data The key share data received from the client. + * len The length of the key share data from the client. + * ssOutput The destination buffer for the shared secret. + * ssOutSz The size of the generated shared secret. + * + * returns 0 on success and other values indicate failure. + */ +static int TLSX_KeyShare_HandlePqcKeyServer(WOLFSSL* ssl, + KeyShareEntry* keyShareEntry, byte* clientData, word16 clientLen, + unsigned char* ssOutput, word32* ssOutSz) +{ + /* We are on the server side. The key share contains a PQC KEM public key + * that we are using for an encapsulate operation. The resulting ciphertext + * is stored in the server key share. */ + KyberKey* kemKey = (KyberKey*)keyShareEntry->key; + byte* ciphertext = NULL; + int ret = 0; + word32 pubSz = 0; + word32 ctSz = 0; + word32 ssSz = 0; + + if (clientData == NULL) { + WOLFSSL_MSG("No KEM public key from the client."); + return BAD_FUNC_ARG; + } + + if (kemKey == NULL) { + int type = 0; + + /* Allocate a Kyber key to hold private key. */ + kemKey = (KyberKey*) XMALLOC(sizeof(KyberKey), ssl->heap, + DYNAMIC_TYPE_PRIVATE_KEY); + if (kemKey == NULL) { + WOLFSSL_MSG("GenPqcKey memory error"); + ret = MEMORY_E; + } + if (ret == 0) { + ret = kyber_id2type(keyShareEntry->group, &type); + } + if (ret != 0) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + ret = wc_KyberKey_Init(type, kemKey, ssl->heap, ssl->devId); + if (ret != 0) { + WOLFSSL_MSG("Error creating Kyber KEM"); + } + } + } + + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(kemKey, &pubSz); + } + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize(kemKey, &ctSz); + } + if (ret == 0) { + ret = wc_KyberKey_SharedSecretSize(kemKey, &ssSz); + } + + if (ret == 0 && clientLen != pubSz) { + WOLFSSL_MSG("Invalid public key."); + ret = BAD_FUNC_ARG; + } + + if (ret == 0) { + ciphertext = (byte*)XMALLOC(ctSz, ssl->heap, DYNAMIC_TYPE_TLSX); + + if (ciphertext == NULL) { + WOLFSSL_MSG("Ciphertext memory allocation failure."); + ret = MEMORY_E; + } + } + + if (ret == 0) { + ret = wc_KyberKey_DecodePublicKey(kemKey, clientData, pubSz); + } + if (ret == 0) { + ret = wc_KyberKey_Encapsulate(kemKey, ciphertext, + ssOutput, ssl->rng); + if (ret != 0) { + WOLFSSL_MSG("wc_KyberKey encapsulation failure."); + } + } + + if (ret == 0) { + XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); - kse = (KeyShareEntry*)XMALLOC(sizeof(KeyShareEntry), heap, - DYNAMIC_TYPE_TLSX); - if (kse == NULL) - return MEMORY_E; + *ssOutSz = ssSz; + keyShareEntry->ke = NULL; + keyShareEntry->keLen = 0; - XMEMSET(kse, 0, sizeof(*kse)); - kse->group = (word16)group; + keyShareEntry->pubKey = ciphertext; + keyShareEntry->pubKeyLen = ctSz; + ciphertext = NULL; - /* Add it to the back and maintain the links. */ - while (*list != NULL) { - /* Assign to temporary to work around compiler bug found by customer. */ - next = &((*list)->next); - list = next; + /* Set namedGroup so wolfSSL_get_curve_name() can function properly on + * the server side. */ + ssl->namedGroup = keyShareEntry->group; } - *list = kse; - *keyShareEntry = kse; - (void)heap; + XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); - return 0; + wc_KyberKey_Free(kemKey); + XFREE(kemKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + keyShareEntry->key = NULL; + return ret; } -#if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_ENCAPSULATE) -static int server_generate_pqc_ciphertext(WOLFSSL* ssl, +static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, KeyShareEntry* keyShareEntry, byte* data, word16 len) { - /* I am the server. The data parameter is the client's public key. I need - * to generate the public information (AKA ciphertext) and shared secret - * here. Note the "public information" is equivalent to a the public key in - * key exchange parlance. That's why it is being assigned to pubKey. + /* I am the server. The data parameter is the concatenation of the client's + * ECDH public key and the KEM public key. I need to generate a matching + * public key for ECDH and encapsulate a shared secret using the KEM public + * key. We send the ECDH public key and the KEM ciphertext back to the + * client. Additionally, we create the ECDH shared secret here already. */ - int type; - KyberKey kem[1]; - byte* sharedSecret = NULL; - byte* ciphertext = NULL; - int ret = 0; - int oqs_group = 0; - int ecc_group = 0; + int type; + byte* ciphertext = NULL; + int ret = 0; + int pqc_group = 0; + int ecc_group = 0; + int pqc_first = 0; KeyShareEntry *ecc_kse = NULL; - ecc_key eccpubkey; - word32 outlen = 0; + KeyShareEntry *pqc_kse = NULL; word32 pubSz = 0; word32 ctSz = 0; - word32 ssSz = 0; + word32 ssSzPqc = 0; + word32 ssSzEcc = 0; - findEccPqc(&ecc_group, &oqs_group, keyShareEntry->group); - ret = kyber_id2type(oqs_group, &type); - if (ret != 0) { - WOLFSSL_MSG("Invalid Kyber algorithm specified."); + if (data == NULL) { + WOLFSSL_MSG("No hybrid key share data from the client."); return BAD_FUNC_ARG; } - ret = wc_ecc_init_ex(&eccpubkey, ssl->heap, ssl->devId); - if (ret != 0) { - WOLFSSL_MSG("Could not do ECC public key initialization."); - return MEMORY_E; - } - - ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); - if (ret != 0) { - wc_ecc_free(&eccpubkey); - WOLFSSL_MSG("Error creating Kyber KEM"); - return MEMORY_E; + /* Determine the ECC and PQC group of the hybrid combination */ + findEccPqc(&ecc_group, &pqc_group, &pqc_first, keyShareEntry->group); + if (ecc_group == 0 || pqc_group == 0) { + WOLFSSL_MSG("Invalid hybrid group"); + ret = BAD_FUNC_ARG; } if (ret == 0) { ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, - DYNAMIC_TYPE_TLSX); - if (ecc_kse == NULL) { - WOLFSSL_MSG("ecc_kse memory allocation failure"); + DYNAMIC_TYPE_TLSX); + pqc_kse = (KeyShareEntry*)XMALLOC(sizeof(*pqc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + if (ecc_kse == NULL || pqc_kse == NULL) { + WOLFSSL_MSG("kse memory allocation failure"); ret = MEMORY_ERROR; } } + /* The ciphertext and shared secret sizes of a KEM are fixed. Hence, we + * decode these sizes to properly concatenate the KEM ciphertext with the + * ECDH public key. */ if (ret == 0) { - XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); + XMEMSET(pqc_kse, 0, sizeof(*pqc_kse)); + pqc_kse->group = pqc_group; + + /* Allocate a Kyber key to hold private key. */ + pqc_kse->key = (KyberKey*) XMALLOC(sizeof(KyberKey), ssl->heap, + DYNAMIC_TYPE_PRIVATE_KEY); + if (pqc_kse->key == NULL) { + WOLFSSL_MSG("GenPqcKey memory error"); + ret = MEMORY_E; + } + if (ret == 0) { + ret = kyber_id2type(pqc_kse->group, &type); + } + if (ret != 0) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + ret = BAD_FUNC_ARG; + } + if (ret == 0) { + ret = wc_KyberKey_Init(type, (KyberKey*)pqc_kse->key, + ssl->heap, ssl->devId); + if (ret != 0) { + WOLFSSL_MSG("Error creating Kyber KEM"); + } + } + if (ret == 0) { + ret = wc_KyberKey_SharedSecretSize((KyberKey*)pqc_kse->key, + &ssSzPqc); + } + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize((KyberKey*)pqc_kse->key, + &ctSz); + } + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize((KyberKey*)pqc_kse->key, + &pubSz); + } } + /* Generate the ECDH key share part to be sent to the client */ if (ret == 0 && ecc_group != 0) { + XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); ecc_kse->group = ecc_group; - ret = TLSX_KeyShare_GenEccKey(ssl, ecc_kse); - /* No message, TLSX_KeyShare_GenEccKey() will do it. */ - } - - if (ret == 0) { - ret = wc_KyberKey_PublicKeySize(kem, &pubSz); - } - if (ret == 0) { - ret = wc_KyberKey_CipherTextSize(kem, &ctSz); - } - if (ret == 0) { - ret = wc_KyberKey_SharedSecretSize(kem, &ssSz); + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_GenX25519Key(ssl, ecc_kse); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_GenX448Key(ssl, ecc_kse); + } + else + #endif + { + ret = TLSX_KeyShare_GenEccKey(ssl, ecc_kse); + } + /* No error message, TLSX_KeyShare_GenKey will do it. */ } if (ret == 0 && len != pubSz + ecc_kse->pubKeyLen) { @@ -9580,72 +10114,113 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, ret = BAD_FUNC_ARG; } + /* Allocate buffer for the concatenated client key share data + * (PQC KEM ciphertext + ECDH public key) */ if (ret == 0) { - sharedSecret = (byte*)XMALLOC(ecc_kse->keyLen + ssSz, ssl->heap, - DYNAMIC_TYPE_SECRET); ciphertext = (byte*)XMALLOC(ecc_kse->pubKeyLen + ctSz, ssl->heap, DYNAMIC_TYPE_TLSX); - if (sharedSecret == NULL || ciphertext == NULL) { - WOLFSSL_MSG("Ciphertext/shared secret memory allocation failure."); + if (ciphertext == NULL) { + WOLFSSL_MSG("Ciphertext memory allocation failure."); ret = MEMORY_E; } } - if (ecc_group != 0) { + /* Process ECDH key share part. The generated shared secret is directly + * stored in the ssl->arrays->preMasterSecret buffer. Depending on the + * pqc_first flag, the ECDH shared secret part goes before or after the + * KEM part. */ + if (ret == 0) { + ecc_kse->keLen = len - pubSz; + ecc_kse->ke = (byte*)XMALLOC(ecc_kse->keLen, ssl->heap, + DYNAMIC_TYPE_PUBLIC_KEY); + if (ecc_kse->ke == NULL) { + WOLFSSL_MSG("ecc_kse memory allocation failure"); + ret = MEMORY_ERROR; + } if (ret == 0) { - /* Point is validated by import function. */ - ret = wc_ecc_import_x963(data, len - pubSz, &eccpubkey); - if (ret != 0) { - WOLFSSL_MSG("Bad ECC public key."); + int pubOffset = 0; + int ssOffset = 0; + + /* Set the ECC size variable to the initial buffer size */ + ssSzEcc = ssl->arrays->preMasterSz; + + if (pqc_first) { + pubOffset = pubSz; + ssOffset = ssSzPqc; } - } -#if defined(ECC_TIMING_RESISTANT) && (!defined(HAVE_FIPS) || \ - (!defined(HAVE_FIPS_VERSION) || (HAVE_FIPS_VERSION != 2))) && \ - !defined(HAVE_SELFTEST) - if (ret == 0) { - ret = wc_ecc_set_rng((ecc_key *)ecc_kse->key, ssl->rng); - } -#endif + XMEMCPY(ecc_kse->ke, data + pubOffset, ecc_kse->keLen); + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssOffset, &ssSzEcc); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssOffset, &ssSzEcc); + } + else + #endif + { + ret = TLSX_KeyShare_ProcessEcc_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssOffset, &ssSzEcc); + } + } if (ret == 0) { - outlen = ecc_kse->keyLen; - PRIVATE_KEY_UNLOCK(); - ret = wc_ecc_shared_secret((ecc_key *)ecc_kse->key, &eccpubkey, - sharedSecret, - &outlen); - PRIVATE_KEY_LOCK(); - if (outlen != ecc_kse->keyLen) { + if (ssSzEcc != ecc_kse->keyLen) { WOLFSSL_MSG("Data length mismatch."); ret = BAD_FUNC_ARG; } } } - if (ret == 0) { - ret = wc_KyberKey_DecodePublicKey(kem, data + ecc_kse->pubKeyLen, - pubSz); + if (ret == 0 && ssSzEcc + ssSzPqc > ENCRYPT_LEN) { + WOLFSSL_MSG("shared secret is too long."); + ret = LENGTH_ERROR; } + + /* Process PQC KEM key share part. Depending on the pqc_first flag, the + * KEM shared secret part goes before or after the ECDH part. */ if (ret == 0) { - ret = wc_KyberKey_Encapsulate(kem, ciphertext + ecc_kse->pubKeyLen, - sharedSecret + outlen, ssl->rng); - if (ret != 0) { - WOLFSSL_MSG("wc_KyberKey encapsulation failure."); + int input_offset = ecc_kse->keLen; + int output_offset = ssSzEcc; + + if (pqc_first) { + input_offset = 0; + output_offset = 0; } + + ret = TLSX_KeyShare_HandlePqcKeyServer(ssl, pqc_kse, + data + input_offset, pubSz, + ssl->arrays->preMasterSecret + output_offset, &ssSzPqc); } if (ret == 0) { XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); - keyShareEntry->ke = sharedSecret; - keyShareEntry->keLen = outlen + ssSz; - sharedSecret = NULL; + ssl->arrays->preMasterSz = ssSzEcc + ssSzPqc; + keyShareEntry->ke = NULL; + keyShareEntry->keLen = 0; - if (ecc_kse->pubKeyLen > 0) + /* Concatenate the ECDH public key and the PQC KEM ciphertext. Based on + * the pqc_first flag, the ECDH public key goes before or after the KEM + * ciphertext. */ + if (pqc_first) { + XMEMCPY(ciphertext, pqc_kse->pubKey, ctSz); + XMEMCPY(ciphertext + ctSz, ecc_kse->pubKey, ecc_kse->pubKeyLen); + } + else { XMEMCPY(ciphertext, ecc_kse->pubKey, ecc_kse->pubKeyLen); + XMEMCPY(ciphertext + ecc_kse->pubKeyLen, pqc_kse->pubKey, ctSz); + } + keyShareEntry->pubKey = ciphertext; - keyShareEntry->pubKeyLen = (word32)(ecc_kse->pubKeyLen + ctSz); + keyShareEntry->pubKeyLen = ecc_kse->pubKeyLen + ctSz; ciphertext = NULL; /* Set namedGroup so wolfSSL_get_curve_name() can function properly on @@ -9654,13 +10229,11 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); - XFREE(sharedSecret, ssl->heap, DYNAMIC_TYPE_SECRET); + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); - wc_ecc_free(&eccpubkey); - wc_KyberKey_Free(kem); return ret; } -#endif /* WOLFSSL_HAVE_KYBER */ +#endif /* WOLFSSL_HAVE_KYBER && !WOLFSSL_KYBER_NO_ENCAPSULATE */ /* Use the data to create a new key share object in the extensions. * @@ -9710,10 +10283,21 @@ int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, #if defined(WOLFSSL_HAVE_KYBER) && !defined(WOLFSSL_KYBER_NO_ENCAPSULATE) - if (WOLFSSL_NAMED_GROUP_IS_PQC(group) && - ssl->options.side == WOLFSSL_SERVER_END) { - ret = server_generate_pqc_ciphertext((WOLFSSL*)ssl, keyShareEntry, data, - len); + if (ssl->options.side == WOLFSSL_SERVER_END && + WOLFSSL_NAMED_GROUP_IS_PQC(group)) { + ret = TLSX_KeyShare_HandlePqcKeyServer((WOLFSSL*)ssl, + keyShareEntry, + data, len, + ssl->arrays->preMasterSecret, + &ssl->arrays->preMasterSz); + if (ret != 0) + return ret; + } + else if (ssl->options.side == WOLFSSL_SERVER_END && + WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) { + ret = TLSX_KeyShare_HandlePqcHybridKeyServer((WOLFSSL*)ssl, + keyShareEntry, + data, len); if (ret != 0) return ret; } @@ -9880,27 +10464,56 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) #ifndef WOLFSSL_NO_ML_KEM_512 case WOLFSSL_ML_KEM_512: case WOLFSSL_P256_ML_KEM_512: + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + case WOLFSSL_X25519_ML_KEM_512: + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_768 case WOLFSSL_ML_KEM_768: case WOLFSSL_P384_ML_KEM_768: + case WOLFSSL_P256_ML_KEM_768: + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + case WOLFSSL_X25519_ML_KEM_768: + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + case WOLFSSL_X448_ML_KEM_768: + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_1024 case WOLFSSL_ML_KEM_1024: case WOLFSSL_P521_ML_KEM_1024: + case WOLFSSL_P384_ML_KEM_1024: #endif break; #elif defined(HAVE_LIBOQS) case WOLFSSL_ML_KEM_512: case WOLFSSL_ML_KEM_768: case WOLFSSL_ML_KEM_1024: + { + int ret; + int id; + ret = kyber_id2type(namedGroup, &id); + if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { + return 0; + } + + if (! ext_kyber_enabled(id)) { + return 0; + } + break; + } case WOLFSSL_P256_ML_KEM_512: case WOLFSSL_P384_ML_KEM_768: + case WOLFSSL_P256_ML_KEM_768: case WOLFSSL_P521_ML_KEM_1024: + case WOLFSSL_P384_ML_KEM_1024: + case WOLFSSL_X25519_ML_KEM_512: + case WOLFSSL_X448_ML_KEM_768: + case WOLFSSL_X25519_ML_KEM_768: { int ret; int id; - findEccPqc(NULL, &namedGroup, namedGroup); + findEccPqc(NULL, &namedGroup, NULL, namedGroup); ret = kyber_id2type(namedGroup, &id); if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { return 0; @@ -9912,16 +10525,26 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) break; } #endif -#endif +#endif /* WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL #ifdef WOLFSSL_WC_KYBER #ifdef WOLFSSL_KYBER512 case WOLFSSL_KYBER_LEVEL1: case WOLFSSL_P256_KYBER_LEVEL1: + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + case WOLFSSL_X25519_KYBER_LEVEL1: + #endif #endif #ifdef WOLFSSL_KYBER768 case WOLFSSL_KYBER_LEVEL3: case WOLFSSL_P384_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL3: + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + case WOLFSSL_X25519_KYBER_LEVEL3: + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + case WOLFSSL_X448_KYBER_LEVEL3: + #endif #endif #ifdef WOLFSSL_KYBER1024 case WOLFSSL_KYBER_LEVEL5: @@ -9932,13 +10555,30 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) case WOLFSSL_KYBER_LEVEL1: case WOLFSSL_KYBER_LEVEL3: case WOLFSSL_KYBER_LEVEL5: + { + int ret; + int id; + ret = kyber_id2type(namedGroup, &id); + if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { + return 0; + } + + if (! ext_kyber_enabled(id)) { + return 0; + } + break; + } case WOLFSSL_P256_KYBER_LEVEL1: case WOLFSSL_P384_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL3: case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_X25519_KYBER_LEVEL1: + case WOLFSSL_X448_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: { int ret; int id; - findEccPqc(NULL, &namedGroup, namedGroup); + findEccPqc(NULL, &namedGroup, NULL, namedGroup); ret = kyber_id2type(namedGroup, &id); if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { return 0; @@ -10002,14 +10642,25 @@ static const word16 preferredGroup[] = { #ifndef WOLFSSL_NO_ML_KEM_512 WOLFSSL_ML_KEM_512, WOLFSSL_P256_ML_KEM_512, + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_ML_KEM_512, + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_768 WOLFSSL_ML_KEM_768, WOLFSSL_P384_ML_KEM_768, + WOLFSSL_P256_ML_KEM_768, + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_ML_KEM_768, + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + WOLFSSL_X448_ML_KEM_768, + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_1024 WOLFSSL_ML_KEM_1024, WOLFSSL_P521_ML_KEM_1024, + WOLFSSL_P384_ML_KEM_1024, #endif #elif defined(HAVE_LIBOQS) /* These require a runtime call to TLSX_KeyShare_IsSupported to use */ @@ -10018,7 +10669,16 @@ static const word16 preferredGroup[] = { WOLFSSL_ML_KEM_1024, WOLFSSL_P256_ML_KEM_512, WOLFSSL_P384_ML_KEM_768, + WOLFSSL_P256_ML_KEM_768, WOLFSSL_P521_ML_KEM_1024, + WOLFSSL_P384_ML_KEM_1024, + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_ML_KEM_512, + WOLFSSL_X25519_ML_KEM_768, + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + WOLFSSL_X448_ML_KEM_768, + #endif #endif #endif /* !WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL @@ -10026,10 +10686,20 @@ static const word16 preferredGroup[] = { #ifdef WOLFSSL_KYBER512 WOLFSSL_KYBER_LEVEL1, WOLFSSL_P256_KYBER_LEVEL1, + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_KYBER_LEVEL1, + #endif #endif #ifdef WOLFSSL_KYBER768 WOLFSSL_KYBER_LEVEL3, WOLFSSL_P384_KYBER_LEVEL3, + WOLFSSL_P256_KYBER_LEVEL3, + #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_KYBER1024 WOLFSSL_KYBER_LEVEL5, @@ -10042,7 +10712,15 @@ static const word16 preferredGroup[] = { WOLFSSL_KYBER_LEVEL5, WOLFSSL_P256_KYBER_LEVEL1, WOLFSSL_P384_KYBER_LEVEL3, + WOLFSSL_P256_KYBER_LEVEL3, WOLFSSL_P521_KYBER_LEVEL5, + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + WOLFSSL_X25519_KYBER_LEVEL1, + WOLFSSL_X25519_KYBER_LEVEL3, + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + WOLFSSL_X448_KYBER_LEVEL3, + #endif #endif #endif /* WOLFSSL_KYBER_ORIGINAL */ WOLFSSL_NAMED_GROUP_INVALID @@ -10337,7 +11015,9 @@ int TLSX_KeyShare_Choose(const WOLFSSL *ssl, TLSX* extensions, /* Use server's preference order. */ for (clientKSE = list; clientKSE != NULL; clientKSE = clientKSE->next) { - if (clientKSE->ke == NULL) + if ((clientKSE->ke == NULL) && + (!WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group)) && + (!WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(clientKSE->group))) continue; #ifdef WOLFSSL_SM2 @@ -10357,11 +11037,12 @@ int TLSX_KeyShare_Choose(const WOLFSSL *ssl, TLSX* extensions, if (!TLSX_SupportedGroups_Find(ssl, clientKSE->group, extensions)) continue; - if (!WOLFSSL_NAMED_GROUP_IS_FFHDE(clientKSE->group)) { + if (!WOLFSSL_NAMED_GROUP_IS_FFDHE(clientKSE->group)) { /* Check max value supported. */ if (clientKSE->group > WOLFSSL_ECC_MAX) { #ifdef WOLFSSL_HAVE_KYBER - if (!WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group)) + if (!WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group) && + !WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(clientKSE->group)) #endif continue; } @@ -10416,7 +11097,7 @@ int TLSX_KeyShare_Setup(WOLFSSL *ssl, KeyShareEntry* clientKSE) return BAD_FUNC_ARG; } - /* Generate a new key pair except in the case of OQS KEM because we + /* Generate a new key pair except in the case of PQC KEM because we * are going to encapsulate and that does not require us to generate a * key pair. */ @@ -10426,7 +11107,8 @@ int TLSX_KeyShare_Setup(WOLFSSL *ssl, KeyShareEntry* clientKSE) if (clientKSE->key == NULL) { #ifdef WOLFSSL_HAVE_KYBER - if (WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group)) { + if (WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group) || + WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(clientKSE->group)) { /* Going to need the public key (AKA ciphertext). */ serverKSE->pubKey = clientKSE->pubKey; clientKSE->pubKey = NULL; @@ -13674,6 +14356,11 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_ML_KEM_512, ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_ML_KEM_512, + ssl->heap); + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_768 if (ret == WOLFSSL_SUCCESS) @@ -13682,6 +14369,19 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_ML_KEM_768, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_ML_KEM_768, + ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_ML_KEM_768, + ssl->heap); + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448_ML_KEM_768, + ssl->heap); + #endif #endif #ifndef WOLFSSL_NO_ML_KEM_1024 if (ret == WOLFSSL_SUCCESS) @@ -13690,6 +14390,9 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P521_ML_KEM_1024, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_ML_KEM_1024, + ssl->heap); #endif #elif defined(HAVE_LIBOQS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_ML_KEM_512, ssl->heap); @@ -13705,9 +14408,28 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_ML_KEM_768, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_ML_KEM_768, + ssl->heap); if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P521_ML_KEM_1024, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_ML_KEM_1024, + ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_ML_KEM_512, + ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_ML_KEM_768, + ssl->heap); + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448_ML_KEM_768, + ssl->heap); + #endif #endif /* HAVE_LIBOQS */ #endif /* !WOLFSSL_NO_ML_KEM */ #ifdef WOLFSSL_KYBER_ORIGINAL @@ -13719,6 +14441,11 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_KYBER_LEVEL1, ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL1, + ssl->heap); + #endif #endif #ifdef WOLFSSL_KYBER768 if (ret == WOLFSSL_SUCCESS) @@ -13727,6 +14454,19 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_KYBER_LEVEL3, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_KYBER_LEVEL3, + ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL3, + ssl->heap); + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448_KYBER_LEVEL3, + ssl->heap); + #endif #endif #ifdef WOLFSSL_KYBER1024 if (ret == WOLFSSL_SUCCESS) @@ -13750,9 +14490,25 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_KYBER_LEVEL3, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P256_KYBER_LEVEL3, + ssl->heap); if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P521_KYBER_LEVEL5, ssl->heap); + #if defined(HAVE_CURVE25519) && ECC_MIN_KEY_SZ <= 256 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL1, + ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X25519_KYBER_LEVEL3, + ssl->heap); + #endif + #if defined(HAVE_CURVE448) && ECC_MIN_KEY_SZ <= 448 + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_X448_KYBER_LEVEL3, + ssl->heap); + #endif #endif /* HAVE_LIBOQS */ #endif /* WOLFSSL_KYBER_ORIGINAL */ #endif /* WOLFSSL_HAVE_KYBER */ diff --git a/src/tls13.c b/src/tls13.c index f41f1f42f5..7bc18d5cc2 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -13607,7 +13607,8 @@ int wolfSSL_UseKeyShare(WOLFSSL* ssl, word16 group) #endif #if defined(WOLFSSL_HAVE_KYBER) - if (WOLFSSL_NAMED_GROUP_IS_PQC(group)) { + if (WOLFSSL_NAMED_GROUP_IS_PQC(group) || + WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) { if (ssl->ctx != NULL && ssl->ctx->method != NULL && !IsAtLeastTLSv1_3(ssl->version)) { diff --git a/tests/include.am b/tests/include.am index 0f4df2de1c..cd629eed20 100644 --- a/tests/include.am +++ b/tests/include.am @@ -30,11 +30,11 @@ EXTRA_DIST += tests/unit.h \ tests/test-tls13-ecc.conf \ tests/test-tls13-psk.conf \ tests/test-tls13-pq.conf \ - tests/test-tls13-pq-2.conf \ + tests/test-tls13-pq-hybrid.conf \ tests/test-dtls13-pq.conf \ tests/test-dtls13-pq-frag.conf \ - tests/test-dtls13-pq-2.conf \ - tests/test-dtls13-pq-2-frag.conf \ + tests/test-dtls13-pq-hybrid.conf \ + tests/test-dtls13-pq-hybrid-frag.conf \ tests/test-psk.conf \ tests/test-psk-no-id.conf \ tests/test-psk-no-id-sha2.conf \ diff --git a/tests/suites.c b/tests/suites.c index 074a9a03f6..503dec1de9 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -992,9 +992,8 @@ int SuiteTest(int argc, char** argv) args.return_code = EXIT_FAILURE; goto exit; } - #ifdef HAVE_LIBOQS - /* add TLSv13 pq tests */ - XSTRLCPY(argv0[1], "tests/test-tls13-pq-2.conf", sizeof(argv0[1])); + /* add TLSv13 pq hybrid tests */ + XSTRLCPY(argv0[1], "tests/test-tls13-pq-hybrid.conf", sizeof(argv0[1])); printf("starting TLSv13 post-quantum groups tests\n"); test_harness(&args); if (args.return_code != 0) { @@ -1003,29 +1002,6 @@ int SuiteTest(int argc, char** argv) goto exit; } #endif - #endif - #ifdef HAVE_PQC - /* add TLSv13 pq tests */ - XSTRLCPY(argv0[1], "tests/test-tls13-pq.conf", sizeof(argv0[1])); - printf("starting TLSv13 post-quantum groups tests\n"); - test_harness(&args); - if (args.return_code != 0) { - printf("error from script %d\n", args.return_code); - args.return_code = EXIT_FAILURE; - goto exit; - } - #ifdef HAVE_LIBOQS - /* add TLSv13 pq tests */ - XSTRLCPY(argv0[1], "tests/test-tls13-pq-2.conf", sizeof(argv0[1])); - printf("starting TLSv13 post-quantum groups tests\n"); - test_harness(&args); - if (args.return_code != 0) { - printf("error from script %d\n", args.return_code); - args.return_code = EXIT_FAILURE; - goto exit; - } - #endif - #endif #if defined(HAVE_PQC) && defined(WOLFSSL_DTLS13) /* add DTLSv13 pq tests */ XSTRLCPY(argv0[1], "tests/test-dtls13-pq.conf", sizeof(argv0[1])); @@ -1036,30 +1012,27 @@ int SuiteTest(int argc, char** argv) args.return_code = EXIT_FAILURE; goto exit; } - #ifdef WOLFSSL_DTLS_CH_FRAG - /* add DTLSv13 pq frag tests */ - XSTRLCPY(argv0[1], "tests/test-dtls13-pq-frag.conf", sizeof(argv0[1])); - printf("starting DTLSv13 post-quantum groups tests with fragmentation\n"); + /* add DTLSv13 pq hybrid tests */ + XSTRLCPY(argv0[1], "tests/test-dtls13-pq-hybrid.conf", sizeof(argv0[1])); + printf("starting DTLSv13 post-quantum 2 groups tests\n"); test_harness(&args); if (args.return_code != 0) { printf("error from script %d\n", args.return_code); args.return_code = EXIT_FAILURE; goto exit; } - #endif - #ifdef HAVE_LIBOQS - /* add DTLSv13 pq 2 tests */ - XSTRLCPY(argv0[1], "tests/test-dtls13-pq-2.conf", sizeof(argv0[1])); - printf("starting DTLSv13 post-quantum 2 groups tests\n"); + #ifdef WOLFSSL_DTLS_CH_FRAG + /* add DTLSv13 pq frag tests */ + XSTRLCPY(argv0[1], "tests/test-dtls13-pq-frag.conf", sizeof(argv0[1])); + printf("starting DTLSv13 post-quantum groups tests with fragmentation\n"); test_harness(&args); if (args.return_code != 0) { printf("error from script %d\n", args.return_code); args.return_code = EXIT_FAILURE; goto exit; } - #ifdef WOLFSSL_DTLS_CH_FRAG - /* add DTLSv13 pq 2 frag tests */ - XSTRLCPY(argv0[1], "tests/test-dtls13-pq-2-frag.conf", sizeof(argv0[1])); + /* add DTLSv13 pq hybrid frag tests */ + XSTRLCPY(argv0[1], "tests/test-dtls13-pq-hybrid-frag.conf", sizeof(argv0[1])); printf("starting DTLSv13 post-quantum 2 groups tests with fragmentation\n"); test_harness(&args); if (args.return_code != 0) { @@ -1069,7 +1042,6 @@ int SuiteTest(int argc, char** argv) } #endif #endif - #endif #endif #if defined(WC_RSA_PSS) && (!defined(HAVE_FIPS) || \ (defined(HAVE_FIPS_VERSION) && (HAVE_FIPS_VERSION > 2))) && \ diff --git a/tests/test-dtls13-pq-2-frag.conf b/tests/test-dtls13-pq-2-frag.conf deleted file mode 100644 index 6ea8317db0..0000000000 --- a/tests/test-dtls13-pq-2-frag.conf +++ /dev/null @@ -1,23 +0,0 @@ -# server DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_KYBER_LEVEL3 - -# client DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_KYBER_LEVEL3 - -# server DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_KYBER_LEVEL5 - -# client DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_KYBER_LEVEL5 diff --git a/tests/test-dtls13-pq-2.conf b/tests/test-dtls13-pq-2.conf deleted file mode 100644 index bd5e32697d..0000000000 --- a/tests/test-dtls13-pq-2.conf +++ /dev/null @@ -1,27 +0,0 @@ -# server DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_ML_KEM_512 - -# client DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_ML_KEM_512 - -# P384_ML_KEM_768 and P521_ML_KEM_1024 would fragment the ClientHello. - -# server DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_KYBER_LEVEL1 - -# client DTLSv1.3 with post-quantum group --u --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_KYBER_LEVEL1 - -# P384_KYBER_LEVEL3 and P521_KYBER_LEVEL5 would fragment the ClientHello. diff --git a/tests/test-dtls13-pq-frag.conf b/tests/test-dtls13-pq-frag.conf index 01aaf477fe..5592ecb795 100644 --- a/tests/test-dtls13-pq-frag.conf +++ b/tests/test-dtls13-pq-frag.conf @@ -1,3 +1,27 @@ +# server DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc ML_KEM_768 + +# client DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc ML_KEM_768 + +# server DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc ML_KEM_1024 + +# client DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc ML_KEM_1024 + # server DTLSv1.3 with post-quantum group -u -v 4 @@ -21,4 +45,3 @@ -v 4 -l TLS13-AES256-GCM-SHA384 --pqc KYBER_LEVEL5 - diff --git a/tests/test-dtls13-pq-hybrid-frag.conf b/tests/test-dtls13-pq-hybrid-frag.conf new file mode 100644 index 0000000000..c9edc6907f --- /dev/null +++ b/tests/test-dtls13-pq-hybrid-frag.conf @@ -0,0 +1,131 @@ +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_768 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_768 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_768 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_768 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_ML_KEM_1024 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_ML_KEM_1024 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_1024 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_1024 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_768 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_768 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_ML_KEM_768 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_ML_KEM_768 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_KYBER_LEVEL3 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_KYBER_LEVEL3 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL3 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL3 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_KYBER_LEVEL5 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_KYBER_LEVEL5 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL3 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL3 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_KYBER_LEVEL3 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_KYBER_LEVEL3 diff --git a/tests/test-dtls13-pq-hybrid.conf b/tests/test-dtls13-pq-hybrid.conf new file mode 100644 index 0000000000..3bb14cac54 --- /dev/null +++ b/tests/test-dtls13-pq-hybrid.conf @@ -0,0 +1,51 @@ +# server DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_512 + +# client DTLSv1.3 with post-quantum group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_512 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_512 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_512 + +# Hybrids with ML_KEM_768 and ML_KEM_1024 would fragment the ClientHello. + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL1 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL1 + +# server DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL1 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL1 + +# Hybrids with KYBER_LEVEL3 and KYBER_LEVEL5 would fragment the ClientHello. diff --git a/tests/test-dtls13-pq.conf b/tests/test-dtls13-pq.conf index 37abf2c77a..559741f32d 100644 --- a/tests/test-dtls13-pq.conf +++ b/tests/test-dtls13-pq.conf @@ -16,12 +16,12 @@ -u -v 4 -l TLS13-AES256-GCM-SHA384 ---pqc ML_KEM_512 +--pqc KYBER_LEVEL1 # client DTLSv1.3 with post-quantum group -u -v 4 -l TLS13-AES256-GCM-SHA384 ---pqc ML_KEM_512 +--pqc KYBER_LEVEL1 # KYBER_LEVEL3 and KYBER_LEVEL5 would fragment the ClientHello. diff --git a/tests/test-tls13-pq-2.conf b/tests/test-tls13-pq-2.conf deleted file mode 100644 index 26f5f525d8..0000000000 --- a/tests/test-tls13-pq-2.conf +++ /dev/null @@ -1,59 +0,0 @@ -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_ML_KEM_512 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_ML_KEM_512 - -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_ML_KEM_768 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_ML_KEM_768 - -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_ML_KEM1024 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_ML_KEM1024 - -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_KYBER_LEVEL1 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P256_KYBER_LEVEL1 - -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_KYBER_LEVEL3 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P384_KYBER_LEVEL3 - -# server TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_KYBER_LEVEL5 - -# client TLSv1.3 with post-quantum group --v 4 --l TLS13-AES256-GCM-SHA384 ---pqc P521_KYBER_LEVEL5 diff --git a/tests/test-tls13-pq-hybrid.conf b/tests/test-tls13-pq-hybrid.conf new file mode 100644 index 0000000000..242cd3089b --- /dev/null +++ b/tests/test-tls13-pq-hybrid.conf @@ -0,0 +1,149 @@ +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_512 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_512 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_768 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_768 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_768 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_ML_KEM_768 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_ML_KEM_1024 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_ML_KEM_1024 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_1024 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_ML_KEM_1024 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_512 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_512 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_768 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_ML_KEM_768 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_ML_KEM_768 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_ML_KEM_768 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL1 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL1 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_KYBER_LEVEL3 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_KYBER_LEVEL3 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL3 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P256_KYBER_LEVEL3 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_KYBER_LEVEL5 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P521_KYBER_LEVEL5 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL1 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL1 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL3 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X25519_KYBER_LEVEL3 + +# server TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_KYBER_LEVEL3 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc X448_KYBER_LEVEL3 diff --git a/wolfssl/internal.h b/wolfssl/internal.h index 8ce9aac4e3..2198b917c0 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1906,14 +1906,16 @@ enum Misc { #define AEAD_AUTH_DATA_SZ 13 #endif -#define WOLFSSL_NAMED_GROUP_IS_FFHDE(group) \ - (MIN_FFHDE_GROUP <= (group) && (group) <= MAX_FFHDE_GROUP) +#define WOLFSSL_NAMED_GROUP_IS_FFDHE(group) \ + (WOLFSSL_FFDHE_START <= (group) && (group) <= WOLFSSL_FFDHE_END) #ifdef WOLFSSL_HAVE_KYBER -#define WOLFSSL_NAMED_GROUP_IS_PQC(group) \ - ((WOLFSSL_PQC_SIMPLE_MIN <= (group) && (group) <= WOLFSSL_PQC_SIMPLE_MAX) || \ - (WOLFSSL_PQC_HYBRID_MIN <= (group) && (group) <= WOLFSSL_PQC_HYBRID_MAX)) +WOLFSSL_LOCAL int NamedGroupIsPqc(int group); +WOLFSSL_LOCAL int NamedGroupIsPqcHybrid(int group); +#define WOLFSSL_NAMED_GROUP_IS_PQC(group) NamedGroupIsPqc(group) +#define WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group) NamedGroupIsPqcHybrid(group) #else -#define WOLFSSL_NAMED_GROUP_IS_PQC(group) ((void)(group), 0) +#define WOLFSSL_NAMED_GROUP_IS_PQC(group) ((void)(group), 0) +#define WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group) ((void)(group), 0) #endif /* WOLFSSL_HAVE_KYBER */ /* minimum Downgrade Minor version */ @@ -3603,8 +3605,8 @@ typedef struct KeyShareEntry { byte* pubKey; /* Public key */ word32 pubKeyLen; /* Public key length */ #if !defined(NO_DH) || defined(WOLFSSL_HAVE_KYBER) - byte* privKey; /* Private key - DH and PQ KEMs only */ - word32 privKeyLen;/* Only for PQ KEMs. */ + byte* privKey; /* Private key */ + word32 privKeyLen;/* Private key length - PQC only */ #endif #ifdef WOLFSSL_ASYNC_CRYPT int lastRet; diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index 1c46f2b35e..7260799fe9 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -4568,62 +4568,54 @@ enum { WOLFSSL_FFDHE_4096 = 258, WOLFSSL_FFDHE_6144 = 259, WOLFSSL_FFDHE_8192 = 260, + WOLFSSL_FFDHE_END = 511, #ifdef HAVE_PQC - /* These group numbers were taken from OQS's openssl provider, see: - * https://github.com/open-quantum-safe/oqs-provider/blob/main/oqs-template/ - * oqs-kem-info.md. - * - * The levels in the group name refer to the claimed NIST level of each - * parameter set. The associated parameter set name is listed as a comment - * beside the group number. Please see the NIST PQC Competition's submitted - * papers for more details. - * - * LEVEL1 means that an attack on that parameter set would require the same - * or more resources as a key search on AES 128. LEVEL3 would require the - * same or more resources as a key search on AES 192. LEVEL5 would require - * the same or more resources as a key search on AES 256. None of the - * algorithms have LEVEL2 and LEVEL4 because none of these submissions - * included them. */ #ifdef WOLFSSL_KYBER_ORIGINAL - WOLFSSL_PQC_MIN = 570, - WOLFSSL_PQC_SIMPLE_MIN = 570, + /* Old code points to keep compatibility with Kyber Round 3. + * Taken from OQS's openssl provider, see: + * https://github.com/open-quantum-safe/oqs-provider/blob/main/oqs-template/ + * oqs-kem-info.md + */ WOLFSSL_KYBER_LEVEL1 = 570, /* KYBER_512 */ WOLFSSL_KYBER_LEVEL3 = 572, /* KYBER_768 */ WOLFSSL_KYBER_LEVEL5 = 573, /* KYBER_1024 */ -#ifdef WOLFSSL_NO_ML_KEM - WOLFSSL_PQC_SIMPLE_MAX = 573, -#endif - WOLFSSL_PQC_HYBRID_MIN = 12090, WOLFSSL_P256_KYBER_LEVEL1 = 12090, WOLFSSL_P384_KYBER_LEVEL3 = 12092, WOLFSSL_P521_KYBER_LEVEL5 = 12093, -#ifdef WOLFSSL_NO_ML_KEM - WOLFSSL_PQC_HYBRID_MAX = 12093, - WOLFSSL_PQC_MAX = 12093, -#endif -#endif + WOLFSSL_X25519_KYBER_LEVEL1 = 12089, + WOLFSSL_X448_KYBER_LEVEL3 = 12176, + WOLFSSL_X25519_KYBER_LEVEL3 = 25497, + WOLFSSL_P256_KYBER_LEVEL3 = 25498, +#endif /* WOLFSSL_KYBER_ORIGINAL */ #ifndef WOLFSSL_NO_ML_KEM -#ifndef WOLFSSL_KYBER_ORIGINAL - WOLFSSL_PQC_MIN = 512, - WOLFSSL_PQC_SIMPLE_MIN = 512, -#endif - WOLFSSL_ML_KEM_512 = 512, /* ML-KEM 512 */ - WOLFSSL_ML_KEM_768 = 513, /* ML-KEM 768 */ - WOLFSSL_ML_KEM_1024 = 514, /* ML-KEM 1024 */ - WOLFSSL_PQC_SIMPLE_MAX = 514, + /* Taken from draft-connolly-tls-mlkem-key-agreement, see: + * https://github.com/dconnolly/draft-connolly-tls-mlkem-key-agreement/ + */ + WOLFSSL_ML_KEM_512 = 512, + WOLFSSL_ML_KEM_768 = 513, + WOLFSSL_ML_KEM_1024 = 514, -#ifndef WOLFSSL_KYBER_ORIGINAL - WOLFSSL_PQC_HYBRID_MIN = 12107, -#endif + /* Taken from draft-kwiatkowski-tls-ecdhe-mlkem. see: + * https://github.com/post-quantum-cryptography/ + * draft-kwiatkowski-tls-ecdhe-mlkem/ + */ + WOLFSSL_P256_ML_KEM_768 = 4587, + WOLFSSL_X25519_ML_KEM_768 = 4588, + WOLFSSL_P384_ML_KEM_1024 = 4589, + + /* Taken from OQS's openssl provider, see: + * https://github.com/open-quantum-safe/oqs-provider/blob/main/oqs-template/ + * oqs-kem-info.md + */ WOLFSSL_P256_ML_KEM_512 = 12107, WOLFSSL_P384_ML_KEM_768 = 12108, WOLFSSL_P521_ML_KEM_1024 = 12109, - WOLFSSL_PQC_HYBRID_MAX = 12109, - WOLFSSL_PQC_MAX = 12109, -#endif /* !WOLFSSL_NO_ML_KEM */ + WOLFSSL_X25519_ML_KEM_512 = 12214, + WOLFSSL_X448_ML_KEM_768 = 12215, +#endif /* WOLFSSL_NO_ML_KEM */ #endif /* HAVE_PQC */ WOLF_ENUM_DUMMY_LAST_ELEMENT(SSL_H) }; diff --git a/wolfssl/wolfcrypt/ext_kyber.h b/wolfssl/wolfcrypt/ext_kyber.h index e4f20a7903..0fe421f563 100644 --- a/wolfssl/wolfcrypt/ext_kyber.h +++ b/wolfssl/wolfcrypt/ext_kyber.h @@ -39,8 +39,14 @@ #if defined (HAVE_LIBOQS) #include - #define EXT_KYBER_MAX_PRIV_SZ OQS_KEM_kyber_1024_length_secret_key - #define EXT_KYBER_MAX_PUB_SZ OQS_KEM_kyber_1024_length_public_key + + #ifndef WOLFSSL_NO_ML_KEM + #define EXT_KYBER_MAX_PRIV_SZ OQS_KEM_ml_kem_1024_length_secret_key + #define EXT_KYBER_MAX_PUB_SZ OQS_KEM_ml_kem_1024_length_public_key + #elif defined(WOLFSSL_KYBER_ORIGINAL) + #define EXT_KYBER_MAX_PRIV_SZ OQS_KEM_kyber_1024_length_secret_key + #define EXT_KYBER_MAX_PUB_SZ OQS_KEM_kyber_1024_length_public_key + #endif #endif struct KyberKey { From c899f79cfab3b34e8bf8f94e747c235b3367ee02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Mon, 11 Nov 2024 12:39:09 +0100 Subject: [PATCH 2/2] Update key share group ranking algorithm MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In case no user group ranking is set, all groups are now ranked equally instead of the order in the `preferredGroup` array. This is the behavior already indicated in the comment header of the function. This change is necessary for applications that do not set their own group ranking (via `wolfSSL_CTX_set_groups()` for example). When such an application creates a TLS server and receives a ClientHello message with multiple key shares, now the first key share is selected instead of the one with the lowest index in the `preferredGroup` array. Recent browsers with PQC support place two key shares in their ClientHello message: a hybrid PQC + X25519 one and at least one classic-only one. The hybrid one is the first one, indicating a preference. Without this change, however, always the classic-only key share has been selected, as these algorithms have a lower index in the `preferredGroup` array compared to the PQC hybrids. Tested using a patched version of NGINX. This change also results in a different selection of a key share group in case of a HelloRetryRequest message. For the tests, where static ephemeral keys are used (`WOLFSSL_STATIC_EPHEMERAL`), an additional check is necessary to make sure the correct key is used for the ECDH calculation. Signed-off-by: Tobias Frauenschläger --- src/tls.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tls.c b/src/tls.c index 617f8de7eb..5fc3557f28 100644 --- a/src/tls.c +++ b/src/tls.c @@ -8012,7 +8012,7 @@ static int TLSX_KeyShare_GenEccKey(WOLFSSL *ssl, KeyShareEntry* kse) #ifdef WOLFSSL_STATIC_EPHEMERAL ret = wolfSSL_StaticEphemeralKeyLoad(ssl, WC_PK_TYPE_ECDH, kse->key); - if (ret != 0) + if (ret != 0 || eccKey->dp->id != curveId) #endif { /* set curve info for EccMakeKey "peer" info */ @@ -10745,8 +10745,7 @@ static int TLSX_KeyShare_GroupRank(const WOLFSSL* ssl, int group) byte numGroups; if (ssl->numGroups == 0) { - groups = preferredGroup; - numGroups = PREFERRED_GROUP_SZ; + return 0; } else { groups = ssl->group;