Skip to content
Open
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
18 changes: 16 additions & 2 deletions doc/dox_comments/header_files/pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,20 @@ int wc_PKCS7_EncodeSignedData_ex(wc_PKCS7* pkcs7, const byte* hashBuf,
\brief This function takes in a transmitted PKCS7 signed data message,
extracts the certificate list and certificate revocation list, and then
verifies the signature. It stores the extracted content in the given
PKCS7 structure.
PKCS7 structure. For legacy PKCS#7 ANY-typed eContent (a non-OCTET STRING
element, e.g. a SEQUENCE as RFC 2315 permits), pkcs7->content is the whole
DER element (so contentSz includes the tag and length octets), whereas a
CMS OCTET STRING eContent reports the value only. The element shape is
enforced only when the signer uses signed attributes: in that case it must
be a single DER element spanning the whole eContent, and an element shorter
than two octets, trailing bytes after it, an indefinite length, a
non-minimal or out-of-range length, the end-of-contents identifier (0x00),
or a high-tag-number identifier are
rejected with ASN_PARSE_E; with no signed attributes no shape check runs
and the signature covers the eContent region as received. pkcs7->content
is valid only until the next verify on this structure or wc_PKCS7_Free();
when the message is verified from a single input buffer it may point into
that buffer, so it is also invalid once the input buffer is freed or reused.

\return 0 Returned on successfully extracting the information
from the message
Expand Down Expand Up @@ -404,7 +417,8 @@ int wc_PKCS7_VerifySignedData(wc_PKCS7* pkcs7,
\brief This function takes in a transmitted PKCS7 signed data message as
hash/header/footer, then extracts the certificate list and certificate
revocation list, and then verifies the signature. It stores the extracted
content in the given PKCS7 structure.
content in the given PKCS7 structure, in the same form as
wc_PKCS7_VerifySignedData() reports it.

\return 0 Returned on successfully extracting the information
from the message
Expand Down
8 changes: 7 additions & 1 deletion doc/dox_comments/header_files/tsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -1621,7 +1621,13 @@ int wc_TspTstInfo_SignWithPkcs7(const TspTstInfo* tstInfo, wc_PKCS7* pkcs7,
signer's certificate.
\return HASH_TYPE_E Returned when a hash algorithm is not available or
below WC_TSP_MIN_HASH_STRENGTH_BITS.
\return ASN_PARSE_E Returned when an encoding is invalid.
\return ASN_PARSE_E Returned when an encoding is invalid, including a
legacy ANY eContent that is not a single DER element spanning the whole
content (an element shorter than two octets, trailing bytes, a non-minimal,
out-of-range or indefinite length, the end-of-contents identifier (0x00),
or a high-tag-number identifier). A
well-formed ANY eContent that is not a TSTInfo SEQUENCE is also rejected
with ASN_PARSE_E when the content is decoded as a TSTInfo.
\return MEMORY_E Returned on dynamic memory allocation failure.

\param [in] pkcs7 Pointer to an initialized PKCS7 object.
Expand Down
821 changes: 821 additions & 0 deletions tests/api/test_pkcs7.c

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions tests/api/test_pkcs7.h
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ int test_wc_PKCS7_DecodeAuthEnvelopedData_shortTagCcm(void);
int test_wc_PKCS7_DecodeAuthEnvelopedData_shortTagChunked(void);
int test_wc_PKCS7_AuthEnvelopedData_stream_leak(void);
int test_wc_PKCS7_VerifySignedData_PKCS7ContentSeq(void);
int test_wc_PKCS7_VerifySignedData_eContentAnySpan(void);
int test_wc_PKCS7_VerifySignedData_IndefLenOOB(void);
int test_wc_PKCS7_VerifySignedData_TruncEContentTag(void);
int test_wc_PKCS7_VerifySignedData_TruncCertSetTag(void);
Expand Down Expand Up @@ -153,6 +154,7 @@ int test_wc_PKCS7_VerifySignedData_NoDigestParams(void);
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_VerifySignedData_PKCS7ContentSeq), \
TEST_DECL_GROUP("pkcs7_sd", test_wc_PKCS7_VerifySignedData_eContentAnySpan), \
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), \
Expand Down
114 changes: 114 additions & 0 deletions tests/api/test_tsp.c
Original file line number Diff line number Diff line change
Expand Up @@ -2145,6 +2145,120 @@ int test_wc_TspTstInfo_SignWithPkcs7_create(void)
return EXPECT_RESULT();
}

