From 83a150c4dfbee30b60b630181be77cfab614be22 Mon Sep 17 00:00:00 2001 From: Jacob Barthelmeh Date: Fri, 5 Oct 2018 13:28:20 -0600 Subject: [PATCH] stream of PKCS7 decode encrypted --- tests/api.c | 1 + wolfcrypt/src/error.c | 3 + wolfcrypt/src/pkcs7.c | 556 ++++++++++++++++++++++++++------ wolfcrypt/test/test.c | 19 ++ wolfssl/wolfcrypt/asn.h | 1 + wolfssl/wolfcrypt/error-crypt.h | 3 +- wolfssl/wolfcrypt/pkcs7.h | 15 + 7 files changed, 504 insertions(+), 94 deletions(-) diff --git a/tests/api.c b/tests/api.c index e87552e66..b22fff53d 100644 --- a/tests/api.c +++ b/tests/api.c @@ -15022,6 +15022,7 @@ static void test_wc_PKCS7_EncodeData (void) XMEMSET(output, 0, sizeof(output)); + pkcs7.isDynamic = 0; AssertIntEQ(wc_PKCS7_Init(&pkcs7, HEAP_HINT, INVALID_DEVID), 0); AssertIntEQ(wc_PKCS7_InitWithCert(&pkcs7, (byte*)cert, certSz), 0); diff --git a/wolfcrypt/src/error.c b/wolfcrypt/src/error.c index 47217475a..5ebf0e209 100644 --- a/wolfcrypt/src/error.c +++ b/wolfcrypt/src/error.c @@ -281,6 +281,9 @@ const char* wc_GetErrorString(int error) case PKCS7_RECIP_E: return "PKCS#7 error: no matching recipient found"; + case WC_PKCS7_WANT_READ_E: + return "PKCS#7 operations wants more input, call again"; + case FIPS_NOT_ALLOWED_E: return "FIPS mode not allowed error"; diff --git a/wolfcrypt/src/pkcs7.c b/wolfcrypt/src/pkcs7.c index 92b2041fc..c02b3aba5 100644 --- a/wolfcrypt/src/pkcs7.c +++ b/wolfcrypt/src/pkcs7.c @@ -58,6 +58,145 @@ typedef enum { WC_PKCS7_DECODE } pkcs7Direction; + +#ifndef NO_PKCS7_STREAM + +#define MAX_PKCS7_STREAM_BUFFER 256 +typedef struct PKCS7State { + byte* tmpCert; + word32 varOne; + word32 varTwo; + word32 varThree; + word32 vers; + word32 idx; /* index read into current input buffer */ + word32 maxLen; /* sanity cap on maximum amount of data to allow + * needed for GetSequence and other calls */ + word32 length; /* amount of data stored */ + word32 expected; /* next amount of data expected, if needed */ + word32 totalRd; /* total amount of bytes read */ + byte hasAtrib:1; + byte buffer[4096]; + byte tmpIv[MAX_CONTENT_IV_SIZE]; /* store IV if needed */ +} PKCS7State; + + +enum PKCS7_MaxLen { + PKCS7_DEFAULT_PEEK = 0, + PKCS7_SEQ_PEEK +}; + +/* creates a PKCS7State structure and returns 0 on success */ +static int wc_PKCS7_CreateStream(PKCS7* pkcs7) +{ + WOLFSSL_MSG("creating PKCS7 stream structure"); + pkcs7->stream = (PKCS7State*)XMALLOC(sizeof(PKCS7State), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream == NULL) { + return MEMORY_E; + } + XMEMSET(pkcs7->stream, 0, sizeof(PKCS7State)); + return 0; +} + + +static void wc_PKCS7_ResetStream(PKCS7* pkcs7) +{ + /* free any buffers that may be allocated */ + + /* reset values */ + XMEMSET(pkcs7->stream, 0, sizeof(PKCS7State)); +} + + +static void wc_PKCS7_FreeStream(PKCS7* pkcs7) +{ + if (pkcs7 != NULL && pkcs7->stream != NULL) { + wc_PKCS7_ResetStream(pkcs7); + XFREE(pkcs7->stream, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } +} + + +/* pt gets set to the buffer that is holding data in the case that stream struct + * is used. + * + * returns 0 on success + */ +static int wc_PKCS7_AddDataToStream(PKCS7* pkcs7, byte* in, word32 inSz, + word32 expected, byte** pt, word32* idx) +{ + word32 rdSz; + + if (inSz >= expected && pkcs7->stream->length == 0) { + /* storing input buffer is not needed */ + return 0; + } + + /* is there enough stored in buffer already? */ + if (pkcs7->stream->length >= expected) { + *idx = 0; + *pt = pkcs7->stream->buffer; + return 0; + } + + /* check if all data has been read from input */ + rdSz = pkcs7->stream->idx; + if (rdSz >= inSz) { + /* no more input to read */ + pkcs7->stream->idx = 0; + return WC_PKCS7_WANT_READ_E; + } + + if (inSz - rdSz > 0 && pkcs7->stream->length < expected) { + int len = min(inSz - rdSz, expected - pkcs7->stream->length); + XMEMCPY(pkcs7->stream->buffer + pkcs7->stream->length, in + rdSz, len); + pkcs7->stream->length += len; + pkcs7->stream->idx += len; + pkcs7->stream->totalRd += len; + } + + if (pkcs7->stream->length < expected) { + pkcs7->stream->idx = 0; + return WC_PKCS7_WANT_READ_E; + } + + /* read from stored buffer */ + *idx = 0; + *pt = pkcs7->stream->buffer; + return 0; +} + + +/* Does two things + * 1) Tries to get the length from current buffer and set it as max length + * 2) Retrieves the set max length + * + * if no flag value is set then the stored max length is returned. + * returns length found on success and defSz if no stored data is found + */ +static word32 wc_PKCS7_GetMaxStream(PKCS7* pkcs7, byte flag, word32 defSz) +{ + /* check there is a buffer to read from */ + if (pkcs7 && pkcs7->stream->length > 0) { + int length = 0, ret; + word32 idx = 0; + if (flag & PKCS7_SEQ_PEEK) { + if ((ret = GetSequence(pkcs7->stream->buffer, &idx, + &length, (word32)-1)) < 0) { + return ret; + } + pkcs7->stream->maxLen = length + idx; + } + return pkcs7->stream->maxLen; + } + + if (pkcs7->stream->maxLen == 0) { + pkcs7->stream->maxLen = defSz; + } + return defSz; +} +#endif /* NO_PKCS7_STREAM */ + #define MAX_PKCS7_DIGEST_SZ (MAX_SEQ_SZ + MAX_ALGO_SZ + \ MAX_OCTET_STR_SZ + WC_MAX_DIGEST_SIZE) @@ -595,6 +734,10 @@ void wc_PKCS7_Free(PKCS7* pkcs7) if (pkcs7 == NULL) return; +#ifndef NO_PKCS7_STREAM + wc_PKCS7_FreeStream(pkcs7); +#endif + wc_PKCS7_FreeDecodedAttrib(pkcs7->decodedAttrib, pkcs7->heap); wc_PKCS7_FreeCertSet(pkcs7); @@ -6774,7 +6917,7 @@ static int wc_PKCS7_ParseToRecipientInfoSet(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, word32* idx, int type) { - int version, length; + int version, length, ret; word32 contentType; if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0 || idx == NULL) @@ -6859,6 +7002,7 @@ static int wc_PKCS7_ParseToRecipientInfoSet(PKCS7* pkcs7, byte* pkiMsg, if (GetSet(pkiMsg, idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; + (void)ret; return length; } @@ -8076,20 +8220,24 @@ static int wc_PKCS7_DecodeUnprotectedAttributes(PKCS7* pkcs7, byte* pkiMsg, /* unwrap and decrypt PKCS#7/CMS encrypted-data object, returned decoded size */ -int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, +int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* in, word32 inSz, byte* output, word32 outputSz) { - int ret, version, length, haveAttribs; - word32 idx = 0; + int ret = 0, version, length, haveAttribs; + word32 idx = 0, tmpIdx = 0; word32 contentType, encOID; - int expBlockSz; - byte tmpIv[MAX_CONTENT_IV_SIZE]; + int expBlockSz = 0; + byte tmpIvBuf[MAX_CONTENT_IV_SIZE]; + byte *tmpIv = tmpIvBuf; int encryptedContentSz; byte padLen; byte* encryptedContent = NULL; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + if (pkcs7 == NULL || pkcs7->encryptionKey == NULL || pkcs7->encryptionKeySz == 0) return BAD_FUNC_ARG; @@ -8098,119 +8246,343 @@ int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, output == NULL || outputSz == 0) return BAD_FUNC_ARG; - /* read past ContentInfo, verify type is encrypted-data */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; - - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) - return ASN_PARSE_E; - - if (contentType != ENCRYPTED_DATA) { - WOLFSSL_MSG("PKCS#7 input not of type EncryptedData"); - return PKCS7_OID_E; +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { + return ret; + } } +#endif - if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) - return ASN_PARSE_E; + switch (pkcs7->state) { + case WC_PKCS7_START: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_SEQ_SZ + + MAX_ALGO_SZ, &pkiMsg, &idx)) != 0) { + return ret; + } - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_SEQ_PEEK, inSz); +#endif - /* remove EncryptedData and version */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + /* read past ContentInfo, verify type is encrypted-data */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - /* get version, check later */ - haveAttribs = 0; - if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; - /* remove EncryptedContentInfo */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (ret == 0 && contentType != ENCRYPTED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type EncryptedData"); + ret = PKCS7_OID_E; + } - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < idx) { + ret = BUFFER_E; + break; + } + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + idx, + pkcs7->stream->length - idx); + pkcs7->stream->length -= idx; + } + else { + pkcs7->stream->totalRd += idx - tmpIdx; + tmpIdx = idx; + } +#endif + pkcs7->state = WC_PKCS7_STAGE2; + /* end of stage 1 */ - if (GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, pkiMsgSz) < 0) - return ASN_PARSE_E; + case WC_PKCS7_STAGE2: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + MAX_LENGTH_SZ + MAX_SEQ_SZ + ASN_TAG_SZ, &pkiMsg, + &idx)) != 0) { + return ret; + } - expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID); - if (expBlockSz < 0) - return expBlockSz; + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, inSz); +#endif + if (ret == 0 && pkiMsg[idx++] != (ASN_CONSTRUCTED | + ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; - /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ - if (pkiMsg[idx++] != ASN_OCTET_STRING) - return ASN_PARSE_E; + if (ret == 0 && GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + /* remove EncryptedData and version */ + if (ret == 0 && GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - if (length != expBlockSz) { - WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); - return ASN_PARSE_E; - } + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < idx) { + ret = BUFFER_E; + break; + } + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + idx, + pkcs7->stream->length - idx); + pkcs7->stream->length -= idx; + } + else { + pkcs7->stream->totalRd += idx - tmpIdx; + tmpIdx = idx; + } +#endif + pkcs7->state = WC_PKCS7_STAGE3; + /* end of stage 2 */ - XMEMCPY(tmpIv, &pkiMsg[idx], length); - idx += length; + case WC_PKCS7_STAGE3: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + MAX_VERSION_SZ + MAX_SEQ_SZ + MAX_ALGO_SZ * 2, + &pkiMsg, &idx)) != 0) { + return ret; + } - /* read encryptedContent, cont[0] */ - if (pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) - return ASN_PARSE_E; + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, inSz); +#endif + /* get version, check later */ + haveAttribs = 0; + if (ret == 0 && GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - if (GetLength(pkiMsg, &idx, &encryptedContentSz, pkiMsgSz) <= 0) - return ASN_PARSE_E; + /* remove EncryptedContentInfo */ + if (ret == 0 && GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - encryptedContent = (byte*)XMALLOC(encryptedContentSz, pkcs7->heap, - DYNAMIC_TYPE_PKCS7); - if (encryptedContent == NULL) - return MEMORY_E; + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; - XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); - idx += encryptedContentSz; + if (ret == 0 && (ret = GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, + pkiMsgSz)) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && (expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID)) < 0) + ret = expBlockSz; - /* decrypt encryptedContent */ - ret = wc_PKCS7_DecryptContent(encOID, pkcs7->encryptionKey, + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + /* store expBlockSz for later */ + pkcs7->stream->varOne = expBlockSz; + pkcs7->stream->varTwo = encOID; + + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < idx) { + ret = BUFFER_E; + break; + } + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + idx, + pkcs7->stream->length - idx); + pkcs7->stream->length -= idx; + } + else { + pkcs7->stream->totalRd += idx - tmpIdx; + tmpIdx = idx; + } + + /* store version for later */ + pkcs7->stream->vers = version; +#endif + pkcs7->state = WC_PKCS7_STAGE4; + /* end of stage 3 */ + + /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ + case WC_PKCS7_STAGE4: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + ASN_TAG_SZ + MAX_LENGTH_SZ, &pkiMsg, &idx)) != 0) { + return ret; + } + + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, inSz); + + /* restore saved variables */ + expBlockSz = pkcs7->stream->varOne; +#endif + if (ret == 0 && pkiMsg[idx++] != ASN_OCTET_STRING) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && length != expBlockSz) { + WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); + ret = ASN_PARSE_E; + } + + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + /* next chunk of data expected should have the IV */ + pkcs7->stream->expected = length; + + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < idx) { + ret = BUFFER_E; + break; + } + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + idx, + pkcs7->stream->length - idx); + pkcs7->stream->length -= idx; + } + else { + pkcs7->stream->totalRd += idx - tmpIdx; + tmpIdx = idx; + } +#endif + pkcs7->state = WC_PKCS7_STAGE5; + /* end of stage 4 */ + + case WC_PKCS7_STAGE5: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected + ASN_TAG_SZ + + MAX_LENGTH_SZ, &pkiMsg, &idx)) != 0) { + return ret; + } + + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, inSz); + + /* restore saved variables */ + expBlockSz = pkcs7->stream->varOne; + + /* use IV buffer from stream structure */ + tmpIv = pkcs7->stream->tmpIv; +#endif + XMEMCPY(tmpIv, &pkiMsg[idx], length); + idx += length; + /* read encryptedContent, cont[0] */ + if (ret == 0 && pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(pkiMsg, &idx, &encryptedContentSz, + pkiMsgSz) <= 0) + ret = ASN_PARSE_E; + +#ifndef NO_PKCS7_STREAM + /* next chunk of data should contain encrypted content */ + pkcs7->stream->varThree = encryptedContentSz; + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < idx) { + ret = BUFFER_E; + break; + } + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + idx, + pkcs7->stream->length - idx); + pkcs7->stream->length -= idx; + } + else { + pkcs7->stream->totalRd += idx - tmpIdx; + tmpIdx = idx; + } + + if (pkcs7->stream->totalRd + encryptedContentSz < pkiMsgSz) { + pkcs7->stream->hasAtrib = 1; + } + + pkcs7->stream->expected = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; + +#endif + pkcs7->state = WC_PKCS7_STAGE6; + /* end of stage 5 */ + + case WC_PKCS7_STAGE6: +#ifndef NO_PKCS7_STREAM + //@TODO + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + return ret; + } + + pkiMsgSz = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, inSz); + + /* restore saved variables */ + expBlockSz = pkcs7->stream->varOne; + encOID = pkcs7->stream->varTwo; + encryptedContentSz = pkcs7->stream->varThree; + version = pkcs7->stream->vers; + tmpIv = pkcs7->stream->tmpIv; +#endif + if (ret == 0 && (encryptedContent = (byte*)XMALLOC( + encryptedContentSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7)) == NULL) + ret = MEMORY_E; + + if (ret == 0) { + XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + idx += encryptedContentSz; + } + + /* decrypt encryptedContent */ + ret = wc_PKCS7_DecryptContent(encOID, pkcs7->encryptionKey, pkcs7->encryptionKeySz, tmpIv, expBlockSz, NULL, 0, NULL, 0, encryptedContent, encryptedContentSz, encryptedContent); - if (ret != 0) { - XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - return ret; - } + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } - padLen = encryptedContent[encryptedContentSz-1]; + if (ret == 0) { + padLen = encryptedContent[encryptedContentSz-1]; - /* copy plaintext to output */ - XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + /* copy plaintext to output */ + XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); - /* get implicit[1] unprotected attributes, optional */ - pkcs7->decodedAttrib = NULL; - if (idx < pkiMsgSz) { + /* get implicit[1] unprotected attributes, optional */ + pkcs7->decodedAttrib = NULL; +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream->hasAtrib) { +#else + if (idx < pkiMsgSz) { +#endif + haveAttribs = 1; - haveAttribs = 1; - - ret = wc_PKCS7_DecodeUnprotectedAttributes(pkcs7, pkiMsg, + ret = wc_PKCS7_DecodeUnprotectedAttributes(pkcs7, pkiMsg, pkiMsgSz, &idx); - if (ret != 0) { - ForceZero(encryptedContent, encryptedContentSz); - XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - return ASN_PARSE_E; - } + if (ret != 0) { + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ret = ASN_PARSE_E; + } + } + } + + if (ret == 0) { + /* go back and check the version now that attribs have been processed */ + if ((haveAttribs == 0 && version != 0) || + (haveAttribs == 1 && version != 2) ) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + WOLFSSL_MSG("Wrong PKCS#7 EncryptedData version"); + return ASN_VERSION_E; + } + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ret = encryptedContentSz - padLen; + } + + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + wc_PKCS7_ResetStream(pkcs7); +#endif + pkcs7->state = WC_PKCS7_START; + break; + + default: + WOLFSSL_MSG("Error in unknown PKCS#7 state"); + return BAD_STATE_E; } - /* go back and check the version now that attribs have been processed */ - if ((haveAttribs == 0 && version != 0) || - (haveAttribs == 1 && version != 2) ) { - XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - WOLFSSL_MSG("Wrong PKCS#7 EncryptedData version"); - return ASN_VERSION_E; + if (ret != 0) { + /* restart in error case */ + wc_PKCS7_ResetStream(pkcs7); + pkcs7->state = WC_PKCS7_START; } - - ForceZero(encryptedContent, encryptedContentSz); - XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - - return encryptedContentSz - padLen; + return ret; } #endif /* NO_PKCS7_ENCRYPTED_DATA */ @@ -8378,10 +8750,8 @@ int wc_PKCS7_DecodeCompressedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; - if (contentType != COMPRESSED_DATA) { - printf("ContentInfo not of type CompressedData"); + if (contentType != COMPRESSED_DATA) return ASN_PARSE_E; - } /* get ContentInfo content EXPLICIT SEQUENCE */ if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5e5a653ae..1d2a1b1da 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -20300,6 +20300,25 @@ int pkcs7encrypted_test(void) } /* decode encryptedData */ +#ifndef NO_PKCS7_STREAM + { /* test reading byte by byte */ + int z; + for (z = 0; z < encryptedSz; z++) { + decodedSz = wc_PKCS7_DecodeEncryptedData(pkcs7, encrypted + z, 1, + decoded, sizeof(decoded)); + if (decodedSz <= 0 && decodedSz != WC_PKCS7_WANT_READ_E) { + printf("unexpected error %d\n", decodedSz); + return -9402; + } + } + /* test decode result */ + if (XMEMCMP(decoded, data, sizeof(data)) != 0) { + printf("stream read failed\n"); + wc_PKCS7_Free(pkcs7); + return -9403; + } + } +#endif decodedSz = wc_PKCS7_DecodeEncryptedData(pkcs7, encrypted, encryptedSz, decoded, sizeof(decoded)); if (decodedSz <= 0){ diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 2c7064a1a..19ee854e6 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -303,6 +303,7 @@ enum Misc_ASN { HEADER_ENCRYPTED_KEY_SIZE = 0, #endif TRAILING_ZERO = 1, /* Used for size of zero pad */ + ASN_TAG_SZ = 1, /* single byte ASN.1 tag */ MIN_VERSION_SZ = 3, /* Min bytes needed for GetMyVersion */ #if defined(OPENSSL_ALL) || defined(WOLFSSL_MYSQL_COMPATIBLE) || \ defined(WOLFSSL_NGINX) || defined(WOLFSSL_HAPROXY) || \ diff --git a/wolfssl/wolfcrypt/error-crypt.h b/wolfssl/wolfcrypt/error-crypt.h index d651dc0ba..2581a30b5 100644 --- a/wolfssl/wolfcrypt/error-crypt.h +++ b/wolfssl/wolfcrypt/error-crypt.h @@ -221,8 +221,9 @@ enum { ZLIB_DECOMPRESS_ERROR = -268, /* zlib decompression error */ PKCS7_NO_SIGNER_E = -269, /* No signer in PKCS#7 signed data msg */ + WC_PKCS7_WANT_READ_E= -270, /* PKCS7 operations wants more input */ - WC_LAST_E = -269, /* Update this to indicate last error */ + WC_LAST_E = -270, /* Update this to indicate last error */ MIN_CODE_E = -300 /* errors -101 - -299 */ /* add new companion error id strings for any new error codes diff --git a/wolfssl/wolfcrypt/pkcs7.h b/wolfssl/wolfcrypt/pkcs7.h index fdadba9d9..8999c4455 100644 --- a/wolfssl/wolfcrypt/pkcs7.h +++ b/wolfssl/wolfcrypt/pkcs7.h @@ -86,6 +86,15 @@ enum PKCS7_TYPES { AUTH_ENVELOPED_DATA = 692 /* 1.2.840.113549.1.9.16.1.23, RFC 5083 */ }; +enum PKCS7_STATE { + WC_PKCS7_START = 0, + WC_PKCS7_STAGE2, + WC_PKCS7_STAGE3, + WC_PKCS7_STAGE4, + WC_PKCS7_STAGE5, + WC_PKCS7_STAGE6 +}; + enum Pkcs7_Misc { PKCS7_NONCE_SZ = 16, MAX_ENCRYPTED_KEY_SZ = 512, /* max enc. key size, RSA <= 4096 */ @@ -131,6 +140,7 @@ typedef struct PKCS7DecodedAttrib { word32 valueSz; } PKCS7DecodedAttrib; +typedef struct PKCS7State PKCS7State; typedef struct Pkcs7Cert Pkcs7Cert; typedef struct Pkcs7EncodedRecip Pkcs7EncodedRecip; typedef struct PKCS7 PKCS7; @@ -222,6 +232,11 @@ typedef struct PKCS7 { PKCS7Attrib* unauthAttribs; /* unauthenticated attribs */ word32 unauthAttribsSz; +#ifndef NO_PKCS7_STREAM + PKCS7State* stream; +#endif + enum PKCS7_STATE state; + /* !! NEW DATA MEMBERS MUST BE ADDED AT END !! */ } PKCS7;