DTLS 1.3 and TLS 1.3 focused fixes

dtls13.c:
- Fix wrong return value in Dtls13SendFragmentedInternal error path (return outputSz instead of recordLength)
- Fix incomplete bounds check in Dtls13SendFragmented to account for DTLS_HANDSHAKE_HEADER_SZ
- Fix wrong WOLFSSL_ENTER trace string in Dtls13EpochCopyKeys

tls13.c:
- Remove wrong (byte) cast on cookie->len passed to TlsCheckCookie
- Add missing bounds check on PSK identityLen in SetupPskKey before copying to client_identity
- Fix data race on static header array in ExpectedResumptionSecret
- Add defensive underflow check in EncryptTls13 for consistency with DecryptTls13
- Fix wrong return variable in DTLS 1.3 Finished send error path (return dtlsRet instead of ret)
- Add missing SM3 case and default in Tls13_Exporter hash switch to prevent NULL dereference
- Initialize *outSz to 0 in wolfSSL_write_early_data to match wolfSSL_read_early_data
- Add bounds check for bindersLen against helloSz in CheckPreSharedKeys
- Fix resource leak and hash state corruption in ExpectedResumptionSecret error paths
- Fix memory leak of rsaSigBuf in dual-alg RSA+RSA CertificateVerify
- Guard against word32 underflow in inputLength - HANDSHAKE_HEADER_SZ in DoTls13HandShakeMsg
- Fix swapped side parameter in DeriveFinishedSecret for server-side Finished processing
- Fix no_mac fall-through in ssl_handshake_md to return NULL instead of wrong digest
- Fix strict aliasing violation in FindPsk PSK key size check
- Remove duplicate !ssl->options.dtls check in TLS 1.3 middlebox compat condition

