diff --git a/src/internal.c b/src/internal.c index c0d9680d2c..fc69accdae 100644 --- a/src/internal.c +++ b/src/internal.c @@ -9728,8 +9728,9 @@ static DtlsFragBucket* DtlsMsgCombineFragBuckets(DtlsMsg* msg, newBucket->m.m.next = next; } } - /* Adjust size in msg */ - msg->bytesReceived += newSz - overlapSz; + /* Adjust size in msg. Cap at dataSz to avoid counting gap bytes + * between a non-overlapping fragment and bucket as received. */ + msg->bytesReceived += min(newSz - overlapSz, dataSz); newBucket->m.m.offset = newOffset; newBucket->m.m.sz = newSz; return newBucket; diff --git a/src/ssl_sess.c b/src/ssl_sess.c index e1c3cab7b7..10a0471182 100644 --- a/src/ssl_sess.c +++ b/src/ssl_sess.c @@ -522,6 +522,22 @@ int wolfSSL_memrestore_session_cache(const void* mem, int sz) #endif XMEMCPY(&SessionCache[i], row++, SIZEOF_SESSION_ROW); + #ifndef SESSION_CACHE_DYNAMIC_MEM + /* Reset pointers to safe values after raw copy */ + { + int j; + for (j = 0; j < SESSIONS_PER_ROW; j++) { + WOLFSSL_SESSION* s = &SessionCache[i].Sessions[j]; + #ifdef HAVE_SESSION_TICKET + s->ticket = s->staticTicket; + s->ticketLenAlloc = 0; + #endif + #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) + s->peer = NULL; + #endif + } + } + #endif #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif @@ -681,6 +697,22 @@ int wolfSSL_restore_session_cache(const char *fname) #endif ret = (int)XFREAD(&SessionCache[i], SIZEOF_SESSION_ROW, 1, file); + #ifndef SESSION_CACHE_DYNAMIC_MEM + /* Reset pointers to safe values after raw copy */ + { + int j; + for (j = 0; j < SESSIONS_PER_ROW; j++) { + WOLFSSL_SESSION* s = &SessionCache[i].Sessions[j]; + #ifdef HAVE_SESSION_TICKET + s->ticket = s->staticTicket; + s->ticketLenAlloc = 0; + #endif + #if defined(SESSION_CERTS) && defined(OPENSSL_EXTRA) + s->peer = NULL; + #endif + } + } + #endif #ifdef ENABLE_SESSION_CACHE_ROW_LOCK SESSION_ROW_UNLOCK(&SessionCache[i]); #endif diff --git a/src/tls.c b/src/tls.c index 2208b4c595..da26ee5e9a 100644 --- a/src/tls.c +++ b/src/tls.c @@ -9937,6 +9937,10 @@ static int TLSX_KeyShare_ProcessPqcClient_ex(WOLFSSL* ssl, } #endif + if (ret == 0 && keyShareEntry->keLen < ctSz) { + WOLFSSL_MSG("PQC key share data too short for ciphertext."); + ret = BUFFER_E; + } if (ret == 0) { ret = wc_KyberKey_Decapsulate(kem, ssOutput, keyShareEntry->ke, ctSz); diff --git a/tests/api.c b/tests/api.c index fdb8debb00..bbb0bc75e3 100644 --- a/tests/api.c +++ b/tests/api.c @@ -165,6 +165,19 @@ #include #endif +#ifdef HAVE_DILITHIUM + #include +#endif +#if defined(WOLFSSL_HAVE_MLKEM) + #include +#endif +#if defined(HAVE_PKCS7) + #include +#endif +#if !defined(NO_BIG_INT) + #include +#endif + /* include misc.c here regardless of NO_INLINE, because misc.c implementations * have default (hidden) visibility, and in the absence of visibility, it's * benign to mask out the library implementation. @@ -34270,6 +34283,332 @@ static int test_sniffer_chain_input_overflow(void) } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_CHAIN_INPUT */ +/*----------------------------------------------------------------------------* + | ZD 21412-21426: Regression tests for reported vulnerabilities + *----------------------------------------------------------------------------*/ + +/* ZD 21412: ML-DSA HashML-DSA verify must reject hashLen > WC_MAX_DIGEST_SIZE */ +static int test_zd21412_mldsa_verify_hash_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \ + !defined(WOLFSSL_DILITHIUM_NO_VERIFY) + dilithium_key key; + WC_RNG rng; + int res = 0; + byte sig[4000]; + byte hash[4096]; /* larger than WC_MAX_DIGEST_SIZE */ + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(sig, 0x41, sizeof(sig)); + XMEMSET(hash, 'A', sizeof(hash)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_dilithium_init(&key), 0); +#ifndef WOLFSSL_NO_ML_DSA_65 + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0); +#elif !defined(WOLFSSL_NO_ML_DSA_44) + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0); +#else + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0); +#endif + ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0); + + /* hashLen=4096 must be rejected with BUFFER_E, not overflow the stack */ + ExpectIntEQ(wc_dilithium_verify_ctx_hash(sig, sizeof(sig), NULL, 0, + WC_HASH_TYPE_SHA256, hash, sizeof(hash), &res, &key), + WC_NO_ERR_TRACE(BUFFER_E)); + + wc_dilithium_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + +/* ZD 21414: PKCS7 ORI OID must be bounds-checked against MAX_OID_SZ. + * This DER was generated by encoding a valid EnvelopedData with ORI, then + * patching the OID length from 10 to 64 (exceeds MAX_OID_SZ=32) and inserting + * 54 extra bytes. Without the fix, wc_PKCS7_DecryptOri copies 64 bytes into + * oriOID[32], causing a stack buffer overflow. */ +#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) +static int oriDecryptCb_zd21414(PKCS7* pkcs7, byte* oriType, word32 oriTypeSz, + byte* oriValue, word32 oriValueSz, byte* decryptedKey, + word32* decryptedKeySz, void* ctx) +{ + word32 keySz; + (void)pkcs7; (void)oriType; (void)oriTypeSz; (void)ctx; + if (oriValueSz < 2) return -1; + keySz = oriValue[1]; + if (*decryptedKeySz < keySz) return -1; + XMEMCPY(decryptedKey, oriValue + 2, keySz); + *decryptedKeySz = keySz; + return 0; +} +#endif /* HAVE_PKCS7 && AES_CBC && AES_256 */ +static int test_zd21414_pkcs7_ori_oid_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) + /* Pre-built malformed EnvelopedData: ORI OID length patched from 10 to 64 + * (0x40 at offset 26), with 54 extra 'X' bytes inserted after original OID. + * Generated by POC's encode-then-patch approach. */ + static const byte malformed[] = { + 0x30,0x81,0x82,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01, + 0x07,0x03,0xA0,0x75,0x30,0x73,0x02,0x01,0x03,0x31,0x30,0xA4, + 0x2E,0x06,0x40,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01, + 0x00,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58, + 0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58, + 0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58, + 0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58, + 0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x04,0x20,0xEB,0xCE,0xF2, + 0x43,0xC1,0xCE,0xF8,0xCD,0xE2,0xC1,0x12,0xDB,0x46,0xFE,0x39, + 0xF0,0x2C,0xF2,0x9A,0x6A,0xD4,0x90,0xB8,0xAC,0x72,0x17,0x54, + 0x97,0xAE,0xE2,0x59,0xEA,0x30,0x3C,0x06,0x09,0x2A,0x86,0x48, + 0x86,0xF7,0x0D,0x01,0x07,0x01,0x30,0x1D,0x06,0x09,0x60,0x86, + 0x48,0x01,0x65,0x03,0x04,0x01,0x2A,0x04,0x10,0x98,0xBC,0x3A, + 0xF9,0x15,0xF3,0x6B,0x53,0x9D,0x81,0xBD,0x44,0x7E,0xA1,0x01, + 0x4D,0x80,0x10,0xBD,0x36,0x26,0xF5,0x6E,0x11,0xD0,0xBB,0x5C, + 0x15,0x19,0xA7,0x6B,0xC2,0xC2,0xB6 + }; /* 187 bytes */ + PKCS7* pkcs7 = NULL; + byte decoded[256]; + + ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID)); + if (pkcs7 != NULL) { + /* ORI decrypt callback must be set or parser skips ORI processing */ + wc_PKCS7_SetOriDecryptCb(pkcs7, oriDecryptCb_zd21414); + /* Without fix: overflows oriOID[32] → crash. + * With fix: returns BUFFER_E before the copy. */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(pkcs7, (byte*)malformed, + (word32)sizeof(malformed), decoded, sizeof(decoded)), 0); + wc_PKCS7_Free(pkcs7); + } +#endif + return EXPECT_RESULT(); +} + + +/* ZD 21417: Dilithium verify_ctx_msg must reject absurdly large msgLen */ +static int test_zd21417_dilithium_hash_int_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \ + !defined(WOLFSSL_DILITHIUM_NO_VERIFY) + dilithium_key key; + WC_RNG rng; + int res = 0; + byte sig[4000]; + byte msg[64]; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(sig, 0, sizeof(sig)); + XMEMSET(msg, 'A', sizeof(msg)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_dilithium_init(&key), 0); +#ifndef WOLFSSL_NO_ML_DSA_65 + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0); +#elif !defined(WOLFSSL_NO_ML_DSA_44) + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0); +#else + ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0); +#endif + ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0); + + /* msgLen=0xFFFFFFC0 would cause integer overflow in hash computation. + * Must be rejected with BAD_FUNC_ARG. */ + ExpectIntEQ(wc_dilithium_verify_ctx_msg(sig, sizeof(sig), NULL, 0, + msg, 0xFFFFFFC0u, &res, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + wc_dilithium_free(&key); + DoExpectIntEQ(wc_FreeRng(&rng), 0); +#endif + return EXPECT_RESULT(); +} + +/* ZD 21418: ECC import must validate point is on curve */ +static int test_zd21418_ecc_invalid_curve_point(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ECC) && !defined(NO_ECC256) + ecc_key peerKey; + /* Invalid point (3, 3) — NOT on P-256 curve y^2 = x^3 - 3x + b */ + byte badPt[65]; + + XMEMSET(&peerKey, 0, sizeof(peerKey)); + XMEMSET(badPt, 0, sizeof(badPt)); + badPt[0] = 0x04; /* uncompressed */ + badPt[32] = 3; /* x = 3 */ + badPt[64] = 3; /* y = 3 */ + + ExpectIntEQ(wc_ecc_init(&peerKey), 0); + + /* Import of invalid point as untrusted (TLS peer) must be rejected. + * Uses _ex2 with untrusted=1 to match the TLS key exchange path. */ + ExpectIntNE(wc_ecc_import_x963_ex2(badPt, sizeof(badPt), &peerKey, + ECC_SECP256R1, 1), 0); + + wc_ecc_free(&peerKey); +#endif + return EXPECT_RESULT(); +} + +/* ZD 21421: DTLS fragment reassembly must not inflate bytesReceived + * when a gap fragment is misrouted. Uses DtlsMsgStore/DtlsMsgFind + * internal APIs (same pattern as test_wolfSSL_DTLS_fragment_buckets). */ +static int test_zd21421_dtls_fragment_reassembly(void) +{ + EXPECT_DECLS; +#ifdef WOLFSSL_DTLS + WOLFSSL ssl[1]; + DtlsMsg* cur = NULL; + byte msg[100]; + int i; + word32 seq = 99; /* unique seq to avoid collision with other tests */ + + for (i = 0; i < 100; i++) + msg[i] = (byte)(i + 0x20); + XMEMSET(ssl, 0, sizeof(*ssl)); + + /* Simulate the gap fragment attack from ZD 21421: + * 1. Send fragment [0, 20) → bytesReceived = 20 + * 2. Send fragment [80, 100) → bytesReceived = 40 + * 3. Send GAP fragment [40, 50) — should NOT inflate bytesReceived + * Bug: old code matched fragOffset(40) <= curEnd(100) and misrouted + * the gap fragment, inflating bytesReceived to 80 instead of 50. + * 4. Send fragment [20, 40) → bytesReceived should be 50, NOT 80 */ + + /* Fragment [0, 20) */ + DtlsMsgStore(ssl, 0, seq, msg, 100, certificate, 0, 20, NULL); + ExpectNotNull(cur = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, seq)); + if (cur != NULL) { + ExpectIntEQ((int)cur->bytesReceived, 20); + ExpectIntEQ(cur->ready, 0); + } + + /* Fragment [80, 100) */ + DtlsMsgStore(ssl, 0, seq, msg + 80, 100, certificate, 80, 20, NULL); + ExpectNotNull(cur = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, seq)); + if (cur != NULL) { + ExpectIntEQ((int)cur->bytesReceived, 40); + ExpectIntEQ(cur->ready, 0); + } + + /* GAP fragment [40, 50) — this is the attack vector */ + DtlsMsgStore(ssl, 0, seq, msg + 40, 100, certificate, 40, 10, NULL); + ExpectNotNull(cur = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, seq)); + if (cur != NULL) { + /* With fix: bytesReceived = 50 (20+20+10), ready = 0 + * Without fix: bytesReceived = 80 (inflated by 30), ready = 0 */ + ExpectIntEQ((int)cur->bytesReceived, 50); + ExpectIntEQ(cur->ready, 0); + } + + /* Fragment [20, 40) */ + DtlsMsgStore(ssl, 0, seq, msg + 20, 100, certificate, 20, 20, NULL); + ExpectNotNull(cur = DtlsMsgFind(ssl->dtls_rx_msg_list, 0, seq)); + if (cur != NULL) { + /* With fix: bytesReceived = 70, still missing [50,80), NOT ready + * Without fix: bytesReceived = 100, prematurely ready with heap data */ + ExpectIntEQ((int)cur->bytesReceived, 70); + ExpectIntEQ(cur->ready, 0); + } + + DtlsMsgListDelete(ssl->dtls_rx_msg_list, ssl->heap); + ssl->dtls_rx_msg_list = NULL; + ssl->dtls_rx_msg_list_sz = 0; +#endif + return EXPECT_RESULT(); +} + +/* ZD 21422: PKCS7 CBC padding must validate all padding bytes */ +static int test_zd21422_pkcs7_padding_oracle(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && !defined(NO_PKCS7_ENCRYPTED_DATA) + PKCS7 pkcs7; + byte key[32]; + byte plaintext[27]; /* 27 bytes → padded to 32 → padding = 05 05 05 05 05 */ + byte encoded[4096]; + byte output[256]; + byte modified[4096]; + int encodedSz; + int outSz; + int ctOff = -1; + int ctLen = 0; + int i; + + XMEMSET(key, 0xAA, sizeof(key)); + XMEMSET(plaintext, 'X', sizeof(plaintext)); + + /* Encode EncryptedData */ + XMEMSET(&pkcs7, 0, sizeof(pkcs7)); + ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0); + pkcs7.content = plaintext; + pkcs7.contentSz = sizeof(plaintext); + pkcs7.contentOID = DATA; + pkcs7.encryptOID = AES256CBCb; + pkcs7.encryptionKey = key; + pkcs7.encryptionKeySz = sizeof(key); + + ExpectIntGT(encodedSz = wc_PKCS7_EncodeEncryptedData(&pkcs7, encoded, + sizeof(encoded)), 0); + + /* Verify normal decrypt works */ + ExpectIntEQ(outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, encoded, + (word32)encodedSz, output, sizeof(output)), (int)sizeof(plaintext)); + wc_PKCS7_Free(&pkcs7); + + /* Find ciphertext block in encoded DER */ + if (EXPECT_SUCCESS()) { + for (i = encodedSz - 10; i > 10; i--) { + if (encoded[i] == 0x04 || encoded[i] == 0x80) { + int len, lbytes; + if (encoded[i+1] < 0x80) { len = encoded[i+1]; lbytes = 1; } + else if (encoded[i+1] == 0x81) { len = encoded[i+2]; lbytes = 2; } + else continue; + if (len > 0 && len % 16 == 0 && + i + 1 + lbytes + len <= encodedSz) { + ctOff = i + 1 + lbytes; + ctLen = len; + break; + } + } + } + } + ExpectIntGT(ctOff, 0); + ExpectIntGE(ctLen, 32); + + /* Corrupt an interior padding byte via CBC bit-flip */ + if (EXPECT_SUCCESS()) { + XMEMCPY(modified, encoded, (size_t)encodedSz); + /* Flip byte in penultimate block to corrupt interior padding */ + modified[ctOff + ctLen - 32 + 11] ^= 0x42; + + /* Decrypt modified ciphertext — must fail, not succeed */ + XMEMSET(&pkcs7, 0, sizeof(pkcs7)); + ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0); + pkcs7.encryptionKey = key; + pkcs7.encryptionKeySz = sizeof(key); + + outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, modified, + (word32)encodedSz, output, sizeof(output)); + /* Must return an error — if it returns plaintext size, padding + * oracle vulnerability exists */ + ExpectIntLT(outSz, 0); + wc_PKCS7_Free(&pkcs7); + } +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35085,6 +35424,15 @@ TEST_CASE testCases[] = { TEST_DECL(test_sniffer_chain_input_overflow), #endif + /********************************* + * ZD 21412-21426 Regression Tests + *********************************/ + TEST_DECL(test_zd21412_mldsa_verify_hash_overflow), + TEST_DECL(test_zd21414_pkcs7_ori_oid_overflow), + TEST_DECL(test_zd21417_dilithium_hash_int_overflow), + TEST_DECL(test_zd21418_ecc_invalid_curve_point), + TEST_DECL(test_zd21421_dtls_fragment_reassembly), + TEST_DECL(test_zd21422_pkcs7_padding_oracle), /* This test needs to stay at the end to clean up any caches allocated. */ TEST_DECL(test_wolfSSL_Cleanup) }; diff --git a/wolfcrypt/src/curve25519.c b/wolfcrypt/src/curve25519.c index 9f58aa7a75..703795f7b4 100644 --- a/wolfcrypt/src/curve25519.c +++ b/wolfcrypt/src/curve25519.c @@ -1112,10 +1112,12 @@ curve25519_key* wc_curve25519_new(void* heap, int devId, int *result_code) } int wc_curve25519_delete(curve25519_key* key, curve25519_key** key_p) { + void* heap; if (key == NULL) return BAD_FUNC_ARG; + heap = key->heap; wc_curve25519_free(key); - XFREE(key, key->heap, DYNAMIC_TYPE_CURVE25519); + XFREE(key, heap, DYNAMIC_TYPE_CURVE25519); if (key_p != NULL) *key_p = NULL; return 0; diff --git a/wolfcrypt/src/dilithium.c b/wolfcrypt/src/dilithium.c index 17a2dd8eb5..65af77599f 100644 --- a/wolfcrypt/src/dilithium.c +++ b/wolfcrypt/src/dilithium.c @@ -503,6 +503,9 @@ static int dilithium_hash256(wc_Shake* shake256, const byte* data1, word64* state = shake256->s; word8 *state8 = (word8*)state; + if (data2Len > (UINT32_MAX - data1Len)) { + return BAD_FUNC_ARG; + } if (data1Len + data2Len >= WC_SHA3_256_COUNT * 8) { XMEMCPY(state8, data1, data1Len); XMEMCPY(state8 + data1Len, data2, WC_SHA3_256_COUNT * 8 - data1Len); @@ -9899,6 +9902,9 @@ static int dilithium_verify_ctx_hash(dilithium_key* key, const byte* ctx, if (key == NULL) { ret = BAD_FUNC_ARG; } + if ((ret == 0) && (hashLen > WC_MAX_DIGEST_SIZE)) { + ret = BUFFER_E; + } if (ret == 0) { /* Step 6: Hash public key. */ @@ -10491,6 +10497,10 @@ int wc_dilithium_verify_ctx_msg(const byte* sig, word32 sigLen, const byte* ctx, if ((ret == 0) && (ctx == NULL) && (ctxLen > 0)) { ret = BAD_FUNC_ARG; } + /* Reject msgLen that would cause integer overflow in hash computations */ + if ((ret == 0) && (msgLen > UINT32_MAX / 2)) { + ret = BAD_FUNC_ARG; + } #ifdef WOLF_CRYPTO_CB if (ret == 0) { @@ -10673,10 +10683,12 @@ dilithium_key* wc_dilithium_new(void* heap, int devId) int wc_dilithium_delete(dilithium_key* key, dilithium_key** key_p) { + void* heap; if (key == NULL) return BAD_FUNC_ARG; + heap = key->heap; wc_dilithium_free(key); - XFREE(key, key->heap, DYNAMIC_TYPE_DILITHIUM); + XFREE(key, heap, DYNAMIC_TYPE_DILITHIUM); if (key_p != NULL) *key_p = NULL; diff --git a/wolfcrypt/src/ecc.c b/wolfcrypt/src/ecc.c index 7940679aeb..d7b2aa33b0 100644 --- a/wolfcrypt/src/ecc.c +++ b/wolfcrypt/src/ecc.c @@ -11004,6 +11004,10 @@ int wc_ecc_import_x963_ex2(const byte* in, word32 inLen, ecc_key* key, #ifdef WOLFSSL_VALIDATE_ECC_IMPORT if (err == MP_OKAY) err = wc_ecc_check_key(key); +#else + /* Validate untrusted ECC imports to prevent invalid curve attacks */ + if ((err == MP_OKAY) && untrusted) + err = wc_ecc_check_key(key); #endif #if (!defined(WOLFSSL_VALIDATE_ECC_IMPORT) || \ !defined(HAVE_ECC_CHECK_PUBKEY_ORDER)) && \ diff --git a/wolfcrypt/src/ed25519.c b/wolfcrypt/src/ed25519.c index 2e30d74899..a38cb12a84 100644 --- a/wolfcrypt/src/ed25519.c +++ b/wolfcrypt/src/ed25519.c @@ -1033,10 +1033,12 @@ ed25519_key* wc_ed25519_new(void* heap, int devId, int *result_code) } int wc_ed25519_delete(ed25519_key* key, ed25519_key** key_p) { + void* heap; if (key == NULL) return BAD_FUNC_ARG; + heap = key->heap; wc_ed25519_free(key); - XFREE(key, key->heap, DYNAMIC_TYPE_ED25519); + XFREE(key, heap, DYNAMIC_TYPE_ED25519); if (key_p != NULL) *key_p = NULL; return 0; diff --git a/wolfcrypt/src/evp.c b/wolfcrypt/src/evp.c index 02fe3fa49b..a9021311c6 100644 --- a/wolfcrypt/src/evp.c +++ b/wolfcrypt/src/evp.c @@ -11840,7 +11840,7 @@ static int PrintHexWithColon(WOLFSSL_BIO* out, const byte* input, static int PrintPubKeyRSA(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx) { - byte buff[8] = { 0 }; + byte buff[24] = { 0 }; int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); word32 inOutIdx = 0; word32 nSz; /* size of modulus */ @@ -11984,7 +11984,7 @@ static int PrintPubKeyEC(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, { byte* pub = NULL; word32 pubSz = 0; - byte buff[8] = { 0 }; + byte buff[24] = { 0 }; int res = WOLFSSL_SUCCESS; word32 inOutIdx = 0; int curveId = 0; @@ -12173,7 +12173,7 @@ static int PrintPubKeyDSA(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx) { - byte buff[8] = { 0 }; + byte buff[24] = { 0 }; int length; int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); word32 inOutIdx = 0; @@ -12380,7 +12380,7 @@ static int PrintPubKeyDH(WOLFSSL_BIO* out, const byte* pkey, int pkeySz, int indent, int bitlen, WOLFSSL_ASN1_PCTX* pctx) { - byte buff[8] = { 0 }; + byte buff[24] = { 0 }; int res = WC_NO_ERR_TRACE(WOLFSSL_FAILURE); word32 length; word32 inOutIdx; diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 5a02c97d21..04c9ebc809 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -11504,6 +11504,9 @@ static int wc_PKCS7_DecryptOri(wc_PKCS7* pkcs7, byte* in, word32 inSz, if (GetASNObjectId(pkiMsg, idx, &oriOIDSz, pkiMsgSz) != 0) return ASN_PARSE_E; + if (oriOIDSz > MAX_OID_SZ) { + return BUFFER_E; + } XMEMCPY(oriOID, pkiMsg + *idx, (word32)oriOIDSz); *idx += (word32)oriOIDSz; @@ -13247,10 +13250,24 @@ int wc_PKCS7_DecodeEnvelopedData(wc_PKCS7* pkcs7, byte* in, padLen = encryptedContent[encryptedContentSz-1]; /* copy plaintext to output */ - if (padLen > encryptedContentSz) { + if (padLen == 0 || padLen > expBlockSz || + padLen > encryptedContentSz) { ret = BUFFER_E; break; } + /* Constant-time check all padding bytes */ + { + byte padCheck = 0; + int pi; + for (pi = encryptedContentSz - padLen; + pi < encryptedContentSz; pi++) { + padCheck |= encryptedContent[pi] ^ padLen; + } + if (padCheck != 0) { + ret = BUFFER_E; + break; + } + } #ifdef ASN_BER_TO_DER if (pkcs7->streamOutCb) { @@ -15279,12 +15296,28 @@ int wc_PKCS7_DecodeEncryptedData(wc_PKCS7* pkcs7, byte* in, word32 inSz, if (ret == 0) { padLen = encryptedContent[encryptedContentSz-1]; - if (padLen > encryptedContentSz) { + if (padLen == 0 || padLen > expBlockSz || + padLen > encryptedContentSz) { WOLFSSL_MSG("Bad padding size found"); ret = BUFFER_E; XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); break; } + /* Constant-time check all padding bytes */ + { + byte padCheck = 0; + int pi; + for (pi = encryptedContentSz - padLen; + pi < encryptedContentSz; pi++) { + padCheck |= encryptedContent[pi] ^ padLen; + } + if (padCheck != 0) { + WOLFSSL_MSG("Bad padding bytes found"); + ret = BUFFER_E; + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + break; + } + } /* copy plaintext to output */ XMEMCPY(output, encryptedContent,