wolfSSL 3.11.1 for TLS1.3 beta
Fork of wolfSSL by
wolfcrypt/src/pkcs7.c
- Committer:
- wolfSSL
- Date:
- 2017-05-30
- Revision:
- 13:80fb167dafdf
File content as of revision 13:80fb167dafdf:
/* pkcs7.c * * Copyright (C) 2006-2016 wolfSSL Inc. * * 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 * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * wolfSSL is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * 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-1335, USA */ #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <wolfssl/wolfcrypt/settings.h> #ifdef HAVE_PKCS7 #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 NO_INLINE #include <wolfssl/wolfcrypt/misc.h> #else #define WOLFSSL_MISC_INCLUDED #include <wolfcrypt/src/misc.c> #endif /* direction for processing, encoding or decoding */ typedef enum { WC_PKCS7_ENCODE, WC_PKCS7_DECODE } pkcs7Direction; #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 */ static int wc_SetContentType(int pkcs7TypeOID, byte* output) { /* PKCS#7 content types, RFC 2315, section 14 */ const byte pkcs7[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07 }; const byte data[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; const byte signedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02}; const byte envelopedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x03 }; const byte signedAndEnveloped[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x04 }; const byte digestedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x05 }; const byte encryptedData[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x06 }; int idSz; int typeSz = 0, idx = 0; const byte* typeName = 0; byte ID_Length[MAX_LENGTH_SZ]; switch (pkcs7TypeOID) { case PKCS7_MSG: typeSz = sizeof(pkcs7); typeName = pkcs7; break; case DATA: typeSz = sizeof(data); typeName = data; break; case SIGNED_DATA: typeSz = sizeof(signedData); typeName = signedData; break; case ENVELOPED_DATA: typeSz = sizeof(envelopedData); typeName = envelopedData; break; case SIGNED_AND_ENVELOPED_DATA: typeSz = sizeof(signedAndEnveloped); typeName = signedAndEnveloped; break; case DIGESTED_DATA: typeSz = sizeof(digestedData); typeName = digestedData; break; case ENCRYPTED_DATA: typeSz = sizeof(encryptedData); typeName = encryptedData; break; default: WOLFSSL_MSG("Unknown PKCS#7 Type"); return 0; }; idSz = SetLength(typeSz, ID_Length); output[idx++] = ASN_OBJECT_ID; XMEMCPY(output + idx, ID_Length, idSz); idx += idSz; XMEMCPY(output + idx, typeName, typeSz); idx += typeSz; return idx; } /* get ASN.1 contentType OID sum, return 0 on success, <0 on failure */ static int wc_GetContentType(const byte* input, word32* inOutIdx, word32* oid, word32 maxIdx) { WOLFSSL_ENTER("wc_GetContentType"); if (GetObjectId(input, inOutIdx, oid, oidIgnoreType, maxIdx) < 0) return ASN_PARSE_E; return 0; } /* 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 case AES128CBCb: case AES192CBCb: case AES256CBCb: 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 case AES128CBCb: case AES128_WRAP: blockKeySz = 16; break; case AES192CBCb: case AES192_WRAP: blockKeySz = 24; break; case AES256CBCb: case AES256_WRAP: blockKeySz = 32; break; #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; } /* init PKCS7 struct with recipient cert, decode into DecodedCert */ int wc_PKCS7_InitWithCert(PKCS7* pkcs7, byte* cert, word32 certSz) { int ret = 0; XMEMSET(pkcs7, 0, sizeof(PKCS7)); /* default heap hint is null or test value */ #ifdef WOLFSSL_HEAP_TEST pkcs7->heap = (void*)WOLFSSL_HEAP_TEST; #else pkcs7->heap = NULL; #endif if (cert != NULL && certSz > 0) { #ifdef WOLFSSL_SMALL_STACK DecodedCert* dCert; dCert = (DecodedCert*)XMALLOC(sizeof(DecodedCert), NULL, DYNAMIC_TYPE_PKCS7); if (dCert == NULL) return MEMORY_E; #else DecodedCert stack_dCert; DecodedCert* dCert = &stack_dCert; #endif pkcs7->singleCert = cert; pkcs7->singleCertSz = certSz; InitDecodedCert(dCert, cert, certSz, pkcs7->heap); ret = ParseCert(dCert, CA_TYPE, NO_VERIFY, 0); if (ret < 0) { FreeDecodedCert(dCert); #ifdef WOLFSSL_SMALL_STACK XFREE(dCert, NULL, DYNAMIC_TYPE_PKCS7); #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; FreeDecodedCert(dCert); #ifdef WOLFSSL_SMALL_STACK XFREE(dCert, NULL, DYNAMIC_TYPE_PKCS7); #endif } return ret; } /* 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; } /* releases any memory allocated by a PKCS7 initializer */ void wc_PKCS7_Free(PKCS7* pkcs7) { if (pkcs7 == NULL) return; wc_PKCS7_FreeDecodedAttrib(pkcs7->decodedAttrib, pkcs7->heap); } /* build PKCS#7 data content type */ int wc_PKCS7_EncodeData(PKCS7* pkcs7, byte* output, word32 outputSz) { static const byte oid[] = { ASN_OBJECT_ID, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x01 }; byte seq[MAX_SEQ_SZ]; byte octetStr[MAX_OCTET_STR_SZ]; word32 seqSz; word32 octetStrSz; word32 oidSz = (word32)sizeof(oid); int idx = 0; octetStrSz = SetOctetString(pkcs7->contentSz, octetStr); seqSz = SetSequence(pkcs7->contentSz + octetStrSz + oidSz, seq); if (outputSz < pkcs7->contentSz + octetStrSz + oidSz + seqSz) return BUFFER_E; XMEMCPY(output, seq, seqSz); idx += seqSz; XMEMCPY(output + idx, oid, oidSz); idx += oidSz; XMEMCPY(output + idx, octetStr, octetStrSz); idx += octetStrSz; XMEMCPY(output + idx, pkcs7->content, pkcs7->contentSz); idx += pkcs7->contentSz; return idx; } typedef struct EncodedAttrib { byte valueSeq[MAX_SEQ_SZ]; const byte* oid; byte valueSet[MAX_SET_SZ]; const byte* value; word32 valueSeqSz, oidSz, idSz, valueSetSz, valueSz, totalSz; } EncodedAttrib; typedef struct ESD { 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[512]; byte outerSeq[MAX_SEQ_SZ]; byte outerContent[MAX_EXP_SZ]; byte innerSeq[MAX_SEQ_SZ]; byte version[MAX_VERSION_SZ]; byte digAlgoIdSet[MAX_SET_SZ]; byte singleDigAlgoId[MAX_ALGO_SZ]; byte contentInfoSeq[MAX_SEQ_SZ]; byte innerContSeq[MAX_EXP_SZ]; byte innerOctets[MAX_OCTET_STR_SZ]; byte certsSet[MAX_SET_SZ]; byte signerInfoSet[MAX_SET_SZ]; byte signerInfoSeq[MAX_SEQ_SZ]; byte signerVersion[MAX_VERSION_SZ]; byte issuerSnSeq[MAX_SEQ_SZ]; byte issuerName[MAX_SEQ_SZ]; byte issuerSn[MAX_SN_SZ]; byte signerDigAlgoId[MAX_ALGO_SZ]; byte digEncAlgoId[MAX_ALGO_SZ]; byte signedAttribSet[MAX_SET_SZ]; EncodedAttrib signedAttribs[6]; 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; word32 encContentDigestSz, signedAttribsSz, signedAttribsCount, signedAttribSetSz; } ESD; static int EncodeAttributes(EncodedAttrib* ea, int eaSz, PKCS7Attrib* attribs, int attribsSz) { int i; int maxSz = min(eaSz, attribsSz); int allAttribsSz = 0; for (i = 0; i < maxSz; i++) { int attribSz = 0; ea[i].value = attribs[i].value; ea[i].valueSz = attribs[i].valueSz; attribSz += ea[i].valueSz; ea[i].valueSetSz = SetSet(attribSz, ea[i].valueSet); attribSz += ea[i].valueSetSz; ea[i].oid = attribs[i].oid; ea[i].oidSz = attribs[i].oidSz; attribSz += ea[i].oidSz; ea[i].valueSeqSz = SetSequence(attribSz, ea[i].valueSeq); attribSz += ea[i].valueSeqSz; ea[i].totalSz = attribSz; allAttribsSz += attribSz; } return allAttribsSz; } static int FlattenAttributes(byte* output, EncodedAttrib* ea, int eaSz) { int i, idx; idx = 0; for (i = 0; i < eaSz; i++) { XMEMCPY(output + idx, ea[i].valueSeq, ea[i].valueSeqSz); idx += ea[i].valueSeqSz; XMEMCPY(output + idx, ea[i].oid, ea[i].oidSz); idx += ea[i].oidSz; 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; } 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 RsaKey* privKey; #else RsaKey stack_privKey; RsaKey* privKey = &stack_privKey; #endif if (pkcs7 == NULL || pkcs7->privateKey == NULL || pkcs7->rng == NULL || in == NULL || esd == NULL) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (privKey == NULL) return MEMORY_E; #endif ret = wc_InitRsaKey(privKey, pkcs7->heap); if (ret == 0) { idx = 0; ret = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &idx, privKey, pkcs7->privateKeySz); } if (ret == 0) { ret = wc_RsaSSL_Sign(in, inSz, esd->encContentDigest, sizeof(esd->encContentDigest), privKey, pkcs7->rng); } wc_FreeRsaKey(privKey); #ifdef WOLFSSL_SMALL_STACK XFREE(privKey, NULL, 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 stack_privKey; ecc_key* privKey = &stack_privKey; #endif if (pkcs7 == NULL || pkcs7->privateKey == NULL || pkcs7->rng == NULL || in == NULL || esd == NULL) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK privKey = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (privKey == NULL) return MEMORY_E; #endif ret = wc_ecc_init_ex(privKey, pkcs7->heap, INVALID_DEVID); if (ret == 0) { idx = 0; ret = wc_EccPrivateKeyDecode(pkcs7->privateKey, &idx, privKey, pkcs7->privateKeySz); } if (ret == 0) { outSz = sizeof(esd->encContentDigest); ret = wc_ecc_sign_hash(in, inSz, esd->encContentDigest, &outSz, pkcs7->rng, privKey); if (ret == 0) ret = (int)outSz; } wc_ecc_free(privKey); #ifdef WOLFSSL_SMALL_STACK XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif 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, byte* contentTypeOid, word32 contentTypeOidSz, byte* contentType, word32 contentTypeSz, byte* messageDigestOid, word32 messageDigestOidSz) { int hashSz; PKCS7Attrib cannedAttribs[2]; word32 cannedAttribsCount; if (pkcs7 == NULL || esd == NULL || contentTypeOid == NULL || contentType == NULL || messageDigestOid == NULL) return BAD_FUNC_ARG; hashSz = wc_HashGetDigestSize(esd->hashType); if (hashSz < 0) return hashSz; cannedAttribsCount = sizeof(cannedAttribs)/sizeof(PKCS7Attrib); cannedAttribs[0].oid = contentTypeOid; cannedAttribs[0].oidSz = contentTypeOidSz; cannedAttribs[0].value = contentType; cannedAttribs[0].valueSz = contentTypeSz; cannedAttribs[1].oid = messageDigestOid; cannedAttribs[1].oidSz = messageDigestOidSz; cannedAttribs[1].value = esd->contentDigest; cannedAttribs[1].valueSz = hashSz + 2; /* ASN.1 heading */ esd->signedAttribsCount += cannedAttribsCount; esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[0], 2, cannedAttribs, cannedAttribsCount); esd->signedAttribsCount += pkcs7->signedAttribsSz; esd->signedAttribsSz += EncodeAttributes(&esd->signedAttribs[2], 4, pkcs7->signedAttribs, pkcs7->signedAttribsSz); return 0; } /* gets correct encryption algo ID for SignedData, either RSAk or * CTC_<hash>wECDSA, from pkcs7->publicKeyOID. * * 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) { algoId = pkcs7->encryptOID; algoType = oidKeyType; } else if (pkcs7->publicKeyOID == ECDSAk) { algoType = oidSigType; switch (pkcs7->hashOID) { case SHAh: algoId = CTC_SHAwECDSA; break; case SHA224h: algoId = CTC_SHA224wECDSA; break; case SHA256h: algoId = CTC_SHA256wECDSA; break; case SHA384h: algoId = CTC_SHA384wECDSA; break; case SHA512h: algoId = CTC_SHA512wECDSA; break; } } 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 (pkcs7->signedAttribsSz != 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) return ret; ret = wc_HashUpdate(&esd->hash, esd->hashType, flatSignedAttribs, flatSignedAttribsSz); if (ret < 0) return ret; ret = wc_HashFinal(&esd->hash, esd->hashType, esd->contentAttribsDigest); 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 initizlied 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; #ifdef HAVE_ECC int hashSz; #endif word32 digestInfoSz = MAX_PKCS7_DIGEST_SZ; #ifdef WOLFSSL_SMALL_STACK byte* digestInfo; #else byte digestInfo[MAX_PKCS7_DIGEST_SZ]; #endif if (pkcs7 == NULL || esd == NULL) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK digestInfo = (byte*)XMALLOC(digestInfoSz, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (digestInfo == NULL) { return MEMORY_E; } #endif ret = wc_PKCS7_BuildDigestInfo(pkcs7, flatSignedAttribs, flatSignedAttribsSz, esd, digestInfo, &digestInfoSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } /* sign digestInfo */ switch (pkcs7->publicKeyOID) { #ifndef NO_RSA case RSAk: 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 */ hashSz = wc_HashGetDigestSize(esd->hashType); if (hashSz < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return hashSz; } ret = wc_PKCS7_EcdsaSign(pkcs7, esd->contentAttribsDigest, hashSz, esd); break; #endif default: WOLFSSL_MSG("Unsupported public key type"); ret = BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif if (ret >= 0) { esd->encContentDigestSz = (word32)ret; } return ret; } /* sets the wc_HashType in ESD struct based on pkcs7->hashOID * * pkcs7 - pointer to initialized PKCS7 struct * type - [OUT] pointer to wc_HashType for output * * returns hash digest size on success, negative on error */ static int wc_PKCS7_SetHashType(PKCS7* pkcs7, enum wc_HashType* type) { if (pkcs7 == NULL || type == NULL) return BAD_FUNC_ARG; switch (pkcs7->hashOID) { #ifndef NO_SHA case SHAh: *type = WC_HASH_TYPE_SHA; break; #endif #ifdef WOLFSSL_SHA224 case SHA224h: *type = WC_HASH_TYPE_SHA224; break; #endif #ifndef NO_SHA256 case SHA256h: *type = WC_HASH_TYPE_SHA256; break; #endif #ifdef WOLFSSL_SHA384 case SHA384h: *type = WC_HASH_TYPE_SHA384; break; #endif #ifdef WOLFSSL_SHA512 case SHA512h: *type = WC_HASH_TYPE_SHA512; break; #endif default: return BAD_FUNC_ARG; } return wc_HashGetDigestSize(*type); } /* build PKCS#7 signedData content type */ int wc_PKCS7_EncodeSignedData(PKCS7* pkcs7, byte* output, word32 outputSz) { 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 }; 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 }; #ifdef WOLFSSL_SMALL_STACK ESD* esd = NULL; #else ESD stack_esd; ESD* esd = &stack_esd; #endif word32 signerInfoSz = 0; word32 totalSz = 0; int idx = 0, ret = 0; int digEncAlgoId, digEncAlgoType, hashSz; byte* flatSignedAttribs = NULL; word32 flatSignedAttribsSz = 0; word32 innerOidSz = sizeof(innerOid); word32 outerOidSz = sizeof(outerOid); 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) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK esd = (ESD*)XMALLOC(sizeof(ESD), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (esd == NULL) return MEMORY_E; #endif XMEMSET(esd, 0, sizeof(ESD)); hashSz = wc_PKCS7_SetHashType(pkcs7, &esd->hashType); if (hashSz < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return hashSz; } ret = wc_HashInit(&esd->hash, esd->hashType); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } if (pkcs7->contentSz != 0) { ret = wc_HashUpdate(&esd->hash, esd->hashType, pkcs7->content, pkcs7->contentSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } esd->contentDigest[0] = ASN_OCTET_STRING; esd->contentDigest[1] = hashSz; ret = wc_HashFinal(&esd->hash, esd->hashType, &esd->contentDigest[2]); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } } 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, oidHashType, 0); signerInfoSz += esd->signerDigAlgoIdSz; /* set signatureAlgorithm */ ret = wc_PKCS7_SignedDataGetEncAlgoId(pkcs7, &digEncAlgoId, &digEncAlgoType); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } esd->digEncAlgoIdSz = SetAlgoID(digEncAlgoId, esd->digEncAlgoId, digEncAlgoType, 0); signerInfoSz += esd->digEncAlgoIdSz; if (pkcs7->signedAttribsSz != 0) { /* build up signed attributes */ ret = wc_PKCS7_BuildSignedAttributes(pkcs7, esd, contentTypeOid, sizeof(contentTypeOid), contentType, sizeof(contentType), messageDigestOid, sizeof(messageDigestOid)); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return MEMORY_E; } flatSignedAttribs = (byte*)XMALLOC(esd->signedAttribsSz, pkcs7->heap, DYNAMIC_TYPE_PKCS); flatSignedAttribsSz = esd->signedAttribsSz; if (flatSignedAttribs == NULL) { #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return MEMORY_E; } FlattenAttributes(flatSignedAttribs, esd->signedAttribs, esd->signedAttribsCount); esd->signedAttribSetSz = SetImplicit(ASN_SET, 0, esd->signedAttribsSz, esd->signedAttribSet); } /* 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_PKCS); #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, 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); esd->singleDigAlgoIdSz = SetAlgoID(pkcs7->hashOID, esd->singleDigAlgoId, oidHashType, 0); esd->digAlgoIdSetSz = SetSet(esd->singleDigAlgoIdSz, esd->digAlgoIdSet); 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); totalSz += esd->innerSeqSz; esd->outerContentSz = SetExplicit(0, totalSz, esd->outerContent); totalSz += esd->outerContentSz + outerOidSz; esd->outerSeqSz = SetSequence(totalSz, esd->outerSeq); totalSz += esd->outerSeqSz; if (outputSz < totalSz) { if (pkcs7->signedAttribsSz != 0) XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, 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, esd->outerContent, esd->outerContentSz); idx += esd->outerContentSz; XMEMCPY(output + idx, esd->innerSeq, esd->innerSeqSz); idx += esd->innerSeqSz; XMEMCPY(output + idx, esd->version, esd->versionSz); idx += esd->versionSz; XMEMCPY(output + idx, esd->digAlgoIdSet, esd->digAlgoIdSetSz); idx += esd->digAlgoIdSetSz; XMEMCPY(output + idx, esd->singleDigAlgoId, esd->singleDigAlgoIdSz); idx += esd->singleDigAlgoIdSz; XMEMCPY(output + idx, esd->contentInfoSeq, esd->contentInfoSeqSz); idx += esd->contentInfoSeqSz; XMEMCPY(output + idx, innerOid, innerOidSz); idx += innerOidSz; 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); idx += esd->certsSetSz; XMEMCPY(output + idx, pkcs7->singleCert, pkcs7->singleCertSz); idx += pkcs7->singleCertSz; XMEMCPY(output + idx, esd->signerInfoSet, esd->signerInfoSetSz); idx += esd->signerInfoSetSz; XMEMCPY(output + idx, esd->signerInfoSeq, esd->signerInfoSeqSz); idx += esd->signerInfoSeqSz; XMEMCPY(output + 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); idx += esd->signerDigAlgoIdSz; /* SignerInfo:Attributes */ if (flatSignedAttribsSz > 0) { XMEMCPY(output + idx, esd->signedAttribSet, esd->signedAttribSetSz); idx += esd->signedAttribSetSz; XMEMCPY(output + idx, flatSignedAttribs, flatSignedAttribsSz); idx += flatSignedAttribsSz; XFREE(flatSignedAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); } XMEMCPY(output + idx, esd->digEncAlgoId, esd->digEncAlgoIdSz); idx += esd->digEncAlgoIdSz; XMEMCPY(output + idx, esd->signerDigest, esd->signerDigestSz); idx += esd->signerDigestSz; XMEMCPY(output + idx, esd->encContentDigest, esd->encContentDigestSz); idx += esd->encContentDigestSz; #ifdef WOLFSSL_SMALL_STACK XFREE(esd, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return idx; } #ifndef NO_RSA /* 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; word32 scratch = 0; #ifdef WOLFSSL_SMALL_STACK byte* digest; RsaKey* key; #else byte digest[MAX_PKCS7_DIGEST_SZ]; RsaKey stack_key; RsaKey* key = &stack_key; #endif if (pkcs7 == NULL || sig == NULL || hash == NULL) { return BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (digest == NULL) return MEMORY_E; key = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (key == NULL) { XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } #endif XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); ret = wc_InitRsaKey(key, pkcs7->heap); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } if (wc_RsaPublicKeyDecode(pkcs7->publicKey, &scratch, key, pkcs7->publicKeySz) < 0) { WOLFSSL_MSG("ASN RSA key decode error"); #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return PUBLIC_KEY_E; } ret = wc_RsaSSL_Verify(sig, sigSz, digest, MAX_PKCS7_DIGEST_SZ, key); wc_FreeRsaKey(key); if (((int)hashSz != ret) || (XMEMCMP(digest, hash, ret) != 0)) { ret = SIG_VERIFY_E; } #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, 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_EcdsaVerify(PKCS7* pkcs7, byte* sig, int sigSz, byte* hash, word32 hashSz) { int ret = 0; int res = 0; #ifdef WOLFSSL_SMALL_STACK byte* digest; ecc_key* key; #else byte digest[MAX_PKCS7_DIGEST_SZ]; ecc_key stack_key; ecc_key* key = &stack_key; #endif if (pkcs7 == NULL || sig == NULL) return BAD_FUNC_ARG; #ifdef WOLFSSL_SMALL_STACK digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (digest == NULL) return MEMORY_E; key = (ecc_key*)XMALLOC(sizeof(ecc_key), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (key == NULL) { XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } #endif XMEMSET(digest, 0, MAX_PKCS7_DIGEST_SZ); ret = wc_ecc_init_ex(key, pkcs7->heap, INVALID_DEVID); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } if (wc_ecc_import_x963(pkcs7->publicKey, pkcs7->publicKeySz, key) < 0) { WOLFSSL_MSG("ASN ECDSA key decode error"); #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return PUBLIC_KEY_E; } ret = wc_ecc_verify_hash(sig, sigSz, hash, hashSz, &res, key); wc_ecc_free(key); if (ret == 0 && res != 1) { ret = SIG_VERIFY_E; } #ifdef WOLFSSL_SMALL_STACK XFREE(digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(key, NULL, DYNAMIC_TYPE_TMP_BUFFER); #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) { int ret = 0, digIdx = 0, hashSz; word32 attribSetSz; 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; if (pkcs7 == NULL || pkcs7Digest == NULL || pkcs7DigestSz == NULL || plainDigest == NULL) { return BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK digestInfo = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, NULL, 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); hashSz = wc_PKCS7_SetHashType(pkcs7, &hashType); if (hashSz < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return hashSz; } /* calculate digest */ ret = wc_HashInit(&hash, hashType); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } if (signedAttribSz > 0) { if (signedAttrib == NULL) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return BAD_FUNC_ARG; } attribSetSz = SetSet(signedAttribSz, attribSet); ret = wc_HashUpdate(&hash, hashType, attribSet, attribSetSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } ret = wc_HashUpdate(&hash, hashType, signedAttrib, signedAttribSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } ret = wc_HashFinal(&hash, hashType, digest); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } } else { if (pkcs7->content == NULL) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return BAD_FUNC_ARG; } ret = wc_HashUpdate(&hash, hashType, pkcs7->content, pkcs7->contentSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } ret = wc_HashFinal(&hash, hashType, digest); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(digestInfo, NULL, 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, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif 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) { 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; #ifdef WOLFSSL_SMALL_STACK pkcs7Digest = (byte*)XMALLOC(MAX_PKCS7_DIGEST_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (pkcs7Digest == NULL) return MEMORY_E; #endif /* build hash to verify against */ pkcs7DigestSz = MAX_PKCS7_DIGEST_SZ; ret = wc_PKCS7_BuildSignedDataDigest(pkcs7, signedAttrib, signedAttribSz, pkcs7Digest, &pkcs7DigestSz, &plainDigest, &plainDigestSz); if (ret < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(pkcs7Digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } 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; } #ifdef WOLFSSL_SMALL_STACK XFREE(pkcs7Digest, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } /* Finds the certificates in the message and saves it. */ int wc_PKCS7_VerifySignedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz) { word32 idx, contentType, hashOID; int length, version, ret; byte* content = NULL; byte* sig = NULL; byte* cert = NULL; byte* signedAttrib = NULL; int contentSz = 0, sigSz = 0, certSz = 0, signedAttribSz = 0; if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0) return BAD_FUNC_ARG; idx = 0; /* Get the contentInfo sequence */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Get the contentInfo contentType */ if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; if (contentType != SIGNED_DATA) { WOLFSSL_MSG("PKCS#7 input not of type SignedData"); return PKCS7_OID_E; } /* get the ContentInfo content */ if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Get the signedData sequence */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Get the version */ if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) return ASN_PARSE_E; if (version != 1) { WOLFSSL_MSG("PKCS#7 signedData needs to be of version 1"); return ASN_VERSION_E; } /* Get the set of DigestAlgorithmIdentifiers */ if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Skip the set. */ idx += length; /* Get the inner ContentInfo sequence */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Get the inner ContentInfo contentType */ if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; if (contentType != DATA) { WOLFSSL_MSG("PKCS#7 inner input not of type Data"); return PKCS7_OID_E; } if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) <= 0) return ASN_PARSE_E; if (pkiMsg[idx++] != ASN_OCTET_STRING) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 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 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; 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 (pkiMsg[certIdx++] == (ASN_CONSTRUCTED | ASN_SEQUENCE)) { if (GetLength(pkiMsg, &certIdx, &certSz, pkiMsgSz) < 0) return ASN_PARSE_E; cert = &pkiMsg[idx]; certSz += (certIdx - idx); } wc_PKCS7_InitWithCert(pkcs7, cert, certSz); } 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; } /* Get the set of signerInfos */ if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; 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, pkiMsgSz) < 0) return ASN_PARSE_E; if (version != 1) { WOLFSSL_MSG("PKCS#7 signerInfo needs to be of version 1"); return ASN_VERSION_E; } /* Get the sequence of IssuerAndSerialNumber */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Skip it */ idx += length; /* Get the sequence of digestAlgorithm */ if (GetAlgoId(pkiMsg, &idx, &hashOID, oidHashType, pkiMsgSz) < 0) { return ASN_PARSE_E; } pkcs7->hashOID = (int)hashOID; /* Get the IMPLICIT[0] SET OF signedAttributes */ if (pkiMsg[idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { idx++; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* save pointer and length */ signedAttrib = &pkiMsg[idx]; signedAttribSz = length; idx += length; } /* Get the sequence of digestEncryptionAlgorithm */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* Skip it */ idx += length; /* Get the signature */ if (pkiMsg[idx] == ASN_OCTET_STRING) { idx++; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* save pointer and length */ sig = &pkiMsg[idx]; sigSz = length; idx += length; } pkcs7->content = content; pkcs7->contentSz = contentSz; ret = wc_PKCS7_SignedDataVerifySignature(pkcs7, sig, sigSz, signedAttrib, signedAttribSz); if (ret < 0) return ret; } return 0; } #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 */ 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 */ } WC_PKCS7_KARI; /* wrap CEK (content encryption key) with KEK, 0 on success, < 0 on error */ static int wc_PKCS7_KariKeyWrap(byte* cek, word32 cekSz, byte* kek, word32 kekSz, byte* out, word32 outSz, int keyWrapAlgo, int direction) { int ret; if (cek == NULL || kek == NULL || out == NULL) return BAD_FUNC_ARG; switch (keyWrapAlgo) { #ifndef NO_AES case AES128_WRAP: case AES192_WRAP: case AES256_WRAP: 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; } /* 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->heap = pkcs7->heap; 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) { FreeDecodedCert(kari->decoded); XFREE(kari->decoded, heap, DYNAMIC_TYPE_PKCS7); } if (kari->senderKey) { wc_ecc_free(kari->senderKey); XFREE(kari->senderKey, heap, DYNAMIC_TYPE_PKCS7); } if (kari->recipKey) { 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; if (kari->direction == WC_PKCS7_DECODE && (key == NULL || keySz == 0)) return BAD_FUNC_ARG; /* decode certificate */ InitDecodedCert(kari->decoded, (byte*)cert, certSz, kari->heap); ret = ParseCert(kari->decoded, CA_TYPE, NO_VERIFY, 0); if (ret < 0) return ret; /* 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(kari->recipKey); if (ret != 0) return ret; /* get recip public key */ if (kari->direction == WC_PKCS7_ENCODE) { ret = wc_ecc_import_x963(kari->decoded->publicKey, kari->decoded->pubKeySize, kari->recipKey); } /* get recip private key */ else if (kari->direction == WC_PKCS7_DECODE) { idx = 0; ret = wc_EccPrivateKeyDecode(key, &idx, kari->recipKey, keySz); if (ret != 0) return ret; } else { /* bad direction */ return BAD_FUNC_ARG; } if (ret != 0) return ret; (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, WC_RNG* rng) { int ret; if (kari == NULL || kari->decoded == NULL || kari->recipKey == NULL || kari->recipKey->dp == NULL || rng == 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, INVALID_DEVID); if (ret != 0) return ret; ret = wc_ecc_make_key_ex(rng, kari->recipKey->dp->size, kari->senderKey, kari->recipKey->dp->id); if (ret != 0) return ret; /* 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) 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; } /* create ASN.1 formatted KeyAgreeRecipientInfo (kari) for use with ECDH, * return sequence size or negative on error */ static int wc_CreateKeyAgreeRecipientInfo(PKCS7* pkcs7, const byte* cert, word32 certSz, int keyAgreeAlgo, int blockKeySz, int keyWrapAlgo, int keyEncAlgo, WC_RNG* rng, byte* contentKeyPlain, byte* contentKeyEnc, int* keyEncSz, byte* out, word32 outSz) { int ret = 0, idx = 0; int keySz, direction = 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]; WC_PKCS7_KARI* kari; /* only supports ECDSA for now */ if (keyAgreeAlgo != ECDSAk) return BAD_FUNC_ARG; /* set direction based on keyWrapAlgo */ switch (keyWrapAlgo) { #ifndef NO_AES case AES128_WRAP: case AES192_WRAP: case AES256_WRAP: direction = AES_ENCRYPTION; break; #endif default: WOLFSSL_MSG("Unsupported key wrap algorithm"); return BAD_KEYWRAP_ALG_E; } kari = wc_PKCS7_KariNew(pkcs7, WC_PKCS7_ENCODE); if (kari == NULL) return MEMORY_E; /* set user keying material if available */ if ((pkcs7->ukmSz > 0) && (pkcs7->ukm != NULL)) { kari->ukm = pkcs7->ukm; kari->ukmSz = pkcs7->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); return ret; } /* generate sender ephemeral ECC key */ ret = wc_PKCS7_KariGenerateEphemeralKey(kari, rng); if (ret != 0) { wc_PKCS7_KariFree(kari); return ret; } /* generate KEK (key encryption key) */ ret = wc_PKCS7_KariGenerateKEK(kari, keyWrapAlgo, keyEncAlgo); if (ret != 0) { wc_PKCS7_KariFree(kari); return ret; } /* encrypt CEK with KEK */ keySz = wc_PKCS7_KariKeyWrap(contentKeyPlain, blockKeySz, kari->kek, kari->kekSz, contentKeyEnc, *keyEncSz, keyWrapAlgo, direction); if (keySz <= 0) { wc_PKCS7_KariFree(kari); return ret; } *keyEncSz = (word32)keySz; /* Start of RecipientEncryptedKeys */ /* EncryptedKey */ encryptedKeyOctetSz = SetOctetString(*keyEncSz, encryptedKeyOctet); totalSz += (encryptedKeyOctetSz + *keyEncSz); /* 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(keyWrapAlgo, keyWrapAlg, oidKeyWrapType, 0); totalSz += keyWrapAlgSz; /* KeyEncryptionAlgorithmIdentifier */ keyEncryptAlgoIdSz = SetAlgoID(keyEncAlgo, 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 */ origAlgIdSz = SetAlgoID(ECDSAk, origAlgId, oidKeyType, 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; /* outer IMPLICIT [1] kari */ kariSeqSz = SetImplicit(ASN_SEQUENCE, 1, totalSz, kariSeq); totalSz += kariSeqSz; if ((word32)totalSz > outSz) { WOLFSSL_MSG("KeyAgreeRecipientInfo output buffer too small"); wc_PKCS7_KariFree(kari); return BUFFER_E; } XMEMCPY(out + idx, kariSeq, kariSeqSz); idx += kariSeqSz; XMEMCPY(out + idx, ver, verSz); idx += verSz; XMEMCPY(out + idx, origIdOrKeySeq, origIdOrKeySeqSz); idx += origIdOrKeySeqSz; XMEMCPY(out + idx, origPubKeySeq, origPubKeySeqSz); idx += origPubKeySeqSz; XMEMCPY(out + idx, origAlgId, origAlgIdSz); idx += origAlgIdSz; XMEMCPY(out + idx, origPubKeyStr, origPubKeyStrSz); idx += origPubKeyStrSz; /* ephemeral public key */ XMEMCPY(out + idx, kari->senderKeyExport, kari->senderKeyExportSz); idx += kari->senderKeyExportSz; if (kari->ukmSz > 0) { XMEMCPY(out + idx, ukmExplicitSeq, ukmExplicitSz); idx += ukmExplicitSz; XMEMCPY(out + idx, ukmOctetStr, ukmOctetSz); idx += ukmOctetSz; XMEMCPY(out + idx, kari->ukm, kari->ukmSz); idx += kari->ukmSz; } XMEMCPY(out + idx, keyEncryptAlgoId, keyEncryptAlgoIdSz); idx += keyEncryptAlgoIdSz; XMEMCPY(out + idx, keyWrapAlg, keyWrapAlgSz); idx += keyWrapAlgSz; XMEMCPY(out + idx, recipEncKeysSeq, recipEncKeysSeqSz); idx += recipEncKeysSeqSz; XMEMCPY(out + idx, recipEncKeySeq, recipEncKeySeqSz); idx += recipEncKeySeqSz; XMEMCPY(out + idx, recipKeyIdSeq, recipKeyIdSeqSz); idx += recipKeyIdSeqSz; XMEMCPY(out + idx, subjKeyIdOctet, subjKeyIdOctetSz); idx += subjKeyIdOctetSz; /* subject key id */ XMEMCPY(out + idx, kari->decoded->extSubjKeyId, KEYID_SIZE); idx += KEYID_SIZE; XMEMCPY(out + idx, encryptedKeyOctet, encryptedKeyOctetSz); idx += encryptedKeyOctetSz; /* encrypted CEK */ XMEMCPY(out + idx, contentKeyEnc, *keyEncSz); idx += *keyEncSz; wc_PKCS7_KariFree(kari); return idx; } #endif /* HAVE_ECC */ /* create ASN.1 formatted RecipientInfo structure, returns sequence size */ static int wc_CreateRecipientInfo(const byte* cert, word32 certSz, int keyEncAlgo, int blockKeySz, WC_RNG* rng, byte* contentKeyPlain, byte* contentKeyEnc, int* keyEncSz, byte* out, word32 outSz, void* heap) { word32 idx = 0; int ret = 0, totalSz = 0; int verSz, issuerSz, snSz, keyEncAlgSz; int issuerSeqSz, recipSeqSz, issuerSerialSeqSz; int encKeyOctetStrSz; byte ver[MAX_VERSION_SZ]; byte issuerSerialSeq[MAX_SEQ_SZ]; byte recipSeq[MAX_SEQ_SZ]; byte issuerSeq[MAX_SEQ_SZ]; byte encKeyOctetStr[MAX_OCTET_STR_SZ]; #ifdef WOLFSSL_SMALL_STACK byte *serial; byte *keyAlgArray; 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); 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; #endif InitDecodedCert(decoded, (byte*)cert, certSz, 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); #endif return ret; } /* version */ verSz = SetMyVersion(0, ver, 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); #endif return -1; } issuerSz = decoded->issuerRawLen; issuerSeqSz = SetSequence(issuerSz, issuerSeq); if (decoded->serialSz == 0) { WOLFSSL_MSG("DecodedCert missing serial number"); 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); #endif return -1; } snSz = SetSerialNumber(decoded->serial, decoded->serialSz, serial); issuerSerialSeqSz = SetSequence(issuerSeqSz + issuerSz + snSz, issuerSerialSeq); /* KeyEncryptionAlgorithmIdentifier, only support RSA now */ if (keyEncAlgo != 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); #endif return ALGO_ID_E; } keyEncAlgSz = SetAlgoID(keyEncAlgo, 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); #endif return BAD_FUNC_ARG; } #ifdef WOLFSSL_SMALL_STACK pubKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, 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); return MEMORY_E; } #endif /* EncryptedKey */ ret = wc_InitRsaKey(pubKey, 0); 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); #endif return ret; } if (wc_RsaPublicKeyDecode(decoded->publicKey, &idx, pubKey, 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); #endif return PUBLIC_KEY_E; } *keyEncSz = wc_RsaPublicEncrypt(contentKeyPlain, blockKeySz, contentKeyEnc, MAX_ENCRYPTED_KEY_SZ, pubKey, rng); wc_FreeRsaKey(pubKey); #ifdef WOLFSSL_SMALL_STACK XFREE(pubKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif if (*keyEncSz < 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); #endif return *keyEncSz; } encKeyOctetStrSz = SetOctetString(*keyEncSz, encKeyOctetStr); /* RecipientInfo */ recipSeqSz = SetSequence(verSz + issuerSerialSeqSz + issuerSeqSz + issuerSz + snSz + keyEncAlgSz + encKeyOctetStrSz + *keyEncSz, recipSeq); if (recipSeqSz + verSz + issuerSerialSeqSz + issuerSeqSz + snSz + keyEncAlgSz + encKeyOctetStrSz + *keyEncSz > (int)outSz) { 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); #endif return BUFFER_E; } XMEMCPY(out + totalSz, recipSeq, recipSeqSz); totalSz += recipSeqSz; XMEMCPY(out + totalSz, ver, verSz); 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; 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); #endif return totalSz; } /* encrypt content using encryptOID algo */ static int wc_PKCS7_EncryptContent(int encryptOID, byte* key, int keySz, byte* iv, int ivSz, 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 case AES128CBCb: case AES192CBCb: case AES256CBCb: if ( (encryptOID == AES128CBCb && keySz != 16 ) || (encryptOID == AES192CBCb && keySz != 24 ) || (encryptOID == AES256CBCb && keySz != 32 ) || (ivSz != AES_BLOCK_SIZE) ) return BAD_FUNC_ARG; ret = wc_AesSetKey(&aes, key, keySz, iv, AES_ENCRYPTION); if (ret == 0) ret = wc_AesCbcEncrypt(&aes, out, in, inSz); break; #endif #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_Des3_SetKey(&des3, key, iv, DES_ENCRYPTION); if (ret == 0) ret = wc_Des3_CbcEncrypt(&des3, out, in, inSz); break; #endif default: WOLFSSL_MSG("Unsupported content cipher type"); return ALGO_ID_E; }; return ret; } /* decrypt content using encryptOID algo */ static int wc_PKCS7_DecryptContent(int encryptOID, byte* key, int keySz, byte* iv, int ivSz, 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 case AES128CBCb: case AES192CBCb: case AES256CBCb: if ( (encryptOID == AES128CBCb && keySz != 16 ) || (encryptOID == AES192CBCb && keySz != 24 ) || (encryptOID == AES256CBCb && keySz != 32 ) || (ivSz != AES_BLOCK_SIZE) ) return BAD_FUNC_ARG; ret = wc_AesSetKey(&aes, key, keySz, iv, AES_DECRYPTION); if (ret == 0) ret = wc_AesCbcDecrypt(&aes, out, in, inSz); break; #endif #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_Des3_SetKey(&des3, key, iv, DES_DECRYPTION); if (ret == 0) ret = wc_Des3_CbcDecrypt(&des3, out, in, inSz); break; #endif default: WOLFSSL_MSG("Unsupported content cipher type"); return ALGO_ID_E; }; return ret; } /* generate random IV, place in iv, return 0 on success negative on error */ static int wc_PKCS7_GenerateIV(WC_RNG* rng, byte* iv, word32 ivSz) { int ret; WC_RNG* rnd = NULL; if (iv == NULL || ivSz == 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), NULL, DYNAMIC_TYPE_RNG); if (rnd == NULL) return MEMORY_E; ret = wc_InitRng(rnd); if (ret != 0) { XFREE(rnd, NULL, DYNAMIC_TYPE_RNG); return ret; } } else { rnd = rng; } ret = wc_RNG_GenerateBlock(rnd, iv, ivSz); if (rng == NULL) { wc_FreeRng(rnd); XFREE(rnd, NULL, DYNAMIC_TYPE_RNG); } return ret; } /* return size of padded data, padded to blockSz chunks, or negative on error */ static 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_GetPadLength() helper. */ static 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 = blockSz - (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; } /* build PKCS#7 envelopedData content type, return enveloped size */ int wc_PKCS7_EncodeEnvelopedData(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 envDataSeqSz, verSz; byte envDataSeq[MAX_SEQ_SZ]; byte ver[MAX_VERSION_SZ]; WC_RNG rng; int contentKeyEncSz, blockSz, blockKeySz; byte contentKeyPlain[MAX_CONTENT_KEY_LEN]; #ifdef WOLFSSL_SMALL_STACK byte* contentKeyEnc; #else byte contentKeyEnc[MAX_ENCRYPTED_KEY_SZ]; #endif byte* plain; byte* encryptedContent; 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; int contentEncAlgoSz, ivOctetStringSz; byte encContentSeq[MAX_SEQ_SZ]; byte contentType[MAX_ALGO_SZ]; byte contentEncAlgo[MAX_ALGO_SZ]; 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 || pkcs7->publicKeyOID == 0) return BAD_FUNC_ARG; if (output == NULL || outputSz == 0) 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 */ outerContentTypeSz = wc_SetContentType(ENVELOPED_DATA, outerContentType); /* version, defined as 0 in RFC 2315 */ if (pkcs7->publicKeyOID == ECDSAk) { verSz = SetMyVersion(2, ver, 0); } else { verSz = SetMyVersion(0, ver, 0); } /* generate random content encryption key */ ret = wc_InitRng_ex(&rng, pkcs7->heap, INVALID_DEVID); if (ret != 0) return ret; ret = wc_RNG_GenerateBlock(&rng, contentKeyPlain, blockKeySz); if (ret != 0) { wc_FreeRng(&rng); return ret; } #ifdef WOLFSSL_SMALL_STACK recip = (byte*)XMALLOC(MAX_RECIP_SZ, NULL, DYNAMIC_TYPE_PKCS7); contentKeyEnc = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, NULL, DYNAMIC_TYPE_PKCS7); if (contentKeyEnc == NULL || recip == NULL) { if (recip) XFREE(recip, NULL, DYNAMIC_TYPE_PKCS7); if (contentKeyEnc) XFREE(contentKeyEnc, NULL, DYNAMIC_TYPE_PKCS7); wc_FreeRng(&rng); return MEMORY_E; } #endif contentKeyEncSz = MAX_ENCRYPTED_KEY_SZ; /* build RecipientInfo, only handle 1 for now */ switch (pkcs7->publicKeyOID) { case RSAk: recipSz = wc_CreateRecipientInfo(pkcs7->singleCert, pkcs7->singleCertSz, pkcs7->publicKeyOID, blockKeySz, &rng, contentKeyPlain, contentKeyEnc, &contentKeyEncSz, recip, MAX_RECIP_SZ, pkcs7->heap); break; #ifdef HAVE_ECC case ECDSAk: recipSz = wc_CreateKeyAgreeRecipientInfo(pkcs7, pkcs7->singleCert, pkcs7->singleCertSz, pkcs7->publicKeyOID, blockKeySz, pkcs7->keyWrapOID, pkcs7->keyAgreeOID, &rng, contentKeyPlain, contentKeyEnc, &contentKeyEncSz, recip, MAX_RECIP_SZ); break; #endif default: WOLFSSL_MSG("Unsupported RecipientInfo public key type"); return BAD_FUNC_ARG; }; ForceZero(contentKeyEnc, MAX_ENCRYPTED_KEY_SZ); #ifdef WOLFSSL_SMALL_STACK XFREE(contentKeyEnc, NULL, DYNAMIC_TYPE_PKCS7); #endif if (recipSz < 0) { WOLFSSL_MSG("Failed to create RecipientInfo"); wc_FreeRng(&rng); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif return recipSz; } recipSetSz = SetSet(recipSz, recipSet); /* generate IV for block cipher */ ret = wc_PKCS7_GenerateIV(&rng, tmpIv, blockSz); wc_FreeRng(&rng); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif return ret; } /* EncryptedContentInfo */ contentTypeSz = wc_SetContentType(pkcs7->contentOID, contentType); if (contentTypeSz == 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif return BAD_FUNC_ARG; } /* 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); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif 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); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMIC_TYPE_PKCS7); #endif return BAD_FUNC_ARG; } /* encrypt content */ ret = wc_PKCS7_EncryptContent(pkcs7->encryptOID, contentKeyPlain, blockKeySz, tmpIv, blockSz, plain, encryptedOutSz, encryptedContent); if (ret != 0) { XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMIC_TYPE_PKCS7); #endif 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; /* 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); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif 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, envDataSeq, envDataSeqSz); idx += envDataSeqSz; XMEMCPY(output + idx, ver, verSz); idx += verSz; XMEMCPY(output + idx, recipSet, recipSetSz); idx += recipSetSz; XMEMCPY(output + idx, recip, recipSz); idx += recipSz; 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; ForceZero(contentKeyPlain, MAX_CONTENT_KEY_LEN); XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); #ifdef WOLFSSL_SMALL_STACK XFREE(recip, NULL, DYNAMMIC_TYPE_TMP_BUFFER); #endif return idx; } /* decode KeyTransRecipientInfo (ktri), return 0 on success, <0 on error */ static int wc_PKCS7_DecodeKtri(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, word32* idx, byte* decryptedKey, word32* decryptedKeySz, int* recipFound) { int length, encryptedKeySz, ret; int keySz; word32 encOID; word32 keyIdx; byte issuerHash[SHA_DIGEST_SIZE]; byte* outKey = NULL; #ifdef WC_RSA_BLINDING WC_RNG rng; #endif #ifdef WOLFSSL_SMALL_STACK mp_int* serialNum; byte* encryptedKey; RsaKey* privKey; #else mp_int stack_serialNum; mp_int* serialNum = &stack_serialNum; byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; RsaKey stack_privKey; RsaKey* privKey = &stack_privKey; #endif /* 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, SHA_DIGEST_SIZE) == 0) { *recipFound = 1; } #ifdef WOLFSSL_SMALL_STACK serialNum = (mp_int*)XMALLOC(sizeof(mp_int), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (serialNum == NULL) return MEMORY_E; #endif if (GetInt(serialNum, pkiMsg, idx, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(serialNum, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ASN_PARSE_E; } mp_clear(serialNum); #ifdef WOLFSSL_SMALL_STACK XFREE(serialNum, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif 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 */ #ifdef WOLFSSL_SMALL_STACK encryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, NULL, DYNAMIC_TYPE_TMP_BUFFER); if (encryptedKey == NULL) return MEMORY_E; #endif 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); #endif return ASN_PARSE_E; } if (*recipFound == 1) XMEMCPY(encryptedKey, &pkiMsg[*idx], encryptedKeySz); *idx += encryptedKeySz; /* load private key */ #ifdef WOLFSSL_SMALL_STACK privKey = (RsaKey*)XMALLOC(sizeof(RsaKey), NULL, DYNAMIC_TYPE_TMP_BUFFER); if (privKey == NULL) { XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); return MEMORY_E; } #endif ret = wc_InitRsaKey(privKey, 0); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } keyIdx = 0; ret = wc_RsaPrivateKeyDecode(pkcs7->privateKey, &keyIdx, privKey, pkcs7->privateKeySz); if (ret != 0) { WOLFSSL_MSG("Failed to decode RSA private key"); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return ret; } /* decrypt encryptedKey */ #ifdef WC_RSA_BLINDING ret = wc_InitRng(&rng); 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, MAX_ENCRYPTED_KEY_SZ); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return keySz; } else { *decryptedKeySz = keySz; XMEMCPY(decryptedKey, outKey, keySz); ForceZero(encryptedKey, MAX_ENCRYPTED_KEY_SZ); } #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); XFREE(privKey, NULL, DYNAMIC_TYPE_TMP_BUFFER); #endif return 0; } #ifdef HAVE_ECC /* 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; if (kari == NULL || pkiMsg == NULL || idx == NULL) return BAD_FUNC_ARG; /* remove OriginatorIdentifierOrKey */ if (pkiMsg[*idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) { (*idx)++; if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; } else { return ASN_PARSE_E; } /* remove OriginatorPublicKey */ if (pkiMsg[*idx] == (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) { (*idx)++; 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; /* remove ECPoint BIT STRING */ if ((pkiMsgSz > (*idx + 1)) && (pkiMsg[(*idx)++] != ASN_BIT_STRING)) return ASN_PARSE_E; if (GetLength(pkiMsg, idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if ((pkiMsgSz < (*idx + 1)) || (pkiMsg[(*idx)++] != 0x00)) return ASN_EXPECT_0_E; /* get sender ephemeral public ECDSA key */ ret = wc_ecc_init(kari->senderKey); if (ret != 0) return ret; /* length-1 for unused bits counter */ ret = wc_ecc_import_x963(pkiMsg + (*idx), length - 1, kari->senderKey); if (ret != 0) return ret; (*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; if (kari == NULL || pkiMsg == NULL || idx == NULL) return BAD_FUNC_ARG; savedIdx = *idx; /* starts with EXPLICIT [1] */ if (pkiMsg[(*idx)++] != (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 ( (pkiMsgSz > ((*idx) + 1)) && (pkiMsg[(*idx)++] != 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) { if (kari == NULL || pkiMsg == NULL || idx == NULL || keyAgreeOID == NULL || keyWrapOID == NULL) return BAD_FUNC_ARG; /* remove KeyEncryptionAlgorithmIdentifier */ if (GetAlgoId(pkiMsg, idx, keyAgreeOID, oidCmsKeyAgreeType, pkiMsgSz) < 0) return ASN_PARSE_E; /* remove KeyWrapAlgorithm, stored in parameter of KeyEncAlgoId */ if (GetAlgoId(pkiMsg, idx, keyWrapOID, oidKeyWrapType, pkiMsgSz) < 0) return ASN_PARSE_E; 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) { int length; byte subjKeyId[KEYID_SIZE]; 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; /* remove RecipientKeyIdentifier IMPLICIT [0] */ if ( (pkiMsgSz > (*idx + 1)) && (pkiMsg[(*idx)++] == (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 ( (pkiMsgSz > (*idx + 1)) && (pkiMsg[(*idx)++] != 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(subjKeyId, pkiMsg + (*idx), KEYID_SIZE); (*idx) += length; /* subject key id should match if recipient found */ if (XMEMCMP(subjKeyId, kari->decoded->extSubjKeyId, KEYID_SIZE) == 0) { *recipFound = 1; } /* remove EncryptedKey */ if ( (pkiMsgSz > (*idx + 1)) && (pkiMsg[(*idx)++] != 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 */ /* decode ASN.1 KeyAgreeRecipientInfo (kari), return 0 on success, * < 0 on error */ static int wc_PKCS7_DecodeKari(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, word32* idx, byte* decryptedKey, word32* decryptedKeySz, int* recipFound) { #ifdef HAVE_ECC int ret, keySz; int encryptedKeySz; int direction = 0; word32 keyAgreeOID, keyWrapOID; #ifdef WOLFSSL_SMALL_STACK byte* encryptedKey; #else byte encryptedKey[MAX_ENCRYPTED_KEY_SZ]; #endif WC_PKCS7_KARI* kari; if (pkcs7 == NULL || pkcs7->singleCert == NULL || pkcs7->singleCertSz == 0 || pkiMsg == NULL || idx == NULL || decryptedKey == NULL || decryptedKeySz == NULL) { return BAD_FUNC_ARG; } 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, NULL, DYNAMIC_TYPE_PKCS7); if (encryptedKey == NULL) { wc_PKCS7_KariFree(kari); return MEMORY_E; } #endif encryptedKeySz = MAX_ENCRYPTED_KEY_SZ; /* parse cert and key */ ret = wc_PKCS7_KariParseRecipCert(kari, (byte*)pkcs7->singleCert, pkcs7->singleCertSz, pkcs7->privateKey, pkcs7->privateKeySz); if (ret != 0) { wc_PKCS7_KariFree(kari); 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, NULL, 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, NULL, 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, NULL, DYNAMIC_TYPE_PKCS7); #endif return ret; } /* set direction based on key wrap algorithm */ switch (keyWrapOID) { #ifndef NO_AES case AES128_WRAP: case AES192_WRAP: case AES256_WRAP: direction = AES_DECRYPTION; break; #endif default: wc_PKCS7_KariFree(kari); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif WOLFSSL_MSG("AES key wrap algorithm unsupported"); return BAD_KEYWRAP_ALG_E; } /* remove RecipientEncryptedKeys */ ret = wc_PKCS7_KariGetRecipientEncryptedKeys(kari, pkiMsg, pkiMsgSz, idx, recipFound, encryptedKey, &encryptedKeySz); if (ret != 0) { wc_PKCS7_KariFree(kari); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ret; } /* create KEK */ ret = wc_PKCS7_KariGenerateKEK(kari, keyWrapOID, pkcs7->keyAgreeOID); if (ret != 0) { wc_PKCS7_KariFree(kari); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ret; } /* decrypt CEK with KEK */ keySz = wc_PKCS7_KariKeyWrap(encryptedKey, encryptedKeySz, kari->kek, kari->kekSz, decryptedKey, *decryptedKeySz, keyWrapOID, direction); if (keySz <= 0) { wc_PKCS7_KariFree(kari); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return keySz; } *decryptedKeySz = (word32)keySz; wc_PKCS7_KariFree(kari); #ifdef WOLFSSL_SMALL_STACK XFREE(encryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return 0; #else (void)pkcs7; (void)pkiMsg; (void)pkiMsgSz; (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_DecodeRecipientInfos(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, word32* idx, byte* decryptedKey, word32* decryptedKeySz, int* recipFound) { word32 savedIdx; int version, ret, length; if (pkcs7 == NULL || pkiMsg == NULL || idx == NULL || decryptedKey == NULL || decryptedKeySz == NULL || recipFound == NULL) { return BAD_FUNC_ARG; } savedIdx = *idx; /* 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) { if (GetMyVersion(pkiMsg, idx, &version, pkiMsgSz) < 0) { *idx = savedIdx; break; } if (version != 0) return ASN_VERSION_E; /* found ktri */ ret = wc_PKCS7_DecodeKtri(pkcs7, pkiMsg, pkiMsgSz, idx, decryptedKey, decryptedKeySz, recipFound); if (ret != 0) return ret; } else { /* kari is IMPLICIT[1] */ *idx = savedIdx; if (pkiMsg[*idx] == (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 */ ret = wc_PKCS7_DecodeKari(pkcs7, pkiMsg, pkiMsgSz, 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 0; } /* 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) { int recipFound = 0; int ret, version, length; word32 idx = 0; word32 contentType, encOID; word32 decryptedKeySz; int expBlockSz, blockKeySz; byte tmpIv[MAX_CONTENT_IV_SIZE]; #ifdef WOLFSSL_SMALL_STACK byte* decryptedKey; #else byte decryptedKey[MAX_ENCRYPTED_KEY_SZ]; #endif int encryptedContentSz; byte padLen; byte* encryptedContent = NULL; if (pkcs7 == NULL || pkcs7->singleCert == NULL || pkcs7->singleCertSz == 0 || pkcs7->privateKey == NULL || pkcs7->privateKeySz == 0) return BAD_FUNC_ARG; if (pkiMsg == NULL || pkiMsgSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; /* read past ContentInfo, verify type is envelopedData */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; if (contentType != ENVELOPED_DATA) { WOLFSSL_MSG("PKCS#7 input not of type EnvelopedData"); return PKCS7_OID_E; } if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* remove EnvelopedData and version */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) return ASN_PARSE_E; /* TODO :: make this more accurate */ if ((pkcs7->publicKeyOID == RSAk && version != 0) || (pkcs7->publicKeyOID == ECDSAk && version != 2)) { WOLFSSL_MSG("PKCS#7 envelopedData needs to be of version 0"); return ASN_VERSION_E; } /* walk through RecipientInfo set, find correct recipient */ if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; #ifdef WOLFSSL_SMALL_STACK decryptedKey = (byte*)XMALLOC(MAX_ENCRYPTED_KEY_SZ, NULL, DYNAMIC_TYPE_PKCS7); if (decryptedKey == NULL) return MEMORY_E; #endif decryptedKeySz = MAX_ENCRYPTED_KEY_SZ; ret = wc_PKCS7_DecodeRecipientInfos(pkcs7, pkiMsg, pkiMsgSz, &idx, decryptedKey, &decryptedKeySz, &recipFound); if (ret != 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ret; } if (recipFound == 0) { WOLFSSL_MSG("No recipient found in envelopedData that matches input"); #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return PKCS7_RECIP_E; } /* remove EncryptedContentInfo */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } if (GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } blockKeySz = wc_PKCS7_GetOIDKeySize(encOID); if (blockKeySz < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return blockKeySz; } expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID); if (expBlockSz < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return expBlockSz; } /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ if (pkiMsg[idx++] != ASN_OCTET_STRING) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } if (length != expBlockSz) { WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } XMEMCPY(tmpIv, &pkiMsg[idx], length); idx += length; /* read encryptedContent, cont[0] */ if (pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } if (GetLength(pkiMsg, &idx, &encryptedContentSz, pkiMsgSz) <= 0) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ASN_PARSE_E; } encryptedContent = (byte*)XMALLOC(encryptedContentSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); if (encryptedContent == NULL) { #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return MEMORY_E; } XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); /* decrypt encryptedContent */ ret = wc_PKCS7_DecryptContent(encOID, decryptedKey, blockKeySz, tmpIv, expBlockSz, encryptedContent, encryptedContentSz, encryptedContent); if (ret != 0) { XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return ret; } padLen = encryptedContent[encryptedContentSz-1]; /* copy plaintext to output */ XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); /* free memory, zero out keys */ ForceZero(decryptedKey, MAX_ENCRYPTED_KEY_SZ); ForceZero(encryptedContent, encryptedContentSz); XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); #ifdef WOLFSSL_SMALL_STACK XFREE(decryptedKey, NULL, DYNAMIC_TYPE_PKCS7); #endif return encryptedContentSz - padLen; } /* 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_ALGO_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; /* outer content type */ outerContentTypeSz = wc_SetContentType(ENCRYPTED_DATA, outerContentType); /* version, 2 if unprotectedAttrs present, 0 if absent */ if (pkcs7->unprotectedAttribsSz > 0) { verSz = SetMyVersion(2, ver, 0); } else { verSz = SetMyVersion(0, ver, 0); } /* EncryptedContentInfo */ contentTypeSz = wc_SetContentType(pkcs7->contentOID, contentType); if (contentTypeSz == 0) return BAD_FUNC_ARG; /* 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 */ ret = wc_PKCS7_GenerateIV(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, 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; } attribs = (EncodedAttrib*)XMALLOC( sizeof(EncodedAttrib) * pkcs7->unprotectedAttribsSz, pkcs7->heap, DYNAMIC_TYPE_PKCS); if (attribs == NULL) { XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return MEMORY_E; } attribsCount = pkcs7->unprotectedAttribsSz; attribsSz = EncodeAttributes(attribs, pkcs7->unprotectedAttribsSz, pkcs7->unprotectedAttribs, pkcs7->unprotectedAttribsSz); flatAttribs = (byte*)XMALLOC(attribsSz, pkcs7->heap, DYNAMIC_TYPE_PKCS); if (flatAttribs == NULL) { XFREE(attribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); XFREE(plain, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return MEMORY_E; } FlattenAttributes(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; /* outer content */ outerContentSz = SetExplicit(0, totalSz, outerContent); totalSz += outerContentTypeSz; totalSz += outerContentSz; /* ContentInfo */ contentInfoSeqSz = SetSequence(totalSz, contentInfoSeq); totalSz += contentInfoSeqSz; if (totalSz > (int)outputSz) { WOLFSSL_MSG("PKCS#7 output buffer too small"); if (pkcs7->unprotectedAttribsSz != 0) { XFREE(attribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); XFREE(flatAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); } 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_PKCS); XFREE(flatAttribs, pkcs7->heap, DYNAMIC_TYPE_PKCS); } 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 length, attribLen; word32 oid, savedIdx, idx; PKCS7DecodedAttrib* attrib = NULL; if (pkcs7 == NULL || pkiMsg == NULL || pkiMsgSz == 0 || inOutIdx == NULL) return BAD_FUNC_ARG; idx = *inOutIdx; if (pkiMsg[idx] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 1)) return ASN_PARSE_E; idx++; if (GetLength(pkiMsg, &idx, &attribLen, pkiMsgSz) < 0) return ASN_PARSE_E; /* loop through attributes */ while (attribLen > 0) { if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; attribLen -= (length + 2); /* TAG + LENGTH + DATA */ savedIdx = idx; attrib = (PKCS7DecodedAttrib*)XMALLOC(sizeof(PKCS7DecodedAttrib), pkcs7->heap, DYNAMIC_TYPE_PKCS); if (attrib == NULL) { return MEMORY_E; } XMEMSET(attrib, 0, sizeof(PKCS7DecodedAttrib)); /* save attribute OID bytes and size */ if (GetObjectId(pkiMsg, &idx, &oid, oidIgnoreType, pkiMsgSz) < 0) { XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS); return ASN_PARSE_E; } attrib->oidSz = idx - savedIdx; attrib->oid = (byte*)XMALLOC(attrib->oidSz, pkcs7->heap, DYNAMIC_TYPE_PKCS); if (attrib->oid == NULL) { XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS); return MEMORY_E; } XMEMCPY(attrib->oid, pkiMsg + savedIdx, attrib->oidSz); /* save attribute value bytes and size */ if (GetSet(pkiMsg, &idx, &length, pkiMsgSz) < 0) { XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS); XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS); return ASN_PARSE_E; } if ((pkiMsgSz - idx) < (word32)length) { XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS); XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS); return ASN_PARSE_E; } attrib->valueSz = (word32)length; attrib->value = (byte*)XMALLOC(attrib->valueSz, pkcs7->heap, DYNAMIC_TYPE_PKCS); if (attrib->value == NULL) { XFREE(attrib->oid, pkcs7->heap, DYNAMIC_TYPE_PKCS); XFREE(attrib, pkcs7->heap, DYNAMIC_TYPE_PKCS); return MEMORY_E; } XMEMCPY(attrib->value, pkiMsg + 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; } } *inOutIdx = idx; return 0; } /* unwrap and decrypt PKCS#7/CMS encrypted-data object, returned decoded size */ int wc_PKCS7_DecodeEncryptedData(PKCS7* pkcs7, byte* pkiMsg, word32 pkiMsgSz, byte* output, word32 outputSz) { int ret, version, length, haveAttribs; word32 idx = 0; word32 contentType, encOID; int expBlockSz; byte tmpIv[MAX_CONTENT_IV_SIZE]; int encryptedContentSz; byte padLen; byte* encryptedContent = NULL; if (pkcs7 == NULL || pkcs7->encryptionKey == NULL || pkcs7->encryptionKeySz == 0) return BAD_FUNC_ARG; if (pkiMsg == NULL || pkiMsgSz == 0 || output == NULL || outputSz == 0) return BAD_FUNC_ARG; /* read past ContentInfo, verify type is encrypted-data */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; if (contentType != ENCRYPTED_DATA) { WOLFSSL_MSG("PKCS#7 input not of type EncryptedData"); return PKCS7_OID_E; } if (pkiMsg[idx++] != (ASN_CONSTRUCTED | ASN_CONTEXT_SPECIFIC | 0)) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* remove EncryptedData and version */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; /* get version, check later */ haveAttribs = 0; if (GetMyVersion(pkiMsg, &idx, &version, pkiMsgSz) < 0) return ASN_PARSE_E; /* remove EncryptedContentInfo */ if (GetSequence(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if (wc_GetContentType(pkiMsg, &idx, &contentType, pkiMsgSz) < 0) return ASN_PARSE_E; if (GetAlgoId(pkiMsg, &idx, &encOID, oidBlkType, pkiMsgSz) < 0) return ASN_PARSE_E; expBlockSz = wc_PKCS7_GetOIDBlockSize(encOID); if (expBlockSz < 0) return expBlockSz; /* get block cipher IV, stored in OPTIONAL parameter of AlgoID */ if (pkiMsg[idx++] != ASN_OCTET_STRING) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &length, pkiMsgSz) < 0) return ASN_PARSE_E; if (length != expBlockSz) { WOLFSSL_MSG("Incorrect IV length, must be of content alg block size"); return ASN_PARSE_E; } XMEMCPY(tmpIv, &pkiMsg[idx], length); idx += length; /* read encryptedContent, cont[0] */ if (pkiMsg[idx++] != (ASN_CONTEXT_SPECIFIC | 0)) return ASN_PARSE_E; if (GetLength(pkiMsg, &idx, &encryptedContentSz, pkiMsgSz) <= 0) return ASN_PARSE_E; encryptedContent = (byte*)XMALLOC(encryptedContentSz, pkcs7->heap, DYNAMIC_TYPE_PKCS7); if (encryptedContent == NULL) return MEMORY_E; XMEMCPY(encryptedContent, &pkiMsg[idx], encryptedContentSz); idx += encryptedContentSz; /* decrypt encryptedContent */ ret = wc_PKCS7_DecryptContent(encOID, pkcs7->encryptionKey, pkcs7->encryptionKeySz, tmpIv, expBlockSz, encryptedContent, encryptedContentSz, encryptedContent); if (ret != 0) { XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ret; } padLen = encryptedContent[encryptedContentSz-1]; /* copy plaintext to output */ XMEMCPY(output, encryptedContent, encryptedContentSz - padLen); /* get implicit[1] unprotected attributes, optional */ pkcs7->decodedAttrib = NULL; if (idx < pkiMsgSz) { haveAttribs = 1; ret = wc_PKCS7_DecodeUnprotectedAttributes(pkcs7, pkiMsg, pkiMsgSz, &idx); if (ret != 0) { ForceZero(encryptedContent, encryptedContentSz); XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return ASN_PARSE_E; } } /* go back and check the version now that attribs have been processed */ if ((haveAttribs == 0 && version != 0) || (haveAttribs == 1 && version != 2) ) { WOLFSSL_MSG("Wrong PKCS#7 EncryptedData version"); return ASN_VERSION_E; } ForceZero(encryptedContent, encryptedContentSz); XFREE(encryptedContent, pkcs7->heap, DYNAMIC_TYPE_PKCS7); return encryptedContentSz - padLen; } #else /* HAVE_PKCS7 */ #ifdef _MSC_VER /* 4206 warning for blank file */ #pragma warning(disable: 4206) #endif #endif /* HAVE_PKCS7 */