forked from wolfSSL/wolfssl
PKCS7: support PKCS#7 definition for ContentType eContent ANY, in addition to CMS OCTET_STRING definition
This commit is contained in:
@@ -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,75 +4718,114 @@ 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)) {
|
if (ret == 0 &&
|
||||||
multiPart = 1;
|
(tag != (ASN_OCTET_STRING | ASN_CONSTRUCTED) &&
|
||||||
|
(tag != ASN_OCTET_STRING))) {
|
||||||
|
|
||||||
/* Get length of all OCTET_STRINGs. */
|
/* If reached end of ContentInfo, or we see the next element
|
||||||
if (GetLength_ex(pkiMsg, &localIdx, &contentLen, pkiMsgSz,
|
* ([0] IMPLICIT CertificateSet), set error state. Either
|
||||||
NO_USER_CHECK) < 0)
|
* true error or detached */
|
||||||
|
if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) {
|
||||||
ret = ASN_PARSE_E;
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
/* Check whether there is one OCTET_STRING inside. */
|
|
||||||
start = localIdx;
|
|
||||||
if (localIdx >= pkiMsgSz) {
|
|
||||||
ret = BUFFER_E;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz)
|
/* Back up before getting tag, process as PKCS#7 ANY and use
|
||||||
!= 0)
|
* this as start of content. */
|
||||||
ret = ASN_PARSE_E;
|
localIdx = start;
|
||||||
|
pkcs7->contentIsPkcs7Type = 1;
|
||||||
if (ret == 0 && tag != ASN_OCTET_STRING)
|
|
||||||
ret = ASN_PARSE_E;
|
|
||||||
|
|
||||||
if (ret == 0 && GetLength_ex(pkiMsg, &localIdx, &length,
|
|
||||||
pkiMsgSz, NO_USER_CHECK) < 0)
|
|
||||||
ret = ASN_PARSE_E;
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
/* Use single OCTET_STRING directly, or reset length. */
|
|
||||||
if (localIdx - start + length == (word32)contentLen) {
|
|
||||||
multiPart = 0;
|
|
||||||
} else {
|
|
||||||
/* reset length to outer OCTET_STRING for bundle size
|
|
||||||
* check below */
|
|
||||||
length = contentLen;
|
|
||||||
}
|
|
||||||
localIdx = start;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ret != 0) {
|
|
||||||
/* failed ASN1 parsing during OCTET_STRING checks */
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
/* CMS eContent OCTET_STRING */
|
||||||
|
if (ret == 0 && tag == (ASN_OCTET_STRING | ASN_CONSTRUCTED)) {
|
||||||
|
multiPart = 1;
|
||||||
|
|
||||||
/* get length of content in case of single part */
|
/* Get length of all OCTET_STRINGs. */
|
||||||
if (ret == 0 && !multiPart) {
|
if (GetLength_ex(pkiMsg, &localIdx, &contentLen, pkiMsgSz,
|
||||||
if (tag != ASN_OCTET_STRING)
|
NO_USER_CHECK) < 0)
|
||||||
ret = ASN_PARSE_E;
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
if (ret == 0 && GetLength_ex(pkiMsg, &localIdx,
|
/* Check whether there is one OCTET_STRING inside. */
|
||||||
&length, pkiMsgSz, NO_USER_CHECK) < 0)
|
start = localIdx;
|
||||||
ret = ASN_PARSE_E;
|
if (localIdx >= pkiMsgSz) {
|
||||||
|
ret = BUFFER_E;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz)
|
||||||
|
!= 0)
|
||||||
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
|
if (ret == 0 && tag != ASN_OCTET_STRING)
|
||||||
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
|
if (ret == 0 && GetLength_ex(pkiMsg, &localIdx, &length,
|
||||||
|
pkiMsgSz, NO_USER_CHECK) < 0)
|
||||||
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
|
if (ret == 0) {
|
||||||
|
/* Use single OCTET_STRING directly, or reset length. */
|
||||||
|
if (localIdx - start + length == (word32)contentLen) {
|
||||||
|
multiPart = 0;
|
||||||
|
} else {
|
||||||
|
/* reset length to outer OCTET_STRING for bundle
|
||||||
|
* size check below */
|
||||||
|
length = contentLen;
|
||||||
|
}
|
||||||
|
localIdx = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret != 0) {
|
||||||
|
/* failed ASN1 parsing during OCTET_STRING checks */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* get length of content in case of single part */
|
||||||
|
if (ret == 0 && !multiPart) {
|
||||||
|
if (tag != ASN_OCTET_STRING)
|
||||||
|
ret = ASN_PARSE_E;
|
||||||
|
|
||||||
|
if (ret == 0 && GetLength_ex(pkiMsg, &localIdx,
|
||||||
|
&length, pkiMsgSz, NO_USER_CHECK) < 0)
|
||||||
|
ret = ASN_PARSE_E;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* update idx if successful */
|
/* update idx if successful */
|
||||||
@@ -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
|
||||||
|
@@ -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 !! */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user