diff options
Diffstat (limited to 'FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c')
-rw-r--r-- | FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c | 12701 |
1 files changed, 11683 insertions, 1018 deletions
diff --git a/FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c b/FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c index 2f66ea216..e420cad37 100644 --- a/FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c +++ b/FreeRTOS-Plus/Source/WolfSSL/wolfcrypt/src/pkcs7.c @@ -1,8 +1,8 @@ /* pkcs7.c * - * Copyright (C) 2006-2015 wolfSSL Inc. + * Copyright (C) 2006-2020 wolfSSL Inc. * - * This file is part of wolfSSL. (formerly known as CyaSSL) + * This file is part of wolfSSL. * * wolfSSL is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -16,9 +16,10 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA */ + #ifdef HAVE_CONFIG_H #include <config.h> #endif @@ -30,45 +31,512 @@ #include <wolfssl/wolfcrypt/pkcs7.h> #include <wolfssl/wolfcrypt/error-crypt.h> #include <wolfssl/wolfcrypt/logging.h> +#include <wolfssl/wolfcrypt/hash.h> +#ifndef NO_RSA + #include <wolfssl/wolfcrypt/rsa.h> +#endif +#ifdef HAVE_ECC + #include <wolfssl/wolfcrypt/ecc.h> +#endif +#ifdef HAVE_LIBZ + #include <wolfssl/wolfcrypt/compress.h> +#endif +#ifndef NO_PWDBASED + #include <wolfssl/wolfcrypt/pwdbased.h> +#endif #ifdef NO_INLINE #include <wolfssl/wolfcrypt/misc.h> #else + #define WOLFSSL_MISC_INCLUDED #include <wolfcrypt/src/misc.c> #endif -#ifndef WOLFSSL_HAVE_MIN -#define WOLFSSL_HAVE_MIN +/* direction for processing, encoding or decoding */ +typedef enum { + WC_PKCS7_ENCODE, + WC_PKCS7_DECODE +} pkcs7Direction; + +#define NO_USER_CHECK 0 + +/* holds information about the signers */ +struct PKCS7SignerInfo { + int version; + byte *sid; + word32 sidSz; +}; + + +#ifndef NO_PKCS7_STREAM + +#define MAX_PKCS7_STREAM_BUFFER 256 +struct PKCS7State { + byte* tmpCert; + byte* bufferPt; + byte* key; + byte* nonce; /* stored nonce */ + byte* aad; /* additional data for AEAD algos */ + byte* tag; /* tag data for AEAD algos */ + byte* content; + byte* buffer; /* main internal read buffer */ + + /* stack variables to store for when returning */ + word32 varOne; + int varTwo; + int 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 bufferSz; /* size of internal buffer */ + word32 expected; /* next amount of data expected, if needed */ + word32 totalRd; /* total amount of bytes read */ + word32 nonceSz; /* size of nonce stored */ + word32 aadSz; /* size of additional AEAD data */ + word32 tagSz; /* size of tag for AEAD */ + word32 contentSz; + byte tmpIv[MAX_CONTENT_IV_SIZE]; /* store IV if needed */ +#ifdef WC_PKCS7_STREAM_DEBUG + word32 peakUsed; /* most bytes used for struct at any one time */ + word32 peakRead; /* most bytes used by read buffer */ +#endif + byte multi:1; /* flag for if content is in multiple parts */ + byte flagOne:1; + byte detached:1; /* flag to indicate detached signature is present */ +}; + - static INLINE word32 min(word32 a, word32 b) - { - return a > b ? b : a; +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)); +#ifdef WC_PKCS7_STREAM_DEBUG + printf("\nCreating new PKCS#7 stream %p\n", pkcs7->stream); +#endif + return 0; +} + + +static void wc_PKCS7_ResetStream(PKCS7* pkcs7) +{ + if (pkcs7 != NULL && pkcs7->stream != NULL) { +#ifdef WC_PKCS7_STREAM_DEBUG + /* collect final data point in case more was read right before reset */ + if (pkcs7->stream->length > pkcs7->stream->peakRead) { + pkcs7->stream->peakRead = pkcs7->stream->length; + } + if (pkcs7->stream->bufferSz + pkcs7->stream->aadSz + + pkcs7->stream->nonceSz + pkcs7->stream->tagSz > + pkcs7->stream->peakUsed) { + pkcs7->stream->peakUsed = pkcs7->stream->bufferSz + + pkcs7->stream->aadSz + pkcs7->stream->nonceSz + + pkcs7->stream->tagSz; + } + + /* print out debugging statistics */ + if (pkcs7->stream->peakUsed > 0 || pkcs7->stream->peakRead > 0) { + printf("PKCS#7 STREAM:\n\tPeak heap used by struct = %d" + "\n\tPeak read buffer bytes = %d" + "\n\tTotal bytes read = %d" + "\n", + pkcs7->stream->peakUsed, pkcs7->stream->peakRead, + pkcs7->stream->totalRd); + } + printf("PKCS#7 stream reset : Address [%p]\n", pkcs7->stream); + #endif + + /* free any buffers that may be allocated */ + XFREE(pkcs7->stream->aad, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(pkcs7->stream->tag, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(pkcs7->stream->nonce, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(pkcs7->stream->buffer, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(pkcs7->stream->key, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream->aad = NULL; + pkcs7->stream->tag = NULL; + pkcs7->stream->nonce = NULL; + pkcs7->stream->buffer = NULL; + pkcs7->stream->key = NULL; + + /* reset values, note that content and tmpCert are saved */ + pkcs7->stream->maxLen = 0; + pkcs7->stream->length = 0; + pkcs7->stream->idx = 0; + pkcs7->stream->expected = 0; + pkcs7->stream->totalRd = 0; + pkcs7->stream->bufferSz = 0; + + pkcs7->stream->multi = 0; + pkcs7->stream->flagOne = 0; + pkcs7->stream->detached = 0; + pkcs7->stream->varOne = 0; + pkcs7->stream->varTwo = 0; + pkcs7->stream->varThree = 0; + } +} + + +static void wc_PKCS7_FreeStream(PKCS7* pkcs7) +{ + if (pkcs7 != NULL && pkcs7->stream != NULL) { + wc_PKCS7_ResetStream(pkcs7); + + XFREE(pkcs7->stream->content, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(pkcs7->stream->tmpCert, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream->content = NULL; + pkcs7->stream->tmpCert = NULL; + + XFREE(pkcs7->stream, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream = NULL; + } +} + + +/* used to increase the max size for internal buffer + * returns 0 on success */ +static int wc_PKCS7_GrowStream(PKCS7* pkcs7, word32 newSz) +{ + byte* pt; + + pt = (byte*)XMALLOC(newSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pt == NULL) { + return MEMORY_E; + } + XMEMCPY(pt, pkcs7->stream->buffer, pkcs7->stream->bufferSz); + +#ifdef WC_PKCS7_STREAM_DEBUG + printf("PKCS7 increasing internal stream buffer %d -> %d\n", + pkcs7->stream->bufferSz, newSz); +#endif + pkcs7->stream->bufferSz = newSz; + XFREE(pkcs7->stream->buffer, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream->buffer = pt; + return 0; +} + + +/* pt gets set to the buffer that is holding data in the case that stream struct + * is used. + * + * Sets idx to be the current offset into "pt" buffer + * returns 0 on success + */ +static int wc_PKCS7_AddDataToStream(PKCS7* pkcs7, byte* in, word32 inSz, + word32 expected, byte** pt, word32* idx) +{ + word32 rdSz = pkcs7->stream->idx; + + /* If the input size minus current index into input buffer is greater than + * the expected size then use the input buffer. If data is already stored + * in stream buffer or if there is not enough input data available then use + * the stream buffer. */ + if (inSz - rdSz >= expected && pkcs7->stream->length == 0) { + /* storing input buffer is not needed */ + *pt = in; /* reset in case previously used internal buffer */ + *idx = rdSz; + return 0; + } + + /* is there enough stored in buffer already? */ + if (pkcs7->stream->length >= expected) { + *idx = 0; /* start reading from beginning of stream buffer */ + *pt = pkcs7->stream->buffer; + return 0; + } + + /* check if all data has been read from input */ + if (rdSz >= inSz) { + /* no more input to read, reset input index and request more data */ + pkcs7->stream->idx = 0; + return WC_PKCS7_WANT_READ_E; + } + + /* try to store input data into stream buffer */ + if (inSz - rdSz > 0 && pkcs7->stream->length < expected) { + int len = min(inSz - rdSz, expected - pkcs7->stream->length); + + /* sanity check that the input buffer is not internal buffer */ + if (in == pkcs7->stream->buffer) { + return WC_PKCS7_WANT_READ_E; + } + + /* check if internal buffer size needs to be increased */ + if (len + pkcs7->stream->length > pkcs7->stream->bufferSz) { + int ret = wc_PKCS7_GrowStream(pkcs7, expected); + if (ret < 0) { + return ret; + } + } + XMEMCPY(pkcs7->stream->buffer + pkcs7->stream->length, in + rdSz, len); + pkcs7->stream->length += len; + pkcs7->stream->idx += len; + pkcs7->stream->totalRd += len; + } + +#ifdef WC_PKCS7_STREAM_DEBUG + /* collects memory usage for debugging */ + if (pkcs7->stream->length > pkcs7->stream->peakRead) { + pkcs7->stream->peakRead = pkcs7->stream->length; } + if (pkcs7->stream->bufferSz + pkcs7->stream->aadSz + pkcs7->stream->nonceSz + + pkcs7->stream->tagSz > pkcs7->stream->peakUsed) { + pkcs7->stream->peakUsed = pkcs7->stream->bufferSz + + pkcs7->stream->aadSz + pkcs7->stream->nonceSz + pkcs7->stream->tagSz; + } +#endif + + /* if not enough data was read in then request more */ + if (pkcs7->stream->length < expected) { + pkcs7->stream->idx = 0; + return WC_PKCS7_WANT_READ_E; + } + + /* adjust pointer to read from stored buffer */ + *idx = 0; + *pt = pkcs7->stream->buffer; + return 0; +} + -#endif /* WOLFSSL_HAVE_MIN */ +/* 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 long wc_PKCS7_GetMaxStream(PKCS7* pkcs7, byte flag, byte* in, + word32 defSz) +{ + /* check there is a buffer to read from */ + if (pkcs7) { + int length = 0, ret; + word32 idx = 0, maxIdx; + byte* pt; + + if (flag != PKCS7_DEFAULT_PEEK) { + if (pkcs7->stream->length > 0) { + length = pkcs7->stream->length; + pt = pkcs7->stream->buffer; + } + else { + length = defSz; + pt = in; + } + maxIdx = (word32)length; + + if (length < MAX_SEQ_SZ) { + WOLFSSL_MSG("PKCS7 Error not enough data for SEQ peek\n"); + return 0; + } + if (flag == PKCS7_SEQ_PEEK) { + if ((ret = GetSequence_ex(pt, &idx, &length, maxIdx, + NO_USER_CHECK)) < 0) { + return ret; + } + + #ifdef ASN_BER_TO_DER + if (length == 0 && ret == 0) { + idx = 0; + if ((ret = wc_BerToDer(pt, defSz, NULL, + (word32*)&length)) != LENGTH_ONLY_E) { + return ret; + } + } + #endif /* ASN_BER_TO_DER */ + pkcs7->stream->maxLen = length + idx; + } + } + + if (pkcs7->stream->maxLen == 0) { + pkcs7->stream->maxLen = defSz; + } + + return pkcs7->stream->maxLen; + } + + return defSz; +} + + +/* setter function for stored variables */ +static void wc_PKCS7_StreamStoreVar(PKCS7* pkcs7, word32 var1, int var2, + int var3) +{ + if (pkcs7 != NULL && pkcs7->stream != NULL) { + pkcs7->stream->varOne = var1; + pkcs7->stream->varTwo = var2; + pkcs7->stream->varThree = var3; + } +} + +/* getter function for stored variables */ +static void wc_PKCS7_StreamGetVar(PKCS7* pkcs7, word32* var1, int* var2, + int* var3) +{ + if (pkcs7 != NULL && pkcs7->stream != NULL) { + if (var1 != NULL) *var1 = pkcs7->stream->varOne; + if (var2 != NULL) *var2 = pkcs7->stream->varTwo; + if (var3 != NULL) *var3 = pkcs7->stream->varThree; + } +} + + +/* common update of index and total read after section complete + * returns 0 on success */ +static int wc_PKCS7_StreamEndCase(PKCS7* pkcs7, word32* tmpIdx, word32* idx) +{ + int ret = 0; + + if (pkcs7->stream->length > 0) { + if (pkcs7->stream->length < *idx) { + WOLFSSL_MSG("PKCS7 read too much data from internal buffer"); + ret = BUFFER_E; + } + else { + XMEMMOVE(pkcs7->stream->buffer, pkcs7->stream->buffer + *idx, + pkcs7->stream->length - *idx); + pkcs7->stream->length -= *idx; + } + } + else { + pkcs7->stream->totalRd += *idx - *tmpIdx; + pkcs7->stream->idx = *idx; /* adjust index into input buffer */ + *tmpIdx = *idx; + } + + return ret; +} +#endif /* NO_PKCS7_STREAM */ + +#ifdef WC_PKCS7_STREAM_DEBUG +/* used to print out human readable state for debugging */ +static const char* wc_PKCS7_GetStateName(int in) +{ + switch (in) { + case WC_PKCS7_START: return "WC_PKCS7_START"; + + case WC_PKCS7_STAGE2: return "WC_PKCS7_STAGE2"; + case WC_PKCS7_STAGE3: return "WC_PKCS7_STAGE3"; + case WC_PKCS7_STAGE4: return "WC_PKCS7_STAGE4"; + case WC_PKCS7_STAGE5: return "WC_PKCS7_STAGE5"; + case WC_PKCS7_STAGE6: return "WC_PKCS7_STAGE6"; + + /* parse info set */ + case WC_PKCS7_INFOSET_START: return "WC_PKCS7_INFOSET_START"; + case WC_PKCS7_INFOSET_BER: return "WC_PKCS7_INFOSET_BER"; + case WC_PKCS7_INFOSET_STAGE1: return "WC_PKCS7_INFOSET_STAGE1"; + case WC_PKCS7_INFOSET_STAGE2: return "WC_PKCS7_INFOSET_STAGE2"; + case WC_PKCS7_INFOSET_END: return "WC_PKCS7_INFOSET_END"; + + /* decode enveloped data */ + case WC_PKCS7_ENV_2: return "WC_PKCS7_ENV_2"; + case WC_PKCS7_ENV_3: return "WC_PKCS7_ENV_3"; + case WC_PKCS7_ENV_4: return "WC_PKCS7_ENV_4"; + case WC_PKCS7_ENV_5: return "WC_PKCS7_ENV_5"; + + /* decode auth enveloped */ + case WC_PKCS7_AUTHENV_2: return "WC_PKCS7_AUTHENV_2"; + case WC_PKCS7_AUTHENV_3: return "WC_PKCS7_AUTHENV_3"; + case WC_PKCS7_AUTHENV_4: return "WC_PKCS7_AUTHENV_4"; + case WC_PKCS7_AUTHENV_5: return "WC_PKCS7_AUTHENV_5"; + case WC_PKCS7_AUTHENV_6: return "WC_PKCS7_AUTHENV_6"; + case WC_PKCS7_AUTHENV_ATRB: return "WC_PKCS7_AUTHENV_ATRB"; + case WC_PKCS7_AUTHENV_ATRBEND: return "WC_PKCS7_AUTHENV_ATRBEND"; + case WC_PKCS7_AUTHENV_7: return "WC_PKCS7_AUTHENV_7"; + + /* decryption state types */ + case WC_PKCS7_DECRYPT_KTRI: return "WC_PKCS7_DECRYPT_KTRI"; + case WC_PKCS7_DECRYPT_KTRI_2: return "WC_PKCS7_DECRYPT_KTRI_2"; + case WC_PKCS7_DECRYPT_KTRI_3: return "WC_PKCS7_DECRYPT_KTRI_3"; + + case WC_PKCS7_DECRYPT_KARI: return "WC_PKCS7_DECRYPT_KARI"; + case WC_PKCS7_DECRYPT_KEKRI: return "WC_PKCS7_DECRYPT_KEKRI"; + case WC_PKCS7_DECRYPT_PWRI: return "WC_PKCS7_DECRYPT_PWRI"; + case WC_PKCS7_DECRYPT_ORI: return "WC_PKCS7_DECRYPT_ORI"; + case WC_PKCS7_DECRYPT_DONE: return "WC_PKCS7_DECRYPT_DONE"; + + case WC_PKCS7_VERIFY_STAGE2: return "WC_PKCS7_VERIFY_STAGE2"; + case WC_PKCS7_VERIFY_STAGE3: return "WC_PKCS7_VERIFY_STAGE3"; + case WC_PKCS7_VERIFY_STAGE4: return "WC_PKCS7_VERIFY_STAGE4"; + case WC_PKCS7_VERIFY_STAGE5: return "WC_PKCS7_VERIFY_STAGE5"; + case WC_PKCS7_VERIFY_STAGE6: return "WC_PKCS7_VERIFY_STAGE6"; + + default: + return "Unknown state"; + } +} +#endif + +/* Used to change the PKCS7 state. Having state change as a function allows + * for easier debugging */ +static void wc_PKCS7_ChangeState(PKCS7* pkcs7, int newState) +{ +#ifdef WC_PKCS7_STREAM_DEBUG + printf("\tChanging from state [%02d] %s to [%02d] %s\n", + pkcs7->state, wc_PKCS7_GetStateName(pkcs7->state), + newState, wc_PKCS7_GetStateName(newState)); +#endif + pkcs7->state = newState; +} + +#define MAX_PKCS7_DIGEST_SZ (MAX_SEQ_SZ + MAX_ALGO_SZ + \ + MAX_OCTET_STR_SZ + WC_MAX_DIGEST_SIZE) /* placed ASN.1 contentType OID into *output, return idx on success, * 0 upon failure */ -WOLFSSL_LOCAL int wc_SetContentType(int pkcs7TypeOID, byte* output) +static int wc_SetContentType(int pkcs7TypeOID, byte* output, word32 outputSz) { /* PKCS#7 content types, RFC 2315, section 14 */ - static const byte pkcs7[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte pkcs7[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07 }; - static const byte data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; - static const byte signedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte signedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02}; - static const byte envelopedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte envelopedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x03 }; - static const byte signedAndEnveloped[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte authEnvelopedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + 0x0D, 0x01, 0x09, 0x10, 0x01, 0x17}; + const byte signedAndEnveloped[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x04 }; - static const byte digestedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, + const byte digestedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x05 }; - static const byte encryptedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, +#ifndef NO_PKCS7_ENCRYPTED_DATA + const byte encryptedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; +#endif + /* FirmwarePkgData (1.2.840.113549.1.9.16.1.16), RFC 4108 */ + const byte firmwarePkgData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x09, 0x10, 0x01, 0x10 }; +#if defined(HAVE_LIBZ) && !defined(NO_PKCS7_COMPRESSED_DATA) + /* id-ct-compressedData (1.2.840.113549.1.9.16.1.9), RFC 3274 */ + const byte compressedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x09, 0x10, 0x01, 0x09 }; +#endif - int idSz; - int typeSz = 0, idx = 0; +#if !defined(NO_PWDBASED) && !defined(NO_SHA) + const byte pwriKek[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x09, 0x10, 0x03, 0x09 }; + const byte pbkdf2[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, + 0x01, 0x05, 0x0C }; +#endif + + int idSz, idx = 0; + word32 typeSz = 0; const byte* typeName = 0; byte ID_Length[MAX_LENGTH_SZ]; @@ -87,12 +555,17 @@ WOLFSSL_LOCAL int wc_SetContentType(int pkcs7TypeOID, byte* output) typeSz = sizeof(signedData); typeName = signedData; break; - + case ENVELOPED_DATA: typeSz = sizeof(envelopedData); typeName = envelopedData; break; + case AUTH_ENVELOPED_DATA: + typeSz = sizeof(authEnvelopedData); + typeName = authEnvelopedData; + break; + case SIGNED_AND_ENVELOPED_DATA: typeSz = sizeof(signedAndEnveloped); typeName = signedAndEnveloped; @@ -103,16 +576,45 @@ WOLFSSL_LOCAL int wc_SetContentType(int pkcs7TypeOID, byte* output) typeName = digestedData; break; +#ifndef NO_PKCS7_ENCRYPTED_DATA case ENCRYPTED_DATA: typeSz = sizeof(encryptedData); typeName = encryptedData; break; +#endif +#if defined(HAVE_LIBZ) && !defined(NO_PKCS7_COMPRESSED_DATA) + case COMPRESSED_DATA: + typeSz = sizeof(compressedData); + typeName = compressedData; + break; +#endif + case FIRMWARE_PKG_DATA: + typeSz = sizeof(firmwarePkgData); + typeName = firmwarePkgData; + break; + +#if !defined(NO_PWDBASED) && !defined(NO_SHA) + case PWRI_KEK_WRAP: + typeSz = sizeof(pwriKek); + typeName = pwriKek; + break; + + case PBKDF2_OID: + typeSz = sizeof(pbkdf2); + typeName = pbkdf2; + break; +#endif default: WOLFSSL_MSG("Unknown PKCS#7 Type"); return 0; }; + if (outputSz < (MAX_LENGTH_SZ + 1 + typeSz)) { + WOLFSSL_MSG("CMS content type buffer too small"); + return BAD_FUNC_ARG; + } + idSz = SetLength(typeSz, ID_Length); output[idx++] = ASN_OBJECT_ID; XMEMCPY(output + idx, ID_Length, idSz); @@ -121,82 +623,384 @@ WOLFSSL_LOCAL int wc_SetContentType(int pkcs7TypeOID, byte* output) idx += typeSz; return idx; - } /* get ASN.1 contentType OID sum, return 0 on success, <0 on failure */ -int wc_GetContentType(const byte* input, word32* inOutIdx, word32* oid, - word32 maxIdx) +static int wc_GetContentType(const byte* input, word32* inOutIdx, word32* oid, + word32 maxIdx) { - int length; - word32 i = *inOutIdx; - byte b; - *oid = 0; - WOLFSSL_ENTER("wc_GetContentType"); + if (GetObjectId(input, inOutIdx, oid, oidIgnoreType, maxIdx) < 0) + return ASN_PARSE_E; - b = input[i++]; - if (b != ASN_OBJECT_ID) - return ASN_OBJECT_ID_E; + return 0; +} - if (GetLength(input, &i, &length, maxIdx) < 0) - return ASN_PARSE_E; - while(length--) { - *oid += input[i]; - i++; +/* return block size for algorithm represented by oid, or <0 on error */ +static int wc_PKCS7_GetOIDBlockSize(int oid) +{ + int blockSz; + + switch (oid) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128CBCb: + case AES128GCMb: + case AES128CCMb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192CBCb: + case AES192GCMb: + case AES192CCMb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256CBCb: + case AES256GCMb: + case AES256CCMb: + #endif + blockSz = AES_BLOCK_SIZE; + break; +#endif +#ifndef NO_DES3 + case DESb: + case DES3b: + blockSz = DES_BLOCK_SIZE; + break; +#endif + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + + return blockSz; +} + + +/* get key size for algorithm represented by oid, or <0 on error */ +static int wc_PKCS7_GetOIDKeySize(int oid) +{ + int blockKeySz; + + switch (oid) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128CBCb: + case AES128GCMb: + case AES128CCMb: + case AES128_WRAP: + blockKeySz = 16; + break; + #endif + #ifdef WOLFSSL_AES_192 + case AES192CBCb: + case AES192GCMb: + case AES192CCMb: + case AES192_WRAP: + blockKeySz = 24; + break; + #endif + #ifdef WOLFSSL_AES_256 + case AES256CBCb: + case AES256GCMb: + case AES256CCMb: + case AES256_WRAP: + blockKeySz = 32; + break; + #endif +#endif +#ifndef NO_DES3 + case DESb: + blockKeySz = DES_KEYLEN; + break; + + case DES3b: + blockKeySz = DES3_KEYLEN; + break; +#endif + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + + return blockKeySz; +} + + +PKCS7* wc_PKCS7_New(void* heap, int devId) +{ + PKCS7* pkcs7 = (PKCS7*)XMALLOC(sizeof(PKCS7), heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7) { + XMEMSET(pkcs7, 0, sizeof(PKCS7)); + if (wc_PKCS7_Init(pkcs7, heap, devId) == 0) { + pkcs7->isDynamic = 1; + } + else { + XFREE(pkcs7, heap, DYNAMIC_TYPE_PKCS7); + pkcs7 = NULL; + } } + return pkcs7; +} + +/* This is to initialize a PKCS7 structure. It sets all values to 0 and can be + * used to set the heap hint. + * + * pkcs7 PKCS7 structure to initialize + * heap memory heap hint for PKCS7 structure to use + * devId currently not used but a place holder for async operations + * + * returns 0 on success or a negative value for failure + */ +int wc_PKCS7_Init(PKCS7* pkcs7, void* heap, int devId) +{ + word16 isDynamic; + + WOLFSSL_ENTER("wc_PKCS7_Init"); + + if (pkcs7 == NULL) { + return BAD_FUNC_ARG; + } + + isDynamic = pkcs7->isDynamic; + XMEMSET(pkcs7, 0, sizeof(PKCS7)); + pkcs7->isDynamic = isDynamic; +#ifdef WOLFSSL_HEAP_TEST + pkcs7->heap = (void*)WOLFSSL_HEAP_TEST; +#else + pkcs7->heap = heap; +#endif + pkcs7->devId = devId; + + return 0; +} + + +/* Certificate structure holding der pointer, size, and pointer to next + * Pkcs7Cert struct. Used when creating SignedData types with multiple + * certificates. */ +struct Pkcs7Cert { + byte* der; + word32 derSz; + Pkcs7Cert* next; +}; + + +/* Linked list of ASN.1 encoded RecipientInfos */ +struct Pkcs7EncodedRecip { + byte recip[MAX_RECIP_SZ]; + word32 recipSz; + int recipType; + int recipVersion; + Pkcs7EncodedRecip* next; +}; + + +/* free all members of Pkcs7Cert linked list */ +static void wc_PKCS7_FreeCertSet(PKCS7* pkcs7) +{ + Pkcs7Cert* curr = NULL; + Pkcs7Cert* next = NULL; + + if (pkcs7 == NULL) + return; + + curr = pkcs7->certList; + pkcs7->certList = NULL; + + while (curr != NULL) { + next = curr->next; + curr->next = NULL; + XFREE(curr, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + curr = next; + } + + return; +} - *inOutIdx = i; + +/* Get total size of all recipients in recipient list. + * + * Returns total size of recipients, or negative upon error */ +static int wc_PKCS7_GetRecipientListSize(PKCS7* pkcs7) +{ + int totalSz = 0; + Pkcs7EncodedRecip* tmp = NULL; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + tmp = pkcs7->recipList; + + while (tmp != NULL) { + totalSz += tmp->recipSz; + tmp = tmp->next; + } + + return totalSz; +} + + +/* free all members of Pkcs7EncodedRecip linked list */ +static void wc_PKCS7_FreeEncodedRecipientSet(PKCS7* pkcs7) +{ + Pkcs7EncodedRecip* curr = NULL; + Pkcs7EncodedRecip* next = NULL; + + if (pkcs7 == NULL) + return; + + curr = pkcs7->recipList; + pkcs7->recipList = NULL; + + while (curr != NULL) { + next = curr->next; + curr->next = NULL; + XFREE(curr, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + curr = next; + } + + return; +} + + +/* search through RecipientInfo list for specific type. + * return 1 if ANY recipient of type specified is present, otherwise + * return 0 */ +static int wc_PKCS7_RecipientListIncludesType(PKCS7* pkcs7, int type) +{ + Pkcs7EncodedRecip* tmp = NULL; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + tmp = pkcs7->recipList; + + while (tmp != NULL) { + if (tmp->recipType == type) + return 1; + + tmp = tmp->next; + } return 0; } -/* init PKCS7 struct with recipient cert, decode into DecodedCert */ -int wc_PKCS7_InitWithCert(PKCS7* pkcs7, byte* cert, word32 certSz) +/* searches through RecipientInfo list, returns 1 if all structure + * versions are set to 0, otherwise returns 0 */ +static int wc_PKCS7_RecipientListVersionsAllZero(PKCS7* pkcs7) +{ + Pkcs7EncodedRecip* tmp = NULL; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + tmp = pkcs7->recipList; + + while (tmp != NULL) { + if (tmp->recipVersion != 0) + return 0; + + tmp = tmp->next; + } + + return 1; +} + + +/* Init PKCS7 struct with recipient cert, decode into DecodedCert + * NOTE: keeps previously set pkcs7 heap hint, devId and isDynamic */ +int wc_PKCS7_InitWithCert(PKCS7* pkcs7, byte* derCert, word32 derCertSz) { int ret = 0; + void* heap; + int devId; + Pkcs7Cert* cert; + Pkcs7Cert* lastCert; - XMEMSET(pkcs7, 0, sizeof(PKCS7)); - if (cert != NULL && certSz > 0) { + if (pkcs7 == NULL || (derCert == NULL && derCertSz != 0)) { + return BAD_FUNC_ARG; + } + + heap = pkcs7->heap; + devId = pkcs7->devId; + cert = pkcs7->certList; + ret = wc_PKCS7_Init(pkcs7, heap, devId); + if (ret != 0) + return ret; + pkcs7->certList = cert; + + if (derCert != NULL && derCertSz > 0) { #ifdef WOLFSSL_SMALL_STACK DecodedCert* dCert; - dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, - DYNAMIC_TYPE_TMP_BUFFER); + dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), pkcs7->heap, + DYNAMIC_TYPE_DCERT); if (dCert == NULL) return MEMORY_E; #else - DecodedCert stack_dCert; - DecodedCert* dCert = &stack_dCert; + DecodedCert dCert[1]; #endif - pkcs7->singleCert = cert; - pkcs7->singleCertSz = certSz; - InitDecodedCert(dCert, cert, certSz, 0); + pkcs7->singleCert = derCert; + pkcs7->singleCertSz = derCertSz; + pkcs7->cert[0] = derCert; + pkcs7->certSz[0] = derCertSz; + + /* create new Pkcs7Cert for recipient, freed during cleanup */ + cert = (Pkcs7Cert*)XMALLOC(sizeof(Pkcs7Cert), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + XMEMSET(cert, 0, sizeof(Pkcs7Cert)); + cert->der = derCert; + cert->derSz = derCertSz; + cert->next = NULL; + + /* free existing cert list if existing */ + wc_PKCS7_FreeCertSet(pkcs7); + + /* add cert to list */ + if (pkcs7->certList == NULL) { + pkcs7->certList = cert; + } else { + lastCert = pkcs7->certList; + while (lastCert->next != NULL) { + lastCert = lastCert->next; + } + lastCert->next = cert; + } + InitDecodedCert(dCert, derCert, derCertSz, pkcs7->heap); ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0); if (ret < 0) { FreeDecodedCert(dCert); #ifdef WOLFSSL_SMALL_STACK - XFREE(dCert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); #endif return ret; } XMEMCPY(pkcs7->publicKey, dCert->publicKey, dCert->pubKeySize); pkcs7->publicKeySz = dCert->pubKeySize; + pkcs7->publicKeyOID = dCert->keyOID; XMEMCPY(pkcs7->issuerHash, dCert->issuerHash, KEYID_SIZE); pkcs7->issuer = dCert->issuerRaw; pkcs7->issuerSz = dCert->issuerRawLen; XMEMCPY(pkcs7->issuerSn, dCert->serial, dCert->serialSz); pkcs7->issuerSnSz = dCert->serialSz; + XMEMCPY(pkcs7->issuerSubjKeyId, dCert->extSubjKeyId, KEYID_SIZE); + + /* default to IssuerAndSerialNumber for SignerIdentifier */ + pkcs7->sidType = CMS_ISSUER_AND_SERIAL_NUMBER; + + /* free existing recipient list if existing */ + wc_PKCS7_FreeEncodedRecipientSet(pkcs7); + FreeDecodedCert(dCert); #ifdef WOLFSSL_SMALL_STACK - XFREE(dCert, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); #endif } @@ -204,10 +1008,265 @@ int wc_PKCS7_InitWithCert(PKCS7* pkcs7, byte* cert, word32 certSz) } +/* Adds one DER-formatted certificate to the internal PKCS7/CMS certificate + * list, to be added as part of the certificates CertificateSet. Currently + * used in SignedData content type. + * + * Must be called after wc_PKCS7_Init() or wc_PKCS7_InitWithCert(). + * + * Does not represent the recipient/signer certificate, only certificates that + * are part of the certificate chain used to build and verify signer + * certificates. + * + * This API does not currently validate certificates. + * + * Returns 0 on success, negative upon error */ +int wc_PKCS7_AddCertificate(PKCS7* pkcs7, byte* derCert, word32 derCertSz) +{ + Pkcs7Cert* cert; + + if (pkcs7 == NULL || derCert == NULL || derCertSz == 0) + return BAD_FUNC_ARG; + + cert = (Pkcs7Cert*)XMALLOC(sizeof(Pkcs7Cert), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (cert == NULL) + return MEMORY_E; + + cert->der = derCert; + cert->derSz = derCertSz; + + if (pkcs7->certList == NULL) { + pkcs7->certList = cert; + } else { + cert->next = pkcs7->certList; + pkcs7->certList = cert; + } + + return 0; +} + + +/* free linked list of PKCS7DecodedAttrib structs */ +static void wc_PKCS7_FreeDecodedAttrib(PKCS7DecodedAttrib* attrib, void* heap) +{ + PKCS7DecodedAttrib* current; + + if (attrib == NULL) { + return; + } + + current = attrib; + while (current != NULL) { + PKCS7DecodedAttrib* next = current->next; + if (current->oid != NULL) { + XFREE(current->oid, heap, DYNAMIC_TYPE_PKCS7); + } + if (current->value != NULL) { + XFREE(current->value, heap, DYNAMIC_TYPE_PKCS7); + } + XFREE(current, heap, DYNAMIC_TYPE_PKCS7); + current = next; + } + + (void)heap; +} + + +/* return 0 on success */ +static int wc_PKCS7_SignerInfoNew(PKCS7* pkcs7) +{ + if (pkcs7->signerInfo != NULL) { + XFREE(pkcs7->signerInfo, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->signerInfo = NULL; + } + + pkcs7->signerInfo = (PKCS7SignerInfo*)XMALLOC(sizeof(PKCS7SignerInfo), + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->signerInfo == NULL) { + WOLFSSL_MSG("Unable to malloc memory for signer info"); + return MEMORY_E; + } + XMEMSET(pkcs7->signerInfo, 0, sizeof(PKCS7SignerInfo)); + return 0; +} + + +static void wc_PKCS7_SignerInfoFree(PKCS7* pkcs7) +{ + if (pkcs7->signerInfo != NULL) { + if (pkcs7->signerInfo->sid != NULL) { + XFREE(pkcs7->signerInfo->sid, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->signerInfo->sid = NULL; + } + XFREE(pkcs7->signerInfo, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->signerInfo = NULL; + } +} + + +/* free's any current SID and sets it to "in" + * returns 0 on success + */ +static int wc_PKCS7_SignerInfoSetSID(PKCS7* pkcs7, byte* in, int inSz) +{ + if (pkcs7 == NULL || in == NULL || inSz < 0) { + return BAD_FUNC_ARG; + } + + if (pkcs7->signerInfo->sid != NULL) { + XFREE(pkcs7->signerInfo->sid, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->signerInfo->sid = NULL; + } + pkcs7->signerInfo->sid = (byte*)XMALLOC(inSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->signerInfo->sid == NULL) { + return MEMORY_E; + } + XMEMCPY(pkcs7->signerInfo->sid, in, inSz); + pkcs7->signerInfo->sidSz = inSz; + return 0; +} + + /* releases any memory allocated by a PKCS7 initializer */ void wc_PKCS7_Free(PKCS7* pkcs7) { - (void)pkcs7; + if (pkcs7 == NULL) + return; + +#ifndef NO_PKCS7_STREAM + wc_PKCS7_FreeStream(pkcs7); +#endif + + wc_PKCS7_SignerInfoFree(pkcs7); + wc_PKCS7_FreeDecodedAttrib(pkcs7->decodedAttrib, pkcs7->heap); + wc_PKCS7_FreeCertSet(pkcs7); + +#ifdef ASN_BER_TO_DER + if (pkcs7->der != NULL) + XFREE(pkcs7->der, pkcs7->heap, DYNAMIC_TYPE_PKCS7); +#endif + if (pkcs7->contentDynamic != NULL) + XFREE(pkcs7->contentDynamic, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + if (pkcs7->cek != NULL) { + ForceZero(pkcs7->cek, pkcs7->cekSz); + XFREE(pkcs7->cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } + + pkcs7->contentTypeSz = 0; + + if (pkcs7->signature) { + XFREE(pkcs7->signature, pkcs7->heap, DYNAMIC_TYPE_SIGNATURE); + pkcs7->signature = NULL; + pkcs7->signatureSz = 0; + } + if (pkcs7->plainDigest) { + XFREE(pkcs7->plainDigest, pkcs7->heap, DYNAMIC_TYPE_DIGEST); + pkcs7->plainDigest = NULL; + pkcs7->plainDigestSz = 0; + } + if (pkcs7->pkcs7Digest) { + XFREE(pkcs7->pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_DIGEST); + pkcs7->pkcs7Digest = NULL; + pkcs7->pkcs7DigestSz = 0; + } + if (pkcs7->cachedEncryptedContent != NULL) { + XFREE(pkcs7->cachedEncryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->cachedEncryptedContent = NULL; + pkcs7->cachedEncryptedContentSz = 0; + } + + if (pkcs7->isDynamic) { + pkcs7->isDynamic = 0; + XFREE(pkcs7, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } +} + + +/* helper function for parsing through attributes and finding a specific one. + * returns PKCS7DecodedAttrib pointer on success */ +static PKCS7DecodedAttrib* findAttrib(PKCS7* pkcs7, const byte* oid, word32 oidSz) +{ + PKCS7DecodedAttrib* list; + + if (pkcs7 == NULL || oid == NULL) { + return NULL; + } + + /* search attributes for pkiStatus */ + list = pkcs7->decodedAttrib; + while (list != NULL) { + word32 sz = oidSz; + word32 idx = 0; + int length = 0; + byte tag; + + if (GetASNTag(list->oid, &idx, &tag, list->oidSz) < 0) { + return NULL; + } + if (tag != ASN_OBJECT_ID) { + WOLFSSL_MSG("Bad attribute ASN1 syntax"); + return NULL; + } + + if (GetLength(list->oid, &idx, &length, list->oidSz) < 0) { + WOLFSSL_MSG("Bad attribute length"); + return NULL; + } + + sz = (sz < (word32)length)? sz : (word32)length; + if (XMEMCMP(oid, list->oid + idx, sz) == 0) { + return list; + } + list = list->next; + } + return NULL; +} + + +/* Searches through decoded attributes and returns the value for the first one + * matching the oid passed in. Note that this value includes the leading ASN1 + * syntax. So for a printable string of "3" this would be something like + * + * 0x13, 0x01, 0x33 + * ID SIZE "3" + * + * pkcs7 structure to get value from + * oid OID value to search for with attributes + * oidSz size of oid buffer + * out buffer to hold result + * outSz size of out buffer (if out is NULL this is set to needed size and + LENGTH_ONLY_E is returned) + * + * returns size of value on success + */ +int wc_PKCS7_GetAttributeValue(PKCS7* pkcs7, const byte* oid, word32 oidSz, + byte* out, word32* outSz) +{ + PKCS7DecodedAttrib* attrib; + + if (pkcs7 == NULL || oid == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + attrib = findAttrib(pkcs7, oid, oidSz); + if (attrib == NULL) { + return ASN_PARSE_E; + } + + if (out == NULL) { + *outSz = attrib->valueSz; + return LENGTH_ONLY_E; + } + + if (*outSz < attrib->valueSz) { + return BUFFER_E; + } + + XMEMCPY(out, attrib->value, attrib->valueSz); + return attrib->valueSz; } @@ -224,6 +1283,10 @@ int wc_PKCS7_EncodeData(PKCS7* pkcs7, byte* output, word32 outputSz) word32 oidSz = (word32)sizeof(oid); int idx = 0; + if (pkcs7 == NULL || output == NULL) { + return BAD_FUNC_ARG; + } + octetStrSz = SetOctetString(pkcs7->contentSz, octetStr); seqSz = SetSequence(pkcs7->contentSz + octetStrSz + oidSz, seq); @@ -253,10 +1316,11 @@ typedef struct EncodedAttrib { typedef struct ESD { - Sha sha; - byte contentDigest[SHA_DIGEST_SIZE + 2]; /* content only + ASN.1 heading */ - byte contentAttribsDigest[SHA_DIGEST_SIZE]; - byte encContentDigest[512]; + wc_HashAlg hash; + enum wc_HashType hashType; + byte contentDigest[WC_MAX_DIGEST_SIZE + 2]; /* content only + ASN.1 heading */ + byte contentAttribsDigest[WC_MAX_DIGEST_SIZE]; + byte encContentDigest[MAX_ENCRYPTED_KEY_SZ]; byte outerSeq[MAX_SEQ_SZ]; byte outerContent[MAX_EXP_SZ]; @@ -274,20 +1338,24 @@ typedef struct ESD { byte signerInfoSet[MAX_SET_SZ]; byte signerInfoSeq[MAX_SEQ_SZ]; byte signerVersion[MAX_VERSION_SZ]; + /* issuerAndSerialNumber ...*/ byte issuerSnSeq[MAX_SEQ_SZ]; byte issuerName[MAX_SEQ_SZ]; byte issuerSn[MAX_SN_SZ]; + /* OR subjectKeyIdentifier */ + byte issuerSKIDSeq[MAX_SEQ_SZ]; + byte issuerSKID[MAX_OCTET_STR_SZ]; byte signerDigAlgoId[MAX_ALGO_SZ]; byte digEncAlgoId[MAX_ALGO_SZ]; byte signedAttribSet[MAX_SET_SZ]; - EncodedAttrib signedAttribs[6]; + EncodedAttrib signedAttribs[7]; byte signerDigest[MAX_OCTET_STR_SZ]; word32 innerOctetsSz, innerContSeqSz, contentInfoSeqSz; word32 outerSeqSz, outerContentSz, innerSeqSz, versionSz, digAlgoIdSetSz, singleDigAlgoIdSz, certsSetSz; word32 signerInfoSetSz, signerInfoSeqSz, signerVersionSz, - issuerSnSeqSz, issuerNameSz, issuerSnSz, - signerDigAlgoIdSz, digEncAlgoIdSz, signerDigestSz; + issuerSnSeqSz, issuerNameSz, issuerSnSz, issuerSKIDSz, + issuerSKIDSeqSz, signerDigAlgoIdSz, digEncAlgoIdSz, signerDigestSz; word32 encContentDigestSz, signedAttribsSz, signedAttribsCount, signedAttribSetSz; } ESD; @@ -322,12 +1390,112 @@ static int EncodeAttributes(EncodedAttrib* ea, int eaSz, } -static int FlattenAttributes(byte* output, EncodedAttrib* ea, int eaSz) +typedef struct FlatAttrib { + byte* data; + word32 dataSz; +} FlatAttrib; + +/* Returns a pointer to FlatAttrib whose members are initialized to 0. +* Caller is expected to free. +*/ +static FlatAttrib* NewAttrib(void* heap) { - int i, idx; + FlatAttrib* fb = (FlatAttrib*) XMALLOC(sizeof(FlatAttrib), heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (fb != NULL) { + ForceZero(fb, sizeof(FlatAttrib)); + } + (void)heap; + return fb; +} + +/* Free FlatAttrib array and memory allocated to internal struct members */ +static void FreeAttribArray(PKCS7* pkcs7, FlatAttrib** arr, int rows) +{ + int i; + + if (arr) { + for (i = 0; i < rows; i++) { + if (arr[i]) { + if (arr[i]->data) { + ForceZero(arr[i]->data, arr[i]->dataSz); + XFREE(arr[i]->data, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + } + ForceZero(arr[i], sizeof(FlatAttrib)); + XFREE(arr[i], pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + } + } + ForceZero(arr, rows); + XFREE(arr, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + } + (void)pkcs7; +} + + +/* Sort FlatAttrib array in ascending order */ +static int SortAttribArray(FlatAttrib** arr, int rows) +{ + int i, j; + word32 minSz, minIdx; + FlatAttrib* a = NULL; + FlatAttrib* b = NULL; + FlatAttrib* tmp = NULL; + + if (arr == NULL) { + return BAD_FUNC_ARG; + } + + for (i = 0; i < rows; i++) { + a = arr[i]; + minSz = a->dataSz; + minIdx = i; + for (j = i+1; j < rows; j++) { + b = arr[j]; + if (b->dataSz < minSz) { + minSz = b->dataSz; + minIdx = j; + } + } + if (minSz < a->dataSz) { + /* swap array positions */ + tmp = arr[i]; + arr[i] = arr[minIdx]; + arr[minIdx] = tmp; + } + } + + return 0; +} + + +/* Build up array of FlatAttrib structs from EncodedAttrib ones. FlatAttrib + * holds flattened DER encoding of each attribute */ +static int FlattenEncodedAttribs(PKCS7* pkcs7, FlatAttrib** derArr, int rows, + EncodedAttrib* ea, int eaSz) +{ + int i, idx, sz; + byte* output = NULL; + FlatAttrib* fa = NULL; + + if (pkcs7 == NULL || derArr == NULL || ea == NULL) { + WOLFSSL_MSG("Invalid arguments to FlattenEncodedAttribs"); + return BAD_FUNC_ARG; + } + + if (rows != eaSz) { + WOLFSSL_MSG("DER array not large enough to hold attribute count"); + return BAD_FUNC_ARG; + } - idx = 0; for (i = 0; i < eaSz; i++) { + sz = ea[i].valueSeqSz + ea[i].oidSz + ea[i].valueSetSz + ea[i].valueSz; + + output = (byte*)XMALLOC(sz, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (output == NULL) { + return MEMORY_E; + } + + idx = 0; XMEMCPY(output + idx, ea[i].valueSeq, ea[i].valueSeqSz); idx += ea[i].valueSeqSz; XMEMCPY(output + idx, ea[i].oid, ea[i].oidSz); @@ -335,293 +1503,893 @@ static int FlattenAttributes(byte* output, EncodedAttrib* ea, int eaSz) XMEMCPY(output + idx, ea[i].valueSet, ea[i].valueSetSz); idx += ea[i].valueSetSz; XMEMCPY(output + idx, ea[i].value, ea[i].valueSz); - idx += ea[i].valueSz; + + fa = derArr[i]; + fa->data = output; + fa->dataSz = sz; } + return 0; } -/* build PKCS#7 signedData content type */ -int wc_PKCS7_EncodeSignedData(PKCS7* pkcs7, byte* output, word32 outputSz) +/* Sort and Flatten EncodedAttrib attributes into output buffer */ +static int FlattenAttributes(PKCS7* pkcs7, byte* output, EncodedAttrib* ea, + int eaSz) { - static const byte outerOid[] = - { ASN_OBJECT_ID, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, - 0x07, 0x02 }; - static const byte innerOid[] = - { ASN_OBJECT_ID, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, - 0x07, 0x01 }; + int i, idx, ret; + FlatAttrib** derArr = NULL; + FlatAttrib* fa = NULL; + + if (pkcs7 == NULL || output == NULL || ea == NULL) { + return BAD_FUNC_ARG; + } + /* create array of FlatAttrib struct pointers to hold DER attribs */ + derArr = (FlatAttrib**) XMALLOC(eaSz * sizeof(FlatAttrib*), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (derArr == NULL) { + return MEMORY_E; + } + XMEMSET(derArr, 0, eaSz * sizeof(FlatAttrib*)); + + for (i = 0; i < eaSz; i++) { + derArr[i] = NewAttrib(pkcs7->heap); + if (derArr[i] == NULL) { + FreeAttribArray(pkcs7, derArr, eaSz); + return MEMORY_E; + } + ForceZero(derArr[i], sizeof(FlatAttrib)); + } + + /* flatten EncodedAttrib into DER byte arrays */ + ret = FlattenEncodedAttribs(pkcs7, derArr, eaSz, ea, eaSz); + if (ret != 0) { + FreeAttribArray(pkcs7, derArr, eaSz); + return ret; + } + + /* SET OF DER signed attributes must be sorted in ascending order */ + ret = SortAttribArray(derArr, eaSz); + if (ret != 0) { + FreeAttribArray(pkcs7, derArr, eaSz); + return ret; + } + + /* copy sorted DER attribute arrays into output buffer */ + idx = 0; + for (i = 0; i < eaSz; i++) { + fa = derArr[i]; + XMEMCPY(output + idx, fa->data, fa->dataSz); + idx += fa->dataSz; + } + + FreeAttribArray(pkcs7, derArr, eaSz); + + return 0; +} + + +#ifndef NO_RSA + +/* returns size of signature put into out, negative on error */ +static int wc_PKCS7_RsaSign(PKCS7* pkcs7, byte* in, word32 inSz, ESD* esd) +{ + int ret; + word32 idx; #ifdef WOLFSSL_SMALL_STACK - ESD* esd = NULL; + RsaKey* privKey; #else - ESD stack_esd; - ESD* esd = &stack_esd; + RsaKey privKey[1]; #endif - word32 signerInfoSz = 0; - word32 totalSz = 0; - int idx = 0, ret = 0; - byte* flatSignedAttribs = NULL; - word32 flatSignedAttribsSz = 0; - word32 innerOidSz = sizeof(innerOid); - word32 outerOidSz = sizeof(outerOid); + if (pkcs7 == NULL || pkcs7->rng == NULL || in == NULL || esd == NULL) { + return BAD_FUNC_ARG; + } - if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || - pkcs7->encryptOID == 0 || pkcs7->hashOID == 0 || pkcs7->rng == 0 || - pkcs7->singleCert == NULL || pkcs7->singleCertSz == 0 || - pkcs7->privateKey == NULL || pkcs7->privateKeySz == 0 || - output == NULL || outputSz == 0) +#ifdef WOLFSSL_SMALL_STACK + privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (privKey == NULL) + return MEMORY_E; +#endif + + ret = wc_InitRsaKey_ex(privKey, pkcs7->heap, pkcs7->devId); + if (ret == 0) { + if (pkcs7->privateKey != NULL && pkcs7->privateKeySz > 0) { + idx = 0; + ret = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &idx, privKey, + pkcs7->privateKeySz); + } + else if (pkcs7->devId == INVALID_DEVID) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + #ifdef WOLFSSL_ASYNC_CRYPT + do { + ret = wc_AsyncWait(ret, &privKey->asyncDev, + WC_ASYNC_FLAG_CALL_AGAIN); + if (ret >= 0) + #endif + { + ret = wc_RsaSSL_Sign(in, inSz, esd->encContentDigest, + sizeof(esd->encContentDigest), + privKey, pkcs7->rng); + } + #ifdef WOLFSSL_ASYNC_CRYPT + } while (ret == WC_PENDING_E); + #endif + } + + wc_FreeRsaKey(privKey); +#ifdef WOLFSSL_SMALL_STACK + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} + +#endif /* NO_RSA */ + + +#ifdef HAVE_ECC + +/* returns size of signature put into out, negative on error */ +static int wc_PKCS7_EcdsaSign(PKCS7* pkcs7, byte* in, word32 inSz, ESD* esd) +{ + int ret; + word32 outSz, idx; +#ifdef WOLFSSL_SMALL_STACK + ecc_key* privKey; +#else + ecc_key privKey[1]; +#endif + + if (pkcs7 == NULL || pkcs7->rng == NULL || in == NULL || esd == NULL) { return BAD_FUNC_ARG; + } #ifdef WOLFSSL_SMALL_STACK - esd = (ESD*)XMALLOC(sizeof(ESD), NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (esd == NULL) + privKey = (ecc_key*)XMALLOC(sizeof(ecc_key), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (privKey == NULL) return MEMORY_E; #endif - XMEMSET(esd, 0, sizeof(ESD)); - ret = wc_InitSha(&esd->sha); - if (ret != 0) { + ret = wc_ecc_init_ex(privKey, pkcs7->heap, pkcs7->devId); + if (ret == 0) { + if (pkcs7->privateKey != NULL && pkcs7->privateKeySz > 0) { + idx = 0; + ret = wc_EccPrivateKeyDecode(pkcs7->privateKey, &idx, privKey, + pkcs7->privateKeySz); + } + else if (pkcs7->devId == INVALID_DEVID) { + ret = BAD_FUNC_ARG; + } + } + if (ret == 0) { + outSz = sizeof(esd->encContentDigest); + #ifdef WOLFSSL_ASYNC_CRYPT + do { + ret = wc_AsyncWait(ret, &privKey->asyncDev, + WC_ASYNC_FLAG_CALL_AGAIN); + if (ret >= 0) + #endif + { + ret = wc_ecc_sign_hash(in, inSz, esd->encContentDigest, + &outSz, pkcs7->rng, privKey); + } + #ifdef WOLFSSL_ASYNC_CRYPT + } while (ret == WC_PENDING_E); + #endif + if (ret == 0) + ret = (int)outSz; + } + + wc_ecc_free(privKey); #ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ret; + + return ret; +} + +#endif /* HAVE_ECC */ + + +/* builds up SignedData signed attributes, including default ones. + * + * pkcs7 - pointer to initialized PKCS7 structure + * esd - pointer to initialized ESD structure, used for output + * + * return 0 on success, negative on error */ +static int wc_PKCS7_BuildSignedAttributes(PKCS7* pkcs7, ESD* esd, + const byte* contentType, word32 contentTypeSz, + const byte* contentTypeOid, word32 contentTypeOidSz, + const byte* messageDigestOid, word32 messageDigestOidSz, + const byte* signingTimeOid, word32 signingTimeOidSz, + byte* signingTime, word32 signingTimeSz) +{ + int hashSz; +#ifdef NO_ASN_TIME + PKCS7Attrib cannedAttribs[2]; +#else + time_t tm; + int timeSz; + PKCS7Attrib cannedAttribs[3]; +#endif + word32 idx = 0; + word32 cannedAttribsCount; + + if (pkcs7 == NULL || esd == NULL || contentType == NULL || + contentTypeOid == NULL || messageDigestOid == NULL || + signingTimeOid == NULL) { + return BAD_FUNC_ARG; } - if (pkcs7->contentSz != 0) - { - wc_ShaUpdate(&esd->sha, pkcs7->content, pkcs7->contentSz); - esd->contentDigest[0] = ASN_OCTET_STRING; - esd->contentDigest[1] = SHA_DIGEST_SIZE; - wc_ShaFinal(&esd->sha, &esd->contentDigest[2]); - } - - esd->innerOctetsSz = SetOctetString(pkcs7->contentSz, esd->innerOctets); - esd->innerContSeqSz = SetExplicit(0, esd->innerOctetsSz + pkcs7->contentSz, - esd->innerContSeq); - esd->contentInfoSeqSz = SetSequence(pkcs7->contentSz + esd->innerOctetsSz + - innerOidSz + esd->innerContSeqSz, - esd->contentInfoSeq); - - esd->issuerSnSz = SetSerialNumber(pkcs7->issuerSn, pkcs7->issuerSnSz, - esd->issuerSn); - signerInfoSz += esd->issuerSnSz; - esd->issuerNameSz = SetSequence(pkcs7->issuerSz, esd->issuerName); - signerInfoSz += esd->issuerNameSz + pkcs7->issuerSz; - esd->issuerSnSeqSz = SetSequence(signerInfoSz, esd->issuerSnSeq); - signerInfoSz += esd->issuerSnSeqSz; - esd->signerVersionSz = SetMyVersion(1, esd->signerVersion, 0); - signerInfoSz += esd->signerVersionSz; - esd->signerDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd->signerDigAlgoId, - hashType, 0); - signerInfoSz += esd->signerDigAlgoIdSz; - esd->digEncAlgoIdSz = SetAlgoID(pkcs7->encryptOID, esd->digEncAlgoId, - keyType, 0); - signerInfoSz += esd->digEncAlgoIdSz; - - if (pkcs7->signedAttribsSz != 0) { - byte contentTypeOid[] = - { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0d, 0x01, - 0x09, 0x03 }; - byte contentType[] = - { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x07, 0x01 }; - byte messageDigestOid[] = - { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, - 0x09, 0x04 }; - - PKCS7Attrib cannedAttribs[2] = - { - { contentTypeOid, sizeof(contentTypeOid), - contentType, sizeof(contentType) }, - { messageDigestOid, sizeof(messageDigestOid), - esd->contentDigest, sizeof(esd->contentDigest) } - }; - word32 cannedAttribsCount = sizeof(cannedAttribs)/sizeof(PKCS7Attrib); + if (pkcs7->skipDefaultSignedAttribs == 0) { + hashSz = wc_HashGetDigestSize(esd->hashType); + if (hashSz < 0) + return hashSz; + + #ifndef NO_ASN_TIME + if (signingTime == NULL || signingTimeSz == 0) + return BAD_FUNC_ARG; + + tm = XTIME(0); + timeSz = GetAsnTimeString(&tm, signingTime, signingTimeSz); + if (timeSz < 0) + return timeSz; + #endif + + cannedAttribsCount = sizeof(cannedAttribs)/sizeof(PKCS7Attrib); + + cannedAttribs[idx].oid = contentTypeOid; + cannedAttribs[idx].oidSz = contentTypeOidSz; + cannedAttribs[idx].value = contentType; + cannedAttribs[idx].valueSz = contentTypeSz; + idx++; + #ifndef NO_ASN_TIME + cannedAttribs[idx].oid = signingTimeOid; + cannedAttribs[idx].oidSz = signingTimeOidSz; + cannedAttribs[idx].value = signingTime; + cannedAttribs[idx].valueSz = timeSz; + idx++; + #endif + cannedAttribs[idx].oid = messageDigestOid; + cannedAttribs[idx].oidSz = messageDigestOidSz; + cannedAttribs[idx].value = esd->contentDigest; + cannedAttribs[idx].valueSz = hashSz + 2; /* ASN.1 heading */ esd->signedAttribsCount += cannedAttribsCount; - esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[0], 2, + esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[0], 3, cannedAttribs, cannedAttribsCount); + } else { + esd->signedAttribsCount = 0; + esd->signedAttribsSz = 0; + } + /* add custom signed attributes if set */ + if (pkcs7->signedAttribsSz > 0 && pkcs7->signedAttribs != NULL) { esd->signedAttribsCount += pkcs7->signedAttribsSz; + #ifdef NO_ASN_TIME esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[2], 4, pkcs7->signedAttribs, pkcs7->signedAttribsSz); + #else + esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[3], 4, + pkcs7->signedAttribs, pkcs7->signedAttribsSz); + #endif + } - flatSignedAttribs = (byte*)XMALLOC(esd->signedAttribsSz, 0, NULL); - flatSignedAttribsSz = esd->signedAttribsSz; - if (flatSignedAttribs == NULL) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return MEMORY_E; +#ifdef NO_ASN_TIME + (void)signingTimeOidSz; + (void)signingTime; + (void)signingTimeSz; +#endif + + return 0; +} + + +/* gets correct encryption algo ID for SignedData, either CTC_<hash>wRSA or + * CTC_<hash>wECDSA, from pkcs7->publicKeyOID and pkcs7->hashOID. + * + * pkcs7 - pointer to PKCS7 structure + * digEncAlgoId - [OUT] output int to store correct algo ID in + * digEncAlgoType - [OUT] output for algo ID type + * + * return 0 on success, negative on error */ +static int wc_PKCS7_SignedDataGetEncAlgoId(PKCS7* pkcs7, int* digEncAlgoId, + int* digEncAlgoType) +{ + int algoId = 0; + int algoType = 0; + + if (pkcs7 == NULL || digEncAlgoId == NULL || digEncAlgoType == NULL) + return BAD_FUNC_ARG; + + if (pkcs7->publicKeyOID == RSAk) { + + algoType = oidSigType; + + switch (pkcs7->hashOID) { + #ifndef NO_SHA + case SHAh: + algoId = CTC_SHAwRSA; + break; + #endif + #ifdef WOLFSSL_SHA224 + case SHA224h: + algoId = CTC_SHA224wRSA; + break; + #endif + #ifndef NO_SHA256 + case SHA256h: + algoId = CTC_SHA256wRSA; + break; + #endif + #ifdef WOLFSSL_SHA384 + case SHA384h: + algoId = CTC_SHA384wRSA; + break; + #endif + #ifdef WOLFSSL_SHA512 + case SHA512h: + algoId = CTC_SHA512wRSA; + break; + #endif } - FlattenAttributes(flatSignedAttribs, - esd->signedAttribs, esd->signedAttribsCount); - esd->signedAttribSetSz = SetImplicit(ASN_SET, 0, esd->signedAttribsSz, - esd->signedAttribSet); + } - /* Calculate the final hash and encrypt it. */ - { - int result; - word32 scratch = 0; +#ifdef HAVE_ECC + else if (pkcs7->publicKeyOID == ECDSAk) { + + algoType = oidSigType; + + switch (pkcs7->hashOID) { + #ifndef NO_SHA + case SHAh: + algoId = CTC_SHAwECDSA; + break; + #endif + #ifdef WOLFSSL_SHA224 + case SHA224h: + algoId = CTC_SHA224wECDSA; + break; + #endif + #ifndef NO_SHA256 + case SHA256h: + algoId = CTC_SHA256wECDSA; + break; + #endif + #ifdef WOLFSSL_SHA384 + case SHA384h: + algoId = CTC_SHA384wECDSA; + break; + #endif + #ifdef WOLFSSL_SHA512 + case SHA512h: + algoId = CTC_SHA512wECDSA; + break; + #endif + } + } +#endif /* HAVE_ECC */ + + if (algoId == 0) { + WOLFSSL_MSG("Invalid signature algorithm type"); + return BAD_FUNC_ARG; + } + + *digEncAlgoId = algoId; + *digEncAlgoType = algoType; + + return 0; +} + + +/* build SignedData DigestInfo for use with PKCS#7/RSA + * + * pkcs7 - pointer to initialized PKCS7 struct + * flatSignedAttribs - flattened, signed attributes + * flatSignedAttrbsSz - size of flatSignedAttribs, octets + * esd - pointer to initialized ESD struct + * digestInfo - [OUT] output array for DigestInfo + * digestInfoSz - [IN/OUT] - input size of array, size of digestInfo + * + * return 0 on success, negative on error */ +static int wc_PKCS7_BuildDigestInfo(PKCS7* pkcs7, byte* flatSignedAttribs, + word32 flatSignedAttribsSz, ESD* esd, + byte* digestInfo, word32* digestInfoSz) +{ + int ret, hashSz, digIdx = 0; + byte digestInfoSeq[MAX_SEQ_SZ]; + byte digestStr[MAX_OCTET_STR_SZ]; + byte attribSet[MAX_SET_SZ]; + byte algoId[MAX_ALGO_SZ]; + word32 digestInfoSeqSz, digestStrSz, algoIdSz; + word32 attribSetSz; + + if (pkcs7 == NULL || esd == NULL || digestInfo == NULL || + digestInfoSz == NULL) { + return BAD_FUNC_ARG; + } + + hashSz = wc_HashGetDigestSize(esd->hashType); + if (hashSz < 0) + return hashSz; + + if (flatSignedAttribsSz != 0) { + + if (flatSignedAttribs == NULL) + return BAD_FUNC_ARG; + + attribSetSz = SetSet(flatSignedAttribsSz, attribSet); + + ret = wc_HashInit(&esd->hash, esd->hashType); + if (ret < 0) + return ret; + + ret = wc_HashUpdate(&esd->hash, esd->hashType, + attribSet, attribSetSz); + if (ret == 0) + ret = wc_HashUpdate(&esd->hash, esd->hashType, + flatSignedAttribs, flatSignedAttribsSz); + if (ret == 0) + ret = wc_HashFinal(&esd->hash, esd->hashType, + esd->contentAttribsDigest); + wc_HashFree(&esd->hash, esd->hashType); + + if (ret < 0) + return ret; + + } else { + /* when no attrs, digest is contentDigest without tag and length */ + XMEMCPY(esd->contentAttribsDigest, esd->contentDigest + 2, hashSz); + } + + /* set algoID, with NULL attributes */ + algoIdSz = SetAlgoID(pkcs7->hashOID, algoId, oidHashType, 0); + + digestStrSz = SetOctetString(hashSz, digestStr); + digestInfoSeqSz = SetSequence(algoIdSz + digestStrSz + hashSz, + digestInfoSeq); + if (*digestInfoSz < (digestInfoSeqSz + algoIdSz + digestStrSz + hashSz)) { + return BUFFER_E; + } + + XMEMCPY(digestInfo + digIdx, digestInfoSeq, digestInfoSeqSz); + digIdx += digestInfoSeqSz; + XMEMCPY(digestInfo + digIdx, algoId, algoIdSz); + digIdx += algoIdSz; + XMEMCPY(digestInfo + digIdx, digestStr, digestStrSz); + digIdx += digestStrSz; + XMEMCPY(digestInfo + digIdx, esd->contentAttribsDigest, hashSz); + digIdx += hashSz; + + *digestInfoSz = digIdx; + + return 0; +} + + +/* build SignedData signature over DigestInfo or content digest + * + * pkcs7 - pointer to initialized PKCS7 struct + * flatSignedAttribs - flattened, signed attributes + * flatSignedAttribsSz - size of flatSignedAttribs, octets + * esd - pointer to initialized ESD struct + * + * returns length of signature on success, negative on error */ +static int wc_PKCS7_SignedDataBuildSignature(PKCS7* pkcs7, + byte* flatSignedAttribs, + word32 flatSignedAttribsSz, + ESD* esd) +{ + int ret = 0; +#if defined(HAVE_ECC) || \ + (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) + int hashSz = 0; +#endif +#if defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA) + int hashOID; +#endif + word32 digestInfoSz = MAX_PKCS7_DIGEST_SZ; #ifdef WOLFSSL_SMALL_STACK - byte* digestInfo; - RsaKey* privKey; + byte* digestInfo; #else - RsaKey stack_privKey; - RsaKey* privKey = &stack_privKey; - byte digestInfo[MAX_SEQ_SZ + MAX_ALGO_SZ + - MAX_OCTET_STR_SZ + SHA_DIGEST_SIZE]; + byte digestInfo[MAX_PKCS7_DIGEST_SZ]; #endif - byte digestInfoSeq[MAX_SEQ_SZ]; - byte digestStr[MAX_OCTET_STR_SZ]; - word32 digestInfoSeqSz, digestStrSz; - int digIdx = 0; - if (pkcs7->signedAttribsSz != 0) { - byte attribSet[MAX_SET_SZ]; - word32 attribSetSz; + if (pkcs7 == NULL || esd == NULL) + return BAD_FUNC_ARG; - attribSetSz = SetSet(flatSignedAttribsSz, attribSet); +#ifdef WOLFSSL_SMALL_STACK + digestInfo = (byte*)XMALLOC(digestInfoSz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (digestInfo == NULL) { + return MEMORY_E; + } +#endif + XMEMSET(digestInfo, 0, digestInfoSz); - ret = wc_InitSha(&esd->sha); - if (ret < 0) { - XFREE(flatSignedAttribs, 0, NULL); + ret = wc_PKCS7_BuildDigestInfo(pkcs7, flatSignedAttribs, + flatSignedAttribsSz, esd, digestInfo, + &digestInfoSz); + if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ret; - } - wc_ShaUpdate(&esd->sha, attribSet, attribSetSz); - wc_ShaUpdate(&esd->sha, flatSignedAttribs, flatSignedAttribsSz); - } - wc_ShaFinal(&esd->sha, esd->contentAttribsDigest); + return ret; + } - digestStrSz = SetOctetString(SHA_DIGEST_SIZE, digestStr); - digestInfoSeqSz = SetSequence(esd->signerDigAlgoIdSz + - digestStrSz + SHA_DIGEST_SIZE, - digestInfoSeq); +#if defined(HAVE_ECC) || \ + (defined(HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK) && !defined(NO_RSA)) + /* get digest size from hash type */ + hashSz = wc_HashGetDigestSize(esd->hashType); + if (hashSz < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return hashSz; + } +#endif -#ifdef WOLFSSL_SMALL_STACK - digestInfo = (byte*)XMALLOC(MAX_SEQ_SZ + MAX_ALGO_SZ + - MAX_OCTET_STR_SZ + SHA_DIGEST_SIZE, - NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (digestInfo == NULL) { - if (pkcs7->signedAttribsSz != 0) - XFREE(flatSignedAttribs, 0, NULL); - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return MEMORY_E; - } + /* sign digestInfo */ + switch (pkcs7->publicKeyOID) { + +#ifndef NO_RSA + case RSAk: + #ifdef HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK + if (pkcs7->rsaSignRawDigestCb != NULL) { + /* get hash OID */ + hashOID = wc_HashGetOID(esd->hashType); + + /* user signing plain digest, build DigestInfo themselves */ + ret = pkcs7->rsaSignRawDigestCb(pkcs7, + esd->contentAttribsDigest, hashSz, + esd->encContentDigest, sizeof(esd->encContentDigest), + pkcs7->privateKey, pkcs7->privateKeySz, pkcs7->devId, + hashOID); + break; + } + #endif + ret = wc_PKCS7_RsaSign(pkcs7, digestInfo, digestInfoSz, esd); + break; +#endif + +#ifdef HAVE_ECC + case ECDSAk: + /* CMS with ECDSA does not sign DigestInfo structure + * like PKCS#7 with RSA does */ + ret = wc_PKCS7_EcdsaSign(pkcs7, esd->contentAttribsDigest, + hashSz, esd); + break; #endif - XMEMCPY(digestInfo + digIdx, digestInfoSeq, digestInfoSeqSz); - digIdx += digestInfoSeqSz; - XMEMCPY(digestInfo + digIdx, - esd->signerDigAlgoId, esd->signerDigAlgoIdSz); - digIdx += esd->signerDigAlgoIdSz; - XMEMCPY(digestInfo + digIdx, digestStr, digestStrSz); - digIdx += digestStrSz; - XMEMCPY(digestInfo + digIdx, esd->contentAttribsDigest, - SHA_DIGEST_SIZE); - digIdx += SHA_DIGEST_SIZE; + default: + WOLFSSL_MSG("Unsupported public key type"); + ret = BAD_FUNC_ARG; + } #ifdef WOLFSSL_SMALL_STACK - privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (privKey == NULL) { - if (pkcs7->signedAttribsSz != 0) - XFREE(flatSignedAttribs, 0, NULL); - XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return MEMORY_E; - } + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - result = wc_InitRsaKey(privKey, NULL); - if (result == 0) - result = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &scratch, privKey, - pkcs7->privateKeySz); - if (result < 0) { - if (pkcs7->signedAttribsSz != 0) - XFREE(flatSignedAttribs, 0, NULL); + if (ret >= 0) { + esd->encContentDigestSz = (word32)ret; + } + + return ret; +} + + +/* build PKCS#7 signedData content type */ +static int PKCS7_EncodeSigned(PKCS7* pkcs7, ESD* esd, + const byte* hashBuf, word32 hashSz, byte* output, word32* outputSz, + byte* output2, word32* output2Sz) +{ + /* contentType OID (1.2.840.113549.1.9.3) */ + const byte contentTypeOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0d, 0x01, + 0x09, 0x03 }; + + /* messageDigest OID (1.2.840.113549.1.9.4) */ + const byte messageDigestOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x04 }; + + /* signingTime OID () */ + byte signingTimeOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, + 0x09, 0x05}; + + Pkcs7Cert* certPtr = NULL; + word32 certSetSz = 0; + + word32 signerInfoSz = 0; + word32 totalSz, total2Sz; + int idx = 0, ret = 0; + int digEncAlgoId, digEncAlgoType; + byte* flatSignedAttribs = NULL; + word32 flatSignedAttribsSz = 0; + + byte signedDataOid[MAX_OID_SZ]; + word32 signedDataOidSz; + + byte signingTime[MAX_TIME_STRING_SZ]; + + if (pkcs7 == NULL || pkcs7->contentSz == 0 || + pkcs7->encryptOID == 0 || pkcs7->hashOID == 0 || pkcs7->rng == 0 || + output == NULL || outputSz == NULL || *outputSz == 0 || hashSz == 0 || + hashBuf == NULL) { + return BAD_FUNC_ARG; + } + + /* verify the hash size matches */ #ifdef WOLFSSL_SMALL_STACK - XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); + esd = (ESD*)XMALLOC(sizeof(ESD), pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (esd == NULL) + return MEMORY_E; #endif - return PUBLIC_KEY_E; - } - result = wc_RsaSSL_Sign(digestInfo, digIdx, - esd->encContentDigest, - sizeof(esd->encContentDigest), - privKey, pkcs7->rng); + XMEMSET(esd, 0, sizeof(ESD)); - wc_FreeRsaKey(privKey); + /* set content type based on contentOID, unless user has set custom one + with wc_PKCS7_SetContentType() */ + if (pkcs7->contentTypeSz == 0) { + /* default to DATA content type if user has not set */ + if (pkcs7->contentOID == 0) { + pkcs7->contentOID = DATA; + } + + ret = wc_SetContentType(pkcs7->contentOID, pkcs7->contentType, + sizeof(pkcs7->contentType)); + if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + return ret; + } + pkcs7->contentTypeSz = ret; + } - if (result < 0) { - if (pkcs7->signedAttribsSz != 0) - XFREE(flatSignedAttribs, 0, NULL); + /* set signedData outer content type */ + ret = wc_SetContentType(SIGNED_DATA, signedDataOid, sizeof(signedDataOid)); + if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return result; + return ret; + } + signedDataOidSz = ret; + + if (pkcs7->sidType != DEGENERATE_SID) { + esd->hashType = wc_OidGetHash(pkcs7->hashOID); + if (wc_HashGetDigestSize(esd->hashType) != (int)hashSz) { + WOLFSSL_MSG("hashSz did not match hashOID"); + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return BUFFER_E; } - esd->encContentDigestSz = (word32)result; + + /* include hash */ + esd->contentDigest[0] = ASN_OCTET_STRING; + esd->contentDigest[1] = (byte)hashSz; + XMEMCPY(&esd->contentDigest[2], hashBuf, hashSz); } - signerInfoSz += flatSignedAttribsSz + esd->signedAttribSetSz; - esd->signerDigestSz = SetOctetString(esd->encContentDigestSz, - esd->signerDigest); - signerInfoSz += esd->signerDigestSz + esd->encContentDigestSz; + if (pkcs7->detached == 1) { + /* do not include content if generating detached signature */ + esd->innerOctetsSz = 0; + esd->innerContSeqSz = 0; + esd->contentInfoSeqSz = SetSequence(pkcs7->contentTypeSz, + esd->contentInfoSeq); + } else { + esd->innerOctetsSz = SetOctetString(pkcs7->contentSz, esd->innerOctets); + esd->innerContSeqSz = SetExplicit(0, esd->innerOctetsSz + + pkcs7->contentSz, esd->innerContSeq); + esd->contentInfoSeqSz = SetSequence(pkcs7->contentSz + + esd->innerOctetsSz + pkcs7->contentTypeSz + + esd->innerContSeqSz, esd->contentInfoSeq); + } - esd->signerInfoSeqSz = SetSequence(signerInfoSz, esd->signerInfoSeq); - signerInfoSz += esd->signerInfoSeqSz; + /* SignerIdentifier */ + if (pkcs7->sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + /* IssuerAndSerialNumber */ + esd->issuerSnSz = SetSerialNumber(pkcs7->issuerSn, pkcs7->issuerSnSz, + esd->issuerSn, MAX_SN_SZ, MAX_SN_SZ); + signerInfoSz += esd->issuerSnSz; + esd->issuerNameSz = SetSequence(pkcs7->issuerSz, esd->issuerName); + signerInfoSz += esd->issuerNameSz + pkcs7->issuerSz; + esd->issuerSnSeqSz = SetSequence(signerInfoSz, esd->issuerSnSeq); + signerInfoSz += esd->issuerSnSeqSz; + + if (pkcs7->version == 3) { + /* RFC 4108 version MUST be 3 for firmware package signer */ + esd->signerVersionSz = SetMyVersion(3, esd->signerVersion, 0); + } + else { + /* version MUST be 1 otherwise*/ + esd->signerVersionSz = SetMyVersion(1, esd->signerVersion, 0); + } + + } else if (pkcs7->sidType == CMS_SKID) { + /* SubjectKeyIdentifier */ + esd->issuerSKIDSz = SetOctetString(KEYID_SIZE, esd->issuerSKID); + esd->issuerSKIDSeqSz = SetExplicit(0, esd->issuerSKIDSz + KEYID_SIZE, + esd->issuerSKIDSeq); + signerInfoSz += (esd->issuerSKIDSz + esd->issuerSKIDSeqSz + + KEYID_SIZE); + + /* version MUST be 3 */ + esd->signerVersionSz = SetMyVersion(3, esd->signerVersion, 0); + } else if (pkcs7->sidType == DEGENERATE_SID) { + /* no signer info added */ + } else { + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return SKID_E; + } + + if (pkcs7->sidType != DEGENERATE_SID) { + signerInfoSz += esd->signerVersionSz; + esd->signerDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd->signerDigAlgoId, + oidHashType, 0); + signerInfoSz += esd->signerDigAlgoIdSz; + + /* set signatureAlgorithm */ + ret = wc_PKCS7_SignedDataGetEncAlgoId(pkcs7, &digEncAlgoId, + &digEncAlgoType); + if (ret < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + esd->digEncAlgoIdSz = SetAlgoID(digEncAlgoId, esd->digEncAlgoId, + digEncAlgoType, 0); + signerInfoSz += esd->digEncAlgoIdSz; + + /* build up signed attributes, include contentType, signingTime, and + messageDigest by default */ + ret = wc_PKCS7_BuildSignedAttributes(pkcs7, esd, pkcs7->contentType, + pkcs7->contentTypeSz, + contentTypeOid, sizeof(contentTypeOid), + messageDigestOid, sizeof(messageDigestOid), + signingTimeOid, sizeof(signingTimeOid), + signingTime, sizeof(signingTime)); + if (ret < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + + if (esd->signedAttribsSz > 0) { + flatSignedAttribs = (byte*)XMALLOC(esd->signedAttribsSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + flatSignedAttribsSz = esd->signedAttribsSz; + if (flatSignedAttribs == NULL) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; + } + + FlattenAttributes(pkcs7, flatSignedAttribs, + esd->signedAttribs, esd->signedAttribsCount); + esd->signedAttribSetSz = SetImplicit(ASN_SET, 0, esd->signedAttribsSz, + esd->signedAttribSet); + } else { + esd->signedAttribSetSz = 0; + } + + /* Calculate the final hash and encrypt it. */ + ret = wc_PKCS7_SignedDataBuildSignature(pkcs7, flatSignedAttribs, + flatSignedAttribsSz, esd); + if (ret < 0) { + if (pkcs7->signedAttribsSz != 0) + XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + + signerInfoSz += flatSignedAttribsSz + esd->signedAttribSetSz; + + esd->signerDigestSz = SetOctetString(esd->encContentDigestSz, + esd->signerDigest); + signerInfoSz += esd->signerDigestSz + esd->encContentDigestSz; + + esd->signerInfoSeqSz = SetSequence(signerInfoSz, esd->signerInfoSeq); + signerInfoSz += esd->signerInfoSeqSz; + } esd->signerInfoSetSz = SetSet(signerInfoSz, esd->signerInfoSet); signerInfoSz += esd->signerInfoSetSz; - esd->certsSetSz = SetImplicit(ASN_SET, 0, pkcs7->singleCertSz, - esd->certsSet); + /* certificates [0] IMPLICIT CertificateSet */ + /* get total certificates size */ + certPtr = pkcs7->certList; + while (certPtr != NULL) { + certSetSz += certPtr->derSz; + certPtr = certPtr->next; + } + certPtr = NULL; - esd->singleDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd->singleDigAlgoId, - hashType, 0); - esd->digAlgoIdSetSz = SetSet(esd->singleDigAlgoIdSz, esd->digAlgoIdSet); + if (certSetSz > 0) + esd->certsSetSz = SetImplicit(ASN_SET, 0, certSetSz, esd->certsSet); + if (pkcs7->sidType != DEGENERATE_SID) { + esd->singleDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd->singleDigAlgoId, + oidHashType, 0); + } + esd->digAlgoIdSetSz = SetSet(esd->singleDigAlgoIdSz, esd->digAlgoIdSet); - esd->versionSz = SetMyVersion(1, esd->version, 0); + if (pkcs7->version == 3) { + /* RFC 4108 version MUST be 3 for firmware package signer */ + esd->versionSz = SetMyVersion(3, esd->version, 0); + } + else { + esd->versionSz = SetMyVersion(1, esd->version, 0); + } totalSz = esd->versionSz + esd->singleDigAlgoIdSz + esd->digAlgoIdSetSz + - esd->contentInfoSeqSz + esd->certsSetSz + pkcs7->singleCertSz + - esd->innerOctetsSz + esd->innerContSeqSz + - innerOidSz + pkcs7->contentSz + - signerInfoSz; - esd->innerSeqSz = SetSequence(totalSz, esd->innerSeq); + esd->contentInfoSeqSz + pkcs7->contentTypeSz + + esd->innerContSeqSz + esd->innerOctetsSz + pkcs7->contentSz; + total2Sz = esd->certsSetSz + certSetSz + signerInfoSz; + + if (pkcs7->detached) { + totalSz -= pkcs7->contentSz; + } + + esd->innerSeqSz = SetSequence(totalSz + total2Sz, esd->innerSeq); totalSz += esd->innerSeqSz; - esd->outerContentSz = SetExplicit(0, totalSz, esd->outerContent); - totalSz += esd->outerContentSz + outerOidSz; - esd->outerSeqSz = SetSequence(totalSz, esd->outerSeq); + esd->outerContentSz = SetExplicit(0, totalSz + total2Sz, esd->outerContent); + totalSz += esd->outerContentSz + signedDataOidSz; + esd->outerSeqSz = SetSequence(totalSz + total2Sz, esd->outerSeq); totalSz += esd->outerSeqSz; - if (outputSz < totalSz) { + /* if using header/footer, we are not returning the content */ + if (output2 && output2Sz) { + if (total2Sz > *output2Sz) { + if (pkcs7->signedAttribsSz != 0) + XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return BUFFER_E; + } + + if (!pkcs7->detached) { + totalSz -= pkcs7->contentSz; + } + } + else { + /* if using single output buffer include content and footer */ + totalSz += total2Sz; + } + + if (totalSz > *outputSz) { if (pkcs7->signedAttribsSz != 0) - XFREE(flatSignedAttribs, 0, NULL); -#ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif + XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif return BUFFER_E; } idx = 0; XMEMCPY(output + idx, esd->outerSeq, esd->outerSeqSz); idx += esd->outerSeqSz; - XMEMCPY(output + idx, outerOid, outerOidSz); - idx += outerOidSz; + XMEMCPY(output + idx, signedDataOid, signedDataOidSz); + idx += signedDataOidSz; XMEMCPY(output + idx, esd->outerContent, esd->outerContentSz); idx += esd->outerContentSz; XMEMCPY(output + idx, esd->innerSeq, esd->innerSeqSz); @@ -634,326 +2402,3617 @@ int wc_PKCS7_EncodeSignedData(PKCS7* pkcs7, byte* output, word32 outputSz) idx += esd->singleDigAlgoIdSz; XMEMCPY(output + idx, esd->contentInfoSeq, esd->contentInfoSeqSz); idx += esd->contentInfoSeqSz; - XMEMCPY(output + idx, innerOid, innerOidSz); - idx += innerOidSz; + XMEMCPY(output + idx, pkcs7->contentType, pkcs7->contentTypeSz); + idx += pkcs7->contentTypeSz; XMEMCPY(output + idx, esd->innerContSeq, esd->innerContSeqSz); idx += esd->innerContSeqSz; XMEMCPY(output + idx, esd->innerOctets, esd->innerOctetsSz); idx += esd->innerOctetsSz; - XMEMCPY(output + idx, pkcs7->content, pkcs7->contentSz); - idx += pkcs7->contentSz; - XMEMCPY(output + idx, esd->certsSet, esd->certsSetSz); + + /* support returning header and footer without content */ + if (output2 && output2Sz) { + *outputSz = idx; + idx = 0; + } + else { + if (!pkcs7->detached) { + XMEMCPY(output + idx, pkcs7->content, pkcs7->contentSz); + idx += pkcs7->contentSz; + } + output2 = output; + } + + /* certificates */ + XMEMCPY(output2 + idx, esd->certsSet, esd->certsSetSz); idx += esd->certsSetSz; - XMEMCPY(output + idx, pkcs7->singleCert, pkcs7->singleCertSz); - idx += pkcs7->singleCertSz; - XMEMCPY(output + idx, esd->signerInfoSet, esd->signerInfoSetSz); + certPtr = pkcs7->certList; + while (certPtr != NULL) { + XMEMCPY(output2 + idx, certPtr->der, certPtr->derSz); + idx += certPtr->derSz; + certPtr = certPtr->next; + } + wc_PKCS7_FreeCertSet(pkcs7); + + XMEMCPY(output2 + idx, esd->signerInfoSet, esd->signerInfoSetSz); idx += esd->signerInfoSetSz; - XMEMCPY(output + idx, esd->signerInfoSeq, esd->signerInfoSeqSz); + XMEMCPY(output2 + idx, esd->signerInfoSeq, esd->signerInfoSeqSz); idx += esd->signerInfoSeqSz; - XMEMCPY(output + idx, esd->signerVersion, esd->signerVersionSz); + XMEMCPY(output2 + idx, esd->signerVersion, esd->signerVersionSz); idx += esd->signerVersionSz; - XMEMCPY(output + idx, esd->issuerSnSeq, esd->issuerSnSeqSz); - idx += esd->issuerSnSeqSz; - XMEMCPY(output + idx, esd->issuerName, esd->issuerNameSz); - idx += esd->issuerNameSz; - XMEMCPY(output + idx, pkcs7->issuer, pkcs7->issuerSz); - idx += pkcs7->issuerSz; - XMEMCPY(output + idx, esd->issuerSn, esd->issuerSnSz); - idx += esd->issuerSnSz; - XMEMCPY(output + idx, esd->signerDigAlgoId, esd->signerDigAlgoIdSz); + /* SignerIdentifier */ + if (pkcs7->sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + /* IssuerAndSerialNumber */ + XMEMCPY(output2 + idx, esd->issuerSnSeq, esd->issuerSnSeqSz); + idx += esd->issuerSnSeqSz; + XMEMCPY(output2 + idx, esd->issuerName, esd->issuerNameSz); + idx += esd->issuerNameSz; + XMEMCPY(output2 + idx, pkcs7->issuer, pkcs7->issuerSz); + idx += pkcs7->issuerSz; + XMEMCPY(output2 + idx, esd->issuerSn, esd->issuerSnSz); + idx += esd->issuerSnSz; + } else if (pkcs7->sidType == CMS_SKID) { + /* SubjectKeyIdentifier */ + XMEMCPY(output2 + idx, esd->issuerSKIDSeq, esd->issuerSKIDSeqSz); + idx += esd->issuerSKIDSeqSz; + XMEMCPY(output2 + idx, esd->issuerSKID, esd->issuerSKIDSz); + idx += esd->issuerSKIDSz; + XMEMCPY(output2 + idx, pkcs7->issuerSubjKeyId, KEYID_SIZE); + idx += KEYID_SIZE; + } else if (pkcs7->sidType == DEGENERATE_SID) { + /* no signer infos in degenerate case */ + } else { + #ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return SKID_E; + } + XMEMCPY(output2 + idx, esd->signerDigAlgoId, esd->signerDigAlgoIdSz); idx += esd->signerDigAlgoIdSz; /* SignerInfo:Attributes */ - if (pkcs7->signedAttribsSz != 0) { - XMEMCPY(output + idx, esd->signedAttribSet, esd->signedAttribSetSz); + if (flatSignedAttribsSz > 0) { + XMEMCPY(output2 + idx, esd->signedAttribSet, esd->signedAttribSetSz); idx += esd->signedAttribSetSz; - XMEMCPY(output + idx, flatSignedAttribs, flatSignedAttribsSz); + XMEMCPY(output2 + idx, flatSignedAttribs, flatSignedAttribsSz); idx += flatSignedAttribsSz; - XFREE(flatSignedAttribs, 0, NULL); + XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); } - XMEMCPY(output + idx, esd->digEncAlgoId, esd->digEncAlgoIdSz); + XMEMCPY(output2 + idx, esd->digEncAlgoId, esd->digEncAlgoIdSz); idx += esd->digEncAlgoIdSz; - XMEMCPY(output + idx, esd->signerDigest, esd->signerDigestSz); + XMEMCPY(output2 + idx, esd->signerDigest, esd->signerDigestSz); idx += esd->signerDigestSz; - XMEMCPY(output + idx, esd->encContentDigest, esd->encContentDigestSz); + XMEMCPY(output2 + idx, esd->encContentDigest, esd->encContentDigestSz); idx += esd->encContentDigestSz; + if (output2 && output2Sz) { + *output2Sz = idx; + idx = 0; /* success */ + } + else { + *outputSz = idx; + } + #ifdef WOLFSSL_SMALL_STACK - XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return idx; } +/* hashBuf: The computed digest for the pkcs7->content + * hashSz: The size of computed digest for the pkcs7->content based on hashOID + * outputHead: The PKCS7 header that goes on top of the raw data signed. + * outputFoot: The PKCS7 footer that goes at the end of the raw data signed. + * pkcs7->content: Not used + * pkcs7->contentSz: Must be provided as actual sign of raw data + * return codes: 0=success, negative=error + */ +int wc_PKCS7_EncodeSignedData_ex(PKCS7* pkcs7, const byte* hashBuf, + word32 hashSz, byte* outputHead, word32* outputHeadSz, byte* outputFoot, + word32* outputFootSz) +{ + int ret; +#ifdef WOLFSSL_SMALL_STACK + ESD* esd; +#else + ESD esd[1]; +#endif -/* Finds the certificates in the message and saves it. */ -int wc_PKCS7_VerifySignedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) + /* other args checked in wc_PKCS7_EncodeSigned_ex */ + if (pkcs7 == NULL || outputFoot == NULL || outputFootSz == NULL) { + return BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SMALL_STACK + esd = (ESD*)XMALLOC(sizeof(ESD), pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (esd == NULL) + return MEMORY_E; +#endif + + XMEMSET(esd, 0, sizeof(ESD)); + + ret = PKCS7_EncodeSigned(pkcs7, esd, hashBuf, hashSz, + outputHead, outputHeadSz, outputFoot, outputFootSz); + +#ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} + +/* Toggle detached signature mode on/off for PKCS#7/CMS SignedData content type. + * By default wolfCrypt includes the data to be signed in the SignedData + * bundle. This data can be omitted in the case when a detached signature is + * being created. To enable generation of detached signatures, set flag to "1", + * otherwise set to "0": + * + * flag 1 turns on support + * flag 0 turns off support + * + * pkcs7 - pointer to initialized PKCS7 structure + * flag - turn on/off detached signature generation (1 or 0) + * + * Returns 0 on success, negative upon error. */ +int wc_PKCS7_SetDetached(PKCS7* pkcs7, word16 flag) { - word32 idx, contentType; - int length, version, ret; - byte* content = NULL; - byte* sig = NULL; - byte* cert = NULL; - int contentSz = 0, sigSz = 0, certSz = 0; + if (pkcs7 == NULL || (flag != 0 && flag != 1)) + return BAD_FUNC_ARG; + + pkcs7->detached = flag; + + return 0; +} - if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0) +/* By default, SignedData bundles have the following signed attributes attached: + * contentType (1.2.840.113549.1.9.3) + * signgingTime (1.2.840.113549.1.9.5) + * messageDigest (1.2.840.113549.1.9.4) + * + * Calling this API before wc_PKCS7_EncodeSignedData() will disable the + * inclusion of those attributes. + * + * pkcs7 - pointer to initialized PKCS7 structure + * + * Returns 0 on success, negative upon error. */ +int wc_PKCS7_NoDefaultSignedAttribs(PKCS7* pkcs7) +{ + if (pkcs7 == NULL) return BAD_FUNC_ARG; - idx = 0; + pkcs7->skipDefaultSignedAttribs = 1; - /* Get the contentInfo sequence */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + return 0; +} - /* Get the contentInfo contentType */ - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) - return ASN_PARSE_E; +/* return codes: >0: Size of signed PKCS7 output buffer, negative: error */ +int wc_PKCS7_EncodeSignedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + int ret; + int hashSz; + enum wc_HashType hashType; + byte hashBuf[WC_MAX_DIGEST_SIZE]; +#ifdef WOLFSSL_SMALL_STACK + ESD* esd; +#else + ESD esd[1]; +#endif - if (contentType != SIGNED_DATA) { - WOLFSSL_MSG("PKCS#7 input not of type SignedData"); - return PKCS7_OID_E; + /* other args checked in wc_PKCS7_EncodeSigned_ex */ + if (pkcs7 == NULL || pkcs7->contentSz == 0 || pkcs7->content == NULL) { + return BAD_FUNC_ARG; } - /* get the ContentInfo content */ - if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) - return ASN_PARSE_E; + /* get hash type and size, validate hashOID */ + hashType = wc_OidGetHash(pkcs7->hashOID); + hashSz = wc_HashGetDigestSize(hashType); + if (hashSz < 0) + return hashSz; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; +#ifdef WOLFSSL_SMALL_STACK + esd = (ESD*)XMALLOC(sizeof(ESD), pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (esd == NULL) + return MEMORY_E; +#endif - /* Get the signedData sequence */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + XMEMSET(esd, 0, sizeof(ESD)); + esd->hashType = hashType; + + /* calculate hash for content */ + ret = wc_HashInit(&esd->hash, esd->hashType); + if (ret == 0) { + ret = wc_HashUpdate(&esd->hash, esd->hashType, + pkcs7->content, pkcs7->contentSz); + if (ret == 0) { + ret = wc_HashFinal(&esd->hash, esd->hashType, hashBuf); + } + wc_HashFree(&esd->hash, esd->hashType); + } - /* Get the version */ - if (GetMyVersion(pkiMsg, &idx, &version) < 0) - return ASN_PARSE_E; + if (ret == 0) { + ret = PKCS7_EncodeSigned(pkcs7, esd, hashBuf, hashSz, + output, &outputSz, NULL, NULL); + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(esd, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + return ret; +} + + +/* Single-shot API to generate a CMS SignedData bundle that encapsulates a + * content of type FirmwarePkgData. Any recipient certificates should be + * loaded into the PKCS7 structure prior to calling this function, using + * wc_PKCS7_InitWithCert() and/or wc_PKCS7_AddCertificate(). + * + * pkcs7 - pointer to initialized PKCS7 struct + * privateKey - private RSA/ECC key, used for signing SignedData + * privateKeySz - size of privateKey, octets + * signOID - public key algorithm OID, used for sign operation + * hashOID - hash algorithm OID, used for signature generation + * content - content to be encapsulated, of type FirmwarePkgData + * contentSz - size of content, octets + * signedAttribs - optional signed attributes + * signedAttribsSz - number of PKCS7Attrib members in signedAttribs + * output - output buffer for final bundle + * outputSz - size of output buffer, octets + * + * Returns length of generated bundle on success, negative upon error. */ +int wc_PKCS7_EncodeSignedFPD(PKCS7* pkcs7, byte* privateKey, + word32 privateKeySz, int signOID, int hashOID, + byte* content, word32 contentSz, + PKCS7Attrib* signedAttribs, word32 signedAttribsSz, + byte* output, word32 outputSz) +{ + int ret = 0; + WC_RNG rng; + + if (pkcs7 == NULL || privateKey == NULL || privateKeySz == 0 || + content == NULL || contentSz == 0 || output == NULL || outputSz == 0) + return BAD_FUNC_ARG; - if (version != 1) { - WOLFSSL_MSG("PKCS#7 signedData needs to be of version 1"); - return ASN_VERSION_E; + ret = wc_InitRng(&rng); + if (ret != 0) + return ret; + + pkcs7->rng = &rng; + pkcs7->content = content; + pkcs7->contentSz = contentSz; + pkcs7->contentOID = FIRMWARE_PKG_DATA; + pkcs7->hashOID = hashOID; + pkcs7->encryptOID = signOID; + pkcs7->privateKey = privateKey; + pkcs7->privateKeySz = privateKeySz; + pkcs7->signedAttribs = signedAttribs; + pkcs7->signedAttribsSz = signedAttribsSz; + pkcs7->version = 3; + + ret = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz); + if (ret <= 0) { + WOLFSSL_MSG("Error encoding CMS SignedData content type"); } - /* Get the set of DigestAlgorithmIdentifiers */ - if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + pkcs7->rng = NULL; + wc_FreeRng(&rng); - /* Skip the set. */ - idx += length; + return ret; +} - /* Get the inner ContentInfo sequence */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; +#ifndef NO_PKCS7_ENCRYPTED_DATA - /* Get the inner ContentInfo contentType */ - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) - return ASN_PARSE_E; +/* Single-shot API to generate a CMS SignedData bundle that encapsulates a + * CMS EncryptedData bundle. Content of inner EncryptedData is set to that + * of FirmwarePkgData. Any recipient certificates should be loaded into the + * PKCS7 structure prior to calling this function, using wc_PKCS7_InitWithCert() + * and/or wc_PKCS7_AddCertificate(). + * + * pkcs7 - pointer to initialized PKCS7 struct + * encryptKey - encryption key used for encrypting EncryptedData + * encryptKeySz - size of encryptKey, octets + * privateKey - private RSA/ECC key, used for signing SignedData + * privateKeySz - size of privateKey, octets + * encryptOID - encryption algorithm OID, to be used as encryption + * algorithm for EncryptedData + * signOID - public key algorithm OID, to be used for sign + * operation in SignedData generation + * hashOID - hash algorithm OID, to be used for signature in + * SignedData generation + * content - content to be encapsulated + * contentSz - size of content, octets + * unprotectedAttribs - optional unprotected attributes, for EncryptedData + * unprotectedAttribsSz - number of PKCS7Attrib members in unprotectedAttribs + * signedAttribs - optional signed attributes, for SignedData + * signedAttribsSz - number of PKCS7Attrib members in signedAttribs + * output - output buffer for final bundle + * outputSz - size of output buffer, octets + * + * Returns length of generated bundle on success, negative upon error. */ +int wc_PKCS7_EncodeSignedEncryptedFPD(PKCS7* pkcs7, byte* encryptKey, + word32 encryptKeySz, byte* privateKey, + word32 privateKeySz, int encryptOID, + int signOID, int hashOID, + byte* content, word32 contentSz, + PKCS7Attrib* unprotectedAttribs, + word32 unprotectedAttribsSz, + PKCS7Attrib* signedAttribs, + word32 signedAttribsSz, + byte* output, word32 outputSz) +{ + int ret = 0, encryptedSz = 0; + byte* encrypted = NULL; + WC_RNG rng; + + if (pkcs7 == NULL || encryptKey == NULL || encryptKeySz == 0 || + privateKey == NULL || privateKeySz == 0 || content == NULL || + contentSz == 0 || output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* 1: build up EncryptedData using FirmwarePkgData type, use output + * buffer as tmp for storage and to get size */ + + /* set struct elements, inner content type is FirmwarePkgData */ + pkcs7->content = content; + pkcs7->contentSz = contentSz; + pkcs7->contentOID = FIRMWARE_PKG_DATA; + pkcs7->encryptOID = encryptOID; + pkcs7->encryptionKey = encryptKey; + pkcs7->encryptionKeySz = encryptKeySz; + pkcs7->unprotectedAttribs = unprotectedAttribs; + pkcs7->unprotectedAttribsSz = unprotectedAttribsSz; + pkcs7->version = 3; + + encryptedSz = wc_PKCS7_EncodeEncryptedData(pkcs7, output, outputSz); + if (encryptedSz < 0) { + WOLFSSL_MSG("Error encoding CMS EncryptedData content type"); + return encryptedSz; + } + + /* save encryptedData, reset output buffer and struct */ + encrypted = (byte*)XMALLOC(encryptedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (encrypted == NULL) { + ForceZero(output, outputSz); + return MEMORY_E; + } + + XMEMCPY(encrypted, output, encryptedSz); + ForceZero(output, outputSz); + + ret = wc_InitRng(&rng); + if (ret != 0) { + ForceZero(encrypted, encryptedSz); + XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* 2: build up SignedData, encapsulating EncryptedData */ + pkcs7->rng = &rng; + pkcs7->content = encrypted; + pkcs7->contentSz = encryptedSz; + pkcs7->contentOID = ENCRYPTED_DATA; + pkcs7->hashOID = hashOID; + pkcs7->encryptOID = signOID; + pkcs7->privateKey = privateKey; + pkcs7->privateKeySz = privateKeySz; + pkcs7->signedAttribs = signedAttribs; + pkcs7->signedAttribsSz = signedAttribsSz; + + ret = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz); + if (ret <= 0) { + WOLFSSL_MSG("Error encoding CMS SignedData content type"); + } + + ForceZero(encrypted, encryptedSz); + XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->rng = NULL; + wc_FreeRng(&rng); + + return ret; +} + +#endif /* NO_PKCS7_ENCRYPTED_DATA */ + +#if defined(HAVE_LIBZ) && !defined(NO_PKCS7_COMPRESSED_DATA) +/* Single-shot API to generate a CMS SignedData bundle that encapsulates a + * CMS CompressedData bundle. Content of inner CompressedData is set to that + * of FirmwarePkgData. Any recipient certificates should be loaded into the + * PKCS7 structure prior to calling this function, using wc_PKCS7_InitWithCert() + * and/or wc_PKCS7_AddCertificate(). + * + * pkcs7 - pointer to initialized PKCS7 struct + * privateKey - private RSA/ECC key, used for signing SignedData + * privateKeySz - size of privateKey, octets + * signOID - public key algorithm OID, to be used for sign + * operation in SignedData generation + * hashOID - hash algorithm OID, to be used for signature in + * SignedData generation + * content - content to be encapsulated + * contentSz - size of content, octets + * signedAttribs - optional signed attributes, for SignedData + * signedAttribsSz - number of PKCS7Attrib members in signedAttribs + * output - output buffer for final bundle + * outputSz - size of output buffer, octets + * + * Returns length of generated bundle on success, negative upon error. */ +int wc_PKCS7_EncodeSignedCompressedFPD(PKCS7* pkcs7, byte* privateKey, + word32 privateKeySz, int signOID, + int hashOID, byte* content, + word32 contentSz, + PKCS7Attrib* signedAttribs, + word32 signedAttribsSz, byte* output, + word32 outputSz) +{ + int ret = 0, compressedSz = 0; + byte* compressed = NULL; + WC_RNG rng; + + if (pkcs7 == NULL || privateKey == NULL || privateKeySz == 0 || + content == NULL || contentSz == 0 || output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* 1: build up CompressedData using FirmwarePkgData type, use output + * buffer as tmp for storage and to get size */ + + /* set struct elements, inner content type is FirmwarePkgData */ + pkcs7->content = content; + pkcs7->contentSz = contentSz; + pkcs7->contentOID = FIRMWARE_PKG_DATA; + pkcs7->version = 3; + + compressedSz = wc_PKCS7_EncodeCompressedData(pkcs7, output, outputSz); + if (compressedSz < 0) { + WOLFSSL_MSG("Error encoding CMS CompressedData content type"); + return compressedSz; + } + + /* save compressedData, reset output buffer and struct */ + compressed = (byte*)XMALLOC(compressedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (compressed == NULL) { + ForceZero(output, outputSz); + return MEMORY_E; + } + + XMEMCPY(compressed, output, compressedSz); + ForceZero(output, outputSz); + + ret = wc_InitRng(&rng); + if (ret != 0) { + ForceZero(compressed, compressedSz); + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* 2: build up SignedData, encapsulating EncryptedData */ + pkcs7->rng = &rng; + pkcs7->content = compressed; + pkcs7->contentSz = compressedSz; + pkcs7->contentOID = COMPRESSED_DATA; + pkcs7->hashOID = hashOID; + pkcs7->encryptOID = signOID; + pkcs7->privateKey = privateKey; + pkcs7->privateKeySz = privateKeySz; + pkcs7->signedAttribs = signedAttribs; + pkcs7->signedAttribsSz = signedAttribsSz; + + ret = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz); + if (ret <= 0) { + WOLFSSL_MSG("Error encoding CMS SignedData content type"); + } + + ForceZero(compressed, compressedSz); + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->rng = NULL; + wc_FreeRng(&rng); + + return ret; +} + +#ifndef NO_PKCS7_ENCRYPTED_DATA + +/* Single-shot API to generate a CMS SignedData bundle that encapsulates a + * CMS EncryptedData bundle, which then encapsulates a CMS CompressedData + * bundle. Content of inner CompressedData is set to that of FirmwarePkgData. + * Any recipient certificates should be loaded into the PKCS7 structure prior + * to calling this function, using wc_PKCS7_InitWithCert() and/or + * wc_PKCS7_AddCertificate(). + * + * pkcs7 - pointer to initialized PKCS7 struct + * encryptKey - encryption key used for encrypting EncryptedData + * encryptKeySz - size of encryptKey, octets + * privateKey - private RSA/ECC key, used for signing SignedData + * privateKeySz - size of privateKey, octets + * encryptOID - encryption algorithm OID, to be used as encryption + * algorithm for EncryptedData + * signOID - public key algorithm OID, to be used for sign + * operation in SignedData generation + * hashOID - hash algorithm OID, to be used for signature in + * SignedData generation + * content - content to be encapsulated + * contentSz - size of content, octets + * unprotectedAttribs - optional unprotected attributes, for EncryptedData + * unprotectedAttribsSz - number of PKCS7Attrib members in unprotectedAttribs + * signedAttribs - optional signed attributes, for SignedData + * signedAttribsSz - number of PKCS7Attrib members in signedAttribs + * output - output buffer for final bundle + * outputSz - size of output buffer, octets + * + * Returns length of generated bundle on success, negative upon error. */ +int wc_PKCS7_EncodeSignedEncryptedCompressedFPD(PKCS7* pkcs7, byte* encryptKey, + word32 encryptKeySz, byte* privateKey, + word32 privateKeySz, int encryptOID, + int signOID, int hashOID, byte* content, + word32 contentSz, + PKCS7Attrib* unprotectedAttribs, + word32 unprotectedAttribsSz, + PKCS7Attrib* signedAttribs, + word32 signedAttribsSz, + byte* output, word32 outputSz) +{ + int ret = 0, compressedSz = 0, encryptedSz = 0; + byte* compressed = NULL; + byte* encrypted = NULL; + WC_RNG rng; + + if (pkcs7 == NULL || encryptKey == NULL || encryptKeySz == 0 || + privateKey == NULL || privateKeySz == 0 || content == NULL || + contentSz == 0 || output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* 1: build up CompressedData using FirmwarePkgData type, use output + * buffer as tmp for storage and to get size */ + pkcs7->content = content; + pkcs7->contentSz = contentSz; + pkcs7->contentOID = FIRMWARE_PKG_DATA; + pkcs7->version = 3; + + compressedSz = wc_PKCS7_EncodeCompressedData(pkcs7, output, outputSz); + if (compressedSz < 0) { + WOLFSSL_MSG("Error encoding CMS CompressedData content type"); + return compressedSz; + } + + /* save compressedData, reset output buffer and struct */ + compressed = (byte*)XMALLOC(compressedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (compressed == NULL) + return MEMORY_E; + + XMEMCPY(compressed, output, compressedSz); + ForceZero(output, outputSz); + + /* 2: build up EncryptedData using CompressedData, use output + * buffer as tmp for storage and to get size */ + pkcs7->content = compressed; + pkcs7->contentSz = compressedSz; + pkcs7->contentOID = COMPRESSED_DATA; + pkcs7->encryptOID = encryptOID; + pkcs7->encryptionKey = encryptKey; + pkcs7->encryptionKeySz = encryptKeySz; + pkcs7->unprotectedAttribs = unprotectedAttribs; + pkcs7->unprotectedAttribsSz = unprotectedAttribsSz; + + encryptedSz = wc_PKCS7_EncodeEncryptedData(pkcs7, output, outputSz); + if (encryptedSz < 0) { + WOLFSSL_MSG("Error encoding CMS EncryptedData content type"); + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return encryptedSz; + } + + /* save encryptedData, reset output buffer and struct */ + encrypted = (byte*)XMALLOC(encryptedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (encrypted == NULL) { + ForceZero(compressed, compressedSz); + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + XMEMCPY(encrypted, output, encryptedSz); + ForceZero(compressed, compressedSz); + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ForceZero(output, outputSz); + + ret = wc_InitRng(&rng); + if (ret != 0) { + ForceZero(encrypted, encryptedSz); + XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* 3: build up SignedData, encapsulating EncryptedData */ + pkcs7->rng = &rng; + pkcs7->content = encrypted; + pkcs7->contentSz = encryptedSz; + pkcs7->contentOID = ENCRYPTED_DATA; + pkcs7->hashOID = hashOID; + pkcs7->encryptOID = signOID; + pkcs7->privateKey = privateKey; + pkcs7->privateKeySz = privateKeySz; + pkcs7->signedAttribs = signedAttribs; + pkcs7->signedAttribsSz = signedAttribsSz; + + ret = wc_PKCS7_EncodeSignedData(pkcs7, output, outputSz); + if (ret <= 0) { + WOLFSSL_MSG("Error encoding CMS SignedData content type"); + } + + ForceZero(encrypted, encryptedSz); + XFREE(encrypted, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->rng = NULL; + wc_FreeRng(&rng); + + return ret; +} + +#endif /* !NO_PKCS7_ENCRYPTED_DATA */ +#endif /* HAVE_LIBZ && !NO_PKCS7_COMPRESSED_DATA */ + + +#ifndef NO_RSA + +#ifdef HAVE_PKCS7_RSA_RAW_SIGN_CALLBACK +/* register raw RSA sign digest callback */ +int wc_PKCS7_SetRsaSignRawDigestCb(PKCS7* pkcs7, CallbackRsaSignRawDigest cb) +{ + if (pkcs7 == NULL || cb == NULL) { + return BAD_FUNC_ARG; + } + + pkcs7->rsaSignRawDigestCb = cb; + + return 0; +} +#endif + +/* returns size of signature put into out, negative on error */ +static int wc_PKCS7_RsaVerify(PKCS7* pkcs7, byte* sig, int sigSz, + byte* hash, word32 hashSz) +{ + int ret = 0, i; + word32 scratch = 0, verified = 0; +#ifdef WOLFSSL_SMALL_STACK + byte* digest; + RsaKey* key; + DecodedCert* dCert; +#else + byte digest[MAX_PKCS7_DIGEST_SZ]; + RsaKey key[1]; + DecodedCert stack_dCert; + DecodedCert* dCert = &stack_dCert; +#endif + + if (pkcs7 == NULL || sig == NULL || hash == NULL) { + return BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SMALL_STACK + digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (digest == NULL) + return MEMORY_E; + + key = (RsaKey*)XMALLOC(sizeof(RsaKey), pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (key == NULL) { + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + + dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), pkcs7->heap, + DYNAMIC_TYPE_DCERT); + if (dCert == NULL) { + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } +#endif + + XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); + + /* loop over certs received in certificates set, try to find one + * that will validate signature */ + for (i = 0; i < MAX_PKCS7_CERTS; i++) { + + verified = 0; + scratch = 0; + + if (pkcs7->certSz[i] == 0) + continue; + + ret = wc_InitRsaKey_ex(key, pkcs7->heap, pkcs7->devId); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); +#endif + return ret; + } + + InitDecodedCert(dCert, pkcs7->cert[i], pkcs7->certSz[i], pkcs7->heap); + /* not verifying, only using this to extract public key */ + ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) { + WOLFSSL_MSG("ASN RSA cert parse error"); + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + continue; + } + + if (wc_RsaPublicKeyDecode(dCert->publicKey, &scratch, key, + dCert->pubKeySize) < 0) { + WOLFSSL_MSG("ASN RSA key decode error"); + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + continue; + } + + #ifdef WOLFSSL_ASYNC_CRYPT + do { + ret = wc_AsyncWait(ret, &key->asyncDev, + WC_ASYNC_FLAG_CALL_AGAIN); + #endif + if (ret >= 0) { + ret = wc_RsaSSL_Verify(sig, sigSz, digest, MAX_PKCS7_DIGEST_SZ, + key); + } + #ifdef WOLFSSL_ASYNC_CRYPT + } while (ret == WC_PENDING_E); + #endif + FreeDecodedCert(dCert); + wc_FreeRsaKey(key); + + if ((ret > 0) && (hashSz == (word32)ret)) { + if (XMEMCMP(digest, hash, hashSz) == 0) { + /* found signer that successfully verified signature */ + verified = 1; + break; + } + } + } + + if (verified == 0) { + ret = SIG_VERIFY_E; + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); +#endif + + return ret; +} + +#endif /* NO_RSA */ + + +#ifdef HAVE_ECC + +/* returns size of signature put into out, negative on error */ +static int wc_PKCS7_EcdsaVerify(PKCS7* pkcs7, byte* sig, int sigSz, + byte* hash, word32 hashSz) +{ + int ret = 0, i; + int res = 0; + int verified = 0; +#ifdef WOLFSSL_SMALL_STACK + byte* digest; + ecc_key* key; + DecodedCert* dCert; +#else + byte digest[MAX_PKCS7_DIGEST_SZ]; + ecc_key key[1]; + DecodedCert stack_dCert; + DecodedCert* dCert = &stack_dCert; +#endif + word32 idx = 0; + + if (pkcs7 == NULL || sig == NULL) + return BAD_FUNC_ARG; + +#ifdef WOLFSSL_SMALL_STACK + digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (digest == NULL) + return MEMORY_E; + + key = (ecc_key*)XMALLOC(sizeof(ecc_key), pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (key == NULL) { + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + + dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), pkcs7->heap, + DYNAMIC_TYPE_DCERT); + if (dCert == NULL) { + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } +#endif + + XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); + + /* loop over certs received in certificates set, try to find one + * that will validate signature */ + for (i = 0; i < MAX_PKCS7_CERTS; i++) { + + verified = 0; + + if (pkcs7->certSz[i] == 0) + continue; + + ret = wc_ecc_init_ex(key, pkcs7->heap, pkcs7->devId); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); +#endif + return ret; + } + + InitDecodedCert(dCert, pkcs7->cert[i], pkcs7->certSz[i], pkcs7->heap); + /* not verifying, only using this to extract public key */ + ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) { + WOLFSSL_MSG("ASN ECC cert parse error"); + FreeDecodedCert(dCert); + wc_ecc_free(key); + continue; + } + + if (wc_EccPublicKeyDecode(pkcs7->publicKey, &idx, key, + pkcs7->publicKeySz) < 0) { + WOLFSSL_MSG("ASN ECC key decode error"); + FreeDecodedCert(dCert); + wc_ecc_free(key); + continue; + } + + #ifdef WOLFSSL_ASYNC_CRYPT + do { + ret = wc_AsyncWait(ret, &key->asyncDev, + WC_ASYNC_FLAG_CALL_AGAIN); + #endif + if (ret >= 0) { + ret = wc_ecc_verify_hash(sig, sigSz, hash, hashSz, &res, key); + } + #ifdef WOLFSSL_ASYNC_CRYPT + } while (ret == WC_PENDING_E); + #endif + + FreeDecodedCert(dCert); + wc_ecc_free(key); + + if (ret == 0 && res == 1) { + /* found signer that successfully verified signature */ + verified = 1; + break; + } + } + + if (verified == 0) { + ret = SIG_VERIFY_E; + } + +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(key, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(dCert, pkcs7->heap, DYNAMIC_TYPE_DCERT); +#endif + + return ret; +} + +#endif /* HAVE_ECC */ + + +/* build SignedData digest, both in PKCS#7 DigestInfo format and + * as plain digest for CMS. + * + * pkcs7 - pointer to initialized PKCS7 struct + * signedAttrib - signed attributes + * signedAttribSz - size of signedAttrib, octets + * pkcs7Digest - [OUT] PKCS#7 DigestInfo + * pkcs7DigestSz - [IN/OUT] size of pkcs7Digest + * plainDigest - [OUT] pointer to plain digest, offset into pkcs7Digest + * plainDigestSz - [OUT] size of digest at plainDigest + * + * returns 0 on success, negative on error */ +static int wc_PKCS7_BuildSignedDataDigest(PKCS7* pkcs7, byte* signedAttrib, + word32 signedAttribSz, byte* pkcs7Digest, + word32* pkcs7DigestSz, byte** plainDigest, + word32* plainDigestSz, + const byte* hashBuf, word32 hashBufSz) +{ + int ret = 0, digIdx = 0; + word32 attribSetSz = 0, hashSz = 0; + byte attribSet[MAX_SET_SZ]; + byte digest[WC_MAX_DIGEST_SIZE]; + byte digestInfoSeq[MAX_SEQ_SZ]; + byte digestStr[MAX_OCTET_STR_SZ]; + byte algoId[MAX_ALGO_SZ]; + word32 digestInfoSeqSz, digestStrSz, algoIdSz; +#ifdef WOLFSSL_SMALL_STACK + byte* digestInfo; +#else + byte digestInfo[MAX_PKCS7_DIGEST_SZ]; +#endif + + wc_HashAlg hash; + enum wc_HashType hashType; + + /* check arguments */ + if (pkcs7 == NULL || pkcs7Digest == NULL || + pkcs7DigestSz == NULL || plainDigest == NULL) { + return BAD_FUNC_ARG; + } + + hashType = wc_OidGetHash(pkcs7->hashOID); + ret = wc_HashGetDigestSize(hashType); + if (ret < 0) + return ret; + hashSz = ret; + + if (signedAttribSz > 0) { + if (signedAttrib == NULL) + return BAD_FUNC_ARG; + } + else { + if (hashBuf && hashBufSz > 0) { + if (hashSz != hashBufSz) + return BAD_FUNC_ARG; + } + else if (pkcs7->content == NULL) + return BAD_FUNC_ARG; + } + +#ifdef WOLFSSL_SMALL_STACK + digestInfo = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (digestInfo == NULL) + return MEMORY_E; +#endif + + XMEMSET(pkcs7Digest, 0, *pkcs7DigestSz); + XMEMSET(digest, 0, WC_MAX_DIGEST_SIZE); + XMEMSET(digestInfo, 0, MAX_PKCS7_DIGEST_SZ); - if (contentType != DATA) { - WOLFSSL_MSG("PKCS#7 inner input not of type Data"); - return PKCS7_OID_E; + + /* calculate digest */ + if (hashBuf && hashBufSz > 0 && signedAttribSz == 0) { + XMEMCPY(digest, hashBuf, hashBufSz); } + else { + ret = wc_HashInit(&hash, hashType); + if (ret < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + + if (signedAttribSz > 0) { + attribSetSz = SetSet(signedAttribSz, attribSet); + + /* calculate digest */ + ret = wc_HashUpdate(&hash, hashType, attribSet, attribSetSz); + if (ret == 0) + ret = wc_HashUpdate(&hash, hashType, signedAttrib, signedAttribSz); + if (ret == 0) + ret = wc_HashFinal(&hash, hashType, digest); + } else { + ret = wc_HashUpdate(&hash, hashType, pkcs7->content, pkcs7->contentSz); + if (ret == 0) + ret = wc_HashFinal(&hash, hashType, digest); + } + + wc_HashFree(&hash, hashType); + if (ret < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + } + + /* Set algoID, with NULL attributes */ + algoIdSz = SetAlgoID(pkcs7->hashOID, algoId, oidHashType, 0); + + digestStrSz = SetOctetString(hashSz, digestStr); + digestInfoSeqSz = SetSequence(algoIdSz + digestStrSz + hashSz, + digestInfoSeq); + + XMEMCPY(digestInfo + digIdx, digestInfoSeq, digestInfoSeqSz); + digIdx += digestInfoSeqSz; + XMEMCPY(digestInfo + digIdx, algoId, algoIdSz); + digIdx += algoIdSz; + XMEMCPY(digestInfo + digIdx, digestStr, digestStrSz); + digIdx += digestStrSz; + XMEMCPY(digestInfo + digIdx, digest, hashSz); + digIdx += hashSz; + + XMEMCPY(pkcs7Digest, digestInfo, digIdx); + *pkcs7DigestSz = digIdx; + + /* set plain digest pointer */ + *plainDigest = pkcs7Digest + digIdx - hashSz; + *plainDigestSz = hashSz; + +#ifdef WOLFSSL_SMALL_STACK + XFREE(digestInfo, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return 0; +} - if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + +/* Verifies CMS/PKCS7 SignedData content digest matches that which is + * included in the messageDigest signed attribute. Only called when + * signed attributes are present, otherwise original signature verification + * is done over content. + * + * pkcs7 - pointer to initialized PKCS7 struct + * hashBuf - pointer to user-provided hash buffer, used with + * wc_PKCS7_VerifySignedData_ex() + * hashBufSz - size of hashBuf, octets + * + * return 0 on success, negative on error */ +static int wc_PKCS7_VerifyContentMessageDigest(PKCS7* pkcs7, + const byte* hashBuf, + word32 hashSz) +{ + int ret = 0, digestSz = 0, innerAttribSz = 0; + word32 idx = 0; + byte* digestBuf = NULL; +#ifdef WOLFSSL_SMALL_STACK + byte* digest = NULL; +#else + byte digest[MAX_PKCS7_DIGEST_SZ]; +#endif + PKCS7DecodedAttrib* attrib; + enum wc_HashType hashType; + + /* messageDigest OID (1.2.840.113549.1.9.4) */ + const byte mdOid[] = + { 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x09, 0x04 }; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + if ((pkcs7->content == NULL || pkcs7->contentSz == 0) && + (hashBuf == NULL || hashSz == 0)) { + WOLFSSL_MSG("SignedData bundle has no content or hash to verify"); + return BAD_FUNC_ARG; + } + + /* lookup messageDigest attribute */ + attrib = findAttrib(pkcs7, mdOid, sizeof(mdOid)); + if (attrib == NULL) { + WOLFSSL_MSG("messageDigest attribute not in bundle, must be when " + "signed attribs are present"); return ASN_PARSE_E; + } - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + /* advance past attrib->value ASN.1 header and length */ + if (attrib->value == NULL || attrib->valueSz == 0) return ASN_PARSE_E; - if (pkiMsg[idx++] != ASN_OCTET_STRING) + if (attrib->value[idx++] != ASN_OCTET_STRING) return ASN_PARSE_E; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + if (GetLength(attrib->value, &idx, &innerAttribSz, attrib->valueSz) < 0) return ASN_PARSE_E; - /* Save the inner data as the content. */ - if (length > 0) { - /* Local pointer for calculating hashes later */ - pkcs7->content = content = &pkiMsg[idx]; - pkcs7->contentSz = contentSz = length; - idx += length; + /* get hash type and size */ + hashType = wc_OidGetHash(pkcs7->hashOID); + if (hashType == WC_HASH_TYPE_NONE) { + WOLFSSL_MSG("Error getting hash type for PKCS7 content verification"); + return BAD_FUNC_ARG; } - /* Get the implicit[0] set of certificates */ - if (pkiMsg[idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { - idx++; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + /* build content hash if needed, or use existing hash value */ + if (hashBuf == NULL) { - if (length > 0) { - /* At this point, idx is at the first certificate in - * a set of certificates. There may be more than one, - * or none, or they may be a PKCS 6 extended - * certificate. We want to save the first cert if it - * is X.509. */ +#ifdef WOLFSSL_SMALL_STACK + digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (digest == NULL) + return MEMORY_E; +#endif + XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); - word32 certIdx = idx; + ret = wc_Hash(hashType, pkcs7->content, pkcs7->contentSz, digest, + MAX_PKCS7_DIGEST_SZ); + if (ret < 0) { + WOLFSSL_MSG("Error hashing PKCS7 content for verification"); +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } - if (pkiMsg[certIdx++] == (ASN_CONSTRUCTED | ASN_SEQUENCE)) { - if (GetLength(pkiMsg, &certIdx, &certSz, pkiMsgSz) < 0) - return ASN_PARSE_E; + digestBuf = digest; + digestSz = wc_HashGetDigestSize(hashType); + if (digestSz < 0) { + WOLFSSL_MSG("Invalid hash type"); +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return digestSz; + } + } else { + + /* user passed in pre-computed hash */ + digestBuf = (byte*)hashBuf; + digestSz = (int)hashSz; + } + + /* compare generated to hash in messageDigest attribute */ + if ((innerAttribSz != digestSz) || + (XMEMCMP(attrib->value + idx, digestBuf, (word32)digestSz) != 0)) { + WOLFSSL_MSG("Content digest does not match messageDigest attrib value"); +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return SIG_VERIFY_E; + } + + if (hashBuf == NULL) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + } - cert = &pkiMsg[idx]; - certSz += (certIdx - idx); + return 0; +} + + +/* verifies SignedData signature, over either PKCS#7 DigestInfo or + * content digest. + * + * pkcs7 - pointer to initialized PKCS7 struct + * sig - signature to verify + * sigSz - size of sig + * signedAttrib - signed attributes, or null if empty + * signedAttribSz - size of signedAttributes + * + * return 0 on success, negative on error */ +static int wc_PKCS7_SignedDataVerifySignature(PKCS7* pkcs7, byte* sig, + word32 sigSz, byte* signedAttrib, + word32 signedAttribSz, + const byte* hashBuf, word32 hashSz) +{ + int ret = 0; + word32 plainDigestSz = 0, pkcs7DigestSz; + byte* plainDigest = NULL; /* offset into pkcs7Digest */ +#ifdef WOLFSSL_SMALL_STACK + byte* pkcs7Digest; +#else + byte pkcs7Digest[MAX_PKCS7_DIGEST_SZ]; +#endif + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + /* allocate space to build hash */ + pkcs7DigestSz = MAX_PKCS7_DIGEST_SZ; +#ifdef WOLFSSL_SMALL_STACK + pkcs7Digest = (byte*)XMALLOC(pkcs7DigestSz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (pkcs7Digest == NULL) + return MEMORY_E; +#endif + + XMEMSET(pkcs7Digest, 0, pkcs7DigestSz); + + /* verify signed attrib digest matches that of content */ + if (signedAttrib != NULL) { + ret = wc_PKCS7_VerifyContentMessageDigest(pkcs7, hashBuf, hashSz); + if (ret != 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + } + + /* build hash to verify against */ + ret = wc_PKCS7_BuildSignedDataDigest(pkcs7, signedAttrib, + signedAttribSz, pkcs7Digest, + &pkcs7DigestSz, &plainDigest, + &plainDigestSz, hashBuf, hashSz); + if (ret < 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; + } + + /* If no certificates are available then store the signature and hash for + * user to verify. Make sure that different return value than success is + * returned because the signature was not verified here. */ + if (ret == 0) { + byte haveCert = 0; + int i; + + for (i = 0; i < MAX_PKCS7_CERTS; i++) { + if (pkcs7->certSz[i] == 0) + continue; + haveCert = 1; + } + + if (!haveCert) { + WOLFSSL_MSG("No certificates in bundle to verify signature"); + + /* store signature */ + XFREE(pkcs7->signature, pkcs7->heap, DYNAMIC_TYPE_SIGNATURE); + pkcs7->signature = NULL; + pkcs7->signatureSz = 0; + pkcs7->signature = (byte*)XMALLOC(sigSz, pkcs7->heap, + DYNAMIC_TYPE_SIGNATURE); + if (pkcs7->signature == NULL) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; + } + XMEMCPY(pkcs7->signature, sig, sigSz); + pkcs7->signatureSz = sigSz; + + /* store plain digest (CMS and ECC) */ + XFREE(pkcs7->plainDigest, pkcs7->heap, DYNAMIC_TYPE_DIGEST); + pkcs7->plainDigest = NULL; + pkcs7->plainDigestSz = 0; + pkcs7->plainDigest = (byte*)XMALLOC(plainDigestSz, pkcs7->heap, + DYNAMIC_TYPE_DIGEST); + if (pkcs7->plainDigest == NULL) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; } - wc_PKCS7_InitWithCert(pkcs7, cert, certSz); + XMEMCPY(pkcs7->plainDigest, plainDigest, plainDigestSz); + pkcs7->plainDigestSz = plainDigestSz; + + /* store pkcs7 digest (default RSA) */ + XFREE(pkcs7->pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_DIGEST); + pkcs7->pkcs7Digest = NULL; + pkcs7->pkcs7DigestSz = 0; + pkcs7->pkcs7Digest = (byte*)XMALLOC(pkcs7DigestSz, pkcs7->heap, + DYNAMIC_TYPE_DIGEST); + if (pkcs7->pkcs7Digest == NULL) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return MEMORY_E; + } + XMEMCPY(pkcs7->pkcs7Digest, pkcs7Digest, pkcs7DigestSz); + pkcs7->pkcs7DigestSz = pkcs7DigestSz; + + #ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return PKCS7_SIGNEEDS_CHECK; } - idx += length; } - /* Get the implicit[1] set of crls */ - if (pkiMsg[idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { - idx++; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; - /* Skip the set */ - idx += length; + + switch (pkcs7->publicKeyOID) { + +#ifndef NO_RSA + case RSAk: + ret = wc_PKCS7_RsaVerify(pkcs7, sig, sigSz, pkcs7Digest, + pkcs7DigestSz); + if (ret < 0) { + WOLFSSL_MSG("PKCS#7 verification failed, trying CMS"); + ret = wc_PKCS7_RsaVerify(pkcs7, sig, sigSz, plainDigest, + plainDigestSz); + } + break; +#endif + +#ifdef HAVE_ECC + case ECDSAk: + ret = wc_PKCS7_EcdsaVerify(pkcs7, sig, sigSz, plainDigest, + plainDigestSz); + break; +#endif + + default: + WOLFSSL_MSG("Unsupported public key type"); + ret = BAD_FUNC_ARG; } - /* Get the set of signerInfos */ - if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; +#ifdef WOLFSSL_SMALL_STACK + XFREE(pkcs7Digest, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return ret; +} - if (length > 0) { - /* Get the sequence of the first signerInfo */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; - /* Get the version */ - if (GetMyVersion(pkiMsg, &idx, &version) < 0) +/* set correct public key OID based on signature OID, stores in + * pkcs7->publicKeyOID and returns same value */ +static int wc_PKCS7_SetPublicKeyOID(PKCS7* pkcs7, int sigOID) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + pkcs7->publicKeyOID = 0; + + switch (sigOID) { + + #ifndef NO_RSA + /* RSA signature types */ + case CTC_MD2wRSA: + case CTC_MD5wRSA: + case CTC_SHAwRSA: + case CTC_SHA224wRSA: + case CTC_SHA256wRSA: + case CTC_SHA384wRSA: + case CTC_SHA512wRSA: + pkcs7->publicKeyOID = RSAk; + break; + + /* if sigOID is already RSAk */ + case RSAk: + pkcs7->publicKeyOID = sigOID; + break; + #endif + + #ifndef NO_DSA + /* DSA signature types */ + case CTC_SHAwDSA: + pkcs7->publicKeyOID = DSAk; + break; + + /* if sigOID is already DSAk */ + case DSAk: + pkcs7->publicKeyOID = sigOID; + break; + #endif + + #ifdef HAVE_ECC + /* ECDSA signature types */ + case CTC_SHAwECDSA: + case CTC_SHA224wECDSA: + case CTC_SHA256wECDSA: + case CTC_SHA384wECDSA: + case CTC_SHA512wECDSA: + pkcs7->publicKeyOID = ECDSAk; + break; + + /* if sigOID is already ECDSAk */ + case ECDSAk: + pkcs7->publicKeyOID = sigOID; + break; + #endif + + default: + WOLFSSL_MSG("Unsupported public key algorithm"); + return ASN_SIG_KEY_E; + } + + return pkcs7->publicKeyOID; +} + + +/* Parses through the attributes and adds them to the PKCS7 structure + * Creates dynamic attribute structures that are free'd with calling + * wc_PKCS7_Free() + * + * NOTE: An attribute has the ASN1 format of + ** Sequence + ****** Object ID + ****** Set + ********** {PritnableString, UTCTime, OCTET STRING ...} + * + * pkcs7 the PKCS7 structure to put the parsed attributes into + * in buffer holding all attributes + * inSz size of in buffer + * + * returns the number of attributes parsed on success + */ +static int wc_PKCS7_ParseAttribs(PKCS7* pkcs7, byte* in, int inSz) +{ + int found = 0; + word32 idx = 0; + word32 oid; + + if (pkcs7 == NULL || in == NULL || inSz < 0) { + return BAD_FUNC_ARG; + } + + while (idx < (word32)inSz) { + int length = 0; + int oidIdx; + PKCS7DecodedAttrib* attrib; + + if (GetSequence(in, &idx, &length, inSz) < 0) return ASN_PARSE_E; - if (version != 1) { - WOLFSSL_MSG("PKCS#7 signerInfo needs to be of version 1"); - return ASN_VERSION_E; + attrib = (PKCS7DecodedAttrib*)XMALLOC(sizeof(PKCS7DecodedAttrib), + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (attrib == NULL) { + return MEMORY_E; } + XMEMSET(attrib, 0, sizeof(PKCS7DecodedAttrib)); - /* Get the sequence of IssuerAndSerialNumber */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + oidIdx = idx; + if (GetObjectId(in, &idx, &oid, oidIgnoreType, inSz) + < 0) { + XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ASN_PARSE_E; + } + attrib->oidSz = idx - oidIdx; + attrib->oid = (byte*)XMALLOC(attrib->oidSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (attrib->oid == NULL) { + XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + XMEMCPY(attrib->oid, in + oidIdx, attrib->oidSz); - /* Skip it */ - idx += length; + /* Get Set that contains the printable string value */ + if (GetSet(in, &idx, &length, inSz) < 0) { + XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } - /* Get the sequence of digestAlgorithm */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + if ((inSz - idx) < (word32)length) { + XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ASN_PARSE_E; + } - /* Skip it */ + attrib->valueSz = (word32)length; + attrib->value = (byte*)XMALLOC(attrib->valueSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (attrib->value == NULL) { + XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + XMEMCPY(attrib->value, in + idx, attrib->valueSz); idx += length; + /* store attribute in linked list */ + if (pkcs7->decodedAttrib != NULL) { + attrib->next = pkcs7->decodedAttrib; + pkcs7->decodedAttrib = attrib; + } else { + pkcs7->decodedAttrib = attrib; + } + found++; + } + + return found; +} + + +/* option to turn off support for degenerate cases + * flag 0 turns off support + * flag 1 turns on support + * + * by default support for SignedData degenerate cases is on + */ +void wc_PKCS7_AllowDegenerate(PKCS7* pkcs7, word16 flag) +{ + if (pkcs7) { + if (flag) { /* flag of 1 turns on support for degenerate */ + pkcs7->noDegenerate = 0; + } + else { /* flag of 0 turns off support */ + pkcs7->noDegenerate = 1; + } + } +} + +/* Parses through a signerInfo set. Reads buffer "in" from "idxIn" to "idxIn" + + * length treating the current "idxIn" plus the length of set as max possible + * index. + * + * In the case that signed attributes are found "signedAttrib" gets set to point + * at their location in the buffer "in". Also in this case signedAttribSz gets + * set to the size of the signedAttrib buffer. + * + * returns 0 on success + */ +static int wc_PKCS7_ParseSignerInfo(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idxIn, int degenerate, byte** signedAttrib, int* signedAttribSz) +{ + int ret = 0; + int length; + int version; + word32 sigOID = 0, hashOID = 0; + word32 idx = *idxIn, localIdx; + byte tag; + + WOLFSSL_ENTER("wc_PKCS7_ParseSignerInfo"); + /* require a signer if degenerate case not allowed */ + if (inSz == 0 && pkcs7->noDegenerate == 1) { + WOLFSSL_MSG("Set to not allow degenerate cases"); + return PKCS7_NO_SIGNER_E; + } + + if (inSz == 0 && degenerate == 0) { + WOLFSSL_MSG("PKCS7 signers expected"); + return PKCS7_NO_SIGNER_E; + } + + /* not a degenerate case and there is elements in the set */ + if (inSz > 0 && degenerate == 0) { + ret = wc_PKCS7_SignerInfoNew(pkcs7); + + /* Get the sequence of the first signerInfo */ + if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + + /* Get the version */ + if (ret == 0 && GetMyVersion(in, &idx, &version, inSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0) { + pkcs7->signerInfo->version = version; + } + + if (ret == 0 && version == 1) { + /* Get the sequence of IssuerAndSerialNumber */ + if (GetSequence(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0) { + ret = wc_PKCS7_SignerInfoSetSID(pkcs7, in + idx, length); + idx += length; + } + + } else if (ret == 0 && version == 3) { + /* Get the sequence of SubjectKeyIdentifier */ + if (idx + 1 > inSz) + ret = BUFFER_E; + + localIdx = idx; + if (ret == 0 && GetASNTag(in, &localIdx, &tag, inSz) == 0 && + tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + idx++; + + if (GetLength(in, &idx, &length, inSz) <= 0) + ret = ASN_PARSE_E; + + if (ret == 0 && idx + 1 > inSz) + ret = BUFFER_E; + + if (ret == 0 && GetASNTag(in, &idx, &tag, inSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && tag != ASN_OCTET_STRING) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + } + else { + /* check if SKID with ASN_CONTEXT_SPECIFIC otherwise in version + * 3 try to get issuerAndSerial */ + localIdx = idx; + if (GetASNTag(in, &localIdx, &tag, inSz) == 0 && + tag == ASN_CONTEXT_SPECIFIC) { + idx++; + if (ret == 0 && GetLength(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + } + else { + if (pkcs7->version != 3) { + WOLFSSL_MSG("Unexpected signer info found with version"); + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetSequence(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + } + } + + if (ret == 0) { + ret = wc_PKCS7_SignerInfoSetSID(pkcs7, in + idx, length); + idx += length; + } + + } else { + WOLFSSL_MSG("PKCS#7 signerInfo version must be 1 or 3"); + ret = ASN_VERSION_E; + } + + /* Get the sequence of digestAlgorithm */ + if (ret == 0 && GetAlgoId(in, &idx, &hashOID, oidHashType, inSz) < 0) { + ret = ASN_PARSE_E; + } + pkcs7->hashOID = (int)hashOID; + /* Get the IMPLICIT[0] SET OF signedAttributes */ - if (pkiMsg[idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + localIdx = idx; + if (ret == 0 && GetASNTag(in, &localIdx, &tag, inSz) == 0 && + tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { idx++; - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (GetLength(in, &idx, &length, inSz) < 0) + ret = ASN_PARSE_E; + + /* save pointer and length */ + *signedAttrib = &in[idx]; + *signedAttribSz = length; + + if (ret == 0 && wc_PKCS7_ParseAttribs(pkcs7, *signedAttrib, + *signedAttribSz) < 0) { + WOLFSSL_MSG("Error parsing signed attributes"); + ret = ASN_PARSE_E; + } idx += length; } - /* Get the sequence of digestEncryptionAlgorithm */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + /* Get digestEncryptionAlgorithm */ + if (ret == 0 && GetAlgoId(in, &idx, &sigOID, oidSigType, inSz) < 0) { + ret = ASN_PARSE_E; + } - /* Skip it */ - idx += length; + /* store public key type based on digestEncryptionAlgorithm */ + if (ret == 0) { + ret = wc_PKCS7_SetPublicKeyOID(pkcs7, sigOID); + if (ret < 0) { + WOLFSSL_MSG("Failed to set public key OID from signature"); + } + else { + /* if previous return was positive then was success */ + ret = 0; + } + } + } - /* Get the signature */ - if (pkiMsg[idx] == ASN_OCTET_STRING) { - idx++; + /* update index on success */ + if (ret == 0) { + *idxIn = idx; + } - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + return ret; +} - /* save pointer and length */ - sig = &pkiMsg[idx]; - sigSz = length; +/* Finds the certificates in the message and saves it. By default allows + * degenerate cases which can have no signer. + * + * By default expects type SIGNED_DATA (SignedData) which can have any number of + * elements in signerInfos collection, including zero. (RFC2315 section 9.1) + * When adding support for the case of SignedAndEnvelopedData content types a + * signer is required. In this case the PKCS7 flag noDegenerate could be set. + */ +static int PKCS7_VerifySignedData(PKCS7* pkcs7, const byte* hashBuf, + word32 hashSz, byte* in, word32 inSz, + byte* in2, word32 in2Sz) +{ + word32 idx, maxIdx = inSz, outerContentType, contentTypeSz = 0, totalSz = 0; + int length = 0, version = 0, ret = 0; + byte* content = NULL; + byte* contentDynamic = NULL; + byte* sig = NULL; + byte* cert = NULL; + byte* signedAttrib = NULL; + byte* contentType = NULL; + int contentSz = 0, sigSz = 0, certSz = 0, signedAttribSz = 0; + word32 localIdx, start; + byte degenerate = 0; + byte detached = 0; + byte tag = 0; +#ifdef ASN_BER_TO_DER + byte* der; +#endif + int multiPart = 0, keepContent; + int contentLen = 0; + + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; +#ifndef NO_PKCS7_STREAM + word32 stateIdx = 0; + long rc; +#endif + + byte* pkiMsg2 = in2; + word32 pkiMsg2Sz = in2Sz; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + +#ifndef NO_PKCS7_STREAM + /* allow for 0 size inputs with stream mode */ + if (pkiMsg == NULL && pkiMsgSz > 0) + return BAD_FUNC_ARG; + +#else + if (pkiMsg == NULL || pkiMsgSz == 0) + return BAD_FUNC_ARG; +#endif + + if ((hashSz > 0 && hashBuf == NULL) || (pkiMsg2Sz > 0 && pkiMsg2 == NULL)) { + return BAD_FUNC_ARG; + } + idx = 0; + +#ifdef ASN_BER_TO_DER + if (pkcs7->derSz > 0 && pkcs7->der) { + pkiMsg = in = pkcs7->der; + } +#endif + +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { + return ret; + } + } +#endif + + switch (pkcs7->state) { + case WC_PKCS7_START: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_SEQ_SZ + + MAX_VERSION_SZ + MAX_SEQ_SZ + MAX_LENGTH_SZ + + ASN_TAG_SZ + MAX_OID_SZ + MAX_SEQ_SZ, + &pkiMsg, &idx)) != 0) { + break; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_SEQ_PEEK, in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (pkcs7->stream->length > 0)? pkcs7->stream->length :inSz; + #endif + + /* determine total message size */ + totalSz = pkiMsgSz; + if (pkiMsg2 && pkiMsg2Sz > 0) { + totalSz += pkiMsg2Sz + pkcs7->contentSz; + } + + /* Get the contentInfo sequence */ + if (ret == 0 && GetSequence_ex(pkiMsg, &idx, &length, totalSz, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && length == 0 && pkiMsg[idx-1] == 0x80) { + #ifdef ASN_BER_TO_DER + word32 len = 0; + + ret = wc_BerToDer(pkiMsg, pkiMsgSz, NULL, &len); + if (ret != LENGTH_ONLY_E) + return ret; + pkcs7->der = (byte*)XMALLOC(len, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->der == NULL) + return MEMORY_E; + ret = wc_BerToDer(pkiMsg, pkiMsgSz, pkcs7->der, &len); + if (ret < 0) + return ret; + + pkiMsg = in = pkcs7->der; + pkiMsgSz = pkcs7->derSz = len; + idx = 0; + if (GetSequence_ex(pkiMsg, &idx, &length, pkiMsgSz, + NO_USER_CHECK) < 0) + return ASN_PARSE_E; + + #ifndef NO_PKCS7_STREAM + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_SEQ_PEEK, + pkiMsg, pkiMsgSz); + if (rc < 0) { + ret = (int)rc; + break; + } + #endif + #else + ret = BER_INDEF_E; + #endif + } + + /* Get the contentInfo contentType */ + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &outerContentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && outerContentType != SIGNED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type SignedData"); + ret = PKCS7_OID_E; + } + + /* get the ContentInfo content */ + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, totalSz) != 0) + ret = ASN_PARSE_E; + + if (ret == 0 && tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength_ex(pkiMsg, &idx, &length, totalSz, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + + /* Get the signedData sequence */ + if (ret == 0 && GetSequence_ex(pkiMsg, &idx, &length, totalSz, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + + /* Get the version */ + if (ret == 0 && GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + + /* version 1 follows RFC 2315 */ + /* version 3 follows RFC 4108 */ + if (ret == 0 && (version != 1 && version != 3)) { + WOLFSSL_MSG("PKCS#7 signedData needs to be version 1 or 3"); + ret = ASN_VERSION_E; + } + pkcs7->version = version; + + /* Get the set of DigestAlgorithmIdentifiers */ + if (ret == 0 && GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + /* Skip the set. */ idx += length; + degenerate = (length == 0)? 1 : 0; + if (pkcs7->noDegenerate == 1 && degenerate == 1) { + ret = PKCS7_NO_SIGNER_E; + } + + if (ret != 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, &idx)) != 0) { + break; + } + if (pkiMsg2 && pkiMsg2Sz > 0) { + pkcs7->stream->maxLen += pkiMsg2Sz + pkcs7->contentSz; + } + wc_PKCS7_StreamStoreVar(pkcs7, totalSz, 0, 0); + #endif + + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE2); + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE2: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz + in2Sz, + MAX_SEQ_SZ + MAX_OID_SZ + ASN_TAG_SZ + MAX_LENGTH_SZ + + ASN_TAG_SZ + MAX_LENGTH_SZ, &pkiMsg, &idx)) != 0) { + break; + } + + wc_PKCS7_StreamGetVar(pkcs7, &totalSz, 0, 0); + if (pkcs7->stream->length > 0) + pkiMsgSz = pkcs7->stream->length; + #ifdef ASN_BER_TO_DER + else if (pkcs7->der) + pkiMsgSz = pkcs7->derSz; + #endif + else + pkiMsgSz = inSz; + + #endif + /* Get the inner ContentInfo sequence */ + if (GetSequence_ex(pkiMsg, &idx, &length, pkiMsgSz, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + + /* Get the inner ContentInfo contentType */ + if (ret == 0) { + word32 tmpIdx = idx; + + if (GetASNObjectId(pkiMsg, &idx, &length, pkiMsgSz) != 0) + ret = ASN_PARSE_E; + + contentType = pkiMsg + tmpIdx; + contentTypeSz = length + (idx - tmpIdx); + + idx += length; + } + + if (ret != 0) + break; + + /* Check for content info, it could be omitted when degenerate */ + localIdx = idx; + ret = 0; + if (localIdx + 1 > pkiMsgSz) { + ret = BUFFER_E; + break; + } + + if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) != 0) + ret = ASN_PARSE_E; + + if (ret == 0 && tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength_ex(pkiMsg, &localIdx, &length, pkiMsgSz, + NO_USER_CHECK) <= 0) + ret = ASN_PARSE_E; + + if (localIdx >= pkiMsgSz) { + ret = BUFFER_E; + } + + /* get length of content in the case that there is multiple parts */ + if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && tag == (ASN_OCTET_STRING | ASN_CONSTRUCTED)) { + multiPart = 1; + + /* Get length of all OCTET_STRINGs. */ + if (GetLength_ex(pkiMsg, &localIdx, &contentLen, pkiMsgSz, + NO_USER_CHECK) < 0) + 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) + != 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. */ + if (localIdx - start + length == (word32)contentLen) + multiPart = 0; + localIdx = start; + } + } + + /* 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 */ + if (ret == 0) { + /* support using header and footer without content */ + if (pkiMsg2 && pkiMsg2Sz > 0 && hashBuf && hashSz > 0) { + localIdx = 0; + } + idx = localIdx; + } + else { + + /* if pkcs7->content and pkcs7->contentSz are set, try to + process as a detached signature */ + if (!degenerate && + (pkcs7->content != NULL && pkcs7->contentSz != 0)) { + detached = 1; + } + + if (!degenerate && !detached && ret != 0) + break; + + length = 0; /* no content to read */ + pkiMsg2 = pkiMsg; + pkiMsg2Sz = pkiMsgSz; + } + + #ifndef NO_PKCS7_STREAM + /* save detached flag value */ + pkcs7->stream->detached = detached; + + /* save contentType */ + pkcs7->stream->nonce = (byte*)XMALLOC(contentTypeSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->nonce == NULL) { + ret = MEMORY_E; + break; + } + else { + pkcs7->stream->nonceSz = contentTypeSz; + XMEMCPY(pkcs7->stream->nonce, contentType, contentTypeSz); + } + + /* content expected? */ + if ((ret == 0 && length > 0) && + !(pkiMsg2 && pkiMsg2Sz > 0 && hashBuf && hashSz > 0)) { + pkcs7->stream->expected = length + ASN_TAG_SZ + MAX_LENGTH_SZ; + } + else { + pkcs7->stream->expected = ASN_TAG_SZ + MAX_LENGTH_SZ; + } + + if (pkcs7->stream->expected > (pkcs7->stream->maxLen - idx)) { + pkcs7->stream->expected = pkcs7->stream->maxLen - idx; + } + + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, &idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, pkiMsg2Sz, localIdx, length); + + /* content length is in multiple parts */ + if (multiPart) { + pkcs7->stream->expected = contentLen + ASN_TAG_SZ; + } + pkcs7->stream->multi = multiPart; + + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE3); + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE3: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz + in2Sz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + break; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + pkiMsg, pkiMsgSz); + if (rc < 0) { + ret = (int)rc; + break; + } + #ifdef ASN_BER_TO_DER + if (pkcs7->derSz != 0) + pkiMsgSz = pkcs7->derSz; + else + #endif + pkiMsgSz = (word32)rc; + wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, (int*)&localIdx, &length); + + if (pkcs7->stream->length > 0) { + localIdx = 0; + } + multiPart = pkcs7->stream->multi; + detached = pkcs7->stream->detached; + maxIdx = idx + pkcs7->stream->expected; + #endif + + /* Break out before content because it can be optional in degenerate + * cases. */ + if (ret != 0 && !degenerate) + break; + + /* get parts of content */ + if (ret == 0 && multiPart) { + int i = 0; + keepContent = !(pkiMsg2 && pkiMsg2Sz > 0 && hashBuf && hashSz > 0); + + if (keepContent) { + /* Create a buffer to hold content of OCTET_STRINGs. */ + pkcs7->contentDynamic = (byte*)XMALLOC(contentLen, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->contentDynamic == NULL) + ret = MEMORY_E; + } + + start = localIdx; + /* Use the data from each OCTET_STRING. */ + while (ret == 0 && localIdx < start + contentLen) { + if (GetASNTag(pkiMsg, &localIdx, &tag, totalSz) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && tag != ASN_OCTET_STRING) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(pkiMsg, &localIdx, &length, totalSz) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && length + localIdx > start + contentLen) + ret = ASN_PARSE_E; + + if (ret == 0) { + if (keepContent) { + XMEMCPY(pkcs7->contentDynamic + i, pkiMsg + localIdx, + length); + } + i += length; + localIdx += length; + } + } + localIdx = start; /* reset for sanity check, increment later */ + length = i; + } + + /* Save the inner data as the content. */ + if (ret == 0 && length > 0) { + contentSz = length; + + /* support using header and footer without content */ + if (pkiMsg2 && pkiMsg2Sz > 0 && hashBuf && hashSz > 0) { + /* Content not provided, use provided pkiMsg2 footer */ + content = NULL; + localIdx = 0; + if (contentSz != (int)pkcs7->contentSz) { + WOLFSSL_MSG("Data signed does not match contentSz provided"); + ret = BUFFER_E; + } + } + else { + if ((word32)length > pkiMsgSz - localIdx) { + ret = BUFFER_E; + } + + /* Content pointer for calculating hashes later */ + if (ret == 0 && !multiPart) { + content = &pkiMsg[localIdx]; + } + if (ret == 0 && multiPart) { + content = pkcs7->contentDynamic; + } + + if (ret == 0) { + idx += length; + + pkiMsg2 = pkiMsg; + pkiMsg2Sz = pkiMsgSz; + #ifndef NO_PKCS7_STREAM + pkcs7->stream->varOne = pkiMsg2Sz; + pkcs7->stream->flagOne = 1; + #endif + } + } + } + else { + pkiMsg2 = pkiMsg; + pkiMsg2Sz = pkiMsgSz; + #ifndef NO_PKCS7_STREAM + pkcs7->stream->varOne = pkiMsg2Sz; + pkcs7->stream->flagOne = 1; + #endif + } + + /* If getting the content info failed with non degenerate then return the + * error case. Otherwise with a degenerate it is ok if the content + * info was omitted */ + if (!degenerate && !detached && (ret != 0)) { + break; + } + else { + ret = 0; /* reset ret state on degenerate case */ + } + + #ifndef NO_PKCS7_STREAM + /* save content */ + if (detached == 1) { + /* if detached, use content from user in pkcs7 struct */ + content = pkcs7->content; + contentSz = pkcs7->contentSz; + } + + if (content != NULL) { + XFREE(pkcs7->stream->content, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream->content = (byte*)XMALLOC(contentSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->content == NULL) { + ret = MEMORY_E; + break; + } + else { + XMEMCPY(pkcs7->stream->content, content, contentSz); + pkcs7->stream->contentSz = contentSz; + } + } + #endif /* !NO_PKCS7_STREAM */ + + /* Get the implicit[0] set of certificates */ + if (ret == 0 && idx >= pkiMsg2Sz) + ret = BUFFER_E; + + length = 0; /* set length to 0 to check if reading in any certs */ + localIdx = idx; + if (ret == 0 && GetASNTag(pkiMsg2, &localIdx, &tag, pkiMsg2Sz) == 0 + && tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + idx++; + if (GetLength_ex(pkiMsg2, &idx, &length, maxIdx, NO_USER_CHECK) + < 0) + ret = ASN_PARSE_E; + } + + if (ret != 0) { + break; + } + #ifndef NO_PKCS7_STREAM + if (in2 && in2Sz > 0 && hashBuf && hashSz > 0) { + stateIdx = idx; /* case where all data was read from in2 */ + } + + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, &idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, pkiMsg2Sz, 0, length); + if (length > 0) { + pkcs7->stream->expected = length; + } + else { + pkcs7->stream->expected = MAX_SEQ_SZ; + 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; + } + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE4); + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE4: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz + in2Sz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + break; + } + + wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, 0, &length); + if (pkcs7->stream->flagOne) { + pkiMsg2 = pkiMsg; + } + + /* restore content */ + content = pkcs7->stream->content; + contentSz = pkcs7->stream->contentSz; + + /* restore detached flag */ + detached = pkcs7->stream->detached; + + /* store certificate if needed */ + if (length > 0 && in2Sz == 0) { + /* free tmpCert if not NULL */ + XFREE(pkcs7->stream->tmpCert, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->stream->tmpCert = (byte*)XMALLOC(length, + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if ((pkiMsg2 == NULL) || (pkcs7->stream->tmpCert == NULL)) { + ret = MEMORY_E; + break; + } + XMEMCPY(pkcs7->stream->tmpCert, pkiMsg2 + idx, length); + pkiMsg2 = pkcs7->stream->tmpCert; + pkiMsg2Sz = length; + idx = 0; + } + #endif + + if (length > 0) { + /* At this point, idx is at the first certificate in + * a set of certificates. There may be more than one, + * or none, or they may be a PKCS 6 extended + * certificate. We want to save the first cert if it + * is X.509. */ + + word32 certIdx = idx; + + if (length < MAX_LENGTH_SZ + ASN_TAG_SZ) + ret = BUFFER_E; + + if (ret == 0) + ret = GetASNTag(pkiMsg2, &certIdx, &tag, pkiMsg2Sz); + + if (ret == 0 && tag == (ASN_CONSTRUCTED | ASN_SEQUENCE)) { + if (GetLength(pkiMsg2, &certIdx, &certSz, pkiMsg2Sz) < 0) + ret = ASN_PARSE_E; + + cert = &pkiMsg2[idx]; + certSz += (certIdx - idx); + if (certSz > length) { + ret = BUFFER_E; + break; + } + } + #ifdef ASN_BER_TO_DER + der = pkcs7->der; + #endif + contentDynamic = pkcs7->contentDynamic; + version = pkcs7->version; + + + if (ret == 0) { + #ifndef NO_PKCS7_STREAM + PKCS7State* stream = pkcs7->stream; + #endif + /* This will reset PKCS7 structure and then set the + * certificate */ + ret = wc_PKCS7_InitWithCert(pkcs7, cert, certSz); + #ifndef NO_PKCS7_STREAM + pkcs7->stream = stream; + #endif + } + pkcs7->contentDynamic = contentDynamic; + pkcs7->version = version; + #ifdef ASN_BER_TO_DER + pkcs7->der = der; + #endif + if (ret != 0) + break; + + /* iterate through any additional certificates */ + if (ret == 0 && MAX_PKCS7_CERTS > 0) { + int sz = 0; + int i; + + pkcs7->cert[0] = cert; + pkcs7->certSz[0] = certSz; + certIdx = idx + certSz; + + for (i = 1; i < MAX_PKCS7_CERTS && + certIdx + 1 < pkiMsg2Sz && + certIdx + 1 < (word32)length; i++) { + localIdx = certIdx; + + if (ret == 0 && GetASNTag(pkiMsg2, &certIdx, &tag, + pkiMsg2Sz) < 0) { + ret = ASN_PARSE_E; + break; + } + + if (ret == 0 && + tag == (ASN_CONSTRUCTED | ASN_SEQUENCE)) { + if (GetLength(pkiMsg2, &certIdx, &sz, + pkiMsg2Sz) < 0) { + ret = ASN_PARSE_E; + break; + } + + pkcs7->cert[i] = &pkiMsg2[localIdx]; + pkcs7->certSz[i] = sz + (certIdx - localIdx); + certIdx += sz; + } + } + } + } + idx += length; + + if (!detached) { + /* set content and size after init of PKCS7 structure */ + pkcs7->content = content; + pkcs7->contentSz = contentSz; + } + #ifndef NO_PKCS7_STREAM + else { + /* save content if detached and using streaming API */ + if (pkcs7->content != NULL) { + XFREE(pkcs7->stream->content, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + pkcs7->stream->content = (byte*)XMALLOC(pkcs7->contentSz, + pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->content == NULL) { + ret = MEMORY_E; + break; + } + else { + XMEMCPY(pkcs7->stream->content, pkcs7->content, + contentSz); + pkcs7->stream->contentSz = pkcs7->contentSz; + } + } + } + #endif + + if (ret != 0) { + break; + } + #ifndef NO_PKCS7_STREAM + /* factor in that recent idx was in cert buffer. If in2 buffer was + * used then don't advance idx. */ + if (length > 0 && pkcs7->stream->flagOne && + pkcs7->stream->length == 0) { + idx = stateIdx + idx; + if (idx > inSz) { + /* index is more than input size */ + ret = BUFFER_E; + break; + } + } + else { + stateIdx = idx; /* didn't read any from internal buffer */ + } + + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, &idx)) != 0) { + break; + } + if (pkcs7->stream->flagOne && pkcs7->stream->length > 0) { + idx = stateIdx + idx; + } + + pkcs7->stream->expected = MAX_OID_SZ + ASN_TAG_SZ + MAX_LENGTH_SZ + + MAX_SET_SZ; + + 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; + + wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, 0, 0); + wc_PKCS7_StreamStoreVar(pkcs7, pkiMsg2Sz, 0, length); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE5); + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE5: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz + in2Sz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + break; + } + wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, 0, &length); + if (pkcs7->stream->flagOne) { + pkiMsg2 = pkiMsg; + } + + /* restore content type */ + contentType = pkcs7->stream->nonce; + contentTypeSz = pkcs7->stream->nonceSz; + + maxIdx = idx + pkcs7->stream->expected; + if (maxIdx > pkiMsg2Sz) { + ret = BUFFER_E; + break; + } + stateIdx = idx; + #endif + + /* set contentType and size after init of PKCS7 structure */ + if (ret == 0 && wc_PKCS7_SetContentType(pkcs7, contentType, + contentTypeSz) < 0) + ret = ASN_PARSE_E; + + /* Get the implicit[1] set of crls */ + if (ret == 0 && idx >= maxIdx) + ret = BUFFER_E; + + localIdx = idx; + if (ret == 0 && GetASNTag(pkiMsg2, &localIdx, &tag, pkiMsg2Sz) == 0 + && tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + idx++; + if (GetLength(pkiMsg2, &idx, &length, pkiMsg2Sz) < 0) + ret = ASN_PARSE_E; + + /* Skip the set */ + idx += length; + } + + /* Get the set of signerInfos */ + if (ret == 0 && GetSet_ex(pkiMsg2, &idx, &length, maxIdx, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + + if (ret != 0) + break; + #ifndef NO_PKCS7_STREAM + if (!pkcs7->stream->flagOne) { + stateIdx = idx; /* didn't read any from internal buffer */ + } + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, &idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, pkiMsg2Sz, 0, length); + + if (in2 && in2Sz > 0 && hashBuf && hashSz > 0) { + if (length > 0) { + pkcs7->stream->expected = length; + } + else { + pkcs7->stream->expected = 0; + } + } + else { + /* last state expect the reset of the buffer */ + pkcs7->stream->expected = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; + } + + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_VERIFY_STAGE6); + FALL_THROUGH; + + case WC_PKCS7_VERIFY_STAGE6: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz + in2Sz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + break; + } + + wc_PKCS7_StreamGetVar(pkcs7, &pkiMsg2Sz, 0, &length); + if (pkcs7->stream->flagOne) { + pkiMsg2 = pkiMsg; + } + + /* restore content */ + content = pkcs7->stream->content; + contentSz = pkcs7->stream->contentSz; + #endif + + ret = wc_PKCS7_ParseSignerInfo(pkcs7, pkiMsg2, pkiMsg2Sz, &idx, + degenerate, &signedAttrib, &signedAttribSz); + + /* parse out the signature if present and verify it */ + if (ret == 0 && length > 0 && degenerate == 0) { + WOLFSSL_MSG("Parsing signature and verifying"); + if (idx >= pkiMsg2Sz) + ret = BUFFER_E; + + /* Get the signature */ + localIdx = idx; + if (ret == 0 && GetASNTag(pkiMsg2, &localIdx, &tag, + pkiMsg2Sz) == 0 && tag == ASN_OCTET_STRING) { + idx++; + + if (GetLength(pkiMsg2, &idx, &length, pkiMsg2Sz) < 0) + ret = ASN_PARSE_E; + + /* save pointer and length */ + sig = &pkiMsg2[idx]; + sigSz = length; + + idx += length; + } + + pkcs7->content = content; + pkcs7->contentSz = contentSz; + + if (ret == 0) { + ret = wc_PKCS7_SignedDataVerifySignature(pkcs7, sig, sigSz, + signedAttrib, signedAttribSz, + hashBuf, hashSz); + } + } + + if (ret < 0) + break; + + ret = 0; /* success */ + #ifndef NO_PKCS7_STREAM + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); + break; + + default: + WOLFSSL_MSG("PKCS7 Unknown verify state"); + ret = BAD_FUNC_ARG; + } + + if (ret != 0 && ret != WC_PKCS7_WANT_READ_E) { + #ifndef NO_PKCS7_STREAM + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); + } + return ret; +} + + +/* Gets a copy of the SID parsed from signerInfo. This can be called after + * wc_PKCS7_VerifySignedData has been called. SID can be SKID in version 3 case + * or issuerAndSerialNumber. + * + * return 0 on success and LENGTH_ONLY_E if just setting "outSz" for buffer + * length needed. + */ +int wc_PKCS7_GetSignerSID(PKCS7* pkcs7, byte* out, word32* outSz) +{ + if (outSz == NULL || pkcs7 == NULL) { + return BAD_FUNC_ARG; + } + + if (pkcs7->signerInfo == NULL) { + WOLFSSL_MSG("Either the bundle had no signers or" + "wc_PKCS7_VerifySignedData needs called yet"); + return PKCS7_NO_SIGNER_E; + } + + if (pkcs7->signerInfo->sidSz == 0) { + WOLFSSL_MSG("Bundle had no signer SID set"); + return PKCS7_NO_SIGNER_E; + } + + if (out == NULL) { + *outSz = pkcs7->signerInfo->sidSz; + return LENGTH_ONLY_E; + } + + if (*outSz < pkcs7->signerInfo->sidSz) { + WOLFSSL_MSG("Buffer being passed in is not large enough for SKID"); + return BUFFER_E; + } + XMEMCPY(out, pkcs7->signerInfo->sid, pkcs7->signerInfo->sidSz); + *outSz = pkcs7->signerInfo->sidSz; + return 0; +} + + +/* variant that allows computed data hash and header/foot, + * which is useful for large data signing */ +int wc_PKCS7_VerifySignedData_ex(PKCS7* pkcs7, const byte* hashBuf, + word32 hashSz, byte* pkiMsgHead, word32 pkiMsgHeadSz, byte* pkiMsgFoot, + word32 pkiMsgFootSz) +{ + return PKCS7_VerifySignedData(pkcs7, hashBuf, hashSz, + pkiMsgHead, pkiMsgHeadSz, pkiMsgFoot, pkiMsgFootSz); +} + +int wc_PKCS7_VerifySignedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) +{ + return PKCS7_VerifySignedData(pkcs7, NULL, 0, pkiMsg, pkiMsgSz, NULL, 0); +} + + +/* Generate random content encryption key, store into pkcs7->cek and + * pkcs7->cekSz. + * + * pkcs7 - pointer to initialized PKCS7 structure + * len - length of key to be generated + * + * Returns 0 on success, negative upon error */ +static int PKCS7_GenerateContentEncryptionKey(PKCS7* pkcs7, word32 len) +{ + int ret; + WC_RNG rng; + byte* tmpKey; + + if (pkcs7 == NULL || len == 0) + return BAD_FUNC_ARG; + + /* if key already exists, don't need to re-generate */ + if (pkcs7->cek != NULL && pkcs7->cekSz != 0) { + + /* if key exists, but is different size, return error */ + if (pkcs7->cekSz != len) { + WOLFSSL_MSG("Random content-encryption key size is inconsistent " + "between CMS recipients"); + return WC_KEY_SIZE_E; } - pkcs7->content = content; - pkcs7->contentSz = contentSz; + return 0; + } - { - word32 scratch = 0; - int plainSz = 0; - int digestSz = MAX_SEQ_SZ + MAX_ALGO_SZ + - MAX_OCTET_STR_SZ + SHA_DIGEST_SIZE; + /* allocate space for cek */ + tmpKey = (byte*)XMALLOC(len, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (tmpKey == NULL) + return MEMORY_E; -#ifdef WOLFSSL_SMALL_STACK - byte* digest; - RsaKey* key; + XMEMSET(tmpKey, 0, len); - digest = (byte*)XMALLOC(digestSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret != 0) { + XFREE(tmpKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } - if (digest == NULL) - return MEMORY_E; + ret = wc_RNG_GenerateBlock(&rng, tmpKey, len); + if (ret != 0) { + wc_FreeRng(&rng); + XFREE(tmpKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } - key = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (key == NULL) { - XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); - return MEMORY_E; + /* store into PKCS7, memory freed during final cleanup */ + pkcs7->cek = tmpKey; + pkcs7->cekSz = len; + + wc_FreeRng(&rng); + + return 0; +} + + +/* wrap CEK (content encryption key) with KEK, 0 on success, < 0 on error */ +static int wc_PKCS7_KeyWrap(byte* cek, word32 cekSz, byte* kek, + word32 kekSz, byte* out, word32 outSz, + int keyWrapAlgo, int direction) +{ + int ret = 0; + + if (cek == NULL || kek == NULL || out == NULL) + return BAD_FUNC_ARG; + + switch (keyWrapAlgo) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128_WRAP: + #endif + #ifdef WOLFSSL_AES_192 + case AES192_WRAP: + #endif + #ifdef WOLFSSL_AES_256 + case AES256_WRAP: + #endif + + if (direction == AES_ENCRYPTION) { + + ret = wc_AesKeyWrap(kek, kekSz, cek, cekSz, + out, outSz, NULL); + + } else if (direction == AES_DECRYPTION) { + + ret = wc_AesKeyUnWrap(kek, kekSz, cek, cekSz, + out, outSz, NULL); + } else { + WOLFSSL_MSG("Bad key un/wrap direction"); + return BAD_FUNC_ARG; + } + + if (ret <= 0) + return ret; + break; +#endif /* NO_AES */ + + default: + WOLFSSL_MSG("Unsupported key wrap algorithm"); + return BAD_KEYWRAP_ALG_E; + }; + + (void)cekSz; + (void)kekSz; + (void)outSz; + (void)direction; + return ret; +} + + +#ifdef HAVE_ECC + +/* KARI == KeyAgreeRecipientInfo (key agreement) */ +typedef struct WC_PKCS7_KARI { + DecodedCert* decoded; /* decoded recip cert */ + void* heap; /* user heap, points to PKCS7->heap */ + int devId; /* device ID for HW based private key */ + ecc_key* recipKey; /* recip key (pub | priv) */ + ecc_key* senderKey; /* sender key (pub | priv) */ + byte* senderKeyExport; /* sender ephemeral key DER */ + byte* kek; /* key encryption key */ + byte* ukm; /* OPTIONAL user keying material */ + byte* sharedInfo; /* ECC-CMS-SharedInfo ASN.1 encoded blob */ + word32 senderKeyExportSz; /* size of sender ephemeral key DER */ + word32 kekSz; /* size of key encryption key */ + word32 ukmSz; /* size of user keying material */ + word32 sharedInfoSz; /* size of ECC-CMS-SharedInfo encoded */ + byte ukmOwner; /* do we own ukm buffer? 1:yes, 0:no */ + byte direction; /* WC_PKCS7_ENCODE | WC_PKCS7_DECODE */ + byte decodedInit : 1; /* indicates decoded was initialized */ + byte recipKeyInit : 1; /* indicates recipKey was initialized */ + byte senderKeyInit : 1; /* indicates senderKey was initialized */ +} WC_PKCS7_KARI; + + +/* allocate and create new WC_PKCS7_KARI struct, + * returns struct pointer on success, NULL on failure */ +static WC_PKCS7_KARI* wc_PKCS7_KariNew(PKCS7* pkcs7, byte direction) +{ + WC_PKCS7_KARI* kari = NULL; + + if (pkcs7 == NULL) + return NULL; + + kari = (WC_PKCS7_KARI*)XMALLOC(sizeof(WC_PKCS7_KARI), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (kari == NULL) { + WOLFSSL_MSG("Failed to allocate WC_PKCS7_KARI"); + return NULL; + } + + kari->decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (kari->decoded == NULL) { + WOLFSSL_MSG("Failed to allocate DecodedCert"); + XFREE(kari, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return NULL; + } + + kari->recipKey = (ecc_key*)XMALLOC(sizeof(ecc_key), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (kari->recipKey == NULL) { + WOLFSSL_MSG("Failed to allocate recipient ecc_key"); + XFREE(kari->decoded, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kari, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return NULL; + } + + kari->senderKey = (ecc_key*)XMALLOC(sizeof(ecc_key), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (kari->senderKey == NULL) { + WOLFSSL_MSG("Failed to allocate sender ecc_key"); + XFREE(kari->recipKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kari->decoded, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kari, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return NULL; + } + + kari->senderKeyExport = NULL; + kari->senderKeyExportSz = 0; + kari->kek = NULL; + kari->kekSz = 0; + kari->ukm = NULL; + kari->ukmSz = 0; + kari->ukmOwner = 0; + kari->sharedInfo = NULL; + kari->sharedInfoSz = 0; + kari->direction = direction; + kari->decodedInit = 0; + kari->recipKeyInit = 0; + kari->senderKeyInit = 0; + + kari->heap = pkcs7->heap; + kari->devId = pkcs7->devId; + + return kari; +} + + +/* free WC_PKCS7_KARI struct, return 0 on success */ +static int wc_PKCS7_KariFree(WC_PKCS7_KARI* kari) +{ + void* heap; + + if (kari) { + heap = kari->heap; + + if (kari->decoded) { + if (kari->decodedInit) + FreeDecodedCert(kari->decoded); + XFREE(kari->decoded, heap, DYNAMIC_TYPE_PKCS7); + } + if (kari->senderKey) { + if (kari->senderKeyInit) + wc_ecc_free(kari->senderKey); + XFREE(kari->senderKey, heap, DYNAMIC_TYPE_PKCS7); + } + if (kari->recipKey) { + if (kari->recipKeyInit) + wc_ecc_free(kari->recipKey); + XFREE(kari->recipKey, heap, DYNAMIC_TYPE_PKCS7); + } + if (kari->senderKeyExport) { + ForceZero(kari->senderKeyExport, kari->senderKeyExportSz); + XFREE(kari->senderKeyExport, heap, DYNAMIC_TYPE_PKCS7); + kari->senderKeyExportSz = 0; + } + if (kari->kek) { + ForceZero(kari->kek, kari->kekSz); + XFREE(kari->kek, heap, DYNAMIC_TYPE_PKCS7); + kari->kekSz = 0; + } + if (kari->ukm) { + if (kari->ukmOwner == 1) { + XFREE(kari->ukm, heap, DYNAMIC_TYPE_PKCS7); } + kari->ukmSz = 0; + } + if (kari->sharedInfo) { + ForceZero(kari->sharedInfo, kari->sharedInfoSz); + XFREE(kari->sharedInfo, heap, DYNAMIC_TYPE_PKCS7); + kari->sharedInfoSz = 0; + } + XFREE(kari, heap, DYNAMIC_TYPE_PKCS7); + } + + (void)heap; + + return 0; +} + + +/* parse recipient cert/key, return 0 on success, negative on error + * key/keySz only needed during decoding (WC_PKCS7_DECODE) */ +static int wc_PKCS7_KariParseRecipCert(WC_PKCS7_KARI* kari, const byte* cert, + word32 certSz, const byte* key, + word32 keySz) +{ + int ret; + word32 idx; + + if (kari == NULL || kari->decoded == NULL || + cert == NULL || certSz == 0) + return BAD_FUNC_ARG; + + /* decode certificate */ + InitDecodedCert(kari->decoded, (byte*)cert, certSz, kari->heap); + kari->decodedInit = 1; + ret = ParseCert(kari->decoded, CA_TYPE, NO_VERIFY, 0); + if (ret < 0) + return ret; + + /* only supports ECDSA for now */ + if (kari->decoded->keyOID != ECDSAk) { + WOLFSSL_MSG("CMS KARI only supports ECDSA key types"); + return BAD_FUNC_ARG; + } + + /* make sure subject key id was read from cert */ + if (kari->decoded->extSubjKeyIdSet == 0) { + WOLFSSL_MSG("Failed to read subject key ID from recipient cert"); + return BAD_FUNC_ARG; + } + + ret = wc_ecc_init_ex(kari->recipKey, kari->heap, kari->devId); + if (ret != 0) + return ret; + + kari->recipKeyInit = 1; + + /* get recip public key */ + if (kari->direction == WC_PKCS7_ENCODE) { + + idx = 0; + ret = wc_EccPublicKeyDecode(kari->decoded->publicKey, &idx, + kari->recipKey, kari->decoded->pubKeySize); + if (ret != 0) + return ret; + } + /* get recip private key */ + else if (kari->direction == WC_PKCS7_DECODE) { + if (key != NULL && keySz > 0) { + idx = 0; + ret = wc_EccPrivateKeyDecode(key, &idx, kari->recipKey, keySz); + } + else if (kari->devId == INVALID_DEVID) { + ret = BAD_FUNC_ARG; + } + if (ret != 0) + return ret; + + } else { + /* bad direction */ + return BAD_FUNC_ARG; + } + + (void)idx; + + return 0; +} + + +/* create ephemeral ECC key, places ecc_key in kari->senderKey, + * DER encoded in kari->senderKeyExport. return 0 on success, + * negative on error */ +static int wc_PKCS7_KariGenerateEphemeralKey(WC_PKCS7_KARI* kari) +{ + int ret; + WC_RNG rng; + + if (kari == NULL || kari->decoded == NULL || + kari->recipKey == NULL || kari->recipKey->dp == NULL) + return BAD_FUNC_ARG; + + kari->senderKeyExport = (byte*)XMALLOC(kari->decoded->pubKeySize, + kari->heap, DYNAMIC_TYPE_PKCS7); + if (kari->senderKeyExport == NULL) + return MEMORY_E; + + kari->senderKeyExportSz = kari->decoded->pubKeySize; + + ret = wc_ecc_init_ex(kari->senderKey, kari->heap, kari->devId); + if (ret != 0) { + XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + kari->senderKeyInit = 1; + + ret = wc_InitRng_ex(&rng, kari->heap, kari->devId); + if (ret != 0) { + XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + ret = wc_ecc_make_key_ex(&rng, kari->recipKey->dp->size, + kari->senderKey, kari->recipKey->dp->id); + if (ret != 0) { + XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); + wc_FreeRng(&rng); + return ret; + } + + wc_FreeRng(&rng); + + /* dump generated key to X.963 DER for output in CMS bundle */ + ret = wc_ecc_export_x963(kari->senderKey, kari->senderKeyExport, + &kari->senderKeyExportSz); + if (ret != 0) { + XFREE(kari->senderKeyExport, kari->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + return 0; +} + + +/* create ASN.1 encoded ECC-CMS-SharedInfo using specified key wrap algorithm, + * place in kari->sharedInfo. returns 0 on success, negative on error */ +static int wc_PKCS7_KariGenerateSharedInfo(WC_PKCS7_KARI* kari, int keyWrapOID) +{ + int idx = 0; + int sharedInfoSeqSz = 0; + int keyInfoSz = 0; + int suppPubInfoSeqSz = 0; + int entityUInfoOctetSz = 0; + int entityUInfoExplicitSz = 0; + int kekOctetSz = 0; + int sharedInfoSz = 0; + + word32 kekBitSz = 0; + + byte sharedInfoSeq[MAX_SEQ_SZ]; + byte keyInfo[MAX_ALGO_SZ]; + byte suppPubInfoSeq[MAX_SEQ_SZ]; + byte entityUInfoOctet[MAX_OCTET_STR_SZ]; + byte entityUInfoExplicitSeq[MAX_SEQ_SZ]; + byte kekOctet[MAX_OCTET_STR_SZ]; + + if (kari == NULL) + return BAD_FUNC_ARG; + + if ((kari->ukmSz > 0) && (kari->ukm == NULL)) + return BAD_FUNC_ARG; + + /* kekOctet */ + kekOctetSz = SetOctetString(sizeof(word32), kekOctet); + sharedInfoSz += (kekOctetSz + sizeof(word32)); + + /* suppPubInfo */ + suppPubInfoSeqSz = SetImplicit(ASN_SEQUENCE, 2, + kekOctetSz + sizeof(word32), + suppPubInfoSeq); + sharedInfoSz += suppPubInfoSeqSz; + + /* optional ukm/entityInfo */ + if (kari->ukmSz > 0) { + entityUInfoOctetSz = SetOctetString(kari->ukmSz, entityUInfoOctet); + sharedInfoSz += (entityUInfoOctetSz + kari->ukmSz); + + entityUInfoExplicitSz = SetExplicit(0, entityUInfoOctetSz + + kari->ukmSz, + entityUInfoExplicitSeq); + sharedInfoSz += entityUInfoExplicitSz; + } + + /* keyInfo */ + keyInfoSz = SetAlgoID(keyWrapOID, keyInfo, oidKeyWrapType, 0); + sharedInfoSz += keyInfoSz; + + /* sharedInfo */ + sharedInfoSeqSz = SetSequence(sharedInfoSz, sharedInfoSeq); + sharedInfoSz += sharedInfoSeqSz; + + kari->sharedInfo = (byte*)XMALLOC(sharedInfoSz, kari->heap, + DYNAMIC_TYPE_PKCS7); + if (kari->sharedInfo == NULL) + return MEMORY_E; + + kari->sharedInfoSz = sharedInfoSz; + + XMEMCPY(kari->sharedInfo + idx, sharedInfoSeq, sharedInfoSeqSz); + idx += sharedInfoSeqSz; + XMEMCPY(kari->sharedInfo + idx, keyInfo, keyInfoSz); + idx += keyInfoSz; + if (kari->ukmSz > 0) { + XMEMCPY(kari->sharedInfo + idx, entityUInfoExplicitSeq, + entityUInfoExplicitSz); + idx += entityUInfoExplicitSz; + XMEMCPY(kari->sharedInfo + idx, entityUInfoOctet, entityUInfoOctetSz); + idx += entityUInfoOctetSz; + XMEMCPY(kari->sharedInfo + idx, kari->ukm, kari->ukmSz); + idx += kari->ukmSz; + } + XMEMCPY(kari->sharedInfo + idx, suppPubInfoSeq, suppPubInfoSeqSz); + idx += suppPubInfoSeqSz; + XMEMCPY(kari->sharedInfo + idx, kekOctet, kekOctetSz); + idx += kekOctetSz; + + kekBitSz = (kari->kekSz) * 8; /* convert to bits */ +#ifdef LITTLE_ENDIAN_ORDER + kekBitSz = ByteReverseWord32(kekBitSz); /* network byte order */ +#endif + XMEMCPY(kari->sharedInfo + idx, &kekBitSz, sizeof(kekBitSz)); + + return 0; +} + + +/* create key encryption key (KEK) using key wrap algorithm and key encryption + * algorithm, place in kari->kek. return 0 on success, <0 on error. */ +static int wc_PKCS7_KariGenerateKEK(WC_PKCS7_KARI* kari, + int keyWrapOID, int keyEncOID) +{ + int ret; + int kSz; + enum wc_HashType kdfType; + byte* secret; + word32 secretSz; + + if (kari == NULL || kari->recipKey == NULL || + kari->senderKey == NULL || kari->senderKey->dp == NULL) + return BAD_FUNC_ARG; + + /* get KEK size, allocate buff */ + kSz = wc_PKCS7_GetOIDKeySize(keyWrapOID); + if (kSz < 0) + return kSz; + + kari->kek = (byte*)XMALLOC(kSz, kari->heap, DYNAMIC_TYPE_PKCS7); + if (kari->kek == NULL) + return MEMORY_E; + + kari->kekSz = (word32)kSz; + + /* generate ECC-CMS-SharedInfo */ + ret = wc_PKCS7_KariGenerateSharedInfo(kari, keyWrapOID); + if (ret != 0) + return ret; + + /* generate shared secret */ + secretSz = kari->senderKey->dp->size; + secret = (byte*)XMALLOC(secretSz, kari->heap, DYNAMIC_TYPE_PKCS7); + if (secret == NULL) + return MEMORY_E; + + if (kari->direction == WC_PKCS7_ENCODE) { + + ret = wc_ecc_shared_secret(kari->senderKey, kari->recipKey, + secret, &secretSz); + + } else if (kari->direction == WC_PKCS7_DECODE) { + + ret = wc_ecc_shared_secret(kari->recipKey, kari->senderKey, + secret, &secretSz); + + } else { + /* bad direction */ + XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; + } + + if (ret != 0) { + XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* run through KDF */ + switch (keyEncOID) { + + #ifndef NO_SHA + case dhSinglePass_stdDH_sha1kdf_scheme: + kdfType = WC_HASH_TYPE_SHA; + break; + #endif + #ifndef WOLFSSL_SHA224 + case dhSinglePass_stdDH_sha224kdf_scheme: + kdfType = WC_HASH_TYPE_SHA224; + break; + #endif + #ifndef NO_SHA256 + case dhSinglePass_stdDH_sha256kdf_scheme: + kdfType = WC_HASH_TYPE_SHA256; + break; + #endif + #ifdef WOLFSSL_SHA384 + case dhSinglePass_stdDH_sha384kdf_scheme: + kdfType = WC_HASH_TYPE_SHA384; + break; + #endif + #ifdef WOLFSSL_SHA512 + case dhSinglePass_stdDH_sha512kdf_scheme: + kdfType = WC_HASH_TYPE_SHA512; + break; + #endif + default: + WOLFSSL_MSG("Unsupported key agreement algorithm"); + XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; + }; + + ret = wc_X963_KDF(kdfType, secret, secretSz, kari->sharedInfo, + kari->sharedInfoSz, kari->kek, kari->kekSz); + if (ret != 0) { + XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + XFREE(secret, kari->heap, DYNAMIC_TYPE_PKCS7); + + return 0; +} + + +/* Encode and add CMS EnvelopedData KARI (KeyAgreeRecipientInfo) RecipientInfo + * to CMS/PKCS#7 EnvelopedData structure. + * + * Returns 0 on success, negative upon error */ +int wc_PKCS7_AddRecipient_KARI(PKCS7* pkcs7, const byte* cert, word32 certSz, + int keyWrapOID, int keyAgreeOID, byte* ukm, + word32 ukmSz, int options) +{ + Pkcs7EncodedRecip* recip; + Pkcs7EncodedRecip* lastRecip = NULL; + WC_PKCS7_KARI* kari = NULL; + + word32 idx = 0; + word32 encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + + int ret = 0; + int keySz, direction = 0; + int blockKeySz = 0; + + /* ASN.1 layout */ + int totalSz = 0; + int kariSeqSz = 0; + byte kariSeq[MAX_SEQ_SZ]; /* IMPLICIT [1] */ + int verSz = 0; + byte ver[MAX_VERSION_SZ]; + + int origIdOrKeySeqSz = 0; + byte origIdOrKeySeq[MAX_SEQ_SZ]; /* IMPLICIT [0] */ + int origPubKeySeqSz = 0; + byte origPubKeySeq[MAX_SEQ_SZ]; /* IMPLICIT [1] */ + int origAlgIdSz = 0; + byte origAlgId[MAX_ALGO_SZ]; + int origPubKeyStrSz = 0; + byte origPubKeyStr[MAX_OCTET_STR_SZ]; + + /* optional user keying material */ + int ukmOctetSz = 0; + byte ukmOctetStr[MAX_OCTET_STR_SZ]; + int ukmExplicitSz = 0; + byte ukmExplicitSeq[MAX_SEQ_SZ]; + + int keyEncryptAlgoIdSz = 0; + byte keyEncryptAlgoId[MAX_ALGO_SZ]; + int keyWrapAlgSz = 0; + byte keyWrapAlg[MAX_ALGO_SZ]; + + int recipEncKeysSeqSz = 0; + byte recipEncKeysSeq[MAX_SEQ_SZ]; + int recipEncKeySeqSz = 0; + byte recipEncKeySeq[MAX_SEQ_SZ]; + int recipKeyIdSeqSz = 0; + byte recipKeyIdSeq[MAX_SEQ_SZ]; /* IMPLICIT [0] */ + int subjKeyIdOctetSz = 0; + byte subjKeyIdOctet[MAX_OCTET_STR_SZ]; + int encryptedKeyOctetSz = 0; + byte encryptedKeyOctet[MAX_OCTET_STR_SZ]; + +#ifdef WOLFSSL_SMALL_STACK + byte* encryptedKey; + + encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedKey == NULL) { + return MEMORY_E; + } #else - byte digest[digestSz]; - RsaKey stack_key; - RsaKey* key = &stack_key; + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; #endif - XMEMSET(digest, 0, digestSz); + /* allocate and init memory for recipient */ + recip = (Pkcs7EncodedRecip*)XMALLOC(sizeof(Pkcs7EncodedRecip), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (recip == NULL) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return MEMORY_E; + } + XMEMSET(recip, 0, sizeof(Pkcs7EncodedRecip)); - ret = wc_InitRsaKey(key, NULL); - if (ret != 0) { + /* get key size for content-encryption key based on algorithm */ + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ret; - } - if (wc_RsaPublicKeyDecode(pkcs7->publicKey, &scratch, key, - pkcs7->publicKeySz) < 0) { - WOLFSSL_MSG("ASN RSA key decode error"); + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return blockKeySz; + } + + /* generate random content encryption key, if needed */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return PUBLIC_KEY_E; - } + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } - plainSz = wc_RsaSSL_Verify(sig, sigSz, digest, digestSz, key); - wc_FreeRsaKey(key); + /* set direction based on keyWrapAlgo */ + switch (keyWrapOID) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128_WRAP: + #endif + #ifdef WOLFSSL_AES_192 + case AES192_WRAP: + #endif + #ifdef WOLFSSL_AES_256 + case AES256_WRAP: + #endif + direction = AES_ENCRYPTION; + break; +#endif + default: + WOLFSSL_MSG("Unsupported key wrap algorithm"); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BAD_KEYWRAP_ALG_E; + } + kari = wc_PKCS7_KariNew(pkcs7, WC_PKCS7_ENCODE); + if (kari == NULL) { #ifdef WOLFSSL_SMALL_STACK - XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } - if (plainSz < 0) - return plainSz; + /* set user keying material if available */ + if (ukmSz > 0 && ukm != NULL) { + kari->ukm = ukm; + kari->ukmSz = ukmSz; + kari->ukmOwner = 0; + } + + /* parse recipient cert, get public key */ + ret = wc_PKCS7_KariParseRecipCert(kari, cert, certSz, NULL, 0); + if (ret != 0) { + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* generate sender ephemeral ECC key */ + ret = wc_PKCS7_KariGenerateEphemeralKey(kari); + if (ret != 0) { + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* generate KEK (key encryption key) */ + ret = wc_PKCS7_KariGenerateKEK(kari, keyWrapOID, keyAgreeOID); + if (ret != 0) { + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* encrypt CEK with KEK */ + keySz = wc_PKCS7_KeyWrap(pkcs7->cek, pkcs7->cekSz, kari->kek, + kari->kekSz, encryptedKey, encryptedKeySz, + keyWrapOID, direction); + if (keySz <= 0) { + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return keySz; + } + encryptedKeySz = (word32)keySz; + + /* Start of RecipientEncryptedKeys */ + + /* EncryptedKey */ + encryptedKeyOctetSz = SetOctetString(encryptedKeySz, encryptedKeyOctet); + totalSz += (encryptedKeyOctetSz + encryptedKeySz); + + /* SubjectKeyIdentifier */ + subjKeyIdOctetSz = SetOctetString(KEYID_SIZE, subjKeyIdOctet); + totalSz += (subjKeyIdOctetSz + KEYID_SIZE); + + /* RecipientKeyIdentifier IMPLICIT [0] */ + recipKeyIdSeqSz = SetImplicit(ASN_SEQUENCE, 0, subjKeyIdOctetSz + + KEYID_SIZE, recipKeyIdSeq); + totalSz += recipKeyIdSeqSz; + + /* RecipientEncryptedKey */ + recipEncKeySeqSz = SetSequence(totalSz, recipEncKeySeq); + totalSz += recipEncKeySeqSz; + + /* RecipientEncryptedKeys */ + recipEncKeysSeqSz = SetSequence(totalSz, recipEncKeysSeq); + totalSz += recipEncKeysSeqSz; + + /* Start of optional UserKeyingMaterial */ + + if (kari->ukmSz > 0) { + ukmOctetSz = SetOctetString(kari->ukmSz, ukmOctetStr); + totalSz += (ukmOctetSz + kari->ukmSz); + + ukmExplicitSz = SetExplicit(1, ukmOctetSz + kari->ukmSz, + ukmExplicitSeq); + totalSz += ukmExplicitSz; + } + + /* Start of KeyEncryptionAlgorithmIdentifier */ + + /* KeyWrapAlgorithm */ + keyWrapAlgSz = SetAlgoID(keyWrapOID, keyWrapAlg, oidKeyWrapType, 0); + totalSz += keyWrapAlgSz; + + /* KeyEncryptionAlgorithmIdentifier */ + keyEncryptAlgoIdSz = SetAlgoID(keyAgreeOID, keyEncryptAlgoId, + oidCmsKeyAgreeType, keyWrapAlgSz); + totalSz += keyEncryptAlgoIdSz; + + /* Start of OriginatorIdentifierOrKey */ + + /* recipient ECPoint, public key */ + XMEMSET(origPubKeyStr, 0, sizeof(origPubKeyStr)); /* no unused bits */ + origPubKeyStr[0] = ASN_BIT_STRING; + origPubKeyStrSz = SetLength(kari->senderKeyExportSz + 1, + origPubKeyStr + 1) + 2; + totalSz += (origPubKeyStrSz + kari->senderKeyExportSz); + + /* Originator AlgorithmIdentifier, params set to NULL for interop + compatibility */ + origAlgIdSz = SetAlgoID(ECDSAk, origAlgId, oidKeyType, 2); + origAlgId[origAlgIdSz++] = ASN_TAG_NULL; + origAlgId[origAlgIdSz++] = 0; + totalSz += origAlgIdSz; + + /* outer OriginatorPublicKey IMPLICIT [1] */ + origPubKeySeqSz = SetImplicit(ASN_SEQUENCE, 1, + origAlgIdSz + origPubKeyStrSz + + kari->senderKeyExportSz, origPubKeySeq); + totalSz += origPubKeySeqSz; + + /* outer OriginatorIdentiferOrKey IMPLICIT [0] */ + origIdOrKeySeqSz = SetImplicit(ASN_SEQUENCE, 0, + origPubKeySeqSz + origAlgIdSz + + origPubKeyStrSz + kari->senderKeyExportSz, + origIdOrKeySeq); + totalSz += origIdOrKeySeqSz; + + /* version, always 3 */ + verSz = SetMyVersion(3, ver, 0); + totalSz += verSz; + recip->recipVersion = 3; + + /* outer IMPLICIT [1] kari */ + kariSeqSz = SetImplicit(ASN_SEQUENCE, 1, totalSz, kariSeq); + totalSz += kariSeqSz; + + if (totalSz > MAX_RECIP_SZ) { + WOLFSSL_MSG("KeyAgreeRecipientInfo output buffer too small"); + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + XMEMCPY(recip->recip + idx, kariSeq, kariSeqSz); + idx += kariSeqSz; + XMEMCPY(recip->recip + idx, ver, verSz); + idx += verSz; + + XMEMCPY(recip->recip + idx, origIdOrKeySeq, origIdOrKeySeqSz); + idx += origIdOrKeySeqSz; + XMEMCPY(recip->recip + idx, origPubKeySeq, origPubKeySeqSz); + idx += origPubKeySeqSz; + + /* AlgorithmIdentifier with NULL parameter */ + XMEMCPY(recip->recip + idx, origAlgId, origAlgIdSz); + idx += origAlgIdSz; + + XMEMCPY(recip->recip + idx, origPubKeyStr, origPubKeyStrSz); + idx += origPubKeyStrSz; + /* ephemeral public key */ + XMEMCPY(recip->recip + idx, kari->senderKeyExport, kari->senderKeyExportSz); + idx += kari->senderKeyExportSz; + + if (kari->ukmSz > 0) { + XMEMCPY(recip->recip + idx, ukmExplicitSeq, ukmExplicitSz); + idx += ukmExplicitSz; + XMEMCPY(recip->recip + idx, ukmOctetStr, ukmOctetSz); + idx += ukmOctetSz; + XMEMCPY(recip->recip + idx, kari->ukm, kari->ukmSz); + idx += kari->ukmSz; + } + + XMEMCPY(recip->recip + idx, keyEncryptAlgoId, keyEncryptAlgoIdSz); + idx += keyEncryptAlgoIdSz; + XMEMCPY(recip->recip + idx, keyWrapAlg, keyWrapAlgSz); + idx += keyWrapAlgSz; + + XMEMCPY(recip->recip + idx, recipEncKeysSeq, recipEncKeysSeqSz); + idx += recipEncKeysSeqSz; + XMEMCPY(recip->recip + idx, recipEncKeySeq, recipEncKeySeqSz); + idx += recipEncKeySeqSz; + XMEMCPY(recip->recip + idx, recipKeyIdSeq, recipKeyIdSeqSz); + idx += recipKeyIdSeqSz; + XMEMCPY(recip->recip + idx, subjKeyIdOctet, subjKeyIdOctetSz); + idx += subjKeyIdOctetSz; + /* subject key id */ + XMEMCPY(recip->recip + idx, kari->decoded->extSubjKeyId, KEYID_SIZE); + idx += KEYID_SIZE; + XMEMCPY(recip->recip + idx, encryptedKeyOctet, encryptedKeyOctetSz); + idx += encryptedKeyOctetSz; + /* encrypted CEK */ + XMEMCPY(recip->recip + idx, encryptedKey, encryptedKeySz); + idx += encryptedKeySz; + + wc_PKCS7_KariFree(kari); +#ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + /* store recipient size */ + recip->recipSz = idx; + recip->recipType = PKCS7_KARI; + + /* add recipient to recip list */ + if (pkcs7->recipList == NULL) { + pkcs7->recipList = recip; + } else { + lastRecip = pkcs7->recipList; + while (lastRecip->next != NULL) { + lastRecip = lastRecip->next; } + lastRecip->next = recip; } - return 0; + (void)options; + + return idx; } +#endif /* HAVE_ECC */ + +#ifndef NO_RSA -/* create ASN.1 fomatted RecipientInfo structure, returns sequence size */ -WOLFSSL_LOCAL int wc_CreateRecipientInfo(const byte* cert, word32 certSz, - int keyEncAlgo, int blockKeySz, - RNG* rng, byte* contentKeyPlain, - byte* contentKeyEnc, - int* keyEncSz, byte* out, word32 outSz) +/* Encode and add CMS EnvelopedData KTRI (KeyTransRecipientInfo) RecipientInfo + * to CMS/PKCS#7 EnvelopedData structure. + * + * Returns 0 on success, negative upon error */ +int wc_PKCS7_AddRecipient_KTRI(PKCS7* pkcs7, const byte* cert, word32 certSz, + int options) { + Pkcs7EncodedRecip* recip = NULL; + Pkcs7EncodedRecip* lastRecip = NULL; + + WC_RNG rng; word32 idx = 0; - int ret = 0, totalSz = 0; - int verSz, issuerSz, snSz, keyEncAlgSz; - int issuerSeqSz, recipSeqSz, issuerSerialSeqSz; + word32 encryptedKeySz = 0; + + int ret = 0, blockKeySz; + int verSz = 0, issuerSz = 0, snSz = 0, keyEncAlgSz = 0; + int issuerSeqSz = 0, recipSeqSz = 0, issuerSerialSeqSz = 0; int encKeyOctetStrSz; + int sidType; byte ver[MAX_VERSION_SZ]; byte issuerSerialSeq[MAX_SEQ_SZ]; @@ -961,241 +6020,1696 @@ WOLFSSL_LOCAL int wc_CreateRecipientInfo(const byte* cert, word32 certSz, byte issuerSeq[MAX_SEQ_SZ]; byte encKeyOctetStr[MAX_OCTET_STR_SZ]; + byte issuerSKIDSeq[MAX_SEQ_SZ]; + byte issuerSKID[MAX_OCTET_STR_SZ]; + word32 issuerSKIDSeqSz = 0, issuerSKIDSz = 0; + #ifdef WOLFSSL_SMALL_STACK - byte *serial; - byte *keyAlgArray; - + byte* serial; + byte* keyAlgArray; + byte* encryptedKey; RsaKey* pubKey; DecodedCert* decoded; - serial = (byte*)XMALLOC(MAX_SN_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); - keyAlgArray = (byte*)XMALLOC(MAX_SN_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); - decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, - DYNAMIC_TYPE_TMP_BUFFER); - - if (decoded == NULL || serial == NULL || keyAlgArray == NULL) { - if (serial) XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (keyAlgArray) XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (decoded) XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + serial = (byte*)XMALLOC(MAX_SN_SZ, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + keyAlgArray = (byte*)XMALLOC(MAX_SN_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + decoded = (DecodedCert*)XMALLOC(sizeof(DecodedCert), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + + if (decoded == NULL || serial == NULL || + encryptedKey == NULL || keyAlgArray == NULL) { + if (serial) + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (keyAlgArray) + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedKey) + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (decoded) + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } - #else byte serial[MAX_SN_SZ]; byte keyAlgArray[MAX_ALGO_SZ]; - - RsaKey stack_pubKey; - RsaKey* pubKey = &stack_pubKey; - DecodedCert stack_decoded; - DecodedCert* decoded = &stack_decoded; + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; + + RsaKey pubKey[1]; + DecodedCert decoded[1]; +#endif + + encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + XMEMSET(encryptedKey, 0, encryptedKeySz); + + /* default to IssuerAndSerialNumber if not set */ + if (pkcs7->sidType != 0) { + sidType = pkcs7->sidType; + } else { + sidType = CMS_ISSUER_AND_SERIAL_NUMBER; + } + + /* allow options to override SubjectIdentifier type if set */ + if (options & CMS_SKID) { + sidType = CMS_SKID; + } else if (options & CMS_ISSUER_AND_SERIAL_NUMBER) { + sidType = CMS_ISSUER_AND_SERIAL_NUMBER; + } + + /* allocate recipient struct */ + recip = (Pkcs7EncodedRecip*)XMALLOC(sizeof(Pkcs7EncodedRecip), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (recip == NULL) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + return MEMORY_E; + } + XMEMSET(recip, 0, sizeof(Pkcs7EncodedRecip)); + + /* get key size for content-encryption key based on algorithm */ + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return blockKeySz; + } + + /* generate random content encryption key, if needed */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret < 0) { +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } - InitDecodedCert(decoded, (byte*)cert, certSz, 0); + InitDecodedCert(decoded, (byte*)cert, certSz, pkcs7->heap); ret = ParseCert(decoded, CA_TYPE, NO_VERIFY, 0); if (ret < 0) { FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } - /* version */ - verSz = SetMyVersion(0, ver, 0); + if (sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { - /* IssuerAndSerialNumber */ - if (decoded->issuerRaw == NULL || decoded->issuerRawLen == 0) { - WOLFSSL_MSG("DecodedCert lacks raw issuer pointer and length"); - FreeDecodedCert(decoded); + /* version, must be 0 for IssuerAndSerialNumber */ + verSz = SetMyVersion(0, ver, 0); + recip->recipVersion = 0; + + /* IssuerAndSerialNumber */ + if (decoded->issuerRaw == NULL || decoded->issuerRawLen == 0) { + WOLFSSL_MSG("DecodedCert lacks raw issuer pointer and length"); + FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return -1; - } - issuerSz = decoded->issuerRawLen; - issuerSeqSz = SetSequence(issuerSz, issuerSeq); + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return -1; + } + issuerSz = decoded->issuerRawLen; + issuerSeqSz = SetSequence(issuerSz, issuerSeq); - if (decoded->serialSz == 0) { - WOLFSSL_MSG("DecodedCert missing serial number"); + if (decoded->serialSz == 0) { + WOLFSSL_MSG("DecodedCert missing serial number"); + FreeDecodedCert(decoded); +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return -1; + } + snSz = SetSerialNumber(decoded->serial, decoded->serialSz, serial, + MAX_SN_SZ, MAX_SN_SZ); + + issuerSerialSeqSz = SetSequence(issuerSeqSz + issuerSz + snSz, + issuerSerialSeq); + + } else if (sidType == CMS_SKID) { + + /* version, must be 2 for SubjectKeyIdentifier */ + verSz = SetMyVersion(2, ver, 0); + recip->recipVersion = 2; + + issuerSKIDSz = SetOctetString(KEYID_SIZE, issuerSKID); + issuerSKIDSeqSz = SetExplicit(0, issuerSKIDSz + KEYID_SIZE, + issuerSKIDSeq); + } else { FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return -1; + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return PKCS7_RECIP_E; } - snSz = SetSerialNumber(decoded->serial, decoded->serialSz, serial); - issuerSerialSeqSz = SetSequence(issuerSeqSz + issuerSz + snSz, - issuerSerialSeq); + pkcs7->publicKeyOID = decoded->keyOID; /* KeyEncryptionAlgorithmIdentifier, only support RSA now */ - if (keyEncAlgo != RSAk) { + if (pkcs7->publicKeyOID != RSAk) { FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ALGO_ID_E; } - keyEncAlgSz = SetAlgoID(keyEncAlgo, keyAlgArray, keyType, 0); + keyEncAlgSz = SetAlgoID(pkcs7->publicKeyOID, keyAlgArray, oidKeyType, 0); if (keyEncAlgSz == 0) { FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK - pubKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); + pubKey = (RsaKey*)XMALLOC(sizeof(RsaKey), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); if (pubKey == NULL) { FreeDecodedCert(decoded); - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return MEMORY_E; } #endif /* EncryptedKey */ - ret = wc_InitRsaKey(pubKey, 0); + ret = wc_InitRsaKey_ex(pubKey, pkcs7->heap, INVALID_DEVID); if (ret != 0) { FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(pubKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } if (wc_RsaPublicKeyDecode(decoded->publicKey, &idx, pubKey, - decoded->pubKeySize) < 0) { + decoded->pubKeySize) < 0) { WOLFSSL_MSG("ASN RSA key decode error"); wc_FreeRsaKey(pubKey); FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(pubKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return PUBLIC_KEY_E; } - *keyEncSz = wc_RsaPublicEncrypt(contentKeyPlain, blockKeySz, contentKeyEnc, - MAX_ENCRYPTED_KEY_SZ, pubKey, rng); + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret != 0) { + wc_FreeRsaKey(pubKey); + FreeDecodedCert(decoded); +#ifdef WOLFSSL_SMALL_STACK + XFREE(pubKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + + ret = wc_RsaPublicEncrypt(pkcs7->cek, pkcs7->cekSz, encryptedKey, + encryptedKeySz, pubKey, &rng); wc_FreeRsaKey(pubKey); + wc_FreeRng(&rng); #ifdef WOLFSSL_SMALL_STACK - XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(pubKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - if (*keyEncSz < 0) { + if (ret < 0) { WOLFSSL_MSG("RSA Public Encrypt failed"); FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return *keyEncSz; + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; } + encryptedKeySz = ret; - encKeyOctetStrSz = SetOctetString(*keyEncSz, encKeyOctetStr); + encKeyOctetStrSz = SetOctetString(encryptedKeySz, encKeyOctetStr); /* RecipientInfo */ - recipSeqSz = SetSequence(verSz + issuerSerialSeqSz + issuerSeqSz + - issuerSz + snSz + keyEncAlgSz + encKeyOctetStrSz + - *keyEncSz, recipSeq); + if (sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + recipSeqSz = SetSequence(verSz + issuerSerialSeqSz + issuerSeqSz + + issuerSz + snSz + keyEncAlgSz + + encKeyOctetStrSz + encryptedKeySz, recipSeq); + + if (recipSeqSz + verSz + issuerSerialSeqSz + issuerSeqSz + snSz + + keyEncAlgSz + encKeyOctetStrSz + encryptedKeySz > MAX_RECIP_SZ) { + WOLFSSL_MSG("RecipientInfo output buffer too small"); + FreeDecodedCert(decoded); +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } - if (recipSeqSz + verSz + issuerSerialSeqSz + issuerSeqSz + snSz + - keyEncAlgSz + encKeyOctetStrSz + *keyEncSz > (int)outSz) { - WOLFSSL_MSG("RecipientInfo output buffer too small"); - FreeDecodedCert(decoded); + } else { + recipSeqSz = SetSequence(verSz + issuerSKIDSeqSz + issuerSKIDSz + + KEYID_SIZE + keyEncAlgSz + encKeyOctetStrSz + + encryptedKeySz, recipSeq); + + if (recipSeqSz + verSz + issuerSKIDSeqSz + issuerSKIDSz + KEYID_SIZE + + keyEncAlgSz + encKeyOctetStrSz + encryptedKeySz > MAX_RECIP_SZ) { + WOLFSSL_MSG("RecipientInfo output buffer too small"); + FreeDecodedCert(decoded); #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + } + + idx = 0; + XMEMCPY(recip->recip + idx, recipSeq, recipSeqSz); + idx += recipSeqSz; + XMEMCPY(recip->recip + idx, ver, verSz); + idx += verSz; + if (sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + XMEMCPY(recip->recip + idx, issuerSerialSeq, issuerSerialSeqSz); + idx += issuerSerialSeqSz; + XMEMCPY(recip->recip + idx, issuerSeq, issuerSeqSz); + idx += issuerSeqSz; + XMEMCPY(recip->recip + idx, decoded->issuerRaw, issuerSz); + idx += issuerSz; + XMEMCPY(recip->recip + idx, serial, snSz); + idx += snSz; + } else { + XMEMCPY(recip->recip + idx, issuerSKIDSeq, issuerSKIDSeqSz); + idx += issuerSKIDSeqSz; + XMEMCPY(recip->recip + idx, issuerSKID, issuerSKIDSz); + idx += issuerSKIDSz; + XMEMCPY(recip->recip + idx, pkcs7->issuerSubjKeyId, KEYID_SIZE); + idx += KEYID_SIZE; + } + XMEMCPY(recip->recip + idx, keyAlgArray, keyEncAlgSz); + idx += keyEncAlgSz; + XMEMCPY(recip->recip + idx, encKeyOctetStr, encKeyOctetStrSz); + idx += encKeyOctetStrSz; + XMEMCPY(recip->recip + idx, encryptedKey, encryptedKeySz); + idx += encryptedKeySz; + + FreeDecodedCert(decoded); + +#ifdef WOLFSSL_SMALL_STACK + XFREE(serial, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(keyAlgArray, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decoded, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); +#endif + + /* store recipient size */ + recip->recipSz = idx; + recip->recipType = PKCS7_KTRI; + + /* add recipient to recip list */ + if (pkcs7->recipList == NULL) { + pkcs7->recipList = recip; + } else { + lastRecip = pkcs7->recipList; + while (lastRecip->next != NULL) { + lastRecip = lastRecip->next; + } + lastRecip->next = recip; + } + + return idx; +} + +#endif /* !NO_RSA */ + + +/* encrypt content using encryptOID algo */ +static int wc_PKCS7_EncryptContent(int encryptOID, byte* key, int keySz, + byte* iv, int ivSz, byte* aad, word32 aadSz, + byte* authTag, word32 authTagSz, byte* in, + int inSz, byte* out) +{ + int ret; +#ifndef NO_AES + Aes aes; +#endif +#ifndef NO_DES3 + Des des; + Des3 des3; +#endif + + if (key == NULL || iv == NULL || in == NULL || out == NULL) + return BAD_FUNC_ARG; + + switch (encryptOID) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128CBCb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192CBCb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256CBCb: + #endif + if ( + #ifdef WOLFSSL_AES_128 + (encryptOID == AES128CBCb && keySz != 16 ) || + #endif + #ifdef WOLFSSL_AES_192 + (encryptOID == AES192CBCb && keySz != 24 ) || + #endif + #ifdef WOLFSSL_AES_256 + (encryptOID == AES256CBCb && keySz != 32 ) || + #endif + (ivSz != AES_BLOCK_SIZE) ) + return BAD_FUNC_ARG; + + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesSetKey(&aes, key, keySz, iv, AES_ENCRYPTION); + if (ret == 0) + ret = wc_AesCbcEncrypt(&aes, out, in, inSz); + wc_AesFree(&aes); + } + break; + #ifdef HAVE_AESGCM + #ifdef WOLFSSL_AES_128 + case AES128GCMb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192GCMb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256GCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + if (authTag == NULL) + return BAD_FUNC_ARG; + + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, keySz); + if (ret == 0) + ret = wc_AesGcmEncrypt(&aes, out, in, inSz, iv, ivSz, + authTag, authTagSz, aad, aadSz); + wc_AesFree(&aes); + } + break; + #endif + #endif /* HAVE_AESGCM */ + #ifdef HAVE_AESCCM + #ifdef WOLFSSL_AES_128 + case AES128CCMb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192CCMb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256CCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + if (authTag == NULL) + return BAD_FUNC_ARG; + + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesCcmSetKey(&aes, key, keySz); + if (ret == 0) + ret = wc_AesCcmEncrypt(&aes, out, in, inSz, iv, ivSz, + authTag, authTagSz, aad, aadSz); + wc_AesFree(&aes); + } + break; + #endif + #endif /* HAVE_AESCCM */ +#endif /* NO_AES */ +#ifndef NO_DES3 + case DESb: + if (keySz != DES_KEYLEN || ivSz != DES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + ret = wc_Des_SetKey(&des, key, iv, DES_ENCRYPTION); + if (ret == 0) + ret = wc_Des_CbcEncrypt(&des, out, in, inSz); + + break; + + case DES3b: + if (keySz != DES3_KEYLEN || ivSz != DES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + ret = wc_Des3Init(&des3, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Des3_SetKey(&des3, key, iv, DES_ENCRYPTION); + if (ret == 0) + ret = wc_Des3_CbcEncrypt(&des3, out, in, inSz); + wc_Des3Free(&des3); + } + break; #endif + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + +#if defined(NO_AES) || (!defined(HAVE_AESGCM) && !defined(HAVE_AESCCM)) + (void)authTag; + (void)authTagSz; + (void)aad; + (void)aadSz; +#endif + return ret; +} + + +/* decrypt content using encryptOID algo + * returns 0 on success */ +static int wc_PKCS7_DecryptContent(PKCS7* pkcs7, int encryptOID, byte* key, + int keySz, byte* iv, int ivSz, byte* aad, word32 aadSz, byte* authTag, + word32 authTagSz, byte* in, int inSz, byte* out) +{ + int ret; +#ifndef NO_AES + Aes aes; +#endif +#ifndef NO_DES3 + Des des; + Des3 des3; +#endif + + if (iv == NULL || in == NULL || out == NULL) + return BAD_FUNC_ARG; + + if (pkcs7->decryptionCb != NULL) { + return pkcs7->decryptionCb(pkcs7, encryptOID, iv, ivSz, + aad, aadSz, authTag, authTagSz, in, + inSz, out, pkcs7->decryptionCtx); + } + + if (key == NULL) + return BAD_FUNC_ARG; + + switch (encryptOID) { +#ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128CBCb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192CBCb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256CBCb: + #endif + if ( + #ifdef WOLFSSL_AES_128 + (encryptOID == AES128CBCb && keySz != 16 ) || + #endif + #ifdef WOLFSSL_AES_192 + (encryptOID == AES192CBCb && keySz != 24 ) || + #endif + #ifdef WOLFSSL_AES_256 + (encryptOID == AES256CBCb && keySz != 32 ) || + #endif + (ivSz != AES_BLOCK_SIZE) ) + return BAD_FUNC_ARG; + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesSetKey(&aes, key, keySz, iv, AES_DECRYPTION); + if (ret == 0) + ret = wc_AesCbcDecrypt(&aes, out, in, inSz); + wc_AesFree(&aes); + } + break; + #ifdef HAVE_AESGCM + #ifdef WOLFSSL_AES_128 + case AES128GCMb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192GCMb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256GCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + if (authTag == NULL) + return BAD_FUNC_ARG; + + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesGcmSetKey(&aes, key, keySz); + if (ret == 0) + ret = wc_AesGcmDecrypt(&aes, out, in, inSz, iv, ivSz, + authTag, authTagSz, aad, aadSz); + wc_AesFree(&aes); + } + break; + #endif + #endif /* HAVE_AESGCM */ + #ifdef HAVE_AESCCM + #ifdef WOLFSSL_AES_128 + case AES128CCMb: + #endif + #ifdef WOLFSSL_AES_192 + case AES192CCMb: + #endif + #ifdef WOLFSSL_AES_256 + case AES256CCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + if (authTag == NULL) + return BAD_FUNC_ARG; + + ret = wc_AesInit(&aes, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_AesCcmSetKey(&aes, key, keySz); + if (ret == 0) + ret = wc_AesCcmDecrypt(&aes, out, in, inSz, iv, ivSz, + authTag, authTagSz, aad, aadSz); + wc_AesFree(&aes); + } + break; + #endif + #endif /* HAVE_AESCCM */ +#endif /* NO_AES */ +#ifndef NO_DES3 + case DESb: + if (keySz != DES_KEYLEN || ivSz != DES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + ret = wc_Des_SetKey(&des, key, iv, DES_DECRYPTION); + if (ret == 0) + ret = wc_Des_CbcDecrypt(&des, out, in, inSz); + + break; + case DES3b: + if (keySz != DES3_KEYLEN || ivSz != DES_BLOCK_SIZE) + return BAD_FUNC_ARG; + + ret = wc_Des3Init(&des3, NULL, INVALID_DEVID); + if (ret == 0) { + ret = wc_Des3_SetKey(&des3, key, iv, DES_DECRYPTION); + if (ret == 0) + ret = wc_Des3_CbcDecrypt(&des3, out, in, inSz); + wc_Des3Free(&des3); + } + + break; +#endif + default: + WOLFSSL_MSG("Unsupported content cipher type"); + return ALGO_ID_E; + }; + +#if defined(NO_AES) || (!defined(HAVE_AESGCM) && !defined(HAVE_AESCCM)) + (void)authTag; + (void)authTagSz; + (void)aad; + (void)aadSz; +#endif + + return ret; +} + + +/* Generate random block, place in out, return 0 on success negative on error. + * Used for generation of IV, nonce, etc */ +static int wc_PKCS7_GenerateBlock(PKCS7* pkcs7, WC_RNG* rng, byte* out, + word32 outSz) +{ + int ret; + WC_RNG* rnd = NULL; + + if (out == NULL || outSz == 0) + return BAD_FUNC_ARG; + + /* input RNG is optional, init local one if input rng is NULL */ + if (rng == NULL) { + rnd = (WC_RNG*)XMALLOC(sizeof(WC_RNG), pkcs7->heap, DYNAMIC_TYPE_RNG); + if (rnd == NULL) + return MEMORY_E; + + ret = wc_InitRng_ex(rnd, pkcs7->heap, pkcs7->devId); + if (ret != 0) { + XFREE(rnd, pkcs7->heap, DYNAMIC_TYPE_RNG); + return ret; + } + + } else { + rnd = rng; + } + + ret = wc_RNG_GenerateBlock(rnd, out, outSz); + + if (rng == NULL) { + wc_FreeRng(rnd); + XFREE(rnd, pkcs7->heap, DYNAMIC_TYPE_RNG); + } + + return ret; +} + + +/* Set default SignerIdentifier type to be used. Is either + * IssuerAndSerialNumber or SubjectKeyIdentifier. Encoding defaults to using + * IssuerAndSerialNumber unless set with this function or explicitly + * overridden via options when adding RecipientInfo type. + * + * Using the type DEGENERATE_SID skips over signer information. In degenerate + * cases there are no signers. + * + * pkcs7 - pointer to initialized PKCS7 structure + * type - either CMS_ISSUER_AND_SERIAL_NUMBER, CMS_SKID or DEGENERATE_SID + * + * return 0 on success, negative upon error */ +int wc_PKCS7_SetSignerIdentifierType(PKCS7* pkcs7, int type) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + if (type != CMS_ISSUER_AND_SERIAL_NUMBER && + type != CMS_SKID && + type != DEGENERATE_SID) { + return BAD_FUNC_ARG; + } + + pkcs7->sidType = type; + + return 0; +} + + +/* Set custom contentType, currently supported with SignedData type + * + * pkcs7 - pointer to initialized PKCS7 structure + * contentType - pointer to array with ASN.1 encoded OID value + * sz - length of contentType array, octets + * + * return 0 on success, negative upon error */ +int wc_PKCS7_SetContentType(PKCS7* pkcs7, byte* contentType, word32 sz) +{ + if (pkcs7 == NULL || contentType == NULL || sz == 0) + return BAD_FUNC_ARG; + + if (sz > MAX_OID_SZ) { + WOLFSSL_MSG("input array too large, bounded by MAX_OID_SZ"); + return BAD_FUNC_ARG; + } + + XMEMCPY(pkcs7->contentType, contentType, sz); + pkcs7->contentTypeSz = sz; + + return 0; +} + + +/* return size of padded data, padded to blockSz chunks, or negative on error */ +int wc_PKCS7_GetPadSize(word32 inputSz, word32 blockSz) +{ + int padSz; + + if (blockSz == 0) + return BAD_FUNC_ARG; + + padSz = blockSz - (inputSz % blockSz); + + return padSz; +} + + +/* pad input data to blockSz chunk, place in outSz. out must be big enough + * for input + pad bytes. See wc_PKCS7_GetPadSize() helper. */ +int wc_PKCS7_PadData(byte* in, word32 inSz, byte* out, word32 outSz, + word32 blockSz) +{ + int i, padSz; + + if (in == NULL || inSz == 0 || + out == NULL || outSz == 0) + return BAD_FUNC_ARG; + + padSz = wc_PKCS7_GetPadSize(inSz, blockSz); + + if (outSz < (inSz + padSz)) + return BAD_FUNC_ARG; + + XMEMCPY(out, in, inSz); + + for (i = 0; i < padSz; i++) { + out[inSz + i] = (byte)padSz; + } + + return inSz + padSz; +} + + +/* Encode and add CMS EnvelopedData ORI (OtherRecipientInfo) RecipientInfo + * to CMS/PKCS#7 EnvelopedData structure. + * + * Return 0 on success, negative upon error */ +int wc_PKCS7_AddRecipient_ORI(PKCS7* pkcs7, CallbackOriEncrypt oriEncryptCb, + int options) +{ + int oriTypeLenSz, blockKeySz, ret; + word32 idx, recipSeqSz; + + Pkcs7EncodedRecip* recip = NULL; + Pkcs7EncodedRecip* lastRecip = NULL; + + byte recipSeq[MAX_SEQ_SZ]; + byte oriTypeLen[MAX_LENGTH_SZ]; + + byte oriType[MAX_ORI_TYPE_SZ]; + byte oriValue[MAX_ORI_VALUE_SZ]; + word32 oriTypeSz = MAX_ORI_TYPE_SZ; + word32 oriValueSz = MAX_ORI_VALUE_SZ; + + if (pkcs7 == NULL || oriEncryptCb == NULL) { + return BAD_FUNC_ARG; + } + + /* allocate memory for RecipientInfo, KEK, encrypted key */ + recip = (Pkcs7EncodedRecip*)XMALLOC(sizeof(Pkcs7EncodedRecip), + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (recip == NULL) + return MEMORY_E; + XMEMSET(recip, 0, sizeof(Pkcs7EncodedRecip)); + + /* get key size for content-encryption key based on algorithm */ + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return blockKeySz; + } + + /* generate random content encryption key, if needed */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* call user callback to encrypt CEK and get oriType and oriValue + values back */ + ret = oriEncryptCb(pkcs7, pkcs7->cek, pkcs7->cekSz, oriType, &oriTypeSz, + oriValue, &oriValueSz, pkcs7->oriEncryptCtx); + if (ret != 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + oriTypeLenSz = SetLength(oriTypeSz, oriTypeLen); + + recipSeqSz = SetImplicit(ASN_SEQUENCE, 4, 1 + oriTypeLenSz + oriTypeSz + + oriValueSz, recipSeq); + + idx = 0; + XMEMCPY(recip->recip + idx, recipSeq, recipSeqSz); + idx += recipSeqSz; + /* oriType */ + recip->recip[idx] = ASN_OBJECT_ID; + idx += 1; + XMEMCPY(recip->recip + idx, oriTypeLen, oriTypeLenSz); + idx += oriTypeLenSz; + XMEMCPY(recip->recip + idx, oriType, oriTypeSz); + idx += oriTypeSz; + /* oriValue, input MUST already be ASN.1 encoded */ + XMEMCPY(recip->recip + idx, oriValue, oriValueSz); + idx += oriValueSz; + + /* store recipient size */ + recip->recipSz = idx; + recip->recipType = PKCS7_ORI; + recip->recipVersion = 4; + + /* add recipient to recip list */ + if (pkcs7->recipList == NULL) { + pkcs7->recipList = recip; + } else { + lastRecip = pkcs7->recipList; + while (lastRecip->next != NULL) { + lastRecip = lastRecip->next; + } + lastRecip->next = recip; + } + + (void)options; + + return idx; +} + +#if !defined(NO_PWDBASED) && !defined(NO_SHA) + + +static int wc_PKCS7_GenerateKEK_PWRI(PKCS7* pkcs7, byte* passwd, word32 pLen, + byte* salt, word32 saltSz, int kdfOID, + int prfOID, int iterations, byte* out, + word32 outSz) +{ + int ret; + + if (pkcs7 == NULL || passwd == NULL || salt == NULL || out == NULL) + return BAD_FUNC_ARG; + + switch (kdfOID) { + + case PBKDF2_OID: + + ret = wc_PBKDF2(out, passwd, pLen, salt, saltSz, iterations, + outSz, prfOID); + if (ret != 0) { + return ret; + } + + break; + + default: + WOLFSSL_MSG("Unsupported KDF OID"); + return PKCS7_OID_E; + } + + return 0; +} + + +/* RFC3211 (Section 2.3.1) key wrap algorithm (id-alg-PWRI-KEK). + * + * Returns output size on success, negative upon error */ +static int wc_PKCS7_PwriKek_KeyWrap(PKCS7* pkcs7, const byte* kek, word32 kekSz, + const byte* cek, word32 cekSz, + byte* out, word32 *outSz, + const byte* iv, word32 ivSz, int algID) +{ + WC_RNG rng; + int blockSz, outLen, ret; + word32 padSz; + byte* lastBlock; + + if (kek == NULL || cek == NULL || iv == NULL || outSz == NULL) + return BAD_FUNC_ARG; + + /* get encryption algorithm block size */ + blockSz = wc_PKCS7_GetOIDBlockSize(algID); + if (blockSz < 0) + return blockSz; + + /* get pad bytes needed to block boundary */ + padSz = blockSz - ((4 + cekSz) % blockSz); + outLen = 4 + cekSz + padSz; + + /* must be at least two blocks long */ + if (outLen < 2 * blockSz) + padSz += blockSz; + + /* if user set out to NULL, give back required length */ + if (out == NULL) { + *outSz = outLen; + return LENGTH_ONLY_E; + } + + /* verify output buffer is large enough */ + if (*outSz < (word32)outLen) return BUFFER_E; + + out[0] = cekSz; + out[1] = ~cek[0]; + out[2] = ~cek[1]; + out[3] = ~cek[2]; + XMEMCPY(out + 4, cek, cekSz); + + /* random padding of size padSz */ + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret != 0) + return ret; + + ret = wc_RNG_GenerateBlock(&rng, out + 4 + cekSz, padSz); + + if (ret == 0) { + /* encrypt, normal */ + ret = wc_PKCS7_EncryptContent(algID, (byte*)kek, kekSz, (byte*)iv, + ivSz, NULL, 0, NULL, 0, out, outLen, out); + } + + if (ret == 0) { + /* encrypt again, using last ciphertext block as IV */ + lastBlock = out + (((outLen / blockSz) - 1) * blockSz); + ret = wc_PKCS7_EncryptContent(algID, (byte*)kek, kekSz, lastBlock, + blockSz, NULL, 0, NULL, 0, out, + outLen, out); } - XMEMCPY(out + totalSz, recipSeq, recipSeqSz); + if (ret == 0) { + *outSz = outLen; + } else { + outLen = ret; + } + + wc_FreeRng(&rng); + + return outLen; +} + + +/* RFC3211 (Section 2.3.2) key unwrap algorithm (id-alg-PWRI-KEK). + * + * Returns cek size on success, negative upon error */ +static int wc_PKCS7_PwriKek_KeyUnWrap(PKCS7* pkcs7, const byte* kek, + word32 kekSz, const byte* in, word32 inSz, + byte* out, word32 outSz, const byte* iv, + word32 ivSz, int algID) +{ + int blockSz, cekLen, ret; + byte* tmpIv = NULL; + byte* lastBlock = NULL; + byte* outTmp = NULL; + + if (pkcs7 == NULL || kek == NULL || in == NULL || + out == NULL || iv == NULL) { + return BAD_FUNC_ARG; + } + + outTmp = (byte*)XMALLOC(inSz, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (outTmp == NULL) + return MEMORY_E; + + /* get encryption algorithm block size */ + blockSz = wc_PKCS7_GetOIDBlockSize(algID); + if (blockSz < 0) { + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return blockSz; + } + + /* input needs to be blockSz multiple and at least 2 * blockSz */ + if (((inSz % blockSz) != 0) || (inSz < (2 * (word32)blockSz))) { + WOLFSSL_MSG("PWRI-KEK unwrap input must of block size and >= 2 " + "times block size"); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; + } + + /* use block out[n-1] as IV to decrypt block out[n] */ + lastBlock = (byte*)in + inSz - blockSz; + tmpIv = lastBlock - blockSz; + + /* decrypt last block */ + ret = wc_PKCS7_DecryptContent(pkcs7, algID, (byte*)kek, kekSz, tmpIv, + blockSz, NULL, 0, NULL, 0, lastBlock, blockSz, + outTmp + inSz - blockSz); + + if (ret == 0) { + /* using last decrypted block as IV, decrypt [0 ... n-1] blocks */ + lastBlock = outTmp + inSz - blockSz; + ret = wc_PKCS7_DecryptContent(pkcs7, algID, (byte*)kek, kekSz, + lastBlock, blockSz, NULL, 0, NULL, 0, (byte*)in, inSz - blockSz, + outTmp); + } + + if (ret == 0) { + /* decrypt using original kek and iv */ + ret = wc_PKCS7_DecryptContent(pkcs7, algID, (byte*)kek, kekSz, + (byte*)iv, ivSz, NULL, 0, NULL, 0, outTmp, inSz, outTmp); + } + + if (ret != 0) { + ForceZero(outTmp, inSz); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + + cekLen = outTmp[0]; + + /* verify length */ + if ((word32)cekLen > inSz) { + ForceZero(outTmp, inSz); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; + } + + /* verify check bytes */ + if ((outTmp[1] ^ outTmp[4]) != 0xFF || + (outTmp[2] ^ outTmp[5]) != 0xFF || + (outTmp[3] ^ outTmp[6]) != 0xFF) { + ForceZero(outTmp, inSz); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BAD_FUNC_ARG; + } + + if (outSz < (word32)cekLen) { + ForceZero(outTmp, inSz); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BUFFER_E; + } + + XMEMCPY(out, outTmp + 4, outTmp[0]); + ForceZero(outTmp, inSz); + XFREE(outTmp, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + + return cekLen; +} + + +/* Encode and add CMS EnvelopedData PWRI (PasswordRecipientInfo) RecipientInfo + * to CMS/PKCS#7 EnvelopedData structure. + * + * Return 0 on success, negative upon error */ +int wc_PKCS7_AddRecipient_PWRI(PKCS7* pkcs7, byte* passwd, word32 pLen, + byte* salt, word32 saltSz, int kdfOID, + int hashOID, int iterations, int kekEncryptOID, + int options) +{ + Pkcs7EncodedRecip* recip = NULL; + Pkcs7EncodedRecip* lastRecip = NULL; + + /* PasswordRecipientInfo */ + byte recipSeq[MAX_SEQ_SZ]; + byte ver[MAX_VERSION_SZ]; + word32 recipSeqSz, verSz; + + /* KeyDerivationAlgorithmIdentifier */ + byte kdfAlgoIdSeq[MAX_SEQ_SZ]; + byte kdfAlgoId[MAX_OID_SZ]; + byte kdfParamsSeq[MAX_SEQ_SZ]; /* PBKDF2-params */ + byte kdfSaltOctetStr[MAX_OCTET_STR_SZ]; /* salt OCTET STRING */ + byte kdfIterations[MAX_VERSION_SZ]; + word32 kdfAlgoIdSeqSz, kdfAlgoIdSz; + word32 kdfParamsSeqSz, kdfSaltOctetStrSz, kdfIterationsSz; + /* OPTIONAL: keyLength, not supported yet */ + /* OPTIONAL: prf AlgorithIdentifier, not supported yet */ + + /* KeyEncryptionAlgorithmIdentifier */ + byte keyEncAlgoIdSeq[MAX_SEQ_SZ]; + byte keyEncAlgoId[MAX_OID_SZ]; /* id-alg-PWRI-KEK */ + byte pwriEncAlgoId[MAX_ALGO_SZ]; + byte ivOctetString[MAX_OCTET_STR_SZ]; + word32 keyEncAlgoIdSeqSz, keyEncAlgoIdSz; + word32 pwriEncAlgoIdSz, ivOctetStringSz; + + /* EncryptedKey */ + byte encKeyOctetStr[MAX_OCTET_STR_SZ]; + word32 encKeyOctetStrSz; + + byte tmpIv[MAX_CONTENT_IV_SIZE]; + byte* encryptedKey = NULL; + byte* kek = NULL; + + int cekKeySz = 0, kekKeySz = 0, kekBlockSz = 0, ret = 0; + int encryptOID; + word32 idx, totalSz = 0, encryptedKeySz; + + if (pkcs7 == NULL || passwd == NULL || pLen == 0 || + salt == NULL || saltSz == 0) { + return BAD_FUNC_ARG; + } + + /* allow user to use different KEK encryption algorithm than used for + * main content encryption algorithm, if passed in */ + if (kekEncryptOID != 0) { + encryptOID = kekEncryptOID; + } else { + encryptOID = pkcs7->encryptOID; + } + + /* get content-encryption key size, based on algorithm */ + cekKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (cekKeySz < 0) + return cekKeySz; + + /* get KEK encryption key size, based on algorithm */ + if (encryptOID != pkcs7->encryptOID) { + kekKeySz = wc_PKCS7_GetOIDKeySize(encryptOID); + } else { + kekKeySz = cekKeySz; + } + + /* get KEK encryption block size */ + kekBlockSz = wc_PKCS7_GetOIDBlockSize(encryptOID); + if (kekBlockSz < 0) + return kekBlockSz; + + /* generate random CEK */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, cekKeySz); + if (ret < 0) + return ret; + + /* generate random IV */ + ret = wc_PKCS7_GenerateBlock(pkcs7, NULL, tmpIv, kekBlockSz); + if (ret != 0) + return ret; + + /* allocate memory for RecipientInfo, KEK, encrypted key */ + recip = (Pkcs7EncodedRecip*)XMALLOC(sizeof(Pkcs7EncodedRecip), + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (recip == NULL) + return MEMORY_E; + + kek = (byte*)XMALLOC(kekKeySz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (kek == NULL) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (encryptedKey == NULL) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + XMEMSET(recip, 0, sizeof(Pkcs7EncodedRecip)); + XMEMSET(kek, 0, kekKeySz); + XMEMSET(encryptedKey, 0, encryptedKeySz); + + /* generate KEK: expand password into KEK */ + ret = wc_PKCS7_GenerateKEK_PWRI(pkcs7, passwd, pLen, salt, saltSz, + kdfOID, hashOID, iterations, kek, + kekKeySz); + if (ret < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* generate encrypted key: encrypt CEK with KEK */ + ret = wc_PKCS7_PwriKek_KeyWrap(pkcs7, kek, kekKeySz, pkcs7->cek, + pkcs7->cekSz, encryptedKey, &encryptedKeySz, + tmpIv, kekBlockSz, encryptOID); + if (ret < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + encryptedKeySz = ret; + + /* put together encrypted key OCTET STRING */ + encKeyOctetStrSz = SetOctetString(encryptedKeySz, encKeyOctetStr); + totalSz += (encKeyOctetStrSz + encryptedKeySz); + + /* put together IV OCTET STRING */ + ivOctetStringSz = SetOctetString(kekBlockSz, ivOctetString); + totalSz += (ivOctetStringSz + kekBlockSz); + + /* set PWRIAlgorithms AlgorithmIdentifier, adding (ivOctetStringSz + + blockKeySz) for IV OCTET STRING */ + pwriEncAlgoIdSz = SetAlgoID(encryptOID, pwriEncAlgoId, + oidBlkType, ivOctetStringSz + kekBlockSz); + totalSz += pwriEncAlgoIdSz; + + /* set KeyEncryptionAlgorithms OID */ + ret = wc_SetContentType(PWRI_KEK_WRAP, keyEncAlgoId, sizeof(keyEncAlgoId)); + if (ret <= 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + keyEncAlgoIdSz = ret; + totalSz += keyEncAlgoIdSz; + + /* KeyEncryptionAlgorithm SEQ */ + keyEncAlgoIdSeqSz = SetSequence(keyEncAlgoIdSz + pwriEncAlgoIdSz + + ivOctetStringSz + kekBlockSz, + keyEncAlgoIdSeq); + totalSz += keyEncAlgoIdSeqSz; + + /* set KDF salt */ + kdfSaltOctetStrSz = SetOctetString(saltSz, kdfSaltOctetStr); + totalSz += (kdfSaltOctetStrSz + saltSz); + + /* set KDF iteration count */ + kdfIterationsSz = SetMyVersion(iterations, kdfIterations, 0); + totalSz += kdfIterationsSz; + + /* set KDF params SEQ */ + kdfParamsSeqSz = SetSequence(kdfSaltOctetStrSz + saltSz + kdfIterationsSz, + kdfParamsSeq); + totalSz += kdfParamsSeqSz; + + /* set KDF algo OID */ + ret = wc_SetContentType(kdfOID, kdfAlgoId, sizeof(kdfAlgoId)); + if (ret <= 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + kdfAlgoIdSz = ret; + totalSz += kdfAlgoIdSz; + + /* set KeyDerivationAlgorithmIdentifier EXPLICIT [0] SEQ */ + kdfAlgoIdSeqSz = SetExplicit(0, kdfAlgoIdSz + kdfParamsSeqSz + + kdfSaltOctetStrSz + saltSz + kdfIterationsSz, + kdfAlgoIdSeq); + totalSz += kdfAlgoIdSeqSz; + + /* set PasswordRecipientInfo CMSVersion, MUST be 0 */ + verSz = SetMyVersion(0, ver, 0); + totalSz += verSz; + recip->recipVersion = 0; + + /* set PasswordRecipientInfo SEQ */ + recipSeqSz = SetImplicit(ASN_SEQUENCE, 3, totalSz, recipSeq); totalSz += recipSeqSz; - XMEMCPY(out + totalSz, ver, verSz); + + if (totalSz > MAX_RECIP_SZ) { + WOLFSSL_MSG("CMS Recipient output buffer too small"); + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + idx = 0; + XMEMCPY(recip->recip + idx, recipSeq, recipSeqSz); + idx += recipSeqSz; + XMEMCPY(recip->recip + idx, ver, verSz); + idx += verSz; + XMEMCPY(recip->recip + idx, kdfAlgoIdSeq, kdfAlgoIdSeqSz); + idx += kdfAlgoIdSeqSz; + XMEMCPY(recip->recip + idx, kdfAlgoId, kdfAlgoIdSz); + idx += kdfAlgoIdSz; + XMEMCPY(recip->recip + idx, kdfParamsSeq, kdfParamsSeqSz); + idx += kdfParamsSeqSz; + XMEMCPY(recip->recip + idx, kdfSaltOctetStr, kdfSaltOctetStrSz); + idx += kdfSaltOctetStrSz; + XMEMCPY(recip->recip + idx, salt, saltSz); + idx += saltSz; + XMEMCPY(recip->recip + idx, kdfIterations, kdfIterationsSz); + idx += kdfIterationsSz; + XMEMCPY(recip->recip + idx, keyEncAlgoIdSeq, keyEncAlgoIdSeqSz); + idx += keyEncAlgoIdSeqSz; + XMEMCPY(recip->recip + idx, keyEncAlgoId, keyEncAlgoIdSz); + idx += keyEncAlgoIdSz; + XMEMCPY(recip->recip + idx, pwriEncAlgoId, pwriEncAlgoIdSz); + idx += pwriEncAlgoIdSz; + XMEMCPY(recip->recip + idx, ivOctetString, ivOctetStringSz); + idx += ivOctetStringSz; + XMEMCPY(recip->recip + idx, tmpIv, kekBlockSz); + idx += kekBlockSz; + XMEMCPY(recip->recip + idx, encKeyOctetStr, encKeyOctetStrSz); + idx += encKeyOctetStrSz; + XMEMCPY(recip->recip + idx, encryptedKey, encryptedKeySz); + idx += encryptedKeySz; + + ForceZero(kek, kekBlockSz); + ForceZero(encryptedKey, encryptedKeySz); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + /* store recipient size */ + recip->recipSz = idx; + recip->recipType = PKCS7_PWRI; + + /* add recipient to recip list */ + if (pkcs7->recipList == NULL) { + pkcs7->recipList = recip; + } else { + lastRecip = pkcs7->recipList; + while (lastRecip->next != NULL) { + lastRecip = lastRecip->next; + } + lastRecip->next = recip; + } + + (void)options; + + return idx; +} + +/* Import password and KDF settings into a PKCS7 structure. Used for setting + * the password info for decryption a EnvelopedData PWRI RecipientInfo. + * + * Returns 0 on success, negative upon error */ +int wc_PKCS7_SetPassword(PKCS7* pkcs7, byte* passwd, word32 pLen) +{ + if (pkcs7 == NULL || passwd == NULL || pLen == 0) + return BAD_FUNC_ARG; + + pkcs7->pass = passwd; + pkcs7->passSz = pLen; + + return 0; +} + +#endif /* NO_PWDBASED */ + + +/* Encode and add CMS EnvelopedData KEKRI (KEKRecipientInfo) RecipientInfo + * to CMS/PKCS#7 EnvelopedData structure. + * + * pkcs7 - pointer to initialized PKCS7 structure + * keyWrapOID - OID sum of key wrap algorithm identifier + * kek - key encryption key + * kekSz - size of kek, bytes + * keyID - key-encryption key identifier, pre-distributed to endpoints + * keyIDSz - size of keyID, bytes + * timePtr - pointer to "time_t", which is typically "long" (OPTIONAL) + * otherOID - ASN.1 encoded OID of other attribute (OPTIONAL) + * otherOIDSz - size of otherOID, bytes (OPTIONAL) + * other - other attribute (OPTIONAL) + * otherSz - size of other (OPTIONAL) + * + * Returns 0 on success, negative upon error */ +int wc_PKCS7_AddRecipient_KEKRI(PKCS7* pkcs7, int keyWrapOID, byte* kek, + word32 kekSz, byte* keyId, word32 keyIdSz, + void* timePtr, byte* otherOID, + word32 otherOIDSz, byte* other, word32 otherSz, + int options) +{ + Pkcs7EncodedRecip* recip = NULL; + Pkcs7EncodedRecip* lastRecip = NULL; + + byte recipSeq[MAX_SEQ_SZ]; + byte ver[MAX_VERSION_SZ]; + byte kekIdSeq[MAX_SEQ_SZ]; + byte kekIdOctetStr[MAX_OCTET_STR_SZ]; + byte genTime[ASN_GENERALIZED_TIME_SIZE]; + byte otherAttSeq[MAX_SEQ_SZ]; + byte encAlgoId[MAX_ALGO_SZ]; + byte encKeyOctetStr[MAX_OCTET_STR_SZ]; +#ifdef WOLFSSL_SMALL_STACK + byte* encryptedKey; +#else + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; +#endif + + int blockKeySz = 0, ret = 0, direction; + word32 idx = 0; + word32 totalSz = 0; + word32 recipSeqSz = 0, verSz = 0; + word32 kekIdSeqSz = 0, kekIdOctetStrSz = 0; + word32 otherAttSeqSz = 0, encAlgoIdSz = 0, encKeyOctetStrSz = 0; + int encryptedKeySz; + + int timeSz = 0; +#ifndef NO_ASN_TIME + time_t* tm = NULL; +#endif + + if (pkcs7 == NULL || kek == NULL || keyId == NULL) + return BAD_FUNC_ARG; + + recip = (Pkcs7EncodedRecip*)XMALLOC(sizeof(Pkcs7EncodedRecip), pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (recip == NULL) + return MEMORY_E; + + XMEMSET(recip, 0, sizeof(Pkcs7EncodedRecip)); + + /* get key size for content-encryption key based on algorithm */ + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return blockKeySz; + } + + /* generate random content encryption key, if needed */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* EncryptedKey */ +#ifdef WOLFSSL_SMALL_STACK + encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (encryptedKey == NULL) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } +#endif + encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + XMEMSET(encryptedKey, 0, encryptedKeySz); + + #ifndef NO_AES + direction = AES_ENCRYPTION; + #else + direction = DES_ENCRYPTION; + #endif + + encryptedKeySz = wc_PKCS7_KeyWrap(pkcs7->cek, pkcs7->cekSz, kek, kekSz, + encryptedKey, encryptedKeySz, keyWrapOID, + direction); + if (encryptedKeySz < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return encryptedKeySz; + } + /* handle a zero size encKey case as WC_KEY_SIZE_E */ + if (encryptedKeySz == 0 || encryptedKeySz > MAX_ENCRYPTED_KEY_SZ) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return WC_KEY_SIZE_E; + } + + encKeyOctetStrSz = SetOctetString(encryptedKeySz, encKeyOctetStr); + totalSz += (encKeyOctetStrSz + encryptedKeySz); + + /* KeyEncryptionAlgorithmIdentifier */ + encAlgoIdSz = SetAlgoID(keyWrapOID, encAlgoId, oidKeyWrapType, 0); + totalSz += encAlgoIdSz; + + /* KEKIdentifier: keyIdentifier */ + kekIdOctetStrSz = SetOctetString(keyIdSz, kekIdOctetStr); + totalSz += (kekIdOctetStrSz + keyIdSz); + + /* KEKIdentifier: GeneralizedTime (OPTIONAL) */ +#ifndef NO_ASN_TIME + if (timePtr != NULL) { + tm = (time_t*)timePtr; + timeSz = GetAsnTimeString(tm, genTime, sizeof(genTime)); + if (timeSz < 0) { + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return timeSz; + } + totalSz += timeSz; + } +#endif + + /* KEKIdentifier: OtherKeyAttribute SEQ (OPTIONAL) */ + if (other != NULL && otherSz > 0) { + otherAttSeqSz = SetSequence(otherOIDSz + otherSz, otherAttSeq); + totalSz += otherAttSeqSz + otherOIDSz + otherSz; + } + + /* KEKIdentifier SEQ */ + kekIdSeqSz = SetSequence(kekIdOctetStrSz + keyIdSz + timeSz + + otherAttSeqSz + otherOIDSz + otherSz, kekIdSeq); + totalSz += kekIdSeqSz; + + /* version */ + verSz = SetMyVersion(4, ver, 0); totalSz += verSz; - XMEMCPY(out + totalSz, issuerSerialSeq, issuerSerialSeqSz); - totalSz += issuerSerialSeqSz; - XMEMCPY(out + totalSz, issuerSeq, issuerSeqSz); - totalSz += issuerSeqSz; - XMEMCPY(out + totalSz, decoded->issuerRaw, issuerSz); - totalSz += issuerSz; - XMEMCPY(out + totalSz, serial, snSz); - totalSz += snSz; - XMEMCPY(out + totalSz, keyAlgArray, keyEncAlgSz); - totalSz += keyEncAlgSz; - XMEMCPY(out + totalSz, encKeyOctetStr, encKeyOctetStrSz); - totalSz += encKeyOctetStrSz; - XMEMCPY(out + totalSz, contentKeyEnc, *keyEncSz); - totalSz += *keyEncSz; + recip->recipVersion = 4; - FreeDecodedCert(decoded); + /* KEKRecipientInfo SEQ */ + recipSeqSz = SetImplicit(ASN_SEQUENCE, 2, totalSz, recipSeq); + totalSz += recipSeqSz; + + if (totalSz > MAX_RECIP_SZ) { + WOLFSSL_MSG("CMS Recipient output buffer too small"); + XFREE(recip, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return BUFFER_E; + } + + XMEMCPY(recip->recip + idx, recipSeq, recipSeqSz); + idx += recipSeqSz; + XMEMCPY(recip->recip + idx, ver, verSz); + idx += verSz; + XMEMCPY(recip->recip + idx, kekIdSeq, kekIdSeqSz); + idx += kekIdSeqSz; + XMEMCPY(recip->recip + idx, kekIdOctetStr, kekIdOctetStrSz); + idx += kekIdOctetStrSz; + XMEMCPY(recip->recip + idx, keyId, keyIdSz); + idx += keyIdSz; + if (timePtr != NULL) { + XMEMCPY(recip->recip + idx, genTime, timeSz); + idx += timeSz; + } + if (other != NULL && otherSz > 0) { + XMEMCPY(recip->recip + idx, otherAttSeq, otherAttSeqSz); + idx += otherAttSeqSz; + XMEMCPY(recip->recip + idx, otherOID, otherOIDSz); + idx += otherOIDSz; + XMEMCPY(recip->recip + idx, other, otherSz); + idx += otherSz; + } + XMEMCPY(recip->recip + idx, encAlgoId, encAlgoIdSz); + idx += encAlgoIdSz; + XMEMCPY(recip->recip + idx, encKeyOctetStr, encKeyOctetStrSz); + idx += encKeyOctetStrSz; + XMEMCPY(recip->recip + idx, encryptedKey, encryptedKeySz); + idx += encryptedKeySz; #ifdef WOLFSSL_SMALL_STACK - XFREE(serial, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(keyAlgArray, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(decoded, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); #endif - return totalSz; + /* store recipient size */ + recip->recipSz = idx; + recip->recipType = PKCS7_KEKRI; + + /* add recipient to recip list */ + if (pkcs7->recipList == NULL) { + pkcs7->recipList = recip; + } else { + lastRecip = pkcs7->recipList; + while(lastRecip->next != NULL) { + lastRecip = lastRecip->next; + } + lastRecip->next = recip; + } + + (void)options; + + return idx; +} + + +static int wc_PKCS7_GetCMSVersion(PKCS7* pkcs7, int cmsContentType) +{ + int version = -1; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + switch (cmsContentType) { + case ENVELOPED_DATA: + + /* NOTE: EnvelopedData does not currently support + originatorInfo or unprotectedAttributes. When either of these + are added, version checking below needs to be updated to match + Section 6.1 of RFC 5652 */ + + /* if RecipientInfos include pwri or ori, version is 3 */ + if (wc_PKCS7_RecipientListIncludesType(pkcs7, PKCS7_PWRI) || + wc_PKCS7_RecipientListIncludesType(pkcs7, PKCS7_ORI)) { + version = 3; + break; + } + + /* if unprotectedAttrs is absent AND all RecipientInfo structs + are version 0, version is 0 */ + if (wc_PKCS7_RecipientListVersionsAllZero(pkcs7)) { + version = 0; + break; + } + + /* otherwise, version is 2 */ + version = 2; + break; + + default: + break; + } + + return version; } /* build PKCS#7 envelopedData content type, return enveloped size */ int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, byte* output, word32 outputSz) { - int i, ret = 0, idx = 0; - int totalSz = 0, padSz = 0, desOutSz = 0; + int ret, idx = 0; + int totalSz, padSz, encryptedOutSz; - int contentInfoSeqSz, outerContentTypeSz, outerContentSz; + int contentInfoSeqSz = 0, outerContentTypeSz = 0, outerContentSz; byte contentInfoSeq[MAX_SEQ_SZ]; byte outerContentType[MAX_ALGO_SZ]; byte outerContent[MAX_SEQ_SZ]; + int kariVersion; int envDataSeqSz, verSz; byte envDataSeq[MAX_SEQ_SZ]; byte ver[MAX_VERSION_SZ]; - RNG rng; - int contentKeyEncSz, blockKeySz; - int dynamicFlag = 0; - byte contentKeyPlain[MAX_CONTENT_KEY_LEN]; -#ifdef WOLFSSL_SMALL_STACK - byte* contentKeyEnc; -#else - byte contentKeyEnc[MAX_ENCRYPTED_KEY_SZ]; -#endif + WC_RNG rng; + int blockSz, blockKeySz; byte* plain; byte* encryptedContent; + Pkcs7EncodedRecip* tmpRecip = NULL; int recipSz, recipSetSz; -#ifdef WOLFSSL_SMALL_STACK - byte* recip; -#else - byte recip[MAX_RECIP_SZ]; -#endif byte recipSet[MAX_SET_SZ]; int encContentOctetSz, encContentSeqSz, contentTypeSz; @@ -1203,204 +7717,3109 @@ int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, byte* output, word32 outputSz) byte encContentSeq[MAX_SEQ_SZ]; byte contentType[MAX_ALGO_SZ]; byte contentEncAlgo[MAX_ALGO_SZ]; - byte tmpIv[DES_BLOCK_SIZE]; + byte tmpIv[MAX_CONTENT_IV_SIZE]; byte ivOctetString[MAX_OCTET_STR_SZ]; byte encContentOctet[MAX_OCTET_STR_SZ]; - if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || - pkcs7->encryptOID == 0 || pkcs7->singleCert == NULL) + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0) return BAD_FUNC_ARG; if (output == NULL || outputSz == 0) return BAD_FUNC_ARG; - /* PKCS#7 only supports DES, 3DES for now */ - switch (pkcs7->encryptOID) { - case DESb: - blockKeySz = DES_KEYLEN; - break; + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) + return blockKeySz; - case DES3b: - blockKeySz = DES3_KEYLEN; + blockSz = wc_PKCS7_GetOIDBlockSize(pkcs7->encryptOID); + if (blockSz < 0) + return blockSz; + + if (pkcs7->contentOID != FIRMWARE_PKG_DATA) { + /* outer content type */ + ret = wc_SetContentType(ENVELOPED_DATA, outerContentType, + sizeof(outerContentType)); + if (ret < 0) + return ret; + + outerContentTypeSz = ret; + } + + /* generate random content encryption key */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret != 0) { + return ret; + } + + /* build RecipientInfo, only if user manually set singleCert and size */ + if (pkcs7->singleCert != NULL && pkcs7->singleCertSz > 0) { + switch (pkcs7->publicKeyOID) { + #ifndef NO_RSA + case RSAk: + ret = wc_PKCS7_AddRecipient_KTRI(pkcs7, pkcs7->singleCert, + pkcs7->singleCertSz, 0); + break; + #endif + #ifdef HAVE_ECC + case ECDSAk: + ret = wc_PKCS7_AddRecipient_KARI(pkcs7, pkcs7->singleCert, + pkcs7->singleCertSz, + pkcs7->keyWrapOID, + pkcs7->keyAgreeOID, pkcs7->ukm, + pkcs7->ukmSz, 0); + break; + #endif + + default: + WOLFSSL_MSG("Unsupported RecipientInfo public key type"); + return BAD_FUNC_ARG; + }; + + if (ret < 0) { + WOLFSSL_MSG("Failed to create RecipientInfo"); + return ret; + } + } + + recipSz = wc_PKCS7_GetRecipientListSize(pkcs7); + if (recipSz < 0) { + return ret; + + } else if (recipSz == 0) { + WOLFSSL_MSG("You must add at least one CMS recipient"); + return PKCS7_RECIP_E; + } + recipSetSz = SetSet(recipSz, recipSet); + + /* version, defined in Section 6.1 of RFC 5652 */ + kariVersion = wc_PKCS7_GetCMSVersion(pkcs7, ENVELOPED_DATA); + if (kariVersion < 0) { + WOLFSSL_MSG("Failed to set CMS EnvelopedData version"); + return PKCS7_RECIP_E; + } + + verSz = SetMyVersion(kariVersion, ver, 0); + + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret != 0) + return ret; + + /* generate IV for block cipher */ + ret = wc_PKCS7_GenerateBlock(pkcs7, &rng, tmpIv, blockSz); + wc_FreeRng(&rng); + if (ret != 0) + return ret; + + /* EncryptedContentInfo */ + ret = wc_SetContentType(pkcs7->contentOID, contentType, + sizeof(contentType)); + if (ret < 0) + return ret; + + contentTypeSz = ret; + + /* allocate encrypted content buffer and PKCS#7 padding */ + padSz = wc_PKCS7_GetPadSize(pkcs7->contentSz, blockSz); + if (padSz < 0) + return padSz; + + encryptedOutSz = pkcs7->contentSz + padSz; + + plain = (byte*)XMALLOC(encryptedOutSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (plain == NULL) + return MEMORY_E; + + ret = wc_PKCS7_PadData(pkcs7->content, pkcs7->contentSz, plain, + encryptedOutSz, blockSz); + if (ret < 0) { + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + encryptedContent = (byte*)XMALLOC(encryptedOutSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (encryptedContent == NULL) { + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + /* put together IV OCTET STRING */ + ivOctetStringSz = SetOctetString(blockSz, ivOctetString); + + /* build up our ContentEncryptionAlgorithmIdentifier sequence, + * adding (ivOctetStringSz + blockSz) for IV OCTET STRING */ + contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, + oidBlkType, ivOctetStringSz + blockSz); + + if (contentEncAlgoSz == 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; + } + + /* encrypt content */ + ret = wc_PKCS7_EncryptContent(pkcs7->encryptOID, pkcs7->cek, + pkcs7->cekSz, tmpIv, blockSz, NULL, 0, NULL, 0, plain, + encryptedOutSz, encryptedContent); + + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, encryptedOutSz, + encContentOctet); + + encContentSeqSz = SetSequence(contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + blockSz + + encContentOctetSz + encryptedOutSz, + encContentSeq); + + /* keep track of sizes for outer wrapper layering */ + totalSz = verSz + recipSetSz + recipSz + encContentSeqSz + contentTypeSz + + contentEncAlgoSz + ivOctetStringSz + blockSz + + encContentOctetSz + encryptedOutSz; + + /* EnvelopedData */ + envDataSeqSz = SetSequence(totalSz, envDataSeq); + totalSz += envDataSeqSz; + + /* outer content */ + outerContentSz = SetExplicit(0, totalSz, outerContent); + totalSz += outerContentTypeSz; + totalSz += outerContentSz; + + if (pkcs7->contentOID != FIRMWARE_PKG_DATA) { + /* ContentInfo */ + contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); + totalSz += contentInfoSeqSz; + } + + if (totalSz > (int)outputSz) { + WOLFSSL_MSG("Pkcs7_encrypt output buffer too small"); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return BUFFER_E; + } + + if (pkcs7->contentOID != FIRMWARE_PKG_DATA) { + XMEMCPY(output + idx, contentInfoSeq, contentInfoSeqSz); + idx += contentInfoSeqSz; + XMEMCPY(output + idx, outerContentType, outerContentTypeSz); + idx += outerContentTypeSz; + XMEMCPY(output + idx, outerContent, outerContentSz); + idx += outerContentSz; + } + XMEMCPY(output + idx, envDataSeq, envDataSeqSz); + idx += envDataSeqSz; + XMEMCPY(output + idx, ver, verSz); + idx += verSz; + XMEMCPY(output + idx, recipSet, recipSetSz); + idx += recipSetSz; + /* copy in recipients from list */ + tmpRecip = pkcs7->recipList; + while (tmpRecip != NULL) { + XMEMCPY(output + idx, tmpRecip->recip, tmpRecip->recipSz); + idx += tmpRecip->recipSz; + tmpRecip = tmpRecip->next; + } + wc_PKCS7_FreeEncodedRecipientSet(pkcs7); + XMEMCPY(output + idx, encContentSeq, encContentSeqSz); + idx += encContentSeqSz; + XMEMCPY(output + idx, contentType, contentTypeSz); + idx += contentTypeSz; + XMEMCPY(output + idx, contentEncAlgo, contentEncAlgoSz); + idx += contentEncAlgoSz; + XMEMCPY(output + idx, ivOctetString, ivOctetStringSz); + idx += ivOctetStringSz; + XMEMCPY(output + idx, tmpIv, blockSz); + idx += blockSz; + XMEMCPY(output + idx, encContentOctet, encContentOctetSz); + idx += encContentOctetSz; + XMEMCPY(output + idx, encryptedContent, encryptedOutSz); + idx += encryptedOutSz; + + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return idx; +} + +#ifndef NO_RSA +/* decode KeyTransRecipientInfo (ktri), return 0 on success, <0 on error */ +static int wc_PKCS7_DecryptKtri(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ + int length, encryptedKeySz = 0, ret = 0; + int keySz, version, sidType = 0; + word32 encOID; + word32 keyIdx; + byte issuerHash[KEYID_SIZE]; + byte* outKey = NULL; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte tag; + + +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = *idx; + long rc; +#endif +#ifdef WC_RSA_BLINDING + WC_RNG rng; +#endif + +#ifdef WOLFSSL_SMALL_STACK + mp_int* serialNum = NULL; + byte* encryptedKey = NULL; + RsaKey* privKey = NULL; +#else + mp_int serialNum[1]; + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; + RsaKey privKey[1]; +#endif + + switch (pkcs7->state) { + case WC_PKCS7_DECRYPT_KTRI: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_VERSION_SZ, + &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + #endif + if (GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (version == 0) { + sidType = CMS_ISSUER_AND_SERIAL_NUMBER; + } else if (version == 2) { + sidType = CMS_SKID; + } else { + return ASN_VERSION_E; + } + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, 0, sidType, version); + + /* @TODO getting total amount left because of GetInt call later on + * this could be optimized to stream better */ + pkcs7->stream->expected = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_KTRI_2); + FALL_THROUGH; + + case WC_PKCS7_DECRYPT_KTRI_2: + #ifndef NO_PKCS7_STREAM + + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, pkcs7->stream->expected, + &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + wc_PKCS7_StreamGetVar(pkcs7, NULL, &sidType, &version); + + /* @TODO get expected size for next part, does not account for + * GetInt call well */ + if (pkcs7->stream->expected == MAX_SEQ_SZ) { + int sz; + word32 lidx; + + if (sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + lidx = *idx; + ret = GetSequence(pkiMsg, &lidx, &sz, pkiMsgSz); + if (ret < 0) + return ret; + } + else { + lidx = *idx + ASN_TAG_SZ; + ret = GetLength(pkiMsg, &lidx, &sz, pkiMsgSz); + if (ret < 0) + return ret; + } + + pkcs7->stream->expected = sz + MAX_ALGO_SZ + ASN_TAG_SZ + + MAX_LENGTH_SZ; + if (pkcs7->stream->length > 0 && + pkcs7->stream->length < pkcs7->stream->expected) { + return WC_PKCS7_WANT_READ_E; + } + } + #endif /* !NO_PKCS7_STREAM */ + + if (sidType == CMS_ISSUER_AND_SERIAL_NUMBER) { + + /* remove IssuerAndSerialNumber */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetNameHash(pkiMsg, idx, issuerHash, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* if we found correct recipient, issuer hashes will match */ + if (XMEMCMP(issuerHash, pkcs7->issuerHash, KEYID_SIZE) == 0) { + *recipFound = 1; + } + + #ifdef WOLFSSL_SMALL_STACK + serialNum = (mp_int*)XMALLOC(sizeof(mp_int), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (serialNum == NULL) + return MEMORY_E; + #endif + + if (GetInt(serialNum, pkiMsg, idx, pkiMsgSz) < 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(serialNum, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ASN_PARSE_E; + } + + mp_clear(serialNum); + + #ifdef WOLFSSL_SMALL_STACK + XFREE(serialNum, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + + } else { + /* remove SubjectKeyIdentifier */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* if we found correct recipient, SKID will match */ + if (XMEMCMP(pkiMsg + (*idx), pkcs7->issuerSubjKeyId, + KEYID_SIZE) == 0) { + *recipFound = 1; + } + (*idx) += KEYID_SIZE; + } + + if (GetAlgoId(pkiMsg, idx, &encOID, oidKeyType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* key encryption algorithm must be RSA for now */ + if (encOID != RSAk) + return ALGO_ID_E; + + /* read encryptedKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &encryptedKeySz, pkiMsgSz) < 0) { + return ASN_PARSE_E; + } + if (encryptedKeySz > MAX_ENCRYPTED_KEY_SZ) { + return BUFFER_E; + } + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, encryptedKeySz, sidType, version); + pkcs7->stream->expected = encryptedKeySz; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_KTRI_3); + FALL_THROUGH; + + case WC_PKCS7_DECRYPT_KTRI_3: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, idx)) != 0) { + return ret; + } + encryptedKeySz = pkcs7->stream->expected; + #endif + + #ifdef WOLFSSL_SMALL_STACK + encryptedKey = (byte*)XMALLOC(encryptedKeySz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (encryptedKey == NULL) + return MEMORY_E; + #endif + + if (*recipFound == 1) + XMEMCPY(encryptedKey, &pkiMsg[*idx], encryptedKeySz); + *idx += encryptedKeySz; + + /* load private key */ + #ifdef WOLFSSL_SMALL_STACK + privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (privKey == NULL) { + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return MEMORY_E; + } + #endif + + ret = wc_InitRsaKey_ex(privKey, pkcs7->heap, INVALID_DEVID); + if (ret != 0) { + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + + if (pkcs7->privateKey != NULL && pkcs7->privateKeySz > 0) { + keyIdx = 0; + ret = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &keyIdx, + privKey, pkcs7->privateKeySz); + } + else if (pkcs7->devId == INVALID_DEVID) { + ret = BAD_FUNC_ARG; + } + if (ret != 0) { + WOLFSSL_MSG("Failed to decode RSA private key"); + wc_FreeRsaKey(privKey); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return ret; + } + + /* decrypt encryptedKey */ + #ifdef WC_RSA_BLINDING + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret == 0) { + ret = wc_RsaSetRNG(privKey, &rng); + } + #endif + if (ret == 0) { + keySz = wc_RsaPrivateDecryptInline(encryptedKey, encryptedKeySz, + &outKey, privKey); + #ifdef WC_RSA_BLINDING + wc_FreeRng(&rng); + #endif + } else { + keySz = ret; + } + wc_FreeRsaKey(privKey); + + if (keySz <= 0 || outKey == NULL) { + ForceZero(encryptedKey, encryptedKeySz); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + return keySz; + } else { + *decryptedKeySz = keySz; + XMEMCPY(decryptedKey, outKey, keySz); + ForceZero(encryptedKey, encryptedKeySz); + } + + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(privKey, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + #endif + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + ret = 0; /* success */ break; default: - WOLFSSL_MSG("Unsupported content cipher type"); - return ALGO_ID_E; - }; + WOLFSSL_MSG("PKCS7 Unknown KTRI decrypt state"); + ret = BAD_FUNC_ARG; + } - /* outer content type */ - outerContentTypeSz = wc_SetContentType(ENVELOPED_DATA, outerContentType); + return ret; +} +#endif /* !NO_RSA */ - /* version, defined as 0 in RFC 2315 */ - verSz = SetMyVersion(0, ver, 0); +#ifdef HAVE_ECC - /* generate random content encryption key */ - ret = wc_InitRng(&rng); +/* remove ASN.1 OriginatorIdentifierOrKey, return 0 on success, <0 on error */ +static int wc_PKCS7_KariGetOriginatorIdentifierOrKey(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx) +{ + int ret, length; + word32 keyOID, oidSum = 0; + int curve_id = ECC_CURVE_DEF; + byte tag; + + if (kari == NULL || pkiMsg == NULL || idx == NULL) + return BAD_FUNC_ARG; + + /* remove OriginatorIdentifierOrKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) == 0 && + tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + } else { + return ASN_PARSE_E; + } + + /* remove OriginatorPublicKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) == 0 && + tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + } else { + return ASN_PARSE_E; + } + + /* remove AlgorithmIdentifier */ + if (GetAlgoId(pkiMsg, idx, &keyOID, oidKeyType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (keyOID != ECDSAk) + return ASN_PARSE_E; + + /* optional algorithm parameters */ + ret = GetObjectId(pkiMsg, idx, &oidSum, oidIgnoreType, pkiMsgSz); + if (ret == 0) { + /* get curve id */ + curve_id = wc_ecc_get_oid(oidSum, NULL, 0); + if (curve_id < 0) + return ECC_CURVE_OID_E; + } + + /* remove ECPoint BIT STRING */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_BIT_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_EXPECT_0_E; + + if (tag != ASN_OTHER_TYPE) + return ASN_EXPECT_0_E; + + /* get sender ephemeral public ECDSA key */ + ret = wc_ecc_init_ex(kari->senderKey, kari->heap, kari->devId); if (ret != 0) return ret; - ret = wc_RNG_GenerateBlock(&rng, contentKeyPlain, blockKeySz); + kari->senderKeyInit = 1; + + /* length-1 for unused bits counter */ + ret = wc_ecc_import_x963_ex(pkiMsg + (*idx), length - 1, kari->senderKey, + curve_id); if (ret != 0) { - wc_FreeRng(&rng); - return ret; + ret = wc_EccPublicKeyDecode(pkiMsg, idx, kari->senderKey, *idx + length - 1); + if (ret != 0) + return ret; + } + else { + (*idx) += length - 1; + } + + return 0; +} + + +/* remove optional UserKeyingMaterial if available, return 0 on success, + * < 0 on error */ +static int wc_PKCS7_KariGetUserKeyingMaterial(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx) +{ + int length; + word32 savedIdx; + byte tag; + + if (kari == NULL || pkiMsg == NULL || idx == NULL) + return BAD_FUNC_ARG; + + savedIdx = *idx; + + /* starts with EXPLICIT [1] */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + *idx = savedIdx; + return 0; + } + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + *idx = savedIdx; + return 0; + } + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) { + *idx = savedIdx; + return 0; + } + + /* get OCTET STRING */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + *idx = savedIdx; + return 0; + } + if (tag != ASN_OCTET_STRING) { + *idx = savedIdx; + return 0; } + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) { + *idx = savedIdx; + return 0; + } + + kari->ukm = NULL; + if (length > 0) { + kari->ukm = (byte*)XMALLOC(length, kari->heap, DYNAMIC_TYPE_PKCS7); + if (kari->ukm == NULL) + return MEMORY_E; + + XMEMCPY(kari->ukm, pkiMsg + (*idx), length); + kari->ukmOwner = 1; + } + + (*idx) += length; + kari->ukmSz = length; + + return 0; +} + + +/* remove ASN.1 KeyEncryptionAlgorithmIdentifier, return 0 on success, + * < 0 on error */ +static int wc_PKCS7_KariGetKeyEncryptionAlgorithmId(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx, + word32* keyAgreeOID, word32* keyWrapOID) +{ + int length = 0; + word32 localIdx; + + if (kari == NULL || pkiMsg == NULL || idx == NULL || + keyAgreeOID == NULL || keyWrapOID == NULL) + return BAD_FUNC_ARG; + + localIdx = *idx; + + /* remove KeyEncryptionAlgorithmIdentifier */ + if (GetSequence(pkiMsg, &localIdx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + localIdx = *idx; + if (GetAlgoId(pkiMsg, &localIdx, keyAgreeOID, oidCmsKeyAgreeType, + pkiMsgSz) < 0) { + return ASN_PARSE_E; + } + + if (localIdx < *idx + length) { + *idx = localIdx; + } + /* remove KeyWrapAlgorithm, stored in parameter of KeyEncAlgoId */ + if (GetAlgoId(pkiMsg, idx, keyWrapOID, oidKeyWrapType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + return 0; +} + + +/* remove ASN.1 SubjectKeyIdentifier, return 0 on success, < 0 on error + * if subject key ID matches, recipFound is set to 1 */ +static int wc_PKCS7_KariGetSubjectKeyIdentifier(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx, + int* recipFound, byte* rid) +{ + int length; + byte tag; + + if (kari == NULL || pkiMsg == NULL || idx == NULL || recipFound == NULL || + rid == NULL) + return BAD_FUNC_ARG; + + /* remove RecipientKeyIdentifier IMPLICIT [0] */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + return ASN_PARSE_E; + } + + if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + } else { + return ASN_PARSE_E; + } + + /* remove SubjectKeyIdentifier */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + return ASN_PARSE_E; + } + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (length != KEYID_SIZE) + return ASN_PARSE_E; + + XMEMCPY(rid, pkiMsg + (*idx), KEYID_SIZE); + (*idx) += length; + + /* subject key id should match if recipient found */ + if (XMEMCMP(rid, kari->decoded->extSubjKeyId, KEYID_SIZE) == 0) { + *recipFound = 1; + } + + return 0; +} + + +/* remove ASN.1 IssuerAndSerialNumber, return 0 on success, < 0 on error + * if issuer and serial number match, recipFound is set to 1 */ +static int wc_PKCS7_KariGetIssuerAndSerialNumber(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx, + int* recipFound, byte* rid) +{ + int length, ret; #ifdef WOLFSSL_SMALL_STACK - recip = (byte*)XMALLOC(MAX_RECIP_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); - contentKeyEnc = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (contentKeyEnc == NULL || recip == NULL) { - if (recip) XFREE(recip, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (contentKeyEnc) XFREE(contentKeyEnc, NULL, DYNAMIC_TYPE_TMP_BUFFER); - wc_FreeRng(&rng); + mp_int* serial; + mp_int* recipSerial; +#else + mp_int serial[1]; + mp_int recipSerial[1]; +#endif + + if (rid == NULL) { + return BAD_FUNC_ARG; + } + + /* remove IssuerAndSerialNumber */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetNameHash(pkiMsg, idx, rid, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* if we found correct recipient, issuer hashes will match */ + if (XMEMCMP(rid, kari->decoded->issuerHash, KEYID_SIZE) == 0) { + *recipFound = 1; + } + +#ifdef WOLFSSL_SMALL_STACK + serial = (mp_int*)XMALLOC(sizeof(mp_int), kari->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (serial == NULL) + return MEMORY_E; + + recipSerial = (mp_int*)XMALLOC(sizeof(mp_int), kari->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (recipSerial == NULL) { + XFREE(serial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } - #endif - /* build RecipientInfo, only handle 1 for now */ - recipSz = wc_CreateRecipientInfo(pkcs7->singleCert, pkcs7->singleCertSz, RSAk, - blockKeySz, &rng, contentKeyPlain, - contentKeyEnc, &contentKeyEncSz, recip, - MAX_RECIP_SZ); - - ForceZero(contentKeyEnc, MAX_ENCRYPTED_KEY_SZ); - + if (GetInt(serial, pkiMsg, idx, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(contentKeyEnc, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(serial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(recipSerial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + return ASN_PARSE_E; + } - if (recipSz < 0) { - WOLFSSL_MSG("Failed to create RecipientInfo"); - wc_FreeRng(&rng); + ret = mp_read_unsigned_bin(recipSerial, kari->decoded->serial, + kari->decoded->serialSz); + if (ret != MP_OKAY) { + mp_clear(serial); + WOLFSSL_MSG("Failed to parse CMS recipient serial number"); #ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + XFREE(serial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(recipSerial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return recipSz; + return ret; } - recipSetSz = SetSet(recipSz, recipSet); - /* generate IV for block cipher */ - ret = wc_RNG_GenerateBlock(&rng, tmpIv, DES_BLOCK_SIZE); - wc_FreeRng(&rng); - if (ret != 0) { + if (mp_cmp(recipSerial, serial) != MP_EQ) { + mp_clear(serial); + mp_clear(recipSerial); + WOLFSSL_MSG("CMS serial number does not match recipient"); #ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + XFREE(serial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(recipSerial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ret; + return PKCS7_RECIP_E; } - /* EncryptedContentInfo */ - contentTypeSz = wc_SetContentType(pkcs7->contentOID, contentType); - if (contentTypeSz == 0) { + mp_clear(serial); + mp_clear(recipSerial); + #ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + XFREE(serial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(recipSerial, kari->heap, DYNAMIC_TYPE_TMP_BUFFER); #endif + + return 0; +} + + +/* remove ASN.1 RecipientEncryptedKeys, return 0 on success, < 0 on error */ +static int wc_PKCS7_KariGetRecipientEncryptedKeys(WC_PKCS7_KARI* kari, + byte* pkiMsg, word32 pkiMsgSz, word32* idx, + int* recipFound, byte* encryptedKey, + int* encryptedKeySz, byte* rid) +{ + int length; + int ret = 0; + byte tag; + word32 localIdx; + + if (kari == NULL || pkiMsg == NULL || idx == NULL || + recipFound == NULL || encryptedKey == NULL) return BAD_FUNC_ARG; + + /* remove RecipientEncryptedKeys */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* remove RecipientEncryptedKeys */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* KeyAgreeRecipientIdentifier is CHOICE of IssuerAndSerialNumber + * or [0] IMMPLICIT RecipientKeyIdentifier */ + localIdx = *idx; + if (GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { + /* try to get RecipientKeyIdentifier */ + ret = wc_PKCS7_KariGetSubjectKeyIdentifier(kari, pkiMsg, pkiMsgSz, + idx, recipFound, rid); + } else { + /* try to get IssuerAndSerialNumber */ + ret = wc_PKCS7_KariGetIssuerAndSerialNumber(kari, pkiMsg, pkiMsgSz, + idx, recipFound, rid); } - /* allocate encrypted content buffer, pad if necessary, PKCS#7 padding */ - padSz = DES_BLOCK_SIZE - (pkcs7->contentSz % DES_BLOCK_SIZE); - desOutSz = pkcs7->contentSz + padSz; + /* if we don't have either option, malformed CMS */ + if (ret != 0) + return ret; + + /* remove EncryptedKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* put encrypted CEK in decryptedKey buffer for now, decrypt later */ + if (length > *encryptedKeySz) + return BUFFER_E; + + XMEMCPY(encryptedKey, pkiMsg + (*idx), length); + *encryptedKeySz = length; + (*idx) += length; + + return 0; +} + +#endif /* HAVE_ECC */ + + +int wc_PKCS7_SetOriEncryptCtx(PKCS7* pkcs7, void* ctx) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + pkcs7->oriEncryptCtx = ctx; + + return 0; +} + + +int wc_PKCS7_SetOriDecryptCtx(PKCS7* pkcs7, void* ctx) +{ + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + pkcs7->oriDecryptCtx = ctx; + + return 0; +} + + +int wc_PKCS7_SetOriDecryptCb(PKCS7* pkcs7, CallbackOriDecrypt cb) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + pkcs7->oriDecryptCb = cb; + + return 0; +} + + +/* return 0 on success */ +int wc_PKCS7_SetWrapCEKCb(PKCS7* pkcs7, CallbackWrapCEK cb) +{ + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + pkcs7->wrapCEKCb = cb; + + return 0; +} + +/* Decrypt ASN.1 OtherRecipientInfo (ori), as defined by: + * + * OtherRecipientInfo ::= SEQUENCE { + * oriType OBJECT IDENTIFIER, + * oriValue ANY DEFINED BY oriType } + * + * pkcs7 - pointer to initialized PKCS7 structure + * pkiMsg - pointer to encoded CMS bundle + * pkiMsgSz - size of pkiMsg, bytes + * idx - [IN/OUT] pointer to index into pkiMsg + * decryptedKey - [OUT] output buf for decrypted content encryption key + * decryptedKeySz - [IN/OUT] size of buffer, size of decrypted key + * recipFound - [OUT] 1 if recipient has been found, 0 if not + * + * Return 0 on success, negative upon error. + */ +static int wc_PKCS7_DecryptOri(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ + int ret, seqSz, oriOIDSz; + word32 oriValueSz, tmpIdx; + byte* oriValue; + byte oriOID[MAX_OID_SZ]; + + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; +#ifndef NO_PKCS7_STREAM + word32 stateIdx = *idx; + long rc; +#endif + + if (pkcs7->oriDecryptCb == NULL) { + WOLFSSL_MSG("You must register an ORI Decrypt callback"); + return BAD_FUNC_ARG; + } + + switch (pkcs7->state) { + + case WC_PKCS7_DECRYPT_ORI: + #ifndef NO_PKCS7_STREAM + /* @TODO for now just get full buffer, needs divided up */ + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + (pkcs7->stream->maxLen - pkcs7->stream->totalRd) + + pkcs7->stream->length, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + /* get OtherRecipientInfo sequence length */ + if (GetLength(pkiMsg, idx, &seqSz, pkiMsgSz) < 0) + return ASN_PARSE_E; + + tmpIdx = *idx; + + /* remove and store oriType OBJECT IDENTIFIER */ + if (GetASNObjectId(pkiMsg, idx, &oriOIDSz, pkiMsgSz) != 0) + return ASN_PARSE_E; + + XMEMCPY(oriOID, pkiMsg + *idx, oriOIDSz); + *idx += oriOIDSz; + + /* get oriValue, increment idx */ + oriValue = pkiMsg + *idx; + oriValueSz = seqSz - (*idx - tmpIdx); + *idx += oriValueSz; + + /* pass oriOID and oriValue to user callback, expect back + decryptedKey and size */ + ret = pkcs7->oriDecryptCb(pkcs7, oriOID, (word32)oriOIDSz, oriValue, + oriValueSz, decryptedKey, decryptedKeySz, + pkcs7->oriDecryptCtx); + + if (ret != 0 || decryptedKey == NULL || *decryptedKeySz == 0) { + /* decrypt operation failed */ + *recipFound = 0; + return PKCS7_RECIP_E; + } + + /* mark recipFound, since we only support one RecipientInfo for now */ + *recipFound = 1; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &stateIdx, idx)) != 0) { + break; + } + #endif + ret = 0; /* success */ + break; + + default: + WOLFSSL_MSG("PKCS7 ORI unknown state"); + ret = BAD_FUNC_ARG; + + } + + return ret; +} + +#if !defined(NO_PWDBASED) && !defined(NO_SHA) + +/* decode ASN.1 PasswordRecipientInfo (pwri), return 0 on success, + * < 0 on error */ +static int wc_PKCS7_DecryptPwri(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ + byte* salt; + byte* cek; + byte* kek; + + byte tmpIv[MAX_CONTENT_IV_SIZE]; + + int ret = 0, length, saltSz, iterations, blockSz, kekKeySz; + int hashOID = WC_SHA; /* default to SHA1 */ + word32 kdfAlgoId, pwriEncAlgoId, keyEncAlgoId, cekSz; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte tag; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = *idx; + long rc; +#endif + + switch (pkcs7->state) { + case WC_PKCS7_DECRYPT_PWRI: + #ifndef NO_PKCS7_STREAM + /*@TODO for now just get full buffer, needs divided up */ + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + (pkcs7->stream->maxLen - pkcs7->stream->totalRd) + + pkcs7->stream->length, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + /* remove KeyDerivationAlgorithmIdentifier */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get KeyDerivationAlgorithmIdentifier */ + if (wc_GetContentType(pkiMsg, idx, &kdfAlgoId, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get KDF params SEQ */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get KDF salt OCTET STRING */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &saltSz, pkiMsgSz) < 0) + return ASN_PARSE_E; + + salt = (byte*)XMALLOC(saltSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (salt == NULL) + return MEMORY_E; + + XMEMCPY(salt, pkiMsg + (*idx), saltSz); + *idx += saltSz; + + /* get KDF iterations */ + if (GetMyVersion(pkiMsg, idx, &iterations, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + /* get KeyEncAlgoId SEQ */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + /* get KeyEncAlgoId */ + if (wc_GetContentType(pkiMsg, idx, &keyEncAlgoId, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + /* get pwriEncAlgoId */ + if (GetAlgoId(pkiMsg, idx, &pwriEncAlgoId, oidBlkType, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + blockSz = wc_PKCS7_GetOIDBlockSize(pwriEncAlgoId); + if (blockSz < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return blockSz; + } + + /* get content-encryption key size, based on algorithm */ + kekKeySz = wc_PKCS7_GetOIDKeySize(pwriEncAlgoId); + if (kekKeySz < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return kekKeySz; + } + + /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + if (tag != ASN_OCTET_STRING) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + if (length != blockSz) { + WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + XMEMCPY(tmpIv, pkiMsg + (*idx), length); + *idx += length; + + /* get EncryptedKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + if (tag != ASN_OCTET_STRING) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + /* allocate temporary space for decrypted key */ + cekSz = length; + cek = (byte*)XMALLOC(cekSz, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (cek == NULL) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + /* generate KEK */ + kek = (byte*)XMALLOC(kekKeySz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (kek == NULL) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + ret = wc_PKCS7_GenerateKEK_PWRI(pkcs7, pkcs7->pass, pkcs7->passSz, + salt, saltSz, kdfAlgoId, hashOID, + iterations, kek, kekKeySz); + if (ret < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ASN_PARSE_E; + } + + /* decrypt CEK with KEK */ + ret = wc_PKCS7_PwriKek_KeyUnWrap(pkcs7, kek, kekKeySz, + pkiMsg + (*idx), length, cek, + cekSz, tmpIv, blockSz, + pwriEncAlgoId); + if (ret < 0) { + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + cekSz = ret; + + if (*decryptedKeySz < cekSz) { + WOLFSSL_MSG("Decrypted key buffer too small for CEK"); + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + XMEMCPY(decryptedKey, cek, cekSz); + *decryptedKeySz = cekSz; + + XFREE(salt, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(kek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(cek, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + /* mark recipFound, since we only support one RecipientInfo for now */ + *recipFound = 1; + *idx += length; + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + ret = 0; /* success */ + break; + + default: + WOLFSSL_MSG("PKCS7 PWRI unknown state"); + ret = BAD_FUNC_ARG; + } + + return ret; +} + +#endif /* NO_PWDBASED | NO_SHA */ + +/* decode ASN.1 KEKRecipientInfo (kekri), return 0 on success, + * < 0 on error */ +static int wc_PKCS7_DecryptKekri(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ + int length, keySz, dateLen, direction; + byte* keyId = NULL; + const byte* datePtr = NULL; + byte dateFormat, tag; + word32 keyIdSz, kekIdSz, keyWrapOID, localIdx; + + int ret = 0; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = *idx; + long rc; +#endif + + WOLFSSL_ENTER("wc_PKCS7_DecryptKekri"); + switch (pkcs7->state) { + case WC_PKCS7_DECRYPT_KEKRI: + #ifndef NO_PKCS7_STREAM + /* @TODO for now just get full buffer, needs divided up */ + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + (pkcs7->stream->maxLen - pkcs7->stream->totalRd) + + pkcs7->stream->length, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + /* remove KEKIdentifier */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + kekIdSz = length; + + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* save keyIdentifier and length */ + keyId = pkiMsg + *idx; + keyIdSz = length; + *idx += keyIdSz; + + /* may have OPTIONAL GeneralizedTime */ + localIdx = *idx; + if ((*idx < kekIdSz) && GetASNTag(pkiMsg, &localIdx, &tag, + pkiMsgSz) == 0 && tag == ASN_GENERALIZED_TIME) { + if (wc_GetDateInfo(pkiMsg + *idx, pkiMsgSz, &datePtr, &dateFormat, + &dateLen) != 0) { + return ASN_PARSE_E; + } + *idx += (dateLen + 1); + } + + /* may have OPTIONAL OtherKeyAttribute */ + localIdx = *idx; + if ((*idx < kekIdSz) && GetASNTag(pkiMsg, &localIdx, &tag, + pkiMsgSz) == 0 && tag == (ASN_SEQUENCE | + ASN_CONSTRUCTED)) { + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* skip it */ + *idx += length; + } + + /* get KeyEncryptionAlgorithmIdentifier */ + if (GetAlgoId(pkiMsg, idx, &keyWrapOID, oidKeyWrapType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get EncryptedKey */ + if (GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + #ifndef NO_AES + direction = AES_DECRYPTION; + #else + direction = DES_DECRYPTION; + #endif + + /* decrypt CEK with KEK */ + if (pkcs7->wrapCEKCb) { + keySz = pkcs7->wrapCEKCb(pkcs7, pkiMsg + *idx, length, keyId, + keyIdSz, NULL, 0, decryptedKey, + *decryptedKeySz, keyWrapOID, + (int)PKCS7_KEKRI, direction); + } + else { + keySz = wc_PKCS7_KeyWrap(pkiMsg + *idx, length, pkcs7->privateKey, + pkcs7->privateKeySz, decryptedKey, *decryptedKeySz, + keyWrapOID, direction); + } + if (keySz <= 0) + return keySz; + + *decryptedKeySz = (word32)keySz; + + /* mark recipFound, since we only support one RecipientInfo for now */ + *recipFound = 1; + *idx += length; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + ret = 0; /* success */ + break; + + default: + WOLFSSL_MSG("PKCS7 KEKRI unknown state"); + ret = BAD_FUNC_ARG; + + } + + (void)keyId; + return ret; +} + + +/* decode ASN.1 KeyAgreeRecipientInfo (kari), return 0 on success, + * < 0 on error */ +static int wc_PKCS7_DecryptKari(PKCS7* pkcs7, byte* in, word32 inSz, + word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ +#ifdef HAVE_ECC + int ret, keySz; + int encryptedKeySz; + int direction = 0; + word32 keyAgreeOID, keyWrapOID; + byte rid[KEYID_SIZE]; - if (padSz != 0) { - plain = (byte*)XMALLOC(desOutSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (plain == NULL) { #ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + byte* encryptedKey; +#else + byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; #endif - return MEMORY_E; - } - XMEMCPY(plain, pkcs7->content, pkcs7->contentSz); - dynamicFlag = 1; - for (i = 0; i < padSz; i++) { - plain[pkcs7->contentSz + i] = padSz; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = (idx) ? *idx : 0; + long rc; +#endif + + WOLFSSL_ENTER("wc_PKCS7_DecryptKari"); + if (pkcs7 == NULL || pkiMsg == NULL || + ((pkcs7->singleCert == NULL || pkcs7->singleCertSz == 0) && + pkcs7->wrapCEKCb == NULL) || + idx == NULL || decryptedKey == NULL || decryptedKeySz == NULL) { + return BAD_FUNC_ARG; + } + + switch (pkcs7->state) { + case WC_PKCS7_DECRYPT_KARI: { + #ifndef NO_PKCS7_STREAM + /* @TODO for now just get full buffer, needs divided up */ + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + (pkcs7->stream->maxLen - pkcs7->stream->totalRd) + + pkcs7->stream->length, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + WC_PKCS7_KARI* kari; + + kari = wc_PKCS7_KariNew(pkcs7, WC_PKCS7_DECODE); + if (kari == NULL) + return MEMORY_E; + + #ifdef WOLFSSL_SMALL_STACK + encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (encryptedKey == NULL) { + wc_PKCS7_KariFree(kari); + return MEMORY_E; + } + #endif + encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + + /* parse cert and key */ + if (pkcs7->singleCert != NULL) { + ret = wc_PKCS7_KariParseRecipCert(kari, (byte*)pkcs7->singleCert, + pkcs7->singleCertSz, pkcs7->privateKey, + pkcs7->privateKeySz); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + } + + /* remove OriginatorIdentifierOrKey */ + ret = wc_PKCS7_KariGetOriginatorIdentifierOrKey(kari, pkiMsg, + pkiMsgSz, idx); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + + /* try and remove optional UserKeyingMaterial */ + ret = wc_PKCS7_KariGetUserKeyingMaterial(kari, pkiMsg, pkiMsgSz, idx); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + + /* remove KeyEncryptionAlgorithmIdentifier */ + ret = wc_PKCS7_KariGetKeyEncryptionAlgorithmId(kari, pkiMsg, + pkiMsgSz, idx, &keyAgreeOID, &keyWrapOID); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + + /* if user has not explicitly set keyAgreeOID, set from one in bundle */ + if (pkcs7->keyAgreeOID == 0) + pkcs7->keyAgreeOID = keyAgreeOID; + + /* set direction based on key wrap algorithm */ + switch (keyWrapOID) { + #ifndef NO_AES + #ifdef WOLFSSL_AES_128 + case AES128_WRAP: + #endif + #ifdef WOLFSSL_AES_192 + case AES192_WRAP: + #endif + #ifdef WOLFSSL_AES_256 + case AES256_WRAP: + #endif + direction = AES_DECRYPTION; + break; + #endif + default: + WOLFSSL_MSG("AES key wrap algorithm unsupported"); + if (pkcs7->wrapCEKCb) { + WOLFSSL_MSG("Direction not set!"); + break; /* if unwrapping callback is set then do not + * force restriction of supported wrap + * algorithms */ + } + + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return BAD_KEYWRAP_ALG_E; + } + + /* remove RecipientEncryptedKeys */ + ret = wc_PKCS7_KariGetRecipientEncryptedKeys(kari, pkiMsg, pkiMsgSz, + idx, recipFound, encryptedKey, &encryptedKeySz, rid); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + + /* decrypt CEK with KEK */ + if (pkcs7->wrapCEKCb) { + word32 tmpKeySz = 0; + byte* tmpKeyDer = NULL; + + ret = wc_ecc_export_x963(kari->senderKey, NULL, &tmpKeySz); + if (ret != LENGTH_ONLY_E) { + return ret; + } + + /* buffer space for algorithm/curve */ + tmpKeySz += MAX_SEQ_SZ; + tmpKeySz += 2 * MAX_ALGO_SZ; + + /* buffer space for public key sequence */ + tmpKeySz += MAX_SEQ_SZ; + tmpKeySz += TRAILING_ZERO; + + tmpKeyDer = (byte*)XMALLOC(tmpKeySz, pkcs7->heap, + DYNAMIC_TYPE_TMP_BUFFER); + if (tmpKeyDer == NULL) { + return MEMORY_E; + } + + ret = wc_EccPublicKeyToDer(kari->senderKey, tmpKeyDer, + tmpKeySz, 1); + if (ret < 0) { + XFREE(tmpKeyDer, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + return ret; + } + tmpKeySz = (word32)ret; + + keySz = pkcs7->wrapCEKCb(pkcs7, encryptedKey, encryptedKeySz, + rid, KEYID_SIZE, tmpKeyDer, tmpKeySz, + decryptedKey, *decryptedKeySz, + keyWrapOID, (int)PKCS7_KARI, direction); + XFREE(tmpKeyDer, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + + if (keySz > 0) { + /* If unwrapping was successful then consider recipient + * found. Checking for NULL singleCert to confirm previous + * SID check was not done */ + if (pkcs7->singleCert == NULL) + *recipFound = 1; + } + } + else { + /* create KEK */ + ret = wc_PKCS7_KariGenerateKEK(kari, keyWrapOID, pkcs7->keyAgreeOID); + if (ret != 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return ret; + } + + /* decrypt CEK with KEK */ + keySz = wc_PKCS7_KeyWrap(encryptedKey, encryptedKeySz, kari->kek, + kari->kekSz, decryptedKey, *decryptedKeySz, + keyWrapOID, direction); + } + if (keySz <= 0) { + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + return keySz; + } + *decryptedKeySz = (word32)keySz; + + wc_PKCS7_KariFree(kari); + #ifdef WOLFSSL_SMALL_STACK + XFREE(encryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + #endif + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + ret = 0; /* success */ } + break; + + default: + WOLFSSL_MSG("PKCS7 kari unknown state"); + ret = BAD_FUNC_ARG; - } else { - plain = pkcs7->content; - desOutSz = pkcs7->contentSz; } - encryptedContent = (byte*)XMALLOC(desOutSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (encryptedContent == NULL) { - if (dynamicFlag) - XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + (void)pkiMsg; + (void)pkiMsgSz; + + return ret; +#else + (void)in; + (void)inSz; + (void)pkcs7; + (void)idx; + (void)decryptedKey; + (void)decryptedKeySz; + (void)recipFound; + + return NOT_COMPILED_IN; +#endif /* HAVE_ECC */ +} + + +/* decode ASN.1 RecipientInfos SET, return 0 on success, < 0 on error */ +static int wc_PKCS7_DecryptRecipientInfos(PKCS7* pkcs7, byte* in, + word32 inSz, word32* idx, byte* decryptedKey, + word32* decryptedKeySz, int* recipFound) +{ + word32 savedIdx; + int version, ret = 0, length; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte tag; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx; + long rc; #endif - return MEMORY_E; + + if (pkcs7 == NULL || pkiMsg == NULL || idx == NULL || + decryptedKey == NULL || decryptedKeySz == NULL || + recipFound == NULL) { + return BAD_FUNC_ARG; } - /* put together IV OCTET STRING */ - ivOctetStringSz = SetOctetString(DES_BLOCK_SIZE, ivOctetString); + WOLFSSL_ENTER("wc_PKCS7_DecryptRecipientInfos"); +#ifndef NO_PKCS7_STREAM + tmpIdx = *idx; +#endif - /* build up our ContentEncryptionAlgorithmIdentifier sequence, - * adding (ivOctetStringSz + DES_BLOCK_SIZE) for IV OCTET STRING */ - contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, - blkType, ivOctetStringSz + DES_BLOCK_SIZE); + /* check if in the process of decrypting */ + switch (pkcs7->state) { + case WC_PKCS7_DECRYPT_KTRI: + case WC_PKCS7_DECRYPT_KTRI_2: + case WC_PKCS7_DECRYPT_KTRI_3: + #ifndef NO_RSA + ret = wc_PKCS7_DecryptKtri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, recipFound); + #else + return NOT_COMPILED_IN; + #endif + break; - if (contentEncAlgoSz == 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (dynamicFlag) - XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + case WC_PKCS7_DECRYPT_KARI: + ret = wc_PKCS7_DecryptKari(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, recipFound); + break; + + case WC_PKCS7_DECRYPT_KEKRI: + ret = wc_PKCS7_DecryptKekri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, recipFound); + break; + + case WC_PKCS7_DECRYPT_PWRI: + #if !defined(NO_PWDBASED) && !defined(NO_SHA) + ret = wc_PKCS7_DecryptPwri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, recipFound); + #else + return NOT_COMPILED_IN; + #endif + break; + + case WC_PKCS7_DECRYPT_ORI: + ret = wc_PKCS7_DecryptOri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, recipFound); + break; + + default: + /* not in decrypting state */ + break; + } + + if (ret < 0) { + return ret; + } + + savedIdx = *idx; +#ifndef NO_PKCS7_STREAM + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, inSz); + if (rc < 0) { + return (int)rc; + } + pkiMsgSz = (word32)rc; + if (pkcs7->stream->length > 0) + pkiMsg = pkcs7->stream->buffer; +#endif + + /* when looking for next recipient, use first sequence and version to + * indicate there is another, if not, move on */ + while(*recipFound == 0) { + + /* remove RecipientInfo, if we don't have a SEQUENCE, back up idx to + * last good saved one */ + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) > 0) { + + #ifndef NO_RSA + /* found ktri */ + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_KTRI); + ret = wc_PKCS7_DecryptKtri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, + recipFound); + if (ret != 0) + return ret; + #else + return NOT_COMPILED_IN; + #endif + } + else { + word32 localIdx; + /* kari is IMPLICIT[1] */ + *idx = savedIdx; + localIdx = *idx; + + if (GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) != 0) { + /* no room for recipient info */ + break; + } + + if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + (*idx)++; + if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) { + *idx = savedIdx; + break; + } + + if (version != 3) + return ASN_VERSION_E; + + /* found kari */ + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_KARI); + ret = wc_PKCS7_DecryptKari(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, + recipFound); + if (ret != 0) + return ret; + + /* kekri is IMPLICIT[2] */ + } else if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 2)) { + (*idx)++; + + if (GetLength(pkiMsg, idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) { + *idx = savedIdx; + break; + } + + if (version != 4) + return ASN_VERSION_E; + + /* found kekri */ + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_KEKRI); + ret = wc_PKCS7_DecryptKekri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, + recipFound); + if (ret != 0) + return ret; + + /* pwri is IMPLICIT[3] */ + } else if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 3)) { + #if !defined(NO_PWDBASED) && !defined(NO_SHA) + (*idx)++; + + if (GetLength(pkiMsg, idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) { + *idx = savedIdx; + break; + } + + if (version != 0) + return ASN_VERSION_E; + + /* found pwri */ + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_PWRI); + ret = wc_PKCS7_DecryptPwri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, + recipFound); + if (ret != 0) + return ret; + #else + return NOT_COMPILED_IN; + #endif + + /* ori is IMPLICIT[4] */ + } else if (tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 4)) { + (*idx)++; + + /* found ori */ + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_DECRYPT_ORI); + ret = wc_PKCS7_DecryptOri(pkcs7, in, inSz, idx, + decryptedKey, decryptedKeySz, + recipFound); + if (ret != 0) + return ret; + + } else { + /* failed to find RecipientInfo, restore idx and continue */ + *idx = savedIdx; + break; + } + } + + /* update good idx */ + savedIdx = *idx; + } + + return ret; +} + + +/* Parse encoded EnvelopedData bundle up to RecipientInfo set. + * + * return size of RecipientInfo SET on success, negative upon error */ +static int wc_PKCS7_ParseToRecipientInfoSet(PKCS7* pkcs7, byte* in, + word32 inSz, word32* idx, + int type) +{ + int version = 0, length, ret = 0; + word32 contentType; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte tag; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = 0; + long rc; #endif + + if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0 || idx == NULL) + return BAD_FUNC_ARG; + + if ((type != ENVELOPED_DATA) && (type != AUTH_ENVELOPED_DATA) && + pkcs7->contentOID != FIRMWARE_PKG_DATA) return BAD_FUNC_ARG; + +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { + return ret; + } } +#endif - /* encrypt content */ - if (pkcs7->encryptOID == DESb) { - Des des; + switch (pkcs7->state) { + case WC_PKCS7_INFOSET_START: + case WC_PKCS7_INFOSET_BER: + case WC_PKCS7_INFOSET_STAGE1: + case WC_PKCS7_INFOSET_STAGE2: + case WC_PKCS7_INFOSET_END: + break; - ret = wc_Des_SetKey(&des, contentKeyPlain, tmpIv, DES_ENCRYPTION); + default: + WOLFSSL_MSG("Warning, setting PKCS7 info state to start"); + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_INFOSET_START); + } - if (ret == 0) - wc_Des_CbcEncrypt(&des, encryptedContent, plain, desOutSz); + switch (pkcs7->state) { + case WC_PKCS7_INFOSET_START: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_SEQ_SZ + + ASN_TAG_SZ, &pkiMsg, idx)) != 0) { + return ret; + } - if (ret != 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (dynamicFlag) - XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_SEQ_PEEK, in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + /* read past ContentInfo, verify type is envelopedData */ + if (ret == 0 && GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + { + ret = ASN_PARSE_E; + } + + if (ret == 0 && length == 0 && pkiMsg[(*idx)-1] == 0x80) { + #ifdef ASN_BER_TO_DER + word32 len; + + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_INFOSET_BER); + FALL_THROUGH; + + /* full buffer is needed for conversion */ + case WC_PKCS7_INFOSET_BER: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->maxLen - pkcs7->stream->length, + &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + + len = 0; + + ret = wc_BerToDer(pkiMsg, pkiMsgSz, NULL, &len); + if (ret != LENGTH_ONLY_E) + return ret; + pkcs7->der = (byte*)XMALLOC(len, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->der == NULL) + return MEMORY_E; + ret = wc_BerToDer(pkiMsg, pkiMsgSz, pkcs7->der, &len); + if (ret < 0) + return ret; + + pkiMsg = in = pkcs7->der; + pkiMsgSz = pkcs7->derSz = len; + *idx = 0; + + if (GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + #else + return BER_INDEF_E; + #endif + } + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_INFOSET_STAGE1); + FALL_THROUGH; + + case WC_PKCS7_INFOSET_STAGE1: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_OID_SZ + + MAX_LENGTH_SZ + ASN_TAG_SZ, &pkiMsg, idx)) != 0) { + return ret; + } + + pkiMsgSz = (pkcs7->stream->length > 0)? pkcs7->stream->length :inSz; + #endif + if (pkcs7->contentOID != FIRMWARE_PKG_DATA || + type == AUTH_ENVELOPED_DATA) { + if (ret == 0 && wc_GetContentType(pkiMsg, idx, &contentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0) { + if (type == ENVELOPED_DATA && contentType != ENVELOPED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type EnvelopedData"); + ret = PKCS7_OID_E; + } else if (type == AUTH_ENVELOPED_DATA && + contentType != AUTH_ENVELOPED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type AuthEnvelopedData"); + ret = PKCS7_OID_E; + } + } + + if (ret == 0 && GetASNTag(pkiMsg, idx, &tag, pkiMsgSz) != 0) + ret = ASN_PARSE_E; + + if (ret == 0 && tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC + | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength_ex(pkiMsg, idx, &length, pkiMsgSz, + NO_USER_CHECK) < 0) + ret = ASN_PARSE_E; + } + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_INFOSET_STAGE2); + FALL_THROUGH; + + case WC_PKCS7_INFOSET_STAGE2: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_SEQ_SZ + + MAX_VERSION_SZ, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + /* remove EnvelopedData and version */ + if (pkcs7->contentOID != FIRMWARE_PKG_DATA || + type == AUTH_ENVELOPED_DATA) { + if (ret == 0 && GetSequence(pkiMsg, idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + + pkcs7->stream->varOne = version; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_INFOSET_END); + FALL_THROUGH; + + case WC_PKCS7_INFOSET_END: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + MAX_SET_SZ, &pkiMsg, idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + version = pkcs7->stream->varOne; + #endif + + if (type == ENVELOPED_DATA) { + /* TODO :: make this more accurate */ + if ((pkcs7->publicKeyOID == RSAk && + (version != 0 && version != 2)) + #ifdef HAVE_ECC + || (pkcs7->publicKeyOID == ECDSAk && + (version != 0 && version != 2 && version != 3)) + #endif + ) { + WOLFSSL_MSG("PKCS#7 envelopedData version incorrect"); + ret = ASN_VERSION_E; + } + } else { + /* AuthEnvelopedData version MUST be 0 */ + if (version != 0) { + WOLFSSL_MSG("PKCS#7 AuthEnvelopedData needs to be of version 0"); + ret = ASN_VERSION_E; + } + } + + /* remove RecipientInfo set, get length of set */ + if (ret == 0 && GetSet(pkiMsg, idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, idx)) != 0) { + break; + } + #endif + + if (ret == 0) + ret = length; + + break; + + default: + WOLFSSL_MSG("Bad PKCS7 info set state"); + ret = BAD_FUNC_ARG; + break; + } + + return ret; +} + + +/* Import secret/private key into a PKCS7 structure. Used for setting + * the secret key for decryption a EnvelopedData KEKRI RecipientInfo. + * + * Returns 0 on success, negative upon error */ +WOLFSSL_API int wc_PKCS7_SetKey(PKCS7* pkcs7, byte* key, word32 keySz) +{ + if (pkcs7 == NULL || key == NULL || keySz == 0) + return BAD_FUNC_ARG; + + pkcs7->privateKey = key; + pkcs7->privateKeySz = keySz; + + return 0; +} + + +/* append data to encrypted content cache in PKCS7 structure + * return 0 on success, negative on error */ +static int PKCS7_CacheEncryptedContent(PKCS7* pkcs7, byte* in, word32 inSz) +{ + byte* oldCache; + word32 oldCacheSz; + + if (pkcs7 == NULL || in == NULL) + return BAD_FUNC_ARG; + + /* save pointer to old cache */ + oldCache = pkcs7->cachedEncryptedContent; + oldCacheSz = pkcs7->cachedEncryptedContentSz; + + /* re-allocate new buffer to fit appended data */ + pkcs7->cachedEncryptedContent = (byte*)XMALLOC(oldCacheSz + inSz, + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->cachedEncryptedContent == NULL) { + pkcs7->cachedEncryptedContentSz = 0; + XFREE(oldCache, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + if (oldCache != NULL) { + XMEMCPY(pkcs7->cachedEncryptedContent, oldCache, oldCacheSz); + } + XMEMCPY(pkcs7->cachedEncryptedContent + oldCacheSz, in, inSz); + pkcs7->cachedEncryptedContentSz += inSz; + + XFREE(oldCache, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return 0; +} + + +/* unwrap and decrypt PKCS#7 envelopedData object, return decoded size */ +WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* in, + word32 inSz, byte* output, + word32 outputSz) +{ + int recipFound = 0; + int ret, length = 0; + word32 idx = 0; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = 0; + long rc; #endif + word32 contentType, encOID = 0; + word32 decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + + int expBlockSz = 0, blockKeySz = 0; + byte tmpIvBuf[MAX_CONTENT_IV_SIZE]; + byte* tmpIv = tmpIvBuf; + + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte* decryptedKey = NULL; + int encryptedContentTotalSz = 0; + int encryptedContentSz = 0; + byte padLen; + byte* encryptedContent = NULL; + int explicitOctet = 0; + word32 localIdx; + byte tag; + + if (pkcs7 == NULL) + return BAD_FUNC_ARG; + + if (pkiMsg == NULL || pkiMsgSz == 0 || + output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + +#ifndef NO_PKCS7_STREAM + (void)tmpIv; /* help out static analysis */ + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { return ret; } } - else if (pkcs7->encryptOID == DES3b) { - Des3 des3; +#endif - ret = wc_Des3_SetKey(&des3, contentKeyPlain, tmpIv, DES_ENCRYPTION); + switch (pkcs7->state) { + case WC_PKCS7_START: + case WC_PKCS7_INFOSET_START: + case WC_PKCS7_INFOSET_BER: + case WC_PKCS7_INFOSET_STAGE1: + case WC_PKCS7_INFOSET_STAGE2: + case WC_PKCS7_INFOSET_END: + ret = wc_PKCS7_ParseToRecipientInfoSet(pkcs7, pkiMsg, pkiMsgSz, + &idx, ENVELOPED_DATA); + if (ret < 0) { + break; + } - if (ret == 0) - ret = wc_Des3_CbcEncrypt(&des3, encryptedContent, plain, desOutSz); + #ifdef ASN_BER_TO_DER + /* check if content was BER and has been converted to DER */ + if (pkcs7->derSz > 0) + pkiMsg = in = pkcs7->der; + #endif - if (ret != 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (dynamicFlag) - XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); + decryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (decryptedKey == NULL) + return MEMORY_E; + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_2); + #ifndef NO_PKCS7_STREAM + tmpIdx = idx; + pkcs7->stream->aad = decryptedKey; + #endif + FALL_THROUGH; + + case WC_PKCS7_ENV_2: + #ifndef NO_PKCS7_STREAM + /* store up enough buffer for initial info set decode */ + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + MAX_VERSION_SZ + ASN_TAG_SZ, &pkiMsg, &idx)) != 0) { + return ret; + } + #endif + FALL_THROUGH; + + case WC_PKCS7_DECRYPT_KTRI: + case WC_PKCS7_DECRYPT_KTRI_2: + case WC_PKCS7_DECRYPT_KTRI_3: + case WC_PKCS7_DECRYPT_KARI: + case WC_PKCS7_DECRYPT_KEKRI: + case WC_PKCS7_DECRYPT_PWRI: + case WC_PKCS7_DECRYPT_ORI: + #ifndef NO_PKCS7_STREAM + decryptedKey = pkcs7->stream->aad; + decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + #endif + + ret = wc_PKCS7_DecryptRecipientInfos(pkcs7, in, inSz, &idx, + decryptedKey, &decryptedKeySz, + &recipFound); + if (ret == 0 && recipFound == 0) { + WOLFSSL_MSG("No recipient found in envelopedData that matches input"); + ret = PKCS7_RECIP_E; + } + + if (ret != 0) + break; + #ifndef NO_PKCS7_STREAM + tmpIdx = idx; + pkcs7->stream->aadSz = decryptedKeySz; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_3); + FALL_THROUGH; + + case WC_PKCS7_ENV_3: + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + MAX_VERSION_SZ + ASN_TAG_SZ + + MAX_LENGTH_SZ, &pkiMsg, &idx)) + != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #else + ret = 0; + #endif + + /* remove EncryptedContentInfo */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, + pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + blockKeySz = wc_PKCS7_GetOIDKeySize(encOID); + if (ret == 0 && blockKeySz < 0) { + ret = blockKeySz; + } + + expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID); + if (ret == 0 && expBlockSz < 0) { + ret = expBlockSz; + } + + /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) != 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && tag != 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 + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, encOID, expBlockSz, length); + pkcs7->stream->contentSz = blockKeySz; + pkcs7->stream->expected = length + MAX_LENGTH_SZ + MAX_LENGTH_SZ + + ASN_TAG_SZ + ASN_TAG_SZ; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_4); + FALL_THROUGH; + + case WC_PKCS7_ENV_4: + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + wc_PKCS7_StreamGetVar(pkcs7, 0, 0, &length); + tmpIv = pkcs7->stream->tmpIv; + if (tmpIv == NULL) { + /* check added to help out static analysis tool */ + ret = MEMORY_E; + break; + } + #else + ret = 0; + #endif + + XMEMCPY(tmpIv, &pkiMsg[idx], length); + idx += length; + + explicitOctet = 0; + localIdx = idx; + if (GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) == 0 && + tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)) { + explicitOctet = 1; + } + + /* read encryptedContent, cont[0] */ + if (tag != (ASN_CONTEXT_SPECIFIC | 0) && + tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)) { + ret = ASN_PARSE_E; + } + idx++; + + if (ret == 0 && GetLength(pkiMsg, &idx, &encryptedContentTotalSz, + pkiMsgSz) <= 0) { + ret = ASN_PARSE_E; + } + + if (ret != 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + pkcs7->stream->expected = encryptedContentTotalSz; + wc_PKCS7_StreamGetVar(pkcs7, &encOID, &expBlockSz, 0); + wc_PKCS7_StreamStoreVar(pkcs7, encOID, expBlockSz, explicitOctet); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_ENV_5); + FALL_THROUGH; + + case WC_PKCS7_ENV_5: + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + return ret; + } + + wc_PKCS7_StreamGetVar(pkcs7, &encOID, &expBlockSz, &explicitOctet); + tmpIv = pkcs7->stream->tmpIv; + encryptedContentTotalSz = pkcs7->stream->expected; + + /* restore decrypted key */ + decryptedKey = pkcs7->stream->aad; + decryptedKeySz = pkcs7->stream->aadSz; + blockKeySz = pkcs7->stream->contentSz; + #else + ret = 0; + #endif + + if (explicitOctet) { + /* encrypted content may be fragmented into multiple + * consecutive OCTET STRINGs, if so loop through + * collecting and caching encrypted content bytes */ + localIdx = idx; + while (idx < (localIdx + encryptedContentTotalSz)) { + + if (GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && (tag != ASN_OCTET_STRING)) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetLength(pkiMsg, &idx, + &encryptedContentSz, pkiMsgSz) <= 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0) { + ret = PKCS7_CacheEncryptedContent(pkcs7, &pkiMsg[idx], + encryptedContentSz); + } + + if (ret != 0) { + break; + } + + /* advance idx past encrypted content */ + idx += encryptedContentSz; + } + + if (ret != 0) { + break; + } + + } else { + /* cache encrypted content, no OCTET STRING */ + ret = PKCS7_CacheEncryptedContent(pkcs7, &pkiMsg[idx], + encryptedContentTotalSz); + if (ret != 0) { + break; + } + idx += encryptedContentTotalSz; + } + + /* use cached content */ + encryptedContent = pkcs7->cachedEncryptedContent; + encryptedContentSz = pkcs7->cachedEncryptedContentSz; + + /* decrypt encryptedContent */ + ret = wc_PKCS7_DecryptContent(pkcs7, encOID, decryptedKey, + blockKeySz, tmpIv, expBlockSz, NULL, 0, NULL, 0, + encryptedContent, encryptedContentSz, encryptedContent); + if (ret != 0) { + break; + } + + padLen = encryptedContent[encryptedContentSz-1]; + + /* copy plaintext to output */ + if (padLen > encryptedContentSz || + (word32)(encryptedContentSz - padLen) > outputSz) { + ret = BUFFER_E; + break; + } + XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + + /* free memory, zero out keys */ + ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ); + XFREE(decryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->cachedEncryptedContent != NULL) { + XFREE(pkcs7->cachedEncryptedContent, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + pkcs7->cachedEncryptedContent = NULL; + pkcs7->cachedEncryptedContentSz = 0; + } + + ret = encryptedContentSz - padLen; + #ifndef NO_PKCS7_STREAM + pkcs7->stream->aad = NULL; + pkcs7->stream->aadSz = 0; + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); + break; + + default: + WOLFSSL_MSG("PKCS#7 unknown decode enveloped state"); + ret = BAD_FUNC_ARG; + } + +#ifndef NO_PKCS7_STREAM + if (ret < 0 && ret != WC_PKCS7_WANT_READ_E) { + wc_PKCS7_ResetStream(pkcs7); + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); + if (pkcs7->cachedEncryptedContent != NULL) { + XFREE(pkcs7->cachedEncryptedContent, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + pkcs7->cachedEncryptedContent = NULL; + pkcs7->cachedEncryptedContentSz = 0; + } + } +#else + if (decryptedKey != NULL && ret < 0) { + ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ); + XFREE(decryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } + if (pkcs7->cachedEncryptedContent != NULL && ret < 0) { + XFREE(pkcs7->cachedEncryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + pkcs7->cachedEncryptedContent = NULL; + pkcs7->cachedEncryptedContentSz = 0; + } #endif + return ret; +} + + +/* build PKCS#7 authEnvelopedData content type, return enveloped size */ +int wc_PKCS7_EncodeAuthEnvelopedData(PKCS7* pkcs7, byte* output, + word32 outputSz) +{ +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) + int ret, idx = 0; + int totalSz, encryptedOutSz; + + int contentInfoSeqSz, outerContentTypeSz, outerContentSz; + byte contentInfoSeq[MAX_SEQ_SZ]; + byte outerContentType[MAX_ALGO_SZ]; + byte outerContent[MAX_SEQ_SZ]; + + int envDataSeqSz, verSz; + byte envDataSeq[MAX_SEQ_SZ]; + byte ver[MAX_VERSION_SZ]; + + WC_RNG rng; + int blockSz, blockKeySz; + byte* encryptedContent; + + Pkcs7EncodedRecip* tmpRecip = NULL; + int recipSz, recipSetSz; + byte recipSet[MAX_SET_SZ]; + + int encContentOctetSz, encContentSeqSz, contentTypeSz; + int contentEncAlgoSz, nonceOctetStringSz, macOctetStringSz; + byte encContentSeq[MAX_SEQ_SZ]; + byte contentType[MAX_ALGO_SZ]; + byte contentEncAlgo[MAX_ALGO_SZ]; + byte nonceOctetString[MAX_OCTET_STR_SZ]; + byte encContentOctet[MAX_OCTET_STR_SZ]; + byte macOctetString[MAX_OCTET_STR_SZ]; + + byte authTag[AES_BLOCK_SIZE]; + byte nonce[GCM_NONCE_MID_SZ]; /* GCM nonce is larger than CCM */ + byte macInt[MAX_VERSION_SZ]; + word32 nonceSz = 0, macIntSz = 0; + + /* authAttribs */ + byte* flatAuthAttribs = NULL; + byte authAttribSet[MAX_SET_SZ]; + EncodedAttrib authAttribs[MAX_AUTH_ATTRIBS_SZ]; + word32 authAttribsSz = 0, authAttribsCount = 0; + word32 authAttribsSetSz = 0; + + byte* aadBuffer = NULL; + word32 aadBufferSz = 0; + byte authAttribAadSet[MAX_SET_SZ]; + word32 authAttribsAadSetSz = 0; + + /* unauthAttribs */ + byte* flatUnauthAttribs = NULL; + byte unauthAttribSet[MAX_SET_SZ]; + EncodedAttrib unauthAttribs[MAX_UNAUTH_ATTRIBS_SZ]; + word32 unauthAttribsSz = 0, unauthAttribsCount = 0; + word32 unauthAttribsSetSz = 0; + + + PKCS7Attrib contentTypeAttrib; + byte contentTypeValue[MAX_OID_SZ]; + /* contentType OID (1.2.840.113549.1.9.3) */ + const byte contentTypeOid[] = + { ASN_OBJECT_ID, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0d, 0x01, + 0x09, 0x03 }; + + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0) + return BAD_FUNC_ARG; + + if (output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + + switch (pkcs7->encryptOID) { +#ifdef HAVE_AESGCM + #ifdef WOLFSSL_AES_128 + case AES128GCMb: + break; + #endif + #ifdef WOLFSSL_AES_192 + case AES192GCMb: + break; + #endif + #ifdef WOLFSSL_AES_256 + case AES256GCMb: + break; + #endif +#endif +#ifdef HAVE_AESCCM + #ifdef WOLFSSL_AES_128 + case AES128CCMb: + break; + #endif + #ifdef WOLFSSL_AES_192 + case AES192CCMb: + break; + #endif + #ifdef WOLFSSL_AES_256 + case AES256CCMb: + break; + #endif +#endif + default: + WOLFSSL_MSG("CMS AuthEnvelopedData must use AES-GCM or AES-CCM"); + return BAD_FUNC_ARG; + } + + blockKeySz = wc_PKCS7_GetOIDKeySize(pkcs7->encryptOID); + if (blockKeySz < 0) + return blockKeySz; + + blockSz = wc_PKCS7_GetOIDBlockSize(pkcs7->encryptOID); + if (blockSz < 0) + return blockSz; + + /* outer content type */ + ret = wc_SetContentType(AUTH_ENVELOPED_DATA, outerContentType, + sizeof(outerContentType)); + if (ret < 0) + return ret; + + outerContentTypeSz = ret; + + /* version, defined as 0 in RFC 5083 */ + verSz = SetMyVersion(0, ver, 0); + + /* generate random content encryption key */ + ret = PKCS7_GenerateContentEncryptionKey(pkcs7, blockKeySz); + if (ret != 0) { + return ret; + } + + /* build RecipientInfo, only if user manually set singleCert and size */ + if (pkcs7->singleCert != NULL && pkcs7->singleCertSz > 0) { + switch (pkcs7->publicKeyOID) { + #ifndef NO_RSA + case RSAk: + ret = wc_PKCS7_AddRecipient_KTRI(pkcs7, pkcs7->singleCert, + pkcs7->singleCertSz, 0); + break; + #endif + #ifdef HAVE_ECC + case ECDSAk: + ret = wc_PKCS7_AddRecipient_KARI(pkcs7, pkcs7->singleCert, + pkcs7->singleCertSz, + pkcs7->keyWrapOID, + pkcs7->keyAgreeOID, pkcs7->ukm, + pkcs7->ukmSz, 0); + break; + #endif + + default: + WOLFSSL_MSG("Unsupported RecipientInfo public key type"); + return BAD_FUNC_ARG; + }; + + if (ret < 0) { + WOLFSSL_MSG("Failed to create RecipientInfo"); return ret; } } - encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, - desOutSz, encContentOctet); + recipSz = wc_PKCS7_GetRecipientListSize(pkcs7); + if (recipSz < 0) { + return ret; + + } else if (recipSz == 0) { + WOLFSSL_MSG("You must add at least one CMS recipient"); + return PKCS7_RECIP_E; + } + recipSetSz = SetSet(recipSz, recipSet); + + /* generate random nonce and IV for encryption */ + switch (pkcs7->encryptOID) { +#ifdef HAVE_AESGCM + #ifdef WOLFSSL_AES_128 + case AES128GCMb: + FALL_THROUGH; + #endif + #ifdef WOLFSSL_AES_192 + case AES192GCMb: + FALL_THROUGH; + #endif + #ifdef WOLFSSL_AES_256 + case AES256GCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + /* GCM nonce is GCM_NONCE_MID_SZ (12) */ + nonceSz = GCM_NONCE_MID_SZ; + break; + #endif +#endif /* HAVE_AESGCM */ +#ifdef HAVE_AESCCM + #ifdef WOLFSSL_AES_128 + case AES128CCMb: + FALL_THROUGH; + #endif + #ifdef WOLFSSL_AES_192 + case AES192CCMb: + FALL_THROUGH; + #endif + #ifdef WOLFSSL_AES_256 + case AES256CCMb: + #endif + #if defined(WOLFSSL_AES_128) || defined(WOLFSSL_AES_192) || \ + defined(WOLFSSL_AES_256) + /* CCM nonce is CCM_NONCE_MIN_SZ (7) */ + nonceSz = CCM_NONCE_MIN_SZ; + break; + #endif +#endif /* HAVE_AESCCM */ + } + + ret = wc_InitRng_ex(&rng, pkcs7->heap, pkcs7->devId); + if (ret != 0) + return ret; + + ret = wc_PKCS7_GenerateBlock(pkcs7, &rng, nonce, nonceSz); + wc_FreeRng(&rng); + if (ret != 0) { + return ret; + } + + + /* authAttribs: add contentType attrib if needed */ + if (pkcs7->contentOID != DATA) { + + /* if type is not id-data, contentType attribute MUST be added */ + contentTypeAttrib.oid = contentTypeOid; + contentTypeAttrib.oidSz = sizeof(contentTypeOid); + + /* try to set from contentOID first, known types */ + ret = wc_SetContentType(pkcs7->contentOID, contentTypeValue, + sizeof(contentTypeValue)); + if (ret > 0) { + contentTypeAttrib.value = contentTypeValue; + contentTypeAttrib.valueSz = ret; + + /* otherwise, try to set from custom content type */ + } else { + if (pkcs7->contentTypeSz == 0) { + WOLFSSL_MSG("CMS pkcs7->contentType must be set if " + "contentOID is not"); + return BAD_FUNC_ARG; + } + contentTypeAttrib.value = pkcs7->contentType; + contentTypeAttrib.valueSz = pkcs7->contentTypeSz; + } + + authAttribsSz += EncodeAttributes(authAttribs, 1, + &contentTypeAttrib, 1); + authAttribsCount += 1; + } + + /* authAttribs: add in user authenticated attributes */ + if (pkcs7->authAttribs != NULL && pkcs7->authAttribsSz > 0) { + authAttribsSz += EncodeAttributes(authAttribs + authAttribsCount, + MAX_AUTH_ATTRIBS_SZ - authAttribsCount, + pkcs7->authAttribs, + pkcs7->authAttribsSz); + authAttribsCount += pkcs7->authAttribsSz; + } + + /* authAttribs: flatten authAttribs */ + if (authAttribsSz > 0 && authAttribsCount > 0) { + flatAuthAttribs = (byte*)XMALLOC(authAttribsSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs == NULL) { + return MEMORY_E; + } + + FlattenAttributes(pkcs7, flatAuthAttribs, authAttribs, + authAttribsCount); + + authAttribsSetSz = SetImplicit(ASN_SET, 1, authAttribsSz, + authAttribSet); + + /* From RFC5083, "For the purpose of constructing the AAD, the + * IMPLICIT [1] tag in the authAttrs field is not used for the + * DER encoding: rather a universal SET OF tag is used. */ + authAttribsAadSetSz = SetSet(authAttribsSz, authAttribAadSet); + + /* allocate temp buffer to hold alternate attrib encoding for aad */ + aadBuffer = (byte*)XMALLOC(authAttribsSz + authAttribsAadSetSz, + pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (aadBuffer == NULL) { + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + /* build up alternate attrib encoding for aad */ + aadBufferSz = 0; + XMEMCPY(aadBuffer + aadBufferSz, authAttribAadSet, authAttribsAadSetSz); + aadBufferSz += authAttribsAadSetSz; + XMEMCPY(aadBuffer + aadBufferSz, flatAuthAttribs, authAttribsSz); + aadBufferSz += authAttribsSz; + } + + /* build up unauthenticated attributes (unauthAttrs) */ + if (pkcs7->unauthAttribsSz > 0) { + unauthAttribsSz = EncodeAttributes(unauthAttribs + unauthAttribsCount, + MAX_UNAUTH_ATTRIBS_SZ - unauthAttribsCount, + pkcs7->unauthAttribs, + pkcs7->unauthAttribsSz); + unauthAttribsCount = pkcs7->unauthAttribsSz; + + flatUnauthAttribs = (byte*)XMALLOC(unauthAttribsSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (flatUnauthAttribs == NULL) { + if (aadBuffer) + XFREE(aadBuffer, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + FlattenAttributes(pkcs7, flatUnauthAttribs, unauthAttribs, + unauthAttribsCount); + unauthAttribsSetSz = SetImplicit(ASN_SET, 2, unauthAttribsSz, + unauthAttribSet); + } + + /* allocate encrypted content buffer */ + encryptedOutSz = pkcs7->contentSz; + encryptedContent = (byte*)XMALLOC(encryptedOutSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (encryptedContent == NULL) { + if (aadBuffer) + XFREE(aadBuffer, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + if (flatUnauthAttribs) + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + /* encrypt content */ + ret = wc_PKCS7_EncryptContent(pkcs7->encryptOID, pkcs7->cek, + pkcs7->cekSz, nonce, nonceSz, aadBuffer, aadBufferSz, authTag, + sizeof(authTag), pkcs7->content, encryptedOutSz, encryptedContent); + + if (aadBuffer) { + XFREE(aadBuffer, pkcs7->heap, DYNAMIC_TYPE_TMP_BUFFER); + aadBuffer = NULL; + } + + if (ret != 0) { + if (flatUnauthAttribs) + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + /* EncryptedContentInfo */ + ret = wc_SetContentType(pkcs7->contentOID, contentType, + sizeof(contentType)); + if (ret < 0) { + if (flatUnauthAttribs) + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + contentTypeSz = ret; + + /* put together nonce OCTET STRING */ + nonceOctetStringSz = SetOctetString(nonceSz, nonceOctetString); + + /* put together aes-ICVlen INTEGER */ + macIntSz = SetMyVersion(sizeof(authTag), macInt, 0); + + /* build up our ContentEncryptionAlgorithmIdentifier sequence, + * adding (nonceOctetStringSz + blockSz + macIntSz) for nonce OCTET STRING + * and tag size */ + contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, + oidBlkType, nonceOctetStringSz + nonceSz + + macIntSz); + + if (contentEncAlgoSz == 0) { + if (flatUnauthAttribs) + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; + } + + encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, encryptedOutSz, + encContentOctet); encContentSeqSz = SetSequence(contentTypeSz + contentEncAlgoSz + - ivOctetStringSz + DES_BLOCK_SIZE + - encContentOctetSz + desOutSz, encContentSeq); + nonceOctetStringSz + nonceSz + macIntSz + + encContentOctetSz + encryptedOutSz, + encContentSeq); + + macOctetStringSz = SetOctetString(sizeof(authTag), macOctetString); /* keep track of sizes for outer wrapper layering */ totalSz = verSz + recipSetSz + recipSz + encContentSeqSz + contentTypeSz + - contentEncAlgoSz + ivOctetStringSz + DES_BLOCK_SIZE + - encContentOctetSz + desOutSz; + contentEncAlgoSz + nonceOctetStringSz + nonceSz + macIntSz + + encContentOctetSz + encryptedOutSz + authAttribsSz + + authAttribsSetSz + macOctetStringSz + sizeof(authTag) + + unauthAttribsSz + unauthAttribsSetSz; /* EnvelopedData */ envDataSeqSz = SetSequence(totalSz, envDataSeq); @@ -1417,12 +10836,11 @@ int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, byte* output, word32 outputSz) if (totalSz > (int)outputSz) { WOLFSSL_MSG("Pkcs7_encrypt output buffer too small"); - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (dynamicFlag) - XFREE(plain, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); -#endif + if (flatUnauthAttribs) + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAuthAttribs) + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return BUFFER_E; } @@ -1438,412 +10856,1659 @@ int wc_PKCS7_EncodeEnvelopedData(PKCS7* pkcs7, byte* output, word32 outputSz) idx += verSz; XMEMCPY(output + idx, recipSet, recipSetSz); idx += recipSetSz; - XMEMCPY(output + idx, recip, recipSz); - idx += recipSz; + /* copy in recipients from list */ + tmpRecip = pkcs7->recipList; + while (tmpRecip != NULL) { + XMEMCPY(output + idx, tmpRecip->recip, tmpRecip->recipSz); + idx += tmpRecip->recipSz; + tmpRecip = tmpRecip->next; + } + wc_PKCS7_FreeEncodedRecipientSet(pkcs7); XMEMCPY(output + idx, encContentSeq, encContentSeqSz); idx += encContentSeqSz; XMEMCPY(output + idx, contentType, contentTypeSz); idx += contentTypeSz; XMEMCPY(output + idx, contentEncAlgo, contentEncAlgoSz); idx += contentEncAlgoSz; - XMEMCPY(output + idx, ivOctetString, ivOctetStringSz); - idx += ivOctetStringSz; - XMEMCPY(output + idx, tmpIv, DES_BLOCK_SIZE); - idx += DES_BLOCK_SIZE; + XMEMCPY(output + idx, nonceOctetString, nonceOctetStringSz); + idx += nonceOctetStringSz; + XMEMCPY(output + idx, nonce, nonceSz); + idx += nonceSz; + XMEMCPY(output + idx, macInt, macIntSz); + idx += macIntSz; XMEMCPY(output + idx, encContentOctet, encContentOctetSz); idx += encContentOctetSz; - XMEMCPY(output + idx, encryptedContent, desOutSz); - idx += desOutSz; + XMEMCPY(output + idx, encryptedContent, encryptedOutSz); + idx += encryptedOutSz; + + /* authenticated attributes */ + if (flatAuthAttribs && authAttribsSz > 0) { + XMEMCPY(output + idx, authAttribSet, authAttribsSetSz); + idx += authAttribsSetSz; + XMEMCPY(output + idx, flatAuthAttribs, authAttribsSz); + idx += authAttribsSz; + XFREE(flatAuthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } - ForceZero(contentKeyPlain, MAX_CONTENT_KEY_LEN); + XMEMCPY(output + idx, macOctetString, macOctetStringSz); + idx += macOctetStringSz; + XMEMCPY(output + idx, authTag, sizeof(authTag)); + idx += sizeof(authTag); + + /* unauthenticated attributes */ + if (unauthAttribsSz > 0) { + XMEMCPY(output + idx, unauthAttribSet, unauthAttribsSetSz); + idx += unauthAttribsSetSz; + XMEMCPY(output + idx, flatUnauthAttribs, unauthAttribsSz); + idx += unauthAttribsSz; + } - if (dynamicFlag) - XFREE(plain, NULL, DYNAMMIC_TYPE_TMP_BUFFER); - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - -#ifdef WOLFSSL_SMALL_STACK - XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); -#endif + if (flatUnauthAttribs != NULL) { + XFREE(flatUnauthAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } + + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return idx; + +#else + WOLFSSL_MSG("AuthEnvelopedData requires AES-GCM or AES-CCM to be enabled"); + (void)pkcs7; + (void)output; + (void)outputSz; + + return NOT_COMPILED_IN; +#endif /* HAVE_AESGCM | HAVE_AESCCM */ } -/* unwrap and decrypt PKCS#7 envelopedData object, return decoded size */ -WOLFSSL_API int wc_PKCS7_DecodeEnvelopedData(PKCS7* pkcs7, byte* pkiMsg, - word32 pkiMsgSz, byte* output, - word32 outputSz) + +/* unwrap and decrypt PKCS#7 AuthEnvelopedData object, return decoded size */ +WOLFSSL_API int wc_PKCS7_DecodeAuthEnvelopedData(PKCS7* pkcs7, byte* in, + word32 inSz, byte* output, + word32 outputSz) { +#if defined(HAVE_AESGCM) || defined(HAVE_AESCCM) int recipFound = 0; - int ret, version, length; - word32 savedIdx = 0, idx = 0; - word32 contentType, encOID; - byte issuerHash[SHA_DIGEST_SIZE]; + int ret = 0, length; + word32 idx = 0; +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = 0; + long rc; +#endif + word32 contentType, encOID = 0; + word32 decryptedKeySz = 0; + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; - int encryptedKeySz, keySz; - byte tmpIv[DES_BLOCK_SIZE]; - byte* decryptedKey = NULL; + int expBlockSz = 0, blockKeySz = 0; + byte authTag[AES_BLOCK_SIZE]; + byte nonce[GCM_NONCE_MID_SZ]; /* GCM nonce is larger than CCM */ + int nonceSz = 0, authTagSz = 0, macSz = 0; #ifdef WOLFSSL_SMALL_STACK - mp_int* serialNum; - byte* encryptedKey; - RsaKey* privKey; + byte* decryptedKey = NULL; #else - mp_int stack_serialNum; - mp_int* serialNum = &stack_serialNum; - byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; - - RsaKey stack_privKey; - RsaKey* privKey = &stack_privKey; + byte decryptedKey[MAX_ENCRYPTED_KEY_SZ]; #endif - int encryptedContentSz; - byte padLen; + int encryptedContentSz = 0; byte* encryptedContent = NULL; + int explicitOctet = 0; + + byte authAttribSetByte = 0; + byte* encodedAttribs = NULL; + word32 encodedAttribIdx = 0, encodedAttribSz = 0; + byte* authAttrib = NULL; + int authAttribSz = 0; + word32 localIdx; + byte tag; - if (pkcs7 == NULL || pkcs7->singleCert == NULL || - pkcs7->singleCertSz == 0 || pkcs7->privateKey == NULL || - pkcs7->privateKeySz == 0) + if (pkcs7 == NULL) return BAD_FUNC_ARG; if (pkiMsg == NULL || pkiMsgSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; +#ifndef NO_PKCS7_STREAM + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { + return ret; + } + } +#endif - /* read past ContentInfo, verify type is envelopedData */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + switch (pkcs7->state) { + case WC_PKCS7_START: + case WC_PKCS7_INFOSET_START: + case WC_PKCS7_INFOSET_STAGE1: + case WC_PKCS7_INFOSET_STAGE2: + case WC_PKCS7_INFOSET_END: + ret = wc_PKCS7_ParseToRecipientInfoSet(pkcs7, pkiMsg, pkiMsgSz, + &idx, AUTH_ENVELOPED_DATA); + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + tmpIdx = idx; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_2); + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_2: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + MAX_VERSION_SZ + ASN_TAG_SZ, &pkiMsg, &idx)) != 0) { + break; + } + #endif + #ifdef WOLFSSL_SMALL_STACK + decryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (decryptedKey == NULL) { + ret = MEMORY_E; + break; + } + #ifndef NO_PKCS7_STREAM + pkcs7->stream->key = decryptedKey; + #endif + #endif + FALL_THROUGH; + + case WC_PKCS7_DECRYPT_KTRI: + case WC_PKCS7_DECRYPT_KTRI_2: + case WC_PKCS7_DECRYPT_KTRI_3: + case WC_PKCS7_DECRYPT_KARI: + case WC_PKCS7_DECRYPT_KEKRI: + case WC_PKCS7_DECRYPT_PWRI: + case WC_PKCS7_DECRYPT_ORI: + + decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; + #ifdef WOLFSSL_SMALL_STACK + #ifndef NO_PKCS7_STREAM + decryptedKey = pkcs7->stream->key; + #endif + #endif + + ret = wc_PKCS7_DecryptRecipientInfos(pkcs7, in, inSz, &idx, + decryptedKey, &decryptedKeySz, + &recipFound); + if (ret != 0) { + break; + } - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (recipFound == 0) { + WOLFSSL_MSG("No recipient found in envelopedData that matches input"); + ret = PKCS7_RECIP_E; + break; + } - if (contentType != ENVELOPED_DATA) { - WOLFSSL_MSG("PKCS#7 input not of type EnvelopedData"); - return PKCS7_OID_E; - } + #ifndef NO_PKCS7_STREAM + tmpIdx = idx; + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_3); + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_3: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_SEQ_SZ + + MAX_ALGO_SZ + MAX_ALGO_SZ + ASN_TAG_SZ, + &pkiMsg, &idx)) != 0) { + break; + } - if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) - return ASN_PARSE_E; + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + /* remove EncryptedContentInfo */ + if (ret == 0 && GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } - /* remove EnvelopedData and version */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } - if (GetMyVersion(pkiMsg, &idx, &version) < 0) - return ASN_PARSE_E; + if (ret == 0 && GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, + pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } - if (version != 0) { - WOLFSSL_MSG("PKCS#7 envelopedData needs to be of version 0"); - return ASN_VERSION_E; - } + blockKeySz = wc_PKCS7_GetOIDKeySize(encOID); + if (ret == 0 && blockKeySz < 0) { + ret = blockKeySz; + } - /* walk through RecipientInfo set, find correct recipient */ - if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) - return ASN_PARSE_E; - -#ifdef WOLFSSL_SMALL_STACK - encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (encryptedKey == NULL) - return MEMORY_E; -#endif - - savedIdx = idx; - recipFound = 0; + expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID); + if (ret == 0 && expBlockSz < 0) { + ret = expBlockSz; + } - /* when looking for next recipient, use first sequence and version to - * indicate there is another, if not, move on */ - while(recipFound == 0) { + /* get nonce, stored in OPTIONAL parameter of AlgoID */ + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } - /* remove RecipientInfo, if we don't have a SEQUENCE, back up idx to - * last good saved one */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { - idx = savedIdx; - break; - } + if (ret == 0 && tag != ASN_OCTET_STRING) { + ret = ASN_PARSE_E; + } + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + wc_PKCS7_StreamStoreVar(pkcs7, encOID, blockKeySz, 0); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_4); + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_4: + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + MAX_VERSION_SZ + ASN_TAG_SZ + MAX_LENGTH_SZ, + &pkiMsg, &idx)) != 0) { + break; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + #endif + if (ret == 0 && GetLength(pkiMsg, &idx, &nonceSz, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && nonceSz > (int)sizeof(nonce)) { + WOLFSSL_MSG("AuthEnvelopedData nonce too large for buffer"); + ret = ASN_PARSE_E; + } + + if (ret == 0) { + XMEMCPY(nonce, &pkiMsg[idx], nonceSz); + idx += nonceSz; + } + + /* get mac size, also stored in OPTIONAL parameter of AlgoID */ + if (ret == 0 && GetMyVersion(pkiMsg, &idx, &macSz, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0) { + explicitOctet = 0; + localIdx = idx; + if (GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) == 0 && + tag == (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)) + explicitOctet = 1; + + /* read encryptedContent, cont[0] */ + ret = GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz); + } + + if (ret == 0 && + tag != (ASN_CONTEXT_SPECIFIC | 0) && + tag != (ASN_CONTEXT_SPECIFIC | ASN_CONSTRUCTED | 0)) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetLength(pkiMsg, &idx, &encryptedContentSz, + pkiMsgSz) <= 0) { + ret = ASN_PARSE_E; + } + + if (explicitOctet) { + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + if (ret == 0 && tag != ASN_OCTET_STRING) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetLength(pkiMsg, &idx, &encryptedContentSz, + pkiMsgSz) <= 0) { + ret = ASN_PARSE_E; + } + } + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + + /* store nonce for later */ + if (nonceSz > 0) { + pkcs7->stream->nonceSz = nonceSz; + pkcs7->stream->nonce = (byte*)XMALLOC(nonceSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->nonce == NULL) { + ret = MEMORY_E; + break; + } + else { + XMEMCPY(pkcs7->stream->nonce, nonce, nonceSz); + } + } + + pkcs7->stream->expected = encryptedContentSz; + wc_PKCS7_StreamStoreVar(pkcs7, encOID, blockKeySz, + encryptedContentSz); + #endif - if (GetMyVersion(pkiMsg, &idx, &version) < 0) { - idx = savedIdx; + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_5); + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_5: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + ASN_TAG_SZ + ASN_TAG_SZ + pkcs7->stream->expected, + &pkiMsg, &idx)) != 0) { + break; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + encryptedContentSz = pkcs7->stream->expected; + #endif + + encryptedContent = (byte*)XMALLOC(encryptedContentSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (ret == 0 && encryptedContent == NULL) { + ret = MEMORY_E; + } + + if (ret == 0) { + XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + idx += encryptedContentSz; + } + #ifndef NO_PKCS7_STREAM + pkcs7->stream->bufferPt = encryptedContent; + #endif + + /* may have IMPLICIT [1] authenticatedAttributes */ + localIdx = idx; + if (ret == 0 && GetASNTag(pkiMsg, &localIdx, &tag, pkiMsgSz) == 0 && + tag == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { + encodedAttribIdx = idx; + encodedAttribs = pkiMsg + idx; + idx++; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + #ifndef NO_PKCS7_STREAM + pkcs7->stream->expected = length; + #endif + encodedAttribSz = length + (idx - encodedAttribIdx); + + if (ret != 0) + break; + + #ifndef NO_PKCS7_STREAM + if (encodedAttribSz > 0) { + pkcs7->stream->aadSz = encodedAttribSz; + pkcs7->stream->aad = (byte*)XMALLOC(encodedAttribSz, + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->aad == NULL) { + ret = MEMORY_E; + break; + } + else { + XMEMCPY(pkcs7->stream->aad, encodedAttribs, + (idx - encodedAttribIdx)); + } + } + + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_ATRB); + } + else { + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + #endif + goto authenv_atrbend; /* jump over attribute cases */ + } + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_ATRB: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + return ret; + } + + length = pkcs7->stream->expected; + encodedAttribs = pkcs7->stream->aad; + #else + length = 0; + #endif + + /* save pointer and length */ + authAttrib = &pkiMsg[idx]; + authAttribSz = length; + + if (ret == 0 && wc_PKCS7_ParseAttribs(pkcs7, authAttrib, authAttribSz) < 0) { + WOLFSSL_MSG("Error parsing authenticated attributes"); + ret = ASN_PARSE_E; + break; + } + + idx += length; + + #ifndef NO_PKCS7_STREAM + if (encodedAttribSz > 0) { + XMEMCPY(pkcs7->stream->aad + (encodedAttribSz - length), + authAttrib, authAttribSz); + } + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_ATRBEND); + FALL_THROUGH; + +authenv_atrbend: + case WC_PKCS7_AUTHENV_ATRBEND: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, MAX_LENGTH_SZ + + ASN_TAG_SZ, &pkiMsg, &idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, + in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + if (pkcs7->stream->aadSz > 0) { + encodedAttribSz = pkcs7->stream->aadSz; + encodedAttribs = pkcs7->stream->aad; + } + #endif + + + /* get authTag OCTET STRING */ + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + if (ret == 0 && tag != ASN_OCTET_STRING) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && GetLength(pkiMsg, &idx, &authTagSz, pkiMsgSz) < 0) { + ret = ASN_PARSE_E; + } + + if (ret == 0 && authTagSz > (int)sizeof(authTag)) { + WOLFSSL_MSG("AuthEnvelopedData authTag too large for buffer"); + ret = ASN_PARSE_E; + } + + if (ret == 0) { + XMEMCPY(authTag, &pkiMsg[idx], authTagSz); + idx += authTagSz; + } + + if (ret == 0 && authAttrib != NULL) { + /* temporarily swap authAttribs byte[0] to SET OF instead of + * IMPLICIT [1], for aad calculation */ + authAttribSetByte = encodedAttribs[0]; + + encodedAttribs[0] = ASN_SET | ASN_CONSTRUCTED; + } + + if (ret < 0) + break; + + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + pkcs7->stream->expected = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; + + + /* store tag for later */ + if (authTagSz > 0) { + pkcs7->stream->tagSz = authTagSz; + pkcs7->stream->tag = (byte*)XMALLOC(authTagSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (pkcs7->stream->tag == NULL) { + ret = MEMORY_E; + break; + } + else { + XMEMCPY(pkcs7->stream->tag, authTag, authTagSz); + } + } + + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_AUTHENV_6); + FALL_THROUGH; + + case WC_PKCS7_AUTHENV_6: + #ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + break; + } + + /* restore all variables needed */ + if (pkcs7->stream->nonceSz > 0) { + nonceSz = pkcs7->stream->nonceSz; + if (nonceSz > GCM_NONCE_MID_SZ) { + WOLFSSL_MSG("PKCS7 saved nonce is too large"); + ret = BUFFER_E; + break; + } + else { + XMEMCPY(nonce, pkcs7->stream->nonce, nonceSz); + } + } + + if (pkcs7->stream->tagSz > 0) { + authTagSz = pkcs7->stream->tagSz; + if (authTagSz > AES_BLOCK_SIZE) { + WOLFSSL_MSG("PKCS7 saved tag is too large"); + ret = BUFFER_E; + break; + } + else { + XMEMCPY(authTag, pkcs7->stream->tag, authTagSz); + } + } + + if (pkcs7->stream->aadSz > 0) { + encodedAttribSz = pkcs7->stream->aadSz; + encodedAttribs = pkcs7->stream->aad; + } + + wc_PKCS7_StreamGetVar(pkcs7, &encOID, &blockKeySz, + &encryptedContentSz); + encryptedContent = pkcs7->stream->bufferPt; + #ifdef WOLFSSL_SMALL_STACK + decryptedKey = pkcs7->stream->key; + #endif + #endif + + /* decrypt encryptedContent */ + ret = wc_PKCS7_DecryptContent(pkcs7, encOID, decryptedKey, + blockKeySz, nonce, nonceSz, encodedAttribs, encodedAttribSz, + authTag, authTagSz, encryptedContent, encryptedContentSz, + encryptedContent); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + if (authAttrib != NULL) { + /* restore authAttrib IMPLICIT [1] */ + encodedAttribs[0] = authAttribSetByte; + } + + /* copy plaintext to output */ + XMEMCPY(output, encryptedContent, encryptedContentSz); + + /* free memory, zero out keys */ + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ); + #ifdef WOLFSSL_SMALL_STACK + XFREE(decryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + decryptedKey = NULL; + #ifdef WOLFSSL_SMALL_STACK + #ifndef NO_PKCS7_STREAM + pkcs7->stream->key = NULL; + #endif + #endif + #endif + ret = encryptedContentSz; + #ifndef NO_PKCS7_STREAM + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); break; - } + default: + WOLFSSL_MSG("Unknown PKCS7 state"); + ret = BAD_FUNC_ARG; + } - if (version != 0) { #ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ASN_VERSION_E; + if (ret != 0 && ret != WC_PKCS7_WANT_READ_E) { + if (decryptedKey != NULL) { + ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ); } - - /* remove IssuerAndSerialNumber */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(decryptedKey, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } #endif - return ASN_PARSE_E; - } - - if (GetNameHash(pkiMsg, &idx, issuerHash, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); +#ifndef NO_PKCS7_STREAM + if (ret != 0 && ret != WC_PKCS7_WANT_READ_E) { + wc_PKCS7_ResetStream(pkcs7); + } #endif - return ASN_PARSE_E; + + return ret; + +#else + WOLFSSL_MSG("AuthEnvelopedData requires AES-GCM or AES-CCM to be enabled"); + (void)pkcs7; + (void)in; + (void)inSz; + (void)output; + (void)outputSz; + + return NOT_COMPILED_IN; +#endif /* HAVE_AESGCM | HAVE_AESCCM */ +} + + +#ifndef NO_PKCS7_ENCRYPTED_DATA + +/* build PKCS#7 encryptedData content type, return encrypted size */ +int wc_PKCS7_EncodeEncryptedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + int ret, idx = 0; + int totalSz, padSz, encryptedOutSz; + + int contentInfoSeqSz, outerContentTypeSz, outerContentSz; + byte contentInfoSeq[MAX_SEQ_SZ]; + byte outerContentType[MAX_ALGO_SZ]; + byte outerContent[MAX_SEQ_SZ]; + + int encDataSeqSz, verSz, blockSz; + byte encDataSeq[MAX_SEQ_SZ]; + byte ver[MAX_VERSION_SZ]; + + byte* plain = NULL; + byte* encryptedContent = NULL; + + int encContentOctetSz, encContentSeqSz, contentTypeSz; + int contentEncAlgoSz, ivOctetStringSz; + byte encContentSeq[MAX_SEQ_SZ]; + byte contentType[MAX_OID_SZ]; + byte contentEncAlgo[MAX_ALGO_SZ]; + byte tmpIv[MAX_CONTENT_IV_SIZE]; + byte ivOctetString[MAX_OCTET_STR_SZ]; + byte encContentOctet[MAX_OCTET_STR_SZ]; + + byte attribSet[MAX_SET_SZ]; + EncodedAttrib* attribs = NULL; + word32 attribsSz; + word32 attribsCount; + word32 attribsSetSz; + + byte* flatAttribs = NULL; + + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || + pkcs7->encryptOID == 0 || pkcs7->encryptionKey == NULL || + pkcs7->encryptionKeySz == 0) + return BAD_FUNC_ARG; + + if (output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + + if (pkcs7->version == 3) { + verSz = SetMyVersion(0, ver, 0); + outerContentTypeSz = 0; + } + else { + /* outer content type */ + ret = wc_SetContentType(ENCRYPTED_DATA, outerContentType, + sizeof(outerContentType)); + if (ret < 0) + return ret; + + outerContentTypeSz = ret; + + /* version, 2 if unprotectedAttrs present, 0 if absent */ + if (pkcs7->unprotectedAttribsSz > 0) { + verSz = SetMyVersion(2, ver, 0); + } else { + verSz = SetMyVersion(0, ver, 0); } - - /* if we found correct recipient, issuer hashes will match */ - if (XMEMCMP(issuerHash, pkcs7->issuerHash, SHA_DIGEST_SIZE) == 0) { - recipFound = 1; + } + + /* EncryptedContentInfo */ + ret = wc_SetContentType(pkcs7->contentOID, contentType, + sizeof(contentType)); + if (ret < 0) + return ret; + + contentTypeSz = ret; + + /* allocate encrypted content buffer, do PKCS#7 padding */ + blockSz = wc_PKCS7_GetOIDBlockSize(pkcs7->encryptOID); + if (blockSz < 0) + return blockSz; + + padSz = wc_PKCS7_GetPadSize(pkcs7->contentSz, blockSz); + if (padSz < 0) + return padSz; + + encryptedOutSz = pkcs7->contentSz + padSz; + + plain = (byte*)XMALLOC(encryptedOutSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (plain == NULL) + return MEMORY_E; + + ret = wc_PKCS7_PadData(pkcs7->content, pkcs7->contentSz, plain, + encryptedOutSz, blockSz); + if (ret < 0) { + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + encryptedContent = (byte*)XMALLOC(encryptedOutSz, pkcs7->heap, + DYNAMIC_TYPE_PKCS7); + if (encryptedContent == NULL) { + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; + } + + /* put together IV OCTET STRING */ + ivOctetStringSz = SetOctetString(blockSz, ivOctetString); + + /* build up ContentEncryptionAlgorithmIdentifier sequence, + adding (ivOctetStringSz + blockSz) for IV OCTET STRING */ + contentEncAlgoSz = SetAlgoID(pkcs7->encryptOID, contentEncAlgo, + oidBlkType, ivOctetStringSz + blockSz); + if (contentEncAlgoSz == 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; + } + + /* encrypt content */ + WOLFSSL_MSG("Encrypting the content"); + ret = wc_PKCS7_GenerateBlock(pkcs7, NULL, tmpIv, blockSz); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + ret = wc_PKCS7_EncryptContent(pkcs7->encryptOID, pkcs7->encryptionKey, + pkcs7->encryptionKeySz, tmpIv, blockSz, NULL, 0, NULL, 0, + plain, encryptedOutSz, encryptedContent); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + + encContentOctetSz = SetImplicit(ASN_OCTET_STRING, 0, + encryptedOutSz, encContentOctet); + + encContentSeqSz = SetSequence(contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + blockSz + + encContentOctetSz + encryptedOutSz, + encContentSeq); + + /* optional UnprotectedAttributes */ + if (pkcs7->unprotectedAttribsSz != 0) { + + if (pkcs7->unprotectedAttribs == NULL) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BAD_FUNC_ARG; } - -#ifdef WOLFSSL_SMALL_STACK - serialNum = (mp_int*)XMALLOC(sizeof(mp_int), NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (serialNum == NULL) { - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + attribs = (EncodedAttrib*)XMALLOC( + sizeof(EncodedAttrib) * pkcs7->unprotectedAttribsSz, + pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (attribs == NULL) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return MEMORY_E; } -#endif - - if (GetInt(serialNum, pkiMsg, &idx, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(serialNum, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ASN_PARSE_E; + + attribsCount = pkcs7->unprotectedAttribsSz; + attribsSz = EncodeAttributes(attribs, pkcs7->unprotectedAttribsSz, + pkcs7->unprotectedAttribs, + pkcs7->unprotectedAttribsSz); + + flatAttribs = (byte*)XMALLOC(attribsSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (flatAttribs == NULL) { + XFREE(attribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return MEMORY_E; } - - mp_clear(serialNum); - -#ifdef WOLFSSL_SMALL_STACK - XFREE(serialNum, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - - if (GetAlgoId(pkiMsg, &idx, &encOID, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ASN_PARSE_E; + + FlattenAttributes(pkcs7, flatAttribs, attribs, attribsCount); + attribsSetSz = SetImplicit(ASN_SET, 1, attribsSz, attribSet); + + } else { + attribsSz = 0; + attribsSetSz = 0; + } + + /* keep track of sizes for outer wrapper layering */ + totalSz = verSz + encContentSeqSz + contentTypeSz + contentEncAlgoSz + + ivOctetStringSz + blockSz + encContentOctetSz + encryptedOutSz + + attribsSz + attribsSetSz; + + /* EncryptedData */ + encDataSeqSz = SetSequence(totalSz, encDataSeq); + totalSz += encDataSeqSz; + + if (pkcs7->version != 3) { + /* outer content */ + outerContentSz = SetExplicit(0, totalSz, outerContent); + totalSz += outerContentTypeSz; + totalSz += outerContentSz; + /* ContentInfo */ + contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); + totalSz += contentInfoSeqSz; + } else { + contentInfoSeqSz = 0; + outerContentSz = 0; + } + + if (totalSz > (int)outputSz) { + WOLFSSL_MSG("PKCS#7 output buffer too small"); + if (pkcs7->unprotectedAttribsSz != 0) { + XFREE(attribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(flatAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); } - - /* key encryption algorithm must be RSA for now */ - if (encOID != RSAk) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + XMEMCPY(output + idx, contentInfoSeq, contentInfoSeqSz); + idx += contentInfoSeqSz; + XMEMCPY(output + idx, outerContentType, outerContentTypeSz); + idx += outerContentTypeSz; + XMEMCPY(output + idx, outerContent, outerContentSz); + idx += outerContentSz; + XMEMCPY(output + idx, encDataSeq, encDataSeqSz); + idx += encDataSeqSz; + XMEMCPY(output + idx, ver, verSz); + idx += verSz; + XMEMCPY(output + idx, encContentSeq, encContentSeqSz); + idx += encContentSeqSz; + XMEMCPY(output + idx, contentType, contentTypeSz); + idx += contentTypeSz; + XMEMCPY(output + idx, contentEncAlgo, contentEncAlgoSz); + idx += contentEncAlgoSz; + XMEMCPY(output + idx, ivOctetString, ivOctetStringSz); + idx += ivOctetStringSz; + XMEMCPY(output + idx, tmpIv, blockSz); + idx += blockSz; + XMEMCPY(output + idx, encContentOctet, encContentOctetSz); + idx += encContentOctetSz; + XMEMCPY(output + idx, encryptedContent, encryptedOutSz); + idx += encryptedOutSz; + + if (pkcs7->unprotectedAttribsSz != 0) { + XMEMCPY(output + idx, attribSet, attribsSetSz); + idx += attribsSetSz; + XMEMCPY(output + idx, flatAttribs, attribsSz); + idx += attribsSz; + XFREE(attribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(flatAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } + + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return idx; +} + + +/* decode and store unprotected attributes in PKCS7->decodedAttrib. Return + * 0 on success, negative on error. User must call wc_PKCS7_Free(). */ +static int wc_PKCS7_DecodeUnprotectedAttributes(PKCS7* pkcs7, byte* pkiMsg, + word32 pkiMsgSz, word32* inOutIdx) +{ + int ret, attribLen; + word32 idx; + byte tag; + + if (pkcs7 == NULL || pkiMsg == NULL || + pkiMsgSz == 0 || inOutIdx == NULL) + return BAD_FUNC_ARG; + + idx = *inOutIdx; + + if (GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &attribLen, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* loop through attributes */ + if ((ret = wc_PKCS7_ParseAttribs(pkcs7, pkiMsg + idx, attribLen)) < 0) { + return ret; + } + + *inOutIdx = idx; + + return 0; +} + + +/* unwrap and decrypt PKCS#7/CMS encrypted-data object, returned decoded size */ +int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* in, word32 inSz, + byte* output, word32 outputSz) +{ + int ret = 0, version, length = 0, haveAttribs = 0; + word32 idx = 0; + +#ifndef NO_PKCS7_STREAM + word32 tmpIdx = 0; + long rc; #endif - return ALGO_ID_E; + word32 contentType, encOID; + + int expBlockSz = 0; + byte tmpIvBuf[MAX_CONTENT_IV_SIZE]; + byte *tmpIv = tmpIvBuf; + + int encryptedContentSz = 0; + byte padLen; + byte* encryptedContent = NULL; + + byte* pkiMsg = in; + word32 pkiMsgSz = inSz; + byte tag; + + if (pkcs7 == NULL || + ((pkcs7->encryptionKey == NULL || pkcs7->encryptionKeySz == 0) && + pkcs7->decryptionCb == NULL)) + return BAD_FUNC_ARG; + + if (pkiMsg == NULL || pkiMsgSz == 0 || + output == NULL || outputSz == 0) + return BAD_FUNC_ARG; + +#ifndef NO_PKCS7_STREAM + (void)tmpIv; /* help out static analysis */ + if (pkcs7->stream == NULL) { + if ((ret = wc_PKCS7_CreateStream(pkcs7)) != 0) { + return ret; } - - /* read encryptedKey */ - if (pkiMsg[idx++] != ASN_OCTET_STRING) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + } #endif - return ASN_PARSE_E; - } - - if (GetLength(pkiMsg, &idx, &encryptedKeySz, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + 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; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_SEQ_PEEK, in, inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; #endif - return ASN_PARSE_E; - } - - if (recipFound == 1) - XMEMCPY(encryptedKey, &pkiMsg[idx], encryptedKeySz); - idx += encryptedKeySz; - /* update good idx */ - savedIdx = idx; - } + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; - if (recipFound == 0) { - WOLFSSL_MSG("No recipient found in envelopedData that matches input"); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (pkcs7->version != 3) { /* ContentInfo not in firmware bundles */ + /* read past ContentInfo, verify type is encrypted-data */ + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && contentType != ENCRYPTED_DATA) { + WOLFSSL_MSG("PKCS#7 input not of type EncryptedData"); + ret = PKCS7_OID_E; + } + } + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } #endif - return PKCS7_RECIP_E; - } + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_STAGE2); + FALL_THROUGH; + /* end of stage 1 */ + + 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; + } - /* remove EncryptedContentInfo */ - if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; #endif - return ASN_PARSE_E; - } - - if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (pkcs7->version != 3) { + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && tag != + (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + /* remove EncryptedData and version */ + if (ret == 0 && GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + } + + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } #endif - return ASN_PARSE_E; - } + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_STAGE3); + FALL_THROUGH; + /* end of stage 2 */ + + 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; + } - if (GetAlgoId(pkiMsg, &idx, &encOID, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; #endif - return ASN_PARSE_E; - } - - /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ - if (pkiMsg[idx++] != ASN_OCTET_STRING) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + /* get version, check later */ + haveAttribs = 0; + if (ret == 0 && GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + /* remove EncryptedContentInfo */ + if (ret == 0 && GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + if (ret == 0 && wc_GetContentType(pkiMsg, &idx, &contentType, + pkiMsgSz) < 0) + ret = ASN_PARSE_E; + + 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; + + if (ret != 0) break; +#ifndef NO_PKCS7_STREAM + /* store expBlockSz for later */ + pkcs7->stream->varOne = expBlockSz; + pkcs7->stream->varTwo = encOID; + + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + + /* store version for later */ + pkcs7->stream->vers = version; #endif - return ASN_PARSE_E; - } - - if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_STAGE4); + FALL_THROUGH; + /* 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; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + /* restore saved variables */ + expBlockSz = pkcs7->stream->varOne; #endif - return ASN_PARSE_E; - } - - if (length != DES_BLOCK_SIZE) { - WOLFSSL_MSG("Incorrect IV length, must be of DES_BLOCK_SIZE"); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && tag != 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 ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } #endif - return ASN_PARSE_E; - } + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_STAGE5); + FALL_THROUGH; + /* 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; + } - XMEMCPY(tmpIv, &pkiMsg[idx], length); - idx += length; + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; - /* read encryptedContent, cont[0] */ - if (pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + /* use IV buffer from stream structure */ + tmpIv = pkcs7->stream->tmpIv; + length = pkcs7->stream->expected; #endif - return ASN_PARSE_E; - } + XMEMCPY(tmpIv, &pkiMsg[idx], length); + idx += length; + /* read encryptedContent, cont[0] */ + if (ret == 0 && GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + ret = ASN_PARSE_E; + if (ret == 0 && tag != (ASN_CONTEXT_SPECIFIC | 0)) + ret = ASN_PARSE_E; + + if (ret == 0 && GetLength(pkiMsg, &idx, &encryptedContentSz, + pkiMsgSz) <= 0) + ret = ASN_PARSE_E; + + if (ret < 0) + break; +#ifndef NO_PKCS7_STREAM + /* next chunk of data should contain encrypted content */ + pkcs7->stream->varThree = encryptedContentSz; + if ((ret = wc_PKCS7_StreamEndCase(pkcs7, &tmpIdx, &idx)) != 0) { + break; + } + + if (pkcs7->stream->totalRd + encryptedContentSz < pkiMsgSz) { + pkcs7->stream->flagOne = 1; + } + + pkcs7->stream->expected = (pkcs7->stream->maxLen - + pkcs7->stream->totalRd) + pkcs7->stream->length; - if (GetLength(pkiMsg, &idx, &encryptedContentSz, pkiMsgSz) < 0) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ASN_PARSE_E; - } - - encryptedContent = (byte*)XMALLOC(encryptedContentSz, NULL, - DYNAMIC_TYPE_TMP_BUFFER); - if (encryptedContent == NULL) { -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_STAGE6); + FALL_THROUGH; + /* end of stage 5 */ + + case WC_PKCS7_STAGE6: +#ifndef NO_PKCS7_STREAM + if ((ret = wc_PKCS7_AddDataToStream(pkcs7, in, inSz, + pkcs7->stream->expected, &pkiMsg, &idx)) != 0) { + return ret; + } + + rc = wc_PKCS7_GetMaxStream(pkcs7, PKCS7_DEFAULT_PEEK, in, + inSz); + if (rc < 0) { + ret = (int)rc; + break; + } + pkiMsgSz = (word32)rc; + + /* restore saved variables */ + expBlockSz = pkcs7->stream->varOne; + encOID = pkcs7->stream->varTwo; + encryptedContentSz = pkcs7->stream->varThree; + version = pkcs7->stream->vers; + tmpIv = pkcs7->stream->tmpIv; +#else + encOID = 0; #endif - return MEMORY_E; - } + if (ret == 0 && (encryptedContent = (byte*)XMALLOC( + encryptedContentSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7)) == NULL) { + ret = MEMORY_E; + break; + } - XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + if (ret == 0) { + XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); + idx += encryptedContentSz; + + /* decrypt encryptedContent */ + ret = wc_PKCS7_DecryptContent(pkcs7, encOID, + pkcs7->encryptionKey, pkcs7->encryptionKeySz, tmpIv, + expBlockSz, NULL, 0, NULL, 0, encryptedContent, + encryptedContentSz, encryptedContent); + if (ret != 0) { + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + } + } - /* load private key */ -#ifdef WOLFSSL_SMALL_STACK - privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); - if (privKey == NULL) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; + if (ret == 0) { + padLen = encryptedContent[encryptedContentSz-1]; + + if (padLen > encryptedContentSz) { + WOLFSSL_MSG("Bad padding size found"); + ret = BUFFER_E; + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + break; + } + + /* copy plaintext to output */ + XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + + /* get implicit[1] unprotected attributes, optional */ + wc_PKCS7_FreeDecodedAttrib(pkcs7->decodedAttrib, pkcs7->heap); + pkcs7->decodedAttrib = NULL; + #ifndef NO_PKCS7_STREAM + if (pkcs7->stream->flagOne) + #else + if (idx < pkiMsgSz) + #endif + { + haveAttribs = 1; + + ret = wc_PKCS7_DecodeUnprotectedAttributes(pkcs7, pkiMsg, + pkiMsgSz, &idx); + if (ret != 0) { + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + ret = ASN_PARSE_E; + } + } + } + + if (ret == 0) { + ForceZero(encryptedContent, encryptedContentSz); + XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + /* go back and check the version now that attribs have been processed */ + if (pkcs7->version == 3 && version != 0) { + WOLFSSL_MSG("Wrong PKCS#7 FirmwareEncryptedData version"); + return ASN_VERSION_E; + } + + if (pkcs7->version != 3 && + ((haveAttribs == 0 && version != 0) || + (haveAttribs == 1 && version != 2))) { + WOLFSSL_MSG("Wrong PKCS#7 EncryptedData version"); + return ASN_VERSION_E; + } + ret = encryptedContentSz - padLen; + } + + if (ret != 0) break; + #ifndef NO_PKCS7_STREAM + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); + break; + + default: + WOLFSSL_MSG("Error in unknown PKCS#7 Decode Encrypted Data state"); + return BAD_STATE_E; } -#endif - ret = wc_InitRsaKey(privKey, 0); if (ret != 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ret; + #ifndef NO_PKCS7_STREAM + /* restart in error case */ + wc_PKCS7_ResetStream(pkcs7); + #endif + wc_PKCS7_ChangeState(pkcs7, WC_PKCS7_START); } + return ret; +} - idx = 0; - ret = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &idx, privKey, - pkcs7->privateKeySz); - if (ret != 0) { - WOLFSSL_MSG("Failed to decode RSA private key"); - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif +/* Function to set callback during decryption, this overrides the default + * decryption function and can be used for choosing a key at run time based + * on the parsed bundle so far. + * returns 0 on success + */ +int wc_PKCS7_SetDecodeEncryptedCb(PKCS7* pkcs7, + CallbackDecryptContent decryptionCb) +{ + if (pkcs7 != NULL) { + pkcs7->decryptionCb = decryptionCb; + } + return 0; +} + + +/* Set an optional user context that gets passed to callback + * returns 0 on success + */ +int wc_PKCS7_SetDecodeEncryptedCtx(PKCS7* pkcs7, void* ctx) +{ + if (pkcs7 != NULL) { + pkcs7->decryptionCtx = ctx; + } + return 0; +} +#endif /* NO_PKCS7_ENCRYPTED_DATA */ + +#if defined(HAVE_LIBZ) && !defined(NO_PKCS7_COMPRESSED_DATA) + +/* build PKCS#7 compressedData content type, return encrypted size */ +int wc_PKCS7_EncodeCompressedData(PKCS7* pkcs7, byte* output, word32 outputSz) +{ + byte contentInfoSeq[MAX_SEQ_SZ]; + byte contentInfoTypeOid[MAX_OID_SZ]; + byte contentInfoContentSeq[MAX_SEQ_SZ]; /* EXPLICIT [0] */ + byte compressedDataSeq[MAX_SEQ_SZ]; + byte cmsVersion[MAX_VERSION_SZ]; + byte compressAlgId[MAX_ALGO_SZ]; + byte encapContentInfoSeq[MAX_SEQ_SZ]; + byte contentTypeOid[MAX_OID_SZ]; + byte contentSeq[MAX_SEQ_SZ]; /* EXPLICIT [0] */ + byte contentOctetStr[MAX_OCTET_STR_SZ]; + + int ret; + word32 totalSz, idx; + word32 contentInfoSeqSz, contentInfoContentSeqSz, contentInfoTypeOidSz; + word32 compressedDataSeqSz, cmsVersionSz, compressAlgIdSz; + word32 encapContentInfoSeqSz, contentTypeOidSz, contentSeqSz; + word32 contentOctetStrSz; + + byte* compressed; + word32 compressedSz; + + if (pkcs7 == NULL || pkcs7->content == NULL || pkcs7->contentSz == 0 || + output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* allocate space for compressed content. The libz code says the compressed + * buffer should be srcSz + 0.1% + 12. */ + compressedSz = (pkcs7->contentSz + (word32)(pkcs7->contentSz * 0.001) + 12); + compressed = (byte*)XMALLOC(compressedSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (compressed == NULL) { + WOLFSSL_MSG("Error allocating memory for CMS compressed content"); + return MEMORY_E; + } + + /* compress content */ + ret = wc_Compress(compressed, compressedSz, pkcs7->content, + pkcs7->contentSz, 0); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } + compressedSz = (word32)ret; - /* decrypt encryptedKey */ - keySz = wc_RsaPrivateDecryptInline(encryptedKey, encryptedKeySz, - &decryptedKey, privKey); - wc_FreeRsaKey(privKey); + /* eContent OCTET STRING, working backwards */ + contentOctetStrSz = SetOctetString(compressedSz, contentOctetStr); + totalSz = contentOctetStrSz + compressedSz; -#ifdef WOLFSSL_SMALL_STACK - XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif + /* EXPLICIT [0] eContentType */ + contentSeqSz = SetExplicit(0, totalSz, contentSeq); + totalSz += contentSeqSz; - if (keySz <= 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return keySz; + /* eContentType OBJECT IDENTIFIER */ + ret = wc_SetContentType(pkcs7->contentOID, contentTypeOid, + sizeof(contentTypeOid)); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; } - /* decrypt encryptedContent */ - if (encOID == DESb) { - Des des; - ret = wc_Des_SetKey(&des, decryptedKey, tmpIv, DES_DECRYPTION); + contentTypeOidSz = ret; + totalSz += contentTypeOidSz; - if (ret == 0) - wc_Des_CbcDecrypt(&des, encryptedContent, encryptedContent, - encryptedContentSz); + /* EncapsulatedContentInfo SEQUENCE */ + encapContentInfoSeqSz = SetSequence(totalSz, encapContentInfoSeq); + totalSz += encapContentInfoSeqSz; - if (ret != 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif + /* compressionAlgorithm AlgorithmIdentifier */ + /* Only supports zlib for compression currently: + * id-alg-zlibCompress (1.2.840.113549.1.9.16.3.8) */ + compressAlgIdSz = SetAlgoID(ZLIBc, compressAlgId, oidCompressType, 0); + totalSz += compressAlgIdSz; + + /* version */ + cmsVersionSz = SetMyVersion(0, cmsVersion, 0); + totalSz += cmsVersionSz; + + /* CompressedData SEQUENCE */ + compressedDataSeqSz = SetSequence(totalSz, compressedDataSeq); + totalSz += compressedDataSeqSz; + + /* ContentInfo content EXPLICIT SEQUENCE */ + contentInfoContentSeqSz = SetExplicit(0, totalSz, contentInfoContentSeq); + totalSz += contentInfoContentSeqSz; + + /* ContentInfo ContentType (compressedData) */ + if (pkcs7->version == 3) { + contentInfoTypeOidSz = 0; + } + else { + ret = wc_SetContentType(COMPRESSED_DATA, contentInfoTypeOid, + sizeof(contentInfoTypeOid)); + if (ret < 0) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } + + contentInfoTypeOidSz = ret; + totalSz += contentInfoTypeOidSz; } - else if (encOID == DES3b) { - Des3 des; - ret = wc_Des3_SetKey(&des, decryptedKey, tmpIv, DES_DECRYPTION); - if (ret == 0) - ret = wc_Des3_CbcDecrypt(&des, encryptedContent, encryptedContent, - encryptedContentSz); - if (ret != 0) { - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ret; - } - } else { - WOLFSSL_MSG("Unsupported content encryption OID type"); - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - return ALGO_ID_E; + /* ContentInfo SEQUENCE */ + contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); + totalSz += contentInfoSeqSz; + + if (outputSz < totalSz) { + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; } - padLen = encryptedContent[encryptedContentSz-1]; + idx = 0; + XMEMCPY(output + idx, contentInfoSeq, contentInfoSeqSz); + idx += contentInfoSeqSz; + XMEMCPY(output + idx, contentInfoTypeOid, contentInfoTypeOidSz); + idx += contentInfoTypeOidSz; + XMEMCPY(output + idx, contentInfoContentSeq, contentInfoContentSeqSz); + idx += contentInfoContentSeqSz; + XMEMCPY(output + idx, compressedDataSeq, compressedDataSeqSz); + idx += compressedDataSeqSz; + XMEMCPY(output + idx, cmsVersion, cmsVersionSz); + idx += cmsVersionSz; + XMEMCPY(output + idx, compressAlgId, compressAlgIdSz); + idx += compressAlgIdSz; + XMEMCPY(output + idx, encapContentInfoSeq, encapContentInfoSeqSz); + idx += encapContentInfoSeqSz; + XMEMCPY(output + idx, contentTypeOid, contentTypeOidSz); + idx += contentTypeOidSz; + XMEMCPY(output + idx, contentSeq, contentSeqSz); + idx += contentSeqSz; + XMEMCPY(output + idx, contentOctetStr, contentOctetStrSz); + idx += contentOctetStrSz; + XMEMCPY(output + idx, compressed, compressedSz); + idx += compressedSz; + + XFREE(compressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); - /* copy plaintext to output */ - XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); + return idx; +} - /* free memory, zero out keys */ - ForceZero(encryptedKey, MAX_ENCRYPTED_KEY_SZ); - ForceZero(encryptedContent, encryptedContentSz); - XFREE(encryptedContent, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#ifdef WOLFSSL_SMALL_STACK - XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); -#endif - - return encryptedContentSz - padLen; +/* unwrap and decompress PKCS#7/CMS compressedData object, + * returned decoded size */ +int wc_PKCS7_DecodeCompressedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, + byte* output, word32 outputSz) +{ + int length, version, ret; + word32 idx = 0, algOID, contentType; + byte tag; + + byte* decompressed; + word32 decompressedSz; + + if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0 || + output == NULL || outputSz == 0) { + return BAD_FUNC_ARG; + } + + /* get ContentInfo SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (pkcs7->version != 3) { + /* get ContentInfo contentType */ + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (contentType != COMPRESSED_DATA) + return ASN_PARSE_E; + } + + /* get ContentInfo content EXPLICIT SEQUENCE */ + if (GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get CompressedData SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get version */ + if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (version != 0) { + WOLFSSL_MSG("CMS CompressedData version MUST be 0, but is not"); + return ASN_PARSE_E; + } + + /* get CompressionAlgorithmIdentifier */ + if (GetAlgoId(pkiMsg, &idx, &algOID, oidIgnoreType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* Only supports zlib for compression currently: + * id-alg-zlibCompress (1.2.840.113549.1.9.16.3.8) */ + if (algOID != ZLIBc) { + WOLFSSL_MSG("CMS CompressedData only supports zlib algorithm"); + return ASN_PARSE_E; + } + + /* get EncapsulatedContentInfo SEQUENCE */ + if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get ContentType OID */ + if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) + return ASN_PARSE_E; + + pkcs7->contentOID = contentType; + + /* get eContent EXPLICIT SEQUENCE */ + if (GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* get content OCTET STRING */ + if (GetASNTag(pkiMsg, &idx, &tag, pkiMsgSz) < 0) + return ASN_PARSE_E; + + if (tag != ASN_OCTET_STRING) + return ASN_PARSE_E; + + if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) + return ASN_PARSE_E; + + /* allocate space for decompressed data */ + decompressed = (byte*)XMALLOC(length, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + if (decompressed == NULL) { + WOLFSSL_MSG("Error allocating memory for CMS decompression buffer"); + return MEMORY_E; + } + + /* decompress content */ + ret = wc_DeCompress(decompressed, length, &pkiMsg[idx], length); + if (ret < 0) { + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return ret; + } + decompressedSz = (word32)ret; + + /* get content */ + if (outputSz < decompressedSz) { + WOLFSSL_MSG("CMS output buffer too small to hold decompressed data"); + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + return BUFFER_E; + } + + XMEMCPY(output, decompressed, decompressedSz); + XFREE(decompressed, pkcs7->heap, DYNAMIC_TYPE_PKCS7); + + return decompressedSz; } +#endif /* HAVE_LIBZ && !NO_PKCS7_COMPRESSED_DATA */ #else /* HAVE_PKCS7 */ |