Skip to content

Commit a0019fb

Browse files
committed
Add regression tests for fixes
1 parent c4bedf2 commit a0019fb

1 file changed

Lines changed: 279 additions & 0 deletions

File tree

tests/api.c

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,19 @@
165165
#include <sys/uio.h>
166166
#endif
167167

168+
#ifdef HAVE_DILITHIUM
169+
#include <wolfssl/wolfcrypt/dilithium.h>
170+
#endif
171+
#if defined(WOLFSSL_HAVE_MLKEM)
172+
#include <wolfssl/wolfcrypt/mlkem.h>
173+
#endif
174+
#if defined(HAVE_PKCS7)
175+
#include <wolfssl/wolfcrypt/pkcs7.h>
176+
#endif
177+
#if !defined(NO_BIG_INT)
178+
#include <wolfssl/wolfcrypt/sp_int.h>
179+
#endif
180+
168181
/* include misc.c here regardless of NO_INLINE, because misc.c implementations
169182
* have default (hidden) visibility, and in the absence of visibility, it's
170183
* benign to mask out the library implementation.
@@ -34387,6 +34400,264 @@ static int test_sniffer_chain_input_overflow(void)
3438734400
}
3438834401
#endif /* WOLFSSL_SNIFFER && WOLFSSL_SNIFFER_CHAIN_INPUT */
3438934402

