diff --git a/tests/api.c b/tests/api.c index 7e20480484..eca1ba38f1 100644 --- a/tests/api.c +++ b/tests/api.c @@ -35960,6 +35960,605 @@ static int test_pkcs7_ori_seqsz_underflow(void) return EXPECT_RESULT(); } +/* Test: PKCS#7 ORI must reject when seqSz extends oriValue past input buffer. + * + * The first ORI bounds check (OID exceeds SEQUENCE boundary) is covered by + * test_pkcs7_ori_seqsz_underflow. This test covers the *second* check: + * oriValue region extends past the end of the input buffer. We craft a + * message where the [4] SEQUENCE length is valid relative to the OID + * (OID fits inside it), but the remaining oriValue portion extends past + * the end of the actual input buffer. + * + * To bypass GetLength's own bounds validation, we set the outer lengths + * (EnvelopedData SEQUENCE, RecipientInfos SET) to match the [4] claim, + * but truncate the actual buffer we pass to DecodeEnvelopedData. */ +static int test_pkcs7_ori_orivalue_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC) + wc_PKCS7* p7 = NULL; + byte out[256]; + + /* EnvelopedData with [4] ORI whose seqSz (40) is valid for the OID + * (6 bytes consumed) but oriValueSz (34) extends past the truncated + * input buffer. + * + * Layout: + * ContentInfo SEQUENCE + * OID envelopedData + * [0] EXPLICIT + * EnvelopedData SEQUENCE + * version = 0 + * RecipientInfos SET + * [4] CONSTRUCTED (seqSz = 40) + * OID (tag 06, len 04, 4 content bytes = 6 total) + * oriValue should be 34 bytes but input is truncated + * EncryptedContentInfo (filler for streaming) + * + * We pass the full array to DecodeEnvelopedData, but the actual + * input ends before [4]'s declared 40 bytes are consumed. + */ + static const byte poc[] = { + /* ContentInfo SEQUENCE */ + 0x30, 0x43, + /* contentType = envelopedData 1.2.840.113549.1.7.3 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03, + /* [0] EXPLICIT */ + 0xa0, 0x36, + /* EnvelopedData SEQUENCE */ + 0x30, 0x34, + /* version = 0 */ + 0x02, 0x01, 0x00, + /* RecipientInfos SET (len = 44 covers [4] tag+len+40) */ + 0x31, 0x2c, + /* [4] CONSTRUCTED = ORI implicit SEQUENCE, seqSz = 40 */ + 0xa4, 0x28, + /* OID: tag=06, len=04, 4 content bytes = 6 total */ + 0x06, 0x04, 0x2a, 0x03, 0x04, 0x05, + /* Only 4 bytes of oriValue here, but seqSz claims 34 more */ + 0x00, 0x00, 0x00, 0x00, + /* EncryptedContentInfo SEQUENCE (filler) */ + 0x30, 0x0b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01 + }; + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + wc_PKCS7_SetOriDecryptCb(p7, test_dummy_ori_cb); + + /* Must return error - oriValue extends past input buffer */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + +/* Test: PKCS#7 KTRI must not match recipient when SKID length differs + * from expected keyIdSize. + * + * The fix adds a `length == keyIdSize` check before comparing the SKID + * bytes. Without this check, XMEMCMP could compare against data beyond + * the SKID content. This test crafts a KTRI RecipientInfo where the + * [0] SubjectKeyIdentifier has length 5 instead of KEYID_SIZE (20). + * The decode must return an error (no matching recipient). */ +static int test_pkcs7_ktri_skid_length_mismatch(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(WOLFSSL_NO_MALLOC) + wc_PKCS7* p7 = NULL; + byte out[256]; + + /* Minimal EnvelopedData with KTRI using version=2 (SKID path). + * The SKID [0] has length 5 instead of 20. + * + * ContentInfo SEQUENCE + * OID envelopedData + * [0] EXPLICIT + * EnvelopedData SEQUENCE + * version = 2 + * RecipientInfos SET + * KTRI SEQUENCE + * version = 2 + * [0] SKID (5 bytes, should be 20) + * AlgorithmIdentifier (RSA OID) + * OCTET STRING (fake encrypted key) + * EncryptedContentInfo (filler) + */ + static const byte poc[] = { + /* ContentInfo SEQUENCE */ + 0x30, 0x46, + /* contentType = envelopedData 1.2.840.113549.1.7.3 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03, + /* [0] EXPLICIT */ + 0xa0, 0x39, + /* EnvelopedData SEQUENCE */ + 0x30, 0x37, + /* version = 2 (triggers SKID-based recipient identification) */ + 0x02, 0x01, 0x02, + /* RecipientInfos SET */ + 0x31, 0x21, + /* KTRI SEQUENCE */ + 0x30, 0x1f, + /* version = 2 (SKID) */ + 0x02, 0x01, 0x02, + /* [0] IMPLICIT SubjectKeyIdentifier, length = 5 (wrong!) */ + 0x80, 0x05, 0x01, 0x02, 0x03, 0x04, 0x05, + /* AlgorithmIdentifier: RSA 1.2.840.113549.1.1.1 */ + 0x30, 0x0d, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x01, + 0x05, 0x00, /* NULL params */ + /* encryptedKey OCTET STRING (2 bytes fake) */ + 0x04, 0x02, 0xAA, 0xBB, + /* EncryptedContentInfo SEQUENCE (filler) */ + 0x30, 0x0b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01 + }; + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + /* Decode without a cert - SKID will never match, and the + * mismatched SKID length must not cause out-of-bounds reads */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + +/* Test: PKCS#7 KARI must reject BIT STRING with length < 2 in + * OriginatorPublicKey. + * + * The fix adds `if (length < 2) return ASN_PARSE_E` after parsing + * the BIT STRING tag and length. A BIT STRING must have at least + * the unused-bits byte plus one byte of content. This test crafts + * a KARI [1] RecipientInfo where the OriginatorPublicKey's BIT STRING + * has length 1 (degenerate). The PKCS7 object is initialized with a + * real ECC cert so that KariParseRecipCert succeeds and parsing reaches + * the BIT STRING validation. */ +static int test_pkcs7_kari_degenerate_bitstring(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && defined(HAVE_ECC) && defined(HAVE_X963_KDF) && \ + !defined(WOLFSSL_NO_MALLOC) + wc_PKCS7* p7 = NULL; + byte out[256]; + byte* eccCert = NULL; + byte* eccPrivKey = NULL; + word32 eccCertSz = 0; + word32 eccPrivKeySz = 0; +#if !defined(USE_CERT_BUFFERS_256) && !defined(NO_FILESYSTEM) + XFILE f = XBADFILE; +#endif + + /* Minimal EnvelopedData with KARI [1] containing a degenerate + * BIT STRING (length 1) in OriginatorPublicKey. + * + * ContentInfo SEQUENCE + * OID envelopedData + * [0] EXPLICIT + * EnvelopedData SEQUENCE + * version = 2 + * RecipientInfos SET + * [1] CONSTRUCTED (KARI) + * version = 3 + * [0] CONSTRUCTED (OriginatorIdentifierOrKey) + * [1] CONSTRUCTED (OriginatorPublicKey) + * AlgorithmIdentifier (ECDSAk) + * BIT STRING length=1 (degenerate!) + * EncryptedContentInfo (filler) + */ + static const byte poc[] = { + /* ContentInfo SEQUENCE */ + 0x30, 0x44, + /* contentType = envelopedData 1.2.840.113549.1.7.3 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03, + /* [0] EXPLICIT */ + 0xa0, 0x37, + /* EnvelopedData SEQUENCE */ + 0x30, 0x35, + /* version = 2 */ + 0x02, 0x01, 0x02, + /* RecipientInfos SET */ + 0x31, 0x1f, + /* [1] CONSTRUCTED (KARI implicit) */ + 0xa1, 0x1d, + /* version = 3 */ + 0x02, 0x01, 0x03, + /* [0] CONSTRUCTED (OriginatorIdentifierOrKey) */ + 0xa0, 0x18, + /* [1] CONSTRUCTED (OriginatorPublicKey) */ + 0xa1, 0x16, + /* AlgorithmIdentifier SEQUENCE */ + 0x30, 0x13, + /* OID: id-ecPublicKey 1.2.840.10045.2.1 */ + 0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, + /* OID: prime256v1 1.2.840.10045.3.1.7 */ + 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, + 0x01, 0x07, + /* BIT STRING with length 1 - degenerate! */ + 0x03, 0x01, 0x00, + /* EncryptedContentInfo SEQUENCE (filler) */ + 0x30, 0x0b, + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01 + }; + + /* Load ECC cert and key so KariParseRecipCert succeeds and + * parsing reaches the BIT STRING check */ +#ifdef USE_CERT_BUFFERS_256 + eccCertSz = (word32)sizeof_cliecc_cert_der_256; + ExpectNotNull(eccCert = (byte*)XMALLOC(eccCertSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (eccCert != NULL) + XMEMCPY(eccCert, cliecc_cert_der_256, eccCertSz); + eccPrivKeySz = (word32)sizeof_ecc_clikey_der_256; + ExpectNotNull(eccPrivKey = (byte*)XMALLOC(eccPrivKeySz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (eccPrivKey != NULL) + XMEMCPY(eccPrivKey, ecc_clikey_der_256, eccPrivKeySz); +#elif !defined(NO_FILESYSTEM) + eccCertSz = FOURK_BUF; + ExpectNotNull(eccCert = (byte*)XMALLOC(eccCertSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectTrue((f = XFOPEN("./certs/client-ecc-cert.der", "rb")) != XBADFILE); + ExpectTrue((eccCertSz = (word32)XFREAD(eccCert, 1, eccCertSz, f)) > 0); + if (f != XBADFILE) { + XFCLOSE(f); + f = XBADFILE; + } + eccPrivKeySz = FOURK_BUF; + ExpectNotNull(eccPrivKey = (byte*)XMALLOC(eccPrivKeySz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectTrue((f = XFOPEN("./certs/ecc-client-key.der", "rb")) != XBADFILE); + ExpectTrue((eccPrivKeySz = (word32)XFREAD(eccPrivKey, 1, eccPrivKeySz, + f)) > 0); + if (f != XBADFILE) + XFCLOSE(f); +#else + eccCert = NULL; + eccCertSz = 0; + eccPrivKey = NULL; + eccPrivKeySz = 0; +#endif + + p7 = wc_PKCS7_New(HEAP_HINT, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL && eccCert != NULL) { + ExpectIntEQ(wc_PKCS7_InitWithCert(p7, eccCert, eccCertSz), 0); + if (p7 != NULL) { + p7->privateKey = eccPrivKey; + p7->privateKeySz = eccPrivKeySz; + } + + /* Must return error - BIT STRING length < 2 is invalid */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + if (p7 != NULL) { + p7->privateKey = NULL; + p7->privateKeySz = 0; + } + wc_PKCS7_Free(p7); + } + else if (p7 != NULL) { + wc_PKCS7_Free(p7); + } + + XFREE(eccCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(eccPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + +/* Test: PKCS#7 EncryptedData must reject when encryptedContentSz exceeds + * the remaining input buffer. + * + * The fix adds `encryptedContentSz > (int)(pkiMsgSz - idx)` to the + * existing `encryptedContentSz <= 0` check in stage 6. This test crafts + * a minimal EncryptedData where the [0] IMPLICIT content length claims + * 0x200 (512) bytes but only 32 bytes of ciphertext are present. */ +static int test_pkcs7_encrypted_content_size_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_AES) && defined(HAVE_AES_CBC) && \ + defined(WOLFSSL_AES_256) && !defined(NO_PKCS7_ENCRYPTED_DATA) && \ + !defined(WOLFSSL_NO_MALLOC) + wc_PKCS7* p7 = NULL; + byte key[32]; + byte out[256]; + + /* EncryptedData with [0] content claiming 512 bytes but only 32 present. + * + * ContentInfo SEQUENCE + * OID encryptedData (1.2.840.113549.1.7.6) + * [0] EXPLICIT + * EncryptedData SEQUENCE + * version = 0 + * EncryptedContentInfo SEQUENCE + * OID data (1.2.840.113549.1.7.1) + * AlgorithmIdentifier + * OID AES-256-CBC (2.16.840.1.101.3.4.1.42) + * OCTET STRING IV (16 zero bytes) + * [0] IMPLICIT content (claimed len=0x200, actual=32 bytes) + */ + static const byte poc[] = { + /* ContentInfo SEQUENCE (len covers entire message) */ + 0x30, 0x50, + /* OID encryptedData 1.2.840.113549.1.7.6 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x06, + /* [0] EXPLICIT */ + 0xa0, 0x43, + /* EncryptedData SEQUENCE */ + 0x30, 0x41, + /* version = 0 */ + 0x02, 0x01, 0x00, + /* EncryptedContentInfo SEQUENCE */ + 0x30, 0x3c, + /* OID data 1.2.840.113549.1.7.1 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01, + /* AlgorithmIdentifier SEQUENCE */ + 0x30, 0x1d, + /* OID AES-256-CBC 2.16.840.1.101.3.4.1.42 */ + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, + 0x2a, + /* IV: OCTET STRING (16 zero bytes) */ + 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* [0] IMPLICIT encryptedContent - claims 512 bytes! + * Only 16 bytes of fake ciphertext follow. */ + 0x80, 0x82, 0x02, 0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA + }; + + XMEMSET(key, 0, sizeof(key)); + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + p7->encryptionKey = key; + p7->encryptionKeySz = sizeof(key); + + /* Must return error - content extends past input buffer */ + ExpectIntLT(wc_PKCS7_DecodeEncryptedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + +/* Test: PKCS#7 SignedData must reject when the signature field is not + * an OCTET STRING. + * + * The fix adds `else if (ret == 0) { ret = ASN_PARSE_E; }` so that + * when the tag at the signature position is not ASN_OCTET_STRING, + * parsing returns an error instead of silently continuing with no + * signature. This test encodes a valid SignedData, then corrupts + * the signature OCTET STRING tag and verifies that decode fails. */ +static int test_pkcs7_signed_bad_sig_tag(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(NO_RSA) && !defined(NO_SHA256) && \ + !defined(WOLFSSL_NO_MALLOC) + PKCS7* pkcs7 = NULL; + WC_RNG rng; + byte encoded[FOURK_BUF]; + int encodedSz = 0; + int i; + byte* rsaCert = NULL; + byte* rsaPrivKey = NULL; + word32 rsaCertSz = 0; + word32 rsaPrivKeySz = 0; +#if !defined(USE_CERT_BUFFERS_2048) && !defined(USE_CERT_BUFFERS_1024) && \ + !defined(NO_FILESYSTEM) + XFILE f = XBADFILE; +#endif + + const byte data[] = "Test signed data"; + + XMEMSET(&rng, 0, sizeof(WC_RNG)); + + /* Load RSA cert and key */ +#if defined(USE_CERT_BUFFERS_2048) + rsaCertSz = (word32)sizeof_client_cert_der_2048; + ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (rsaCert != NULL) + XMEMCPY(rsaCert, client_cert_der_2048, rsaCertSz); + rsaPrivKeySz = (word32)sizeof_client_key_der_2048; + ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (rsaPrivKey != NULL) + XMEMCPY(rsaPrivKey, client_key_der_2048, rsaPrivKeySz); +#elif defined(USE_CERT_BUFFERS_1024) + rsaCertSz = (word32)sizeof_client_cert_der_1024; + ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (rsaCert != NULL) + XMEMCPY(rsaCert, client_cert_der_1024, rsaCertSz); + rsaPrivKeySz = (word32)sizeof_client_key_der_1024; + ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + if (rsaPrivKey != NULL) + XMEMCPY(rsaPrivKey, client_key_der_1024, rsaPrivKeySz); +#elif !defined(NO_FILESYSTEM) + rsaCertSz = FOURK_BUF; + ExpectNotNull(rsaCert = (byte*)XMALLOC(rsaCertSz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectTrue((f = XFOPEN("./certs/client-cert.der", "rb")) != XBADFILE); + ExpectTrue((rsaCertSz = (word32)XFREAD(rsaCert, 1, rsaCertSz, f)) > 0); + if (f != XBADFILE) { XFCLOSE(f); f = XBADFILE; } + rsaPrivKeySz = FOURK_BUF; + ExpectNotNull(rsaPrivKey = (byte*)XMALLOC(rsaPrivKeySz, HEAP_HINT, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectTrue((f = XFOPEN("./certs/client-key.der", "rb")) != XBADFILE); + ExpectTrue((rsaPrivKeySz = (word32)XFREAD(rsaPrivKey, 1, + rsaPrivKeySz, f)) > 0); + if (f != XBADFILE) XFCLOSE(f); +#else + rsaCert = NULL; rsaCertSz = 0; + rsaPrivKey = NULL; rsaPrivKeySz = 0; +#endif + + if (rsaCert == NULL || rsaPrivKey == NULL) { + XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + return TEST_SKIPPED; + } + + ExpectIntEQ(wc_InitRng(&rng), 0); + + /* Encode a valid SignedData */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, rsaCert, rsaCertSz), 0); + if (pkcs7 != NULL) { + pkcs7->content = (byte*)data; + pkcs7->contentSz = (word32)sizeof(data); + pkcs7->contentOID = DATA; + pkcs7->hashOID = SHA256h; + pkcs7->encryptOID = RSAk; + pkcs7->privateKey = rsaPrivKey; + pkcs7->privateKeySz = rsaPrivKeySz; + pkcs7->rng = &rng; + } + + ExpectIntGT(encodedSz = wc_PKCS7_EncodeSignedData(pkcs7, encoded, + sizeof(encoded)), 0); + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + + /* Find the signature OCTET STRING tag (0x04) near the end of the + * encoded message and corrupt it. The signature is the last large + * OCTET STRING in the SignerInfo. Search backwards for 0x04 followed + * by a length that looks like an RSA signature (>= 64 bytes). + * This heuristic depends on the signature being the last large + * OCTET STRING; the found==1 assertion below guards against + * silent false passes if encoding changes. */ + if (EXPECT_SUCCESS()) { + int found = 0; + for (i = encodedSz - 10; i > 10; i--) { + if (encoded[i] == 0x04) { + int len = 0, lbytes = 0; + if (encoded[i+1] < 0x80) { + len = encoded[i+1]; lbytes = 1; + } + else if (encoded[i+1] == 0x81) { + len = encoded[i+2]; lbytes = 2; + } + else if (encoded[i+1] == 0x82) { + len = (encoded[i+2] << 8) | encoded[i+3]; lbytes = 3; + } + /* RSA signature is typically >= 128 bytes */ + if (len >= 64 && i + 1 + lbytes + len <= encodedSz) { + /* Corrupt the OCTET STRING tag to INTEGER */ + encoded[i] = 0x02; /* ASN_INTEGER instead of OCTET STRING */ + found = 1; + break; + } + } + } + ExpectIntEQ(found, 1); + } + + /* Verify the corrupted SignedData - must fail */ + if (EXPECT_SUCCESS()) { + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntLT(wc_PKCS7_VerifySignedData(pkcs7, encoded, + (word32)encodedSz), 0); + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + } + + DoExpectIntEQ(wc_FreeRng(&rng), 0); + XFREE(rsaCert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(rsaPrivKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return EXPECT_RESULT(); +} + +/* Test: PKCS#7 EnvelopedData must reject when encryptedContentTotalSz + * exceeds the remaining input buffer. + * + * The fix adds a bounds check under NO_PKCS7_STREAM, but the same + * crafted message should also be properly handled in streaming mode. + * This test crafts an EnvelopedData where the [0] content length + * claims 512 bytes but only minimal data follows. */ +static int test_pkcs7_enveloped_content_size_overflow(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) && !defined(WOLFSSL_NO_MALLOC) && !defined(NO_RSA) + wc_PKCS7* p7 = NULL; + byte out[256]; + + /* EnvelopedData with KTRI where the EncryptedContentInfo [0] claims + * 512 bytes but only 16 are present. + * + * The outer structure is valid enough to reach the content parsing: + * ContentInfo -> EnvelopedData -> version=0 -> + * RecipientInfos (empty SET) -> EncryptedContentInfo -> + * contentType + AlgorithmIdentifier + [0] oversized content */ + static const byte poc[] = { + /* ContentInfo SEQUENCE */ + 0x30, 0x50, + /* OID envelopedData 1.2.840.113549.1.7.3 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x03, + /* [0] EXPLICIT */ + 0xa0, 0x43, + /* EnvelopedData SEQUENCE */ + 0x30, 0x41, + /* version = 0 */ + 0x02, 0x01, 0x00, + /* RecipientInfos SET (empty - no recipients) */ + 0x31, 0x00, + /* EncryptedContentInfo SEQUENCE */ + 0x30, 0x3c, + /* OID data 1.2.840.113549.1.7.1 */ + 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, + 0x01, + /* AlgorithmIdentifier SEQUENCE */ + 0x30, 0x1d, + /* OID AES-256-CBC 2.16.840.1.101.3.4.1.42 */ + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x01, + 0x2a, + /* IV: OCTET STRING (16 zero bytes) */ + 0x04, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + /* [0] IMPLICIT encryptedContent - claims 512 bytes! */ + 0x80, 0x82, 0x02, 0x00, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, + 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA + }; + + p7 = wc_PKCS7_New(NULL, INVALID_DEVID); + ExpectNotNull(p7); + if (p7 != NULL) { + /* With an empty RecipientInfos SET, the function may fail at + * the recipient matching stage before reaching the content-size + * bounds check. The ExpectIntLT assertion ensures the + * oversized content does not cause a buffer over-read. */ + ExpectIntLT(wc_PKCS7_DecodeEnvelopedData(p7, (byte*)poc, sizeof(poc), + out, sizeof(out)), 0); + + wc_PKCS7_Free(p7); + } +#endif + return EXPECT_RESULT(); +} + /* Dilithium verify_ctx_msg must reject absurdly large msgLen */ static int test_dilithium_hash(void) { @@ -36902,6 +37501,12 @@ TEST_CASE testCases[] = { TEST_DECL(test_pkcs7_decode_encrypted_outputsz), TEST_DECL(test_pkcs7_ori_oversized_oid), TEST_DECL(test_pkcs7_ori_seqsz_underflow), + TEST_DECL(test_pkcs7_ori_orivalue_overflow), + TEST_DECL(test_pkcs7_ktri_skid_length_mismatch), + TEST_DECL(test_pkcs7_kari_degenerate_bitstring), + TEST_DECL(test_pkcs7_encrypted_content_size_overflow), + TEST_DECL(test_pkcs7_signed_bad_sig_tag), + TEST_DECL(test_pkcs7_enveloped_content_size_overflow), TEST_DECL(test_pkcs7_padding), #if defined(WOLFSSL_SNIFFER) && defined(WOLFSSL_SNIFFER_CHAIN_INPUT) diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index cd9f7aa575..646db1bdb8 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -2763,6 +2763,11 @@ int test_wc_PKCS7_EncodeDecodeEnvelopedData(void) AES128CBCb, AES128_WRAP, dhSinglePass_stdDH_sha1kdf_scheme, eccCert, eccCertSz, eccPrivKey, eccPrivKeySz}, #endif + #if defined(WOLFSSL_SHA224) && defined(WOLFSSL_AES_128) + {(byte*)input, (word32)(sizeof(input)/sizeof(char)), DATA, + AES128CBCb, AES128_WRAP, dhSinglePass_stdDH_sha224kdf_scheme, + eccCert, eccCertSz, eccPrivKey, eccPrivKeySz}, + #endif #if !defined(NO_SHA256) && defined(WOLFSSL_AES_256) {(byte*)input, (word32)(sizeof(input)/sizeof(char)), DATA, AES256CBCb, AES256_WRAP, dhSinglePass_stdDH_sha256kdf_scheme,