mirror of https://github.com/wolfSSL/wolfssl.git
Merge pull request #11017 from miyazakh/f7302_pkcs7_verify
Fix truncation and noDegenerate bypasses in wc_PKCS7_VerifySignedDatapull/11150/head
commit
a453077818
|
|
@ -6722,6 +6722,64 @@ int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void)
|
|||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
#if defined(HAVE_PKCS7) && !defined(NO_PKCS7_STREAM)
|
||||
/*
|
||||
* Feeds der to wc_PKCS7_VerifySignedData at every chunk size from
|
||||
* minChunkSz to derSz. If expectContentSz > 0, every chunk size must
|
||||
* decode successfully and produce that content size; otherwise every
|
||||
* chunk size must fail with a real parse error and never leave the
|
||||
* result at WC_PKCS7_WANT_READ_E once the whole buffer has been fed.
|
||||
*/
|
||||
static int test_wc_PKCS7_VerifySignedData_ChunkSweep_once(const byte* der,
|
||||
word32 derSz, word32 minChunkSz, word32 expectContentSz)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
PKCS7* pkcs7 = NULL;
|
||||
int ret;
|
||||
word32 chunkSz;
|
||||
word32 off;
|
||||
word32 thisSz;
|
||||
word32 fed;
|
||||
|
||||
for (chunkSz = minChunkSz; chunkSz <= derSz; chunkSz++) {
|
||||
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);
|
||||
|
||||
fed = 0;
|
||||
ret = WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E);
|
||||
for (off = 0; off < derSz && ret != 0; off += chunkSz) {
|
||||
thisSz = min(chunkSz, derSz - off);
|
||||
ret = wc_PKCS7_VerifySignedData(pkcs7, (byte*)der + off, thisSz);
|
||||
fed = off + thisSz;
|
||||
if (ret < 0 && ret != WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (expectContentSz > 0) {
|
||||
ExpectIntEQ(ret, 0);
|
||||
/* a genuine success must only happen once every byte of the
|
||||
* bundle has actually been fed in; an early success here
|
||||
* means the parser accepted a truncated prefix */
|
||||
ExpectIntEQ(fed, derSz);
|
||||
if (pkcs7 != NULL) {
|
||||
ExpectIntEQ(pkcs7->contentSz, expectContentSz);
|
||||
ExpectNotNull(pkcs7->content);
|
||||
}
|
||||
}
|
||||
else {
|
||||
ExpectIntNE(ret, 0);
|
||||
ExpectIntNE(ret, WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E));
|
||||
}
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
}
|
||||
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
#endif /* HAVE_PKCS7 && !NO_PKCS7_STREAM */
|
||||
|
||||
/*
|
||||
* SignedData bundle truncated at the eContent [0] EXPLICIT tag in
|
||||
* encapContentInfo. Verifies that the parser rejects the malformed
|
||||
|
|
@ -6766,6 +6824,11 @@ int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void)
|
|||
ExpectIntNE(wc_PKCS7_VerifySignedData(pkcs7, der, derSz), 0);
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 0));
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
|
@ -6773,19 +6836,15 @@ int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void)
|
|||
/*
|
||||
* SignedData bundle truncated at the certificates [0] IMPLICIT tag.
|
||||
* Verifies that the parser rejects the malformed input rather than
|
||||
* dereferencing past the end of the buffer.
|
||||
*
|
||||
* TODO: limited to NO_PKCS7_STREAM because the streaming parser's stage 3
|
||||
* early-exit check (pkcs7.c near line 6594) accepts any bundle
|
||||
* whose remaining footer is < 6 bytes as a successful degenerate end,
|
||||
* so the bounds check at line 6765 is unreachable in streaming mode.
|
||||
* Drop the NO_PKCS7_STREAM gate if/when the early-exit check becomes
|
||||
* more accurate.
|
||||
* dereferencing past the end of the buffer. Runs in both streaming and
|
||||
* NO_PKCS7_STREAM builds: the streaming parser's stage 3 early-exit check
|
||||
* used to accept any bundle whose remaining footer was < 6 bytes as a
|
||||
* successful degenerate end, silently accepting this truncated input.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7) && defined(NO_PKCS7_STREAM)
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
|
|
@ -6825,7 +6884,374 @@ int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void)
|
|||
ExpectIntNE(wc_PKCS7_VerifySignedData(pkcs7, der, derSz), 0);
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
|
||||
#endif /* HAVE_PKCS7 && NO_PKCS7_STREAM */
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 0));
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* SignedData bundle that is a genuine, non-truncated degenerate
|
||||
* (certs-only) bundle: no certificates, no CRLs, and an empty signerInfos
|
||||
* SET ("31 00") closing the bundle right after the content. This is the
|
||||
* shortest legitimate ending the stage 3 tail check in the streaming
|
||||
* parser can see, and must still succeed after fixing that check to reject
|
||||
* truncated bundles.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateMinimal(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
/* outer ContentInfo SEQUENCE (99 bytes content) */
|
||||
0x30, 0x63,
|
||||
/* contentType OID signedData */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
|
||||
/* [0] EXPLICIT (86 bytes content) */
|
||||
0xA0, 0x56,
|
||||
/* SignedData SEQUENCE (84 bytes content) */
|
||||
0x30, 0x54,
|
||||
/* version INTEGER 1 */
|
||||
0x02, 0x01, 0x01,
|
||||
/* digestAlgorithms SET (empty - degenerate) */
|
||||
0x31, 0x00,
|
||||
/* encapContentInfo SEQUENCE (75 bytes content) */
|
||||
0x30, 0x4B,
|
||||
/* eContentType OID 1.2.840.113549.1.7.1 (data) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
|
||||
/* eContent [0] EXPLICIT (62 bytes content) */
|
||||
0xA0, 0x3E,
|
||||
/* OCTET STRING (60 bytes content) */
|
||||
0x04, 0x3C,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3A, 0x3B,
|
||||
/* signerInfos SET (empty - degenerate end) */
|
||||
0x31, 0x00
|
||||
};
|
||||
word32 derSz = (word32)sizeof(der);
|
||||
|
||||
/* single-shot call */
|
||||
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, der, derSz), 0);
|
||||
if (pkcs7 != NULL) {
|
||||
ExpectIntEQ(pkcs7->contentSz, 60);
|
||||
ExpectNotNull(pkcs7->content);
|
||||
}
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
/* same bundle fed at every chunk size, including one byte at a time:
|
||||
* chunk boundaries that land mid-octet-string used to leave the
|
||||
* stream's totalRd tracking out of sync, capping stage 4's expected
|
||||
* read to less than the signerInfos SET tag/length needs and
|
||||
* stalling on WC_PKCS7_WANT_READ_E forever. */
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 60));
|
||||
#endif /* !NO_PKCS7_STREAM */
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Same shape as test_wc_PKCS7_VerifySignedData_DegenerateMinimal, but with
|
||||
* an empty certificates [0] SET and empty crls [1] SET both present ahead
|
||||
* of the empty signerInfos SET ("A0 00 A1 00 31 00" footer). Structurally
|
||||
* valid, and must verify the same way whether fed in one shot or in small
|
||||
* chunks that land the footer partway into the stream's internal buffer.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateEmptyCertsCrls(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
/* outer ContentInfo SEQUENCE (103 bytes content) */
|
||||
0x30, 0x67,
|
||||
/* contentType OID signedData */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
|
||||
/* [0] EXPLICIT (90 bytes content) */
|
||||
0xA0, 0x5A,
|
||||
/* SignedData SEQUENCE (88 bytes content) */
|
||||
0x30, 0x58,
|
||||
/* version INTEGER 1 */
|
||||
0x02, 0x01, 0x01,
|
||||
/* digestAlgorithms SET (empty - degenerate) */
|
||||
0x31, 0x00,
|
||||
/* encapContentInfo SEQUENCE (75 bytes content) */
|
||||
0x30, 0x4B,
|
||||
/* eContentType OID 1.2.840.113549.1.7.1 (data) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
|
||||
/* eContent [0] EXPLICIT (62 bytes content) */
|
||||
0xA0, 0x3E,
|
||||
/* OCTET STRING (60 bytes content) */
|
||||
0x04, 0x3C,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3A, 0x3B,
|
||||
/* certificates [0] (empty) */
|
||||
0xA0, 0x00,
|
||||
/* crls [1] (empty) */
|
||||
0xA1, 0x00,
|
||||
/* signerInfos SET (empty - degenerate end) */
|
||||
0x31, 0x00
|
||||
};
|
||||
word32 derSz = (word32)sizeof(der);
|
||||
|
||||
/* single-shot call */
|
||||
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, der, derSz), 0);
|
||||
if (pkcs7 != NULL) {
|
||||
ExpectIntEQ(pkcs7->contentSz, 60);
|
||||
ExpectNotNull(pkcs7->content);
|
||||
}
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
/* same bundle fed at every chunk size: chunk boundaries that leave the
|
||||
* "A0 00 A1 00 31 00" footer partly in the stream's internal buffer
|
||||
* used to make stage 3 undercount the bytes still available (its cap
|
||||
* dropped the buffered-but-unparsed count instead of adding it in),
|
||||
* and separately left HandleOctetStrings' totalRd baseline stale
|
||||
* across a buffered-to-direct read transition, double-counting the
|
||||
* last content byte and starving stage 6 of the final length byte. */
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 60));
|
||||
#endif /* !NO_PKCS7_STREAM */
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Same shape as test_wc_PKCS7_VerifySignedData_DegenerateMinimal, but the
|
||||
* final 2 bytes are "31 01" instead of "31 00": a signerInfos SET claiming
|
||||
* one byte of content that the buffer never supplies. Must be rejected,
|
||||
* not treated as a successful degenerate end.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_TruncSignerInfosTag(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
int ret;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
0x30, 0x63,
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
|
||||
0xA0, 0x56,
|
||||
0x30, 0x54,
|
||||
0x02, 0x01, 0x01,
|
||||
0x31, 0x00,
|
||||
0x30, 0x4B,
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
|
||||
0xA0, 0x3E,
|
||||
0x04, 0x3C,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3A, 0x3B,
|
||||
/* signerInfos SET claims 1 byte of content, buffer ends here */
|
||||
0x31, 0x01
|
||||
};
|
||||
word32 derSz = (word32)sizeof(der);
|
||||
|
||||
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);
|
||||
ret = wc_PKCS7_VerifySignedData(pkcs7, der, derSz);
|
||||
ExpectIntNE(ret, 0);
|
||||
ExpectIntNE(ret, WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E));
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 0));
|
||||
#endif
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* Same shape again, but the signerInfos SET is missing entirely: the
|
||||
* buffer ends right after the content, with nothing following. signerInfos
|
||||
* is a mandatory field, so this must be rejected rather than accepted as
|
||||
* a bundle with no more elements.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_NoSignerInfosTag(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
int ret;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
0x30, 0x61,
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
|
||||
0xA0, 0x54,
|
||||
0x30, 0x52,
|
||||
0x02, 0x01, 0x01,
|
||||
0x31, 0x00,
|
||||
0x30, 0x4B,
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
|
||||
0xA0, 0x3E,
|
||||
0x04, 0x3C,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3A, 0x3B
|
||||
/* buffer ends here -- no signerInfos SET at all */
|
||||
};
|
||||
word32 derSz = (word32)sizeof(der);
|
||||
|
||||
/* single-shot: must fail with a real parse error, not WANT_READ_E
|
||||
* (no more bytes will ever arrive per the outer length) */
|
||||
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);
|
||||
ret = wc_PKCS7_VerifySignedData(pkcs7, der, derSz);
|
||||
ExpectIntNE(ret, 0);
|
||||
ExpectIntNE(ret, WC_NO_ERR_TRACE(WC_PKCS7_WANT_READ_E));
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
pkcs7 = NULL;
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
/* same bundle fed at every chunk size, including one byte at a time:
|
||||
* must not end stuck on WANT_READ_E once all available bytes are
|
||||
* consumed */
|
||||
EXPECT_TEST(test_wc_PKCS7_VerifySignedData_ChunkSweep_once(der, derSz,
|
||||
1, 0));
|
||||
#endif /* !NO_PKCS7_STREAM */
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* SignedData bundle that is well-formed and NOT truncated: digestAlgorithms
|
||||
* SET contains one real AlgorithmIdentifier (so the early heuristic that
|
||||
* flags a bundle as degenerate from an empty digestAlgorithms SET does not
|
||||
* fire), while signerInfos SET is genuinely empty (degenerate, no signer).
|
||||
* With wc_PKCS7_AllowDegenerate(pkcs7, 0) set, this must be rejected once
|
||||
* the accurate signerInfos-based degenerate determination runs, not
|
||||
* silently accepted because the early heuristic missed it.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateNonEmptyDigestAlgos(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
|
||||
WOLFSSL_SMALL_STACK_STATIC byte der[] = {
|
||||
/* outer ContentInfo SEQUENCE (114 bytes content) */
|
||||
0x30, 0x72,
|
||||
/* contentType OID signedData */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02,
|
||||
/* [0] EXPLICIT (101 bytes content) */
|
||||
0xA0, 0x65,
|
||||
/* SignedData SEQUENCE (99 bytes content) */
|
||||
0x30, 0x63,
|
||||
/* version INTEGER 1 */
|
||||
0x02, 0x01, 0x01,
|
||||
/* digestAlgorithms SET (15 bytes content) -- one real
|
||||
* AlgorithmIdentifier (sha256), not empty */
|
||||
0x31, 0x0F,
|
||||
0x30, 0x0D,
|
||||
0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
|
||||
0x05, 0x00,
|
||||
/* encapContentInfo SEQUENCE (75 bytes content) */
|
||||
0x30, 0x4B,
|
||||
/* eContentType OID 1.2.840.113549.1.7.1 (data) */
|
||||
0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01,
|
||||
/* eContent [0] EXPLICIT (62 bytes content) */
|
||||
0xA0, 0x3E,
|
||||
/* OCTET STRING (60 bytes content) */
|
||||
0x04, 0x3C,
|
||||
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
|
||||
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
|
||||
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
|
||||
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F,
|
||||
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
|
||||
0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F,
|
||||
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
|
||||
0x38, 0x39, 0x3A, 0x3B,
|
||||
/* signerInfos SET (empty -- genuinely degenerate) */
|
||||
0x31, 0x00
|
||||
};
|
||||
word32 derSz = (word32)sizeof(der);
|
||||
|
||||
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);
|
||||
wc_PKCS7_AllowDegenerate(pkcs7, 0);
|
||||
ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, der, derSz),
|
||||
WC_NO_ERR_TRACE(PKCS7_NO_SIGNER_E));
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
|
||||
#endif /* HAVE_PKCS7 */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
/*
|
||||
* A genuine, non-degenerate SignedData bundle with a real RSA signerInfos
|
||||
* entry must still verify successfully when the caller has called
|
||||
* wc_PKCS7_AllowDegenerate(pkcs7, 0). The degenerate flag computed from
|
||||
* the signerInfos SET length is also used to reject degenerate bundles
|
||||
* under that setting, so this confirms a real signer does not get
|
||||
* misclassified as degenerate and rejected along with them.
|
||||
*/
|
||||
int test_wc_PKCS7_VerifySignedData_NoDegenerateAcceptsRealSigner(void)
|
||||
{
|
||||
EXPECT_DECLS;
|
||||
#if defined(HAVE_PKCS7) && !defined(NO_FILESYSTEM) && !defined(NO_RSA)
|
||||
PKCS7* pkcs7 = NULL;
|
||||
byte output[6000];
|
||||
word32 outputSz = sizeof(output);
|
||||
byte data[] = "Test data to encode.";
|
||||
|
||||
ExpectIntGT((outputSz = (word32)CreatePKCS7SignedData(output,
|
||||
(int)outputSz, data, (word32)sizeof(data), 0, 0, 0, RSA_TYPE)), 0);
|
||||
|
||||
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);
|
||||
wc_PKCS7_AllowDegenerate(pkcs7, 0);
|
||||
ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, output, outputSz), 0);
|
||||
wc_PKCS7_Free(pkcs7);
|
||||
|
||||
#endif /* HAVE_PKCS7 && !NO_FILESYSTEM && !NO_RSA */
|
||||
return EXPECT_RESULT();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,12 @@ int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void);
|
|||
int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
|
||||
int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void);
|
||||
int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void);
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateMinimal(void);
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateEmptyCertsCrls(void);
|
||||
int test_wc_PKCS7_VerifySignedData_TruncSignerInfosTag(void);
|
||||
int test_wc_PKCS7_VerifySignedData_NoSignerInfosTag(void);
|
||||
int test_wc_PKCS7_VerifySignedData_DegenerateNonEmptyDigestAlgos(void);
|
||||
int test_wc_PKCS7_VerifySignedData_NoDegenerateAcceptsRealSigner(void);
|
||||
int test_wc_PKCS7_VerifySignedData_NoDigestParams(void);
|
||||
|
||||
|
||||
|
|
@ -138,6 +144,12 @@ int test_wc_PKCS7_VerifySignedData_NoDigestParams(void);
|
|||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_IndefLenOOB), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncEContentTag), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncCertSetTag), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_DegenerateMinimal), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_DegenerateEmptyCertsCrls), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_TruncSignerInfosTag), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoSignerInfosTag), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_DegenerateNonEmptyDigestAlgos), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoDegenerateAcceptsRealSigner), \
|
||||
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_NoDigestParams)
|
||||
|
||||
#define TEST_PKCS7_ENCRYPTED_DATA_DECLS \
|
||||
|
|
|
|||
|
|
@ -6395,7 +6395,7 @@ static int wc_PKCS7_ParseSignerInfo(wc_PKCS7* pkcs7, byte* in, word32 inSz,
|
|||
|
||||
WOLFSSL_ENTER("wc_PKCS7_ParseSignerInfo");
|
||||
/* require a signer if degenerate case not allowed */
|
||||
if (inSz == 0 && pkcs7->noDegenerate == 1) {
|
||||
if (pkcs7->noDegenerate == 1 && (inSz == 0 || degenerate == 1)) {
|
||||
WOLFSSL_MSG("Set to not allow degenerate cases");
|
||||
return PKCS7_NO_SIGNER_E;
|
||||
}
|
||||
|
|
@ -6666,6 +6666,13 @@ static int wc_PKCS7_HandleOctetStrings(wc_PKCS7* pkcs7, byte* in, word32 inSz,
|
|||
pkcs7->stream->expected, &msg, idx)) != 0) {
|
||||
break;
|
||||
}
|
||||
/* re-sync totalRd baseline to the fresh idx. Without this, a byte
|
||||
* that wc_PKCS7_AddDataToStream() already charged to totalRd while
|
||||
* buffering (growing stream->length) gets charged again below when
|
||||
* this loop switches to reading directly from "in" starting at
|
||||
* that same position, since *tmpIdx would still hold a position
|
||||
* from before the buffered run started. */
|
||||
*tmpIdx = *idx;
|
||||
|
||||
msgSz = (pkcs7->stream->length > 0)? pkcs7->stream->length:inSz;
|
||||
|
||||
|
|
@ -7510,26 +7517,38 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf,
|
|||
pkcs7->content = pkcs7->contentDynamic;
|
||||
}
|
||||
|
||||
/* check if bundle has more elements or footer, if not, set content
|
||||
* to pkcs7->content and hash to pkcs7->hash.
|
||||
*
|
||||
* NOTE: this check returns success whenever fewer than 6 bytes
|
||||
* follow the content within the outer ContentInfo, which also
|
||||
* accepts truncated bundles whose footer was cut short (e.g. a
|
||||
* lone certificates [0] tag with no length). Distinguishing a
|
||||
* legitimate degenerate end (such as an empty signerInfos SET
|
||||
* "31 00") from truncated junk would require peeking at the
|
||||
* remaining bytes or making stage 4's `expected` window smaller.
|
||||
/* expect data length to be enough to check set and seq of certs,
|
||||
* but never request more than the bundle has left. The old code
|
||||
* unconditionally requested this much and silently treated a
|
||||
* short read here as a successful degenerate end, which also
|
||||
* accepted a truncated bundle. Capping the request lets stage
|
||||
* 4/5/6's existing bounds checks and noDegenerate enforcement
|
||||
* run instead: a genuine short degenerate end (empty signerInfos
|
||||
* SET) still parses and succeeds, while anything truncated or
|
||||
* malformed fails there with a real parse error.
|
||||
*/
|
||||
if (ret == 0 && pkcs7->stream->maxLen > 0 &&
|
||||
(pkcs7->stream->maxLen - pkcs7->stream->totalRd)
|
||||
< ASN_TAG_SZ + MAX_LENGTH_SZ) {
|
||||
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
/* expect data length to be enough to check set and seq of certs */
|
||||
pkcs7->stream->expected = (ASN_TAG_SZ + MAX_LENGTH_SZ) * 2;
|
||||
if (!pkcs7->stream->indefLen) {
|
||||
/* cap to what can actually still show up: bytes already
|
||||
* buffered (stream->length) plus whatever the outer
|
||||
* length still allows past totalRd. Matches the same
|
||||
* (maxLen - totalRd) + length expression used at every
|
||||
* other stage in this function. For indefinite-length
|
||||
* (BER) bundles maxLen is only a running estimate, so
|
||||
* this cap does not apply. */
|
||||
if (pkcs7->stream->totalRd >= pkcs7->stream->maxLen) {
|
||||
if (pkcs7->stream->expected > pkcs7->stream->length) {
|
||||
pkcs7->stream->expected = pkcs7->stream->length;
|
||||
}
|
||||
}
|
||||
else if (pkcs7->stream->expected >
|
||||
(pkcs7->stream->maxLen - pkcs7->stream->totalRd) +
|
||||
pkcs7->stream->length) {
|
||||
pkcs7->stream->expected =
|
||||
(pkcs7->stream->maxLen - pkcs7->stream->totalRd) +
|
||||
pkcs7->stream->length;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
/* Break out before content because it can be optional in degenerate
|
||||
|
|
@ -7670,6 +7689,11 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf,
|
|||
}
|
||||
|
||||
maxIdx = idx + pkcs7->stream->expected;
|
||||
|
||||
/* re-sync totalRd baseline to the fresh idx, same as STAGE6
|
||||
* does; otherwise it can still hold a stale position left
|
||||
* over from stage 3's internal stream buffer. */
|
||||
stateIdx = idx;
|
||||
#endif /* !NO_PKCS7_STREAM */
|
||||
|
||||
if (pkiMsg2 == NULL || pkiMsg2Sz == 0) {
|
||||
|
|
@ -7779,6 +7803,11 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf,
|
|||
pkiMsg2Sz = (pkcs7->stream->length > 0)? pkcs7->stream->length:
|
||||
srcSz;
|
||||
|
||||
/* re-sync totalRd baseline to the fresh idx, same as STAGE4
|
||||
* and STAGE6 do; otherwise it can still hold a stale position
|
||||
* left over from STAGE4's internal stream buffer. */
|
||||
stateIdx = idx;
|
||||
|
||||
wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, 0, &length);
|
||||
|
||||
/* restore content */
|
||||
|
|
@ -8083,6 +8112,30 @@ static int PKCS7_VerifySignedData(wc_PKCS7* pkcs7, const byte* hashBuf,
|
|||
NO_USER_CHECK) < 0)
|
||||
ret = ASN_PARSE_E;
|
||||
|
||||
#ifndef NO_PKCS7_STREAM
|
||||
/* claimed signerInfos content must fit in what can still
|
||||
* arrive, or stage 7 stalls on WANT_READ_E forever */
|
||||
if (ret == 0 && !pkcs7->stream->indefLen) {
|
||||
word32 avail;
|
||||
if (pkcs7->stream->length > 0) {
|
||||
avail = pkcs7->stream->length - idx;
|
||||
if (pkcs7->stream->totalRd < pkcs7->stream->maxLen) {
|
||||
avail += pkcs7->stream->maxLen -
|
||||
pkcs7->stream->totalRd;
|
||||
}
|
||||
}
|
||||
else {
|
||||
word32 used = pkcs7->stream->totalRd +
|
||||
(idx - stateIdx);
|
||||
avail = (used < pkcs7->stream->maxLen) ?
|
||||
pkcs7->stream->maxLen - used : 0;
|
||||
}
|
||||
if ((word32)length > avail) {
|
||||
ret = ASN_PARSE_E;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/* 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
|
||||
|
|
|
|||
Loading…
Reference in New Issue