From 64ba1a1b4bf517690595cf0aa05d4be23c963ee8 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/3] Add more PQC hybrid key exchange algorithms MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for all remaining hybrid PQC + ECC hybrid key exchange groups to match OQS. Next to two new combinations with SECP curves, this mainly also adds support for combinations with X25519 and X448. This also enables compatability with the PQC key exchange support in Chromium browsers and Mozilla Firefox (hybrid Kyber768 and X25519; when `WOLFSSL_KYBER_ORIGINAL` is defined). In the process of extending support, some code and logic cleanup happened. Furthermore, two memory leaks within the hybrid code path have been fixed. Signed-off-by: Tobias Frauenschläger --- examples/benchmark/tls_bench.c | 11 +- examples/client/client.c | 24 ++ examples/server/server.c | 24 ++ src/internal.c | 23 ++ src/ssl.c | 68 ++++- src/tls.c | 403 ++++++++++++++++---------- tests/include.am | 6 +- tests/suites.c | 50 +--- tests/test-dtls13-pq-2-frag.conf | 23 -- tests/test-dtls13-pq-2.conf | 13 - tests/test-dtls13-pq-frag.conf | 1 - tests/test-dtls13-pq-hybrid-frag.conf | 71 +++++ tests/test-dtls13-pq-hybrid.conf | 25 ++ tests/test-tls13-pq-2.conf | 29 -- tests/test-tls13-pq-hybrid.conf | 79 +++++ wolfssl/internal.h | 5 +- wolfssl/ssl.h | 75 ++--- 17 files changed, 606 insertions(+), 324 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 609481a3e0..177c3c3988 100644 --- a/examples/benchmark/tls_bench.c +++ b/examples/benchmark/tls_bench.c @@ -291,9 +291,14 @@ static struct group_info groups[] = { { 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_P384_KYBER_LEVEL5, "P384_KYBER_LEVEL5" }, + { WOLFSSL_X25519_KYBER_LEVEL1, "X25519_KYBER_LEVEL1" }, + { WOLFSSL_X448_KYBER_LEVEL3, "X448_KYBER_LEVEL3" }, + { WOLFSSL_X25519_KYBER_LEVEL3, "X25519_KYBER_LEVEL3" }, #endif { 0, NULL } }; diff --git a/examples/client/client.c b/examples/client/client.c index b8adcc1924..ab851fcd77 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -426,12 +426,36 @@ 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 if (XSTRCMP(pqcAlg, "P521_KYBER_LEVEL5") == 0) { group = WOLFSSL_P521_KYBER_LEVEL5; } + else if (XSTRCMP(pqcAlg, "P384_KYBER_LEVEL5") == 0) { + group = WOLFSSL_P384_KYBER_LEVEL5; + } + 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 { diff --git a/examples/server/server.c b/examples/server/server.c index 2f42a909e3..c2af4dadb7 100644 --- a/examples/server/server.c +++ b/examples/server/server.c @@ -740,12 +740,36 @@ 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 if (XSTRCMP(pqcAlg, "P521_KYBER_LEVEL5") == 0) { groups[count] = WOLFSSL_P521_KYBER_LEVEL5; } + else if (XSTRCMP(pqcAlg, "P384_KYBER_LEVEL5") == 0) { + groups[count] = WOLFSSL_P384_KYBER_LEVEL5; + } + 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 { diff --git a/src/internal.c b/src/internal.c index d05238ec89..49098c22ae 100644 --- a/src/internal.c +++ b/src/internal.c @@ -34146,6 +34146,29 @@ 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) { + case WOLFSSL_KYBER_LEVEL1: + case WOLFSSL_KYBER_LEVEL3: + case WOLFSSL_KYBER_LEVEL5: + case WOLFSSL_P256_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: + case WOLFSSL_P384_KYBER_LEVEL5: + 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: + 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 6badc340c6..084014526e 100644 --- a/src/ssl.c +++ b/src/ssl.c @@ -3304,6 +3304,11 @@ static int isValidCurveGroup(word16 name) case WOLFSSL_P256_KYBER_LEVEL1: case WOLFSSL_P384_KYBER_LEVEL3: case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_P384_KYBER_LEVEL5: + case WOLFSSL_X25519_KYBER_LEVEL1: + case WOLFSSL_X448_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL3: #endif #endif return 1; @@ -14448,37 +14453,67 @@ const char* wolfSSL_get_curve_name(WOLFSSL* ssl) * check to override this result in the case of a hybrid. */ if (IsAtLeastTLSv1_3(ssl->version)) { switch (ssl->namedGroup) { -#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) #ifdef WOLFSSL_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 #ifdef WOLFSSL_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 #ifdef WOLFSSL_KYBER1024 case WOLFSSL_KYBER_LEVEL5: return "KYBER_LEVEL5"; case WOLFSSL_P521_KYBER_LEVEL5: return "P521_KYBER_LEVEL5"; + case WOLFSSL_P384_KYBER_LEVEL5: + return "P384_KYBER_LEVEL5"; + #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"; + case WOLFSSL_P384_KYBER_LEVEL5: + return "P384_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 } @@ -21790,7 +21825,12 @@ const WOLF_EC_NIST_NAME kNistCurves[] = { #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_P256_KYBER_LEVEL1}, + {CURVE_NAME("P256_KYBER_LEVEL3"), WOLFSSL_P256_KYBER_LEVEL3, WOLFSSL_P256_KYBER_LEVEL1}, {CURVE_NAME("P521_KYBER_LEVEL5"), WOLFSSL_P521_KYBER_LEVEL5, WOLFSSL_P256_KYBER_LEVEL1}, + {CURVE_NAME("P384_KYBER_LEVEL5"), WOLFSSL_P384_KYBER_LEVEL5, WOLFSSL_P256_KYBER_LEVEL1}, + {CURVE_NAME("X25519_KYBER_LEVEL1"), WOLFSSL_X25519_KYBER_LEVEL1, WOLFSSL_P256_KYBER_LEVEL1}, + {CURVE_NAME("X448_KYBER_LEVEL3"), WOLFSSL_X448_KYBER_LEVEL3, WOLFSSL_P256_KYBER_LEVEL1}, + {CURVE_NAME("X25519_KYBER_LEVEL3"), WOLFSSL_X25519_KYBER_LEVEL3, WOLFSSL_P256_KYBER_LEVEL1}, #endif #endif #ifdef WOLFSSL_SM2 diff --git a/src/tls.c b/src/tls.c index a519836ec0..096d4f544e 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7435,6 +7435,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); @@ -7520,6 +7521,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); @@ -7777,8 +7779,22 @@ static const PqcHybridMapping pqc_hybrid_mapping[] = { .pqc = WOLFSSL_KYBER_LEVEL1}, {.hybrid = WOLFSSL_P384_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_SECP384R1, .pqc = WOLFSSL_KYBER_LEVEL3}, + {.hybrid = WOLFSSL_P256_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_SECP256R1, + .pqc = WOLFSSL_KYBER_LEVEL3}, {.hybrid = WOLFSSL_P521_KYBER_LEVEL5, .ecc = WOLFSSL_ECC_SECP521R1, .pqc = WOLFSSL_KYBER_LEVEL5}, + {.hybrid = WOLFSSL_P384_KYBER_LEVEL5, .ecc = WOLFSSL_ECC_SECP384R1, + .pqc = WOLFSSL_KYBER_LEVEL5}, +#ifdef HAVE_CURVE25519 + {.hybrid = WOLFSSL_X25519_KYBER_LEVEL1, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_KYBER_LEVEL1}, + {.hybrid = WOLFSSL_X25519_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_X25519, + .pqc = WOLFSSL_KYBER_LEVEL3}, +#endif +#ifdef HAVE_CURVE448 + {.hybrid = WOLFSSL_X448_KYBER_LEVEL3, .ecc = WOLFSSL_ECC_X448, + .pqc = WOLFSSL_KYBER_LEVEL3}, +#endif {.hybrid = 0, .ecc = 0, .pqc = 0} }; @@ -7812,7 +7828,7 @@ static void findEccPqc(int *ecc, int *pqc, int group) } } -/* Create a key share entry using liboqs parameters group. +/* Create a key share entry using pqc parameters group. * Generates a key pair. * * ssl The SSL/TLS object. @@ -7827,7 +7843,7 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) byte* pubKey = NULL; byte* privKey = NULL; KeyShareEntry *ecc_kse = NULL; - int oqs_group = 0; + int pqc_group = 0; int ecc_group = 0; word32 privSz = 0; word32 pubSz = 0; @@ -7839,8 +7855,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); + findEccPqc(&ecc_group, &pqc_group, kse->group); + ret = kyber_id2type(pqc_group, &type); if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { WOLFSSL_MSG("Invalid Kyber algorithm specified."); ret = BAD_FUNC_ARG; @@ -7873,8 +7889,22 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) 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. */ + #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) { @@ -7914,7 +7944,7 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) kse->pubKeyLen = ecc_kse->pubKeyLen + pubSz; pubKey = NULL; - /* Note we are saving the OQS private key and ECC private key + /* 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. Typically do not need the private * key size, but will need to zero it out upon freeing. */ @@ -8303,6 +8333,8 @@ 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; @@ -8381,6 +8413,8 @@ 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; @@ -8548,58 +8582,36 @@ static int TLSX_KeyShare_ProcessPqc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) int ret = 0; int type; KyberKey kem[1]; - byte* sharedSecret = NULL; - word32 sharedSecretLen = 0; - int oqs_group = 0; + int pqc_group = 0; int ecc_group = 0; - ecc_key eccpubkey; + KeyShareEntry *ecc_kse = NULL; word32 outlen = 0; word32 privSz = 0; word32 ctSz = 0; word32 ssSz = 0; - if (keyShareEntry->ke == NULL) { - WOLFSSL_MSG("Invalid OQS algorithm specified."); - return BAD_FUNC_ARG; - } - 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; + if (keyShareEntry->ke == NULL) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + return BAD_FUNC_ARG; } - ret = kyber_id2type(oqs_group, &type); + /* I am the client, the ciphertext is in keyShareEntry->ke */ + findEccPqc(&ecc_group, &pqc_group, keyShareEntry->group); + + ret = kyber_id2type(pqc_group, &type); if (ret != 0) { - wc_ecc_free(&eccpubkey); - WOLFSSL_MSG("Invalid OQS algorithm specified."); + WOLFSSL_MSG("Invalid PQC algorithm specified."); return BAD_FUNC_ARG; } 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; } @@ -8608,35 +8620,62 @@ static int TLSX_KeyShare_ProcessPqc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) 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; - } + ret = wc_KyberKey_CipherTextSize(kem, &ctSz); } - 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 && ecc_group != 0) { + /* We are performing a hybrid key exchange */ + ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, + DYNAMIC_TYPE_TLSX); + if (ecc_kse == NULL) { + WOLFSSL_MSG("ecc_kse memory allocation failure"); + ret = MEMORY_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; + } + } + if (ret == 0) { + XMEMCPY(ecc_kse->ke, keyShareEntry->ke, ecc_kse->keLen); + + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); + } + else + #endif + { + ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); + } + } + if (ret == 0) { + outlen = ssl->arrays->preMasterSz; + ssSz += ssl->arrays->preMasterSz; + keyShareEntry->key = ecc_kse->key; + + if ((ret == 0) && (ssSz > ENCRYPT_LEN)) { + WOLFSSL_MSG("shared secret is too long."); + ret = LENGTH_ERROR; + } + } + /* ecc_kse->ke is freed in the TLSX_KeyShare_Process methods */ + XFREE(ecc_kse, ssl->heap, DYNAMIC_TYPE_TLSX); } - if (ret == 0) { - ret = wc_KyberKey_CipherTextSize(kem, &ctSz); - } + if (ret == 0) { ret = wc_KyberKey_PrivateKeySize(kem, &privSz); } @@ -8644,62 +8683,17 @@ static int TLSX_KeyShare_ProcessPqc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) ret = wc_KyberKey_DecodePrivateKey(kem, keyShareEntry->privKey, privSz); } if (ret == 0) { - ret = wc_KyberKey_Decapsulate(kem, sharedSecret + outlen, - keyShareEntry->ke + keyShareEntry->keLen - ctSz, ctSz); + ret = wc_KyberKey_Decapsulate(kem, ssl->arrays->preMasterSecret + outlen, + keyShareEntry->ke + keyShareEntry->keLen - ctSz, ctSz); if (ret != 0) { WOLFSSL_MSG("wc_KyberKey decapsulation failure."); ret = BAD_FUNC_ARG; } } - - 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."); - } - } - -#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 *)keyShareEntry->key, ssl->rng); - if (ret != 0) { - WOLFSSL_MSG("Failure to set the ECC private key RNG."); - } - } -#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 = BAD_FUNC_ARG; - } - } - } - if ((ret == 0) && (sharedSecretLen > ENCRYPT_LEN)) { - WOLFSSL_MSG("shared secret is too long."); - ret = LENGTH_ERROR; - } - 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; + ssl->arrays->preMasterSz = ssSz; } - XFREE(sharedSecret, ssl->heap, DYNAMIC_TYPE_SECRET); - - wc_ecc_free(&eccpubkey); wc_KyberKey_Free(kem); return ret; } @@ -9062,41 +9056,32 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, */ int type; KyberKey kem[1]; - byte* sharedSecret = NULL; byte* ciphertext = NULL; int ret = 0; - int oqs_group = 0; + int pqc_group = 0; int ecc_group = 0; KeyShareEntry *ecc_kse = NULL; - ecc_key eccpubkey; word32 outlen = 0; word32 pubSz = 0; word32 ctSz = 0; word32 ssSz = 0; - findEccPqc(&ecc_group, &oqs_group, keyShareEntry->group); - ret = kyber_id2type(oqs_group, &type); + findEccPqc(&ecc_group, &pqc_group, keyShareEntry->group); + ret = kyber_id2type(pqc_group, &type); if (ret != 0) { WOLFSSL_MSG("Invalid Kyber algorithm specified."); 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; } if (ret == 0) { ecc_kse = (KeyShareEntry*)XMALLOC(sizeof(*ecc_kse), ssl->heap, - DYNAMIC_TYPE_TLSX); + DYNAMIC_TYPE_TLSX); if (ecc_kse == NULL) { WOLFSSL_MSG("ecc_kse memory allocation failure"); ret = MEMORY_ERROR; @@ -9109,8 +9094,22 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, if (ret == 0 && ecc_group != 0) { ecc_kse->group = ecc_group; - ret = TLSX_KeyShare_GenEccKey(ssl, ecc_kse); - /* No message, TLSX_KeyShare_GenEccKey() will do it. */ + #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) { @@ -9129,41 +9128,45 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } 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) { + if (ret == 0 && ecc_group != 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."); + XMEMCPY(ecc_kse->ke, data, ecc_kse->keLen); + + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); + } + else + #endif + { + ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); } } - -#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 + outlen = ssl->arrays->preMasterSz; - 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) { WOLFSSL_MSG("Data length mismatch."); ret = BAD_FUNC_ARG; @@ -9171,13 +9174,18 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } } + if (ret == 0 && outlen + ssSz > ENCRYPT_LEN) { + WOLFSSL_MSG("shared secret is too long."); + ret = LENGTH_ERROR; + } + if (ret == 0) { ret = wc_KyberKey_DecodePublicKey(kem, data + ecc_kse->pubKeyLen, pubSz); } if (ret == 0) { ret = wc_KyberKey_Encapsulate(kem, ciphertext + ecc_kse->pubKeyLen, - sharedSecret + outlen, ssl->rng); + ssl->arrays->preMasterSecret + outlen, ssl->rng); if (ret != 0) { WOLFSSL_MSG("wc_KyberKey encapsulation failure."); } @@ -9186,9 +9194,9 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, if (ret == 0) { XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); - keyShareEntry->ke = sharedSecret; - keyShareEntry->keLen = outlen + ssSz; - sharedSecret = NULL; + ssl->arrays->preMasterSz = outlen + ssSz; + keyShareEntry->ke = NULL; + keyShareEntry->keLen = 0; if (ecc_kse->pubKeyLen > 0) XMEMCPY(ciphertext, ecc_kse->pubKey, ecc_kse->pubKeyLen); @@ -9202,9 +9210,7 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); - XFREE(sharedSecret, ssl->heap, DYNAMIC_TYPE_SECRET); XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); - wc_ecc_free(&eccpubkey); wc_KyberKey_Free(kem); return ret; } @@ -9427,14 +9433,25 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) #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: case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_P384_KYBER_LEVEL5: #endif break; #elif defined(HAVE_LIBOQS) @@ -9443,7 +9460,12 @@ static int TLSX_KeyShare_IsSupported(int namedGroup) case WOLFSSL_KYBER_LEVEL5: case WOLFSSL_P256_KYBER_LEVEL1: case WOLFSSL_P384_KYBER_LEVEL3: + case WOLFSSL_P256_KYBER_LEVEL3: case WOLFSSL_P521_KYBER_LEVEL5: + case WOLFSSL_P384_KYBER_LEVEL5: + case WOLFSSL_X25519_KYBER_LEVEL1: + case WOLFSSL_X448_KYBER_LEVEL3: + case WOLFSSL_X25519_KYBER_LEVEL3: { int ret; int id; @@ -9509,14 +9531,25 @@ 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, WOLFSSL_P521_KYBER_LEVEL5, + WOLFSSL_P384_KYBER_LEVEL5, #endif #elif defined(HAVE_LIBOQS) /* These require a runtime call to TLSX_KeyShare_IsSupported to use */ @@ -9525,7 +9558,16 @@ static const word16 preferredGroup[] = { WOLFSSL_KYBER_LEVEL5, WOLFSSL_P256_KYBER_LEVEL1, WOLFSSL_P384_KYBER_LEVEL3, + WOLFSSL_P256_KYBER_LEVEL3, WOLFSSL_P521_KYBER_LEVEL5, + WOLFSSL_P384_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 WOLFSSL_NAMED_GROUP_INVALID }; @@ -9809,7 +9851,8 @@ 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))) continue; #ifdef WOLFSSL_SM2 @@ -9888,7 +9931,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. */ @@ -13132,6 +13175,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) @@ -13140,6 +13188,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) @@ -13148,6 +13209,9 @@ static int TLSX_PopulateSupportedGroups(WOLFSSL* ssl, TLSX** extensions) if (ret == WOLFSSL_SUCCESS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P521_KYBER_LEVEL5, ssl->heap); + if (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_KYBER_LEVEL5, + ssl->heap); #endif #elif defined(HAVE_LIBOQS) ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_KYBER_LEVEL1, ssl->heap); @@ -13163,9 +13227,28 @@ 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 (ret == WOLFSSL_SUCCESS) + ret = TLSX_UseSupportedCurve(extensions, WOLFSSL_P384_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_HAVE_KYBER */ diff --git a/tests/include.am b/tests/include.am index 5ed4fe40dc..1bb9fa38ab 100644 --- a/tests/include.am +++ b/tests/include.am @@ -27,11 +27,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 7328789f46..8d65cfaf34 100644 --- a/tests/suites.c +++ b/tests/suites.c @@ -969,9 +969,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) { @@ -980,29 +979,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])); @@ -1013,30 +989,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) { @@ -1046,7 +1019,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 6a4bfac084..0000000000 --- a/tests/test-dtls13-pq-2.conf +++ /dev/null @@ -1,13 +0,0 @@ -# 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..3696286078 100644 --- a/tests/test-dtls13-pq-frag.conf +++ b/tests/test-dtls13-pq-frag.conf @@ -21,4 +21,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..88ee706764 --- /dev/null +++ b/tests/test-dtls13-pq-hybrid-frag.conf @@ -0,0 +1,71 @@ +# 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 P384_KYBER_LEVEL5 + +# client DTLSv1.3 with post-quantum hybrid group +-u +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_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..64332b5dd0 --- /dev/null +++ b/tests/test-dtls13-pq-hybrid.conf @@ -0,0 +1,25 @@ +# 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-tls13-pq-2.conf b/tests/test-tls13-pq-2.conf deleted file mode 100644 index ff09d72a71..0000000000 --- a/tests/test-tls13-pq-2.conf +++ /dev/null @@ -1,29 +0,0 @@ -# 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..3d98f94c45 --- /dev/null +++ b/tests/test-tls13-pq-hybrid.conf @@ -0,0 +1,79 @@ +# 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 P384_KYBER_LEVEL5 + +# client TLSv1.3 with post-quantum hybrid group +-v 4 +-l TLS13-AES256-GCM-SHA384 +--pqc P384_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 7ce0436355..52c03ac501 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1862,9 +1862,8 @@ enum Misc { #define WOLFSSL_NAMED_GROUP_IS_FFHDE(group) \ (MIN_FFHDE_GROUP <= (group) && (group) <= MAX_FFHDE_GROUP) #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); +#define WOLFSSL_NAMED_GROUP_IS_PQC(group) NamedGroupIsPqc(group) #else #define WOLFSSL_NAMED_GROUP_IS_PQC(group) ((void)(group), 0) #endif /* WOLFSSL_HAVE_KYBER */ diff --git a/wolfssl/ssl.h b/wolfssl/ssl.h index f7cf3aa15e..5942dd50a5 100644 --- a/wolfssl/ssl.h +++ b/wolfssl/ssl.h @@ -4113,51 +4113,54 @@ enum { WOLFSSL_FFDHE_8192 = 260, #ifdef HAVE_PQC - /* These group numbers were taken from OQS's openssl provider, see: + +#ifdef WOLFSSL_KYBER_ORIGINAL + /* Old code points to keep compatibility with Kyber Round 3. + * To be removed in the future. + * 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. */ - -#ifndef WOLFSSL_ML_KEM - WOLFSSL_PQC_MIN = 570, - WOLFSSL_PQC_SIMPLE_MIN = 570, + * oqs-kem-info.md + */ WOLFSSL_KYBER_LEVEL1 = 570, /* KYBER_512 */ WOLFSSL_KYBER_LEVEL3 = 572, /* KYBER_768 */ WOLFSSL_KYBER_LEVEL5 = 573, /* KYBER_1024 */ - WOLFSSL_PQC_SIMPLE_MAX = 573, - WOLFSSL_PQC_HYBRID_MIN = 12090, WOLFSSL_P256_KYBER_LEVEL1 = 12090, WOLFSSL_P384_KYBER_LEVEL3 = 12092, WOLFSSL_P521_KYBER_LEVEL5 = 12093, - WOLFSSL_PQC_HYBRID_MAX = 12093, - WOLFSSL_PQC_MAX = 12093, + WOLFSSL_P384_KYBER_LEVEL5 = 12094, /* Not defined in OQS! */ + WOLFSSL_X25519_KYBER_LEVEL1 = 12089, + WOLFSSL_X448_KYBER_LEVEL3 = 12176, + WOLFSSL_X25519_KYBER_LEVEL3 = 25497, + WOLFSSL_P256_KYBER_LEVEL3 = 25498, + #else - WOLFSSL_PQC_MIN = 583, - WOLFSSL_PQC_SIMPLE_MIN = 583, - WOLFSSL_KYBER_LEVEL1 = 583, /* ML-KEM 512 */ - WOLFSSL_KYBER_LEVEL3 = 584, /* ML-KEM 768 */ - WOLFSSL_KYBER_LEVEL5 = 585, /* ML-KEM 1024 */ - WOLFSSL_PQC_SIMPLE_MAX = 585, - - WOLFSSL_PQC_HYBRID_MIN = 12103, - WOLFSSL_P256_KYBER_LEVEL1 = 12103, - WOLFSSL_P384_KYBER_LEVEL3 = 12104, - WOLFSSL_P521_KYBER_LEVEL5 = 12105, - WOLFSSL_PQC_HYBRID_MAX = 12105, - WOLFSSL_PQC_MAX = 12105, -#endif /* WOLFSSL_ML_KEM */ + /* 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 = 586, /* ML-KEM 512 */ + WOLFSSL_KYBER_LEVEL3 = 1896, /* ML-KEM 768 */ + WOLFSSL_KYBER_LEVEL5 = 4132, /* ML-KEM 1024 */ + + /* Taken from draft-kwiatkowski-tls-ecdhe-mlkem. see: + * https://github.com/post-quantum-cryptography/ + * draft-kwiatkowski-tls-ecdhe-mlkem/ + */ + WOLFSSL_P256_KYBER_LEVEL3 = 4587, + WOLFSSL_X25519_KYBER_LEVEL3 = 4588, + WOLFSSL_P384_KYBER_LEVEL5 = 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_KYBER_LEVEL1 = 12107, + WOLFSSL_P384_KYBER_LEVEL3 = 12108, + WOLFSSL_P521_KYBER_LEVEL5 = 12109, + WOLFSSL_X25519_KYBER_LEVEL1 = 12214, + WOLFSSL_X448_KYBER_LEVEL3 = 12215, +#endif /* WOLFSSL_KYBER_ORIGINAL */ #endif /* HAVE_PQC */ }; From 37d186f8a845b9676da72366ae7e4f08200cc15d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Thu, 26 Sep 2024 16:43:17 +0200 Subject: [PATCH 2/3] Split PQC hybrid KEX from PQC-only KEX --- src/internal.c | 10 + src/tls.c | 829 +++++++++++++++++++++++++++++++++------------ src/tls13.c | 3 +- wolfssl/internal.h | 2 + 4 files changed, 635 insertions(+), 209 deletions(-) diff --git a/src/internal.c b/src/internal.c index 49098c22ae..519f1aa526 100644 --- a/src/internal.c +++ b/src/internal.c @@ -34154,6 +34154,16 @@ static int DoSessionTicket(WOLFSSL* ssl, const byte* input, word32* inOutIdx, case WOLFSSL_KYBER_LEVEL1: case WOLFSSL_KYBER_LEVEL3: case WOLFSSL_KYBER_LEVEL5: + return 1; + default: + return 0; + } + } + + /* Returns 1 when the given group is a PQC hybrid group, 0 otherwise. */ + int NamedGroupIsPqcHybrid(int group) + { + switch (group) { case WOLFSSL_P256_KYBER_LEVEL3: case WOLFSSL_X25519_KYBER_LEVEL3: case WOLFSSL_P384_KYBER_LEVEL5: diff --git a/src/tls.c b/src/tls.c index 096d4f544e..5b86005962 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7768,6 +7768,115 @@ static int kyber_id2type(int id, int *type) return ret; } +/* 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_GenPqcKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) +{ + int ret = 0; + KyberKey* kemKey = (KyberKey*)kse->key; + + /* 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; + } + + 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; + } + + /* Make a PQC Kyber key. */ + if (ret == 0) { + 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; + } + if (ret == 0) { + ret = wc_KyberKey_Init(type, kemKey, ssl->heap, ssl->devId); + if (ret != 0) { + WOLFSSL_MSG("Failed to initialize Kyber Key."); + } + } + } + + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(kemKey, &kse->privKeyLen); + } + if (ret == 0) { + ret = wc_KyberKey_PublicKeySize(kemKey, &kse->pubKeyLen); + } + + if (ret == 0) { + 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) { + kse->privKey = (byte*)XMALLOC(kse->privKeyLen, ssl->heap, + DYNAMIC_TYPE_PRIVATE_KEY); + if (kse->privKey == NULL) { + WOLFSSL_MSG("privkey memory allocation failure"); + ret = MEMORY_ERROR; + } + } + + if (ret == 0) { + ret = wc_KyberKey_MakeKey(kemKey, ssl->rng); + if (ret != 0) { + WOLFSSL_MSG("Kyber keygen failure"); + } + } + if (ret == 0) { + ret = wc_KyberKey_EncodePublicKey(kemKey, kse->pubKey, + kse->pubKeyLen); + } + if (ret == 0) { + ret = wc_KyberKey_EncodePrivateKey(kemKey, kse->privKey, + kse->privKeyLen); + } + +#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. */ + XFREE(kse->pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); + kse->pubKey = NULL; + XFREE(kse->privKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + kse->privKey = NULL; + } + + wc_KyberKey_Free(kemKey); + XFREE(kemKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + kse->key = NULL; + + 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; @@ -7798,55 +7907,41 @@ static const PqcHybridMapping pqc_hybrid_mapping[] = { {.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. */ +/* Map an ecc-pqc hybrid group into its ecc group and pqc kem group. */ static void findEccPqc(int *ecc, int *pqc, int group) { int i; - if (pqc == NULL) { - return; - } - *pqc = 0; - if (ecc != NULL) { + if (pqc != NULL) + *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) { + if (pqc != NULL) + *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; - } } -/* Create a key share entry using pqc parameters group. - * Generates a key pair. +/* 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_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) +static int TLSX_KeyShare_GenPqcHybridKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) { int ret = 0; - int type = 0; - KyberKey kem[1]; - byte* pubKey = NULL; - byte* privKey = NULL; KeyShareEntry *ecc_kse = NULL; + KeyShareEntry *pqc_kse = NULL; int pqc_group = 0; int ecc_group = 0; - word32 privSz = 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 @@ -7855,39 +7950,30 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) return ret; } + /* Determine the ECC and PQC group of the hybrid combination */ findEccPqc(&ecc_group, &pqc_group, kse->group); - ret = kyber_id2type(pqc_group, &type); - if (ret == WC_NO_ERR_TRACE(NOT_COMPILED_IN)) { - WOLFSSL_MSG("Invalid Kyber algorithm specified."); + if (ecc_group == 0 || pqc_group == 0) { + WOLFSSL_MSG("Invalid hybrid group"); ret = BAD_FUNC_ARG; } - if (ret == 0) { - ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); - if (ret != 0) { - WOLFSSL_MSG("Failed to initialize Kyber Key."); - } - } - 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"); + 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) { XMEMSET(ecc_kse, 0, sizeof(*ecc_kse)); - - ret = wc_KyberKey_PrivateKeySize(kem, &privSz); - } - if (ret == 0) { - ret = wc_KyberKey_PublicKeySize(kem, &pubSz); + XMEMSET(pqc_kse, 0, sizeof(*pqc_kse)); } - if (ret == 0 && ecc_group != 0) { + /* Generate ECC key share part */ + if (ret == 0) { ecc_kse->group = ecc_group; #ifdef HAVE_CURVE25519 if (ecc_group == WOLFSSL_ECC_X25519) { @@ -7907,52 +7993,42 @@ static int TLSX_KeyShare_GenPqcKey(WOLFSSL *ssl, KeyShareEntry* kse) /* No error message, TLSX_KeyShare_GenKey will do it. */ } + /* Generate PQC key share part */ 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"); - ret = MEMORY_ERROR; - } + pqc_kse->group = pqc_group; + ret = TLSX_KeyShare_GenPqcKeyClient(ssl, pqc_kse); + /* No error message, TLSX_KeyShare_GenPqcKey will do it. */ } + /* Create combined public key */ if (ret == 0) { - privKey = (byte*)XMALLOC(privSz, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); - if (privKey == NULL) { - WOLFSSL_MSG("privkey memory allocation failure"); + 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; } } - if (ret == 0) { - ret = wc_KyberKey_MakeKey(kem, ssl->rng); - if (ret != 0) { - WOLFSSL_MSG("Kyber keygen failure"); - } - } - if (ret == 0) { - ret = wc_KyberKey_EncodePublicKey(kem, pubKey + ecc_kse->pubKeyLen, - pubSz); - } - if (ret == 0) { - ret = wc_KyberKey_EncodePrivateKey(kem, privKey, privSz); + 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) { - 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 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. Typically do not need the private - * key size, but will need to zero it out upon freeing. */ - kse->privKey = privKey; - privKey = NULL; - kse->privKeyLen = privSz; + /* PQC private key is encoded as byte stream */ + kse->privKey = pqc_kse->privKey; + kse->privKeyLen = pqc_kse->privKeyLen; + pqc_kse->privKey = NULL; + /* ECC private key is a pointer to ecc_key object */ kse->key = ecc_kse->key; + kse->keyLen = ecc_kse->keyLen; ecc_kse->key = NULL; } @@ -7961,10 +8037,8 @@ 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); - XFREE(privKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); return ret; } @@ -7987,7 +8061,9 @@ int TLSX_KeyShare_GenKey(WOLFSSL *ssl, KeyShareEntry *kse) ret = TLSX_KeyShare_GenX448Key(ssl, kse); #ifdef WOLFSSL_HAVE_KYBER 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); @@ -8025,15 +8101,30 @@ 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); - } - XFREE(current->pubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY); - current->pubKey = NULL; - if (current->privKey != NULL) { + if (current->privKey != NULL) ForceZero(current->privKey, current->privKeyLen); - XFREE(current->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); - current->privKey = NULL; + } + else if (WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(current->group)) { + int ecc_group = 0; + findEccPqc(&ecc_group, NULL, current->group); + + /* Free PQC private key */ + if (current->privKey != NULL) + ForceZero(current->privKey, current->privKeyLen); + + /* Free ECC private key */ + if (current->group == WOLFSSL_ECC_X25519) { + #ifdef HAVE_CURVE25519 + wc_curve25519_free((curve25519_key*)current->key); + #endif + } + else if (current->group == WOLFSSL_ECC_X448) { + #ifdef HAVE_CURVE448 + wc_curve448_free((curve448_key*)current->key); + #endif + } + else { + wc_ecc_free((ecc_key*)current->key); } } #endif @@ -8043,7 +8134,8 @@ 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(HAVE_FALCON) || defined(HAVE_DILITHIUM)) && \ + (!defined(NO_CERTS) || !defined(NO_PSK)) XFREE(current->privKey, heap, DYNAMIC_TYPE_PRIVATE_KEY); #endif XFREE(current->pubKey, heap, DYNAMIC_TYPE_PUBLIC_KEY); @@ -8338,6 +8430,8 @@ static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, #else (void)ssl; (void)keyShareEntry; + (void)ssOutput; + (void)ssOutSz; ret = PEER_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); @@ -8418,6 +8512,8 @@ static int TLSX_KeyShare_ProcessX448(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) #else (void)ssl; (void)keyShareEntry; + (void)ssOutput; + (void)ssOutSz; ret = PEER_KEY_ERROR; WOLFSSL_ERROR_VERBOSE(ret); @@ -8562,6 +8658,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); @@ -8575,17 +8673,18 @@ static int TLSX_KeyShare_ProcessEcc(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_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; - KyberKey kem[1]; - int pqc_group = 0; - int ecc_group = 0; - KeyShareEntry *ecc_kse = NULL; - word32 outlen = 0; + KyberKey* kemKey = (KyberKey*)keyShareEntry->key; word32 privSz = 0; word32 ctSz = 0; word32 ssSz = 0; @@ -8600,101 +8699,264 @@ static int TLSX_KeyShare_ProcessPqc(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) WOLFSSL_MSG("Invalid PQC algorithm specified."); return BAD_FUNC_ARG; } + if (ssOutSz == NULL) + return BAD_FUNC_ARG; /* I am the client, the ciphertext is in keyShareEntry->ke */ - findEccPqc(&ecc_group, &pqc_group, keyShareEntry->group); - ret = kyber_id2type(pqc_group, &type); - if (ret != 0) { - WOLFSSL_MSG("Invalid PQC algorithm specified."); - 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"); + } + } } - ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); - if (ret != 0) { - WOLFSSL_MSG("Error creating Kyber KEM"); - return MEMORY_E; + if (ret == 0) { + ret = wc_KyberKey_SharedSecretSize(kemKey, &ssSz); + } + if (ret == 0) { + ret = wc_KyberKey_CipherTextSize(kemKey, &ctSz); + if (ret == 0 && ctSz != keyShareEntry->keLen) { + WOLFSSL_MSG("Invalid ciphertext size."); + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + ret = wc_KyberKey_PrivateKeySize(kemKey, &privSz); + if (ret == 0 && privSz != keyShareEntry->privKeyLen) { + WOLFSSL_MSG("Invalid private key size."); + ret = BAD_FUNC_ARG; + } } if (ret == 0) { - ret = wc_KyberKey_SharedSecretSize(kem, &ssSz); + ret = wc_KyberKey_DecodePrivateKey(kemKey, keyShareEntry->privKey, + privSz); } + if (ret == 0) { - ret = wc_KyberKey_CipherTextSize(kem, &ctSz); + ret = wc_KyberKey_Decapsulate(kemKey, ssOutput, + keyShareEntry->ke, ctSz); + if (ret != 0) { + WOLFSSL_MSG("wc_KyberKey decapsulation failure."); + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + *ssOutSz = ssSz; } - if (ret == 0 && ecc_group != 0) { - /* We are performing a hybrid key exchange */ + wc_KyberKey_Free(kemKey); + XFREE(kemKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + keyShareEntry->key = NULL; + + ForceZero(keyShareEntry->privKey, keyShareEntry->privKeyLen); + XFREE(keyShareEntry->privKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + keyShareEntry->privKey = 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 type = 0; + int pqc_group = 0; + int ecc_group = 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, 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"); + 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 separate the KEM ciphertext from the ECDH public + * key. */ + if (ret == 0) { + XMEMSET(pqc_kse, 0, sizeof(*pqc_kse)); + pqc_kse->group = pqc_group; + pqc_kse->privKey = keyShareEntry->privKey; + pqc_kse->privKeyLen = keyShareEntry->privKeyLen; + + /* 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) { - 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; - } + ret = kyber_id2type(pqc_kse->group, &type); + } + if (ret != 0) { + WOLFSSL_MSG("Invalid PQC algorithm specified."); + ret = BAD_FUNC_ARG; } if (ret == 0) { - XMEMCPY(ecc_kse->ke, keyShareEntry->ke, ecc_kse->keLen); - - #ifdef HAVE_CURVE25519 - if (ecc_group == WOLFSSL_ECC_X25519) { - ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); - } - else - #endif - #ifdef HAVE_CURVE448 - if (ecc_group == WOLFSSL_ECC_X448) { - ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); - } - else - #endif - { - ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); + 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) { - outlen = ssl->arrays->preMasterSz; - ssSz += ssl->arrays->preMasterSz; - keyShareEntry->key = ecc_kse->key; - - if ((ret == 0) && (ssSz > ENCRYPT_LEN)) { - WOLFSSL_MSG("shared secret is too long."); - ret = LENGTH_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) { + 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; + } + if (ret == 0) { + XMEMCPY(pqc_kse->ke, + keyShareEntry->ke + keyShareEntry->keLen - ctSz, + ctSz); } } - /* ecc_kse->ke is freed in the TLSX_KeyShare_Process methods */ - XFREE(ecc_kse, ssl->heap, DYNAMIC_TYPE_TLSX); } if (ret == 0) { - ret = wc_KyberKey_PrivateKeySize(kem, &privSz); + 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; + } + if (ret == 0) { + XMEMCPY(ecc_kse->ke, keyShareEntry->ke, ecc_kse->keLen); + } } + + /* Process ECDH key share part */ if (ret == 0) { - ret = wc_KyberKey_DecodePrivateKey(kem, keyShareEntry->privKey, privSz); + #ifdef HAVE_CURVE25519 + if (ecc_group == WOLFSSL_ECC_X25519) { + ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); + } + else + #endif + #ifdef HAVE_CURVE448 + if (ecc_group == WOLFSSL_ECC_X448) { + ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); + } + else + #endif + { + ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); + } } if (ret == 0) { - ret = wc_KyberKey_Decapsulate(kem, ssl->arrays->preMasterSecret + outlen, - keyShareEntry->ke + keyShareEntry->keLen - ctSz, ctSz); - if (ret != 0) { - WOLFSSL_MSG("wc_KyberKey decapsulation failure."); - ret = BAD_FUNC_ARG; + ssSzEcc = ssl->arrays->preMasterSz; + 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 */ if (ret == 0) { - ssl->arrays->preMasterSz = ssSz; + ret = TLSX_KeyShare_ProcessPqcClient_ex(ssl, pqc_kse, + ssl->arrays->preMasterSecret + ssSzEcc, &ssSzPqc); } + if (ret == 0) { + ssl->arrays->preMasterSz = ssSzEcc + ssSzPqc; + + keyShareEntry->privKey = pqc_kse->privKey; + keyShareEntry->privKeyLen = pqc_kse->privKeyLen; + } + + XFREE(ecc_kse, ssl->heap, DYNAMIC_TYPE_TLSX); + XFREE(pqc_kse, ssl->heap, DYNAMIC_TYPE_TLSX); - wc_KyberKey_Free(kem); return ret; } #endif /* WOLFSSL_HAVE_KYBER */ @@ -8725,7 +8987,9 @@ static int TLSX_KeyShare_Process(WOLFSSL* ssl, KeyShareEntry* keyShareEntry) ret = TLSX_KeyShare_ProcessX448(ssl, keyShareEntry); #ifdef WOLFSSL_HAVE_KYBER 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); @@ -9046,7 +9310,116 @@ static int TLSX_KeyShare_New(KeyShareEntry** list, int group, void *heap, } #ifdef WOLFSSL_HAVE_KYBER -static int server_generate_pqc_ciphertext(WOLFSSL* ssl, +/* 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 (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); + + *ssOutSz = ssSz; + keyShareEntry->ke = NULL; + keyShareEntry->keLen = 0; + + keyShareEntry->pubKey = ciphertext; + keyShareEntry->pubKeyLen = ctSz; + ciphertext = NULL; + + /* Set namedGroup so wolfSSL_get_curve_name() can function properly on + * the server side. */ + ssl->namedGroup = keyShareEntry->group; + } + + XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); + + wc_KyberKey_Free(kemKey); + XFREE(kemKey, ssl->heap, DYNAMIC_TYPE_PRIVATE_KEY); + keyShareEntry->key = NULL; + return ret; +} + +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 @@ -9055,44 +9428,80 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, * key exchange parlance. That's why it is being assigned to pubKey. */ int type; - KyberKey kem[1]; byte* ciphertext = NULL; int ret = 0; int pqc_group = 0; int ecc_group = 0; KeyShareEntry *ecc_kse = NULL; - word32 outlen = 0; + KeyShareEntry *pqc_kse = NULL; word32 pubSz = 0; word32 ctSz = 0; - word32 ssSz = 0; + word32 ssSzPqc = 0; + word32 ssSzEcc = 0; + /* Determine the ECC and PQC group of the hybrid combination */ findEccPqc(&ecc_group, &pqc_group, keyShareEntry->group); - ret = kyber_id2type(pqc_group, &type); - if (ret != 0) { - WOLFSSL_MSG("Invalid Kyber algorithm specified."); - return BAD_FUNC_ARG; - } - - ret = wc_KyberKey_Init(type, kem, ssl->heap, ssl->devId); - if (ret != 0) { - WOLFSSL_MSG("Error creating Kyber KEM"); - return MEMORY_E; + 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; #ifdef HAVE_CURVE25519 if (ecc_group == WOLFSSL_ECC_X25519) { @@ -9112,21 +9521,13 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, /* No error message, TLSX_KeyShare_GenKey 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); - } - if (ret == 0 && len != pubSz + ecc_kse->pubKeyLen) { WOLFSSL_MSG("Invalid public key."); ret = BAD_FUNC_ARG; } + /* Allocate buffer for the concatenated client key share data + * (PQC KEM ciphertext + ECDH public key) */ if (ret == 0) { ciphertext = (byte*)XMALLOC(ecc_kse->pubKeyLen + ctSz, ssl->heap, DYNAMIC_TYPE_TLSX); @@ -9137,7 +9538,8 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } } - if (ret == 0 && ecc_group != 0) { + /* Process ECDH key share part */ + if (ret == 0) { ecc_kse->keLen = len - pubSz; ecc_kse->ke = (byte*)XMALLOC(ecc_kse->keLen, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); @@ -9165,41 +9567,38 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } } if (ret == 0) { - outlen = ssl->arrays->preMasterSz; + ssSzEcc = ssl->arrays->preMasterSz; - if (outlen != ecc_kse->keyLen) { + if (ssSzEcc != ecc_kse->keyLen) { WOLFSSL_MSG("Data length mismatch."); ret = BAD_FUNC_ARG; } } } - if (ret == 0 && outlen + ssSz > ENCRYPT_LEN) { + if (ret == 0 && ssSzEcc + ssSzPqc > ENCRYPT_LEN) { WOLFSSL_MSG("shared secret is too long."); ret = LENGTH_ERROR; } + /* Process the PQC KEM key share part */ if (ret == 0) { - ret = wc_KyberKey_DecodePublicKey(kem, data + ecc_kse->pubKeyLen, - pubSz); - } - if (ret == 0) { - ret = wc_KyberKey_Encapsulate(kem, ciphertext + ecc_kse->pubKeyLen, - ssl->arrays->preMasterSecret + outlen, ssl->rng); - if (ret != 0) { - WOLFSSL_MSG("wc_KyberKey encapsulation failure."); - } + ret = TLSX_KeyShare_HandlePqcKeyServer(ssl, pqc_kse, + data + ecc_kse->keLen, pubSz, + ssl->arrays->preMasterSecret + ssSzEcc, &ssSzPqc); } if (ret == 0) { XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); - ssl->arrays->preMasterSz = outlen + ssSz; + ssl->arrays->preMasterSz = ssSzEcc + ssSzPqc; keyShareEntry->ke = NULL; keyShareEntry->keLen = 0; if (ecc_kse->pubKeyLen > 0) XMEMCPY(ciphertext, ecc_kse->pubKey, ecc_kse->pubKeyLen); + if (pqc_kse->pubKeyLen > 0) + XMEMCPY(ciphertext + ecc_kse->pubKeyLen, pqc_kse->pubKey, ctSz); keyShareEntry->pubKey = ciphertext; keyShareEntry->pubKeyLen = (word32)(ecc_kse->pubKeyLen + ctSz); ciphertext = NULL; @@ -9210,8 +9609,8 @@ static int server_generate_pqc_ciphertext(WOLFSSL* ssl, } TLSX_KeyShare_FreeAll(ecc_kse, ssl->heap); + TLSX_KeyShare_FreeAll(pqc_kse, ssl->heap); XFREE(ciphertext, ssl->heap, DYNAMIC_TYPE_TLSX); - wc_KyberKey_Free(kem); return ret; } #endif /* WOLFSSL_HAVE_KYBER */ @@ -9264,10 +9663,21 @@ int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data, #ifdef WOLFSSL_HAVE_KYBER - 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; } @@ -9852,7 +10262,8 @@ 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) && - (!WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group))) + (!WOLFSSL_NAMED_GROUP_IS_PQC(clientKSE->group)) && + (!WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(clientKSE->group))) continue; #ifdef WOLFSSL_SM2 @@ -9876,7 +10287,8 @@ int TLSX_KeyShare_Choose(const WOLFSSL *ssl, TLSX* extensions, /* 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; } @@ -9941,7 +10353,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; diff --git a/src/tls13.c b/src/tls13.c index a4d3aab70d..31c07d44af 100644 --- a/src/tls13.c +++ b/src/tls13.c @@ -13437,7 +13437,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/wolfssl/internal.h b/wolfssl/internal.h index 52c03ac501..13f48b36a8 100644 --- a/wolfssl/internal.h +++ b/wolfssl/internal.h @@ -1863,7 +1863,9 @@ enum Misc { (MIN_FFHDE_GROUP <= (group) && (group) <= MAX_FFHDE_GROUP) #ifdef WOLFSSL_HAVE_KYBER 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) #endif /* WOLFSSL_HAVE_KYBER */ From 907077d3dd4e2f0e6ce68421196f1290ddfaa936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Fri, 27 Sep 2024 11:04:59 +0200 Subject: [PATCH 3/3] Switch order for X25519/X448 based PQC hybrid KEXs --- src/tls.c | 275 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 229 insertions(+), 46 deletions(-) diff --git a/src/tls.c b/src/tls.c index 5b86005962..52cbbe4833 100644 --- a/src/tls.c +++ b/src/tls.c @@ -7997,10 +7997,10 @@ static int TLSX_KeyShare_GenPqcHybridKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) if (ret == 0) { pqc_kse->group = pqc_group; ret = TLSX_KeyShare_GenPqcKeyClient(ssl, pqc_kse); - /* No error message, TLSX_KeyShare_GenPqcKey will do it. */ + /* No error message, TLSX_KeyShare_GenPqcKeyClient will do it. */ } - /* Create combined public key */ + /* Allocate memory for combined public key */ if (ret == 0) { kse->pubKey = (byte*)XMALLOC(ecc_kse->pubKeyLen + pqc_kse->pubKeyLen, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY); @@ -8009,10 +8009,26 @@ static int TLSX_KeyShare_GenPqcHybridKeyClient(WOLFSSL *ssl, KeyShareEntry* kse) ret = MEMORY_ERROR; } } + + /* Create combined public key. + * For hybrids with SECP-based ECC, we put the ECC public key first. + * For X25519/X448 hybrids, the PQC KEM public key is fist. This is done + * to always have a FIPS approved algorithm first. */ if (ret == 0) { - XMEMCPY(kse->pubKey, ecc_kse->pubKey, ecc_kse->pubKeyLen); - XMEMCPY(kse->pubKey + ecc_kse->pubKeyLen, pqc_kse->pubKey, - pqc_kse->pubKeyLen); + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || ecc_group == WOLFSSL_ECC_X448) { + XMEMCPY(kse->pubKey, pqc_kse->pubKey, pqc_kse->pubKeyLen); + XMEMCPY(kse->pubKey + pqc_kse->pubKeyLen, ecc_kse->pubKey, + ecc_kse->pubKeyLen); + } + else + #endif + { + 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; } @@ -8361,10 +8377,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; @@ -8415,9 +8436,7 @@ static int TLSX_KeyShare_ProcessX25519(WOLFSSL* ssl, ssl->ecdhCurveOID = ECC_X25519_OID; 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); @@ -8440,13 +8459,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; @@ -8497,9 +8536,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); @@ -8522,13 +8559,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 @@ -8628,9 +8683,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; @@ -8668,6 +8721,18 @@ 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); +} + #ifdef WOLFSSL_HAVE_KYBER /* Process the Kyber key share extension on the client side. * @@ -8810,7 +8875,8 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, KeyShareEntry *ecc_kse = NULL; word32 ctSz = 0; word32 ssSzPqc = 0; - word32 ssSzEcc = 0; + word32 ssSzEcc = ssl->arrays->preMasterSz; // We need the buffer size for + // the logic to work downwards. if (ssl->options.side == WOLFSSL_SERVER_END) { /* I am the server, the shared secret has already been generated and @@ -8881,6 +8947,10 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, 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; @@ -8890,10 +8960,21 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, WOLFSSL_MSG("pqc_kse memory allocation failure"); ret = MEMORY_ERROR; } + /* Copy the PQC KEM ciphertext. For SECP-based hybrids, the KEM + * ciphertext comes after the ECDH public key. For X25519/X448 + * hybrids, the KEM part comes first. */ if (ret == 0) { - XMEMCPY(pqc_kse->ke, - keyShareEntry->ke + keyShareEntry->keLen - ctSz, - ctSz); + int offset = keyShareEntry->keLen - ctSz; + + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || + ecc_group == WOLFSSL_ECC_X448) { + offset = 0; + } + #endif + + XMEMCPY(pqc_kse->ke, keyShareEntry->ke + offset, ctSz); } } } @@ -8909,31 +8990,58 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, WOLFSSL_MSG("ecc_kse memory allocation failure"); ret = MEMORY_ERROR; } + /* Copy the ECDH public key. */ if (ret == 0) { - XMEMCPY(ecc_kse->ke, keyShareEntry->ke, ecc_kse->keLen); + int offset = 0; + + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || + ecc_group == WOLFSSL_ECC_X448) { + offset = ctSz; + } + #endif + + XMEMCPY(ecc_kse->ke, keyShareEntry->ke + offset, ecc_kse->keLen); } } - /* Process ECDH key share part */ + /* Process ECDH key share part. The generated shared secret is directly + * stored in the ssl->arrays->preMasterSecret buffer. For SECP-based + * hybrids, the ECDH part is stored first. For X25519/X448 based hybrids, + * the PQC KEM part comes first. This is due to the requirement to always + * have material of a FIPS approved algorithm first. */ if (ret == 0) { #ifdef HAVE_CURVE25519 if (ecc_group == WOLFSSL_ECC_X25519) { - ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); + #if defined(WOLFSSL_KYBER_ORIGINAL) + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); + #else + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssSzPqc, &ssSzEcc); + #endif } else #endif #ifdef HAVE_CURVE448 if (ecc_group == WOLFSSL_ECC_X448) { - ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); + #if defined(WOLFSSL_KYBER_ORIGINAL) + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); + #else + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssSzPqc, &ssSzEcc); + #endif } else #endif { - ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); + ret = TLSX_KeyShare_ProcessEcc_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); } } if (ret == 0) { - ssSzEcc = ssl->arrays->preMasterSz; keyShareEntry->key = ecc_kse->key; if ((ret == 0) && ((ssSzEcc + ssSzPqc) > ENCRYPT_LEN)) { @@ -8942,10 +9050,20 @@ static int TLSX_KeyShare_ProcessPqcHybridClient(WOLFSSL* ssl, } } - /* Process PQC Kem key share part */ + /* Process PQC KEM key share part. For SECP-based hybrids, the KEM + * shared secret goes behind the ECDH part. For X25519/X448 hybrids, + * the KEM part goes first. */ if (ret == 0) { + int offset = ssSzEcc; + + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || ecc_group == WOLFSSL_ECC_X448) + offset = 0; + #endif + ret = TLSX_KeyShare_ProcessPqcClient_ex(ssl, pqc_kse, - ssl->arrays->preMasterSecret + ssSzEcc, &ssSzPqc); + ssl->arrays->preMasterSecret + offset, &ssSzPqc); } if (ret == 0) { ssl->arrays->preMasterSz = ssSzEcc + ssSzPqc; @@ -9041,8 +9159,8 @@ static int TLSX_KeyShareEntry_Parse(const WOLFSSL* ssl, const byte* input, #ifdef WOLFSSL_HAVE_KYBER if (WOLFSSL_NAMED_GROUP_IS_PQC(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.*/ + /* For pure KEMs (no hybrid), the public key is not stored. Casting + * away const because we know for KEMs, it will be read-only.*/ ke = (byte *)&input[offset]; } else #endif @@ -9437,7 +9555,8 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, word32 pubSz = 0; word32 ctSz = 0; word32 ssSzPqc = 0; - word32 ssSzEcc = 0; + word32 ssSzEcc = ssl->arrays->preMasterSz; // We need the buffer size for + // the logic to work downwards. /* Determine the ECC and PQC group of the hybrid combination */ findEccPqc(&ecc_group, &pqc_group, keyShareEntry->group); @@ -9538,7 +9657,11 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, } } - /* Process ECDH key share part */ + /* Process ECDH key share part. The generated shared secret is directly + * stored in the ssl->arrays->preMasterSecret buffer. For SECP-based + * hybrids, the ECDH part is stored first. For X25519/X448 based hybrids, + * the PQC KEM part comes first. This is due to the requirement to always + * have material of a FIPS approved algorithm first. */ if (ret == 0) { ecc_kse->keLen = len - pubSz; ecc_kse->ke = (byte*)XMALLOC(ecc_kse->keLen, ssl->heap, @@ -9548,27 +9671,48 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, ret = MEMORY_ERROR; } if (ret == 0) { - XMEMCPY(ecc_kse->ke, data, ecc_kse->keLen); + int offset = 0; + + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_kse->group == WOLFSSL_ECC_X25519 || + ecc_group == WOLFSSL_ECC_X448) { + offset = pubSz; + } + #endif + + XMEMCPY(ecc_kse->ke, data + offset, ecc_kse->keLen); #ifdef HAVE_CURVE25519 if (ecc_group == WOLFSSL_ECC_X25519) { - ret = TLSX_KeyShare_ProcessX25519(ssl, ecc_kse); + #if defined(WOLFSSL_KYBER_ORIGINAL) + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); + #else + ret = TLSX_KeyShare_ProcessX25519_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssSzPqc, &ssSzEcc); + #endif } else #endif #ifdef HAVE_CURVE448 if (ecc_group == WOLFSSL_ECC_X448) { - ret = TLSX_KeyShare_ProcessX448(ssl, ecc_kse); + #if defined(WOLFSSL_KYBER_ORIGINAL) + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); + #else + ret = TLSX_KeyShare_ProcessX448_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret + ssSzPqc, &ssSzEcc); + #endif } else #endif { - ret = TLSX_KeyShare_ProcessEcc(ssl, ecc_kse); + ret = TLSX_KeyShare_ProcessEcc_ex(ssl, ecc_kse, + ssl->arrays->preMasterSecret, &ssSzEcc); } } if (ret == 0) { - ssSzEcc = ssl->arrays->preMasterSz; - if (ssSzEcc != ecc_kse->keyLen) { WOLFSSL_MSG("Data length mismatch."); ret = BAD_FUNC_ARG; @@ -9581,11 +9725,24 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, ret = LENGTH_ERROR; } - /* Process the PQC KEM key share part */ + /* Process PQC KEM key share part. For SECP-based hybrids, the KEM + * shared secret goes behind the ECDH part. For X25519/X448 hybrids, + * the KEM part goes first. */ if (ret == 0) { + int input_offset = ecc_kse->keLen; + int output_offset = ssSzEcc; + + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || ecc_group == WOLFSSL_ECC_X448) { + input_offset = 0; + output_offset = 0; + } + #endif + ret = TLSX_KeyShare_HandlePqcKeyServer(ssl, pqc_kse, - data + ecc_kse->keLen, pubSz, - ssl->arrays->preMasterSecret + ssSzEcc, &ssSzPqc); + data + input_offset, pubSz, + ssl->arrays->preMasterSecret + output_offset, &ssSzPqc); } if (ret == 0) { @@ -9595,12 +9752,25 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl, keyShareEntry->ke = NULL; keyShareEntry->keLen = 0; - if (ecc_kse->pubKeyLen > 0) + /* Concatenate the ECDH public key and the PQC KEM ciphertext. + * For hybrids with SECP-based ECC, the ECC public key is placed first. + * For X25519/X448 based hybrids, the PQC KEM ciphertext comes first in + * order to always have a FIPS approved algorithms first. */ + #if (defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)) && \ + !defined(WOLFSSL_KYBER_ORIGINAL) + if (ecc_group == WOLFSSL_ECC_X25519 || ecc_group == WOLFSSL_ECC_X448) { + XMEMCPY(ciphertext, pqc_kse->pubKey, ctSz); + XMEMCPY(ciphertext + ctSz, ecc_kse->pubKey, ecc_kse->pubKeyLen); + } + else + #endif + { XMEMCPY(ciphertext, ecc_kse->pubKey, ecc_kse->pubKeyLen); - if (pqc_kse->pubKeyLen > 0) 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 @@ -9868,6 +10038,19 @@ 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: