Merge pull request #11096 from Frauschi/pqc_only

Support post-quantum-only (ML-KEM + ML-DSA/SLH-DSA) TLS 1.3 builds
This commit is contained in:
David Garske
2026-08-07 10:08:34 -07:00
committed by GitHub
18 changed files with 501 additions and 74 deletions
+11 -1
View File
@@ -209,5 +209,15 @@
{"name": "pkcs7-mldsa-only", "minutes": 0.3,
"comment": "PKCS#7 SignedData with ML-DSA as the only signature algorithm (no RSA, no ECC); guards the ML-DSA-only PKCS7 build path",
"configure": ["--enable-cryptonly", "--enable-mldsa",
"--enable-pkcs7", "--disable-rsa", "--disable-ecc"]}
"--enable-pkcs7", "--disable-rsa", "--disable-ecc"]},
{"name": "pqc-only", "minutes": 2,
"comment": "PQC-only TLS 1.3: ML-KEM key exchange + ML-DSA auth with RSA, ECC and DH disabled (CNSA 2.0); session tickets exercise ML-KEM-only resumption",
"configure": ["--enable-mlkem", "--enable-mldsa",
"--enable-tls-mlkem-standalone", "--enable-session-ticket",
"--disable-rsa", "--disable-ecc", "--disable-dh"]},
{"name": "slhdsa-pqc-only", "minutes": 2,
"comment": "PQC-only TLS 1.3: ML-KEM key exchange + SLH-DSA (FIPS 205) auth with RSA, ECC and DH disabled; exercises the SLH-DSA CertificateVerify path",
"configure": ["--enable-mlkem", "--enable-slhdsa",
"--enable-tls-mlkem-standalone", "--enable-session-ticket",
"--disable-rsa", "--disable-ecc", "--disable-dh"]}
]
+2 -1
View File
@@ -8251,7 +8251,8 @@ then
# base. If none of those is available, hybrids contribute no usable groups
# for TLS 1.3 key exchange. In that case, auto-enable standalone ML-KEM so
# TLS 1.3 has a functional KEM.
if test "$ENABLED_MLKEM" = "yes" && test "$ENABLED_ML_KEM" != "no" && \
if test "$ENABLED_TLS13" = "yes" && test "$ENABLED_MLKEM" = "yes" && \
test "$ENABLED_ML_KEM" != "no" && \
test "x$ENABLED_ECC" = "xno" && test "x$ENABLED_CURVE25519" = "xno" && \
test "x$ENABLED_CURVE448" = "xno"
then
+9 -2
View File
@@ -2414,6 +2414,14 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
verifyCert = caEd448CertFile;
ourCert = cliEd448CertFile;
ourKey = cliEd448KeyFile;
#elif defined(TEST_HAVE_MLDSA_CERTS)
verifyCert = caMldsaCertFile;
ourCert = cliMldsaCertFile;
ourKey = cliMldsaKeyFile;
#elif defined(TEST_HAVE_SLHDSA_CERTS)
verifyCert = caSlhdsaCertFile;
ourCert = cliSlhdsaCertFile;
ourKey = cliSlhdsaKeyFile;
#else
verifyCert = NULL;
ourCert = NULL;
@@ -3486,8 +3494,7 @@ THREAD_RETURN WOLFSSL_THREAD client_test(void* args)
}
#endif
#if defined(NO_RSA) && !defined(HAVE_ECC) && !defined(HAVE_ED25519) && \
!defined(HAVE_ED448)
#if defined(TEST_NO_CLASSIC_AUTH) && !defined(TEST_HAVE_PQC_CERT_AUTH)
if (!usePsk) {
usePsk = 1;
}
+8 -2
View File
@@ -121,8 +121,8 @@ void echoclient_test(void* args)
#ifdef WOLFSSL_LEANPSK
doPSK = 1;
#endif
#if defined(NO_CERTS) || (defined(NO_RSA) && !defined(HAVE_ECC) && \
!defined(HAVE_ED25519) && !defined(HAVE_ED448))
#if defined(NO_CERTS) || \
(defined(TEST_NO_CLASSIC_AUTH) && !defined(TEST_HAVE_PQC_CERT_AUTH))
doPSK = 1;
#endif
(void)doPSK;
@@ -160,6 +160,12 @@ void echoclient_test(void* args)
#elif defined(HAVE_ED448)
if (SSL_CTX_load_verify_locations(ctx, caEd448CertFile, 0) != WOLFSSL_SUCCESS)
err_sys("can't load ca file, Please run from wolfSSL home dir");
#elif defined(NO_RSA) && defined(TEST_HAVE_MLDSA_CERTS)
if (SSL_CTX_load_verify_locations(ctx, caMldsaCertFile, 0) != WOLFSSL_SUCCESS)
err_sys("can't load ca file, Please run from wolfSSL home dir");
#elif defined(NO_RSA) && defined(TEST_HAVE_SLHDSA_CERTS)
if (SSL_CTX_load_verify_locations(ctx, caSlhdsaCertFile, 0) != WOLFSSL_SUCCESS)
err_sys("can't load ca file, Please run from wolfSSL home dir");
#endif
#elif !defined(NO_CERTS)
if (!doPSK)
+25 -2
View File
@@ -124,8 +124,7 @@ THREAD_RETURN WOLFSSL_THREAD echoserver_test(void* args)
((func_args*)args)->return_code = -1; /* error state */
#if defined(NO_CERTS) || defined(WOLFSSL_LEANPSK) || \
(defined(NO_RSA) && !defined(HAVE_ECC) && !defined(HAVE_ED25519) && \
!defined(HAVE_ED448))
(defined(TEST_NO_CLASSIC_AUTH) && !defined(TEST_HAVE_PQC_CERT_AUTH))
doPSK = 1;
#else
doPSK = 0;
@@ -212,6 +211,30 @@ THREAD_RETURN WOLFSSL_THREAD echoserver_test(void* args)
CERT_FILETYPE) != WOLFSSL_SUCCESS)
err_sys("can't load server key file, "
"Please run from wolfSSL home dir");
#elif defined(NO_RSA) && defined(TEST_HAVE_MLDSA_CERTS) && \
!defined(NO_CERTS) && !defined(WOLFSSL_SNIFFER)
/* ML-DSA (post-quantum-only) */
if (wolfSSL_CTX_use_certificate_chain_file(ctx, mldsaCertFile)
!= WOLFSSL_SUCCESS)
err_sys("can't load server cert file, "
"Please run from wolfSSL home dir");
if (wolfSSL_CTX_use_PrivateKey_file(ctx, mldsaKeyFile, CERT_FILETYPE)
!= WOLFSSL_SUCCESS)
err_sys("can't load server key file, "
"Please run from wolfSSL home dir");
#elif defined(NO_RSA) && defined(TEST_HAVE_SLHDSA_CERTS) && \
!defined(NO_CERTS) && !defined(WOLFSSL_SNIFFER)
/* SLH-DSA (post-quantum-only); entity certs are PEM only */
if (wolfSSL_CTX_use_certificate_chain_file(ctx, slhdsaCertFile)
!= WOLFSSL_SUCCESS)
err_sys("can't load server cert file, "
"Please run from wolfSSL home dir");
if (wolfSSL_CTX_use_PrivateKey_file(ctx, slhdsaKeyFile,
CERT_FILETYPE) != WOLFSSL_SUCCESS)
err_sys("can't load server key file, "
"Please run from wolfSSL home dir");
#elif defined(NO_CERTS)
/* do nothing, just don't load cert files */
#else
+9 -2
View File
@@ -1895,6 +1895,14 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
verifyCert = cliEd448CertFile;
ourCert = ed448CertFile;
ourKey = ed448KeyFile;
#elif defined(TEST_HAVE_MLDSA_CERTS)
verifyCert = caMldsaCertFile;
ourCert = mldsaCertFile;
ourKey = mldsaKeyFile;
#elif defined(TEST_HAVE_SLHDSA_CERTS)
verifyCert = caSlhdsaCertFile;
ourCert = slhdsaCertFile;
ourKey = slhdsaKeyFile;
#else
verifyCert = NULL;
ourCert = NULL;
@@ -2948,8 +2956,7 @@ THREAD_RETURN WOLFSSL_THREAD server_test(void* args)
}
#endif
#if defined(NO_RSA) && !defined(HAVE_ECC) && !defined(HAVE_ED25519) && \
!defined(HAVE_ED448)
#if defined(TEST_NO_CLASSIC_AUTH) && !defined(TEST_HAVE_PQC_CERT_AUTH)
if (!usePsk) {
usePsk = 1;
}
+33
View File
@@ -60,6 +60,18 @@ else
dtls13=yes
fi
# The fragmented-send case needs a client that can request a small maximum
# fragment length and a server that can simulate WANT_WRITE. Both options are
# silently ignored otherwise, which would turn the case into a no-op that still
# reports PASSED.
if ! ./examples/client/client -? 2>&1 | grep -q 'Maximum Fragment Length'; then
fragmented_send=no
elif ./examples/server/server -6 -? 2>&1 | grep -q 'Ignoring -6'; then
fragmented_send=no
else
fragmented_send=yes
fi
if [[ ("$tls13" == "no") && ("$dtls13" == "no") ]]; then
echo 'skipping ocsp-stapling_tls13multi.test because TLS1.3 is not available.' 1>&2
exit 77
@@ -492,6 +504,27 @@ if [ "$tls13" == "yes" ]; then
exit 1
fi
printf '%s\n\n' "Test successfully REVOKED!"
printf '%s\n\n' "------------- TEST CASE 8 FRAGMENTED SEND --------------------"
if [ "$fragmented_send" == "no" ]; then
printf '%s\n\n' "Test SKIPPED: needs HAVE_MAX_FRAGMENT and async I/O."
else
# A small maximum fragment length (-F 1) splits the stapled Certificate
# message over many records, and the server (-6) blocks on every one of
# them, so the send resumes from a WANT_WRITE inside the chain.
remove_single_rF "$ready_file5"
./examples/server/server -c certs/ocsp/server3-cert.pem \
-k certs/ocsp/server3-key.pem -R "$ready_file5" \
-p "$port5" -v 4 -6 &
server_pid5=$!
wait_for_readyFile "$ready_file5" "$server_pid5" "$port5"
./examples/client/client -C -A certs/ocsp/root-ca-cert.pem -W 1 -v 4 \
-F 1 -p "$port5"
RESULT=$?
[ "$RESULT" -ne 0 ] && printf '\n\n%s\n' "Client connection 8 failed" \
&& exit 1
printf '%s\n\n' "Test PASSED!"
fi
fi
if [ "$dtls13" == "yes" ]; then
+25 -3
View File
@@ -9243,7 +9243,8 @@ int AllocKey(WOLFSSL* ssl, int type, void** pKey)
(!defined(NO_RSA) || defined(HAVE_ECC) || defined(HAVE_ED25519) || \
defined(HAVE_CURVE25519) || defined(HAVE_ED448) || \
defined(HAVE_CURVE448) || defined(HAVE_FALCON) || \
defined(WOLFSSL_HAVE_MLDSA) || defined(WOLFSSL_HAVE_SLHDSA))
(defined(WOLFSSL_HAVE_MLDSA) && !defined(WOLFSSL_MLDSA_NO_VERIFY)) || \
defined(WOLFSSL_HAVE_SLHDSA))
static int ReuseKey(WOLFSSL* ssl, int type, void* pKey)
{
int ret = 0;
@@ -9738,6 +9739,17 @@ void wolfSSL_ResourceFree(WOLFSSL* ssl)
XFREE(ssl->buffers.tls13CookieSecret.buffer, ssl->heap,
DYNAMIC_TYPE_COOKIE_PWD);
#endif
#if !defined(NO_CERTS) && defined(WOLFSSL_TLS13) && \
defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER)
{
/* Release the certificate status extensions of a Certificate message
* that was never sent in full. */
int extIdx;
for (extIdx = 0; extIdx < MAX_CERT_EXTENSIONS; extIdx++)
FreeDer(&ssl->buffers.certExts[extIdx]);
}
#endif
#ifdef WOLFSSL_TLS13_STREAM_CERT_VERIFY
/* Release any in-progress streamed CertificateVerify body (e.g. a
* connection torn down mid-send). */
@@ -15403,9 +15415,12 @@ int CopyDecodedToX509(WOLFSSL_X509* x509, DecodedCert* dCert)
ret = MEMORY_E;
}
#endif
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448)
#if defined(HAVE_ECC) || defined(HAVE_ED25519) || defined(HAVE_ED448) || \
defined(HAVE_FALCON) || defined(WOLFSSL_HAVE_MLDSA) || \
defined(WOLFSSL_HAVE_SLHDSA)
x509->pkCurveOID = dCert->pkCurveOID;
#endif /* HAVE_ECC || HAVE_CURVE25519 || HAVE_CURVE448 */
#endif /* HAVE_ECC || HAVE_ED25519 || HAVE_ED448 || HAVE_FALCON ||
* WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */
#ifdef WOLFSSL_DUAL_ALG_CERTS
copyRet = CopyDecodedDualAlg(x509, dCert);
@@ -17172,6 +17187,10 @@ static int ProcessPeerCertDecodeKey(WOLFSSL* ssl, ProcPeerCertArgs* args,
{
int ret = *pRet;
/* Every case below sits under an algorithm guard, so a build with no
* peer-verifiable key type leaves this unused. */
(void)ssl;
switch (args->dCert->keyOID) {
#ifndef NO_RSA
#ifdef WC_RSA_PSS
@@ -32944,6 +32963,9 @@ static int DecodePrivateKey_ex(WOLFSSL *ssl, byte keyType, const DerBuffer* key,
int devSlhParam = -1;
#endif
/* Every reader below sits under an algorithm guard. */
(void)ssl;
/* make sure private key exists */
if (key == NULL || key->buffer == NULL) {
/* allow no private key if using external */
+4 -1
View File
@@ -49,7 +49,10 @@
#if !defined(WOLFSSL_ALLOW_NO_SUITES) && !defined(WOLFCRYPT_ONLY)
#if defined(NO_DH) && !defined(HAVE_ECC) && !defined(WOLFSSL_STATIC_RSA) \
&& !defined(WOLFSSL_STATIC_DH) && !defined(WOLFSSL_STATIC_PSK) \
&& !defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448)
&& !defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448) \
&& (!defined(WOLFSSL_TLS13) \
|| !defined(WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT) \
|| defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE))
#error "No cipher suites defined because DH disabled, ECC disabled, " \
"and no static suites defined. Please see top of README"
#endif
+3
View File
@@ -1164,6 +1164,9 @@ static int ProcessBufferTryDecode(WOLFSSL_CTX* ctx, WOLFSSL* ssl,
(void)heap;
(void)devId;
(void)type;
/* Only the per-algorithm decoders below read these back. */
(void)keyType;
(void)keySz;
/* Validate parameters. */
if ((der == NULL) || (keyFormat == NULL)) {
+3 -12
View File
@@ -4672,17 +4672,6 @@ int TLSX_UseCertificateStatusRequestV2(TLSX** extensions, byte status_type,
#endif /* HAVE_CERTIFICATE_STATUS_REQUEST_V2 */
/* ML-KEM client support requires generating a key pair (encapsulation key) and
* decapsulating the server's ciphertext. */
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
#define WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT
#endif
/* ML-KEM server support requires encapsulating to the client's key. */
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)
#define WOLFSSL_HAVE_MLKEM_SERVER_SUPPORT
#endif
#if defined(HAVE_SUPPORTED_CURVES) || \
(defined(WOLFSSL_TLS13) && defined(HAVE_SUPPORTED_CURVES))
@@ -16752,7 +16741,9 @@ int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isServer)
modes = 1 << PSK_KE;
}
#if !defined(NO_DH) || defined(HAVE_ECC) || \
defined(HAVE_CURVE25519) || defined(HAVE_CURVE448)
defined(HAVE_CURVE25519) || defined(HAVE_CURVE448) || \
(defined(WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT) && \
!defined(WOLFSSL_TLS_NO_MLKEM_STANDALONE))
if (!ssl->options.noPskDheKe) {
modes |= 1 << PSK_DHE_KE;
}
+101 -36
View File
@@ -8816,6 +8816,7 @@ static WC_INLINE void EncodeSigAlg(const WOLFSSL * ssl, byte hashAlgo,
byte hsType, byte* output)
{
(void)ssl;
(void)hashAlgo;
switch (hsType) {
#ifdef HAVE_ECC
case ecc_dsa_sa_algo:
@@ -8848,7 +8849,6 @@ static WC_INLINE void EncodeSigAlg(const WOLFSSL * ssl, byte hashAlgo,
case ed25519_sa_algo:
output[0] = ED25519_SA_MAJOR;
output[1] = ED25519_SA_MINOR;
(void)hashAlgo;
break;
#endif
#ifdef HAVE_ED448
@@ -8856,7 +8856,6 @@ static WC_INLINE void EncodeSigAlg(const WOLFSSL * ssl, byte hashAlgo,
case ed448_sa_algo:
output[0] = ED448_SA_MAJOR;
output[1] = ED448_SA_MINOR;
(void)hashAlgo;
break;
#endif
#ifndef NO_RSA
@@ -9664,33 +9663,46 @@ static int WriteCSRToBuffer(WOLFSSL* ssl, DerBuffer** certExts,
for (extIdx = 0; extIdx < (word16)(extSz_num); extIdx++) {
tmpSz = TLSX_CSR_GetSize_ex(csr, 0, (int)extIdx);
if (tmpSz > (OPAQUE8_LEN + OPAQUE24_LEN) &&
certExts[extIdx] == NULL) {
/* csr extension is not zero */
if (tmpSz > WOLFSSL_MAX_16BIT)
return BUFFER_E;
extSz[extIdx] = (word16)tmpSz;
if (ssl->fragOffset != 0 && certExts[extIdx] != NULL) {
/* A fragmented send is being resumed and this buffer was
* written by the earlier call. extSz starts over on every
* call, so recover this entry's size from the length written
* into the buffer. */
ato16(certExts[extIdx]->buffer, &extSz[extIdx]);
extSz[extIdx] += OPAQUE16_LEN;
}
else {
/* Not a resume, so anything still allocated here is left over
* from a completed message and must not be reused. */
FreeDer(&certExts[extIdx]);
ret = AllocDer(&certExts[extIdx], extSz[extIdx] + ex_offset,
CERT_TYPE, ssl->heap);
if (ret < 0)
return ret;
der = certExts[extIdx];
if (tmpSz > (OPAQUE8_LEN + OPAQUE24_LEN)) {
/* csr extension is not zero */
if (tmpSz > WOLFSSL_MAX_16BIT)
return BUFFER_E;
extSz[extIdx] = (word16)tmpSz;
/* write extension type */
c16toa(ext->type, der->buffer
+ OPAQUE16_LEN);
/* writes extension data length. */
c16toa(extSz[extIdx], der->buffer
+ HELLO_EXT_TYPE_SZ + OPAQUE16_LEN);
/* write extension data */
extSz[extIdx] = (word16)TLSX_CSR_Write_ex(csr,
der->buffer + ex_offset, 0, extIdx);
/* add extension offset */
extSz[extIdx] += (word16)ex_offset;
/* extension length */
c16toa(extSz[extIdx] - OPAQUE16_LEN,
der->buffer);
ret = AllocDer(&certExts[extIdx], extSz[extIdx] + ex_offset,
CERT_TYPE, ssl->heap);
if (ret < 0)
return ret;
der = certExts[extIdx];
/* write extension type */
c16toa(ext->type, der->buffer
+ OPAQUE16_LEN);
/* writes extension data length. */
c16toa(extSz[extIdx], der->buffer
+ HELLO_EXT_TYPE_SZ + OPAQUE16_LEN);
/* write extension data */
extSz[extIdx] = (word16)TLSX_CSR_Write_ex(csr,
der->buffer + ex_offset, 0, extIdx);
/* add extension offset */
extSz[extIdx] += (word16)ex_offset;
/* extension length */
c16toa(extSz[extIdx] - OPAQUE16_LEN,
der->buffer);
}
}
totalSz += extSz[extIdx];
}
@@ -9877,7 +9889,8 @@ static int SendTls13Certificate(WOLFSSL* ssl)
word32 totalextSz = 0;
word32 len = 0;
word32 idx = 0;
word32 offset = OPAQUE16_LEN;
word32 offset = 0;
word32 entrySz = 0;
byte* p = NULL;
byte certReqCtxLen = 0;
sword32 length;
@@ -9957,9 +9970,14 @@ static int SendTls13Certificate(WOLFSSL* ssl)
&& ssl->options.handShakeDone)
#endif
) {
ret = SetupOcspResp(ssl);
if (ret != 0)
return ret;
/* Build the responses once. A resumed send reuses them: looking
* them up again appends another set of requests to the extension
* until it overflows with MAX_CERT_EXTENSIONS_ERR. */
if (ssl->fragOffset == 0) {
ret = SetupOcspResp(ssl);
if (ret != 0)
return ret;
}
if ((1 + ssl->buffers.certChainCnt) > MAX_CERT_EXTENSIONS)
ret = MAX_CERT_EXTENSIONS_ERR;
@@ -10007,6 +10025,50 @@ static int SendTls13Certificate(WOLFSSL* ssl)
extIdx = 0;
/* Only ssl->fragOffset survives a WANT_WRITE, so a resume inside the chain
* has to rebuild the walk cursor from it. */
if (certChainSz > 0 && ssl->fragOffset >= certSz + extSz[0]) {
word32 chainPos = ssl->fragOffset - (certSz + extSz[0]);
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && !defined(NO_WOLFSSL_SERVER)
/* The leaf is behind us and its buffer was rebuilt above. */
FreeDer(&ssl->buffers.certExts[0]);
#endif
while (chainPos > 0) {
word32 prevIdx = idx;
len = NextCert(ssl->buffers.certChain->buffer,
ssl->buffers.certChain->length, &idx);
if (len == 0)
break;
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
!defined(NO_WOLFSSL_SERVER)
if (extIdx + 1 < MAX_CERT_EXTENSIONS)
extIdx++;
#endif
entrySz = len + extSz[extIdx];
if (chainPos < entrySz) {
/* Resume part way through this entry. */
p = ssl->buffers.certChain->buffer + prevIdx;
offset = chainPos;
chainPos = 0;
}
else {
/* Entry already sent in full; stay primed for the next one. */
#if defined(HAVE_CERTIFICATE_STATUS_REQUEST) && \
!defined(NO_WOLFSSL_SERVER)
/* Its buffer was rebuilt above and nothing writes it again. */
FreeDer(&ssl->buffers.certExts[extIdx]);
#endif
chainPos -= entrySz;
offset = 0;
entrySz = 0;
}
}
}
while (length > 0 && ret == 0) {
byte* output = NULL;
word32 fragSz = 0;
@@ -10107,7 +10169,7 @@ static int SendTls13Certificate(WOLFSSL* ssl)
while (fragSz > 0) {
word32 l;
if (offset == len + OPAQUE16_LEN) {
if (offset == entrySz) {
/* Find next CA certificate to write out. */
offset = 0;
/* Point to the start of current cert in chain buffer. */
@@ -10121,6 +10183,8 @@ static int SendTls13Certificate(WOLFSSL* ssl)
if (extIdx + 1 < MAX_CERT_EXTENSIONS)
extIdx++;
#endif
/* Certificate and its extensions make up the entry. */
entrySz = len + extSz[extIdx];
}
/* Write out certificate and extension. */
l = AddCertExt(ssl, p, len, extSz[extIdx], offset, fragSz,
@@ -10133,10 +10197,8 @@ static int SendTls13Certificate(WOLFSSL* ssl)
if (extIdx != 0 && extIdx < MAX_CERT_EXTENSIONS &&
ssl->buffers.certExts[extIdx] != NULL &&
offset == len + extSz[extIdx]) {
offset == entrySz) {
FreeDer(&ssl->buffers.certExts[extIdx]);
/* for next chain cert */
len += extSz[extIdx] - OPAQUE16_LEN;
}
}
}
@@ -10855,6 +10917,8 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
sigOut += OPAQUE16_LEN;
}
#endif
/* Only the per-algorithm signing branches below consume this. */
(void)sigOut;
#ifdef HAVE_ECC
if (ssl->hsType == DYNAMIC_TYPE_ECC) {
#if defined(WOLFSSL_SM2) && defined(WOLFSSL_SM3)
@@ -12661,7 +12725,8 @@ exit_dcv:
return ret;
}
#endif /* !NO_RSA || HAVE_ECC */
#endif /* !NO_RSA || HAVE_ECC || HAVE_ED25519 || HAVE_ED448 ||
* HAVE_FALCON || WOLFSSL_HAVE_MLDSA || WOLFSSL_HAVE_SLHDSA */
#endif /* !NO_CERTS */
/* Parse and handle a TLS v1.3 Finished message.
+6 -2
View File
@@ -11073,7 +11073,10 @@ static int test_wolfSSL_PKCS8(void)
#if !defined(NO_FILESYSTEM) && !defined(NO_ASN) && defined(HAVE_PKCS8) && \
!defined(WOLFCRYPT_ONLY) && !defined(NO_TLS) && \
(!defined(WOLFSSL_NO_TLS12) || defined(WOLFSSL_TLS13))
#if !defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)
/* Without RSA, ECC or PEM decoding every key load below compiles out, leaving
* nothing to test. */
#if (!defined(NO_WOLFSSL_CLIENT) || !defined(NO_WOLFSSL_SERVER)) && \
(!defined(NO_RSA) || defined(HAVE_ECC) || defined(WOLFSSL_PEM_TO_DER))
byte buff[FOURK_BUF];
byte der[FOURK_BUF];
#ifndef NO_RSA
@@ -11289,7 +11292,8 @@ static int test_wolfSSL_PKCS8(void)
#endif /* HAVE_ECC */
wolfSSL_CTX_free(ctx);
#endif /* !NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER */
#endif /* (!NO_WOLFSSL_CLIENT || !NO_WOLFSSL_SERVER) &&
* (!NO_RSA || HAVE_ECC || WOLFSSL_PEM_TO_DER) */
#endif /* !NO_FILESYSTEM && !NO_ASN && HAVE_PKCS8 */
return EXPECT_RESULT();
}
+13
View File
@@ -84,6 +84,19 @@ int test_tls13_apis(void)
#elif defined(HAVE_ED448)
const char* ourCert = ed448CertFile;
const char* ourKey = ed448KeyFile;
/* The CERT_FILES gates, not the signing ones: only a certificate on the context
* is needed here, so the key load below is allowed to fail. */
#elif defined(TEST_HAVE_MLDSA_CERT_FILES)
const char* ourCert = mldsaCertFile;
const char* ourKey = mldsaKeyFile;
#elif defined(TEST_HAVE_SLHDSA_CERT_FILES)
const char* ourCert = slhdsaCertFile;
const char* ourKey = slhdsaKeyFile;
#else
/* No certificate to load. The loads below ignore the return value and a
* NULL filename fails cleanly rather than being dereferenced. */
const char* ourCert = NULL;
const char* ourKey = NULL;
#endif
#endif
#endif
+39
View File
@@ -39,3 +39,42 @@
-k ./certs/slhdsa/client-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
# Server auth with simulated WANT_WRITE (-6): leaf plus root push the
# Certificate message past one record, so the send resumes mid chain.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-d
-6
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
-6
# Mutual auth with simulated WANT_WRITE, so the client's own Certificate
# send resumes mid chain too.
# server TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/server-slhdsa-shake-128s.pem
-k ./certs/slhdsa/server-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-V
-6
# client TLSv1.3 TLS13-AES128-GCM-SHA256
-v 4
-l TLS13-AES128-GCM-SHA256
-c ./certs/slhdsa/client-slhdsa-shake-128s.pem
-k ./certs/slhdsa/client-slhdsa-shake-128s-priv.pem
-A ./certs/slhdsa/root-slhdsa-shake-128s.pem
-C
-6
+29
View File
@@ -29346,11 +29346,23 @@ static int SetValidity(byte* before, byte* after, int daysValid)
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_REQ)
/* MakeSignatureCb backs both MakeSignature()'s local RSA/ECC signing and the
* wc_SignCert_cb() offload path, so either one needs it. */
#if (!defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
!defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
(defined(HAVE_ECC) && defined(HAVE_ECC_SIGN)) || \
defined(WOLFSSL_CERT_SIGN_CB)
/* Forward declaration for internal use */
static int MakeSignatureCb(CertSignCtx* certSignCtx, const byte* buf,
word32 sz, byte* sig, word32 sigSz, int sigAlgoType, int keyType,
wc_SignCertCb signCb, void* signCtx, WC_RNG* rng, void* heap);
#endif /* (!NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY) ||
* (HAVE_ECC && HAVE_ECC_SIGN) || WOLFSSL_CERT_SIGN_CB */
/* InternalSignCb is only used by MakeSignature()'s local signing path. */
#if (!defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
!defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
(defined(HAVE_ECC) && defined(HAVE_ECC_SIGN))
/* Internal context for default signing operations (when no callback provided) */
typedef struct {
void* key;
@@ -29437,6 +29449,8 @@ static int InternalSignCb(const byte* in, word32 inLen,
return ret;
}
#endif /* (!NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY) ||
* (HAVE_ECC && HAVE_ECC_SIGN) */
#endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */
@@ -29716,10 +29730,13 @@ static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
{
int ret = 0;
(void)certSignCtx;
(void)buf;
(void)sz;
(void)sig;
(void)sigSz;
(void)rsaKey;
(void)eccKey;
(void)ed25519Key;
(void)ed448Key;
(void)falconKey;
@@ -29728,6 +29745,7 @@ static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
(void)lmsKey;
(void)xmssKey;
(void)rng;
(void)sigAlgoType;
(void)heap;
/* For RSA and ECC, use the callback path to eliminate duplication */
@@ -29843,7 +29861,12 @@ static int MakeSignature(CertSignCtx* certSignCtx, const byte* buf, word32 sz,
if (ret == -1)
ret = ALGO_ID_E;
#if (!defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
!defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
(defined(HAVE_ECC) && defined(HAVE_ECC_SIGN))
exit_ms:
#endif /* (!NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY) ||
* (HAVE_ECC && HAVE_ECC_SIGN) */
if (ret < 0) {
WOLFSSL_ERROR_VERBOSE(ret);
}
@@ -30861,6 +30884,10 @@ int wc_MakeCertReq(Cert* cert, byte* derBuffer, word32 derSz,
#if defined(WOLFSSL_CERT_GEN) || defined(WOLFSSL_CERT_REQ)
#if (!defined(NO_RSA) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
!defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
(defined(HAVE_ECC) && defined(HAVE_ECC_SIGN)) || \
defined(WOLFSSL_CERT_SIGN_CB)
/* Internal function to create signature using callback
* This allows external signing implementations (e.g., TPM, HSM) without
* requiring the crypto callback infrastructure.
@@ -30993,6 +31020,8 @@ exit_ms:
return ret;
}
#endif /* (!NO_RSA && !WOLFSSL_RSA_PUBLIC_ONLY && !WOLFSSL_RSA_VERIFY_ONLY) ||
* (HAVE_ECC && HAVE_ECC_SIGN) || WOLFSSL_CERT_SIGN_CB */
#endif /* WOLFSSL_CERT_GEN || WOLFSSL_CERT_REQ */
+15 -1
View File
@@ -221,6 +221,17 @@
extern "C" {
#endif
/* ML-KEM client support requires generating a key pair (encapsulation key) and
* decapsulating the server's ciphertext. */
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_MAKE_KEY) && \
!defined(WOLFSSL_MLKEM_NO_DECAPSULATE)
#define WOLFSSL_HAVE_MLKEM_CLIENT_SUPPORT
#endif
/* ML-KEM server support requires encapsulating to the client's key. */
#if defined(WOLFSSL_HAVE_MLKEM) && !defined(WOLFSSL_MLKEM_NO_ENCAPSULATE)
#define WOLFSSL_HAVE_MLKEM_SERVER_SUPPORT
#endif
/* Define or comment out the cipher suites you'd like to be compiled in
make sure to use at least one BUILD_SSL_xxx or BUILD_TLS_xxx is defined
@@ -854,7 +865,10 @@
#if !defined(WOLFCRYPT_ONLY) && defined(NO_PSK) && \
(defined(NO_DH) || !defined(HAVE_ANON)) && \
defined(NO_RSA) && !defined(HAVE_ECC) && \
!defined(HAVE_ED25519) && !defined(HAVE_ED448)
!defined(HAVE_ED25519) && !defined(HAVE_ED448) && \
(!defined(WOLFSSL_TLS13) || \
(!defined(HAVE_FALCON) && !defined(WOLFSSL_HAVE_MLDSA) && \
!defined(WOLFSSL_HAVE_SLHDSA)))
#error "No cipher suites available with this build"
#endif
+166 -9
View File
@@ -52,6 +52,13 @@
#include <wolfssl/wolfcrypt/mem_track.h>
#include <wolfssl/wolfio.h>
#include <wolfssl/wolfcrypt/asn.h>
/* The credential gates below read the derived sub-config macros of these. */
#ifdef WOLFSSL_HAVE_MLDSA
#include <wolfssl/wolfcrypt/wc_mldsa.h>
#endif
#ifdef WOLFSSL_HAVE_SLHDSA
#include <wolfssl/wolfcrypt/wc_slhdsa.h>
#endif
#ifdef ATOMIC_USER
#include <wolfssl/wolfcrypt/aes.h>
@@ -461,19 +468,78 @@ err_sys_with_errno_func(const char* msg, const char *file, int line)
} while(0)
#ifndef WOLFSSL_NO_TLS12
#define SERVER_DEFAULT_VERSION 3
#else
#define SERVER_DEFAULT_VERSION 4
/* No classic public key authentication compiled in. Shared with the PSK
* fallbacks in the examples so the conditions cannot drift apart. */
#if defined(NO_RSA) && !defined(HAVE_ECC) && !defined(HAVE_ED25519) && \
!defined(HAVE_ED448)
#define TEST_NO_CLASSIC_AUTH
#endif
/* The ML-DSA credential paths below name real files. Loading the certificate
* needs verification support only. */
#if defined(WOLFSSL_HAVE_MLDSA) && \
(!defined(WOLFSSL_NO_ML_DSA_44) || !defined(WOLFSSL_NO_ML_DSA_65) || \
!defined(WOLFSSL_NO_ML_DSA_87))
#define TEST_HAVE_MLDSA_CERT_FILES
#endif
/* The same for SLH-DSA. Only the 128s parameter sets have certificates wired
* up, and the entity certificates ship as PEM only. */
#if defined(WOLFSSL_HAVE_SLHDSA) && defined(WOLFSSL_PEM_TO_DER) && \
(defined(WOLFSSL_SLHDSA_PARAM_128S) || \
defined(WOLFSSL_SLHDSA_PARAM_SHA2_128S))
#define TEST_HAVE_SLHDSA_CERT_FILES
#endif
/* The same credentials, plus the ability to sign and to verify: a verify-only
* build can neither load the key nor produce a CertificateVerify, and a
* sign-only build cannot check the peer's chain. */
#if defined(TEST_HAVE_MLDSA_CERT_FILES) && \
defined(WOLFSSL_MLDSA_PRIVATE_KEY) && !defined(WOLFSSL_MLDSA_NO_SIGN) && \
!defined(WOLFSSL_MLDSA_NO_VERIFY)
#define TEST_HAVE_MLDSA_CERTS
#endif
#if defined(TEST_HAVE_SLHDSA_CERT_FILES) && \
!defined(WOLFSSL_SLHDSA_VERIFY_ONLY)
#define TEST_HAVE_SLHDSA_CERTS
#endif
/* The examples can authenticate with a post-quantum certificate. Both
* algorithms are TLS 1.3 only, so the version is folded in. Falcon is absent
* on purpose: it has no credentials in the ladders. */
#if defined(WOLFSSL_TLS13) && \
(defined(TEST_HAVE_MLDSA_CERTS) || defined(TEST_HAVE_SLHDSA_CERTS))
#define TEST_HAVE_PQC_CERT_AUTH
#endif
/* No key exchange that TLS 1.2 and earlier can negotiate. ML-KEM is the only
* one left and it is TLS 1.3 only, so those versions have no cipher suite at
* all. Mirrors the check guarding the "No cipher suites defined" #error in
* src/ssl.c. */
#if defined(WOLFSSL_TLS13) && defined(NO_DH) && !defined(HAVE_ECC) && \
!defined(HAVE_CURVE25519) && !defined(HAVE_CURVE448) && \
!defined(WOLFSSL_STATIC_RSA) && !defined(WOLFSSL_STATIC_DH) && \
!defined(WOLFSSL_STATIC_PSK)
#define TEST_NO_CLASSIC_KEX
#endif
/* A post-quantum-only build can only work over TLS 1.3, so default to it:
* post-quantum certificate authentication and standalone ML-KEM are both
* TLS 1.3 only. A build left with nothing but PSK over a classic key exchange
* keeps the TLS 1.2 default. One macro so the client and server defaults
* cannot drift apart. */
#if defined(WOLFSSL_NO_TLS12) || defined(TEST_NO_CLASSIC_KEX) || \
(defined(TEST_NO_CLASSIC_AUTH) && defined(TEST_HAVE_PQC_CERT_AUTH))
#define TEST_DEFAULT_TLS_VERSION 4
#else
#define TEST_DEFAULT_TLS_VERSION 3
#endif
#define SERVER_DEFAULT_VERSION TEST_DEFAULT_TLS_VERSION
#define SERVER_DTLS_DEFAULT_VERSION (-2)
#define SERVER_INVALID_VERSION (-99)
#define SERVER_DOWNGRADE_VERSION (-98)
#ifndef WOLFSSL_NO_TLS12
#define CLIENT_DEFAULT_VERSION 3
#else
#define CLIENT_DEFAULT_VERSION 4
#endif
#define CLIENT_DEFAULT_VERSION TEST_DEFAULT_TLS_VERSION
#define CLIENT_DTLS_DEFAULT_VERSION (-2)
#define CLIENT_INVALID_VERSION (-99)
#define CLIENT_DOWNGRADE_VERSION (-98)
@@ -682,6 +748,97 @@ err_sys_with_errno_func(const char* msg, const char *file, int line)
#endif
#endif
/* ML-DSA (FIPS 204) certificate material. The mldsa<N>-cert files are
* self-signed, so the same file is both peer certificate and trust anchor.
* MLDSA_TEST_LEVEL and MLDSA_TEST_DIR compose the path, which depends on the
* built parameter level and on the target base directory. */
#if defined(TEST_HAVE_MLDSA_CERT_FILES)
#if !defined(WOLFSSL_NO_ML_DSA_65)
#define MLDSA_TEST_LEVEL "mldsa65"
#elif !defined(WOLFSSL_NO_ML_DSA_44)
#define MLDSA_TEST_LEVEL "mldsa44"
#else
#define MLDSA_TEST_LEVEL "mldsa87"
#endif
#if defined(WOLFSSL_NO_CURRDIR) || defined(WOLFSSL_MDK_SHELL)
#define MLDSA_TEST_DIR "certs/mldsa/"
#elif defined(NETOS) && defined(HAVE_FIPS)
#define MLDSA_TEST_DIR FS_VOLUME1_DIR "certs/mldsa/"
#else
#define MLDSA_TEST_DIR "./certs/mldsa/"
#endif
#ifdef WOLFSSL_PEM_TO_DER
#ifndef mldsaCertFile
#define mldsaCertFile MLDSA_TEST_DIR MLDSA_TEST_LEVEL "-cert.pem"
#endif
#ifndef mldsaKeyFile
#define mldsaKeyFile MLDSA_TEST_DIR MLDSA_TEST_LEVEL "-key.pem"
#endif
#else
#ifndef mldsaCertFile
#define mldsaCertFile MLDSA_TEST_DIR MLDSA_TEST_LEVEL "-cert.der"
#endif
#ifndef mldsaKeyFile
/* Not _priv-only.der: only -key.der matches the certificate. */
#define mldsaKeyFile MLDSA_TEST_DIR MLDSA_TEST_LEVEL "-key.der"
#endif
#endif
#ifndef cliMldsaCertFile
#define cliMldsaCertFile mldsaCertFile
#endif
#ifndef cliMldsaKeyFile
#define cliMldsaKeyFile mldsaKeyFile
#endif
#ifndef caMldsaCertFile
#define caMldsaCertFile mldsaCertFile
#endif
#endif /* TEST_HAVE_MLDSA_CERT_FILES */
/* SLH-DSA (FIPS 205) certificate material. Unlike the ML-DSA files above, the
* leaves are signed by a shared 128s root, so the trust anchor is a distinct
* file. SLHDSA_TEST_FAM picks the SHAKE family, falling back to SHA2 only when
* SHAKE-128s is not compiled in. */
#if defined(TEST_HAVE_SLHDSA_CERT_FILES)
#if defined(WOLFSSL_SLHDSA_PARAM_128S)
#define SLHDSA_TEST_FAM "shake"
#else
#define SLHDSA_TEST_FAM "sha2"
#endif
#if defined(WOLFSSL_NO_CURRDIR) || defined(WOLFSSL_MDK_SHELL)
#define SLHDSA_TEST_DIR "certs/slhdsa/"
#elif defined(NETOS) && defined(HAVE_FIPS)
#define SLHDSA_TEST_DIR FS_VOLUME1_DIR "certs/slhdsa/"
#else
#define SLHDSA_TEST_DIR "./certs/slhdsa/"
#endif
#ifndef slhdsaCertFile
#define slhdsaCertFile SLHDSA_TEST_DIR "server-slhdsa-" SLHDSA_TEST_FAM \
"-128s.pem"
#endif
#ifndef slhdsaKeyFile
#define slhdsaKeyFile SLHDSA_TEST_DIR "server-slhdsa-" SLHDSA_TEST_FAM \
"-128s-priv.pem"
#endif
#ifndef cliSlhdsaCertFile
#define cliSlhdsaCertFile SLHDSA_TEST_DIR "client-slhdsa-" SLHDSA_TEST_FAM \
"-128s.pem"
#endif
#ifndef cliSlhdsaKeyFile
#define cliSlhdsaKeyFile SLHDSA_TEST_DIR "client-slhdsa-" SLHDSA_TEST_FAM \
"-128s-priv.pem"
#endif
#ifndef caSlhdsaCertFile
#define caSlhdsaCertFile SLHDSA_TEST_DIR "root-slhdsa-" SLHDSA_TEST_FAM \
"-128s.pem"
#endif
#endif /* TEST_HAVE_SLHDSA_CERT_FILES */
#ifdef WOLFSSL_PEM_TO_DER
#define CERT_FILETYPE WOLFSSL_FILETYPE_PEM
#else