int test_wc_TspTstInfo_VerifyWithPKCS7_legacyAny(void)
{
EXPECT_DECLS;
#if defined(WOLFSSL_TSP) && defined(HAVE_PKCS7) && !defined(NO_RSA) && \
!defined(NO_SHA256) && !defined(WC_NO_RNG) && \
defined(WOLFSSL_TSP_REQUESTER) && defined(WOLFSSL_TSP_RESPONDER)
wc_PKCS7* pkcs7 = NULL;
WC_RNG rng;
TspTstInfo tstDec;
byte token[3072];
byte mod[3072];
word32 tokenSz;
int i;
int off = -1;
int lenOff = -1;

ExpectIntEQ(wc_InitRng(&rng), 0);
tokenSz = (word32)sizeof(token);
ExpectIntEQ(test_tsp_make_token(token, &tokenSz, tsa_cert_der_2048,
sizeof_tsa_cert_der_2048, tsa_key_der_2048, sizeof_tsa_key_der_2048,
RSAk, &rng), TEST_SUCCESS);
ExpectIntGT(tokenSz, 0);

/* A wolfSSL-encoded token always has a CMS OCTET STRING eContent wrapping
* the TstInfo SEQUENCE; the well-formed success path is covered by
* test_wc_TspTstInfo_SignWithPkcs7_create. Here that eContent is retagged
* to an ANY SEQUENCE to drive the new span check on the TSP verify call. A
* true legacy token (the TstInfo SEQUENCE as the eContent directly) cannot
* be produced by retagging this token: the wolfSSL signer digested the
* OCTET STRING value, i.e. the whole TstInfo SEQUENCE TLV, while after
* retagging the ANY value-skip hashes only the SEQUENCE value, so the
* digests never match.
*
* Locate the eContent OCTET STRING: it wraps the TstInfo SEQUENCE, whose
* first field is the version INTEGER 1 (02 01 01). Short-form and one-byte
* long-form (0x81) OCTET STRING lengths are handled, covering a TstInfo up
* to 255 octets; the fixed test vectors stay within that, so a two-byte
* (0x82) long form does not arise here. */
for (i = 0; off < 0 && i + 8 <= (int)tokenSz; i++) {
int vOff, j;
if (token[i] != 0x04)
continue;
if (token[i + 1] < 0x80) {
lenOff = i + 1;
vOff = i + 2;
}
else if (token[i + 1] == 0x81) {
lenOff = i + 2;
vOff = i + 3;
}
else {
continue;
}
if (vOff >= (int)tokenSz || token[vOff] != 0x30)
continue;
for (j = vOff + 1; j + 2 < (int)tokenSz && j <= vOff + 3; j++) {
if (token[j] == 0x02 && token[j + 1] == 0x01 &&
token[j + 2] == 0x01) {
off = i;
break;
}
}
}
ExpectIntGE(off, 0);

/* Retag the OCTET STRING to a SEQUENCE. The element now spans the eContent
* (its value is the original TstInfo SEQUENCE), so the value-skip digest
* still matches and the span check accepts it: wc_PKCS7_VerifySignedData
* passes. wc_TspTstInfo_VerifyWithPKCS7 then returns ASN_PARSE_E because the
* decoder sees a SEQUENCE wrapping a SEQUENCE, not a TstInfo. */
if (off >= 0) {
XMEMCPY(mod, token, tokenSz);
mod[off] = 0x30;
ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, mod, tokenSz), 0);
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, mod, tokenSz, &tstDec),
WC_NO_ERR_TRACE(ASN_PARSE_E));
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;
}

/* Trailing bytes: retag and shorten the element so four bytes trail it. The
* span check runs on the TSP verify call and rejects it with ASN_PARSE_E
* before the TSP decode is reached. */
if (off >= 0) {
XMEMCPY(mod, token, tokenSz);
mod[off] = 0x30;
mod[lenOff] = (byte)(token[lenOff] - 4);
ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_PKCS7_VerifySignedData(pkcs7, mod, tokenSz),
WC_NO_ERR_TRACE(ASN_PARSE_E));
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;

ExpectNotNull(pkcs7 = wc_PKCS7_New(NULL, testDevId));
ExpectIntEQ(wc_PKCS7_InitWithCert(pkcs7, NULL, 0), 0);
ExpectIntEQ(wc_TspTstInfo_VerifyWithPKCS7(pkcs7, mod, tokenSz, &tstDec),
WC_NO_ERR_TRACE(ASN_PARSE_E));
wc_PKCS7_Free(pkcs7);
pkcs7 = NULL;
}

wc_FreeRng(&rng);
#endif
return EXPECT_RESULT();
}