tests:
- Add regression tests for wolfSSL_write_early_data outSz initialization and DTLS 1.3 Finished send error propagation
This commit is contained in:
Reda Chouk
2026-04-01 11:53:44 +02:00
parent 6fc93ac412
commit d3ce5b8537
4 changed files with 93 additions and 16 deletions
+3 -3
View File
@@ -1040,7 +1040,7 @@ static int Dtls13SendFragmentedInternal(WOLFSSL* ssl)
fragLength + DTLS_HANDSHAKE_HEADER_SZ, isEncrypted);
if (outputSz < 0) {
Dtls13FreeFragmentsBuffer(ssl);
return recordLength;
return outputSz;
}
ret = CheckAvailableSize(ssl, outputSz);
@@ -1102,7 +1102,7 @@ static int Dtls13SendFragmented(WOLFSSL* ssl, byte* message, word16 length,
isEncrypted = Dtls13TypeIsEncrypted(handshake_type);
rlHeaderLength = Dtls13GetRlHeaderLength(ssl, isEncrypted);
if (length < rlHeaderLength)
if (length < rlHeaderLength + DTLS_HANDSHAKE_HEADER_SZ)
return INCOMPLETE_DATA;
/* DTLSv1.3 do not consider fragmentation for hash transcript. Build the
@@ -2212,7 +2212,7 @@ static void Dtls13EpochCopyKeys(WOLFSSL* ssl, Dtls13Epoch* e, Keys* k, int side)
byte clientWrite, serverWrite;
byte enc, dec;
WOLFSSL_ENTER("Dtls13SetEpochKeys");
WOLFSSL_ENTER("Dtls13EpochCopyKeys");
clientWrite = serverWrite = 0;
enc = dec = 0;
+44 -13
View File
@@ -948,6 +948,13 @@ static const byte emptySHA512Hash[] = {
0xF9, 0x27, 0xDA, 0x3E
};
#endif
#ifdef WOLFSSL_SM3
static const byte emptySM3Hash[] = {
0x1A, 0xB2, 0x1D, 0x83, 0x55, 0xCF, 0xA1, 0x7F, 0x8E, 0x61, 0x19, 0x48,
0x31, 0xE8, 0x1A, 0x8F, 0x22, 0xBE, 0xC8, 0xC7, 0x28, 0xFE, 0xFB, 0x74,
0x7E, 0xD0, 0x35, 0xEB, 0x50, 0x82, 0xAA, 0x2B
};
#endif
/**
* Implement section 7.5 of RFC 8446
* @return 0 on success
@@ -1003,6 +1010,17 @@ int Tls13_Exporter(WOLFSSL* ssl, unsigned char *out, size_t outLen,
emptyHash = emptySHA512Hash;
break;
#endif
#ifdef WOLFSSL_SM3
case sm3_mac:
hashType = WC_HASH_TYPE_SM3;
hashLen = WC_SM3_DIGEST_SIZE;
emptyHash = emptySM3Hash;
break;
#endif
default:
return BAD_FUNC_ARG;
}
/* Derive-Secret(Secret, label, "") */
@@ -2572,7 +2590,7 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
word16 sz, const byte* aad, word16 aadSz, int asyncOkay)
{
int ret = 0;
word16 dataSz = sz - ssl->specs.aead_mac_size;
word16 dataSz;
word16 macSz = ssl->specs.aead_mac_size;
word32 nonceSz = 0;
#ifdef WOLFSSL_ASYNC_CRYPT
@@ -2581,6 +2599,9 @@ static int EncryptTls13(WOLFSSL* ssl, byte* output, const byte* input,
#endif
WOLFSSL_ENTER("EncryptTls13");
if (sz < ssl->specs.aead_mac_size)
return BUFFER_E;
dataSz = sz - ssl->specs.aead_mac_size;
(void)output;
(void)input;
@@ -4054,6 +4075,7 @@ static const WOLFSSL_EVP_MD* ssl_handshake_md(const byte mac_alg)
{
switch(mac_alg) {
case no_mac:
return NULL;
#ifndef NO_MD5
case md5_mac:
return wolfSSL_EVP_md5();
@@ -4161,6 +4183,8 @@ static int SetupPskKey(WOLFSSL* ssl, PreSharedKey* psk, int clientHello)
#endif
/* Set the client identity to use. */
if (psk->identityLen > MAX_PSK_ID_LEN)
return PSK_KEY_ERROR;
XMEMSET(ssl->arrays->client_identity, 0,
sizeof(ssl->arrays->client_identity));
XMEMCPY(ssl->arrays->client_identity, psk->identity, psk->identityLen);
@@ -5982,7 +6006,7 @@ int FindPskSuite(const WOLFSSL* ssl, PreSharedKey* psk, byte* psk_key,
}
if (*found) {
if (*psk_keySz > MAX_PSK_KEY_LEN &&
*((int*)psk_keySz) != WC_NO_ERR_TRACE(USE_HW_PSK)) {
(int)*psk_keySz != WC_NO_ERR_TRACE(USE_HW_PSK)) {
WOLFSSL_MSG("Key len too long in FindPsk()");
ret = PSK_KEY_ERROR;
WOLFSSL_ERROR_VERBOSE(ret);
@@ -6322,6 +6346,8 @@ static int CheckPreSharedKeys(WOLFSSL* ssl, const byte* input, word32 helloSz,
client_hello, &bindersLen);
if (ret < 0)
return ret;
if (bindersLen > helloSz)
return BUFFER_ERROR;
/* Refine list for PSK processing. */
sslRefineSuites(ssl, clSuites);
@@ -6564,7 +6590,7 @@ static int RestartHandshakeHashWithCookie(WOLFSSL* ssl, Cookie* cookie)
int keyShareExt = 0;
int ret;
ret = TlsCheckCookie(ssl, cookie->data, (byte)cookie->len);
ret = TlsCheckCookie(ssl, cookie->data, cookie->len);
if (ret < 0)
return ret;
cookieDataSz = (word16)ret;
@@ -9741,6 +9767,7 @@ static int SendTls13CertificateVerify(WOLFSSL* ssl)
#ifndef NO_RSA
if (ssl->hsAltType == DYNAMIC_TYPE_RSA) {
/* build encoded signature buffer */
XFREE(rsaSigBuf->buffer, ssl->heap, DYNAMIC_TYPE_SIGNATURE);
rsaSigBuf->length = WC_MAX_DIGEST_SIZE;
rsaSigBuf->buffer = (byte*)XMALLOC(rsaSigBuf->length,
ssl->heap,
@@ -11488,13 +11515,13 @@ static int SendTls13Finished(WOLFSSL* ssl)
*/
ret = DeriveFinishedSecret(ssl, ssl->clientSecret,
ssl->keys.client_write_MAC_secret,
WOLFSSL_SERVER_END);
WOLFSSL_CLIENT_END);
if (ret != 0)
return ret;
ret = DeriveFinishedSecret(ssl, ssl->serverSecret,
ssl->keys.server_write_MAC_secret,
WOLFSSL_CLIENT_END);
WOLFSSL_SERVER_END);
if (ret != 0)
return ret;
@@ -11520,7 +11547,7 @@ static int SendTls13Finished(WOLFSSL* ssl)
(word16)(Dtls13GetRlHeaderLength(ssl, 1) + headerSz + finishedSz), finished,
1);
if (dtlsRet != 0 && dtlsRet != WC_NO_ERR_TRACE(WANT_WRITE))
return ret;
return dtlsRet;
} else
#endif /* WOLFSSL_DTLS13 */
@@ -12166,7 +12193,7 @@ static int ExpectedResumptionSecret(WOLFSSL* ssl)
word32 finishedSz = 0;
byte mac[WC_MAX_DIGEST_SIZE];
Digest digest;
static byte header[] = { 0x14, 0x00, 0x00, 0x00 };
byte header[] = { 0x14, 0x00, 0x00, 0x00 };
XMEMSET(&digest, 0, sizeof(Digest));
@@ -12206,25 +12233,26 @@ static int ExpectedResumptionSecret(WOLFSSL* ssl)
ret = BuildTls13HandshakeHmac(ssl, ssl->keys.client_write_MAC_secret, mac,
&finishedSz);
if (ret != 0)
return ret;
goto restore;
header[FINISHED_MSG_SIZE_OFFSET] = finishedSz;
#ifdef WOLFSSL_EARLY_DATA
if (ssl->earlyData != no_early_data) {
static byte endOfEarlyData[] = { 0x05, 0x00, 0x00, 0x00 };
ret = HashRaw(ssl, endOfEarlyData, sizeof(endOfEarlyData));
if (ret != 0)
return ret;
goto restore;
}
#endif
if ((ret = HashRaw(ssl, header, sizeof(header))) != 0)
return ret;
goto restore;
if ((ret = HashRaw(ssl, mac, finishedSz)) != 0)
return ret;
goto restore;
if ((ret = DeriveResumptionSecret(ssl, ssl->session->masterSecret)) != 0)
return ret;
goto restore;
/* Restore the hash inline with currently seen messages. */
restore:
switch (ssl->specs.mac_algorithm) {
#ifndef NO_SHA256
case sha256_mac:
@@ -13460,7 +13488,8 @@ int DoTls13HandShakeMsg(WOLFSSL* ssl, byte* input, word32* inOutIdx,
}
ret = EarlySanityCheckMsgReceived(ssl, type,
min(inputLength - HANDSHAKE_HEADER_SZ, size));
(inputLength > HANDSHAKE_HEADER_SZ) ?
min(inputLength - HANDSHAKE_HEADER_SZ, size) : 0);
if (ret != 0) {
WOLFSSL_ERROR(ret);
return ret;
@@ -15308,6 +15337,8 @@ int wolfSSL_write_early_data(WOLFSSL* ssl, const void* data, int sz, int* outSz)
if (!IsAtLeastTLSv1_3(ssl->version))
return BAD_FUNC_ARG;
*outSz = 0;
#ifndef NO_WOLFSSL_CLIENT
if (ssl->options.side == WOLFSSL_SERVER_END)
return SIDE_ERROR;
+41
View File
@@ -33050,6 +33050,46 @@ static int test_dtls13_missing_finished_server(void)
return EXPECT_RESULT();
}
static int test_dtls13_finished_send_error_propagation(void)
{
EXPECT_DECLS;
#if defined(HAVE_MANUAL_MEMIO_TESTS_DEPENDENCIES) && defined(WOLFSSL_DTLS13)
WOLFSSL_CTX *ctx_c = NULL;
WOLFSSL_CTX *ctx_s = NULL;
WOLFSSL *ssl_c = NULL;
WOLFSSL *ssl_s = NULL;
struct test_memio_ctx test_ctx;
XMEMSET(&test_ctx, 0, sizeof(test_ctx));
ExpectIntEQ(test_memio_setup(&test_ctx, &ctx_c, &ctx_s, &ssl_c, &ssl_s,
wolfDTLSv1_3_client_method, wolfDTLSv1_3_server_method), 0);
/* CH1 */
ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* HRR */
ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
/* CH2 */
ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_c, -1), WOLFSSL_ERROR_WANT_READ);
/* Server first flight with finished */
ExpectIntEQ(wolfSSL_negotiate(ssl_s), -1);
ExpectIntEQ(wolfSSL_get_error(ssl_s, -1), WOLFSSL_ERROR_WANT_READ);
/* Client second flight with finished — block sends to force error */
test_ctx.s_len = TEST_MEMIO_BUF_SZ;
ExpectIntEQ(wolfSSL_negotiate(ssl_c), -1);
/* Verify the error is propagated, not silently swallowed as success */
ExpectIntNE(wolfSSL_get_error(ssl_c, -1), 0);
wolfSSL_free(ssl_c);
wolfSSL_free(ssl_s);
wolfSSL_CTX_free(ctx_c);
wolfSSL_CTX_free(ctx_s);
#endif
return EXPECT_RESULT();
}
#if !defined(NO_WOLFSSL_CLIENT) && !defined(NO_WOLFSSL_SERVER)
#ifdef HAVE_CERTIFICATE_STATUS_REQUEST
@@ -35393,6 +35433,7 @@ TEST_CASE testCases[] = {
TEST_DECL(test_dtls12_missing_finished),
TEST_DECL(test_dtls13_missing_finished_client),
TEST_DECL(test_dtls13_missing_finished_server),
TEST_DECL(test_dtls13_finished_send_error_propagation),
TEST_DTLS_DECLS,
TEST_DECL(test_tls_multi_handshakes_one_record),
TEST_DECL(test_write_dup),
+5
View File
@@ -808,6 +808,11 @@ int test_tls13_apis(void)
/* invoking without session or psk cbs */
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData,
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(BAD_STATE_E));
/* verify *outSz is initialized to 0 even on non-success paths */
outSz = 42;
ExpectIntEQ(wolfSSL_write_early_data(clientSsl, earlyData,
sizeof(earlyData), &outSz), WC_NO_ERR_TRACE(BAD_STATE_E));
ExpectIntEQ(outSz, 0);
#endif
ExpectIntEQ(wolfSSL_read_early_data(NULL, earlyDataBuffer,