From b3204f7aeca714f3a2c722314a01a9d371505e12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 1 Apr 2026 14:23:53 +0200 Subject: [PATCH 1/5] fix for wc_DhAgree public key validation Reported by: Nicholas Carlini --- tests/api.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ wolfcrypt/src/dh.c | 3 +- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/tests/api.c b/tests/api.c index 91315cf464..910a7869ae 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34642,6 +34642,78 @@ static int test_sniffer_chain_input_overflow(void) } #endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_CHAIN_INPUT */ +/* Test: wc_DhAgree must reject p-1 as peer public key. + * ffdhe2048 p ends with ...FFFFFFFFFFFFFFFF so p-1 ends ...FFFFFFFFFFFFFFFE */ +static int test_DhAgree_rejects_p_minus_1(void) +{ + EXPECT_DECLS; +#if !defined(HAVE_SELFTEST) && !defined(NO_DH) && \ + defined(HAVE_FFDHE_2048) && !defined(WOLFSSL_NO_MALLOC) && \ + (!defined(HAVE_FIPS) || FIPS_VERSION_GT(2,0)) + DhKey key; + WC_RNG rng; + byte priv[256], pub[256], agree[256]; + word32 privSz = sizeof(priv), pubSz = sizeof(pub), agreeSz; + + /* ffdhe2048 p - copied from wolfcrypt/src/dh.c dh_ffdhe2048_p. + * p ends with 0xFF*8 so p-1 ends with ...0xFE */ + static const byte ffdhe2048_p[256] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + 0xAD,0xF8,0x54,0x58,0xA2,0xBB,0x4A,0x9A, + 0xAF,0xDC,0x56,0x20,0x27,0x3D,0x3C,0xF1, + 0xD8,0xB9,0xC5,0x83,0xCE,0x2D,0x36,0x95, + 0xA9,0xE1,0x36,0x41,0x14,0x64,0x33,0xFB, + 0xCC,0x93,0x9D,0xCE,0x24,0x9B,0x3E,0xF9, + 0x7D,0x2F,0xE3,0x63,0x63,0x0C,0x75,0xD8, + 0xF6,0x81,0xB2,0x02,0xAE,0xC4,0x61,0x7A, + 0xD3,0xDF,0x1E,0xD5,0xD5,0xFD,0x65,0x61, + 0x24,0x33,0xF5,0x1F,0x5F,0x06,0x6E,0xD0, + 0x85,0x63,0x65,0x55,0x3D,0xED,0x1A,0xF3, + 0xB5,0x57,0x13,0x5E,0x7F,0x57,0xC9,0x35, + 0x98,0x4F,0x0C,0x70,0xE0,0xE6,0x8B,0x77, + 0xE2,0xA6,0x89,0xDA,0xF3,0xEF,0xE8,0x72, + 0x1D,0xF1,0x58,0xA1,0x36,0xAD,0xE7,0x35, + 0x30,0xAC,0xCA,0x4F,0x48,0x3A,0x79,0x7A, + 0xBC,0x0A,0xB1,0x82,0xB3,0x24,0xFB,0x61, + 0xD1,0x08,0xA9,0x4B,0xB2,0xC8,0xE3,0xFB, + 0xB9,0x6A,0xDA,0xB7,0x60,0xD7,0xF4,0x68, + 0x1D,0x4F,0x42,0xA3,0xDE,0x39,0x4D,0xF4, + 0xAE,0x56,0xED,0xE7,0x63,0x72,0xBB,0x19, + 0x0B,0x07,0xA7,0xC8,0xEE,0x0A,0x6D,0x70, + 0x9E,0x02,0xFC,0xE1,0xCD,0xF7,0xE2,0xEC, + 0xC0,0x34,0x04,0xCD,0x28,0x34,0x2F,0x61, + 0x91,0x72,0xFE,0x9C,0xE9,0x85,0x83,0xFF, + 0x8E,0x4F,0x12,0x32,0xEE,0xF2,0x81,0x83, + 0xC3,0xFE,0x3B,0x1B,0x4C,0x6F,0xAD,0x73, + 0x3B,0xB5,0xFC,0xBC,0x2E,0xC2,0x20,0x05, + 0xC5,0x8E,0xF1,0x83,0x7D,0x16,0x83,0xB2, + 0xC6,0xF3,0x4A,0x26,0xC1,0xB2,0xEF,0xFA, + 0x88,0x6B,0x42,0x38,0x61,0x28,0x5C,0x97, + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF + }; + byte pMinus1[256]; + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_InitDhKey(&key), 0); + ExpectIntEQ(wc_DhSetNamedKey(&key, WC_FFDHE_2048), 0); + ExpectIntEQ(wc_DhGenerateKeyPair(&key, &rng, priv, &privSz, + pub, &pubSz), 0); + + /* Construct p-1: last byte FF -> FE */ + XMEMCPY(pMinus1, ffdhe2048_p, sizeof(ffdhe2048_p)); + pMinus1[255] -= 1; + + /* wc_DhAgree must reject p-1 */ + agreeSz = sizeof(agree); + ExpectIntNE(wc_DhAgree(&key, agree, &agreeSz, priv, privSz, + pMinus1, sizeof(pMinus1)), 0); + + wc_FreeDhKey(&key); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35455,6 +35527,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_ocsp_responder), TEST_TLS_DECLS, TEST_DECL(test_wc_DhSetNamedKey), + TEST_DECL(test_DhAgree_rejects_p_minus_1), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) TEST_DECL(test_sniffer_chain_input_overflow), diff --git a/wolfcrypt/src/dh.c b/wolfcrypt/src/dh.c index f98aa3b088..72b8717a56 100644 --- a/wolfcrypt/src/dh.c +++ b/wolfcrypt/src/dh.c @@ -2030,12 +2030,13 @@ static int wc_DhAgree_Sync(DhKey* key, byte* agree, word32* agreeSz, WOLFSSL_MSG("wc_DhAgree wc_DhCheckPrivKey failed"); return DH_CHECK_PRIV_E; } +#endif + /* Always validate peer public key (2 <= y <= p-2) per SP 800-56A */ if (wc_DhCheckPubKey(key, otherPub, pubSz) != 0) { WOLFSSL_MSG("wc_DhAgree wc_DhCheckPubKey failed"); return DH_CHECK_PUB_E; } -#endif #if defined(WOLFSSL_SMALL_STACK) && !defined(WOLFSSL_NO_MALLOC) y = (mp_int*)XMALLOC(sizeof(mp_int), key->heap, DYNAMIC_TYPE_DH); From 7373397cbb0e4699427c3d740ee02ab1b6eb5449 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 1 Apr 2026 15:18:20 +0200 Subject: [PATCH 2/5] Cap DTLS1.3 max ACK records to prevent overflow Reported by: Nicholas Carlini --- src/dtls13.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/dtls13.c b/src/dtls13.c index 644af22617..ee04eb10a3 100644 --- a/src/dtls13.c +++ b/src/dtls13.c @@ -720,9 +720,14 @@ static Dtls13RecordNumber* Dtls13NewRecordNumber(w64wrapper epoch, return rn; } +#ifndef DTLS13_MAX_ACK_RECORDS +#define DTLS13_MAX_ACK_RECORDS 512 +#endif + int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) { Dtls13RecordNumber* rn; + int count; WOLFSSL_ENTER("Dtls13RtxAddAck"); @@ -741,7 +746,9 @@ int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) return 0; /* list full, silently drop */ } + count = 0; for (; cur != NULL; prevNext = &cur->next, cur = cur->next) { + count++; if (w64Equal(cur->epoch, epoch) && w64Equal(cur->seq, seq)) { /* already in list. no duplicates. */ #ifdef WOLFSSL_RW_THREADED @@ -756,6 +763,16 @@ int Dtls13RtxAddAck(WOLFSSL* ssl, w64wrapper epoch, w64wrapper seq) } } + /* Cap the ACK list to prevent word16 overflow in + * Dtls13GetAckListLength and bound memory consumption */ + if (count >= DTLS13_MAX_ACK_RECORDS) { + WOLFSSL_MSG("DTLS 1.3 ACK list full, dropping record"); + #ifdef WOLFSSL_RW_THREADED + wc_UnLockMutex(&ssl->dtls13Rtx.mutex); + #endif + return 0; + } + rn = Dtls13NewRecordNumber(epoch, seq, ssl->heap); if (rn == NULL) { #ifdef WOLFSSL_RW_THREADED From af19d9d4ef24562db4830ceded9affb7dfdefef2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 1 Apr 2026 15:48:55 +0200 Subject: [PATCH 3/5] Properly reject Ed448 identity public key Reported by: Nicholas Carlini --- tests/api.c | 36 ++++++++++++++++++++++++++++++++++++ wolfcrypt/src/ed448.c | 32 +++++++++++++++++++++++++++++--- 2 files changed, 65 insertions(+), 3 deletions(-) diff --git a/tests/api.c b/tests/api.c index 910a7869ae..1f49779d2e 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34714,6 +34714,41 @@ static int test_DhAgree_rejects_p_minus_1(void) return EXPECT_RESULT(); } +/* Test: Ed448 must reject identity public key (0,1) */ +static int test_ed448_rejects_identity_key(void) +{ + EXPECT_DECLS; +#ifdef HAVE_ED448 + ed448_key key; + byte identity[ED448_PUB_KEY_SIZE]; + byte forged_sig[ED448_SIG_SIZE]; + const byte msg[] = "test message"; + int res = 0; + + XMEMSET(identity, 0, sizeof(identity)); + identity[0] = 0x01; /* identity (0,1) encoding */ + + XMEMSET(forged_sig, 0, sizeof(forged_sig)); + forged_sig[0] = 0x01; /* R = identity, S = 0 */ + + ExpectIntEQ(wc_ed448_init(&key), 0); + + /* The identity public key must be rejected at import time. */ + ExpectIntNE(wc_ed448_import_public(identity, sizeof(identity), &key), 0); + + /* If import somehow succeeded, verify must also reject the forgery. */ + if (EXPECT_SUCCESS() && key.pubKeySet) { + int verifyRet = wc_ed448_verify_msg(forged_sig, sizeof(forged_sig), + msg, sizeof(msg) - 1, + &res, &key, NULL, 0); + ExpectTrue(verifyRet != 0 || res == 0); + } + + wc_ed448_free(&key); +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35528,6 +35563,7 @@ TEST_CASE testCases[] = { TEST_TLS_DECLS, TEST_DECL(test_wc_DhSetNamedKey), TEST_DECL(test_DhAgree_rejects_p_minus_1), + TEST_DECL(test_ed448_rejects_identity_key), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) TEST_DECL(test_sniffer_chain_input_overflow), diff --git a/wolfcrypt/src/ed448.c b/wolfcrypt/src/ed448.c index 37674cad51..6525b5bb4a 100644 --- a/wolfcrypt/src/ed448.c +++ b/wolfcrypt/src/ed448.c @@ -711,6 +711,18 @@ static int ed448_verify_msg_final_with_sha(const byte* sig, word32 sigLen, if (i == -1) return BAD_FUNC_ARG; + /* Reject identity public key (0,1): 0x01 followed by 56 zero bytes. */ + { + int isIdentity = (key->p[0] == 0x01); + int j; + for (j = 1; j < ED448_PUB_KEY_SIZE && isIdentity; j++) { + if (key->p[j] != 0x00) + isIdentity = 0; + } + if (isIdentity) + return BAD_FUNC_ARG; + } + /* uncompress A (public key), test if valid, and negate it */ if (ge448_from_bytes_negate_vartime(&A, key->p) != 0) return BAD_FUNC_ARG; @@ -1335,14 +1347,28 @@ int wc_ed448_check_key(ed448_key* key) } /* No private key, check Y is valid. */ else if ((ret == 0) && (!key->privKeySet)) { - /* Verify that Q is not identity element 0. - * 0 has no representation for Ed448. */ + /* Reject the identity element (0, 1). + * Encoding: 0x01 followed by 56 zero bytes. */ + { + int isIdentity = 1; + int i; + if (key->p[0] != 0x01) + isIdentity = 0; + for (i = 1; i < ED448_PUB_KEY_SIZE && isIdentity; i++) { + if (key->p[i] != 0x00) + isIdentity = 0; + } + if (isIdentity) { + WOLFSSL_MSG("Ed448 public key is the identity element"); + ret = PUBLIC_KEY_E; + } + } /* Verify that xQ and yQ are integers in the interval [0, p - 1]. * Only have yQ so check that ordinate. * p = 2^448-2^224-1 = 0xff..fe..ff */ - { + if (ret == 0) { int i; ret = PUBLIC_KEY_E; From 08f9fa03d724a2858457ba392adb390e6ccd22f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 1 Apr 2026 16:06:22 +0200 Subject: [PATCH 4/5] Respect outputSz in PKCS7 decode methods Reported by: Nicholas Carlini --- tests/api.c | 84 +++++++++++++++++++++++++++++++++++++++++++ wolfcrypt/src/pkcs7.c | 9 +++++ 2 files changed, 93 insertions(+) diff --git a/tests/api.c b/tests/api.c index 1f49779d2e..d897effeb4 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34749,6 +34749,89 @@ static int test_ed448_rejects_identity_key(void) return EXPECT_RESULT(); } +/* Test: wc_PKCS7_DecodeEncryptedData must respect outputSz. + * 588-byte EncryptedData blob: 496 bytes of 'A' encrypted with + * AES-256-CBC under all-zero key/IV. Decrypted content is 496 bytes, + * but we pass a 128-byte output buffer. Must return BUFFER_E. */ +static int test_pkcs7_decode_encrypted_outputsz(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + !defined(WOLFSSL_NO_MALLOC) && defined(WOLFSSL_AES_256) + wc_PKCS7* p7 = NULL; + byte key[32]; + byte out[128]; /* smaller than 496-byte decrypted content */ + + static const byte encBlob[] = { + 0x30,0x82,0x02,0x48,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d, + 0x01,0x07,0x06,0xa0,0x82,0x02,0x39,0x30,0x82,0x02,0x35,0x02, + 0x01,0x00,0x30,0x82,0x02,0x2e,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,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x80,0x82,0x02,0x00,0x7e,0x0e,0x75,0x77,0xef,0x9c,0x30,0xa6, + 0xbf,0x0b,0x25,0xe0,0x62,0x1e,0x82,0x7e,0x76,0xf7,0x57,0xef, + 0x12,0x71,0xc5,0x74,0x0e,0x60,0xc3,0x3c,0x8e,0xc3,0xce,0x70, + 0xbd,0x3c,0xb4,0x60,0x76,0xb1,0xea,0xb5,0x4b,0xc1,0xfe,0x5b, + 0x88,0xfa,0x43,0x46,0x03,0x28,0x9d,0xab,0x3d,0xb5,0x88,0x50, + 0x07,0xfc,0x3c,0xc9,0x49,0x4d,0x68,0x97,0xb1,0x59,0xb6,0x35, + 0xa1,0x68,0x80,0xb0,0x8f,0xb2,0x21,0x9b,0xfc,0xd8,0xf0,0x67, + 0xbc,0x4b,0x9b,0x28,0x1c,0x3b,0xa0,0x40,0x76,0x8f,0x1f,0x9f, + 0x87,0xe6,0xe4,0xa5,0xdc,0x9a,0x9a,0x84,0x86,0xff,0x43,0x8a, + 0x57,0x8d,0x97,0xd2,0x8f,0x67,0x90,0xd8,0x4f,0x0f,0x0d,0xac, + 0x56,0x6d,0x51,0x33,0xa3,0x37,0x9e,0xe9,0xbf,0x07,0xab,0x93, + 0xbc,0xbe,0xc1,0x64,0x07,0x30,0xf0,0xff,0x89,0x0a,0x36,0x1c, + 0xc8,0xe9,0xae,0x24,0x1f,0x95,0x5c,0xa1,0x8a,0x3e,0x15,0x86, + 0x49,0x70,0x55,0xc2,0xa5,0x90,0xb9,0xda,0x2b,0x97,0x6f,0x5f, + 0x60,0x61,0xc9,0xcf,0x4d,0x3a,0xb1,0xe4,0x9f,0x37,0x0f,0xf3, + 0xba,0x85,0x04,0x52,0x68,0x00,0x47,0x3d,0x83,0xc1,0x3f,0xc6, + 0x70,0x97,0xc1,0x0c,0xc0,0x0b,0xa2,0xcb,0x8c,0x88,0xb5,0x01, + 0x29,0x7f,0x7e,0xd2,0x46,0x24,0x82,0x92,0x28,0xdd,0x49,0x53, + 0x0f,0x76,0xad,0x4b,0x81,0x85,0x3a,0x9f,0xda,0x0d,0x69,0xe2, + 0x57,0xb9,0xe9,0xfa,0x53,0xed,0x20,0x6f,0x62,0x43,0x1d,0x21, + 0xa9,0x53,0x3d,0xd5,0xb9,0x1a,0x4b,0x3e,0xb7,0x05,0x87,0xb6, + 0xe3,0xfe,0x03,0x09,0xe1,0x74,0x34,0x42,0x84,0xb1,0x06,0xf9, + 0xfe,0x64,0xc1,0xd2,0xce,0x3d,0x29,0xf4,0x94,0xb8,0xfc,0xbe, + 0xb1,0x90,0xd6,0x38,0x61,0x4e,0x43,0x36,0x4e,0x27,0x87,0xd3, + 0x24,0xdc,0x34,0xf0,0x28,0x2d,0xc8,0xff,0x89,0x9f,0xeb,0xee, + 0x0d,0x45,0xfb,0xc5,0x53,0xd3,0xdf,0xcb,0xf8,0xeb,0x7e,0xcb, + 0x2a,0xd7,0xa2,0xd6,0x6a,0xf1,0xb8,0x24,0xa1,0x05,0x16,0x56, + 0x2e,0x03,0xfe,0x21,0x19,0x36,0x8a,0xeb,0x50,0x8d,0x42,0x31, + 0x1d,0xb3,0x0a,0x13,0x1d,0x16,0x09,0x0b,0x40,0x4e,0x90,0x78, + 0xbf,0x41,0x5f,0x4d,0xd9,0xce,0x91,0xaa,0xd0,0x38,0x5c,0xfb, + 0xc4,0x9f,0x55,0x4e,0x04,0x9d,0xd0,0xca,0x74,0x3c,0x6c,0x01, + 0x1b,0x84,0x76,0xfa,0xba,0x5d,0x2d,0x35,0x7e,0xe3,0x37,0x8b, + 0x72,0x0a,0xbb,0xa4,0x4d,0x56,0x2b,0x3b,0x9f,0x5a,0xa0,0xe3, + 0x60,0x26,0x65,0x09,0xed,0xfc,0x10,0x0c,0xa1,0x4f,0x12,0x76, + 0xe2,0x4c,0xbd,0x1b,0x67,0xb4,0xb4,0x54,0x5c,0x38,0x47,0x8b, + 0x7e,0x35,0x8e,0x4d,0x4e,0xef,0x91,0x3f,0xff,0x16,0x0a,0x42, + 0x0b,0xe5,0x28,0x26,0x24,0x9f,0x6f,0xb4,0x63,0x8a,0xa1,0x52, + 0x22,0x3a,0xdb,0xd9,0x8e,0x76,0x6e,0x6a,0xa9,0xa1,0xec,0xf2, + 0x9c,0x58,0xf8,0x6b,0xdd,0xf2,0xf8,0x2d,0xcb,0x8c,0x96,0xda, + 0xb4,0x9c,0x44,0xdc,0xab,0x43,0x2c,0x22,0xf3,0xfe,0x1a,0xb9, + 0x3b,0x82,0x30,0xa2,0xc7,0xef,0x52,0x08,0x7b,0x4f,0x3f,0x7a, + 0x7e,0x28,0xd7,0x67,0xb9,0xb1,0x69,0x9b,0x96,0x3e,0xc5,0xe4, + 0x45,0xb0,0x23,0x75,0x00,0xda,0xee,0xdb,0x6d,0xa9,0xe7,0x98 + }; + + XMEMSET(key, 0, sizeof(key)); + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + p7->encryptionKey = key; + p7->encryptionKeySz = sizeof(key); + + /* 496-byte content into 128-byte buffer must return BUFFER_E */ + ExpectIntLT(wc_PKCS7_DecodeEncryptedData(p7, (byte*)encBlob, + (word32)sizeof(encBlob), out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35564,6 +35647,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_wc_DhSetNamedKey), TEST_DECL(test_DhAgree_rejects_p_minus_1), TEST_DECL(test_ed448_rejects_identity_key), + TEST_DECL(test_pkcs7_decode_encrypted_outputsz), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) TEST_DECL(test_sniffer_chain_input_overflow), diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 9efec15ec7..6e336d2c57 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -14606,6 +14606,10 @@ int wc_PKCS7_DecodeAuthEnvelopedData(wc_PKCS7* pkcs7, byte* in, } /* copy plaintext to output */ + if ((word32)encryptedContentSz > outputSz) { + ret = BUFFER_E; + break; + } XMEMCPY(output, encryptedContent, (word32)encryptedContentSz); /* free memory, zero out keys */ @@ -15290,6 +15294,11 @@ int wc_PKCS7_DecodeEncryptedData(wc_PKCS7* pkcs7, byte* in, word32 inSz, } /* copy plaintext to output */ + if ((word32)(encryptedContentSz - padLen) > outputSz) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ret = BUFFER_E; + break; + } XMEMCPY(output, encryptedContent, (unsigned int)(encryptedContentSz - padLen)); From 559d88e02e128bcec1eb630bd9f9a97894511734 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tobias=20Frauenschl=C3=A4ger?= Date: Wed, 1 Apr 2026 16:16:41 +0200 Subject: [PATCH 5/5] Fix stack buffer overflow in wc_PKCS7_DecryptOri Reported by: Nicholas Carlini --- tests/api.c | 60 +++++++++++++++++++++++++++++++++++++++++++ wolfcrypt/src/pkcs7.c | 5 ++++ 2 files changed, 65 insertions(+) diff --git a/tests/api.c b/tests/api.c index d897effeb4..cc34a0fd23 100644 --- a/tests/api.c +++ b/tests/api.c @@ -34832,6 +34832,65 @@ static int test_pkcs7_decode_encrypted_outputsz(void) return EXPECT_RESULT(); } +/* Dummy ORI callback for PKCS#7 ORI overflow test */ +#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC) +static int test_dummy_ori_cb(wc_PKCS7* pkcs7, byte* oriType, word32 oriTypeSz, + byte* oriValue, word32 oriValueSz, + byte* decryptedKey, word32* decryptedKeySz, + void* ctx) +{ + (void)pkcs7; (void)oriType; (void)oriTypeSz; + (void)oriValue; (void)oriValueSz; + (void)decryptedKey; (void)decryptedKeySz; (void)ctx; + return -1; +} +#endif + +/* Test: PKCS#7 ORI must reject OID larger than MAX_OID_SZ (32) */ +static int test_pkcs7_ori_oversized_oid(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC) + wc_PKCS7* p7 = NULL; + byte out[256]; + + /* EnvelopedData with [4] IMPLICIT ORI containing an 80-byte OID */ + static const byte poc[] = { + 0x30, 0x6b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03, + 0xa0, 0x5e, + 0x30, 0x5c, + 0x02, 0x01, 0x00, + 0x31, 0x57, + 0xa4, 0x55, + 0x06, 0x50, + 0x2a, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, + 0x04, 0x01, 0x00 + }; + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + wc_PKCS7_SetOriDecryptCb(p7, test_dummy_ori_cb); + + /* Must return error (ASN_PARSE_E), not overflow the stack */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + TEST_CASE testCases[] = { TEST_DECL(test_fileAccess), @@ -35648,6 +35707,7 @@ TEST_CASE testCases[] = { TEST_DECL(test_DhAgree_rejects_p_minus_1), TEST_DECL(test_ed448_rejects_identity_key), TEST_DECL(test_pkcs7_decode_encrypted_outputsz), + TEST_DECL(test_pkcs7_ori_oversized_oid), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) TEST_DECL(test_sniffer_chain_input_overflow), diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 6e336d2c57..52dadfa748 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -11507,6 +11507,11 @@ 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 <= 0 || (word32)oriOIDSz > MAX_OID_SZ) { + WOLFSSL_MSG("ORI oriType OID too large"); + return ASN_PARSE_E; + } + XMEMCPY(oriOID, pkiMsg + *idx, (word32)oriOIDSz); *idx += (word32)oriOIDSz;