34403+
/*----------------------------------------------------------------------------*
34404+
| ZD 21412-21426: Regression tests for reported vulnerabilities
34405+
*----------------------------------------------------------------------------*/
34406+
34407+
/* ZD 21412: ML-DSA HashML-DSA verify must reject hashLen > WC_MAX_DIGEST_SIZE */
34408+
static int test_zd21412_mldsa_verify_hash_overflow(void)
34409+
{
34410+
EXPECT_DECLS;
34411+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
34412+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
34413+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
34414+
dilithium_key key;
34415+
WC_RNG rng;
34416+
int res = 0;
34417+
byte sig[4000];
34418+
byte hash[4096]; /* larger than WC_MAX_DIGEST_SIZE */
34419+
34420+
XMEMSET(&key, 0, sizeof(key));
34421+
XMEMSET(&rng, 0, sizeof(rng));
34422+
XMEMSET(sig, 0x41, sizeof(sig));
34423+
XMEMSET(hash, 'A', sizeof(hash));
34424+
34425+
ExpectIntEQ(wc_InitRng(&rng), 0);
34426+
ExpectIntEQ(wc_dilithium_init(&key), 0);
34427+
#ifndef WOLFSSL_NO_ML_DSA_65
34428+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0);
34429+
#elif !defined(WOLFSSL_NO_ML_DSA_44)
34430+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0);
34431+
#else
34432+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0);
34433+
#endif
34434+
ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0);
34435+
34436+
/* hashLen=4096 must be rejected with BUFFER_E, not overflow the stack */
34437+
ExpectIntEQ(wc_dilithium_verify_ctx_hash(sig, sizeof(sig), NULL, 0,
34438+
WC_HASH_TYPE_SHA256, hash, sizeof(hash), &res, &key),
34439+
WC_NO_ERR_TRACE(BUFFER_E));
34440+
34441+
wc_dilithium_free(&key);
34442+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
34443+
#endif
34444+
return EXPECT_RESULT();
34445+
}
34446+
34447+
/* ZD 21414: PKCS7 ORI OID must be bounds-checked against MAX_OID_SZ.
34448+
* This DER was generated by encoding a valid EnvelopedData with ORI, then
34449+
* patching the OID length from 10 to 64 (exceeds MAX_OID_SZ=32) and inserting
34450+
* 54 extra bytes. Without the fix, wc_PKCS7_DecryptOri copies 64 bytes into
34451+
* oriOID[32], causing a stack buffer overflow. */
34452+
#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \
34453+
defined(WOLFSSL_AES_256)
34454+
static int oriDecryptCb_zd21414(PKCS7* pkcs7, byte* oriType, word32 oriTypeSz,
34455+
byte* oriValue, word32 oriValueSz, byte* decryptedKey,
34456+
word32* decryptedKeySz, void* ctx)
34457+
{
34458+
word32 keySz;
34459+
(void)pkcs7; (void)oriType; (void)oriTypeSz; (void)ctx;
34460+
if (oriValueSz < 2) return -1;
34461+
keySz = oriValue[1];
34462+
if (*decryptedKeySz < keySz) return -1;
34463+
XMEMCPY(decryptedKey, oriValue + 2, keySz);
34464+
*decryptedKeySz = keySz;
34465+
return 0;
34466+
}
34467+
#endif /* HAVE_PKCS7 && AES_CBC && AES_256 */
34468+
static int test_zd21414_pkcs7_ori_oid_overflow(void)
34469+
{
34470+
EXPECT_DECLS;
34471+
#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \
34472+
defined(WOLFSSL_AES_256)
34473+
/* Pre-built malformed EnvelopedData: ORI OID length patched from 10 to 64
34474+
* (0x40 at offset 26), with 54 extra 'X' bytes inserted after original OID.
34475+
* Generated by POC's encode-then-patch approach. */
34476+
static const byte malformed[] = {
34477+
0x30,0x81,0x82,0x06,0x09,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,
34478+
0x07,0x03,0xA0,0x75,0x30,0x73,0x02,0x01,0x03,0x31,0x30,0xA4,
34479+
0x2E,0x06,0x40,0x2A,0x86,0x48,0x86,0xF7,0x0D,0x01,0x01,0x01,
34480+
0x00,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,
34481+
0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,
34482+
0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,
34483+
0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x58,
34484+
0x58,0x58,0x58,0x58,0x58,0x58,0x58,0x04,0x20,0xEB,0xCE,0xF2,
34485+
0x43,0xC1,0xCE,0xF8,0xCD,0xE2,0xC1,0x12,0xDB,0x46,0xFE,0x39,
34486+
0xF0,0x2C,0xF2,0x9A,0x6A,0xD4,0x90,0xB8,0xAC,0x72,0x17,0x54,
34487+
0x97,0xAE,0xE2,0x59,0xEA,0x30,0x3C,0x06,0x09,0x2A,0x86,0x48,
34488+
0x86,0xF7,0x0D,0x01,0x07,0x01,0x30,0x1D,0x06,0x09,0x60,0x86,
34489+
0x48,0x01,0x65,0x03,0x04,0x01,0x2A,0x04,0x10,0x98,0xBC,0x3A,
34490+
0xF9,0x15,0xF3,0x6B,0x53,0x9D,0x81,0xBD,0x44,0x7E,0xA1,0x01,
34491+
0x4D,0x80,0x10,0xBD,0x36,0x26,0xF5,0x6E,0x11,0xD0,0xBB,0x5C,
34492+
0x15,0x19,0xA7,0x6B,0xC2,0xC2,0xB6
34493+
}; /* 187 bytes */
34494+
PKCS7* pkcs7 = NULL;
34495+
byte decoded[256];
34496+
34497+
ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, INVALID_DEVID));
34498+
if (pkcs7 != NULL) {
34499+
/* ORI decrypt callback must be set or parser skips ORI processing */
34500+
wc_PKCS7_SetOriDecryptCb(pkcs7, oriDecryptCb_zd21414);
34501+
/* Without fix: overflows oriOID[32] → crash.
34502+
* With fix: returns BUFFER_E before the copy. */
34503+
ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(pkcs7, (byte*)malformed,
34504+
(word32)sizeof(malformed), decoded, sizeof(decoded)), 0);
34505+
wc_PKCS7_Free(pkcs7);
34506+
}
34507+
#endif
34508+
return EXPECT_RESULT();
34509+
}
34510+
34511+
34512+
/* ZD 21417: Dilithium verify_ctx_msg must reject absurdly large msgLen */
34513+
static int test_zd21417_dilithium_hash_int_overflow(void)
34514+
{
34515+
EXPECT_DECLS;
34516+
#if defined(HAVE_DILITHIUM) && defined(WOLFSSL_WC_DILITHIUM) && \
34517+
!defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) && \
34518+
!defined(WOLFSSL_DILITHIUM_NO_VERIFY)
34519+
dilithium_key key;
34520+
WC_RNG rng;
34521+
int res = 0;
34522+
byte sig[4000];
34523+
byte msg[64];
34524+
34525+
XMEMSET(&key, 0, sizeof(key));
34526+
XMEMSET(&rng, 0, sizeof(rng));
34527+
XMEMSET(sig, 0, sizeof(sig));
34528+
XMEMSET(msg, 'A', sizeof(msg));
34529+
34530+
ExpectIntEQ(wc_InitRng(&rng), 0);
34531+
ExpectIntEQ(wc_dilithium_init(&key), 0);
34532+
#ifndef WOLFSSL_NO_ML_DSA_65
34533+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_65), 0);
34534+
#elif !defined(WOLFSSL_NO_ML_DSA_44)
34535+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_44), 0);
34536+
#else
34537+
ExpectIntEQ(wc_dilithium_set_level(&key, WC_ML_DSA_87), 0);
34538+
#endif
34539+
ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0);
34540+
34541+
/* msgLen=0xFFFFFFC0 would cause integer overflow in hash computation.
34542+
* Must be rejected with BAD_FUNC_ARG. */
34543+
ExpectIntEQ(wc_dilithium_verify_ctx_msg(sig, sizeof(sig), NULL, 0,
34544+
msg, 0xFFFFFFC0u, &res, &key), WC_NO_ERR_TRACE(BAD_FUNC_ARG));
34545+
34546+
wc_dilithium_free(&key);
34547+
DoExpectIntEQ(wc_FreeRng(&rng), 0);
34548+
#endif
34549+
return EXPECT_RESULT();
34550+
}
34551+
34552+
/* ZD 21418: ECC import must validate point is on curve */
34553+
static int test_zd21418_ecc_invalid_curve_point(void)
34554+
{
34555+
EXPECT_DECLS;
34556+
#if defined(HAVE_ECC) && !defined(NO_ECC256)
34557+
ecc_key peerKey;
34558+
/* Invalid point (3, 3) — NOT on P-256 curve y^2 = x^3 - 3x + b */
34559+
byte badPt[65];
34560+
34561+
XMEMSET(&peerKey, 0, sizeof(peerKey));
34562+
XMEMSET(badPt, 0, sizeof(badPt));
34563+
badPt[0] = 0x04; /* uncompressed */
34564+
badPt[32] = 3; /* x = 3 */
34565+
badPt[64] = 3; /* y = 3 */
34566+
34567+
ExpectIntEQ(wc_ecc_init(&peerKey), 0);
34568+
34569+
/* Import of invalid point as untrusted (TLS peer) must be rejected.
34570+
* Uses _ex2 with untrusted=1 to match the TLS key exchange path. */
34571+
ExpectIntNE(wc_ecc_import_x963_ex2(badPt, sizeof(badPt), &peerKey,
34572+
ECC_SECP256R1, 1), 0);
34573+
34574+
wc_ecc_free(&peerKey);
34575+
#endif
34576+
return EXPECT_RESULT();
34577+
}
34578+
34579+
/* ZD 21422: PKCS7 CBC padding must validate all padding bytes */
34580+
static int test_zd21422_pkcs7_padding_oracle(void)
34581+
{
34582+
EXPECT_DECLS;
34583+
#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \
34584+
defined(WOLFSSL_AES_256) && !defined(NO_PKCS7_ENCRYPTED_DATA)
34585+
PKCS7 pkcs7;
34586+
byte key[32];
34587+
byte plaintext[27]; /* 27 bytes → padded to 32 → padding = 05 05 05 05 05 */
34588+
byte encoded[4096];
34589+
byte output[256];
34590+
byte modified[4096];
34591+
int encodedSz;
34592+
int outSz;
34593+
int ctOff = -1;
34594+
int ctLen = 0;
34595+
int i;
34596+
34597+
XMEMSET(key, 0xAA, sizeof(key));
34598+
XMEMSET(plaintext, 'X', sizeof(plaintext));
34599+
34600+
/* Encode EncryptedData */
34601+
XMEMSET(&pkcs7, 0, sizeof(pkcs7));
34602+
ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0);
34603+
pkcs7.content = plaintext;
34604+
pkcs7.contentSz = sizeof(plaintext);
34605+
pkcs7.contentOID = DATA;
34606+
pkcs7.encryptOID = AES256CBCb;
34607+
pkcs7.encryptionKey = key;
34608+
pkcs7.encryptionKeySz = sizeof(key);
34609+
34610+
ExpectIntGT(encodedSz = wc_PKCS7_EncodeEncryptedData(&pkcs7, encoded,
34611+
sizeof(encoded)), 0);
34612+
34613+
/* Verify normal decrypt works */
34614+
ExpectIntEQ(outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, encoded,
34615+
(word32)encodedSz, output, sizeof(output)), (int)sizeof(plaintext));
34616+
wc_PKCS7_Free(&pkcs7);
34617+
34618+
/* Find ciphertext block in encoded DER */
34619+
if (EXPECT_SUCCESS()) {
34620+
for (i = encodedSz - 10; i > 10; i--) {
34621+
if (encoded[i] == 0x04 || encoded[i] == 0x80) {
34622+
int len, lbytes;
34623+
if (encoded[i+1] < 0x80) { len = encoded[i+1]; lbytes = 1; }
34624+
else if (encoded[i+1] == 0x81) { len = encoded[i+2]; lbytes = 2; }
34625+
else continue;
34626+
if (len > 0 && len % 16 == 0 &&
34627+
i + 1 + lbytes + len <= encodedSz) {
34628+
ctOff = i + 1 + lbytes;
34629+
ctLen = len;
34630+
break;
34631+
}
34632+
}
34633+
}
34634+
}
34635+
ExpectIntGT(ctOff, 0);
34636+
ExpectIntGE(ctLen, 32);
34637+
34638+
/* Corrupt an interior padding byte via CBC bit-flip */
34639+
if (EXPECT_SUCCESS()) {
34640+
XMEMCPY(modified, encoded, (size_t)encodedSz);
34641+
/* Flip byte in penultimate block to corrupt interior padding */
34642+
modified[ctOff + ctLen - 32 + 11] ^= 0x42;
34643+
34644+
/* Decrypt modified ciphertext — must fail, not succeed */
34645+
XMEMSET(&pkcs7, 0, sizeof(pkcs7));
34646+
ExpectIntEQ(wc_PKCS7_Init(&pkcs7, NULL, 0), 0);
34647+
pkcs7.encryptionKey = key;
34648+
pkcs7.encryptionKeySz = sizeof(key);
34649+
34650+
outSz = wc_PKCS7_DecodeEncryptedData(&pkcs7, modified,
34651+
(word32)encodedSz, output, sizeof(output));
34652+
/* Must return an error — if it returns plaintext size, padding
34653+
* oracle vulnerability exists */
34654+
ExpectIntLT(outSz, 0);
34655+
wc_PKCS7_Free(&pkcs7);
34656+
}
34657+
#endif
34658+
return EXPECT_RESULT();
34659+
}
34660+
3439034661
TEST_CASE testCases[] = {
3439134662
TEST_DECL(test_fileAccess),
3439234663

@@ -35203,6 +35474,14 @@ TEST_CASE testCases[] = {
3520335474
TEST_DECL(test_sniffer_chain_input_overflow),
3520435475
#endif
3520535476

35477+
/*********************************
35478+
* ZD 21412-21426 Regression Tests
35479+
*********************************/
35480+
TEST_DECL(test_zd21412_mldsa_verify_hash_overflow),
35481+
TEST_DECL(test_zd21414_pkcs7_ori_oid_overflow),
35482+
TEST_DECL(test_zd21417_dilithium_hash_int_overflow),
35483+
TEST_DECL(test_zd21418_ecc_invalid_curve_point),
35484+
TEST_DECL(test_zd21422_pkcs7_padding_oracle),
3520635485
/* This test needs to stay at the end to clean up any caches allocated. */
3520735486
TEST_DECL(test_wolfSSL_Cleanup)
3520835487
};

0 commit comments

Comments
 (0)