int test_wc_TspTstInfo_SignWithPkcs7_signer_required(void)
{
EXPECT_DECLS;
Expand Down
2 changes: 2 additions & 0 deletions tests/api/test_tsp.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ int test_wc_TspTstInfo_CheckRequest(void);
int test_wc_TspTstInfo_CheckTsaName(void);
int test_wc_TspTstInfo_SignWithPkcs7(void);
int test_wc_TspTstInfo_SignWithPkcs7_create(void);
int test_wc_TspTstInfo_VerifyWithPKCS7_legacyAny(void);
int test_wc_TspTstInfo_SignWithPkcs7_signer_required(void);
int test_wc_TspTstInfo_SignWithPkcs7_hash_and_buffer(void);
int test_wc_TspTstInfo_Sign(void);
Expand Down Expand Up @@ -117,6 +118,7 @@ int test_wc_TspTstInfo_SetFromRequest(void);
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_CheckTsaName), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_create), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_VerifyWithPKCS7_legacyAny), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_signer_required), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_SignWithPkcs7_hash_and_buffer), \
TEST_DECL_GROUP("tsp", test_wc_TspTstInfo_Sign), \
Expand Down
113 changes: 106 additions & 7 deletions tests/unit-mcdc/test_pkcs7_arg_whitebox.c
Original file line number Diff line number Diff line change
Expand Up @@ -566,7 +566,7 @@ static void wb_signerinfo_binding(void)

