ML-KEM fixes

* DTLS 1.3 cookie and CH frag handling
* static memory handling
* Fix memory leak in TLS server PQC handling in case of ECH
* Make sure hybrids are actually tested in testsuite
This commit is contained in:
Tobias Frauenschläger
2026-03-13 17:48:35 +01:00
parent b5c532703a
commit 76b1300adb
9 changed files with 208 additions and 50 deletions
+14 -2
View File
@@ -557,6 +557,18 @@ static void SetKeyShare(WOLFSSL* ssl, int onlyKeyShare, int useX25519,
else {
err_sys("unable to use post-quantum KEM");
}
#ifdef WOLFSSL_DTLS13
if (wolfSSL_dtls(ssl)) {
/* When the KeyShare is too large for an unfragmented
* ClientHello, DTLS sends an empty KeyShare extension to
* use the Hello Retry Request to enable fragmentation.
* In order to enforce our desired PQC algorithm in the
* second ClientHello, we need to set it as the only one
* allowed in the SupportedGroups extension. */
setGroups = 1;
}
#endif /* WOLFSSL_DTLS13 */
}
}
#endif
@@ -2299,8 +2311,8 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
#ifdef WOLFSSL_STATIC_MEMORY
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) \
|| defined(SESSION_CERTS)
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) || \
defined(SESSION_CERTS) || defined(WOLFSSL_HAVE_MLKEM)
/* big enough to handle most cases including session certs */
byte memory[320000];
#else
+2 -2
View File
@@ -1763,8 +1763,8 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
/* Note: Actual memory used is much less, this is the entire buffer buckets,
* which is partitioned into pools of common sizes. To adjust the buckets
* sizes see WOLFMEM_BUCKETS in memory.h */
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) \
|| defined(SESSION_CERTS)
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) || \
defined(SESSION_CERTS) || defined(WOLFSSL_HAVE_MLKEM)
/* big enough to handle most cases including session certs */
#if !defined(WOLFSSL_NO_CLIENT_AUTH) && \
((defined(HAVE_ED25519) && !defined(NO_ED25519_CLIENT_AUTH)) || \
+33 -2
View File
@@ -2251,6 +2251,26 @@ int InitSSL_Side(WOLFSSL* ssl, word16 side)
WOLFSSL_MSG("DTLS Cookie Secret error");
return ret;
}
#if defined(WOLFSSL_DTLS13)
if (IsAtLeastTLSv1_3(ssl->version)) {
#if defined(WOLFSSL_SEND_HRR_COOKIE)
ret = wolfSSL_send_hrr_cookie(ssl, NULL, 0);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("DTLS1.3 Cookie secret error");
return ret;
}
#endif /* WOLFSSL_SEND_HRR_COOKIE */
#if defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM)
/* Allow fragmentation of the second ClientHello due to the
* large PQC key share. */
ret = wolfSSL_dtls13_allow_ch_frag(ssl, 1);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("DTLS1.3 CH frag error");
return ret;
}
#endif /* WOLFSSL_DTLS_CH_FRAG && WOLFSSL_HAVE_MLKEM */
}
#endif /* WOLFSSL_DTLS13 */
}
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */
@@ -8006,15 +8026,26 @@ int InitSSL(WOLFSSL* ssl, WOLFSSL_CTX* ctx, int writeDup)
WOLFSSL_MSG("DTLS Cookie Secret error");
return ret;
}
#if defined(WOLFSSL_DTLS13) && defined(WOLFSSL_SEND_HRR_COOKIE)
#if defined(WOLFSSL_DTLS13)
if (IsAtLeastTLSv1_3(ssl->version)) {
#if defined(WOLFSSL_SEND_HRR_COOKIE)
ret = wolfSSL_send_hrr_cookie(ssl, NULL, 0);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("DTLS1.3 Cookie secret error");
return ret;
}
#endif /* WOLFSSL_SEND_HRR_COOKIE */
#if defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM)
/* Allow fragmentation of the second ClientHello due to the
* large PQC key share. */
ret = wolfSSL_dtls13_allow_ch_frag(ssl, 1);
if (ret != WOLFSSL_SUCCESS) {
WOLFSSL_MSG("DTLS1.3 CH frag error");
return ret;
}
#endif /* WOLFSSL_DTLS_CH_FRAG && WOLFSSL_HAVE_MLKEM */
}
#endif /* WOLFSSL_DTLS13 && WOLFSSL_SEND_HRR_COOKIE */
#endif /* WOLFSSL_DTLS13 */
}
#endif /* WOLFSSL_DTLS && !NO_WOLFSSL_SERVER */
+16 -7
View File
@@ -10534,6 +10534,7 @@ static int TLSX_KeyShare_HandlePqcKeyServer(WOLFSSL* ssl,
keyShareEntry->ke = NULL;
keyShareEntry->keLen = 0;
XFREE(keyShareEntry->pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
keyShareEntry->pubKey = ciphertext;
keyShareEntry->pubKeyLen = ctSz;
ciphertext = NULL;
@@ -10770,6 +10771,7 @@ static int TLSX_KeyShare_HandlePqcHybridKeyServer(WOLFSSL* ssl,
XMEMCPY(ciphertext + ecc_kse->pubKeyLen, pqc_kse->pubKey, ctSz);
}
XFREE(keyShareEntry->pubKey, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
keyShareEntry->pubKey = ciphertext;
keyShareEntry->pubKeyLen = ecc_kse->pubKeyLen + ctSz;
ciphertext = NULL;
@@ -10859,13 +10861,20 @@ int TLSX_KeyShare_Use(const WOLFSSL* ssl, word16 group, word16 len, byte* data,
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)
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;
if (TLSX_IsGroupSupported(group)) {
ret = TLSX_KeyShare_HandlePqcKeyServer((WOLFSSL*)ssl,
keyShareEntry,
data, len,
ssl->arrays->preMasterSecret,
&ssl->arrays->preMasterSz);
if (ret != 0)
return ret;
}
else {
XFREE(keyShareEntry->ke, ssl->heap, DYNAMIC_TYPE_PUBLIC_KEY);
keyShareEntry->ke = NULL;
keyShareEntry->keLen = 0;
}
}
else if (ssl->options.side == WOLFSSL_SERVER_END &&
WOLFSSL_NAMED_GROUP_IS_PQC_HYBRID(group)) {
+3 -2
View File
@@ -259,7 +259,8 @@
#endif
#if defined(WOLFSSL_STATIC_MEMORY) && !defined(WOLFCRYPT_ONLY)
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) || defined(SESSION_CERTS)
#if (defined(HAVE_ECC) && !defined(ALT_ECC_SIZE)) || \
defined(SESSION_CERTS) || defined(WOLFSSL_HAVE_MLKEM)
#ifdef OPENSSL_EXTRA
#define TEST_TLS_STATIC_MEMSZ (400000)
#else
@@ -32014,7 +32015,7 @@ static int test_dtls13_frag_ch_pq(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13) \
&& defined(WOLFSSL_DTLS_CH_FRAG) && defined(HAVE_LIBOQS)
&& defined(WOLFSSL_DTLS_CH_FRAG) && defined(WOLFSSL_HAVE_MLKEM)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
+117 -23
View File
@@ -169,57 +169,151 @@ static int IsValidCipherSuite(const char* line, char *suite, size_t suite_spc)
}
#if defined(WOLFSSL_HAVE_MLKEM)
#define MATCH_PQC(b, s, l) ((l) == sizeof(s) - 1 && \
XSTRNCMP((b), (s), sizeof(s) - 1) == 0)
static int IsKyberLevelAvailable(const char* line)
{
int available = 0;
const char* find = "--pqc ";
const char* begin = strstr(line, find);
const char* end;
const char* begin = XSTRSTR(line, "--pqc");
size_t len = 0;
if (begin != NULL) {
begin += 6;
end = XSTRSTR(begin, " ");
begin += XSTRLEN("--pqc");
while (*begin == ' ' || *begin == '\t') {
begin++;
}
#ifndef WOLFSSL_NO_ML_KEM
if ((size_t)end - (size_t)begin == 10) {
if (*begin != '\0') {
const char* end = begin;
while (*end != '\0' && *end != ' ' && *end != '\t') {
end++;
}
len = (size_t)(end - begin);
}
}
if (begin != NULL && len > 0) {
#ifndef WOLFSSL_NO_ML_KEM
#ifndef WOLFSSL_NO_ML_KEM_512
if (XSTRNCMP(begin, "ML_KEM_512", 10) == 0) {
if (MATCH_PQC(begin, "ML_KEM_512", len)) {
available = 1;
}
#endif
#ifndef WOLFSSL_NO_ML_KEM_768
if (XSTRNCMP(begin, "ML_KEM_768", 10) == 0) {
if (MATCH_PQC(begin, "ML_KEM_768", len)) {
available = 1;
}
#endif
}
#ifndef WOLFSSL_NO_ML_KEM_1024
if ((size_t)end - (size_t)begin == 11) {
if (XSTRNCMP(begin, "ML_KEM_1024", 11) == 0) {
if (MATCH_PQC(begin, "ML_KEM_1024", len)) {
available = 1;
}
}
#endif
#endif
#ifdef WOLFSSL_MLKEM_KYBER
if ((size_t)end - (size_t)begin == 12) {
#ifndef WOLFSSL_NO_KYBER512
if (XSTRNCMP(begin, "KYBER_LEVEL1", 12) == 0) {
#if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_ECC)
if (MATCH_PQC(begin, "SecP256r1MLKEM512", len)) {
available = 1;
}
#ifdef WOLFSSL_ML_KEM_USE_OLD_IDS
if (MATCH_PQC(begin, "P256_ML_KEM_512_OLD", len)) {
available = 1;
}
#endif
#endif
#if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_ECC)
if (MATCH_PQC(begin, "SecP384r1MLKEM768", len)) {
available = 1;
}
if (MATCH_PQC(begin, "SecP256r1MLKEM768", len)) {
available = 1;
}
#ifdef WOLFSSL_ML_KEM_USE_OLD_IDS
if (MATCH_PQC(begin, "P384_ML_KEM_768_OLD", len)) {
available = 1;
}
#endif
#endif
#if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE25519)
if (MATCH_PQC(begin, "X25519MLKEM768", len)) {
available = 1;
}
#endif
#if !defined(WOLFSSL_NO_ML_KEM_1024) && defined(HAVE_ECC)
if (MATCH_PQC(begin, "SecP521r1MLKEM1024", len)) {
available = 1;
}
if (MATCH_PQC(begin, "SecP384r1MLKEM1024", len)) {
available = 1;
}
#ifdef WOLFSSL_ML_KEM_USE_OLD_IDS
if (MATCH_PQC(begin, "P521_ML_KEM_1024_OLD", len)) {
available = 1;
}
#endif
#endif
#if !defined(WOLFSSL_NO_ML_KEM_512) && defined(HAVE_CURVE25519)
if (MATCH_PQC(begin, "X25519MLKEM512", len)) {
available = 1;
}
#endif
#if !defined(WOLFSSL_NO_ML_KEM_768) && defined(HAVE_CURVE448)
if (MATCH_PQC(begin, "X448MLKEM768", len)) {
available = 1;
}
#endif
#endif /* !WOLFSSL_NO_ML_KEM */
#ifdef WOLFSSL_MLKEM_KYBER
#ifndef WOLFSSL_NO_KYBER512
if (MATCH_PQC(begin, "KYBER_LEVEL1", len)) {
available = 1;
}
#ifdef HAVE_ECC
if (MATCH_PQC(begin, "P256_KYBER_LEVEL1", len)) {
available = 1;
}
#endif
#endif
#ifndef WOLFSSL_NO_KYBER768
if (XSTRNCMP(begin, "KYBER_LEVEL3", 12) == 0) {
if (MATCH_PQC(begin, "KYBER_LEVEL3", len)) {
available = 1;
}
#ifdef HAVE_ECC
if (MATCH_PQC(begin, "P384_KYBER_LEVEL3", len)) {
available = 1;
}
if (MATCH_PQC(begin, "P256_KYBER_LEVEL3", len)) {
available = 1;
}
#endif
#endif
#ifndef WOLFSSL_NO_KYBER1024
if (XSTRNCMP(begin, "KYBER_LEVEL5", 12) == 0) {
if (MATCH_PQC(begin, "KYBER_LEVEL5", len)) {
available = 1;
}
#ifdef HAVE_ECC
if (MATCH_PQC(begin, "P521_KYBER_LEVEL5", len)) {
available = 1;
}
#endif
}
#endif
#endif
#if !defined(WOLFSSL_NO_KYBER512) && defined(HAVE_CURVE25519)
if (MATCH_PQC(begin, "X25519_KYBER_LEVEL1", len)) {
available = 1;
}
#endif
#if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE25519)
if (MATCH_PQC(begin, "X25519_KYBER_LEVEL3", len)) {
available = 1;
}
#endif
#if !defined(WOLFSSL_NO_KYBER768) && defined(HAVE_CURVE448)
if (MATCH_PQC(begin, "X448_KYBER_LEVEL3", len)) {
available = 1;
}
#endif
#endif /* WOLFSSL_MLKEM_KYBER */
}
#if defined(WOLFSSL_MLKEM_NO_MAKE_KEY) || \
@@ -910,7 +1004,7 @@ int SuiteTest(int argc, char** argv)
char* myArgv[3];
#ifdef WOLFSSL_STATIC_MEMORY
byte memory[200000];
byte memory[320000];
#endif
printf(" Begin Cipher Suite Tests\n");
+4 -5
View File
@@ -4750,16 +4750,15 @@ enum {
WOLFSSL_P256_KYBER_LEVEL3 = 25498,
#endif /* WOLFSSL_MLKEM_KYBER */
#ifndef WOLFSSL_NO_ML_KEM
/* Taken from draft-connolly-tls-mlkem-key-agreement, see:
* https://github.com/dconnolly/draft-connolly-tls-mlkem-key-agreement/
/* Taken from draft-ietf-tls-mlkem, see:
* https://datatracker.ietf.org/doc/draft-ietf-tls-mlkem/
*/
WOLFSSL_ML_KEM_512 = 512,
WOLFSSL_ML_KEM_768 = 513,
WOLFSSL_ML_KEM_1024 = 514,
/* Taken from draft-kwiatkowski-tls-ecdhe-mlkem. see:
* https://github.com/post-quantum-cryptography/
* draft-kwiatkowski-tls-ecdhe-mlkem/
/* Taken from draft-ietf-tls-ecdhe-mlkem. see:
* https://datatracker.ietf.org/doc/draft-ietf-tls-ecdhe-mlkem/
*/
WOLFSSL_SECP256R1MLKEM768 = 4587,
WOLFSSL_X25519MLKEM768 = 4588,
+15 -3
View File
@@ -175,15 +175,25 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf,
#define WOLFMEM_BUCKETS 64,128,256,512,1024,8192,32768,\
65536,LARGEST_MEM_BUCKET
#endif
#elif defined(WOLFSSL_HAVE_MLKEM)
/* extra storage in structs for multiple attributes and order */
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,4096,8192,\
LARGEST_MEM_BUCKET
#else
/* default size of chunks of memory to separate into */
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,3456,4544,\
LARGEST_MEM_BUCKET
#endif
#elif defined(OPENSSL_EXTRA)
/* extra storage in structs for multiple attributes and order */
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,3360,4480,\
LARGEST_MEM_BUCKET
#ifdef WOLFSSL_HAVE_MLKEM
/* extra storage in structs for multiple attributes and order */
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,4096,8192,\
LARGEST_MEM_BUCKET
#else
/* extra storage in structs for multiple attributes and order */
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,3360,4480,\
LARGEST_MEM_BUCKET
#endif
#elif defined(WOLFSSL_CERT_EXT)
#define WOLFMEM_BUCKETS 64,128,256,512,1024,2432,3456,4544,\
LARGEST_MEM_BUCKET
@@ -203,6 +213,8 @@ WOLFSSL_API int wolfSSL_GetAllocators(wolfSSL_Malloc_cb* mf,
#else
#define WOLFMEM_DIST 30,10,8,15,8,10,8,5,1
#endif
#elif defined(WOLFSSL_HAVE_MLKEM)
#define WOLFMEM_DIST 49,10,6,14,5,6,14,1,1
#elif !defined(WOLFSSL_STATIC_MEMORY_SMALL)
#define WOLFMEM_DIST 49,10,6,14,5,6,9,1,1
#else
+4 -4
View File
@@ -4549,10 +4549,10 @@ extern void uITRON4_free(void *p) ;
#define WOLFSSL_DILITHIUM_VERIFY_NO_MALLOC
#endif
#if defined(HAVE_PQC) && defined(WOLFSSL_DTLS13) && \
!defined(WOLFSSL_DTLS_CH_FRAG)
#warning "Using DTLS 1.3 + pqc without WOLFSSL_DTLS_CH_FRAG will probably" \
"fail.Use --enable-dtls-frag-ch to enable it."
#if defined(HAVE_PQC) && defined(WOLFSSL_HAVE_MLKEM) && \
defined(WOLFSSL_DTLS13) && !defined(WOLFSSL_DTLS_CH_FRAG)
#define WOLFSSL_DTLS_CH_FRAG
#warning "WOLFSSL_DTLS_CH_FRAG is enabled to support PQC in DTLS 1.3"
#endif
#if !defined(WOLFSSL_DTLS13) && defined(WOLFSSL_DTLS_CH_FRAG)
#error "WOLFSSL_DTLS_CH_FRAG only works with DTLS 1.3"