diff --git a/tests/api/test_pkcs7.c b/tests/api/test_pkcs7.c index 6c5706f9d46..d60e3abb67f 100644 --- a/tests/api/test_pkcs7.c +++ b/tests/api/test_pkcs7.c @@ -4383,4 +4383,135 @@ int test_wc_PKCS7_DecodeCompressedData(void) return EXPECT_RESULT(); } +/* + * Test for PKCS#7 SignedData with non-OCTET_STRING content + * (PKCS#7 style vs CMS) + * + * Tests parsing PKCS#7 SignedData where the encapsulated content + * is a SEQUENCE (as allowed by original PKCS#7 spec "ANY DEFINED BY + * contentType") rather than an OCTET STRING (as mandated by CMS). This showed + * up in use case of Authenticode signatures. + */ +int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void) +{ + EXPECT_DECLS; +#if defined(HAVE_PKCS7) + PKCS7* pkcs7 = NULL; +#ifndef NO_PKCS7_STREAM + word32 idx; + int ret; +#endif + + /* + * Hand-crafted PKCS#7 SignedData (degenerate, no signers) with: + * - Content type OID (1.3.6.1.4.1.311.2.1.4 = SPC_INDIRECT_DATA) + * - Content is a SEQUENCE, NOT an OCTET STRING + * - eContent is encoded as "ANY" type per original PKCS#7 spec. + * + * This test ensures wolfSSL's PKCS7 streaming code can correctly + * parse SignedData types when the encapsulated content is not an OCTET + * STRING (as CMS requires) but rather a SEQUENCE or other type + * (as PKCS#7's "ANY" type allows). Microsoft Authenticode signatures + * use this format with SPC_INDIRECT_DATA content. + * + * Structure: + * ContentInfo SEQUENCE + * contentType OID signedData + * [0] SignedData SEQUENCE + * version INTEGER 1 + * digestAlgorithms SET { sha256 } + * encapContentInfo SEQUENCE + * eContentType OID 1.3.6.1.4.1.311.2.1.4 + * [0] eContent + * SEQUENCE { OID, OCTET STRING } - SEQUENCE not OCTET STRING + * signerInfos SET {} (empty = degenerate) + */ + static const byte pkcs7Content[] = { + /* ContentInfo SEQUENCE */ + 0x30, 0x56, + /* contentType OID: 1.2.840.113549.1.7.2 (signedData) */ + 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02, + /* [0] EXPLICIT - content */ + 0xA0, 0x49, + /* SignedData SEQUENCE */ + 0x30, 0x47, + /* version INTEGER 1 */ + 0x02, 0x01, 0x01, + /* digestAlgorithms SET */ + 0x31, 0x0F, + /* AlgorithmIdentifier SEQUENCE */ + 0x30, 0x0D, + /* OID sha256: 2.16.840.1.101.3.4.2.1 */ + 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, + 0x04, 0x02, 0x01, + /* NULL */ + 0x05, 0x00, + /* encapContentInfo SEQUENCE */ + 0x30, 0x2F, + /* eContentType OID: 1.3.6.1.4.1.311.2.1.4 (SPC_INDIRECT_DATA) */ + 0x06, 0x0A, 0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, + 0x37, 0x02, 0x01, 0x04, + /* [0] EXPLICIT - eContent */ + 0xA0, 0x21, + /* Content SEQUENCE (0x30), not OCTET STRING (0x04) + * Following PKCS#7 "ANY" type, not CMS OCTET STRING */ + 0x30, 0x1F, + /* Content: SEQUENCE { OID, OCTET STRING with 24 bytes } */ + 0x06, 0x03, 0x55, 0x04, 0x03, /* OID 2.5.4.3 (5 bytes) */ + 0x04, 0x18, /* OCTET STRING length 24 */ + 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, /* "This is " */ + 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x6F, 0x6E, /* "test con" */ + 0x74, 0x65, 0x6E, 0x74, 0x20, 0x64, 0x61, 0x74, /* "tent dat" */ + /* signerInfos SET - empty for degenerate */ + 0x31, 0x00 + }; + + /* Test non-streaming verification */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_Init(pkcs7, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, (byte*)pkcs7Content, + (word32)sizeof(pkcs7Content)), 0); + + /* Verify content was parsed correctly */ + if (pkcs7 != NULL) { + /* contentIsPkcs7Type should be set */ + ExpectIntEQ(pkcs7->contentIsPkcs7Type, 1); + /* Content should have been parsed (33 bytes) */ + ExpectIntEQ(pkcs7->contentSz, 33); + ExpectNotNull(pkcs7->content); + } + wc_PKCS7_Free(pkcs7); + pkcs7 = NULL; + +#ifndef NO_PKCS7_STREAM + /* Test streaming verification - feed data byte by byte */ + ExpectNotNull(pkcs7 = wc_PKCS7_New(HEAP_HINT, testDevId)); + ExpectIntEQ(wc_PKCS7_Init(pkcs7, HEAP_HINT, INVALID_DEVID), 0); + ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0); + + /* Feed data byte by byte to exercise streaming path */ + ret = WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E); + for (idx = 0; idx < (word32)sizeof(pkcs7Content) && ret != 0; idx++) { + ret = wc_PKCS7_VerifySignedData(pkcs7, + (byte*)pkcs7Content + idx, 1); + if (ret < 0 && ret != WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E)) { + /* Unexpected error */ + break; + } + } + + /* Expecting ret = 0, not ASN_PARSE_E or other negative error */ + ExpectIntEQ(ret, 0); + + if (pkcs7 != NULL) { + ExpectIntEQ(pkcs7->contentIsPkcs7Type, 1); + ExpectIntEQ(pkcs7->contentSz, 33); + ExpectNotNull(pkcs7->content); + } + wc_PKCS7_Free(pkcs7); +#endif /* !NO_PKCS7_STREAM */ +#endif /* HAVE_PKCS7 */ + return EXPECT_RESULT(); +} diff --git a/tests/api/test_pkcs7.h b/tests/api/test_pkcs7.h index 054eda2489c..accd348cebc 100644 --- a/tests/api/test_pkcs7.h +++ b/tests/api/test_pkcs7.h @@ -48,6 +48,7 @@ int test_wc_PKCS7_SetOriEncryptCtx(void); int test_wc_PKCS7_SetOriDecryptCtx(void); int test_wc_PKCS7_DecodeCompressedData(void); int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void); +int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void); #define TEST_PKCS7_DECLS \ @@ -63,7 +64,8 @@ int test_wc_PKCS7_DecodeEnvelopedData_multiple_recipients(void); TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_ECC), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_Degenerate), \ TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_BER), \ - TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_NoDefaultSignedAttribs) + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_NoDefaultSignedAttribs), \ + TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq) #define TEST_PKCS7_ENCRYPTED_DATA_DECLS \ TEST_DECL_GROUP("pkcs7_ed", test_wc_PKCS7_DecodeEnvelopedData_stream), \ diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index a107d835a15..9c99f8cf5b6 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -5455,7 +5455,6 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, if (ret == 0 && GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) ret = ASN_PARSE_E; - /* version 1 follows RFC 2315 */ /* version 3 follows RFC 4108 */ if (ret == 0 && (version != 1 && version != 3)) { @@ -5673,6 +5672,15 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, * this as start of content. */ localIdx = start; pkcs7->contentIsPkcs7Type = 1; + + #ifndef NO_PKCS7_STREAM + /* Set streaming variables for PKCS#7 type content. + * length contains the size from [0] EXPLICIT wrapper */ + pkcs7->stream->multi = 0; + pkcs7->stream->currContIdx = localIdx; + pkcs7->stream->currContSz = (word32)length; + pkcs7->stream->currContRmnSz = (word32)length; + #endif } else { /* CMS eContent OCTET_STRING */ @@ -5762,7 +5770,6 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, idx = localIdx; } else { - /* If either pkcs7->content and pkcs7->contentSz are set * (detached signature where user has set content explicitly * into pkcs7->content/contentSz) OR pkcs7->hashBuf and @@ -5862,7 +5869,7 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, /* copy content to pkcs7->contentDynamic */ if (keepContent && pkcs7->stream->content && - pkcs7->stream->contentSz >0) { + pkcs7->stream->contentSz > 0) { pkcs7->contentDynamic = (byte*)XMALLOC(pkcs7->stream->contentSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); if (pkcs7->contentDynamic == NULL) { @@ -6412,6 +6419,17 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf, NO_USER_CHECK) < 0) ret = ASN_PARSE_E; + /* Update degenerate flag based on if signerInfos SET is empty. + * The earlier degenerate check at digestAlgorithms is an early + * optimization, but depending on degenerate case may not be + * detected until here. */ + if (ret == 0) { + degenerate = (length == 0) ? 1 : 0; + #ifndef NO_PKCS7_STREAM + pkcs7->stream->degenerate = (degenerate != 0); + #endif + } + if (ret != 0) break; #ifndef NO_PKCS7_STREAM