Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 131 additions & 0 deletions tests/api/test_pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}

4 changes: 3 additions & 1 deletion tests/api/test_pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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), \
Expand Down
24 changes: 21 additions & 3 deletions wolfcrypt/src/pkcs7.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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 */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down