/* ------------------------------------------------------------------------- *
* Section 4: wc_PKCS7_VerifyContentMessageDigest() attribute/content shapes
* [:5857, :5890, :5893]
* [:6012, :6069, :6075] plus the ANY-span check [:6030, :6037, :6046]
* ------------------------------------------------------------------------- */
static void wb_verify_content_msgdigest(void)
{
Expand All @@ -577,11 +577,41 @@ static void wb_verify_content_msgdigest(void)
byte content[8];
byte pkcs7Content[8];
byte hash[32];
byte anySpan[2]; /* 30 00: single element spanning the content */
byte anyTrail[3]; /* 30 00 00: element plus a trailing byte */
byte anyHighTag[2]; /* 1f 00: high-tag-number identifier */
byte anyIndef[2]; /* 30 80: indefinite length */
byte anyBadLen[3]; /* 30 81 7f: non-minimal 1-byte long-form length */
byte anyBufExc[3]; /* 30 81 80: long-form length runs past the element */
byte anyLen2NM[4]; /* 30 82 00 13: non-minimal 2-byte long-form length */
byte anyLen4MSB[6]; /* 30 84 80 00 00 00: 4-byte length with the MSB set */
byte anyLenOvr[7]; /* 30 85 ...: overlong length (more than four octets) */
byte anyEoc[3]; /* 00 01 78: end-of-contents identifier as element tag */
byte anyLen3NM[5]; /* 30 83 00 00 13: non-minimal 3-byte long-form length */
byte anyLen4NM[6]; /* 30 84 00 00 00 13: non-minimal 4-byte long-form len */

XMEMSET(&attrib, 0, sizeof(attrib));
XMEMCPY(mdOidDer, wbMessageDigestOid, sizeof(wbMessageDigestOid));
XMEMSET(content, 0x31, sizeof(content));
XMEMSET(hash, 0, sizeof(hash));
anySpan[0] = 0x30; anySpan[1] = 0x00;
anyTrail[0] = 0x30; anyTrail[1] = 0x00; anyTrail[2] = 0x00;
anyHighTag[0] = 0x1f; anyHighTag[1] = 0x00;
anyIndef[0] = 0x30; anyIndef[1] = 0x80;
anyBadLen[0] = 0x30; anyBadLen[1] = 0x81; anyBadLen[2] = 0x7f;
anyBufExc[0] = 0x30; anyBufExc[1] = 0x81; anyBufExc[2] = 0x80;
anyLen2NM[0] = 0x30; anyLen2NM[1] = 0x82; anyLen2NM[2] = 0x00;
anyLen2NM[3] = 0x13;
anyLen4MSB[0] = 0x30; anyLen4MSB[1] = 0x84; anyLen4MSB[2] = 0x80;
anyLen4MSB[3] = 0x00; anyLen4MSB[4] = 0x00; anyLen4MSB[5] = 0x00;
anyLenOvr[0] = 0x30; anyLenOvr[1] = 0x85; anyLenOvr[2] = 0x00;
anyLenOvr[3] = 0x00; anyLenOvr[4] = 0x00; anyLenOvr[5] = 0x00;
anyLenOvr[6] = 0x13;
anyEoc[0] = 0x00; anyEoc[1] = 0x01; anyEoc[2] = 0x78;
anyLen3NM[0] = 0x30; anyLen3NM[1] = 0x83; anyLen3NM[2] = 0x00;
anyLen3NM[3] = 0x00; anyLen3NM[4] = 0x13;
anyLen4NM[0] = 0x30; anyLen4NM[1] = 0x84; anyLen4NM[2] = 0x00;
anyLen4NM[3] = 0x00; anyLen4NM[4] = 0x00; anyLen4NM[5] = 0x13;

/* OCTET STRING wrapping a 32-byte digest */
mdValue[0] = ASN_OCTET_STRING;
Expand All @@ -608,7 +638,7 @@ static void wb_verify_content_msgdigest(void)
attrib.valueSz = (word32)sizeof(mdValue);
p.decodedAttrib = &attrib;

WB_NOTE("VerifyContentMessageDigest attrib->value guards [:5857]");
WB_NOTE("VerifyContentMessageDigest attrib->value guards [:6012]");
/* 1st operand true */
attrib.value = NULL;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
Expand All @@ -618,23 +648,23 @@ static void wb_verify_content_msgdigest(void)
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
attrib.valueSz = (word32)sizeof(mdValue);

WB_NOTE("VerifyContentMessageDigest content shape [:5890,:5893]");
/* all false at :5857; :5890 1st operand false (content == NULL) */
WB_NOTE("VerifyContentMessageDigest content shape [:6069,:6075]");
/* all false at :6012; :6069 1st operand false (content == NULL) */
p.content = NULL;
p.contentSz = 0;
p.contentIsPkcs7Type = 1;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :5890 2nd operand false (content set, not PKCS#7-typed) */
/* :6069 2nd operand false (content set, not PKCS#7-typed) */
p.content = content;
p.contentSz = (word32)sizeof(content);
p.contentIsPkcs7Type = 0;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :5890 both true, :5893 contentLen > 1 */
/* :6069 both true, :6075 contentLen > 1 */
p.content = pkcs7Content;
p.contentSz = (word32)sizeof(pkcs7Content);
p.contentIsPkcs7Type = 1;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :5893 contentLen <= 1 */
/* :6037 contentSz < 2 operand (intercepts the old contentLen <= 1 drive) */
p.contentSz = 1;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* caller-supplied hash path */
Expand All @@ -643,6 +673,75 @@ static void wb_verify_content_msgdigest(void)
p.contentIsPkcs7Type = 0;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, hash, (word32)sizeof(hash));

/* ANY-span check [:6030-:6050], inserted ahead of the decisions above and
* reached with the valid messageDigest attribute set here. */
WB_NOTE("VerifyContentMessageDigest ANY-span check [:6030,:6037,:6046]");
attrib.value = mdValue;
attrib.valueSz = (word32)sizeof(mdValue);
p.contentIsPkcs7Type = 1;
/* :6030 contentSz > 0 false operand (flag set, content set, size 0); this
* also drives the :6075 contentLen <= 1 arm, unreachable once the span
* check runs (it requires contentSz >= 2). */
p.content = anySpan;
p.contentSz = 0;
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6030 content != NULL operand varies alone: content NULL with
* contentSz > 0 (MCDC pair; the parse code never produces this state,
* so it is a defensive guard). */
p.content = NULL;
p.contentSz = (word32)sizeof(content);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6037 high-tag-number identifier operand */
p.content = anyHighTag;
p.contentSz = (word32)sizeof(anyHighTag);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6037 end-of-contents (0x00) operand: not a valid element tag, the
* length still spans so only this operand rejects */
p.content = anyEoc;
p.contentSz = (word32)sizeof(anyEoc);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6037 indefinite-length operand */
p.content = anyIndef;
p.contentSz = (word32)sizeof(anyIndef);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: non-minimal 1-byte long-form length
* (0x7f is below the 0x80 minimum, not a run past the end) */
p.content = anyBadLen;
p.contentSz = (word32)sizeof(anyBadLen);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: long-form length runs past the element */
p.content = anyBufExc;
p.contentSz = (word32)sizeof(anyBufExc);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: non-minimal 2-byte long-form length */
p.content = anyLen2NM;
p.contentSz = (word32)sizeof(anyLen2NM);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: non-minimal 3-byte long-form length */
p.content = anyLen3NM;
p.contentSz = (word32)sizeof(anyLen3NM);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: non-minimal 4-byte long-form length */
p.content = anyLen4NM;
p.contentSz = (word32)sizeof(anyLen4NM);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: 4-byte length with the high bit set */
p.content = anyLen4MSB;
p.contentSz = (word32)sizeof(anyLen4MSB);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6043 GetLength_ex failure: overlong length (more than four octets) */
p.content = anyLenOvr;
p.contentSz = (word32)sizeof(anyLenOvr);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6046 element does not span the content (trailing byte) */
p.content = anyTrail;
p.contentSz = (word32)sizeof(anyTrail);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);
/* :6030-:6046 all pass: a single element spanning the content */
p.content = anySpan;
p.contentSz = (word32)sizeof(anySpan);
(void)wc_PKCS7_VerifyContentMessageDigest(&p, NULL, 0);

p.decodedAttrib = NULL;
p.content = NULL;
p.contentSz = 0;
Expand Down
Loading
Loading