PKCS7: support PKCS#7 definition for ContentType eContent ANY, in addition to CMS OCTET_STRING definition

This commit is contained in:
Chris Conlon
2023-09-08 16:32:15 -06:00
parent 298b488bf1
commit 0bb9b85cd4
2 changed files with 121 additions and 49 deletions

View File

@@ -3786,7 +3786,10 @@ static int wc_PKCS7_VerifyContentMessageDigest(PKCS7* pkcs7,
word32 hashSz) word32 hashSz)
{ {
int ret = 0, digestSz = 0, innerAttribSz = 0; int ret = 0, digestSz = 0, innerAttribSz = 0;
int contentLen = 0;
word32 idx = 0; word32 idx = 0;
word32 contentIdx = 0;
byte* content = NULL;
byte* digestBuf = NULL; byte* digestBuf = NULL;
#ifdef WOLFSSL_SMALL_STACK #ifdef WOLFSSL_SMALL_STACK
byte* digest = NULL; byte* digest = NULL;
@@ -3845,7 +3848,29 @@ static int wc_PKCS7_VerifyContentMessageDigest(PKCS7* pkcs7,
#endif #endif
XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ);
ret = wc_Hash(hashType, pkcs7->content, pkcs7->contentSz, digest, content = pkcs7->content;
contentLen = pkcs7->contentSz;
if (pkcs7->contentIsPkcs7Type == 1) {
/* Content follows PKCS#7 RFC, which defines type as ANY. CMS
* mandates OCTET_STRING which has already been stripped off.
* For PKCS#7 message digest calculation, digest is calculated
* only on the "value" of the DER encoding. As such, advance past
* the tag and length */
if (contentLen > 1) {
contentIdx++;
}
if (GetLength_ex(content, &contentIdx, &contentLen,
contentLen, 1) < 0) {
#ifdef WOLFSSL_SMALL_STACK
XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ASN_PARSE_E;
}
}
ret = wc_Hash(hashType, content + contentIdx, contentLen, digest,
MAX_PKCS7_DIGEST_SZ); MAX_PKCS7_DIGEST_SZ);
if (ret < 0) { if (ret < 0) {
WOLFSSL_MSG("Error hashing PKCS7 content for verification"); WOLFSSL_MSG("Error hashing PKCS7 content for verification");
@@ -4435,11 +4460,13 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
byte* cert = NULL; byte* cert = NULL;
byte* signedAttrib = NULL; byte* signedAttrib = NULL;
byte* contentType = NULL; byte* contentType = NULL;
int encapContentInfoLen = 0;
int contentSz = 0, sigSz = 0, certSz = 0, signedAttribSz = 0; int contentSz = 0, sigSz = 0, certSz = 0, signedAttribSz = 0;
word32 localIdx, start; word32 localIdx, start;
byte degenerate = 0; byte degenerate = 0;
byte detached = 0; byte detached = 0;
byte tag = 0; byte tag = 0;
word16 contentIsPkcs7Type = 0;
#ifdef ASN_BER_TO_DER #ifdef ASN_BER_TO_DER
byte* der; byte* der;
#endif #endif
@@ -4649,7 +4676,7 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
#endif #endif
/* Get the inner ContentInfo sequence */ /* Get the inner ContentInfo sequence */
if (GetSequence_ex(pkiMsg, &idx, &length, pkiMsgSz, if (GetSequence_ex(pkiMsg, &idx, &encapContentInfoLen, pkiMsgSz,
NO_USER_CHECK) < 0) NO_USER_CHECK) < 0)
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
@@ -4657,7 +4684,8 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
if (ret == 0) { if (ret == 0) {
int isIndef = 0; int isIndef = 0;
word32 tmpIdx = idx; word32 tmpIdx = idx;
if (length == 0 && pkiMsg[idx-1] == ASN_INDEF_LENGTH) { if (encapContentInfoLen == 0 &&
pkiMsg[idx-1] == ASN_INDEF_LENGTH) {
isIndef = 1; isIndef = 1;
} }
if (GetASNObjectId(pkiMsg, &idx, &length, pkiMsgSz) == 0) { if (GetASNObjectId(pkiMsg, &idx, &length, pkiMsgSz) == 0) {
@@ -4682,7 +4710,7 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
if (ret != 0) if (ret != 0)
break; break;
/* Check for content info, it could be omitted when degenerate */ /* Check for content, it could be omitted when degenerate */
localIdx = idx; localIdx = idx;
ret = 0; ret = 0;
if (localIdx + 1 > pkiMsgSz) { if (localIdx + 1 > pkiMsgSz) {
@@ -4690,24 +4718,62 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
break; break;
} }
/* Set error state if no more data left in ContentInfo, meaning
* no content - may be detached. Will recover from error below */
if ((encapContentInfoLen != 0) &&
(encapContentInfoLen - contentTypeSz == 0)) {
ret = ASN_PARSE_E;
}
/* PKCS#7 spec:
* content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
* CMS spec:
* eContent [0] EXPLICIT OCTET STRING OPTIONAL
*/
if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) != 0) if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) != 0)
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
if (ret == 0 && tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) if (ret == 0 && tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0))
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
/* Get length of inner eContent payload. For CMS, spec defines
* OCTET_STRING will be next. If so, we use the length retrieved
* there. PKCS#7 spec defines ANY as eContent type. In this case
* we fall back and save this content length for use later */
if (ret == 0 && GetLength_ex(pkiMsg, &localIdx, &length, pkiMsgSz, if (ret == 0 && GetLength_ex(pkiMsg, &localIdx, &length, pkiMsgSz,
NO_USER_CHECK) <= 0) NO_USER_CHECK) <= 0) {
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
}
if (localIdx >= pkiMsgSz) { if (localIdx >= pkiMsgSz) {
ret = BUFFER_E; ret = BUFFER_E;
} }
/* Save idx to back up in case of PKCS#7 eContent */
start = localIdx;
/* get length of content in the case that there is multiple parts */ /* get length of content in the case that there is multiple parts */
if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) < 0) if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) < 0)
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
if (ret == 0 &&
(tag != (ASN_OCTET_STRING | ASN_CONSTRUCTED) &&
(tag != ASN_OCTET_STRING))) {
/* If reached end of ContentInfo, or we see the next element
* ([0] IMPLICIT CertificateSet), set error state. Either
* true error or detached */
if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) {
ret = ASN_PARSE_E;
}
/* Back up before getting tag, process as PKCS#7 ANY and use
* this as start of content. */
localIdx = start;
pkcs7->contentIsPkcs7Type = 1;
}
else {
/* CMS eContent OCTET_STRING */
if (ret == 0 && tag == (ASN_OCTET_STRING | ASN_CONSTRUCTED)) { if (ret == 0 && tag == (ASN_OCTET_STRING | ASN_CONSTRUCTED)) {
multiPart = 1; multiPart = 1;
@@ -4738,8 +4804,8 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
if (localIdx - start + length == (word32)contentLen) { if (localIdx - start + length == (word32)contentLen) {
multiPart = 0; multiPart = 0;
} else { } else {
/* reset length to outer OCTET_STRING for bundle size /* reset length to outer OCTET_STRING for bundle
* check below */ * size check below */
length = contentLen; length = contentLen;
} }
localIdx = start; localIdx = start;
@@ -4760,6 +4826,7 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
&length, pkiMsgSz, NO_USER_CHECK) < 0) &length, pkiMsgSz, NO_USER_CHECK) < 0)
ret = ASN_PARSE_E; ret = ASN_PARSE_E;
} }
}
/* update idx if successful */ /* update idx if successful */
if (ret == 0) { if (ret == 0) {
@@ -5111,6 +5178,7 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
pkcs7->der = NULL; pkcs7->der = NULL;
#endif #endif
version = pkcs7->version; version = pkcs7->version;
contentIsPkcs7Type = pkcs7->contentIsPkcs7Type;
if (ret == 0) { if (ret == 0) {
byte isDynamic = (byte)pkcs7->isDynamic; byte isDynamic = (byte)pkcs7->isDynamic;
@@ -5146,6 +5214,9 @@ static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf,
contentDynamic = NULL; contentDynamic = NULL;
} }
/* Restore content is PKCS#7 flag */
pkcs7->contentIsPkcs7Type = contentIsPkcs7Type;
#ifndef NO_PKCS7_STREAM #ifndef NO_PKCS7_STREAM
pkcs7->stream = stream; pkcs7->stream = stream;
#endif #endif

View File

@@ -341,6 +341,7 @@ struct PKCS7 {
byte* cachedEncryptedContent; byte* cachedEncryptedContent;
word32 cachedEncryptedContentSz; word32 cachedEncryptedContentSz;
word16 contentCRLF:1; /* have content line endings been converted to CRLF */ word16 contentCRLF:1; /* have content line endings been converted to CRLF */
word16 contentIsPkcs7Type:1; /* eContent follows PKCS#7 RFC not CMS */
/* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */
}; };