mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 13:40:49 +02:00
Fix TLS ext bounds checking
This commit is contained in:
@@ -6504,7 +6504,7 @@ static int TLSX_SessionTicket_Parse(WOLFSSL* ssl, const byte* input,
|
||||
return ret;
|
||||
}
|
||||
|
||||
WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
|
||||
WOLFSSL_TEST_VIS SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
|
||||
byte* data, word16 size, void* heap)
|
||||
{
|
||||
SessionTicket* ticket = (SessionTicket*)XMALLOC(sizeof(SessionTicket),
|
||||
@@ -6525,7 +6525,7 @@ WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
|
||||
|
||||
return ticket;
|
||||
}
|
||||
WOLFSSL_LOCAL void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap)
|
||||
WOLFSSL_TEST_VIS void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap)
|
||||
{
|
||||
if (ticket) {
|
||||
XFREE(ticket->data, heap, DYNAMIC_TYPE_TLSX);
|
||||
@@ -14879,9 +14879,27 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
|
||||
{
|
||||
int ret = 0;
|
||||
TLSX* extension;
|
||||
word16 length = 0;
|
||||
/* Use a word32 accumulator so that an extension whose contribution
|
||||
* pushes the running total past 0xFFFF is detected rather than
|
||||
* silently wrapped (the TLS extensions block length prefix on the
|
||||
* wire is a 2-byte field). Callees that take a word16* accumulator
|
||||
* are invoked via a per-iteration shim (`cbShim`) and their delta
|
||||
* is added back into the word32 total.
|
||||
*
|
||||
* MAINTAINER NOTE: do NOT pass &length to any *_GET_SIZE function
|
||||
* that expects a `word16*` out-parameter -- that would be a type
|
||||
* mismatch (UB) and would silently bypass the overflow detection
|
||||
* below. When adding a new extension case, either:
|
||||
* - use `length += FOO_GET_SIZE(...)` when the helper returns a
|
||||
* word16 by value, or
|
||||
* - use the cbShim pattern: `cbShim = 0; ret = FOO_GET_SIZE(...,
|
||||
* &cbShim); length += cbShim;`
|
||||
*/
|
||||
word32 length = 0;
|
||||
word16 cbShim = 0;
|
||||
byte isRequest = (msgType == client_hello ||
|
||||
msgType == certificate_request);
|
||||
(void)cbShim;
|
||||
|
||||
while ((extension = list)) {
|
||||
list = extension->next;
|
||||
@@ -14965,23 +14983,31 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
|
||||
#endif
|
||||
#if defined(HAVE_ENCRYPT_THEN_MAC) && !defined(WOLFSSL_AEAD_ONLY)
|
||||
case TLSX_ENCRYPT_THEN_MAC:
|
||||
ret = ETM_GET_SIZE(msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = ETM_GET_SIZE(msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#endif /* HAVE_ENCRYPT_THEN_MAC */
|
||||
|
||||
#if defined(WOLFSSL_TLS13) || !defined(WOLFSSL_NO_TLS12) || !defined(NO_OLD_TLS)
|
||||
#if defined(HAVE_SESSION_TICKET) || !defined(NO_PSK)
|
||||
case TLSX_PRE_SHARED_KEY:
|
||||
cbShim = 0;
|
||||
ret = PSK_GET_SIZE((PreSharedKey*)extension->data, msgType,
|
||||
&length);
|
||||
&cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#ifdef WOLFSSL_TLS13
|
||||
case TLSX_PSK_KEY_EXCHANGE_MODES:
|
||||
ret = PKM_GET_SIZE((byte)extension->val, msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = PKM_GET_SIZE((byte)extension->val, msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#ifdef WOLFSSL_CERT_WITH_EXTERN_PSK
|
||||
case TLSX_CERT_WITH_EXTERN_PSK:
|
||||
ret = PSK_WITH_CERT_GET_SIZE(msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = PSK_WITH_CERT_GET_SIZE(msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#endif
|
||||
#endif
|
||||
@@ -14993,22 +15019,30 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
|
||||
|
||||
#ifdef WOLFSSL_TLS13
|
||||
case TLSX_SUPPORTED_VERSIONS:
|
||||
ret = SV_GET_SIZE(extension->data, msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = SV_GET_SIZE(extension->data, msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
|
||||
case TLSX_COOKIE:
|
||||
ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = CKE_GET_SIZE((Cookie*)extension->data, msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
|
||||
#ifdef WOLFSSL_EARLY_DATA
|
||||
case TLSX_EARLY_DATA:
|
||||
ret = EDI_GET_SIZE(msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = EDI_GET_SIZE(msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#endif
|
||||
|
||||
#ifdef WOLFSSL_POST_HANDSHAKE_AUTH
|
||||
case TLSX_POST_HANDSHAKE_AUTH:
|
||||
ret = PHA_GET_SIZE(msgType, &length);
|
||||
cbShim = 0;
|
||||
ret = PHA_GET_SIZE(msgType, &cbShim);
|
||||
length += cbShim;
|
||||
break;
|
||||
#endif
|
||||
|
||||
@@ -15061,12 +15095,26 @@ static int TLSX_GetSize(TLSX* list, byte* semaphore, byte msgType,
|
||||
break;
|
||||
}
|
||||
|
||||
/* Early exit: stop accumulating as soon as the running total
|
||||
* cannot possibly fit the 2-byte wire length. Check *before*
|
||||
* marking the extension as processed so the semaphore is not
|
||||
* left in an inconsistent state on the error path. */
|
||||
if (length > WOLFSSL_MAX_16BIT) {
|
||||
WOLFSSL_MSG("TLSX_GetSize extension length exceeds word16");
|
||||
return BUFFER_E;
|
||||
}
|
||||
|
||||
/* marks the extension as processed so ctx level */
|
||||
/* extensions don't overlap with ssl level ones. */
|
||||
TURN_ON(semaphore, TLSX_ToSemaphore((word16)extension->type));
|
||||
}
|
||||
|
||||
*pLength += length;
|
||||
if ((word32)*pLength + length > WOLFSSL_MAX_16BIT) {
|
||||
WOLFSSL_MSG("TLSX_GetSize total extensions length exceeds word16");
|
||||
return BUFFER_E;
|
||||
}
|
||||
|
||||
*pLength += (word16)length;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -15079,6 +15127,7 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
|
||||
TLSX* extension;
|
||||
word16 offset = 0;
|
||||
word16 length_offset = 0;
|
||||
word32 prevOffset;
|
||||
byte isRequest = (msgType == client_hello ||
|
||||
msgType == certificate_request);
|
||||
|
||||
@@ -15093,6 +15142,10 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
|
||||
if (!IS_OFF(semaphore, TLSX_ToSemaphore((word16)extension->type)))
|
||||
continue; /* skip! */
|
||||
|
||||
/* Snapshot offset to detect word16 wrap within this iteration;
|
||||
* see matching comment in TLSX_GetSize. */
|
||||
prevOffset = offset;
|
||||
|
||||
/* writes extension type. */
|
||||
c16toa((word16)extension->type, output + offset);
|
||||
offset += HELLO_EXT_TYPE_SZ + OPAQUE16_LEN;
|
||||
@@ -15326,9 +15379,24 @@ static int TLSX_Write(TLSX* list, byte* output, byte* semaphore,
|
||||
/* if we encountered an error propagate it */
|
||||
if (ret != 0)
|
||||
break;
|
||||
|
||||
if (offset <= prevOffset) {
|
||||
WOLFSSL_MSG("TLSX_Write extension length exceeds word16");
|
||||
return BUFFER_E;
|
||||
}
|
||||
}
|
||||
|
||||
*pOffset += offset;
|
||||
/* Only validate and commit the aggregate offset when the loop
|
||||
* completed without error; on the error path, leave *pOffset
|
||||
* unchanged and return the original failure reason so callers
|
||||
* see the real error instead of a masking BUFFER_E. */
|
||||
if (ret == 0) {
|
||||
if ((word32)*pOffset + (word32)offset > WOLFSSL_MAX_16BIT) {
|
||||
WOLFSSL_MSG("TLSX_Write total extensions length exceeds word16");
|
||||
return BUFFER_E;
|
||||
}
|
||||
*pOffset += offset;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -16432,6 +16500,13 @@ int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType, word32* pLength)
|
||||
}
|
||||
#endif
|
||||
|
||||
/* The TLS extensions block length prefix is a 2-byte field, so any
|
||||
* accumulated total above 0xFFFF must be rejected rather than silently
|
||||
* truncating and producing a short, malformed handshake message. */
|
||||
if (length > (word16)(WOLFSSL_MAX_16BIT - OPAQUE16_LEN)) {
|
||||
WOLFSSL_MSG("TLSX_GetRequestSize extensions exceed word16");
|
||||
return BUFFER_E;
|
||||
}
|
||||
if (length)
|
||||
length += OPAQUE16_LEN; /* for total length storage. */
|
||||
|
||||
@@ -16635,6 +16710,12 @@ int TLSX_WriteRequest(WOLFSSL* ssl, byte* output, byte msgType, word32* pOffset)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Wrap detection for the TLSX_Write calls above is handled inside
|
||||
* TLSX_Write itself: any iteration that would push the local word16
|
||||
* offset past 0xFFFF returns BUFFER_E so we never reach here with a
|
||||
* truncated value. The TLS extensions block length prefix on the
|
||||
* wire is a 2-byte field, matching this invariant. */
|
||||
|
||||
if (offset > OPAQUE16_LEN || msgType != client_hello)
|
||||
c16toa(offset - OPAQUE16_LEN, output); /* extensions length */
|
||||
|
||||
|
||||
@@ -3279,6 +3279,10 @@ int BuildTls13Message(WOLFSSL* ssl, byte* output, int outSz, const byte* input,
|
||||
|
||||
WOLFSSL_ENTER("BuildTls13Message");
|
||||
|
||||
if (ssl == NULL) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
#ifdef WOLFSSL_ASYNC_CRYPT
|
||||
ret = WC_NO_PENDING_E;
|
||||
if (asyncOkay) {
|
||||
|
||||
+139
@@ -10578,6 +10578,144 @@ static int test_tls_ext_duplicate(void)
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/* Regression test: TLSX extension size accumulation must not silently wrap
|
||||
* the internal word16 accumulator. Prior to the fix, a single extension
|
||||
* whose size (plus the 4-byte header) pushes the running total past 0xFFFF
|
||||
* caused TLSX_GetSize / TLSX_Write to return a truncated length, which
|
||||
* in turn led to undersized buffer writes. The on-wire extensions block
|
||||
* length is a 2-byte field per RFC 8446 Section 4.2, so the correct
|
||||
* behavior is to return BUFFER_E rather than wrap. */
|
||||
static int test_tls_ext_word16_overflow(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_SESSION_TICKET) && !defined(NO_WOLFSSL_CLIENT) && \
|
||||
!defined(NO_TLS)
|
||||
WOLFSSL_CTX* ctx = NULL;
|
||||
WOLFSSL* ssl = NULL;
|
||||
SessionTicket* ticket = NULL;
|
||||
byte* big = NULL;
|
||||
/* Size chosen so that 4 (ext header) + size > 0xFFFF. */
|
||||
const word16 bigSz = 0xFFFE;
|
||||
word32 length = 0;
|
||||
|
||||
big = (byte*)XMALLOC(bigSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(big);
|
||||
if (big != NULL)
|
||||
XMEMSET(big, 0xA5, bigSz);
|
||||
|
||||
ExpectNotNull(ctx = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
||||
ExpectNotNull(ssl = wolfSSL_new(ctx));
|
||||
|
||||
/* Build an oversized SessionTicket extension directly on the ssl
|
||||
* extension list. Going via the public API is not enough here because
|
||||
* wolfSSL_set_SessionTicket clamps to word16 without creating the
|
||||
* TLSX entry; the TLSX path is what exercises the accumulator. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ticket = TLSX_SessionTicket_Create(0, big, bigSz, ssl->heap);
|
||||
ExpectNotNull(ticket);
|
||||
}
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(TLSX_UseSessionTicket(&ssl->extensions, ticket, ssl->heap),
|
||||
WOLFSSL_SUCCESS);
|
||||
/* TLSX_UseSessionTicket takes ownership on success. */
|
||||
ticket = NULL;
|
||||
}
|
||||
|
||||
/* TLSX_GetRequestSize must refuse to encode: 4-byte ext header +
|
||||
* 0xFFFE payload + 2-byte block length prefix = 0x10004, which does
|
||||
* not fit in a word16 wire length. Expect BUFFER_E, not a silently
|
||||
* wrapped small value. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
int ret = TLSX_GetRequestSize(ssl, client_hello, &length);
|
||||
ExpectIntEQ(ret, BUFFER_E);
|
||||
}
|
||||
|
||||
if (ticket != NULL)
|
||||
TLSX_SessionTicket_Free(ticket, ssl->heap);
|
||||
wolfSSL_free(ssl);
|
||||
wolfSSL_CTX_free(ctx);
|
||||
XFREE(big, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ssl = NULL;
|
||||
ctx = NULL;
|
||||
big = NULL;
|
||||
|
||||
/* Boundary case: construct a SessionTicket extension sized so that the
|
||||
* total extensions length in TLSX_GetRequestSize is exactly
|
||||
* WOLFSSL_MAX_16BIT - OPAQUE16_LEN (0xFFFD) *before* the OPAQUE16_LEN
|
||||
* block-prefix adjustment, which must succeed. This pins the `>`
|
||||
* comparison in the overflow check -- mutating it to `>=` would
|
||||
* incorrectly reject this valid case and fail this test. */
|
||||
{
|
||||
WOLFSSL_CTX* ctx2 = NULL;
|
||||
WOLFSSL* ssl2 = NULL;
|
||||
SessionTicket* ticket2 = NULL;
|
||||
byte* buf = NULL;
|
||||
word32 baseLen = 0;
|
||||
word32 baseInternal = 0;
|
||||
word32 tickSz = 0;
|
||||
/* TLSX_GetRequestSize rejects when internal sum > 0xFFFD. */
|
||||
const word32 target = (word32)WOLFSSL_MAX_16BIT - (word32)OPAQUE16_LEN;
|
||||
/* Session ticket extension contributes: type (2) + len (2) + size. */
|
||||
const word32 extHdr = (word32)HELLO_EXT_TYPE_SZ
|
||||
+ (word32)OPAQUE16_LEN;
|
||||
int ret;
|
||||
|
||||
ExpectNotNull(ctx2 = wolfSSL_CTX_new(wolfSSLv23_client_method()));
|
||||
ExpectNotNull(ssl2 = wolfSSL_new(ctx2));
|
||||
|
||||
/* Measure baseline length with no session ticket extension. The
|
||||
* returned value already includes the 2-byte block-length prefix
|
||||
* when nonzero; strip it to get the raw internal sum. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
baseLen = 0;
|
||||
ret = TLSX_GetRequestSize(ssl2, client_hello, &baseLen);
|
||||
ExpectIntEQ(ret, 0);
|
||||
baseInternal = (baseLen > 0)
|
||||
? baseLen - (word32)OPAQUE16_LEN : 0;
|
||||
}
|
||||
|
||||
/* Target: baseInternal + extHdr + tickSz == 0xFFFD. */
|
||||
if (EXPECT_SUCCESS() && baseInternal + extHdr < target) {
|
||||
tickSz = target - baseInternal - extHdr;
|
||||
}
|
||||
|
||||
if (EXPECT_SUCCESS() && tickSz > 0 && tickSz <= WOLFSSL_MAX_16BIT) {
|
||||
buf = (byte*)XMALLOC(tickSz, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(buf);
|
||||
if (buf != NULL)
|
||||
XMEMSET(buf, 0x5A, tickSz);
|
||||
}
|
||||
|
||||
if (EXPECT_SUCCESS() && buf != NULL) {
|
||||
ticket2 = TLSX_SessionTicket_Create(0, buf, (word16)tickSz,
|
||||
ssl2->heap);
|
||||
ExpectNotNull(ticket2);
|
||||
}
|
||||
if (EXPECT_SUCCESS() && ticket2 != NULL) {
|
||||
ExpectIntEQ(TLSX_UseSessionTicket(&ssl2->extensions, ticket2,
|
||||
ssl2->heap), WOLFSSL_SUCCESS);
|
||||
ticket2 = NULL;
|
||||
}
|
||||
|
||||
/* Exact boundary: internal sum == 0xFFFD must succeed, and the
|
||||
* final returned length is 0xFFFD + OPAQUE16_LEN == 0xFFFF. */
|
||||
if (EXPECT_SUCCESS()) {
|
||||
word32 lenBoundary = 0;
|
||||
ret = TLSX_GetRequestSize(ssl2, client_hello, &lenBoundary);
|
||||
ExpectIntEQ(ret, 0);
|
||||
ExpectIntEQ(lenBoundary, (word32)WOLFSSL_MAX_16BIT);
|
||||
}
|
||||
|
||||
if (ticket2 != NULL)
|
||||
TLSX_SessionTicket_Free(ticket2, ssl2->heap);
|
||||
wolfSSL_free(ssl2);
|
||||
wolfSSL_CTX_free(ctx2);
|
||||
XFREE(buf, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
#endif
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
||||
/* Test TLS connection abort when legacy version field indicates TLS 1.3 or
|
||||
* higher. Based on test_tls_ext_duplicate() but with legacy version modified
|
||||
@@ -37373,6 +37511,7 @@ TEST_CASE testCases[] = {
|
||||
TEST_DECL(test_wolfSSL_SCR_Reconnect),
|
||||
TEST_DECL(test_wolfSSL_SCR_check_enabled),
|
||||
TEST_DECL(test_tls_ext_duplicate),
|
||||
TEST_DECL(test_tls_ext_word16_overflow),
|
||||
TEST_DECL(test_tls_bad_legacy_version),
|
||||
#if defined(WOLFSSL_TLS13) && defined(HAVE_ECH)
|
||||
#if defined(HAVE_IO_TESTS_DEPENDENCIES)
|
||||
|
||||
+27
-17
@@ -4637,14 +4637,18 @@ int test_tls13_empty_record_limit(void)
|
||||
}
|
||||
|
||||
/* Inject all records as a single message. */
|
||||
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0, (const char*)allRecs,
|
||||
recSz * numRecs), 0);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
||||
(const char*)allRecs, recSz * numRecs), 0);
|
||||
}
|
||||
|
||||
/* The server's wolfSSL_read should fail with EMPTY_RECORD_LIMIT_E. */
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
||||
WC_NO_ERR_TRACE(EMPTY_RECORD_LIMIT_E));
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)),
|
||||
WC_NO_ERR_TRACE(WOLFSSL_FATAL_ERROR));
|
||||
ExpectIntEQ(wolfSSL_get_error(ssl_s, WOLFSSL_FATAL_ERROR),
|
||||
WC_NO_ERR_TRACE(EMPTY_RECORD_LIMIT_E));
|
||||
}
|
||||
|
||||
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
allRecs = NULL;
|
||||
@@ -4679,17 +4683,19 @@ int test_tls13_empty_record_limit(void)
|
||||
if (EXPECT_SUCCESS()) {
|
||||
int emptyBefore = WOLFSSL_MAX_EMPTY_RECORDS - 1;
|
||||
int emptyAfter = WOLFSSL_MAX_EMPTY_RECORDS - 1;
|
||||
int dataRecSz;
|
||||
int dataRecSz = 0;
|
||||
byte dataRec[128];
|
||||
byte payload[1] = { 'a' };
|
||||
int totalSz;
|
||||
int totalSz = 0;
|
||||
|
||||
dataRecSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 1,
|
||||
application_data, 0, 1, 0);
|
||||
ExpectIntGT(dataRecSz, 0);
|
||||
|
||||
totalSz = recSz * (emptyBefore + emptyAfter) + dataRecSz;
|
||||
if (EXPECT_SUCCESS()) {
|
||||
dataRecSz = BuildTls13Message(ssl_c, NULL, 0, NULL, 1,
|
||||
application_data, 0, 1, 0);
|
||||
ExpectIntGT(dataRecSz, 0);
|
||||
}
|
||||
|
||||
if (EXPECT_SUCCESS()) {
|
||||
totalSz = recSz * (emptyBefore + emptyAfter) + dataRecSz;
|
||||
allRecs = (byte*)XMALLOC((size_t)totalSz, NULL,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
ExpectNotNull(allRecs);
|
||||
@@ -4725,15 +4731,19 @@ int test_tls13_empty_record_limit(void)
|
||||
rec, (size_t)recSz);
|
||||
}
|
||||
|
||||
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
||||
(const char*)allRecs, totalSz), 0);
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(test_memio_inject_message(&test_ctx, 0,
|
||||
(const char*)allRecs, totalSz), 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* wolfSSL_read should return the 1-byte payload. The counter resets
|
||||
* on the non-empty record so neither batch of (limit - 1) empties
|
||||
* triggers the error. */
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), 1);
|
||||
ExpectIntEQ(buf[0], 'a');
|
||||
if (EXPECT_SUCCESS()) {
|
||||
ExpectIntEQ(wolfSSL_read(ssl_s, buf, sizeof(buf)), 1);
|
||||
ExpectIntEQ(buf[0], 'a');
|
||||
}
|
||||
|
||||
XFREE(allRecs, NULL, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
wolfSSL_free(ssl_c);
|
||||
|
||||
+7
-2
@@ -16852,6 +16852,11 @@ int wc_AesEaxDecryptAuth(const byte* key, word32 keySz, byte* out,
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
if (authTagSz < WOLFSSL_MIN_AUTH_TAG_SZ
|
||||
|| authTagSz > WC_AES_BLOCK_SIZE) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
#if defined(WOLFSSL_SMALL_STACK)
|
||||
if ((eax = (AesEax *)XMALLOC(sizeof(AesEax),
|
||||
NULL,
|
||||
@@ -17201,8 +17206,8 @@ int wc_AesEaxDecryptFinal(AesEax* eax,
|
||||
byte authTag[WC_AES_BLOCK_SIZE];
|
||||
#endif
|
||||
|
||||
if (eax == NULL || authIn == NULL || authInSz == 0 ||
|
||||
authInSz > WC_AES_BLOCK_SIZE) {
|
||||
if (eax == NULL || authIn == NULL || authInSz > WC_AES_BLOCK_SIZE
|
||||
|| authInSz < WOLFSSL_MIN_AUTH_TAG_SZ) {
|
||||
return BAD_FUNC_ARG;
|
||||
}
|
||||
|
||||
|
||||
@@ -19518,6 +19518,114 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t aes_eax_test(void)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Regression test: wc_AesEaxDecryptAuth must reject authTagSz below
|
||||
* WOLFSSL_MIN_AUTH_TAG_SZ (including zero), otherwise an attacker could
|
||||
* bypass tag verification by supplying an empty tag. */
|
||||
#if WOLFSSL_MIN_AUTH_TAG_SZ > 0
|
||||
{
|
||||
byte zero_ct[16];
|
||||
byte zero_pt[16];
|
||||
byte zero_tag[16];
|
||||
XMEMSET(zero_ct, 0, sizeof(zero_ct));
|
||||
XMEMSET(zero_tag, 0, sizeof(zero_tag));
|
||||
|
||||
ret = wc_AesEaxDecryptAuth(vectors[0].key,
|
||||
(word32)vectors[0].key_length,
|
||||
zero_pt,
|
||||
zero_ct, (word32)sizeof(zero_ct),
|
||||
vectors[0].iv,
|
||||
(word32)vectors[0].iv_length,
|
||||
zero_tag, 0,
|
||||
vectors[0].aad,
|
||||
(word32)vectors[0].aad_length);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
#if WOLFSSL_MIN_AUTH_TAG_SZ > 1
|
||||
ret = wc_AesEaxDecryptAuth(vectors[0].key,
|
||||
(word32)vectors[0].key_length,
|
||||
zero_pt,
|
||||
zero_ct, (word32)sizeof(zero_ct),
|
||||
vectors[0].iv,
|
||||
(word32)vectors[0].iv_length,
|
||||
zero_tag, WOLFSSL_MIN_AUTH_TAG_SZ - 1,
|
||||
vectors[0].aad,
|
||||
(word32)vectors[0].aad_length);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Upper bound: authTagSz > WC_AES_BLOCK_SIZE must be rejected.
|
||||
* Pins the '>' operator in the validation against mutation to '>='
|
||||
* and prevents an over-read of the caller-supplied tag buffer. */
|
||||
ret = wc_AesEaxDecryptAuth(vectors[0].key,
|
||||
(word32)vectors[0].key_length,
|
||||
zero_pt,
|
||||
zero_ct, (word32)sizeof(zero_ct),
|
||||
vectors[0].iv,
|
||||
(word32)vectors[0].iv_length,
|
||||
zero_tag, WC_AES_BLOCK_SIZE + 1,
|
||||
vectors[0].aad,
|
||||
(word32)vectors[0].aad_length);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
/* Direct incremental-API coverage: wc_AesEaxDecryptFinal must also
|
||||
* reject authInSz of zero and below WOLFSSL_MIN_AUTH_TAG_SZ. The
|
||||
* one-shot API above is a separate code path. Heap-allocate the
|
||||
* AesEax context to keep stack usage within Linux kernel limits. */
|
||||
{
|
||||
AesEax *eax = (AesEax *)XMALLOC(sizeof(*eax), HEAP_HINT,
|
||||
DYNAMIC_TYPE_TMP_BUFFER);
|
||||
if (eax == NULL) {
|
||||
return WC_TEST_RET_ENC_NC;
|
||||
}
|
||||
XMEMSET(eax, 0, sizeof(*eax));
|
||||
ret = wc_AesEaxInit(eax,
|
||||
vectors[0].key, (word32)vectors[0].key_length,
|
||||
vectors[0].iv, (word32)vectors[0].iv_length,
|
||||
vectors[0].aad,
|
||||
(word32)vectors[0].aad_length);
|
||||
if (ret != 0) {
|
||||
XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
ret = wc_AesEaxDecryptFinal(eax, zero_tag, 0);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
wc_AesEaxFree(eax);
|
||||
XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
#if WOLFSSL_MIN_AUTH_TAG_SZ > 1
|
||||
ret = wc_AesEaxDecryptFinal(eax, zero_tag,
|
||||
WOLFSSL_MIN_AUTH_TAG_SZ - 1);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
wc_AesEaxFree(eax);
|
||||
XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Upper bound: authInSz > WC_AES_BLOCK_SIZE must be rejected. */
|
||||
ret = wc_AesEaxDecryptFinal(eax, zero_tag, WC_AES_BLOCK_SIZE + 1);
|
||||
if (ret != WC_NO_ERR_TRACE(BAD_FUNC_ARG)) {
|
||||
wc_AesEaxFree(eax);
|
||||
XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
return WC_TEST_RET_ENC_EC(ret);
|
||||
}
|
||||
|
||||
wc_AesEaxFree(eax);
|
||||
XFREE(eax, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
|
||||
}
|
||||
}
|
||||
#endif /* WOLFSSL_MIN_AUTH_TAG_SZ > 0 */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+14
-5
@@ -3189,9 +3189,13 @@ WOLFSSL_LOCAL int TLSX_SupportExtensions(WOLFSSL* ssl);
|
||||
WOLFSSL_LOCAL int TLSX_PopulateExtensions(WOLFSSL* ssl, byte isRequest);
|
||||
|
||||
#if defined(WOLFSSL_TLS13) || !defined(NO_WOLFSSL_CLIENT)
|
||||
WOLFSSL_LOCAL int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType,
|
||||
#ifdef WOLFSSL_API_PREFIX_MAP
|
||||
#define TLSX_GetRequestSize wolfSSL_TLSX_GetRequestSize
|
||||
#define TLSX_WriteRequest wolfSSL_TLSX_WriteRequest
|
||||
#endif
|
||||
WOLFSSL_TEST_VIS int TLSX_GetRequestSize(WOLFSSL* ssl, byte msgType,
|
||||
word32* pLength);
|
||||
WOLFSSL_LOCAL int TLSX_WriteRequest(WOLFSSL* ssl, byte* output,
|
||||
WOLFSSL_TEST_VIS int TLSX_WriteRequest(WOLFSSL* ssl, byte* output,
|
||||
byte msgType, word32* pOffset);
|
||||
#endif
|
||||
|
||||
@@ -3597,11 +3601,16 @@ typedef struct TicketEncCbCtx {
|
||||
|
||||
#endif /* !WOLFSSL_NO_DEF_TICKET_ENC_CB && !NO_WOLFSSL_SERVER */
|
||||
|
||||
WOLFSSL_LOCAL int TLSX_UseSessionTicket(TLSX** extensions,
|
||||
#ifdef WOLFSSL_API_PREFIX_MAP
|
||||
#define TLSX_UseSessionTicket wolfSSL_TLSX_UseSessionTicket
|
||||
#define TLSX_SessionTicket_Create wolfSSL_TLSX_SessionTicket_Create
|
||||
#define TLSX_SessionTicket_Free wolfSSL_TLSX_SessionTicket_Free
|
||||
#endif
|
||||
WOLFSSL_TEST_VIS int TLSX_UseSessionTicket(TLSX** extensions,
|
||||
SessionTicket* ticket, void* heap);
|
||||
WOLFSSL_LOCAL SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
|
||||
WOLFSSL_TEST_VIS SessionTicket* TLSX_SessionTicket_Create(word32 lifetime,
|
||||
byte* data, word16 size, void* heap);
|
||||
WOLFSSL_LOCAL void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap);
|
||||
WOLFSSL_TEST_VIS void TLSX_SessionTicket_Free(SessionTicket* ticket, void* heap);
|
||||
|
||||
#endif /* HAVE_SESSION_TICKET */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user